diff options
Diffstat (limited to 'drivers/regulator')
23 files changed, 1272 insertions, 145 deletions
diff --git a/drivers/regulator/Kconfig b/drivers/regulator/Kconfig index 6e96ef1bd74a..926cee0d0b5f 100644 --- a/drivers/regulator/Kconfig +++ b/drivers/regulator/Kconfig @@ -356,6 +356,13 @@ config REGULATOR_LM363X One boost output voltage is configurable and always on. Other LDOs are used for the display module. +config REGULATOR_LOCHNAGAR + tristate "Cirrus Logic Lochnagar regulator driver" + depends on MFD_LOCHNAGAR + help + This enables regulator support on the Cirrus Logic Lochnagar audio + development board. + config REGULATOR_LP3971 tristate "National Semiconductors LP3971 PMIC regulator driver" depends on I2C @@ -803,6 +810,18 @@ config REGULATOR_STM32_VREFBUF This driver can also be built as a module. If so, the module will be called stm32-vrefbuf. +config REGULATOR_STPMIC1 + tristate "STMicroelectronics STPMIC1 PMIC Regulators" + depends on MFD_STPMIC1 + help + This driver supports STMicroelectronics STPMIC1 PMIC voltage + regulators and switches. The STPMIC1 regulators supply power to + an application processor as well as to external system + peripherals such as DDR, Flash memories and system devices. + + To compile this driver as a module, choose M here: the + module will be called stpmic1_regulator. + 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 eac4d794f3b8..72488ef11b8a 100644 --- a/drivers/regulator/Makefile +++ b/drivers/regulator/Makefile @@ -46,6 +46,7 @@ obj-$(CONFIG_REGULATOR_HI655X) += hi655x-regulator.o obj-$(CONFIG_REGULATOR_ISL6271A) += isl6271a-regulator.o obj-$(CONFIG_REGULATOR_ISL9305) += isl9305.o obj-$(CONFIG_REGULATOR_LM363X) += lm363x-regulator.o +obj-$(CONFIG_REGULATOR_LOCHNAGAR) += lochnagar-regulator.o obj-$(CONFIG_REGULATOR_LP3971) += lp3971.o obj-$(CONFIG_REGULATOR_LP3972) += lp3972.o obj-$(CONFIG_REGULATOR_LP872X) += lp872x.o @@ -101,6 +102,7 @@ obj-$(CONFIG_REGULATOR_S5M8767) += s5m8767.o obj-$(CONFIG_REGULATOR_SC2731) += sc2731-regulator.o obj-$(CONFIG_REGULATOR_SKY81452) += sky81452-regulator.o obj-$(CONFIG_REGULATOR_STM32_VREFBUF) += stm32-vrefbuf.o +obj-$(CONFIG_REGULATOR_STPMIC1) += stpmic1_regulator.o obj-$(CONFIG_REGULATOR_STW481X_VMMC) += stw481x-vmmc.o obj-$(CONFIG_REGULATOR_SY8106A) += sy8106a-regulator.o obj-$(CONFIG_REGULATOR_TI_ABB) += ti-abb-regulator.o diff --git a/drivers/regulator/arizona-ldo1.c b/drivers/regulator/arizona-ldo1.c index e976d073f28d..9a72eae4926d 100644 --- a/drivers/regulator/arizona-ldo1.c +++ b/drivers/regulator/arizona-ldo1.c @@ -260,7 +260,7 @@ static int arizona_ldo1_common_init(struct platform_device *pdev, * so clean up would happen at the wrong time */ config.ena_gpiod = gpiod_get_optional(parent_dev, "wlf,ldoena", - GPIOD_OUT_LOW); + GPIOD_OUT_LOW | GPIOD_FLAGS_BIT_NONEXCLUSIVE); if (IS_ERR(config.ena_gpiod)) return PTR_ERR(config.ena_gpiod); diff --git a/drivers/regulator/axp20x-regulator.c b/drivers/regulator/axp20x-regulator.c index 91b8ff8bac15..a3734039a86a 100644 --- a/drivers/regulator/axp20x-regulator.c +++ b/drivers/regulator/axp20x-regulator.c @@ -509,10 +509,10 @@ static int axp20x_set_dcdc_freq(struct platform_device *pdev, u32 dcdcfreq) /* * AXP803/AXP813 DCDC work frequency setting has the same * range and step as AXP22X, but at a different register. - * Fall through to the check below. * (See include/linux/mfd/axp20x.h) */ reg = AXP803_DCDC_FREQ_CTRL; + /* Fall through to the check below.*/ case AXP806_ID: /* * AXP806 also have DCDC work frequency setting register at a @@ -520,6 +520,7 @@ static int axp20x_set_dcdc_freq(struct platform_device *pdev, u32 dcdcfreq) */ if (axp20x->variant == AXP806_ID) reg = AXP806_DCDC_FREQ_CTRL; + /* Fall through */ case AXP221_ID: case AXP223_ID: case AXP809_ID: diff --git a/drivers/regulator/bd718x7-regulator.c b/drivers/regulator/bd718x7-regulator.c index d2522d4e1505..3a47e0372e77 100644 --- a/drivers/regulator/bd718x7-regulator.c +++ b/drivers/regulator/bd718x7-regulator.c @@ -15,13 +15,6 @@ #include <linux/regulator/of_regulator.h> #include <linux/slab.h> -struct bd718xx_pmic { - struct bd718xx_regulator_data *rdata; - struct bd718xx *mfd; - struct platform_device *pdev; - struct regulator_dev *rdev[BD718XX_REGULATOR_AMOUNT]; -}; - /* * BUCK1/2/3/4 * BUCK1RAMPRATE[1:0] BUCK1 DVS ramp rate setting @@ -33,12 +26,10 @@ struct bd718xx_pmic { static int bd718xx_buck1234_set_ramp_delay(struct regulator_dev *rdev, int ramp_delay) { - struct bd718xx_pmic *pmic = rdev_get_drvdata(rdev); - struct bd718xx *mfd = pmic->mfd; int id = rdev->desc->id; unsigned int ramp_value = BUCK_RAMPRATE_10P00MV; - dev_dbg(&pmic->pdev->dev, "Buck[%d] Set Ramp = %d\n", id + 1, + dev_dbg(&rdev->dev, "Buck[%d] Set Ramp = %d\n", id + 1, ramp_delay); switch (ramp_delay) { case 1 ... 1250: @@ -55,12 +46,12 @@ static int bd718xx_buck1234_set_ramp_delay(struct regulator_dev *rdev, break; default: ramp_value = BUCK_RAMPRATE_10P00MV; - dev_err(&pmic->pdev->dev, + dev_err(&rdev->dev, "%s: ramp_delay: %d not supported, setting 10000mV//us\n", rdev->desc->name, ramp_delay); } - return regmap_update_bits(mfd->regmap, BD718XX_REG_BUCK1_CTRL + id, + return regmap_update_bits(rdev->regmap, BD718XX_REG_BUCK1_CTRL + id, BUCK_RAMPRATE_MASK, ramp_value << 6); } @@ -1022,7 +1013,7 @@ struct bd718xx_pmic_inits { static int bd718xx_probe(struct platform_device *pdev) { - struct bd718xx_pmic *pmic; + struct bd718xx *mfd; struct regulator_config config = { 0 }; struct bd718xx_pmic_inits pmic_regulators[] = { [BD718XX_TYPE_BD71837] = { @@ -1037,54 +1028,46 @@ static int bd718xx_probe(struct platform_device *pdev) int i, j, err; - pmic = devm_kzalloc(&pdev->dev, sizeof(*pmic), GFP_KERNEL); - if (!pmic) - return -ENOMEM; - - pmic->pdev = pdev; - pmic->mfd = dev_get_drvdata(pdev->dev.parent); - - if (!pmic->mfd) { + mfd = dev_get_drvdata(pdev->dev.parent); + if (!mfd) { dev_err(&pdev->dev, "No MFD driver data\n"); err = -EINVAL; goto err; } - if (pmic->mfd->chip_type >= BD718XX_TYPE_AMOUNT || - !pmic_regulators[pmic->mfd->chip_type].r_datas) { + + if (mfd->chip_type >= BD718XX_TYPE_AMOUNT || + !pmic_regulators[mfd->chip_type].r_datas) { dev_err(&pdev->dev, "Unsupported chip type\n"); err = -EINVAL; goto err; } - platform_set_drvdata(pdev, pmic); - /* Register LOCK release */ - err = regmap_update_bits(pmic->mfd->regmap, BD718XX_REG_REGLOCK, + err = regmap_update_bits(mfd->regmap, BD718XX_REG_REGLOCK, (REGLOCK_PWRSEQ | REGLOCK_VREG), 0); if (err) { - dev_err(&pmic->pdev->dev, "Failed to unlock PMIC (%d)\n", err); + dev_err(&pdev->dev, "Failed to unlock PMIC (%d)\n", err); goto err; } else { - dev_dbg(&pmic->pdev->dev, "Unlocked lock register 0x%x\n", + dev_dbg(&pdev->dev, "Unlocked lock register 0x%x\n", BD718XX_REG_REGLOCK); } - for (i = 0; i < pmic_regulators[pmic->mfd->chip_type].r_amount; i++) { + for (i = 0; i < pmic_regulators[mfd->chip_type].r_amount; i++) { const struct regulator_desc *desc; struct regulator_dev *rdev; const struct bd718xx_regulator_data *r; - r = &(*pmic_regulators[pmic->mfd->chip_type].r_datas)[i]; + r = &(*pmic_regulators[mfd->chip_type].r_datas)[i]; desc = &r->desc; config.dev = pdev->dev.parent; - config.driver_data = pmic; - config.regmap = pmic->mfd->regmap; + config.regmap = mfd->regmap; rdev = devm_regulator_register(&pdev->dev, desc, &config); if (IS_ERR(rdev)) { - dev_err(pmic->mfd->dev, + dev_err(&pdev->dev, "failed to register %s regulator\n", desc->name); err = PTR_ERR(rdev); @@ -1096,28 +1079,26 @@ static int bd718xx_probe(struct platform_device *pdev) * can now switch the control from PMIC state machine to the * register interface */ - err = regmap_update_bits(pmic->mfd->regmap, r->init.reg, + err = regmap_update_bits(mfd->regmap, r->init.reg, r->init.mask, r->init.val); if (err) { - dev_err(&pmic->pdev->dev, + dev_err(&pdev->dev, "Failed to write BUCK/LDO SEL bit for (%s)\n", desc->name); goto err; } for (j = 0; j < r->additional_init_amnt; j++) { - err = regmap_update_bits(pmic->mfd->regmap, + err = regmap_update_bits(mfd->regmap, r->additional_inits[j].reg, r->additional_inits[j].mask, r->additional_inits[j].val); if (err) { - dev_err(&pmic->pdev->dev, + dev_err(&pdev->dev, "Buck (%s) initialization failed\n", desc->name); goto err; } } - - pmic->rdev[i] = rdev; } err: diff --git a/drivers/regulator/core.c b/drivers/regulator/core.c index 4b1755a8ef00..2c66b528aede 100644 --- a/drivers/regulator/core.c +++ b/drivers/regulator/core.c @@ -426,19 +426,24 @@ static ssize_t name_show(struct device *dev, struct device_attribute *attr, } static DEVICE_ATTR_RO(name); -static ssize_t regulator_print_opmode(char *buf, int mode) +static const char *regulator_opmode_to_str(int mode) { switch (mode) { case REGULATOR_MODE_FAST: - return sprintf(buf, "fast\n"); + return "fast"; case REGULATOR_MODE_NORMAL: - return sprintf(buf, "normal\n"); + return "normal"; case REGULATOR_MODE_IDLE: - return sprintf(buf, "idle\n"); + return "idle"; case REGULATOR_MODE_STANDBY: - return sprintf(buf, "standby\n"); + return "standby"; } - return sprintf(buf, "unknown\n"); + return "unknown"; +} + +static ssize_t regulator_print_opmode(char *buf, int mode) +{ + return sprintf(buf, "%s\n", regulator_opmode_to_str(mode)); } static ssize_t regulator_opmode_show(struct device *dev, @@ -3475,21 +3480,23 @@ out: } EXPORT_SYMBOL_GPL(regulator_set_current_limit); +static int _regulator_get_current_limit_unlocked(struct regulator_dev *rdev) +{ + /* sanity check */ + if (!rdev->desc->ops->get_current_limit) + return -EINVAL; + + return rdev->desc->ops->get_current_limit(rdev); +} + static int _regulator_get_current_limit(struct regulator_dev *rdev) { int ret; regulator_lock(rdev); - - /* sanity check */ - if (!rdev->desc->ops->get_current_limit) { - ret = -EINVAL; - goto out; - } - - ret = rdev->desc->ops->get_current_limit(rdev); -out: + ret = _regulator_get_current_limit_unlocked(rdev); regulator_unlock(rdev); + return ret; } @@ -3554,21 +3561,23 @@ out: } EXPORT_SYMBOL_GPL(regulator_set_mode); +static unsigned int _regulator_get_mode_unlocked(struct regulator_dev *rdev) +{ + /* sanity check */ + if (!rdev->desc->ops->get_mode) + return -EINVAL; + + return rdev->desc->ops->get_mode(rdev); +} + static unsigned int _regulator_get_mode(struct regulator_dev *rdev) { int ret; regulator_lock(rdev); - - /* sanity check */ - if (!rdev->desc->ops->get_mode) { - ret = -EINVAL; - goto out; - } - - ret = rdev->desc->ops->get_mode(rdev); -out: + ret = _regulator_get_mode_unlocked(rdev); regulator_unlock(rdev); + return ret; } @@ -4460,41 +4469,33 @@ void regulator_unregister(struct regulator_dev *rdev) EXPORT_SYMBOL_GPL(regulator_unregister); #ifdef CONFIG_SUSPEND -static int _regulator_suspend(struct device *dev, void *data) -{ - struct regulator_dev *rdev = dev_to_rdev(dev); - suspend_state_t *state = data; - int ret; - - regulator_lock(rdev); - ret = suspend_set_state(rdev, *state); - regulator_unlock(rdev); - - return ret; -} - /** * regulator_suspend - prepare regulators for system wide suspend - * @state: system suspend state + * @dev: ``&struct device`` pointer that is passed to _regulator_suspend() * * Configure each regulator with it's suspend operating parameters for state. */ static int regulator_suspend(struct device *dev) { + struct regulator_dev *rdev = dev_to_rdev(dev); suspend_state_t state = pm_suspend_target_state; + int ret; + + regulator_lock(rdev); + ret = suspend_set_state(rdev, state); + regulator_unlock(rdev); - return class_for_each_device(®ulator_class, NULL, &state, - _regulator_suspend); + return ret; } -static int _regulator_resume(struct device *dev, void *data) +static int regulator_resume(struct device *dev) { - int ret = 0; + suspend_state_t state = pm_suspend_target_state; struct regulator_dev *rdev = dev_to_rdev(dev); - suspend_state_t *state = data; struct regulator_state *rstate; + int ret = 0; - rstate = regulator_get_suspend_state(rdev, *state); + rstate = regulator_get_suspend_state(rdev, state); if (rstate == NULL) return 0; @@ -4509,15 +4510,6 @@ static int _regulator_resume(struct device *dev, void *data) return ret; } - -static int regulator_resume(struct device *dev) -{ - suspend_state_t state = pm_suspend_target_state; - - return class_for_each_device(®ulator_class, NULL, &state, - _regulator_resume); -} - #else /* !CONFIG_SUSPEND */ #define regulator_suspend NULL @@ -4675,17 +4667,23 @@ static void regulator_summary_show_subtree(struct seq_file *s, struct regulation_constraints *c; struct regulator *consumer; struct summary_data summary_data; + unsigned int opmode; if (!rdev) return; - seq_printf(s, "%*s%-*s %3d %4d %6d ", + regulator_lock_nested(rdev, level); + + opmode = _regulator_get_mode_unlocked(rdev); + seq_printf(s, "%*s%-*s %3d %4d %6d %7s ", level * 3 + 1, "", 30 - level * 3, rdev_get_name(rdev), - rdev->use_count, rdev->open_count, rdev->bypass_count); + rdev->use_count, rdev->open_count, rdev->bypass_count, + regulator_opmode_to_str(opmode)); seq_printf(s, "%5dmV ", _regulator_get_voltage(rdev) / 1000); - seq_printf(s, "%5dmA ", _regulator_get_current_limit(rdev) / 1000); + seq_printf(s, "%5dmA ", + _regulator_get_current_limit_unlocked(rdev) / 1000); c = rdev->constraints; if (c) { @@ -4714,7 +4712,8 @@ static void regulator_summary_show_subtree(struct seq_file *s, switch (rdev->desc->type) { case REGULATOR_VOLTAGE: - seq_printf(s, "%37dmV %5dmV", + seq_printf(s, "%37dmA %5dmV %5dmV", + consumer->uA_load / 1000, consumer->voltage[PM_SUSPEND_ON].min_uV / 1000, consumer->voltage[PM_SUSPEND_ON].max_uV / 1000); break; @@ -4731,6 +4730,8 @@ static void regulator_summary_show_subtree(struct seq_file *s, class_for_each_device(®ulator_class, NULL, &summary_data, regulator_summary_show_children); + + regulator_unlock(rdev); } static int regulator_summary_show_roots(struct device *dev, void *data) @@ -4746,8 +4747,8 @@ static int regulator_summary_show_roots(struct device *dev, void *data) static int regulator_summary_show(struct seq_file *s, void *data) { - seq_puts(s, " regulator use open bypass voltage current min max\n"); - seq_puts(s, "-------------------------------------------------------------------------------\n"); + seq_puts(s, " regulator use open bypass opmode voltage current min max\n"); + seq_puts(s, "---------------------------------------------------------------------------------------\n"); class_for_each_device(®ulator_class, NULL, s, regulator_summary_show_roots); diff --git a/drivers/regulator/da9052-regulator.c b/drivers/regulator/da9052-regulator.c index 9ececfef42d6..37e4025203e3 100644 --- a/drivers/regulator/da9052-regulator.c +++ b/drivers/regulator/da9052-regulator.c @@ -420,7 +420,7 @@ static int da9052_regulator_probe(struct platform_device *pdev) config.dev = &pdev->dev; config.driver_data = regulator; config.regmap = da9052->regmap; - if (pdata && pdata->regulators) { + if (pdata) { config.init_data = pdata->regulators[cell->id]; } else { #ifdef CONFIG_OF diff --git a/drivers/regulator/da9055-regulator.c b/drivers/regulator/da9055-regulator.c index f40c3b8644ae..588c3d2445cf 100644 --- a/drivers/regulator/da9055-regulator.c +++ b/drivers/regulator/da9055-regulator.c @@ -612,7 +612,7 @@ static int da9055_regulator_probe(struct platform_device *pdev) config.driver_data = regulator; config.regmap = da9055->regmap; - if (pdata && pdata->regulators) { + if (pdata) { config.init_data = pdata->regulators[pdev->id]; } else { ret = da9055_regulator_dt_init(pdev, regulator, &config, diff --git a/drivers/regulator/da9211-regulator.c b/drivers/regulator/da9211-regulator.c index 6c122b3df5d0..8f68c7a05d27 100644 --- a/drivers/regulator/da9211-regulator.c +++ b/drivers/regulator/da9211-regulator.c @@ -294,11 +294,11 @@ static struct da9211_pdata *da9211_parse_regulators_dt( pdata->init_data[n] = da9211_matches[i].init_data; pdata->reg_node[n] = da9211_matches[i].of_node; pdata->gpiod_ren[n] = devm_gpiod_get_from_of_node(dev, - da9211_matches[i].of_node, - "enable", - 0, - GPIOD_OUT_HIGH, - "da9211-enable"); + da9211_matches[i].of_node, + "enable", + 0, + GPIOD_OUT_HIGH | GPIOD_FLAGS_BIT_NONEXCLUSIVE, + "da9211-enable"); n++; } diff --git a/drivers/regulator/fixed-helper.c b/drivers/regulator/fixed-helper.c index 777fac6fb4cb..2c6098e6f4bc 100644 --- a/drivers/regulator/fixed-helper.c +++ b/drivers/regulator/fixed-helper.c @@ -43,7 +43,6 @@ struct platform_device *regulator_register_always_on(int id, const char *name, } data->cfg.microvolts = uv; - data->cfg.gpio = -EINVAL; data->cfg.enabled_at_boot = 1; data->cfg.init_data = &data->init_data; diff --git a/drivers/regulator/fixed.c b/drivers/regulator/fixed.c index 988a7472c2ab..ccc29038f19a 100644 --- a/drivers/regulator/fixed.c +++ b/drivers/regulator/fixed.c @@ -24,10 +24,9 @@ #include <linux/platform_device.h> #include <linux/regulator/driver.h> #include <linux/regulator/fixed.h> -#include <linux/gpio.h> +#include <linux/gpio/consumer.h> #include <linux/slab.h> #include <linux/of.h> -#include <linux/of_gpio.h> #include <linux/regulator/of_regulator.h> #include <linux/regulator/machine.h> @@ -78,15 +77,16 @@ of_get_fixed_voltage_config(struct device *dev, if (init_data->constraints.boot_on) config->enabled_at_boot = true; - config->gpio = of_get_named_gpio(np, "gpio", 0); - if ((config->gpio < 0) && (config->gpio != -ENOENT)) - return ERR_PTR(config->gpio); - of_property_read_u32(np, "startup-delay-us", &config->startup_delay); - config->enable_high = of_property_read_bool(np, "enable-active-high"); - config->gpio_is_open_drain = of_property_read_bool(np, - "gpio-open-drain"); + /* + * FIXME: we pulled active low/high and open drain handling into + * gpiolib so it will be handled there. Delete this in the second + * step when we also remove the custom inversion handling for all + * legacy boardfiles. + */ + config->enable_high = 1; + config->gpio_is_open_drain = 0; if (of_find_property(np, "vin-supply", NULL)) config->input_supply = "vin"; @@ -102,6 +102,7 @@ static int reg_fixed_voltage_probe(struct platform_device *pdev) struct fixed_voltage_config *config; struct fixed_voltage_data *drvdata; struct regulator_config cfg = { }; + enum gpiod_flags gflags; int ret; drvdata = devm_kzalloc(&pdev->dev, sizeof(struct fixed_voltage_data), @@ -150,25 +151,41 @@ static int reg_fixed_voltage_probe(struct platform_device *pdev) drvdata->desc.fixed_uV = config->microvolts; - if (gpio_is_valid(config->gpio)) { - cfg.ena_gpio = config->gpio; - if (pdev->dev.of_node) - cfg.ena_gpio_initialized = true; - } cfg.ena_gpio_invert = !config->enable_high; if (config->enabled_at_boot) { if (config->enable_high) - cfg.ena_gpio_flags |= GPIOF_OUT_INIT_HIGH; + gflags = GPIOD_OUT_HIGH; else - cfg.ena_gpio_flags |= GPIOF_OUT_INIT_LOW; + gflags = GPIOD_OUT_LOW; } else { if (config->enable_high) - cfg.ena_gpio_flags |= GPIOF_OUT_INIT_LOW; + gflags = GPIOD_OUT_LOW; else - cfg.ena_gpio_flags |= GPIOF_OUT_INIT_HIGH; + gflags = GPIOD_OUT_HIGH; } - if (config->gpio_is_open_drain) - cfg.ena_gpio_flags |= GPIOF_OPEN_DRAIN; + if (config->gpio_is_open_drain) { + if (gflags == GPIOD_OUT_HIGH) + gflags = GPIOD_OUT_HIGH_OPEN_DRAIN; + else + gflags = GPIOD_OUT_LOW_OPEN_DRAIN; + } + + /* + * Some fixed regulators share the enable line between two + * regulators which makes it necessary to get a handle on the + * same descriptor for two different consumers. This will get + * the GPIO descriptor, but only the first call will initialize + * it so any flags such as inversion or open drain will only + * be set up by the first caller and assumed identical on the + * next caller. + * + * FIXME: find a better way to deal with this. + */ + gflags |= GPIOD_FLAGS_BIT_NONEXCLUSIVE; + + cfg.ena_gpiod = devm_gpiod_get_optional(&pdev->dev, NULL, gflags); + if (IS_ERR(cfg.ena_gpiod)) + return PTR_ERR(cfg.ena_gpiod); cfg.dev = &pdev->dev; cfg.init_data = config->init_data; diff --git a/drivers/regulator/helpers.c b/drivers/regulator/helpers.c index d2b9fc359318..5686a1335bd3 100644 --- a/drivers/regulator/helpers.c +++ b/drivers/regulator/helpers.c @@ -443,17 +443,18 @@ int regulator_map_voltage_linear_range(struct regulator_dev *rdev, ret += range->min_sel; - break; + /* + * Map back into a voltage to verify we're still in bounds. + * If we are not, then continue checking rest of the ranges. + */ + voltage = rdev->desc->ops->list_voltage(rdev, ret); + if (voltage >= min_uV && voltage <= max_uV) + break; } if (i == rdev->desc->n_linear_ranges) return -EINVAL; - /* Map back into a voltage to verify we're still in bounds */ - voltage = rdev->desc->ops->list_voltage(rdev, ret); - if (voltage < min_uV || voltage > max_uV) - return -EINVAL; - return ret; } EXPORT_SYMBOL_GPL(regulator_map_voltage_linear_range); diff --git a/drivers/regulator/lm363x-regulator.c b/drivers/regulator/lm363x-regulator.c index b615a413ca9f..bbedb08d257b 100644 --- a/drivers/regulator/lm363x-regulator.c +++ b/drivers/regulator/lm363x-regulator.c @@ -227,9 +227,11 @@ static struct gpio_desc *lm363x_regulator_of_get_enable_gpio(struct device *dev, */ switch (id) { case LM3632_LDO_POS: - return devm_gpiod_get_index_optional(dev, "enable", 0, GPIOD_OUT_LOW); + return devm_gpiod_get_index_optional(dev, "enable", 0, + GPIOD_OUT_LOW | GPIOD_FLAGS_BIT_NONEXCLUSIVE); case LM3632_LDO_NEG: - return devm_gpiod_get_index_optional(dev, "enable", 1, GPIOD_OUT_LOW); + return devm_gpiod_get_index_optional(dev, "enable", 1, + GPIOD_OUT_LOW | GPIOD_FLAGS_BIT_NONEXCLUSIVE); default: return NULL; } diff --git a/drivers/regulator/lochnagar-regulator.c b/drivers/regulator/lochnagar-regulator.c new file mode 100644 index 000000000000..2b5073b9ff86 --- /dev/null +++ b/drivers/regulator/lochnagar-regulator.c @@ -0,0 +1,254 @@ +// SPDX-License-Identifier: GPL-2.0 +// +// Lochnagar regulator driver +// +// Copyright (c) 2017-2018 Cirrus Logic, Inc. and +// Cirrus Logic International Semiconductor Ltd. +// +// Author: Charles Keepax <ckeepax@opensource.cirrus.com> + +#include <linux/bitops.h> +#include <linux/device.h> +#include <linux/err.h> +#include <linux/module.h> +#include <linux/mutex.h> +#include <linux/of.h> +#include <linux/platform_device.h> +#include <linux/regmap.h> +#include <linux/regulator/driver.h> +#include <linux/regulator/machine.h> +#include <linux/regulator/of_regulator.h> + +#include <linux/mfd/lochnagar.h> + +static const struct regulator_ops lochnagar_micvdd_ops = { + .enable = regulator_enable_regmap, + .disable = regulator_disable_regmap, + .is_enabled = regulator_is_enabled_regmap, + + .list_voltage = regulator_list_voltage_linear_range, + .map_voltage = regulator_map_voltage_linear_range, + + .get_voltage_sel = regulator_get_voltage_sel_regmap, + .set_voltage_sel = regulator_set_voltage_sel_regmap, +}; + +static const struct regulator_linear_range lochnagar_micvdd_ranges[] = { + REGULATOR_LINEAR_RANGE(1000000, 0, 0xC, 50000), + REGULATOR_LINEAR_RANGE(1700000, 0xD, 0x1F, 100000), +}; + +static int lochnagar_micbias_enable(struct regulator_dev *rdev) +{ + struct lochnagar *lochnagar = rdev_get_drvdata(rdev); + int ret; + + mutex_lock(&lochnagar->analogue_config_lock); + + ret = regulator_enable_regmap(rdev); + if (ret < 0) + goto err; + + ret = lochnagar_update_config(lochnagar); + +err: + mutex_unlock(&lochnagar->analogue_config_lock); + + return ret; +} + +static int lochnagar_micbias_disable(struct regulator_dev *rdev) +{ + struct lochnagar *lochnagar = rdev_get_drvdata(rdev); + int ret; + + mutex_lock(&lochnagar->analogue_config_lock); + + ret = regulator_disable_regmap(rdev); + if (ret < 0) + goto err; + + ret = lochnagar_update_config(lochnagar); + +err: + mutex_unlock(&lochnagar->analogue_config_lock); + + return ret; +} + +static const struct regulator_ops lochnagar_micbias_ops = { + .enable = lochnagar_micbias_enable, + .disable = lochnagar_micbias_disable, + .is_enabled = regulator_is_enabled_regmap, +}; + +static const struct regulator_ops lochnagar_vddcore_ops = { + .enable = regulator_enable_regmap, + .disable = regulator_disable_regmap, + .is_enabled = regulator_is_enabled_regmap, + + .list_voltage = regulator_list_voltage_linear_range, + .map_voltage = regulator_map_voltage_linear_range, + + .get_voltage_sel = regulator_get_voltage_sel_regmap, + .set_voltage_sel = regulator_set_voltage_sel_regmap, +}; + +static const struct regulator_linear_range lochnagar_vddcore_ranges[] = { + REGULATOR_LINEAR_RANGE(600000, 0x8, 0x41, 12500), +}; + +enum lochnagar_regulators { + LOCHNAGAR_MICVDD, + LOCHNAGAR_MIC1VDD, + LOCHNAGAR_MIC2VDD, + LOCHNAGAR_VDDCORE, +}; + +static int lochnagar_micbias_of_parse(struct device_node *np, + const struct regulator_desc *desc, + struct regulator_config *config) +{ + struct lochnagar *lochnagar = config->driver_data; + int shift = (desc->id - LOCHNAGAR_MIC1VDD) * + LOCHNAGAR2_P2_MICBIAS_SRC_SHIFT; + int mask = LOCHNAGAR2_P1_MICBIAS_SRC_MASK << shift; + unsigned int val; + int ret; + + ret = of_property_read_u32(np, "cirrus,micbias-input", &val); + if (ret >= 0) { + mutex_lock(&lochnagar->analogue_config_lock); + ret = regmap_update_bits(lochnagar->regmap, + LOCHNAGAR2_ANALOGUE_PATH_CTRL2, + mask, val << shift); + mutex_unlock(&lochnagar->analogue_config_lock); + if (ret < 0) { + dev_err(lochnagar->dev, + "Failed to update micbias source: %d\n", ret); + return ret; + } + } + + return 0; +} + +static const struct regulator_desc lochnagar_regulators[] = { + [LOCHNAGAR_MICVDD] = { + .name = "MICVDD", + .supply_name = "SYSVDD", + .type = REGULATOR_VOLTAGE, + .n_voltages = 32, + .ops = &lochnagar_micvdd_ops, + + .id = LOCHNAGAR_MICVDD, + .of_match = of_match_ptr("MICVDD"), + + .enable_reg = LOCHNAGAR2_MICVDD_CTRL1, + .enable_mask = LOCHNAGAR2_MICVDD_REG_ENA_MASK, + .vsel_reg = LOCHNAGAR2_MICVDD_CTRL2, + .vsel_mask = LOCHNAGAR2_MICVDD_VSEL_MASK, + + .linear_ranges = lochnagar_micvdd_ranges, + .n_linear_ranges = ARRAY_SIZE(lochnagar_micvdd_ranges), + + .enable_time = 3000, + .ramp_delay = 1000, + + .owner = THIS_MODULE, + }, + [LOCHNAGAR_MIC1VDD] = { + .name = "MIC1VDD", + .supply_name = "MICBIAS1", + .type = REGULATOR_VOLTAGE, + .ops = &lochnagar_micbias_ops, + + .id = LOCHNAGAR_MIC1VDD, + .of_match = of_match_ptr("MIC1VDD"), + .of_parse_cb = lochnagar_micbias_of_parse, + + .enable_reg = LOCHNAGAR2_ANALOGUE_PATH_CTRL2, + .enable_mask = LOCHNAGAR2_P1_INPUT_BIAS_ENA_MASK, + + .owner = THIS_MODULE, + }, + [LOCHNAGAR_MIC2VDD] = { + .name = "MIC2VDD", + .supply_name = "MICBIAS2", + .type = REGULATOR_VOLTAGE, + .ops = &lochnagar_micbias_ops, + + .id = LOCHNAGAR_MIC2VDD, + .of_match = of_match_ptr("MIC2VDD"), + .of_parse_cb = lochnagar_micbias_of_parse, + + .enable_reg = LOCHNAGAR2_ANALOGUE_PATH_CTRL2, + .enable_mask = LOCHNAGAR2_P2_INPUT_BIAS_ENA_MASK, + + .owner = THIS_MODULE, + }, + [LOCHNAGAR_VDDCORE] = { + .name = "VDDCORE", + .supply_name = "SYSVDD", + .type = REGULATOR_VOLTAGE, + .n_voltages = 57, + .ops = &lochnagar_vddcore_ops, + + .id = LOCHNAGAR_VDDCORE, + .of_match = of_match_ptr("VDDCORE"), + + .enable_reg = LOCHNAGAR2_VDDCORE_CDC_CTRL1, + .enable_mask = LOCHNAGAR2_VDDCORE_CDC_REG_ENA_MASK, + .vsel_reg = LOCHNAGAR2_VDDCORE_CDC_CTRL2, + .vsel_mask = LOCHNAGAR2_VDDCORE_CDC_VSEL_MASK, + + .linear_ranges = lochnagar_vddcore_ranges, + .n_linear_ranges = ARRAY_SIZE(lochnagar_vddcore_ranges), + + .enable_time = 3000, + .ramp_delay = 1000, + + .owner = THIS_MODULE, + }, +}; + +static int lochnagar_regulator_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct lochnagar *lochnagar = dev_get_drvdata(dev->parent); + struct regulator_config config = { }; + struct regulator_dev *rdev; + int ret, i; + + config.dev = lochnagar->dev; + config.regmap = lochnagar->regmap; + config.driver_data = lochnagar; + + for (i = 0; i < ARRAY_SIZE(lochnagar_regulators); i++) { + const struct regulator_desc *desc = &lochnagar_regulators[i]; + + rdev = devm_regulator_register(dev, desc, &config); + if (IS_ERR(rdev)) { + ret = PTR_ERR(rdev); + dev_err(dev, "Failed to register %s regulator: %d\n", + desc->name, ret); + return ret; + } + } + + return 0; +} + +static struct platform_driver lochnagar_regulator_driver = { + .driver = { + .name = "lochnagar-regulator", + }, + + .probe = lochnagar_regulator_probe, +}; +module_platform_driver(lochnagar_regulator_driver); + +MODULE_AUTHOR("Charles Keepax <ckeepax@opensource.cirrus.com>"); +MODULE_DESCRIPTION("Regulator driver for Cirrus Logic Lochnagar Board"); +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("platform:lochnagar-regulator"); diff --git a/drivers/regulator/lp8788-ldo.c b/drivers/regulator/lp8788-ldo.c index f2347474a106..553b4790050f 100644 --- a/drivers/regulator/lp8788-ldo.c +++ b/drivers/regulator/lp8788-ldo.c @@ -503,9 +503,10 @@ static int lp8788_config_ldo_enable_mode(struct platform_device *pdev, /* FIXME: check default mode for GPIO here: high or low? */ ldo->ena_gpiod = devm_gpiod_get_index_optional(&pdev->dev, - "enable", - enable_id, - GPIOD_OUT_HIGH); + "enable", + enable_id, + GPIOD_OUT_HIGH | + GPIOD_FLAGS_BIT_NONEXCLUSIVE); if (IS_ERR(ldo->ena_gpiod)) return PTR_ERR(ldo->ena_gpiod); diff --git a/drivers/regulator/max8952.c b/drivers/regulator/max8952.c index f1e77ed5dfec..6c39fff73b8a 100644 --- a/drivers/regulator/max8952.c +++ b/drivers/regulator/max8952.c @@ -230,6 +230,7 @@ static int max8952_pmic_probe(struct i2c_client *client, gflags = GPIOD_OUT_HIGH; else gflags = GPIOD_OUT_LOW; + gflags |= GPIOD_FLAGS_BIT_NONEXCLUSIVE; gpiod = devm_gpiod_get_optional(&client->dev, "max8952,en", gflags); diff --git a/drivers/regulator/max8973-regulator.c b/drivers/regulator/max8973-regulator.c index 7cd493ec6315..e7a58b509032 100644 --- a/drivers/regulator/max8973-regulator.c +++ b/drivers/regulator/max8973-regulator.c @@ -758,6 +758,7 @@ static int max8973_probe(struct i2c_client *client, gflags = GPIOD_OUT_HIGH; else gflags = GPIOD_OUT_LOW; + gflags |= GPIOD_FLAGS_BIT_NONEXCLUSIVE; gpiod = devm_gpiod_get_optional(&client->dev, "maxim,enable", gflags); diff --git a/drivers/regulator/pfuze100-regulator.c b/drivers/regulator/pfuze100-regulator.c index 31c3a236120a..dd41a9bb3f5c 100644 --- a/drivers/regulator/pfuze100-regulator.c +++ b/drivers/regulator/pfuze100-regulator.c @@ -31,11 +31,17 @@ #define PFUZE100_COINVOL 0x1a #define PFUZE100_SW1ABVOL 0x20 +#define PFUZE100_SW1ABMODE 0x23 #define PFUZE100_SW1CVOL 0x2e +#define PFUZE100_SW1CMODE 0x31 #define PFUZE100_SW2VOL 0x35 +#define PFUZE100_SW2MODE 0x38 #define PFUZE100_SW3AVOL 0x3c +#define PFUZE100_SW3AMODE 0x3f #define PFUZE100_SW3BVOL 0x43 +#define PFUZE100_SW3BMODE 0x46 #define PFUZE100_SW4VOL 0x4a +#define PFUZE100_SW4MODE 0x4d #define PFUZE100_SWBSTCON1 0x66 #define PFUZE100_VREFDDRCON 0x6a #define PFUZE100_VSNVSVOL 0x6b @@ -46,6 +52,13 @@ #define PFUZE100_VGEN5VOL 0x70 #define PFUZE100_VGEN6VOL 0x71 +#define PFUZE100_SWxMODE_MASK 0xf +#define PFUZE100_SWxMODE_APS_APS 0x8 +#define PFUZE100_SWxMODE_APS_OFF 0x4 + +#define PFUZE100_VGENxLPWR BIT(6) +#define PFUZE100_VGENxSTBY BIT(5) + enum chips { PFUZE100, PFUZE200, PFUZE3000 = 3, PFUZE3001 = 0x31, }; struct pfuze_regulator { @@ -559,6 +572,69 @@ static inline struct device_node *match_of_node(int index) } #endif +static struct pfuze_chip *syspm_pfuze_chip; + +static void pfuze_power_off_prepare(void) +{ + dev_info(syspm_pfuze_chip->dev, "Configure standby mode for power off"); + + /* Switch from default mode: APS/APS to APS/Off */ + regmap_update_bits(syspm_pfuze_chip->regmap, PFUZE100_SW1ABMODE, + PFUZE100_SWxMODE_MASK, PFUZE100_SWxMODE_APS_OFF); + regmap_update_bits(syspm_pfuze_chip->regmap, PFUZE100_SW1CMODE, + PFUZE100_SWxMODE_MASK, PFUZE100_SWxMODE_APS_OFF); + regmap_update_bits(syspm_pfuze_chip->regmap, PFUZE100_SW2MODE, + PFUZE100_SWxMODE_MASK, PFUZE100_SWxMODE_APS_OFF); + regmap_update_bits(syspm_pfuze_chip->regmap, PFUZE100_SW3AMODE, + PFUZE100_SWxMODE_MASK, PFUZE100_SWxMODE_APS_OFF); + regmap_update_bits(syspm_pfuze_chip->regmap, PFUZE100_SW3BMODE, + PFUZE100_SWxMODE_MASK, PFUZE100_SWxMODE_APS_OFF); + regmap_update_bits(syspm_pfuze_chip->regmap, PFUZE100_SW4MODE, + PFUZE100_SWxMODE_MASK, PFUZE100_SWxMODE_APS_OFF); + + regmap_update_bits(syspm_pfuze_chip->regmap, PFUZE100_VGEN1VOL, + PFUZE100_VGENxLPWR | PFUZE100_VGENxSTBY, + PFUZE100_VGENxSTBY); + regmap_update_bits(syspm_pfuze_chip->regmap, PFUZE100_VGEN2VOL, + PFUZE100_VGENxLPWR | PFUZE100_VGENxSTBY, + PFUZE100_VGENxSTBY); + regmap_update_bits(syspm_pfuze_chip->regmap, PFUZE100_VGEN3VOL, + PFUZE100_VGENxLPWR | PFUZE100_VGENxSTBY, + PFUZE100_VGENxSTBY); + regmap_update_bits(syspm_pfuze_chip->regmap, PFUZE100_VGEN4VOL, + PFUZE100_VGENxLPWR | PFUZE100_VGENxSTBY, + PFUZE100_VGENxSTBY); + regmap_update_bits(syspm_pfuze_chip->regmap, PFUZE100_VGEN5VOL, + PFUZE100_VGENxLPWR | PFUZE100_VGENxSTBY, + PFUZE100_VGENxSTBY); + regmap_update_bits(syspm_pfuze_chip->regmap, PFUZE100_VGEN6VOL, + PFUZE100_VGENxLPWR | PFUZE100_VGENxSTBY, + PFUZE100_VGENxSTBY); +} + +static int pfuze_power_off_prepare_init(struct pfuze_chip *pfuze_chip) +{ + if (pfuze_chip->chip_id != PFUZE100) { + dev_warn(pfuze_chip->dev, "Requested pm_power_off_prepare handler for not supported chip\n"); + return -ENODEV; + } + + if (pm_power_off_prepare) { + dev_warn(pfuze_chip->dev, "pm_power_off_prepare is already registered.\n"); + return -EBUSY; + } + + if (syspm_pfuze_chip) { + dev_warn(pfuze_chip->dev, "syspm_pfuze_chip is already set.\n"); + return -EBUSY; + } + + syspm_pfuze_chip = pfuze_chip; + pm_power_off_prepare = pfuze_power_off_prepare; + + return 0; +} + static int pfuze_identify(struct pfuze_chip *pfuze_chip) { unsigned int value; @@ -753,6 +829,20 @@ static int pfuze100_regulator_probe(struct i2c_client *client, } } + if (of_property_read_bool(client->dev.of_node, + "fsl,pmic-stby-poweroff")) + return pfuze_power_off_prepare_init(pfuze_chip); + + return 0; +} + +static int pfuze100_regulator_remove(struct i2c_client *client) +{ + if (syspm_pfuze_chip) { + syspm_pfuze_chip = NULL; + pm_power_off_prepare = NULL; + } + return 0; } @@ -763,6 +853,7 @@ static struct i2c_driver pfuze_driver = { .of_match_table = pfuze_dt_ids, }, .probe = pfuze100_regulator_probe, + .remove = pfuze100_regulator_remove, }; module_i2c_driver(pfuze_driver); diff --git a/drivers/regulator/qcom-rpmh-regulator.c b/drivers/regulator/qcom-rpmh-regulator.c index 301e7f695374..39ccf53fdeb3 100644 --- a/drivers/regulator/qcom-rpmh-regulator.c +++ b/drivers/regulator/qcom-rpmh-regulator.c @@ -504,6 +504,7 @@ static unsigned int rpmh_regulator_pmic4_ldo_of_map_mode(unsigned int rpmh_mode) break; default: mode = REGULATOR_MODE_INVALID; + break; } return mode; @@ -537,6 +538,7 @@ rpmh_regulator_pmic4_smps_of_map_mode(unsigned int rpmh_mode) break; default: mode = REGULATOR_MODE_INVALID; + break; } return mode; @@ -566,6 +568,7 @@ static unsigned int rpmh_regulator_pmic4_bob_of_map_mode(unsigned int rpmh_mode) break; default: mode = REGULATOR_MODE_INVALID; + break; } return mode; diff --git a/drivers/regulator/qcom_smd-regulator.c b/drivers/regulator/qcom_smd-regulator.c index fe2fb36803e0..f5bca77d67c1 100644 --- a/drivers/regulator/qcom_smd-regulator.c +++ b/drivers/regulator/qcom_smd-regulator.c @@ -420,6 +420,60 @@ static const struct regulator_desc pmi8998_bob = { .ops = &rpm_bob_ops, }; +static const struct regulator_desc pms405_hfsmps3 = { + .linear_ranges = (struct regulator_linear_range[]) { + REGULATOR_LINEAR_RANGE(320000, 0, 215, 8000), + }, + .n_linear_ranges = 1, + .n_voltages = 216, + .ops = &rpm_smps_ldo_ops, +}; + +static const struct regulator_desc pms405_nldo300 = { + .linear_ranges = (struct regulator_linear_range[]) { + REGULATOR_LINEAR_RANGE(312000, 0, 127, 8000), + }, + .n_linear_ranges = 1, + .n_voltages = 128, + .ops = &rpm_smps_ldo_ops, +}; + +static const struct regulator_desc pms405_nldo1200 = { + .linear_ranges = (struct regulator_linear_range[]) { + REGULATOR_LINEAR_RANGE(312000, 0, 127, 8000), + }, + .n_linear_ranges = 1, + .n_voltages = 128, + .ops = &rpm_smps_ldo_ops, +}; + +static const struct regulator_desc pms405_pldo50 = { + .linear_ranges = (struct regulator_linear_range[]) { + REGULATOR_LINEAR_RANGE(1664000, 0, 128, 16000), + }, + .n_linear_ranges = 1, + .n_voltages = 129, + .ops = &rpm_smps_ldo_ops, +}; + +static const struct regulator_desc pms405_pldo150 = { + .linear_ranges = (struct regulator_linear_range[]) { + REGULATOR_LINEAR_RANGE(1664000, 0, 128, 16000), + }, + .n_linear_ranges = 1, + .n_voltages = 129, + .ops = &rpm_smps_ldo_ops, +}; + +static const struct regulator_desc pms405_pldo600 = { + .linear_ranges = (struct regulator_linear_range[]) { + REGULATOR_LINEAR_RANGE(1256000, 0, 98, 8000), + }, + .n_linear_ranges = 1, + .n_voltages = 99, + .ops = &rpm_smps_ldo_ops, +}; + struct rpm_regulator_data { const char *name; u32 type; @@ -661,6 +715,28 @@ static const struct rpm_regulator_data rpm_pmi8998_regulators[] = { {} }; +static const struct rpm_regulator_data rpm_pms405_regulators[] = { + { "s1", QCOM_SMD_RPM_SMPA, 1, &pms405_hfsmps3, "vdd_s1" }, + { "s2", QCOM_SMD_RPM_SMPA, 2, &pms405_hfsmps3, "vdd_s2" }, + { "s3", QCOM_SMD_RPM_SMPA, 3, &pms405_hfsmps3, "vdd_s3" }, + { "s4", QCOM_SMD_RPM_SMPA, 4, &pms405_hfsmps3, "vdd_s4" }, + { "s5", QCOM_SMD_RPM_SMPA, 5, &pms405_hfsmps3, "vdd_s5" }, + { "l1", QCOM_SMD_RPM_LDOA, 1, &pms405_nldo1200, "vdd_l1_l2" }, + { "l2", QCOM_SMD_RPM_LDOA, 2, &pms405_nldo1200, "vdd_l1_l2" }, + { "l3", QCOM_SMD_RPM_LDOA, 3, &pms405_nldo1200, "vdd_l3_l8" }, + { "l4", QCOM_SMD_RPM_LDOA, 4, &pms405_nldo300, "vdd_l4" }, + { "l5", QCOM_SMD_RPM_LDOA, 5, &pms405_pldo600, "vdd_l5_l6" }, + { "l6", QCOM_SMD_RPM_LDOA, 6, &pms405_pldo600, "vdd_l5_l6" }, + { "l7", QCOM_SMD_RPM_LDOA, 7, &pms405_pldo150, "vdd_l7" }, + { "l8", QCOM_SMD_RPM_LDOA, 8, &pms405_nldo1200, "vdd_l3_l8" }, + { "l9", QCOM_SMD_RPM_LDOA, 9, &pms405_nldo1200, "vdd_l9" }, + { "l10", QCOM_SMD_RPM_LDOA, 10, &pms405_pldo50, "vdd_l10_l11_l12_l13" }, + { "l11", QCOM_SMD_RPM_LDOA, 11, &pms405_pldo150, "vdd_l10_l11_l12_l13" }, + { "l12", QCOM_SMD_RPM_LDOA, 12, &pms405_pldo150, "vdd_l10_l11_l12_l13" }, + { "l13", QCOM_SMD_RPM_LDOA, 13, &pms405_pldo150, "vdd_l10_l11_l12_l13" }, + {} +}; + static const struct of_device_id rpm_of_match[] = { { .compatible = "qcom,rpm-pm8841-regulators", .data = &rpm_pm8841_regulators }, { .compatible = "qcom,rpm-pm8916-regulators", .data = &rpm_pm8916_regulators }, @@ -669,6 +745,7 @@ static const struct of_device_id rpm_of_match[] = { { .compatible = "qcom,rpm-pm8998-regulators", .data = &rpm_pm8998_regulators }, { .compatible = "qcom,rpm-pma8084-regulators", .data = &rpm_pma8084_regulators }, { .compatible = "qcom,rpm-pmi8998-regulators", .data = &rpm_pmi8998_regulators }, + { .compatible = "qcom,rpm-pms405-regulators", .data = &rpm_pms405_regulators }, {} }; MODULE_DEVICE_TABLE(of, rpm_of_match); diff --git a/drivers/regulator/s5m8767.c b/drivers/regulator/s5m8767.c index 09a97a7093e8..219b9afda0cb 100644 --- a/drivers/regulator/s5m8767.c +++ b/drivers/regulator/s5m8767.c @@ -571,12 +571,13 @@ static int s5m8767_pmic_dt_parse_pdata(struct platform_device *pdev, continue; } - rdata->ext_control_gpiod = devm_gpiod_get_from_of_node(&pdev->dev, - reg_np, - "s5m8767,pmic-ext-control-gpios", - 0, - GPIOD_OUT_HIGH, - "s5m8767"); + rdata->ext_control_gpiod = devm_gpiod_get_from_of_node( + &pdev->dev, + reg_np, + "s5m8767,pmic-ext-control-gpios", + 0, + GPIOD_OUT_HIGH | GPIOD_FLAGS_BIT_NONEXCLUSIVE, + "s5m8767"); if (IS_ERR(rdata->ext_control_gpiod)) return PTR_ERR(rdata->ext_control_gpiod); diff --git a/drivers/regulator/stpmic1_regulator.c b/drivers/regulator/stpmic1_regulator.c new file mode 100644 index 000000000000..e15634edb8ce --- /dev/null +++ b/drivers/regulator/stpmic1_regulator.c @@ -0,0 +1,674 @@ +// SPDX-License-Identifier: GPL-2.0 +// Copyright (C) STMicroelectronics 2018 +// Author: Pascal Paillet <p.paillet@st.com> for STMicroelectronics. + +#include <linux/interrupt.h> +#include <linux/mfd/stpmic1.h> +#include <linux/module.h> +#include <linux/of_irq.h> +#include <linux/platform_device.h> +#include <linux/regmap.h> +#include <linux/regulator/driver.h> +#include <linux/regulator/machine.h> +#include <linux/regulator/of_regulator.h> + +/** + * stpmic1 regulator description + * @desc: regulator framework description + * @mask_reset_reg: mask reset register address + * @mask_reset_mask: mask rank and mask reset register mask + * @icc_reg: icc register address + * @icc_mask: icc register mask + */ +struct stpmic1_regulator_cfg { + struct regulator_desc desc; + u8 mask_reset_reg; + u8 mask_reset_mask; + u8 icc_reg; + u8 icc_mask; +}; + +/** + * stpmic1 regulator data: this structure is used as driver data + * @regul_id: regulator id + * @reg_node: DT node of regulator (unused on non-DT platforms) + * @cfg: stpmic specific regulator description + * @mask_reset: mask_reset bit value + * @irq_curlim: current limit interrupt number + * @regmap: point to parent regmap structure + */ +struct stpmic1_regulator { + unsigned int regul_id; + struct device_node *reg_node; + struct stpmic1_regulator_cfg *cfg; + u8 mask_reset; + int irq_curlim; + struct regmap *regmap; +}; + +static int stpmic1_set_mode(struct regulator_dev *rdev, unsigned int mode); +static unsigned int stpmic1_get_mode(struct regulator_dev *rdev); +static int stpmic1_set_icc(struct regulator_dev *rdev); +static int stpmic1_regulator_parse_dt(void *driver_data); +static unsigned int stpmic1_map_mode(unsigned int mode); + +enum { + STPMIC1_BUCK1 = 0, + STPMIC1_BUCK2 = 1, + STPMIC1_BUCK3 = 2, + STPMIC1_BUCK4 = 3, + STPMIC1_LDO1 = 4, + STPMIC1_LDO2 = 5, + STPMIC1_LDO3 = 6, + STPMIC1_LDO4 = 7, + STPMIC1_LDO5 = 8, + STPMIC1_LDO6 = 9, + STPMIC1_VREF_DDR = 10, + STPMIC1_BOOST = 11, + STPMIC1_VBUS_OTG = 12, + STPMIC1_SW_OUT = 13, +}; + +/* Enable time worst case is 5000mV/(2250uV/uS) */ +#define PMIC_ENABLE_TIME_US 2200 + +#define STPMIC1_BUCK_MODE_NORMAL 0 +#define STPMIC1_BUCK_MODE_LP BUCK_HPLP_ENABLE_MASK + +struct regulator_linear_range buck1_ranges[] = { + REGULATOR_LINEAR_RANGE(600000, 0, 30, 25000), + REGULATOR_LINEAR_RANGE(1350000, 31, 63, 0), +}; + +struct regulator_linear_range buck2_ranges[] = { + REGULATOR_LINEAR_RANGE(1000000, 0, 17, 0), + REGULATOR_LINEAR_RANGE(1050000, 18, 19, 0), + REGULATOR_LINEAR_RANGE(1100000, 20, 21, 0), + REGULATOR_LINEAR_RANGE(1150000, 22, 23, 0), + REGULATOR_LINEAR_RANGE(1200000, 24, 25, 0), + REGULATOR_LINEAR_RANGE(1250000, 26, 27, 0), + REGULATOR_LINEAR_RANGE(1300000, 28, 29, 0), + REGULATOR_LINEAR_RANGE(1350000, 30, 31, 0), + REGULATOR_LINEAR_RANGE(1400000, 32, 33, 0), + REGULATOR_LINEAR_RANGE(1450000, 34, 35, 0), + REGULATOR_LINEAR_RANGE(1500000, 36, 63, 0), +}; + +struct regulator_linear_range buck3_ranges[] = { + REGULATOR_LINEAR_RANGE(1000000, 0, 19, 0), + REGULATOR_LINEAR_RANGE(1100000, 20, 23, 0), + REGULATOR_LINEAR_RANGE(1200000, 24, 27, 0), + REGULATOR_LINEAR_RANGE(1300000, 28, 31, 0), + REGULATOR_LINEAR_RANGE(1400000, 32, 35, 0), + REGULATOR_LINEAR_RANGE(1500000, 36, 55, 100000), + REGULATOR_LINEAR_RANGE(3400000, 56, 63, 0), + +}; + +struct regulator_linear_range buck4_ranges[] = { + REGULATOR_LINEAR_RANGE(600000, 0, 27, 25000), + REGULATOR_LINEAR_RANGE(1300000, 28, 29, 0), + REGULATOR_LINEAR_RANGE(1350000, 30, 31, 0), + REGULATOR_LINEAR_RANGE(1400000, 32, 33, 0), + REGULATOR_LINEAR_RANGE(1450000, 34, 35, 0), + REGULATOR_LINEAR_RANGE(1500000, 36, 60, 100000), + REGULATOR_LINEAR_RANGE(3900000, 61, 63, 0), + +}; + +struct regulator_linear_range ldo1_ranges[] = { + REGULATOR_LINEAR_RANGE(1700000, 0, 7, 0), + REGULATOR_LINEAR_RANGE(1700000, 8, 24, 100000), + REGULATOR_LINEAR_RANGE(3300000, 25, 31, 0), + +}; + +struct regulator_linear_range ldo2_ranges[] = { + REGULATOR_LINEAR_RANGE(1700000, 0, 7, 0), + REGULATOR_LINEAR_RANGE(1700000, 8, 24, 100000), + REGULATOR_LINEAR_RANGE(3300000, 25, 30, 0), + +}; + +struct regulator_linear_range ldo3_ranges[] = { + REGULATOR_LINEAR_RANGE(1700000, 0, 7, 0), + REGULATOR_LINEAR_RANGE(1700000, 8, 24, 100000), + REGULATOR_LINEAR_RANGE(3300000, 25, 30, 0), + /* with index 31 LDO3 is in DDR mode */ + REGULATOR_LINEAR_RANGE(500000, 31, 31, 0), +}; + +struct regulator_linear_range ldo5_ranges[] = { + REGULATOR_LINEAR_RANGE(1700000, 0, 7, 0), + REGULATOR_LINEAR_RANGE(1700000, 8, 30, 100000), + REGULATOR_LINEAR_RANGE(3900000, 31, 31, 0), +}; + +struct regulator_linear_range ldo6_ranges[] = { + REGULATOR_LINEAR_RANGE(900000, 0, 24, 100000), + REGULATOR_LINEAR_RANGE(3300000, 25, 31, 0), +}; + +static struct regulator_ops stpmic1_ldo_ops = { + .list_voltage = regulator_list_voltage_linear_range, + .map_voltage = regulator_map_voltage_linear_range, + .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, + .set_pull_down = regulator_set_pull_down_regmap, + .set_over_current_protection = stpmic1_set_icc, +}; + +static struct regulator_ops stpmic1_ldo3_ops = { + .list_voltage = regulator_list_voltage_linear_range, + .map_voltage = regulator_map_voltage_iterate, + .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, + .set_pull_down = regulator_set_pull_down_regmap, + .get_bypass = regulator_get_bypass_regmap, + .set_bypass = regulator_set_bypass_regmap, + .set_over_current_protection = stpmic1_set_icc, +}; + +static struct regulator_ops stpmic1_ldo4_fixed_regul_ops = { + .is_enabled = regulator_is_enabled_regmap, + .enable = regulator_enable_regmap, + .disable = regulator_disable_regmap, + .set_pull_down = regulator_set_pull_down_regmap, + .set_over_current_protection = stpmic1_set_icc, +}; + +static struct regulator_ops stpmic1_buck_ops = { + .list_voltage = regulator_list_voltage_linear_range, + .map_voltage = regulator_map_voltage_linear_range, + .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, + .set_pull_down = regulator_set_pull_down_regmap, + .set_mode = stpmic1_set_mode, + .get_mode = stpmic1_get_mode, + .set_over_current_protection = stpmic1_set_icc, +}; + +static struct regulator_ops stpmic1_vref_ddr_ops = { + .is_enabled = regulator_is_enabled_regmap, + .enable = regulator_enable_regmap, + .disable = regulator_disable_regmap, + .set_pull_down = regulator_set_pull_down_regmap, +}; + +static struct regulator_ops stpmic1_switch_regul_ops = { + .is_enabled = regulator_is_enabled_regmap, + .enable = regulator_enable_regmap, + .disable = regulator_disable_regmap, + .set_over_current_protection = stpmic1_set_icc, +}; + +#define REG_LDO(ids, base) { \ + .name = #ids, \ + .id = STPMIC1_##ids, \ + .n_voltages = 32, \ + .ops = &stpmic1_ldo_ops, \ + .linear_ranges = base ## _ranges, \ + .n_linear_ranges = ARRAY_SIZE(base ## _ranges), \ + .type = REGULATOR_VOLTAGE, \ + .owner = THIS_MODULE, \ + .vsel_reg = ids##_ACTIVE_CR, \ + .vsel_mask = LDO_VOLTAGE_MASK, \ + .enable_reg = ids##_ACTIVE_CR, \ + .enable_mask = LDO_ENABLE_MASK, \ + .enable_val = 1, \ + .disable_val = 0, \ + .enable_time = PMIC_ENABLE_TIME_US, \ + .pull_down_reg = ids##_PULL_DOWN_REG, \ + .pull_down_mask = ids##_PULL_DOWN_MASK, \ + .supply_name = #base, \ +} + +#define REG_LDO3(ids, base) { \ + .name = #ids, \ + .id = STPMIC1_##ids, \ + .n_voltages = 32, \ + .ops = &stpmic1_ldo3_ops, \ + .linear_ranges = ldo3_ranges, \ + .n_linear_ranges = ARRAY_SIZE(ldo3_ranges), \ + .type = REGULATOR_VOLTAGE, \ + .owner = THIS_MODULE, \ + .vsel_reg = LDO3_ACTIVE_CR, \ + .vsel_mask = LDO_VOLTAGE_MASK, \ + .enable_reg = LDO3_ACTIVE_CR, \ + .enable_mask = LDO_ENABLE_MASK, \ + .enable_val = 1, \ + .disable_val = 0, \ + .enable_time = PMIC_ENABLE_TIME_US, \ + .bypass_reg = LDO3_ACTIVE_CR, \ + .bypass_mask = LDO_BYPASS_MASK, \ + .bypass_val_on = LDO_BYPASS_MASK, \ + .bypass_val_off = 0, \ + .pull_down_reg = ids##_PULL_DOWN_REG, \ + .pull_down_mask = ids##_PULL_DOWN_MASK, \ + .supply_name = #base, \ +} + +#define REG_LDO4(ids, base) { \ + .name = #ids, \ + .id = STPMIC1_##ids, \ + .n_voltages = 1, \ + .ops = &stpmic1_ldo4_fixed_regul_ops, \ + .type = REGULATOR_VOLTAGE, \ + .owner = THIS_MODULE, \ + .min_uV = 3300000, \ + .fixed_uV = 3300000, \ + .enable_reg = LDO4_ACTIVE_CR, \ + .enable_mask = LDO_ENABLE_MASK, \ + .enable_val = 1, \ + .disable_val = 0, \ + .enable_time = PMIC_ENABLE_TIME_US, \ + .pull_down_reg = ids##_PULL_DOWN_REG, \ + .pull_down_mask = ids##_PULL_DOWN_MASK, \ + .supply_name = #base, \ +} + +#define REG_BUCK(ids, base) { \ + .name = #ids, \ + .id = STPMIC1_##ids, \ + .ops = &stpmic1_buck_ops, \ + .n_voltages = 64, \ + .linear_ranges = base ## _ranges, \ + .n_linear_ranges = ARRAY_SIZE(base ## _ranges), \ + .type = REGULATOR_VOLTAGE, \ + .owner = THIS_MODULE, \ + .vsel_reg = ids##_ACTIVE_CR, \ + .vsel_mask = BUCK_VOLTAGE_MASK, \ + .enable_reg = ids##_ACTIVE_CR, \ + .enable_mask = BUCK_ENABLE_MASK, \ + .enable_val = 1, \ + .disable_val = 0, \ + .enable_time = PMIC_ENABLE_TIME_US, \ + .of_map_mode = stpmic1_map_mode, \ + .pull_down_reg = ids##_PULL_DOWN_REG, \ + .pull_down_mask = ids##_PULL_DOWN_MASK, \ + .supply_name = #base, \ +} + +#define REG_VREF_DDR(ids, base) { \ + .name = #ids, \ + .id = STPMIC1_##ids, \ + .n_voltages = 1, \ + .ops = &stpmic1_vref_ddr_ops, \ + .type = REGULATOR_VOLTAGE, \ + .owner = THIS_MODULE, \ + .min_uV = 500000, \ + .fixed_uV = 500000, \ + .enable_reg = VREF_DDR_ACTIVE_CR, \ + .enable_mask = BUCK_ENABLE_MASK, \ + .enable_val = 1, \ + .disable_val = 0, \ + .enable_time = PMIC_ENABLE_TIME_US, \ + .pull_down_reg = ids##_PULL_DOWN_REG, \ + .pull_down_mask = ids##_PULL_DOWN_MASK, \ + .supply_name = #base, \ +} + +#define REG_SWITCH(ids, base, reg, mask, val) { \ + .name = #ids, \ + .id = STPMIC1_##ids, \ + .n_voltages = 1, \ + .ops = &stpmic1_switch_regul_ops, \ + .type = REGULATOR_VOLTAGE, \ + .owner = THIS_MODULE, \ + .min_uV = 0, \ + .fixed_uV = 5000000, \ + .enable_reg = (reg), \ + .enable_mask = (mask), \ + .enable_val = (val), \ + .disable_val = 0, \ + .enable_time = PMIC_ENABLE_TIME_US, \ + .supply_name = #base, \ +} + +struct stpmic1_regulator_cfg stpmic1_regulator_cfgs[] = { + [STPMIC1_BUCK1] = { + .desc = REG_BUCK(BUCK1, buck1), + .icc_reg = BUCKS_ICCTO_CR, + .icc_mask = BIT(0), + .mask_reset_reg = BUCKS_MASK_RESET_CR, + .mask_reset_mask = BIT(0), + }, + [STPMIC1_BUCK2] = { + .desc = REG_BUCK(BUCK2, buck2), + .icc_reg = BUCKS_ICCTO_CR, + .icc_mask = BIT(1), + .mask_reset_reg = BUCKS_MASK_RESET_CR, + .mask_reset_mask = BIT(1), + }, + [STPMIC1_BUCK3] = { + .desc = REG_BUCK(BUCK3, buck3), + .icc_reg = BUCKS_ICCTO_CR, + .icc_mask = BIT(2), + .mask_reset_reg = BUCKS_MASK_RESET_CR, + .mask_reset_mask = BIT(2), + }, + [STPMIC1_BUCK4] = { + .desc = REG_BUCK(BUCK4, buck4), + .icc_reg = BUCKS_ICCTO_CR, + .icc_mask = BIT(3), + .mask_reset_reg = BUCKS_MASK_RESET_CR, + .mask_reset_mask = BIT(3), + }, + [STPMIC1_LDO1] = { + .desc = REG_LDO(LDO1, ldo1), + .icc_reg = LDOS_ICCTO_CR, + .icc_mask = BIT(0), + .mask_reset_reg = LDOS_MASK_RESET_CR, + .mask_reset_mask = BIT(0), + }, + [STPMIC1_LDO2] = { + .desc = REG_LDO(LDO2, ldo2), + .icc_reg = LDOS_ICCTO_CR, + .icc_mask = BIT(1), + .mask_reset_reg = LDOS_MASK_RESET_CR, + .mask_reset_mask = BIT(1), + }, + [STPMIC1_LDO3] = { + .desc = REG_LDO3(LDO3, ldo3), + .icc_reg = LDOS_ICCTO_CR, + .icc_mask = BIT(2), + .mask_reset_reg = LDOS_MASK_RESET_CR, + .mask_reset_mask = BIT(2), + }, + [STPMIC1_LDO4] = { + .desc = REG_LDO4(LDO4, ldo4), + .icc_reg = LDOS_ICCTO_CR, + .icc_mask = BIT(3), + .mask_reset_reg = LDOS_MASK_RESET_CR, + .mask_reset_mask = BIT(3), + }, + [STPMIC1_LDO5] = { + .desc = REG_LDO(LDO5, ldo5), + .icc_reg = LDOS_ICCTO_CR, + .icc_mask = BIT(4), + .mask_reset_reg = LDOS_MASK_RESET_CR, + .mask_reset_mask = BIT(4), + }, + [STPMIC1_LDO6] = { + .desc = REG_LDO(LDO6, ldo6), + .icc_reg = LDOS_ICCTO_CR, + .icc_mask = BIT(5), + .mask_reset_reg = LDOS_MASK_RESET_CR, + .mask_reset_mask = BIT(5), + }, + [STPMIC1_VREF_DDR] = { + .desc = REG_VREF_DDR(VREF_DDR, vref_ddr), + .mask_reset_reg = LDOS_MASK_RESET_CR, + .mask_reset_mask = BIT(6), + }, + [STPMIC1_BOOST] = { + .desc = REG_SWITCH(BOOST, boost, BST_SW_CR, + BOOST_ENABLED, + BOOST_ENABLED), + .icc_reg = BUCKS_ICCTO_CR, + .icc_mask = BIT(6), + }, + [STPMIC1_VBUS_OTG] = { + .desc = REG_SWITCH(VBUS_OTG, pwr_sw1, BST_SW_CR, + USBSW_OTG_SWITCH_ENABLED, + USBSW_OTG_SWITCH_ENABLED), + .icc_reg = BUCKS_ICCTO_CR, + .icc_mask = BIT(4), + }, + [STPMIC1_SW_OUT] = { + .desc = REG_SWITCH(SW_OUT, pwr_sw2, BST_SW_CR, + SWIN_SWOUT_ENABLED, + SWIN_SWOUT_ENABLED), + .icc_reg = BUCKS_ICCTO_CR, + .icc_mask = BIT(5), + }, +}; + +static unsigned int stpmic1_map_mode(unsigned int mode) +{ + switch (mode) { + case STPMIC1_BUCK_MODE_NORMAL: + return REGULATOR_MODE_NORMAL; + case STPMIC1_BUCK_MODE_LP: + return REGULATOR_MODE_STANDBY; + default: + return REGULATOR_MODE_INVALID; + } +} + +static unsigned int stpmic1_get_mode(struct regulator_dev *rdev) +{ + int value; + + regmap_read(rdev->regmap, rdev->desc->enable_reg, &value); + + if (value & STPMIC1_BUCK_MODE_LP) + return REGULATOR_MODE_STANDBY; + + return REGULATOR_MODE_NORMAL; +} + +static int stpmic1_set_mode(struct regulator_dev *rdev, unsigned int mode) +{ + int value; + + switch (mode) { + case REGULATOR_MODE_NORMAL: + value = STPMIC1_BUCK_MODE_NORMAL; + break; + case REGULATOR_MODE_STANDBY: + value = STPMIC1_BUCK_MODE_LP; + break; + default: + return -EINVAL; + } + + return regmap_update_bits(rdev->regmap, rdev->desc->enable_reg, + STPMIC1_BUCK_MODE_LP, value); +} + +static int stpmic1_set_icc(struct regulator_dev *rdev) +{ + struct stpmic1_regulator *regul = rdev_get_drvdata(rdev); + + /* enable switch off in case of over current */ + return regmap_update_bits(regul->regmap, regul->cfg->icc_reg, + regul->cfg->icc_mask, regul->cfg->icc_mask); +} + +static irqreturn_t stpmic1_curlim_irq_handler(int irq, void *data) +{ + struct regulator_dev *rdev = (struct regulator_dev *)data; + + mutex_lock(&rdev->mutex); + + /* Send an overcurrent notification */ + regulator_notifier_call_chain(rdev, + REGULATOR_EVENT_OVER_CURRENT, + NULL); + + mutex_unlock(&rdev->mutex); + + return IRQ_HANDLED; +} + +static int stpmic1_regulator_init(struct platform_device *pdev, + struct regulator_dev *rdev) +{ + struct stpmic1_regulator *regul = rdev_get_drvdata(rdev); + int ret = 0; + + /* set mask reset */ + if (regul->mask_reset && regul->cfg->mask_reset_reg != 0) { + ret = regmap_update_bits(regul->regmap, + regul->cfg->mask_reset_reg, + regul->cfg->mask_reset_mask, + regul->cfg->mask_reset_mask); + if (ret) { + dev_err(&pdev->dev, "set mask reset failed\n"); + return ret; + } + } + + /* setup an irq handler for over-current detection */ + if (regul->irq_curlim > 0) { + ret = devm_request_threaded_irq(&pdev->dev, + regul->irq_curlim, NULL, + stpmic1_curlim_irq_handler, + IRQF_ONESHOT | IRQF_SHARED, + pdev->name, rdev); + if (ret) { + dev_err(&pdev->dev, "Request IRQ failed\n"); + return ret; + } + } + return 0; +} + +#define MATCH(_name, _id) \ + [STPMIC1_##_id] = { \ + .name = #_name, \ + .desc = &stpmic1_regulator_cfgs[STPMIC1_##_id].desc, \ + } + +static struct of_regulator_match stpmic1_regulators_matches[] = { + MATCH(buck1, BUCK1), + MATCH(buck2, BUCK2), + MATCH(buck3, BUCK3), + MATCH(buck4, BUCK4), + MATCH(ldo1, LDO1), + MATCH(ldo2, LDO2), + MATCH(ldo3, LDO3), + MATCH(ldo4, LDO4), + MATCH(ldo5, LDO5), + MATCH(ldo6, LDO6), + MATCH(vref_ddr, VREF_DDR), + MATCH(boost, BOOST), + MATCH(pwr_sw1, VBUS_OTG), + MATCH(pwr_sw2, SW_OUT), +}; + +static int stpmic1_regulator_parse_dt(void *driver_data) +{ + struct stpmic1_regulator *regul = + (struct stpmic1_regulator *)driver_data; + + if (!regul) + return -EINVAL; + + if (of_get_property(regul->reg_node, "st,mask-reset", NULL)) + regul->mask_reset = 1; + + regul->irq_curlim = of_irq_get(regul->reg_node, 0); + + return 0; +} + +static struct +regulator_dev *stpmic1_regulator_register(struct platform_device *pdev, int id, + struct regulator_init_data *init_data, + struct stpmic1_regulator *regul) +{ + struct stpmic1 *pmic_dev = dev_get_drvdata(pdev->dev.parent); + struct regulator_dev *rdev; + struct regulator_config config = {}; + + config.dev = &pdev->dev; + config.init_data = init_data; + config.of_node = stpmic1_regulators_matches[id].of_node; + config.regmap = pmic_dev->regmap; + config.driver_data = regul; + + regul->regul_id = id; + regul->reg_node = config.of_node; + regul->cfg = &stpmic1_regulator_cfgs[id]; + regul->regmap = pmic_dev->regmap; + + rdev = devm_regulator_register(&pdev->dev, ®ul->cfg->desc, &config); + if (IS_ERR(rdev)) { + dev_err(&pdev->dev, "failed to register %s regulator\n", + regul->cfg->desc.name); + } + + return rdev; +} + +static int stpmic1_regulator_probe(struct platform_device *pdev) +{ + struct regulator_dev *rdev; + struct stpmic1_regulator *regul; + struct regulator_init_data *init_data; + struct device_node *np; + int i, ret; + + np = pdev->dev.of_node; + + ret = of_regulator_match(&pdev->dev, np, + stpmic1_regulators_matches, + ARRAY_SIZE(stpmic1_regulators_matches)); + if (ret < 0) { + dev_err(&pdev->dev, + "Error in PMIC regulator device tree node"); + return ret; + } + + regul = devm_kzalloc(&pdev->dev, ARRAY_SIZE(stpmic1_regulator_cfgs) * + sizeof(struct stpmic1_regulator), + GFP_KERNEL); + if (!regul) + return -ENOMEM; + + for (i = 0; i < ARRAY_SIZE(stpmic1_regulator_cfgs); i++) { + /* Parse DT & find regulators to register */ + init_data = stpmic1_regulators_matches[i].init_data; + if (init_data) + init_data->regulator_init = &stpmic1_regulator_parse_dt; + + rdev = stpmic1_regulator_register(pdev, i, init_data, regul); + if (IS_ERR(rdev)) + return PTR_ERR(rdev); + + ret = stpmic1_regulator_init(pdev, rdev); + if (ret) { + dev_err(&pdev->dev, + "failed to initialize regulator %d\n", ret); + return ret; + } + + regul++; + } + + dev_dbg(&pdev->dev, "stpmic1_regulator driver probed\n"); + + return 0; +} + +static const struct of_device_id of_pmic_regulator_match[] = { + { .compatible = "st,stpmic1-regulators" }, + { }, +}; + +MODULE_DEVICE_TABLE(of, of_pmic_regulator_match); + +static struct platform_driver stpmic1_regulator_driver = { + .driver = { + .name = "stpmic1-regulator", + .of_match_table = of_match_ptr(of_pmic_regulator_match), + }, + .probe = stpmic1_regulator_probe, +}; + +module_platform_driver(stpmic1_regulator_driver); + +MODULE_DESCRIPTION("STPMIC1 PMIC voltage regulator driver"); +MODULE_AUTHOR("Pascal Paillet <p.paillet@st.com>"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/regulator/tps65090-regulator.c b/drivers/regulator/tps65090-regulator.c index edaef9e4dc74..db714d5edafc 100644 --- a/drivers/regulator/tps65090-regulator.c +++ b/drivers/regulator/tps65090-regulator.c @@ -374,6 +374,7 @@ static struct tps65090_platform_data *tps65090_parse_dt_reg_data( gflags = GPIOD_OUT_HIGH; else gflags = GPIOD_OUT_LOW; + gflags |= GPIOD_FLAGS_BIT_NONEXCLUSIVE; rpdata->gpiod = devm_gpiod_get_from_of_node(&pdev->dev, tps65090_matches[idx].of_node, |