diff options
Diffstat (limited to 'drivers/regulator')
26 files changed, 2173 insertions, 453 deletions
diff --git a/drivers/regulator/88pm800.c b/drivers/regulator/88pm800-regulator.c index 69ae25886181..69ae25886181 100644 --- a/drivers/regulator/88pm800.c +++ b/drivers/regulator/88pm800-regulator.c diff --git a/drivers/regulator/Kconfig b/drivers/regulator/Kconfig index b5d0e07a62d7..7928960563e6 100644 --- a/drivers/regulator/Kconfig +++ b/drivers/regulator/Kconfig @@ -136,19 +136,20 @@ config REGULATOR_AB8500 signal AB8500 PMIC config REGULATOR_ARIZONA_LDO1 - tristate "Wolfson Arizona class devices LDO1" - depends on MFD_ARIZONA + tristate "Cirrus Madera and Wolfson Arizona class devices LDO1" + depends on MFD_ARIZONA || MFD_MADERA depends on SND_SOC help - Support for the LDO1 regulators found on Wolfson Arizona class - devices. + Support for the LDO1 regulators found on Cirrus Logic Madera codecs + and Wolfson Microelectronic Arizona codecs. config REGULATOR_ARIZONA_MICSUPP - tristate "Wolfson Arizona class devices MICSUPP" - depends on MFD_ARIZONA + tristate "Cirrus Madera and Wolfson Arizona class devices MICSUPP" + depends on MFD_ARIZONA || MFD_MADERA depends on SND_SOC help - Support for the MICSUPP regulators found on Wolfson Arizona class + Support for the MICSUPP regulators found on Cirrus Logic Madera codecs + and Wolfson Microelectronic Arizona codecs devices. config REGULATOR_AS3711 @@ -258,7 +259,7 @@ config REGULATOR_DA9062 config REGULATOR_DA9063 tristate "Dialog Semiconductor DA9063 regulators" - depends on MFD_DA9063 + depends on MFD_DA9063 && OF help Say y here to support the BUCKs and LDOs regulators found on DA9063 PMICs. @@ -829,6 +830,26 @@ config REGULATOR_SKY81452 This driver can also be built as a module. If so, the module will be called sky81452-regulator. +config REGULATOR_SLG51000 + tristate "Dialog Semiconductor SLG51000 regulators" + depends on I2C + select REGMAP_I2C + help + Say y here to support for the Dialog Semiconductor SLG51000. + The SLG51000 is seven compact and customizable low dropout + regulators. + +config REGULATOR_STM32_BOOSTER + tristate "STMicroelectronics STM32 BOOSTER" + depends on ARCH_STM32 || COMPILE_TEST + help + This driver supports internal booster (3V3) embedded in some + STMicroelectronics STM32 chips. It can be used to supply ADC analog + input switches when vdda supply is below 2.7V. + + This driver can also be built as a module. If so, the module + will be called stm32-booster. + config REGULATOR_STM32_VREFBUF tristate "STMicroelectronics STM32 VREFBUF" depends on ARCH_STM32 || COMPILE_TEST diff --git a/drivers/regulator/Makefile b/drivers/regulator/Makefile index 93f53840e8f1..eef73b5a35a4 100644 --- a/drivers/regulator/Makefile +++ b/drivers/regulator/Makefile @@ -11,7 +11,7 @@ obj-$(CONFIG_REGULATOR_VIRTUAL_CONSUMER) += virtual.o obj-$(CONFIG_REGULATOR_USERSPACE_CONSUMER) += userspace-consumer.o obj-$(CONFIG_REGULATOR_88PG86X) += 88pg86x.o -obj-$(CONFIG_REGULATOR_88PM800) += 88pm800.o +obj-$(CONFIG_REGULATOR_88PM800) += 88pm800-regulator.o obj-$(CONFIG_REGULATOR_88PM8607) += 88pm8607.o obj-$(CONFIG_REGULATOR_CPCAP) += cpcap-regulator.o obj-$(CONFIG_REGULATOR_AAT2870) += aat2870-regulator.o @@ -104,6 +104,8 @@ obj-$(CONFIG_REGULATOR_S2MPS11) += s2mps11.o obj-$(CONFIG_REGULATOR_S5M8767) += s5m8767.o obj-$(CONFIG_REGULATOR_SC2731) += sc2731-regulator.o obj-$(CONFIG_REGULATOR_SKY81452) += sky81452-regulator.o +obj-$(CONFIG_REGULATOR_SLG51000) += slg51000-regulator.o +obj-$(CONFIG_REGULATOR_STM32_BOOSTER) += stm32-booster.o obj-$(CONFIG_REGULATOR_STM32_VREFBUF) += stm32-vrefbuf.o obj-$(CONFIG_REGULATOR_STM32_PWR) += stm32-pwr.o obj-$(CONFIG_REGULATOR_STPMIC1) += stpmic1_regulator.o diff --git a/drivers/regulator/arizona-ldo1.c b/drivers/regulator/arizona-ldo1.c index e4bc7b1e5ccd..1a3d7b720f5e 100644 --- a/drivers/regulator/arizona-ldo1.c +++ b/drivers/regulator/arizona-ldo1.c @@ -25,6 +25,10 @@ #include <linux/mfd/arizona/pdata.h> #include <linux/mfd/arizona/registers.h> +#include <linux/mfd/madera/core.h> +#include <linux/mfd/madera/pdata.h> +#include <linux/mfd/madera/registers.h> + struct arizona_ldo1 { struct regulator_dev *regulator; struct regmap *regmap; @@ -158,6 +162,31 @@ static const struct regulator_init_data arizona_ldo1_wm5110 = { .num_consumer_supplies = 1, }; +static const struct regulator_desc madera_ldo1 = { + .name = "LDO1", + .supply_name = "LDOVDD", + .type = REGULATOR_VOLTAGE, + .ops = &arizona_ldo1_ops, + + .vsel_reg = MADERA_LDO1_CONTROL_1, + .vsel_mask = MADERA_LDO1_VSEL_MASK, + .min_uV = 900000, + .uV_step = 25000, + .n_voltages = 13, + .enable_time = 3000, + + .owner = THIS_MODULE, +}; + +static const struct regulator_init_data madera_ldo1_default = { + .constraints = { + .min_uV = 1200000, + .max_uV = 1200000, + .valid_ops_mask = REGULATOR_CHANGE_STATUS, + }, + .num_consumer_supplies = 1, +}; + static int arizona_ldo1_of_get_pdata(struct arizona_ldo1_pdata *pdata, struct regulator_config *config, const struct regulator_desc *desc, @@ -320,6 +349,32 @@ static int arizona_ldo1_remove(struct platform_device *pdev) return 0; } +static int madera_ldo1_probe(struct platform_device *pdev) +{ + struct madera *madera = dev_get_drvdata(pdev->dev.parent); + struct arizona_ldo1 *ldo1; + bool external_dcvdd; + int ret; + + ldo1 = devm_kzalloc(&pdev->dev, sizeof(*ldo1), GFP_KERNEL); + if (!ldo1) + return -ENOMEM; + + ldo1->regmap = madera->regmap; + + ldo1->init_data = madera_ldo1_default; + + ret = arizona_ldo1_common_init(pdev, ldo1, &madera_ldo1, + &madera->pdata.ldo1, + &external_dcvdd); + if (ret) + return ret; + + madera->internal_dcvdd = !external_dcvdd; + + return 0; +} + static struct platform_driver arizona_ldo1_driver = { .probe = arizona_ldo1_probe, .remove = arizona_ldo1_remove, @@ -328,10 +383,36 @@ static struct platform_driver arizona_ldo1_driver = { }, }; -module_platform_driver(arizona_ldo1_driver); +static struct platform_driver madera_ldo1_driver = { + .probe = madera_ldo1_probe, + .remove = arizona_ldo1_remove, + .driver = { + .name = "madera-ldo1", + }, +}; + +static struct platform_driver * const madera_ldo1_drivers[] = { + &arizona_ldo1_driver, + &madera_ldo1_driver, +}; + +static int __init arizona_ldo1_init(void) +{ + return platform_register_drivers(madera_ldo1_drivers, + ARRAY_SIZE(madera_ldo1_drivers)); +} +module_init(arizona_ldo1_init); + +static void __exit madera_ldo1_exit(void) +{ + platform_unregister_drivers(madera_ldo1_drivers, + ARRAY_SIZE(madera_ldo1_drivers)); +} +module_exit(madera_ldo1_exit); /* Module information */ MODULE_AUTHOR("Mark Brown <broonie@opensource.wolfsonmicro.com>"); MODULE_DESCRIPTION("Arizona LDO1 driver"); MODULE_LICENSE("GPL"); MODULE_ALIAS("platform:arizona-ldo1"); +MODULE_ALIAS("platform:madera-ldo1"); diff --git a/drivers/regulator/arizona-micsupp.c b/drivers/regulator/arizona-micsupp.c index be0d46da51a1..ae1a5de3e57d 100644 --- a/drivers/regulator/arizona-micsupp.c +++ b/drivers/regulator/arizona-micsupp.c @@ -16,7 +16,6 @@ #include <linux/regulator/driver.h> #include <linux/regulator/machine.h> #include <linux/regulator/of_regulator.h> -#include <linux/gpio.h> #include <linux/slab.h> #include <linux/workqueue.h> #include <sound/soc.h> @@ -25,6 +24,10 @@ #include <linux/mfd/arizona/pdata.h> #include <linux/mfd/arizona/registers.h> +#include <linux/mfd/madera/core.h> +#include <linux/mfd/madera/pdata.h> +#include <linux/mfd/madera/registers.h> + #include <linux/regulator/arizona-micsupp.h> struct arizona_micsupp { @@ -200,6 +203,28 @@ static const struct regulator_init_data arizona_micsupp_ext_default = { .num_consumer_supplies = 1, }; +static const struct regulator_desc madera_micsupp = { + .name = "MICVDD", + .supply_name = "CPVDD1", + .type = REGULATOR_VOLTAGE, + .n_voltages = 40, + .ops = &arizona_micsupp_ops, + + .vsel_reg = MADERA_LDO2_CONTROL_1, + .vsel_mask = MADERA_LDO2_VSEL_MASK, + .enable_reg = MADERA_MIC_CHARGE_PUMP_1, + .enable_mask = MADERA_CPMIC_ENA, + .bypass_reg = MADERA_MIC_CHARGE_PUMP_1, + .bypass_mask = MADERA_CPMIC_BYPASS, + + .linear_ranges = arizona_micsupp_ext_ranges, + .n_linear_ranges = ARRAY_SIZE(arizona_micsupp_ext_ranges), + + .enable_time = 3000, + + .owner = THIS_MODULE, +}; + static int arizona_micsupp_of_get_pdata(struct arizona_micsupp_pdata *pdata, struct regulator_config *config, const struct regulator_desc *desc) @@ -316,6 +341,24 @@ static int arizona_micsupp_probe(struct platform_device *pdev) &arizona->pdata.micvdd); } +static int madera_micsupp_probe(struct platform_device *pdev) +{ + struct madera *madera = dev_get_drvdata(pdev->dev.parent); + struct arizona_micsupp *micsupp; + + micsupp = devm_kzalloc(&pdev->dev, sizeof(*micsupp), GFP_KERNEL); + if (!micsupp) + return -ENOMEM; + + micsupp->regmap = madera->regmap; + micsupp->dapm = &madera->dapm; + micsupp->dev = madera->dev; + micsupp->init_data = arizona_micsupp_ext_default; + + return arizona_micsupp_common_init(pdev, micsupp, &madera_micsupp, + &madera->pdata.micvdd); +} + static struct platform_driver arizona_micsupp_driver = { .probe = arizona_micsupp_probe, .driver = { @@ -323,10 +366,35 @@ static struct platform_driver arizona_micsupp_driver = { }, }; -module_platform_driver(arizona_micsupp_driver); +static struct platform_driver madera_micsupp_driver = { + .probe = madera_micsupp_probe, + .driver = { + .name = "madera-micsupp", + }, +}; + +static struct platform_driver * const arizona_micsupp_drivers[] = { + &arizona_micsupp_driver, + &madera_micsupp_driver, +}; + +static int __init arizona_micsupp_init(void) +{ + return platform_register_drivers(arizona_micsupp_drivers, + ARRAY_SIZE(arizona_micsupp_drivers)); +} +module_init(arizona_micsupp_init); + +static void __exit arizona_micsupp_exit(void) +{ + platform_unregister_drivers(arizona_micsupp_drivers, + ARRAY_SIZE(arizona_micsupp_drivers)); +} +module_exit(arizona_micsupp_exit); /* Module information */ MODULE_AUTHOR("Mark Brown <broonie@opensource.wolfsonmicro.com>"); MODULE_DESCRIPTION("Arizona microphone supply driver"); MODULE_LICENSE("GPL"); MODULE_ALIAS("platform:arizona-micsupp"); +MODULE_ALIAS("platform:madera-micsupp"); diff --git a/drivers/regulator/bd70528-regulator.c b/drivers/regulator/bd70528-regulator.c index 30e3ed430a8a..0248a61f1006 100644 --- a/drivers/regulator/bd70528-regulator.c +++ b/drivers/regulator/bd70528-regulator.c @@ -4,7 +4,6 @@ #include <linux/delay.h> #include <linux/err.h> -#include <linux/gpio.h> #include <linux/interrupt.h> #include <linux/kernel.h> #include <linux/mfd/rohm-bd70528.h> diff --git a/drivers/regulator/bd718x7-regulator.c b/drivers/regulator/bd718x7-regulator.c index fde4264da6ff..8c22cfb76173 100644 --- a/drivers/regulator/bd718x7-regulator.c +++ b/drivers/regulator/bd718x7-regulator.c @@ -4,7 +4,6 @@ #include <linux/delay.h> #include <linux/err.h> -#include <linux/gpio.h> #include <linux/interrupt.h> #include <linux/kernel.h> #include <linux/mfd/rohm-bd718x7.h> diff --git a/drivers/regulator/core.c b/drivers/regulator/core.c index c894cf0d8a28..86ae1825cec1 100644 --- a/drivers/regulator/core.c +++ b/drivers/regulator/core.c @@ -1,12 +1,11 @@ // SPDX-License-Identifier: GPL-2.0-or-later -/* - * core.c -- Voltage/Current Regulator framework. - * - * Copyright 2007, 2008 Wolfson Microelectronics PLC. - * Copyright 2008 SlimLogic Ltd. - * - * Author: Liam Girdwood <lrg@slimlogic.co.uk> - */ +// +// core.c -- Voltage/Current Regulator framework. +// +// Copyright 2007, 2008 Wolfson Microelectronics PLC. +// Copyright 2008 SlimLogic Ltd. +// +// Author: Liam Girdwood <lrg@slimlogic.co.uk> #include <linux/kernel.h> #include <linux/init.h> @@ -23,6 +22,7 @@ #include <linux/regmap.h> #include <linux/regulator/of_regulator.h> #include <linux/regulator/consumer.h> +#include <linux/regulator/coupler.h> #include <linux/regulator/driver.h> #include <linux/regulator/machine.h> #include <linux/module.h> @@ -50,6 +50,7 @@ static DEFINE_MUTEX(regulator_list_mutex); static LIST_HEAD(regulator_map_list); static LIST_HEAD(regulator_ena_gpio_list); static LIST_HEAD(regulator_supply_alias_list); +static LIST_HEAD(regulator_coupler_list); static bool has_full_constraints; static struct dentry *debugfs_root; @@ -93,7 +94,6 @@ struct regulator_supply_alias { static int _regulator_is_enabled(struct regulator_dev *rdev); static int _regulator_disable(struct regulator *regulator); -static int _regulator_get_voltage(struct regulator_dev *rdev); static int _regulator_get_current_limit(struct regulator_dev *rdev); static unsigned int _regulator_get_mode(struct regulator_dev *rdev); static int _notifier_call_chain(struct regulator_dev *rdev, @@ -102,15 +102,12 @@ static int _regulator_do_set_voltage(struct regulator_dev *rdev, int min_uV, int max_uV); static int regulator_balance_voltage(struct regulator_dev *rdev, suspend_state_t state); -static int regulator_set_voltage_rdev(struct regulator_dev *rdev, - int min_uV, int max_uV, - suspend_state_t state); static struct regulator *create_regulator(struct regulator_dev *rdev, struct device *dev, const char *supply_name); static void _regulator_put(struct regulator *regulator); -static const char *rdev_get_name(struct regulator_dev *rdev) +const char *rdev_get_name(struct regulator_dev *rdev) { if (rdev->constraints && rdev->constraints->name) return rdev->constraints->name; @@ -424,8 +421,8 @@ static struct device_node *of_get_regulator(struct device *dev, const char *supp } /* Platform voltage constraint check */ -static int regulator_check_voltage(struct regulator_dev *rdev, - int *min_uV, int *max_uV) +int regulator_check_voltage(struct regulator_dev *rdev, + int *min_uV, int *max_uV) { BUG_ON(*min_uV > *max_uV); @@ -457,9 +454,9 @@ static int regulator_check_states(suspend_state_t state) /* Make sure we select a voltage that suits the needs of all * regulator consumers */ -static int regulator_check_consumers(struct regulator_dev *rdev, - int *min_uV, int *max_uV, - suspend_state_t state) +int regulator_check_consumers(struct regulator_dev *rdev, + int *min_uV, int *max_uV, + suspend_state_t state) { struct regulator *regulator; struct regulator_voltage *voltage; @@ -570,7 +567,7 @@ static ssize_t regulator_uV_show(struct device *dev, ssize_t ret; regulator_lock(rdev); - ret = sprintf(buf, "%d\n", _regulator_get_voltage(rdev)); + ret = sprintf(buf, "%d\n", regulator_get_voltage_rdev(rdev)); regulator_unlock(rdev); return ret; @@ -941,7 +938,7 @@ static int drms_uA_update(struct regulator_dev *rdev) rdev_err(rdev, "failed to set load %d\n", current_uA); } else { /* get output voltage */ - output_uV = _regulator_get_voltage(rdev); + output_uV = regulator_get_voltage_rdev(rdev); if (output_uV <= 0) { rdev_err(rdev, "invalid output voltage found\n"); return -EINVAL; @@ -1054,7 +1051,7 @@ static void print_constraints(struct regulator_dev *rdev) if (!constraints->min_uV || constraints->min_uV != constraints->max_uV) { - ret = _regulator_get_voltage(rdev); + ret = regulator_get_voltage_rdev(rdev); if (ret > 0) count += scnprintf(buf + count, len - count, "at %d mV ", ret / 1000); @@ -1113,7 +1110,7 @@ static int machine_constraints_voltage(struct regulator_dev *rdev, if (rdev->constraints->apply_uV && rdev->constraints->min_uV && rdev->constraints->max_uV) { int target_min, target_max; - int current_uV = _regulator_get_voltage(rdev); + int current_uV = regulator_get_voltage_rdev(rdev); if (current_uV == -ENOTRECOVERABLE) { /* This regulator can't be read and must be initialized */ @@ -1123,7 +1120,7 @@ static int machine_constraints_voltage(struct regulator_dev *rdev, _regulator_do_set_voltage(rdev, rdev->constraints->min_uV, rdev->constraints->max_uV); - current_uV = _regulator_get_voltage(rdev); + current_uV = regulator_get_voltage_rdev(rdev); } if (current_uV < 0) { @@ -1645,9 +1642,9 @@ static int _regulator_get_enable_time(struct regulator_dev *rdev) { if (rdev->constraints && rdev->constraints->enable_time) return rdev->constraints->enable_time; - if (!rdev->desc->ops->enable_time) - return rdev->desc->enable_time; - return rdev->desc->ops->enable_time(rdev); + if (rdev->desc->ops->enable_time) + return rdev->desc->ops->enable_time(rdev); + return rdev->desc->enable_time; } static struct regulator_supply_alias *regulator_find_supply_alias( @@ -3065,7 +3062,7 @@ static int _regulator_call_set_voltage(struct regulator_dev *rdev, struct pre_voltage_change_data data; int ret; - data.old_uV = _regulator_get_voltage(rdev); + data.old_uV = regulator_get_voltage_rdev(rdev); data.min_uV = min_uV; data.max_uV = max_uV; ret = _notifier_call_chain(rdev, REGULATOR_EVENT_PRE_VOLTAGE_CHANGE, @@ -3089,7 +3086,7 @@ static int _regulator_call_set_voltage_sel(struct regulator_dev *rdev, struct pre_voltage_change_data data; int ret; - data.old_uV = _regulator_get_voltage(rdev); + data.old_uV = regulator_get_voltage_rdev(rdev); data.min_uV = uV; data.max_uV = uV; ret = _notifier_call_chain(rdev, REGULATOR_EVENT_PRE_VOLTAGE_CHANGE, @@ -3107,6 +3104,66 @@ static int _regulator_call_set_voltage_sel(struct regulator_dev *rdev, return ret; } +static int _regulator_set_voltage_sel_step(struct regulator_dev *rdev, + int uV, int new_selector) +{ + const struct regulator_ops *ops = rdev->desc->ops; + int diff, old_sel, curr_sel, ret; + + /* Stepping is only needed if the regulator is enabled. */ + if (!_regulator_is_enabled(rdev)) + goto final_set; + + if (!ops->get_voltage_sel) + return -EINVAL; + + old_sel = ops->get_voltage_sel(rdev); + if (old_sel < 0) + return old_sel; + + diff = new_selector - old_sel; + if (diff == 0) + return 0; /* No change needed. */ + + if (diff > 0) { + /* Stepping up. */ + for (curr_sel = old_sel + rdev->desc->vsel_step; + curr_sel < new_selector; + curr_sel += rdev->desc->vsel_step) { + /* + * Call the callback directly instead of using + * _regulator_call_set_voltage_sel() as we don't + * want to notify anyone yet. Same in the branch + * below. + */ + ret = ops->set_voltage_sel(rdev, curr_sel); + if (ret) + goto try_revert; + } + } else { + /* Stepping down. */ + for (curr_sel = old_sel - rdev->desc->vsel_step; + curr_sel > new_selector; + curr_sel -= rdev->desc->vsel_step) { + ret = ops->set_voltage_sel(rdev, curr_sel); + if (ret) + goto try_revert; + } + } + +final_set: + /* The final selector will trigger the notifiers. */ + return _regulator_call_set_voltage_sel(rdev, uV, new_selector); + +try_revert: + /* + * At least try to return to the previous voltage if setting a new + * one failed. + */ + (void)ops->set_voltage_sel(rdev, old_sel); + return ret; +} + static int _regulator_set_voltage_time(struct regulator_dev *rdev, int old_uV, int new_uV) { @@ -3142,7 +3199,7 @@ static int _regulator_do_set_voltage(struct regulator_dev *rdev, unsigned int selector; int old_selector = -1; const struct regulator_ops *ops = rdev->desc->ops; - int old_uV = _regulator_get_voltage(rdev); + int old_uV = regulator_get_voltage_rdev(rdev); trace_regulator_set_voltage(rdev_get_name(rdev), min_uV, max_uV); @@ -3169,7 +3226,7 @@ static int _regulator_do_set_voltage(struct regulator_dev *rdev, best_val = ops->list_voltage(rdev, selector); else - best_val = _regulator_get_voltage(rdev); + best_val = regulator_get_voltage_rdev(rdev); } } else if (ops->set_voltage_sel) { @@ -3180,6 +3237,9 @@ static int _regulator_do_set_voltage(struct regulator_dev *rdev, selector = ret; if (old_selector == selector) ret = 0; + else if (rdev->desc->vsel_step) + ret = _regulator_set_voltage_sel_step( + rdev, best_val, selector); else ret = _regulator_call_set_voltage_sel( rdev, best_val, selector); @@ -3288,7 +3348,7 @@ static int regulator_set_voltage_unlocked(struct regulator *regulator, * changing the voltage. */ if (!regulator_ops_is_valid(rdev, REGULATOR_CHANGE_VOLTAGE)) { - current_uV = _regulator_get_voltage(rdev); + current_uV = regulator_get_voltage_rdev(rdev); if (min_uV <= current_uV && current_uV <= max_uV) { voltage->min_uV = min_uV; voltage->max_uV = max_uV; @@ -3325,8 +3385,8 @@ out: return ret; } -static int regulator_set_voltage_rdev(struct regulator_dev *rdev, int min_uV, - int max_uV, suspend_state_t state) +int regulator_set_voltage_rdev(struct regulator_dev *rdev, int min_uV, + int max_uV, suspend_state_t state) { int best_supply_uV = 0; int supply_change_uV = 0; @@ -3354,7 +3414,7 @@ static int regulator_set_voltage_rdev(struct regulator_dev *rdev, int min_uV, best_supply_uV += rdev->desc->min_dropout_uV; - current_supply_uV = _regulator_get_voltage(rdev->supply->rdev); + current_supply_uV = regulator_get_voltage_rdev(rdev->supply->rdev); if (current_supply_uV < 0) { ret = current_supply_uV; goto out; @@ -3405,7 +3465,7 @@ static int regulator_limit_voltage_step(struct regulator_dev *rdev, return 1; if (*current_uV < 0) { - *current_uV = _regulator_get_voltage(rdev); + *current_uV = regulator_get_voltage_rdev(rdev); if (*current_uV < 0) return *current_uV; @@ -3434,11 +3494,10 @@ static int regulator_get_optimal_voltage(struct regulator_dev *rdev, struct coupling_desc *c_desc = &rdev->coupling_desc; struct regulator_dev **c_rdevs = c_desc->coupled_rdevs; struct regulation_constraints *constraints = rdev->constraints; - int max_spread = constraints->max_spread; int desired_min_uV = 0, desired_max_uV = INT_MAX; int max_current_uV = 0, min_current_uV = INT_MAX; int highest_min_uV = 0, target_uV, possible_uV; - int i, ret; + int i, ret, max_spread; bool done; *current_uV = -1; @@ -3492,6 +3551,8 @@ static int regulator_get_optimal_voltage(struct regulator_dev *rdev, } } + max_spread = constraints->max_spread[0]; + /* * Let target_uV be equal to the desired one if possible. * If not, set it to minimum voltage, allowed by other coupled @@ -3509,7 +3570,7 @@ static int regulator_get_optimal_voltage(struct regulator_dev *rdev, if (!_regulator_is_enabled(c_rdevs[i])) continue; - tmp_act = _regulator_get_voltage(c_rdevs[i]); + tmp_act = regulator_get_voltage_rdev(c_rdevs[i]); if (tmp_act < 0) return tmp_act; @@ -3551,7 +3612,7 @@ finish: if (n_coupled > 1 && *current_uV == -1) { if (_regulator_is_enabled(rdev)) { - ret = _regulator_get_voltage(rdev); + ret = regulator_get_voltage_rdev(rdev); if (ret < 0) return ret; @@ -3573,9 +3634,11 @@ static int regulator_balance_voltage(struct regulator_dev *rdev, struct regulator_dev **c_rdevs; struct regulator_dev *best_rdev; struct coupling_desc *c_desc = &rdev->coupling_desc; + struct regulator_coupler *coupler = c_desc->coupler; int i, ret, n_coupled, best_min_uV, best_max_uV, best_c_rdev; - bool best_c_rdev_done, c_rdev_done[MAX_COUPLED]; unsigned int delta, best_delta; + unsigned long c_rdev_done = 0; + bool best_c_rdev_done; c_rdevs = c_desc->coupled_rdevs; n_coupled = c_desc->n_coupled; @@ -3592,8 +3655,9 @@ static int regulator_balance_voltage(struct regulator_dev *rdev, return -EPERM; } - for (i = 0; i < n_coupled; i++) - c_rdev_done[i] = false; + /* Invoke custom balancer for customized couplers */ + if (coupler && coupler->balance_voltage) + return coupler->balance_voltage(coupler, rdev, state); /* * Find the best possible voltage change on each loop. Leave the loop @@ -3620,7 +3684,7 @@ static int regulator_balance_voltage(struct regulator_dev *rdev, */ int optimal_uV = 0, optimal_max_uV = 0, current_uV = 0; - if (c_rdev_done[i]) + if (test_bit(i, &c_rdev_done)) continue; ret = regulator_get_optimal_voltage(c_rdevs[i], @@ -3655,7 +3719,8 @@ static int regulator_balance_voltage(struct regulator_dev *rdev, if (ret < 0) goto out; - c_rdev_done[best_c_rdev] = best_c_rdev_done; + if (best_c_rdev_done) + set_bit(best_c_rdev, &c_rdev_done); } while (n_coupled > 1); @@ -3911,7 +3976,7 @@ out: } EXPORT_SYMBOL_GPL(regulator_sync_voltage); -static int _regulator_get_voltage(struct regulator_dev *rdev) +int regulator_get_voltage_rdev(struct regulator_dev *rdev) { int sel, ret; bool bypassed; @@ -3928,7 +3993,7 @@ static int _regulator_get_voltage(struct regulator_dev *rdev) return -EPROBE_DEFER; } - return _regulator_get_voltage(rdev->supply->rdev); + return regulator_get_voltage_rdev(rdev->supply->rdev); } } @@ -3944,7 +4009,7 @@ static int _regulator_get_voltage(struct regulator_dev *rdev) } else if (rdev->desc->fixed_uV && (rdev->desc->n_voltages == 1)) { ret = rdev->desc->fixed_uV; } else if (rdev->supply) { - ret = _regulator_get_voltage(rdev->supply->rdev); + ret = regulator_get_voltage_rdev(rdev->supply->rdev); } else { return -EINVAL; } @@ -3969,7 +4034,7 @@ int regulator_get_voltage(struct regulator *regulator) int ret; regulator_lock_dependent(regulator->rdev, &ww_ctx); - ret = _regulator_get_voltage(regulator->rdev); + ret = regulator_get_voltage_rdev(regulator->rdev); regulator_unlock_dependent(regulator->rdev, &ww_ctx); return ret; @@ -4707,8 +4772,60 @@ static int regulator_register_resolve_supply(struct device *dev, void *data) return 0; } +int regulator_coupler_register(struct regulator_coupler *coupler) +{ + mutex_lock(®ulator_list_mutex); + list_add_tail(&coupler->list, ®ulator_coupler_list); + mutex_unlock(®ulator_list_mutex); + + return 0; +} + +static struct regulator_coupler * +regulator_find_coupler(struct regulator_dev *rdev) +{ + struct regulator_coupler *coupler; + int err; + + /* + * Note that regulators are appended to the list and the generic + * coupler is registered first, hence it will be attached at last + * if nobody cared. + */ + list_for_each_entry_reverse(coupler, ®ulator_coupler_list, list) { + err = coupler->attach_regulator(coupler, rdev); + if (!err) { + if (!coupler->balance_voltage && + rdev->coupling_desc.n_coupled > 2) + goto err_unsupported; + + return coupler; + } + + if (err < 0) + return ERR_PTR(err); + + if (err == 1) + continue; + + break; + } + + return ERR_PTR(-EINVAL); + +err_unsupported: + if (coupler->detach_regulator) + coupler->detach_regulator(coupler, rdev); + + rdev_err(rdev, + "Voltage balancing for multiple regulator couples is unimplemented\n"); + + return ERR_PTR(-EPERM); +} + static void regulator_resolve_coupling(struct regulator_dev *rdev) { + struct regulator_coupler *coupler = rdev->coupling_desc.coupler; struct coupling_desc *c_desc = &rdev->coupling_desc; int n_coupled = c_desc->n_coupled; struct regulator_dev *c_rdev; @@ -4724,6 +4841,12 @@ static void regulator_resolve_coupling(struct regulator_dev *rdev) if (!c_rdev) continue; + if (c_rdev->coupling_desc.coupler != coupler) { + rdev_err(rdev, "coupler mismatch with %s\n", + rdev_get_name(c_rdev)); |