diff --git a/Documentation/devicetree/bindings/regulator/s5m8767-regulator.txt b/Documentation/devicetree/bindings/regulator/s5m8767-regulator.txt index fc6b38f035bd..d290988ed975 100644 --- a/Documentation/devicetree/bindings/regulator/s5m8767-regulator.txt +++ b/Documentation/devicetree/bindings/regulator/s5m8767-regulator.txt @@ -69,13 +69,16 @@ sub-node should be of the format as listed below. }; }; The above regulator entries are defined in regulator bindings documentation -except op_mode description. +except these properties: - op_mode: describes the different operating modes of the LDO's with power mode change in SOC. The different possible values are, 0 - always off mode 1 - on in normal mode 2 - low power mode 3 - suspend mode + - s5m8767,pmic-ext-control-gpios: (optional) GPIO specifier for one + GPIO controlling this regulator (enable/disable); This is + valid only for buck9. The following are the names of the regulators that the s5m8767 pmic block supports. Note: The 'n' in LDOn and BUCKn represents the LDO or BUCK number @@ -148,5 +151,13 @@ Example: regulator-always-on; regulator-boot-on; }; + + vemmc_reg: BUCK9 { + regulator-name = "VMEM_VDD_2.8V"; + regulator-min-microvolt = <2800000>; + regulator-max-microvolt = <2800000>; + op_mode = <3>; /* Standby Mode */ + s5m8767,pmic-ext-control-gpios = <&gpk0 2 0>; + }; }; }; diff --git a/Documentation/devicetree/bindings/regulator/ti-abb-regulator.txt b/Documentation/devicetree/bindings/regulator/ti-abb-regulator.txt index 2e57a33e9029..c58db75f959e 100644 --- a/Documentation/devicetree/bindings/regulator/ti-abb-regulator.txt +++ b/Documentation/devicetree/bindings/regulator/ti-abb-regulator.txt @@ -4,10 +4,14 @@ Required Properties: - compatible: Should be one of: - "ti,abb-v1" for older SoCs like OMAP3 - "ti,abb-v2" for newer SoCs like OMAP4, OMAP5 + - "ti,abb-v3" for a generic definition where setup and control registers are + provided (example: DRA7) - reg: Address and length of the register set for the device. It contains the information of registers in the same order as described by reg-names - reg-names: Should contain the reg names - - "base-address" - contains base address of ABB module + - "base-address" - contains base address of ABB module (ti,abb-v1,ti,abb-v2) + - "control-address" - contains control register address of ABB module (ti,abb-v3) + - "setup-address" - contains setup register address of ABB module (ti,abb-v3) - "int-address" - contains address of interrupt register for ABB module (also see Optional properties) - #address-cell: should be 0 diff --git a/drivers/regulator/Kconfig b/drivers/regulator/Kconfig index e5e4017b1011..8f3866c44a3f 100644 --- a/drivers/regulator/Kconfig +++ b/drivers/regulator/Kconfig @@ -448,6 +448,12 @@ config REGULATOR_S5M8767 via I2C bus. S5M8767A have 9 Bucks and 28 LDOs output and supports DVS mode with 8bits of output voltage control. +config REGULATOR_ST_PWM + tristate "STMicroelectronics PWM voltage regulator" + depends on ARCH_STI + help + This driver supports ST's PWM controlled voltage regulators. + config REGULATOR_TI_ABB tristate "TI Adaptive Body Bias on-chip LDO" depends on ARCH_OMAP diff --git a/drivers/regulator/Makefile b/drivers/regulator/Makefile index c3416728c14d..0cfca37941ec 100644 --- a/drivers/regulator/Makefile +++ b/drivers/regulator/Makefile @@ -61,6 +61,7 @@ obj-$(CONFIG_REGULATOR_RC5T583) += rc5t583-regulator.o obj-$(CONFIG_REGULATOR_S2MPA01) += s2mpa01.o obj-$(CONFIG_REGULATOR_S2MPS11) += s2mps11.o obj-$(CONFIG_REGULATOR_S5M8767) += s5m8767.o +obj-$(CONFIG_REGULATOR_ST_PWM) += st-pwm.o obj-$(CONFIG_REGULATOR_STW481X_VMMC) += stw481x-vmmc.o obj-$(CONFIG_REGULATOR_TI_ABB) += ti-abb-regulator.o obj-$(CONFIG_REGULATOR_TPS6105X) += tps6105x-regulator.o diff --git a/drivers/regulator/s5m8767.c b/drivers/regulator/s5m8767.c index d958dfa05125..f05badabd69e 100644 --- a/drivers/regulator/s5m8767.c +++ b/drivers/regulator/s5m8767.c @@ -11,11 +11,8 @@ * */ -#include #include -#include #include -#include #include #include #include @@ -170,12 +167,11 @@ static unsigned int s5m8767_opmode_reg[][4] = { {0x0, 0x3, 0x1, 0x1}, /* BUCK9 */ }; -static int s5m8767_get_register(struct regulator_dev *rdev, int *reg, - int *enable_ctrl) +static int s5m8767_get_register(struct s5m8767_info *s5m8767, int reg_id, + int *reg, int *enable_ctrl) { - int i, reg_id = rdev_get_id(rdev); + int i; unsigned int mode; - struct s5m8767_info *s5m8767 = rdev_get_drvdata(rdev); switch (reg_id) { case S5M8767_LDO1 ... S5M8767_LDO2: @@ -214,53 +210,6 @@ static int s5m8767_get_register(struct regulator_dev *rdev, int *reg, return 0; } -static int s5m8767_reg_is_enabled(struct regulator_dev *rdev) -{ - struct s5m8767_info *s5m8767 = rdev_get_drvdata(rdev); - int ret, reg; - int enable_ctrl; - unsigned int val; - - ret = s5m8767_get_register(rdev, ®, &enable_ctrl); - if (ret == -EINVAL) - return 1; - else if (ret) - return ret; - - ret = regmap_read(s5m8767->iodev->regmap_pmic, reg, &val); - if (ret) - return ret; - - return (val & S5M8767_ENCTRL_MASK) == enable_ctrl; -} - -static int s5m8767_reg_enable(struct regulator_dev *rdev) -{ - struct s5m8767_info *s5m8767 = rdev_get_drvdata(rdev); - int ret, reg; - int enable_ctrl; - - ret = s5m8767_get_register(rdev, ®, &enable_ctrl); - if (ret) - return ret; - - return regmap_update_bits(s5m8767->iodev->regmap_pmic, reg, - S5M8767_ENCTRL_MASK, enable_ctrl); -} - -static int s5m8767_reg_disable(struct regulator_dev *rdev) -{ - struct s5m8767_info *s5m8767 = rdev_get_drvdata(rdev); - int ret, reg, enable_ctrl; - - ret = s5m8767_get_register(rdev, ®, &enable_ctrl); - if (ret) - return ret; - - return regmap_update_bits(s5m8767->iodev->regmap_pmic, reg, - S5M8767_ENCTRL_MASK, ~S5M8767_ENCTRL_MASK); -} - static int s5m8767_get_vsel_reg(int reg_id, struct s5m8767_info *s5m8767) { int reg; @@ -410,9 +359,9 @@ static int s5m8767_set_voltage_time_sel(struct regulator_dev *rdev, static struct regulator_ops s5m8767_ops = { .list_voltage = regulator_list_voltage_linear, - .is_enabled = s5m8767_reg_is_enabled, - .enable = s5m8767_reg_enable, - .disable = s5m8767_reg_disable, + .is_enabled = regulator_is_enabled_regmap, + .enable = regulator_enable_regmap, + .disable = regulator_disable_regmap, .get_voltage_sel = regulator_get_voltage_sel_regmap, .set_voltage_sel = s5m8767_set_voltage_sel, .set_voltage_time_sel = s5m8767_set_voltage_time_sel, @@ -420,9 +369,9 @@ static struct regulator_ops s5m8767_ops = { static struct regulator_ops s5m8767_buck78_ops = { .list_voltage = regulator_list_voltage_linear, - .is_enabled = s5m8767_reg_is_enabled, - .enable = s5m8767_reg_enable, - .disable = s5m8767_reg_disable, + .is_enabled = regulator_is_enabled_regmap, + .enable = regulator_enable_regmap, + .disable = regulator_disable_regmap, .get_voltage_sel = regulator_get_voltage_sel_regmap, .set_voltage_sel = regulator_set_voltage_sel_regmap, }; @@ -483,6 +432,66 @@ static struct regulator_desc regulators[] = { s5m8767_regulator_desc(BUCK9), }; +/* + * Enable GPIO control over BUCK9 in regulator_config for that regulator. + */ +static void s5m8767_regulator_config_ext_control(struct s5m8767_info *s5m8767, + struct sec_regulator_data *rdata, + struct regulator_config *config) +{ + int i, mode = 0; + + if (rdata->id != S5M8767_BUCK9) + return; + + /* Check if opmode for regulator matches S5M8767_ENCTRL_USE_GPIO */ + for (i = 0; i < s5m8767->num_regulators; i++) { + const struct sec_opmode_data *opmode = &s5m8767->opmode[i]; + if (opmode->id == rdata->id) { + mode = s5m8767_opmode_reg[rdata->id][opmode->mode]; + break; + } + } + if (mode != S5M8767_ENCTRL_USE_GPIO) { + dev_warn(s5m8767->dev, + "ext-control for %s: mismatched op_mode (%x), ignoring\n", + rdata->reg_node->name, mode); + return; + } + + if (!gpio_is_valid(rdata->ext_control_gpio)) { + dev_warn(s5m8767->dev, + "ext-control for %s: GPIO not valid, ignoring\n", + rdata->reg_node->name); + return; + } + + config->ena_gpio = rdata->ext_control_gpio; + config->ena_gpio_flags = GPIOF_OUT_INIT_HIGH; +} + +/* + * Turn on GPIO control over BUCK9. + */ +static int s5m8767_enable_ext_control(struct s5m8767_info *s5m8767, + struct regulator_dev *rdev) +{ + int id = rdev_get_id(rdev); + int ret, reg, enable_ctrl; + + if (id != S5M8767_BUCK9) + return -EINVAL; + + ret = s5m8767_get_register(s5m8767, id, ®, &enable_ctrl); + if (ret) + return ret; + + return regmap_update_bits(s5m8767->iodev->regmap_pmic, + reg, S5M8767_ENCTRL_MASK, + S5M8767_ENCTRL_USE_GPIO << S5M8767_ENCTRL_SHIFT); +} + + #ifdef CONFIG_OF static int s5m8767_pmic_dt_parse_dvs_gpio(struct sec_pmic_dev *iodev, struct sec_platform_data *pdata, @@ -520,6 +529,16 @@ static int s5m8767_pmic_dt_parse_ds_gpio(struct sec_pmic_dev *iodev, return 0; } +static void s5m8767_pmic_dt_parse_ext_control_gpio(struct sec_pmic_dev *iodev, + struct sec_regulator_data *rdata, + struct device_node *reg_np) +{ + rdata->ext_control_gpio = of_get_named_gpio(reg_np, + "s5m8767,pmic-ext-control-gpios", 0); + if (!gpio_is_valid(rdata->ext_control_gpio)) + rdata->ext_control_gpio = 0; +} + static int s5m8767_pmic_dt_parse_pdata(struct platform_device *pdev, struct sec_platform_data *pdata) { @@ -546,19 +565,13 @@ static int s5m8767_pmic_dt_parse_pdata(struct platform_device *pdev, rdata = devm_kzalloc(&pdev->dev, sizeof(*rdata) * pdata->num_regulators, GFP_KERNEL); - if (!rdata) { - dev_err(iodev->dev, - "could not allocate memory for regulator data\n"); + if (!rdata) return -ENOMEM; - } rmode = devm_kzalloc(&pdev->dev, sizeof(*rmode) * pdata->num_regulators, GFP_KERNEL); - if (!rmode) { - dev_err(iodev->dev, - "could not allocate memory for regulator mode\n"); + if (!rmode) return -ENOMEM; - } pdata->regulators = rdata; pdata->opmode = rmode; @@ -574,6 +587,8 @@ static int s5m8767_pmic_dt_parse_pdata(struct platform_device *pdev, continue; } + s5m8767_pmic_dt_parse_ext_control_gpio(iodev, rdata, reg_np); + rdata->id = i; rdata->initdata = of_get_regulator_init_data( &pdev->dev, reg_np); @@ -922,6 +937,7 @@ static int s5m8767_pmic_probe(struct platform_device *pdev) for (i = 0; i < pdata->num_regulators; i++) { const struct sec_voltage_desc *desc; int id = pdata->regulators[i].id; + int enable_reg, enable_val; desc = reg_voltage_map[id]; if (desc) { @@ -935,6 +951,12 @@ static int s5m8767_pmic_probe(struct platform_device *pdev) regulators[id].vsel_mask = 0x3f; else regulators[id].vsel_mask = 0xff; + + s5m8767_get_register(s5m8767, id, &enable_reg, + &enable_val); + regulators[id].enable_reg = enable_reg; + regulators[id].enable_mask = S5M8767_ENCTRL_MASK; + regulators[id].enable_val = enable_val; } config.dev = s5m8767->dev; @@ -942,6 +964,9 @@ static int s5m8767_pmic_probe(struct platform_device *pdev) config.driver_data = s5m8767; config.regmap = iodev->regmap_pmic; config.of_node = pdata->regulators[i].reg_node; + if (pdata->regulators[i].ext_control_gpio) + s5m8767_regulator_config_ext_control(s5m8767, + &pdata->regulators[i], &config); rdev[i] = devm_regulator_register(&pdev->dev, ®ulators[id], &config); @@ -951,6 +976,16 @@ static int s5m8767_pmic_probe(struct platform_device *pdev) id); return ret; } + + if (pdata->regulators[i].ext_control_gpio) { + ret = s5m8767_enable_ext_control(s5m8767, rdev[i]); + if (ret < 0) { + dev_err(s5m8767->dev, + "failed to enable gpio control over %s: %d\n", + rdev[i]->desc->name, ret); + return ret; + } + } } return 0; diff --git a/drivers/regulator/st-pwm.c b/drivers/regulator/st-pwm.c new file mode 100644 index 000000000000..e367af1c5f9d --- /dev/null +++ b/drivers/regulator/st-pwm.c @@ -0,0 +1,190 @@ +/* + * Regulator driver for ST's PWM Regulators + * + * Copyright (C) 2014 - STMicroelectronics Inc. + * + * Author: Lee Jones + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define ST_PWM_REG_PERIOD 8448 + +struct st_pwm_regulator_pdata { + const struct regulator_desc *desc; + struct st_pwm_voltages *duty_cycle_table; +}; + +struct st_pwm_regulator_data { + const struct st_pwm_regulator_pdata *pdata; + struct pwm_device *pwm; + bool enabled; + int state; +}; + +struct st_pwm_voltages { + unsigned int uV; + unsigned int dutycycle; +}; + +static int st_pwm_regulator_get_voltage_sel(struct regulator_dev *dev) +{ + struct st_pwm_regulator_data *drvdata = rdev_get_drvdata(dev); + + return drvdata->state; +} + +static int st_pwm_regulator_set_voltage_sel(struct regulator_dev *dev, + unsigned selector) +{ + struct st_pwm_regulator_data *drvdata = rdev_get_drvdata(dev); + int dutycycle; + int ret; + + dutycycle = (ST_PWM_REG_PERIOD / 100) * + drvdata->pdata->duty_cycle_table[selector].dutycycle; + + ret = pwm_config(drvdata->pwm, dutycycle, ST_PWM_REG_PERIOD); + if (ret) { + dev_err(&dev->dev, "Failed to configure PWM\n"); + return ret; + } + + drvdata->state = selector; + + if (!drvdata->enabled) { + ret = pwm_enable(drvdata->pwm); + if (ret) { + dev_err(&dev->dev, "Failed to enable PWM\n"); + return ret; + } + drvdata->enabled = true; + } + + return 0; +} + +static int st_pwm_regulator_list_voltage(struct regulator_dev *dev, + unsigned selector) +{ + struct st_pwm_regulator_data *drvdata = rdev_get_drvdata(dev); + + if (selector >= dev->desc->n_voltages) + return -EINVAL; + + return drvdata->pdata->duty_cycle_table[selector].uV; +} + +static struct regulator_ops st_pwm_regulator_voltage_ops = { + .set_voltage_sel = st_pwm_regulator_set_voltage_sel, + .get_voltage_sel = st_pwm_regulator_get_voltage_sel, + .list_voltage = st_pwm_regulator_list_voltage, + .map_voltage = regulator_map_voltage_iterate, +}; + +static struct st_pwm_voltages b2105_duty_cycle_table[] = { + { .uV = 1114000, .dutycycle = 0, }, + { .uV = 1095000, .dutycycle = 10, }, + { .uV = 1076000, .dutycycle = 20, }, + { .uV = 1056000, .dutycycle = 30, }, + { .uV = 1036000, .dutycycle = 40, }, + { .uV = 1016000, .dutycycle = 50, }, + /* WARNING: Values above 50% duty-cycle cause boot failures. */ +}; + +static const struct regulator_desc b2105_desc = { + .name = "b2105-pwm-regulator", + .ops = &st_pwm_regulator_voltage_ops, + .type = REGULATOR_VOLTAGE, + .owner = THIS_MODULE, + .n_voltages = ARRAY_SIZE(b2105_duty_cycle_table), + .supply_name = "pwm", +}; + +static const struct st_pwm_regulator_pdata b2105_info = { + .desc = &b2105_desc, + .duty_cycle_table = b2105_duty_cycle_table, +}; + +static struct of_device_id st_pwm_of_match[] = { + { .compatible = "st,b2105-pwm-regulator", .data = &b2105_info, }, + { }, +}; +MODULE_DEVICE_TABLE(of, st_pwm_of_match); + +static int st_pwm_regulator_probe(struct platform_device *pdev) +{ + struct st_pwm_regulator_data *drvdata; + struct regulator_dev *regulator; + struct regulator_config config = { }; + struct device_node *np = pdev->dev.of_node; + const struct of_device_id *of_match; + + if (!np) { + dev_err(&pdev->dev, "Device Tree node missing\n"); + return -EINVAL; + } + + drvdata = devm_kzalloc(&pdev->dev, sizeof(*drvdata), GFP_KERNEL); + if (!drvdata) + return -ENOMEM; + + of_match = of_match_device(st_pwm_of_match, &pdev->dev); + if (!of_match) { + dev_err(&pdev->dev, "failed to match of device\n"); + return -ENODEV; + } + drvdata->pdata = of_match->data; + + config.init_data = of_get_regulator_init_data(&pdev->dev, np); + if (!config.init_data) + return -ENOMEM; + + config.of_node = np; + config.dev = &pdev->dev; + config.driver_data = drvdata; + + drvdata->pwm = devm_pwm_get(&pdev->dev, NULL); + if (IS_ERR(drvdata->pwm)) { + dev_err(&pdev->dev, "Failed to get PWM\n"); + return PTR_ERR(drvdata->pwm); + } + + regulator = devm_regulator_register(&pdev->dev, + drvdata->pdata->desc, &config); + if (IS_ERR(regulator)) { + dev_err(&pdev->dev, "Failed to register regulator %s\n", + drvdata->pdata->desc->name); + return PTR_ERR(regulator); + } + + return 0; +} + +static struct platform_driver st_pwm_regulator_driver = { + .driver = { + .name = "st-pwm-regulator", + .owner = THIS_MODULE, + .of_match_table = of_match_ptr(st_pwm_of_match), + }, + .probe = st_pwm_regulator_probe, +}; + +module_platform_driver(st_pwm_regulator_driver); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Lee Jones "); +MODULE_DESCRIPTION("ST PWM Regulator Driver"); +MODULE_ALIAS("platform:st_pwm-regulator"); diff --git a/drivers/regulator/ti-abb-regulator.c b/drivers/regulator/ti-abb-regulator.c index b187b6bba7ad..a2dabb575b97 100644 --- a/drivers/regulator/ti-abb-regulator.c +++ b/drivers/regulator/ti-abb-regulator.c @@ -54,8 +54,8 @@ struct ti_abb_info { /** * struct ti_abb_reg - Register description for ABB block - * @setup_reg: setup register offset from base - * @control_reg: control register offset from base + * @setup_off: setup register offset from base + * @control_off: control register offset from base * @sr2_wtcnt_value_mask: setup register- sr2_wtcnt_value mask * @fbb_sel_mask: setup register- FBB sel mask * @rbb_sel_mask: setup register- RBB sel mask @@ -64,8 +64,8 @@ struct ti_abb_info { * @opp_sel_mask: control register - mask for mode to operate */ struct ti_abb_reg { - u32 setup_reg; - u32 control_reg; + u32 setup_off; + u32 control_off; /* Setup register fields */ u32 sr2_wtcnt_value_mask; @@ -83,6 +83,8 @@ struct ti_abb_reg { * @rdesc: regulator descriptor * @clk: clock(usually sysclk) supplying ABB block * @base: base address of ABB block + * @setup_reg: setup register of ABB block + * @control_reg: control register of ABB block * @int_base: interrupt register base address * @efuse_base: (optional) efuse base address for ABB modes * @ldo_base: (optional) LDOVBB vset override base address @@ -99,6 +101,8 @@ struct ti_abb { struct regulator_desc rdesc; struct clk *clk; void __iomem *base; + void __iomem *setup_reg; + void __iomem *control_reg; void __iomem *int_base; void __iomem *efuse_base; void __iomem *ldo_base; @@ -118,20 +122,18 @@ struct ti_abb { * ti_abb_rmw() - handy wrapper to set specific register bits * @mask: mask for register field * @value: value shifted to mask location and written - * @offset: offset of register - * @base: base address + * @reg: register address * * Return: final register value (may be unused) */ -static inline u32 ti_abb_rmw(u32 mask, u32 value, u32 offset, - void __iomem *base) +static inline u32 ti_abb_rmw(u32 mask, u32 value, void __iomem *reg) { u32 val; - val = readl(base + offset); + val = readl(reg); val &= ~mask; val |= (value << __ffs(mask)) & mask; - writel(val, base + offset); + writel(val, reg); return val; } @@ -263,21 +265,19 @@ static int ti_abb_set_opp(struct regulator_dev *rdev, struct ti_abb *abb, if (ret) goto out; - ti_abb_rmw(regs->fbb_sel_mask | regs->rbb_sel_mask, 0, regs->setup_reg, - abb->base); + ti_abb_rmw(regs->fbb_sel_mask | regs->rbb_sel_mask, 0, abb->setup_reg); switch (info->opp_sel) { case TI_ABB_SLOW_OPP: - ti_abb_rmw(regs->rbb_sel_mask, 1, regs->setup_reg, abb->base); + ti_abb_rmw(regs->rbb_sel_mask, 1, abb->setup_reg); break; case TI_ABB_FAST_OPP: - ti_abb_rmw(regs->fbb_sel_mask, 1, regs->setup_reg, abb->base); + ti_abb_rmw(regs->fbb_sel_mask, 1, abb->setup_reg); break; } /* program next state of ABB ldo */ - ti_abb_rmw(regs->opp_sel_mask, info->opp_sel, regs->control_reg, - abb->base); + ti_abb_rmw(regs->opp_sel_mask, info->opp_sel, abb->control_reg); /* * program LDO VBB vset override if needed for !bypass mode @@ -288,7 +288,7 @@ static int ti_abb_set_opp(struct regulator_dev *rdev, struct ti_abb *abb, ti_abb_program_ldovbb(dev, abb, info); /* Initiate ABB ldo change */ - ti_abb_rmw(regs->opp_change_mask, 1, regs->control_reg, abb->base); + ti_abb_rmw(regs->opp_change_mask, 1, abb->control_reg); /* Wait for ABB LDO to complete transition to new Bias setting */ ret = ti_abb_wait_txdone(dev, abb); @@ -490,8 +490,7 @@ static int ti_abb_init_timings(struct device *dev, struct ti_abb *abb) dev_dbg(dev, "%s: Clk_rate=%ld, sr2_cnt=0x%08x\n", __func__, clk_get_rate(abb->clk), sr2_wt_cnt_val); - ti_abb_rmw(regs->sr2_wtcnt_value_mask, sr2_wt_cnt_val, regs->setup_reg, - abb->base); + ti_abb_rmw(regs->sr2_wtcnt_value_mask, sr2_wt_cnt_val, abb->setup_reg); return 0; } @@ -508,32 +507,24 @@ static int ti_abb_init_table(struct device *dev, struct ti_abb *abb, struct regulator_init_data *rinit_data) { struct ti_abb_info *info; - const struct property *prop; - const __be32 *abb_info; const u32 num_values = 6; char *pname = "ti,abb_info"; - u32 num_entries, i; + u32 i; unsigned int *volt_table; - int min_uV = INT_MAX, max_uV = 0; + int num_entries, min_uV = INT_MAX, max_uV = 0; struct regulation_constraints *c = &rinit_data->constraints; - prop = of_find_property(dev->of_node, pname, NULL); - if (!prop) { - dev_err(dev, "No '%s' property?\n", pname); - return -ENODEV; - } - - if (!prop->value) { - dev_err(dev, "Empty '%s' property?\n", pname); - return -ENODATA; - } - /* * Each abb_info is a set of n-tuple, where n is num_values, consisting * of voltage and a set of detection logic for ABB information for that * voltage to apply. */ - num_entries = prop->length / sizeof(u32); + num_entries = of_property_count_u32_elems(dev->of_node, pname); + if (num_entries < 0) { + dev_err(dev, "No '%s' property?\n", pname); + return num_entries; + } + if (!num_entries || (num_entries % num_values)) { dev_err(dev, "All '%s' list entries need %d vals\n", pname, num_values); @@ -542,38 +533,38 @@ static int ti_abb_init_table(struct device *dev, struct ti_abb *abb, num_entries /= num_values; info = devm_kzalloc(dev, sizeof(*info) * num_entries, GFP_KERNEL); - if (!info) { - dev_err(dev, "Can't allocate info table for '%s' property\n", - pname); + if (!info) return -ENOMEM; - } + abb->info = info; volt_table = devm_kzalloc(dev, sizeof(unsigned int) * num_entries, GFP_KERNEL); - if (!volt_table) { - dev_err(dev, "Can't allocate voltage table for '%s' property\n", - pname); + if (!volt_table) return -ENOMEM; - } abb->rdesc.n_voltages = num_entries; abb->rdesc.volt_table = volt_table; /* We do not know where the OPP voltage is at the moment */ abb->current_info_idx = -EINVAL; - abb_info = prop->value; for (i = 0; i < num_entries; i++, info++, volt_table++) { u32 efuse_offset, rbb_mask, fbb_mask, vset_mask; u32 efuse_val; /* NOTE: num_values should equal to entries picked up here */ - *volt_table = be32_to_cpup(abb_info++); - info->opp_sel = be32_to_cpup(abb_info++); - efuse_offset = be32_to_cpup(abb_info++); - rbb_mask = be32_to_cpup(abb_info++); - fbb_mask = be32_to_cpup(abb_info++); - vset_mask = be32_to_cpup(abb_info++); + of_property_read_u32_index(dev->of_node, pname, i * num_values, + volt_table); + of_property_read_u32_index(dev->of_node, pname, + i * num_values + 1, &info->opp_sel); + of_property_read_u32_index(dev->of_node, pname, + i * num_values + 2, &efuse_offset); + of_property_read_u32_index(dev->of_node, pname, + i * num_values + 3, &rbb_mask); + of_property_read_u32_index(dev->of_node, pname, + i * num_values + 4, &fbb_mask); + of_property_read_u32_index(dev->of_node, pname, + i * num_values + 5, &vset_mask); dev_dbg(dev, "[%d]v=%d ABB=%d ef=0x%x rbb=0x%x fbb=0x%x vset=0x%x\n", @@ -648,8 +639,8 @@ static struct regulator_ops ti_abb_reg_ops = { /* Default ABB block offsets, IF this changes in future, create new one */ static const struct ti_abb_reg abb_regs_v1 = { /* WARNING: registers are wrongly documented in TRM */ - .setup_reg = 0x04, - .control_reg = 0x00, + .setup_off = 0x04, + .control_off = 0x00, .sr2_wtcnt_value_mask = (0xff << 8), .fbb_sel_mask = (0x01 << 2), @@ -661,8 +652,8 @@ static const struct ti_abb_reg abb_regs_v1 = { }; static const struct ti_abb_reg abb_regs_v2 = { - .setup_reg = 0x00, - .control_reg = 0x04, + .setup_off = 0x00, + .control_off = 0x04, .sr2_wtcnt_value_mask = (0xff << 8), .fbb_sel_mask = (0x01 << 2), @@ -673,9 +664,20 @@ static const struct ti_abb_reg abb_regs_v2 = { .opp_sel_mask = (0x03 << 0), }; +static const struct ti_abb_reg abb_regs_generic = { + .sr2_wtcnt_value_mask = (0xff << 8), + .fbb_sel_mask = (0x01 << 2), + .rbb_sel_mask = (0x01 << 1), + .sr2_en_mask = (0x01 << 0), + + .opp_change_mask = (0x01 << 2), + .opp_sel_mask = (0x03 << 0), +}; + static const struct of_device_id ti_abb_of_match[] = { {.compatible = "ti,abb-v1", .data = &abb_regs_v1}, {.compatible = "ti,abb-v2", .data = &abb_regs_v2}, + {.compatible = "ti,abb-v3", .data = &abb_regs_generic}, { }, }; @@ -722,11 +724,29 @@ static int ti_abb_probe(struct platform_device *pdev) abb->regs = match->data; /* Map ABB resources */ - pname = "base-address"; - res = platform_get_resource_byname(pdev, IORESOURCE_MEM, pname); - abb->base = devm_ioremap_resource(dev, res); - if (IS_ERR(abb->base)) - return PTR_ERR(abb->base); + if (abb->regs->setup_off || abb->regs->control_off) { + pname = "base-address"; + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, pname); + abb->base = devm_ioremap_resource(dev, res); + if (IS_ERR(abb->base)) + return PTR_ERR(abb->base); + + abb->setup_reg = abb->base + abb->regs->setup_off; + abb->control_reg = abb->base + abb->regs->control_off; + + } else { + pname = "control-address"; + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, pname); + abb->control_reg = devm_ioremap_resource(dev, res); + if (IS_ERR(abb->control_reg)) + return PTR_ERR(abb->control_reg); + + pname = "setup-address"; + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, pname); + abb->setup_reg = devm_ioremap_resource(dev, res); + if (IS_ERR(abb->setup_reg)) + return PTR_ERR(abb->setup_reg); + } pname = "int-address"; res = platform_get_resource_byname(pdev, IORESOURCE_MEM, pname); @@ -860,7 +880,7 @@ skip_opt: platform_set_drvdata(pdev, rdev); /* Enable the ldo if not already done by bootloader */ - ti_abb_rmw(abb->regs->sr2_en_mask, 1, abb->regs->setup_reg, abb->base); + ti_abb_rmw(abb->regs->sr2_en_mask, 1, abb->setup_reg); return 0; } diff --git a/drivers/regulator/tps51632-regulator.c b/drivers/regulator/tps51632-regulator.c index b3764f594ee9..f31f22e3e1bd 100644 --- a/drivers/regulator/tps51632-regulator.c +++ b/drivers/regulator/tps51632-regulator.c @@ -227,10 +227,8 @@ static struct tps51632_regulator_platform_data * struct device_node *np = dev->of_node; pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL); - if (!pdata) { - dev_err(dev, "Memory alloc failed for platform data\n"); + if (!pdata) return NULL; - } pdata->reg_init_data = of_get_regulator_init_data(dev, dev->of_node); if (!pdata->reg_init_data) { @@ -299,10 +297,8 @@ static int tps51632_probe(struct i2c_client *client, } tps = devm_kzalloc(&client->dev, sizeof(*tps), GFP_KERNEL); - if (!tps) { - dev_err(&client->dev, "Memory allocation failed\n"); + if (!tps) return -ENOMEM; - } tps->dev = &client->dev; tps->desc.name = client->name; diff --git a/drivers/regulator/tps62360-regulator.c b/drivers/regulator/tps62360-regulator.c index c3fa15a299b1..a1672044e519 100644 --- a/drivers/regulator/tps62360-regulator.c +++ b/drivers/regulator/tps62360-regulator.c @@ -299,10 +299,8 @@ static struct tps62360_regulator_platform_data * struct device_node *np = dev->of_node; pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL); - if (!pdata) { - dev_err(dev, "Memory alloc failed for platform data\n"); + if (!pdata) return NULL; - } pdata->reg_init_data = of_get_regulator_init_data(dev, dev->of_node); if (!pdata->reg_init_data) { @@ -377,11 +375,8 @@ static int tps62360_probe(struct i2c_client *client, } tps = devm_kzalloc(&client->dev, sizeof(*tps), GFP_KERNEL); - if (!tps) { - dev_err(&client->dev, "%s(): Memory allocation failed\n", - __func__); + if (!tps) return -ENOMEM; - } tps->en_discharge = pdata->en_discharge; tps->en_internal_pulldn = pdata->en_internal_pulldn; diff --git a/drivers/regulator/tps6507x-regulator.c b/drivers/regulator/tps6507x-regulator.c index 162a0fae20b3..98e66ce26723 100644 --- a/drivers/regulator/tps6507x-regulator.c +++ b/drivers/regulator/tps6507x-regulator.c @@ -359,7 +359,6 @@ static struct regulator_ops tps6507x_pmic_ops = { .map_voltage = regulator_map_voltage_ascend, }; -#ifdef CONFIG_OF static struct of_regulator_match tps6507x_matches[] = { { .name = "VDCDC1"}, { .name = "VDCDC2"}, @@ -381,12 +380,10 @@ static struct tps6507x_board *tps6507x_parse_dt_reg_data( tps_board = devm_kzalloc(&pdev->dev, sizeof(*tps_board), GFP_KERNEL); - if (!tps_board) { - dev_err(&pdev->dev, "Failure to alloc pdata for regulators.\n"); + if (!tps_board) return NULL; - } - regulators = of_find_node_by_name(np, "regulators"); + regulators = of_get_child_by_name(np, "regulators"); if (!regulators) { dev_err(&pdev->dev, "regulator node not found\n"); return NULL; @@ -396,6 +393,7 @@ static struct tps6507x_board *tps6507x_parse_dt_reg_data( matches = tps6507x_matches; ret = of_regulator_match(&pdev->dev, regulators, matches, count); + of_node_put(regulators); if (ret < 0) { dev_err(&pdev->dev, "Error parsing regulator init data: %d\n", ret); @@ -406,10 +404,8 @@ static struct tps6507x_board *tps6507x_parse_dt_reg_data( reg_data = devm_kzalloc(&pdev->dev, (sizeof(struct regulator_init_data) * TPS6507X_NUM_REGULATOR), GFP_KERNEL); - if (!reg_data) { - dev_err(&pdev->dev, "Failure to alloc init data for regulators.\n"); + if (!reg_data) return NULL; - } tps_board->tps6507x_pmic_init_data = reg_data; @@ -424,15 +420,7 @@ static struct tps6507x_board *tps6507x_parse_dt_reg_data( return tps_board; } -#else -static inline struct tps6507x_board *tps6507x_parse_dt_reg_data( - struct platform_device *pdev, - struct of_regulator_match **tps6507x_reg_matches) -{ - *tps6507x_reg_matches = NULL; - return NULL; -} -#endif + static int tps6507x_pmic_probe(struct platform_device *pdev) { struct tps6507x_dev *tps6507x_dev = dev_get_drvdata(pdev->dev.parent); @@ -453,9 +441,10 @@ static int tps6507x_pmic_probe(struct platform_device *pdev) */ tps_board = dev_get_platdata(tps6507x_dev->dev); - if (!tps_board && tps6507x_dev->dev->of_node) + if (IS_ENABLED(CONFIG_OF) && !tps_board && + tps6507x_dev->dev->of_node) tps_board = tps6507x_parse_dt_reg_data(pdev, - &tps6507x_reg_matches); + &tps6507x_reg_matches); if (!tps_board) return -EINVAL; @@ -481,7 +470,7 @@ static int tps6507x_pmic_probe(struct platform_device *pdev) tps->info[i] = info; if (init_data->driver_data) { struct tps6507x_reg_platform_data *data = - init_data->driver_data; + init_data->driver_data; tps->info[i]->defdcdc_default = data->defdcdc_default; } diff --git a/drivers/regulator/tps65090-regulator.c b/drivers/regulator/tps65090-regulator.c index 676f75548f00..2e92ef68574d 100644 --- a/drivers/regulator/tps65090-regulator.c +++ b/drivers/regulator/tps65090-regulator.c @@ -168,17 +168,13 @@ static struct tps65090_platform_data *tps65090_parse_dt_reg_data( tps65090_pdata = devm_kzalloc(&pdev->dev, sizeof(*tps65090_pdata), GFP_KERNEL); - if (!tps65090_pdata) { - dev_err(&pdev->dev, "Memory alloc for tps65090_pdata failed\n"); + if (!tps65090_pdata) return ERR_PTR(-ENOMEM); - } reg_pdata = devm_kzalloc(&pdev->dev, TPS65090_REGULATOR_MAX * sizeof(*reg_pdata), GFP_KERNEL); - if (!reg_pdata) { - dev_err(&pdev->dev, "Memory alloc for reg_pdata failed\n"); + if (!reg_pdata) return ERR_PTR(-ENOMEM); - } regulators = of_get_child_by_name(np, "regulators"); if (!regulators) { @@ -188,6 +184,7 @@ static struct tps65090_platform_data *tps65090_parse_dt_reg_data( ret = of_regulator_match(&pdev->dev, regulators, tps65090_matches, ARRAY_SIZE(tps65090_matches)); + of_node_put(regulators); if (ret < 0) { dev_err(&pdev->dev, "Error parsing regulator init data: %d\n", ret); @@ -252,10 +249,8 @@ static int tps65090_regulator_probe(struct platform_device *pdev) pmic = devm_kzalloc(&pdev->dev, TPS65090_REGULATOR_MAX * sizeof(*pmic), GFP_KERNEL); - if (!pmic) { - dev_err(&pdev->dev, "mem alloc for pmic failed\n"); + if (!pmic) return -ENOMEM; - } for (num = 0; num < TPS65090_REGULATOR_MAX; num++) { tps_pdata = tps65090_pdata->reg_pdata[num]; diff --git a/drivers/regulator/tps65217-regulator.c b/drivers/regulator/tps65217-regulator.c index 9ea1bf26bd13..10b78d2b766a 100644 --- a/drivers/regulator/tps65217-regulator.c +++ b/drivers/regulator/tps65217-regulator.c @@ -187,7 +187,7 @@ static struct tps65217_board *tps65217_parse_dt(struct platform_device *pdev) struct device_node *regs; int i, count; - regs = of_find_node_by_name(node, "regulators"); + regs = of_get_child_by_name(node, "regulators"); if (!regs) return NULL; @@ -202,7 +202,7 @@ static struct tps65217_board *tps65217_parse_dt(struct platform_device *pdev) return NULL; for (i = 0; i < count; i++) { - if (!reg_matches[i].init_data || !reg_matches[i].of_node) + if (!reg_matches[i].of_node) continue; pdata->tps65217_init_data[i] = reg_matches[i].init_data; @@ -222,7 +222,6 @@ static int tps65217_regulator_probe(struct platform_device *pdev) { struct tps65217 *tps = dev_get_drvdata(pdev->dev.parent); struct tps65217_board *pdata = dev_get_platdata(tps->dev); - struct regulator_init_data *reg_data; struct regulator_dev *rdev; struct regulator_config config = { }; int i; @@ -243,19 +242,9 @@ static int tps65217_regulator_probe(struct platform_device *pdev) platform_set_drvdata(pdev, tps); for (i = 0; i < TPS65217_NUM_REGULATOR; i++) { - - reg_data = pdata->tps65217_init_data[i]; - - /* - * Regulator API handles empty constraints but not NULL - * constraints - */ - if (!reg_data) - continue; - /* Register the regulators */ config.dev = tps->dev; - config.init_data = reg_data; + config.init_data = pdata->tps65217_init_data[i]; config.driver_data = tps; config.regmap = tps->regmap; if (tps->dev->of_node) diff --git a/include/linux/mfd/samsung/core.h b/include/linux/mfd/samsung/core.h index 900bc9e1888b..157e32b6ca28 100644 --- a/include/linux/mfd/samsung/core.h +++ b/include/linux/mfd/samsung/core.h @@ -126,7 +126,8 @@ struct sec_platform_data { struct sec_regulator_data { int id; struct regulator_init_data *initdata; - struct device_node *reg_node; + struct device_node *reg_node; + int ext_control_gpio; }; /* diff --git a/include/linux/mfd/samsung/s5m8767.h b/include/linux/mfd/samsung/s5m8767.h index 2ab0b0f03641..243b58fec33d 100644 --- a/include/linux/mfd/samsung/s5m8767.h +++ b/include/linux/mfd/samsung/s5m8767.h @@ -183,9 +183,16 @@ enum s5m8767_regulators { S5M8767_REG_MAX, }; +/* LDO_EN/BUCK_EN field in registers */ #define S5M8767_ENCTRL_SHIFT 6 #define S5M8767_ENCTRL_MASK (0x3 << S5M8767_ENCTRL_SHIFT) +/* + * LDO_EN/BUCK_EN register value for controlling this Buck or LDO + * by GPIO (PWREN, BUCKEN). + */ +#define S5M8767_ENCTRL_USE_GPIO 0x1 + /* * Values for BUCK_RAMP field in DVS_RAMP register, matching raw values * in mV/us.