From 61d0de0543a6e982918c6054a6a12cfbdd73018a Mon Sep 17 00:00:00 2001 From: Adam Ford Date: Tue, 30 Oct 2018 09:55:07 -0500 Subject: [PATCH 01/85] regulator: pfuze100-regulator: add coin support to PF0100 The driver currently supports coin cell / super cap charging, so this patch extends it to support PF0100. Signed-off-by: Adam Ford Reviewed-by: Fabio Estevam Signed-off-by: Mark Brown --- drivers/regulator/pfuze100-regulator.c | 2 ++ include/linux/regulator/pfuze100.h | 3 ++- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/drivers/regulator/pfuze100-regulator.c b/drivers/regulator/pfuze100-regulator.c index dd41a9bb3f5c62..df5df1c495adb8 100644 --- a/drivers/regulator/pfuze100-regulator.c +++ b/drivers/regulator/pfuze100-regulator.c @@ -370,6 +370,7 @@ static struct pfuze_regulator pfuze100_regulators[] = { PFUZE100_VGEN_REG(PFUZE100, VGEN4, PFUZE100_VGEN4VOL, 1800000, 3300000, 100000), PFUZE100_VGEN_REG(PFUZE100, VGEN5, PFUZE100_VGEN5VOL, 1800000, 3300000, 100000), PFUZE100_VGEN_REG(PFUZE100, VGEN6, PFUZE100_VGEN6VOL, 1800000, 3300000, 100000), + PFUZE100_COIN_REG(PFUZE100, COIN, PFUZE100_COINVOL, 0x7, pfuze100_coin), }; static struct pfuze_regulator pfuze200_regulators[] = { @@ -436,6 +437,7 @@ static struct of_regulator_match pfuze100_matches[] = { { .name = "vgen4", }, { .name = "vgen5", }, { .name = "vgen6", }, + { .name = "coin", }, }; /* PFUZE200 */ diff --git a/include/linux/regulator/pfuze100.h b/include/linux/regulator/pfuze100.h index cb5aecd40f07d9..331d7d940c7aad 100644 --- a/include/linux/regulator/pfuze100.h +++ b/include/linux/regulator/pfuze100.h @@ -33,7 +33,8 @@ #define PFUZE100_VGEN4 12 #define PFUZE100_VGEN5 13 #define PFUZE100_VGEN6 14 -#define PFUZE100_MAX_REGULATOR 15 +#define PFUZE100_COIN 15 +#define PFUZE100_MAX_REGULATOR 16 #define PFUZE200_SW1AB 0 #define PFUZE200_SW2 1 From de226ebda96cafa981b9c0fef56888931668b671 Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Sun, 28 Oct 2018 17:09:22 +0100 Subject: [PATCH 02/85] regulator: bd718x7: Remove double indirection for bd718xx_pmic_inits.rdatas MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit With gcc 4.1: drivers/regulator/bd718x7-regulator.c: In function ‘bd718xx_probe’: drivers/regulator/bd718x7-regulator.c:1020: warning: initialization from incompatible pointer type drivers/regulator/bd718x7-regulator.c:1024: warning: initialization from incompatible pointer type Apparently this old compiler can't handle the obscure double indirection. However, there is no need for a double indirection. Just store a pointer to the array instead, like other drivers tend to do. Fixes: 494edd266b945f36 ("regulator/mfd: Support ROHM BD71847 power management IC") Signed-off-by: Geert Uytterhoeven Signed-off-by: Mark Brown --- drivers/regulator/bd718x7-regulator.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/regulator/bd718x7-regulator.c b/drivers/regulator/bd718x7-regulator.c index 3a47e0372e77c8..fff5bc4faa2c99 100644 --- a/drivers/regulator/bd718x7-regulator.c +++ b/drivers/regulator/bd718x7-regulator.c @@ -1007,7 +1007,7 @@ static const struct bd718xx_regulator_data bd71837_regulators[] = { }; struct bd718xx_pmic_inits { - const struct bd718xx_regulator_data (*r_datas)[]; + const struct bd718xx_regulator_data *r_datas; unsigned int r_amount; }; @@ -1017,11 +1017,11 @@ static int bd718xx_probe(struct platform_device *pdev) struct regulator_config config = { 0 }; struct bd718xx_pmic_inits pmic_regulators[] = { [BD718XX_TYPE_BD71837] = { - .r_datas = &bd71837_regulators, + .r_datas = bd71837_regulators, .r_amount = ARRAY_SIZE(bd71837_regulators), }, [BD718XX_TYPE_BD71847] = { - .r_datas = &bd71847_regulators, + .r_datas = bd71847_regulators, .r_amount = ARRAY_SIZE(bd71847_regulators), }, }; @@ -1059,7 +1059,7 @@ static int bd718xx_probe(struct platform_device *pdev) struct regulator_dev *rdev; const struct bd718xx_regulator_data *r; - r = &(*pmic_regulators[mfd->chip_type].r_datas)[i]; + r = &pmic_regulators[mfd->chip_type].r_datas[i]; desc = &r->desc; config.dev = pdev->dev.parent; From fa2bb8b97d77b4b91658e448e7701d6a432e5b6f Mon Sep 17 00:00:00 2001 From: Charles Keepax Date: Thu, 8 Nov 2018 10:14:00 +0000 Subject: [PATCH 03/85] regulator: lochnagar: Explicitly include register headers Review of the MFD component has stated we should not include the register headers through lochnagar.h and thus removed them from that header. Explicitly include them in the end drivers manually. Signed-off-by: Charles Keepax Signed-off-by: Mark Brown --- drivers/regulator/lochnagar-regulator.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/regulator/lochnagar-regulator.c b/drivers/regulator/lochnagar-regulator.c index 2b5073b9ff868f..182198246479fa 100644 --- a/drivers/regulator/lochnagar-regulator.c +++ b/drivers/regulator/lochnagar-regulator.c @@ -20,6 +20,8 @@ #include #include +#include +#include static const struct regulator_ops lochnagar_micvdd_ops = { .enable = regulator_enable_regmap, From c054c6c792e165de2be51bc3feeaf3cebdedc8d6 Mon Sep 17 00:00:00 2001 From: Maciej Purski Date: Fri, 5 Oct 2018 18:36:28 +0300 Subject: [PATCH 04/85] regulator: core: Add voltage balancing mechanism On Odroid XU3/4 and other Exynos5422 based boards there is a case, that different devices on the board are supplied by different regulators with non-fixed voltages. If one of these devices temporarily requires higher voltage, there might occur a situation that the spread between two devices' voltages is so high, that there is a risk of changing 'high' and 'low' states on the interconnection between devices powered by those regulators. Introduce new function regulator_balance_voltage(), which keeps max_spread constraint fulfilled between a group of coupled regulators. It should be called if a regulator changes its voltage or after disabling or enabling. Disabled regulators should follow changes of the enabled ones, but their consumers' demands shouldn't be taken into account while calculating voltage of other coupled regulators. Find voltages, which are closest to suiting all the consumers' demands, while fulfilling max_spread constraint, keeping the following rules: - if one regulator is about to rise its voltage, rise others voltages in order to keep the max_spread - if a regulator, which has caused rising other regulators, is lowered, lower other regulators if possible - if one regulator is about to lower its voltage, but it hasn't caused rising other regulators, change its voltage so that it doesn't break the max_spread Change regulators' voltages step by step, keeping max_spread constraint fulfilled all the time. Function regulator_get_optimal_voltage() should find the best possible change for the regulator, which doesn't break max_spread constraint. In function regulator_balance_voltage() optimize number of steps by finding highest voltage difference on each iteration. If a regulator, which is about to change its voltage, is not coupled, method regulator_get_optimal_voltage() should simply return the lowest voltage fulfilling consumers' demands. Coupling should be checked only if the system is in PM_SUSPEND_ON state. Signed-off-by: Maciej Purski Signed-off-by: Dmitry Osipenko Signed-off-by: Mark Brown --- drivers/regulator/core.c | 229 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 229 insertions(+) diff --git a/drivers/regulator/core.c b/drivers/regulator/core.c index 2c66b528aedec0..a2a513780fd7f7 100644 --- a/drivers/regulator/core.c +++ b/drivers/regulator/core.c @@ -105,6 +105,8 @@ static int _notifier_call_chain(struct regulator_dev *rdev, unsigned long event, void *data); 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 struct regulator *create_regulator(struct regulator_dev *rdev, struct device *dev, const char *supply_name); @@ -3126,6 +3128,233 @@ static int regulator_set_voltage_unlocked(struct regulator *regulator, return ret; } +static int regulator_get_optimal_voltage(struct regulator_dev *rdev, + int *current_uV, + int *min_uV, int *max_uV, + suspend_state_t state, + int n_coupled) +{ + 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; + bool done; + + *current_uV = -1; + + /* + * If there are no coupled regulators, simply set the voltage + * demanded by consumers. + */ + if (n_coupled == 1) { + /* + * If consumers don't provide any demands, set voltage + * to min_uV + */ + desired_min_uV = constraints->min_uV; + desired_max_uV = constraints->max_uV; + + ret = regulator_check_consumers(rdev, + &desired_min_uV, + &desired_max_uV, state); + if (ret < 0) + return ret; + + possible_uV = desired_min_uV; + done = true; + + goto finish; + } + + /* Find highest min desired voltage */ + for (i = 0; i < n_coupled; i++) { + int tmp_min = 0; + int tmp_max = INT_MAX; + + lockdep_assert_held_once(&c_rdevs[i]->mutex); + + ret = regulator_check_consumers(c_rdevs[i], + &tmp_min, + &tmp_max, state); + if (ret < 0) + return ret; + + ret = regulator_check_voltage(c_rdevs[i], &tmp_min, &tmp_max); + if (ret < 0) + return ret; + + highest_min_uV = max(highest_min_uV, tmp_min); + + if (i == 0) { + desired_min_uV = tmp_min; + desired_max_uV = tmp_max; + } + } + + /* + * Let target_uV be equal to the desired one if possible. + * If not, set it to minimum voltage, allowed by other coupled + * regulators. + */ + target_uV = max(desired_min_uV, highest_min_uV - max_spread); + + /* + * Find min and max voltages, which currently aren't violating + * max_spread. + */ + for (i = 1; i < n_coupled; i++) { + int tmp_act; + + if (!_regulator_is_enabled(c_rdevs[i])) + continue; + + tmp_act = _regulator_get_voltage(c_rdevs[i]); + if (tmp_act < 0) + return tmp_act; + + min_current_uV = min(tmp_act, min_current_uV); + max_current_uV = max(tmp_act, max_current_uV); + } + + /* There aren't any other regulators enabled */ + if (max_current_uV == 0) { + possible_uV = target_uV; + } else { + /* + * Correct target voltage, so as it currently isn't + * violating max_spread + */ + possible_uV = max(target_uV, max_current_uV - max_spread); + possible_uV = min(possible_uV, min_current_uV + max_spread); + } + + if (possible_uV > desired_max_uV) + return -EINVAL; + + done = (possible_uV == target_uV); + desired_min_uV = possible_uV; + +finish: + /* Set current_uV if wasn't done earlier in the code and if necessary */ + if (n_coupled > 1 && *current_uV == -1) { + + if (_regulator_is_enabled(rdev)) { + ret = _regulator_get_voltage(rdev); + if (ret < 0) + return ret; + + *current_uV = ret; + } else { + *current_uV = desired_min_uV; + } + } + + *min_uV = desired_min_uV; + *max_uV = desired_max_uV; + + return done; +} + +static int regulator_balance_voltage(struct regulator_dev *rdev, + suspend_state_t state) +{ + struct regulator_dev **c_rdevs; + struct regulator_dev *best_rdev; + struct coupling_desc *c_desc = &rdev->coupling_desc; + 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; + + c_rdevs = c_desc->coupled_rdevs; + n_coupled = c_desc->n_coupled; + + /* + * If system is in a state other than PM_SUSPEND_ON, don't check + * other coupled regulators. + */ + if (state != PM_SUSPEND_ON) + n_coupled = 1; + + if (c_desc->n_resolved < n_coupled) { + rdev_err(rdev, "Not all coupled regulators registered\n"); + return -EPERM; + } + + for (i = 0; i < n_coupled; i++) + c_rdev_done[i] = false; + + /* + * Find the best possible voltage change on each loop. Leave the loop + * if there isn't any possible change. + */ + do { + best_c_rdev_done = false; + best_delta = 0; + best_min_uV = 0; + best_max_uV = 0; + best_c_rdev = 0; + best_rdev = NULL; + + /* + * Find highest difference between optimal voltage + * and current voltage. + */ + for (i = 0; i < n_coupled; i++) { + /* + * optimal_uV is the best voltage that can be set for + * i-th regulator at the moment without violating + * max_spread constraint in order to balance + * the coupled voltages. + */ + int optimal_uV = 0, optimal_max_uV = 0, current_uV = 0; + + if (c_rdev_done[i]) + continue; + + ret = regulator_get_optimal_voltage(c_rdevs[i], + ¤t_uV, + &optimal_uV, + &optimal_max_uV, + state, n_coupled); + if (ret < 0) + goto out; + + delta = abs(optimal_uV - current_uV); + + if (delta && best_delta <= delta) { + best_c_rdev_done = ret; + best_delta = delta; + best_rdev = c_rdevs[i]; + best_min_uV = optimal_uV; + best_max_uV = optimal_max_uV; + best_c_rdev = i; + } + } + + /* Nothing to change, return successfully */ + if (!best_rdev) { + ret = 0; + goto out; + } +#if 0 + ret = regulator_set_voltage_rdev(best_rdev, best_min_uV, + best_max_uV, state); +#endif + if (ret < 0) + goto out; + + c_rdev_done[best_c_rdev] = best_c_rdev_done; + + } while (n_coupled > 1); + +out: + return ret; +} + /** * regulator_set_voltage - set regulator output voltage * @regulator: regulator source From 9243a195be7af96a9b69990e9313c4ba1c5fbeca Mon Sep 17 00:00:00 2001 From: Maciej Purski Date: Fri, 5 Oct 2018 18:36:29 +0300 Subject: [PATCH 05/85] regulator: core: Change voltage setting path On Odroid XU3/4 and other Exynos5422 based boards there is a case, that different devices on the board are supplied by different regulators with non-fixed voltages. If one of these devices temporarily requires higher voltage, there might occur a situation that the spread between two devices' voltages is so high, that there is a risk of changing 'high' and 'low' states on the interconnection between devices powered by those regulators. Uncoupled regulators should be a special case of coupled regulators, so they should share a common voltage setting path. When enabling, disabling or setting voltage of a coupled regulator, all coupled regulators should be locked. Regulator's supplies should be locked, when setting voltage of a single regulator. Enabling a coupled regulator or setting its voltage should not be possible if some of its coupled regulators, has not been registered. Add function for locking coupled regulators and supplies. Extract a new function regulator_set_voltage_rdev() from regulator_set_voltage_unlocked(), which is called when setting voltage of a single regulator. Signed-off-by: Maciej Purski Signed-off-by: Dmitry Osipenko Signed-off-by: Mark Brown --- drivers/regulator/core.c | 145 ++++++++++++++++++++++++++------------- 1 file changed, 99 insertions(+), 46 deletions(-) diff --git a/drivers/regulator/core.c b/drivers/regulator/core.c index a2a513780fd7f7..5105eaaf3cefda 100644 --- a/drivers/regulator/core.c +++ b/drivers/regulator/core.c @@ -107,6 +107,9 @@ 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); @@ -198,37 +201,66 @@ static void regulator_unlock(struct regulator_dev *rdev) } } -/** - * regulator_lock_supply - lock a regulator and its supplies - * @rdev: regulator source - */ -static void regulator_lock_supply(struct regulator_dev *rdev) +static int regulator_lock_recursive(struct regulator_dev *rdev, + unsigned int subclass) { + struct regulator_dev *c_rdev; int i; - for (i = 0; rdev; rdev = rdev_get_supply(rdev), i++) - regulator_lock_nested(rdev, i); + for (i = 0; i < rdev->coupling_desc.n_coupled; i++) { + c_rdev = rdev->coupling_desc.coupled_rdevs[i]; + + if (!c_rdev) + continue; + + regulator_lock_nested(c_rdev, subclass++); + + if (c_rdev->supply) + subclass = + regulator_lock_recursive(c_rdev->supply->rdev, + subclass); + } + + return subclass; } /** - * regulator_unlock_supply - unlock a regulator and its supplies - * @rdev: regulator source + * regulator_unlock_dependent - unlock regulator's suppliers and coupled + * regulators + * @rdev: regulator source + * + * Unlock all regulators related with rdev by coupling or suppling. */ -static void regulator_unlock_supply(struct regulator_dev *rdev) +static void regulator_unlock_dependent(struct regulator_dev *rdev) { - struct regulator *supply; + struct regulator_dev *c_rdev; + int i; - while (1) { - regulator_unlock(rdev); - supply = rdev->supply; + for (i = 0; i < rdev->coupling_desc.n_coupled; i++) { + c_rdev = rdev->coupling_desc.coupled_rdevs[i]; - if (!rdev->supply) - return; + if (!c_rdev) + continue; - rdev = supply->rdev; + regulator_unlock(c_rdev); + + if (c_rdev->supply) + regulator_unlock_dependent(c_rdev->supply->rdev); } } +/** + * regulator_lock_dependent - lock regulator's suppliers and coupled regulators + * @rdev: regulator source + * + * This function as a wrapper on regulator_lock_recursive(), which locks + * all regulators related with rdev by coupling or suppling. + */ +static inline void regulator_lock_dependent(struct regulator_dev *rdev) +{ + regulator_lock_recursive(rdev, 0); +} + /** * of_get_regulator - get a regulator device node based on supply name * @dev: Device pointer for the consumer (of regulator) device @@ -2289,9 +2321,16 @@ int regulator_enable(struct regulator *regulator) return ret; } - mutex_lock(&rdev->mutex); + regulator_lock_dependent(rdev); + /* balance only if there are regulators coupled */ + if (rdev->coupling_desc.n_coupled > 1) { + ret = regulator_balance_voltage(rdev, PM_SUSPEND_ON); + if (ret != 0) + goto unlock; + } ret = _regulator_enable(rdev); - mutex_unlock(&rdev->mutex); +unlock: + regulator_unlock_dependent(rdev); if (ret != 0 && rdev->supply) regulator_disable(rdev->supply); @@ -2397,9 +2436,11 @@ int regulator_disable(struct regulator *regulator) if (regulator->always_on) return 0; - mutex_lock(&rdev->mutex); + regulator_lock_dependent(rdev); ret = _regulator_disable(rdev); - mutex_unlock(&rdev->mutex); + if (rdev->coupling_desc.n_coupled > 1) + regulator_balance_voltage(rdev, PM_SUSPEND_ON); + regulator_unlock_dependent(rdev); if (ret == 0 && rdev->supply) regulator_disable(rdev->supply); @@ -2448,10 +2489,12 @@ int regulator_force_disable(struct regulator *regulator) struct regulator_dev *rdev = regulator->rdev; int ret; - mutex_lock(&rdev->mutex); + regulator_lock_dependent(rdev); regulator->uA_load = 0; ret = _regulator_force_disable(regulator->rdev); - mutex_unlock(&rdev->mutex); + if (rdev->coupling_desc.n_coupled > 1) + regulator_balance_voltage(rdev, PM_SUSPEND_ON); + regulator_unlock_dependent(rdev); if (rdev->supply) while (rdev->open_count--) @@ -2599,9 +2642,9 @@ int regulator_is_enabled(struct regulator *regulator) if (regulator->always_on) return 1; - mutex_lock(®ulator->rdev->mutex); + regulator_lock_dependent(regulator->rdev); ret = _regulator_is_enabled(regulator->rdev); - mutex_unlock(®ulator->rdev->mutex); + regulator_unlock_dependent(regulator->rdev); return ret; } @@ -3015,8 +3058,6 @@ static int regulator_set_voltage_unlocked(struct regulator *regulator, int ret = 0; int old_min_uV, old_max_uV; int current_uV; - int best_supply_uV = 0; - int supply_change_uV = 0; /* If we're setting the same range as last time the change * should be a noop (some cpufreq implementations use the same @@ -3056,10 +3097,27 @@ static int regulator_set_voltage_unlocked(struct regulator *regulator, voltage->min_uV = min_uV; voltage->max_uV = max_uV; - ret = regulator_check_consumers(rdev, &min_uV, &max_uV, state); + /* for not coupled regulators this will just set the voltage */ + ret = regulator_balance_voltage(rdev, state); if (ret < 0) goto out2; +out: + return 0; +out2: + voltage->min_uV = old_min_uV; + voltage->max_uV = old_max_uV; + + return ret; +} + +static 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; + int ret; + if (rdev->supply && regulator_ops_is_valid(rdev->supply->rdev, REGULATOR_CHANGE_VOLTAGE) && @@ -3071,13 +3129,13 @@ static int regulator_set_voltage_unlocked(struct regulator *regulator, selector = regulator_map_voltage(rdev, min_uV, max_uV); if (selector < 0) { ret = selector; - goto out2; + goto out; } best_supply_uV = _regulator_list_voltage(rdev, selector, 0); if (best_supply_uV < 0) { ret = best_supply_uV; - goto out2; + goto out; } best_supply_uV += rdev->desc->min_dropout_uV; @@ -3085,7 +3143,7 @@ static int regulator_set_voltage_unlocked(struct regulator *regulator, current_supply_uV = _regulator_get_voltage(rdev->supply->rdev); if (current_supply_uV < 0) { ret = current_supply_uV; - goto out2; + goto out; } supply_change_uV = best_supply_uV - current_supply_uV; @@ -3097,7 +3155,7 @@ static int regulator_set_voltage_unlocked(struct regulator *regulator, if (ret) { dev_err(&rdev->dev, "Failed to increase supply voltage: %d\n", ret); - goto out2; + goto out; } } @@ -3107,7 +3165,7 @@ static int regulator_set_voltage_unlocked(struct regulator *regulator, ret = _regulator_do_set_suspend_voltage(rdev, min_uV, max_uV, state); if (ret < 0) - goto out2; + goto out; if (supply_change_uV < 0) { ret = regulator_set_voltage_unlocked(rdev->supply, @@ -3120,11 +3178,6 @@ static int regulator_set_voltage_unlocked(struct regulator *regulator, } out: - return ret; -out2: - voltage->min_uV = old_min_uV; - voltage->max_uV = old_max_uV; - return ret; } @@ -3340,10 +3393,10 @@ static int regulator_balance_voltage(struct regulator_dev *rdev, ret = 0; goto out; } -#if 0 + ret = regulator_set_voltage_rdev(best_rdev, best_min_uV, best_max_uV, state); -#endif + if (ret < 0) goto out; @@ -3377,12 +3430,12 @@ int regulator_set_voltage(struct regulator *regulator, int min_uV, int max_uV) { int ret = 0; - regulator_lock_supply(regulator->rdev); + regulator_lock_dependent(regulator->rdev); ret = regulator_set_voltage_unlocked(regulator, min_uV, max_uV, PM_SUSPEND_ON); - regulator_unlock_supply(regulator->rdev); + regulator_unlock_dependent(regulator->rdev); return ret; } @@ -3460,12 +3513,12 @@ int regulator_set_suspend_voltage(struct regulator *regulator, int min_uV, if (regulator_check_states(state) || state == PM_SUSPEND_ON) return -EINVAL; - regulator_lock_supply(regulator->rdev); + regulator_lock_dependent(regulator->rdev); ret = _regulator_set_suspend_voltage(regulator, min_uV, max_uV, state); - regulator_unlock_supply(regulator->rdev); + regulator_unlock_dependent(regulator->rdev); return ret; } @@ -3657,11 +3710,11 @@ int regulator_get_voltage(struct regulator *regulator) { int ret; - regulator_lock_supply(regulator->rdev); + regulator_lock_dependent(regulator->rdev); ret = _regulator_get_voltage(regulator->rdev); - regulator_unlock_supply(regulator->rdev); + regulator_unlock_dependent(regulator->rdev); return ret; } From f9503385b1877ac34514cd5ea7683eee61c4bec3 Mon Sep 17 00:00:00 2001 From: Dmitry Osipenko Date: Fri, 5 Oct 2018 18:36:30 +0300 Subject: [PATCH 06/85] regulator: core: Mutually resolve regulators coupling If registered regulator found a couple, then the couple can find the registered regulator too and hence coupling can be mutually resolved at the registration time. Signed-off-by: Dmitry Osipenko Signed-off-by: Mark Brown --- drivers/regulator/core.c | 54 +++++++++++++--------------------------- 1 file changed, 17 insertions(+), 37 deletions(-) diff --git a/drivers/regulator/core.c b/drivers/regulator/core.c index 5105eaaf3cefda..925df9e6f1e373 100644 --- a/drivers/regulator/core.c +++ b/drivers/regulator/core.c @@ -4439,7 +4439,7 @@ static int regulator_register_resolve_supply(struct device *dev, void *data) return 0; } -static int regulator_fill_coupling_array(struct regulator_dev *rdev) +static void regulator_resolve_coupling(struct regulator_dev *rdev) { struct coupling_desc *c_desc = &rdev->coupling_desc; int n_coupled = c_desc->n_coupled; @@ -4453,33 +4453,21 @@ static int regulator_fill_coupling_array(struct regulator_dev *rdev) c_rdev = of_parse_coupled_regulator(rdev, i - 1); - if (c_rdev) { - c_desc->coupled_rdevs[i] = c_rdev; - c_desc->n_resolved++; - } - } - - if (rdev->coupling_desc.n_resolved < n_coupled) - return -1; - else - return 0; -} + if (!c_rdev) + continue; -static int regulator_register_fill_coupling_array(struct device *dev, - void *data) -{ - struct regulator_dev *rdev = dev_to_rdev(dev); + regulator_lock(c_rdev); - if (!IS_ENABLED(CONFIG_OF)) - return 0; + c_desc->coupled_rdevs[i] = c_rdev; + c_desc->n_resolved++; - if (regulator_fill_coupling_array(rdev)) - rdev_dbg(rdev, "unable to resolve coupling\n"); + regulator_unlock(c_rdev); - return 0; + regulator_resolve_coupling(c_rdev); + } } -static int regulator_resolve_coupling(struct regulator_dev *rdev) +static int regulator_init_coupling(struct regulator_dev *rdev) { int n_phandles; @@ -4519,13 +4507,6 @@ static int regulator_resolve_coupling(struct regulator_dev *rdev) if (!of_check_coupling_data(rdev)) return -EPERM; - /* - * After everything has been checked, try to fill rdevs array - * with pointers to regulators parsed from device tree. If some - * regulators are not registered yet, retry in late init call - */ - regulator_fill_coupling_array(rdev); - return 0; } @@ -4662,11 +4643,8 @@ regulator_register(const struct regulator_desc *regulator_desc, if (ret < 0) goto wash; - mutex_lock(®ulator_list_mutex); - ret = regulator_resolve_coupling(rdev); - mutex_unlock(®ulator_list_mutex); - - if (ret != 0) + ret = regulator_init_coupling(rdev); + if (ret < 0) goto wash; /* add consumers devices */ @@ -4700,6 +4678,11 @@ regulator_register(const struct regulator_desc *regulator_desc, rdev_init_debugfs(rdev); + /* try to resolve regulators coupling since a new one was registered */ + mutex_lock(®ulator_list_mutex); + regulator_resolve_coupling(rdev); + mutex_unlock(®ulator_list_mutex); + /* try to resolve regulators supply since a new one was registered */ class_for_each_device(®ulator_class, NULL, NULL, regulator_register_resolve_supply); @@ -5155,9 +5138,6 @@ static int __init regulator_init_complete(void) class_for_each_device(®ulator_class, NULL, NULL, regulator_late_cleanup); - class_for_each_device(®ulator_class, NULL, NULL, - regulator_register_fill_coupling_array); - return 0; } late_initcall_sync(regulator_init_complete); From 79d6f049f4f05f5cb6e1647a9a253dad14bfabbe Mon Sep 17 00:00:00 2001 From: Dmitry Osipenko Date: Fri, 5 Oct 2018 18:36:31 +0300 Subject: [PATCH 07/85] regulator: core: Don't allow to get regulator until all couples resolved Don't allow to get regulator until all of its couples resolved because consumer will get EPERM and coupling shall be transparent for the drivers. Signed-off-by: Dmitry Osipenko Signed-off-by: Mark Brown --- drivers/regulator/core.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/drivers/regulator/core.c b/drivers/regulator/core.c index 925df9e6f1e373..089e8ad8ef57ec 100644 --- a/drivers/regulator/core.c +++ b/drivers/regulator/core.c @@ -1747,6 +1747,16 @@ struct regulator *_regulator_get(struct device *dev, const char *id, return regulator; } + mutex_lock(®ulator_list_mutex); + ret = (rdev->coupling_desc.n_resolved != rdev->coupling_desc.n_coupled); + mutex_unlock(®ulator_list_mutex); + + if (ret != 0) { + regulator = ERR_PTR(-EPROBE_DEFER); + put_device(&rdev->dev); + return regulator; + } + ret = regulator_resolve_supply(rdev); if (ret < 0) { regulator = ERR_PTR(ret); From d3b687c58ad9343532b4a20e541934eb1b557453 Mon Sep 17 00:00:00 2001 From: Dmitry Osipenko Date: Fri, 5 Oct 2018 18:36:32 +0300 Subject: [PATCH 08/85] regulator: Change regulator-coupled-max-spread property Redefine binding for regulator-coupled-max-spread property in a way that max-spread values are defined per regulator couple instead of defining single max-spread for the whole group of coupled regulators. With that change the following regulators coupling configuration will be possible: regA: regulatorA { regulator-coupled-with = <®B ®C>; regulator-coupled-max-spread = <100000 300000>; }; regB: regulatorB { regulator-coupled-with = <®A ®C>; regulator-coupled-max-spread = <100000 200000>; }; regC: regulatorC { regulator-coupled-with = <®A ®B>; regulator-coupled-max-spread = <300000 200000>; }; Signed-off-by: Dmitry Osipenko Signed-off-by: Mark Brown --- Documentation/devicetree/bindings/regulator/regulator.txt | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/Documentation/devicetree/bindings/regulator/regulator.txt b/Documentation/devicetree/bindings/regulator/regulator.txt index a7cd36877bfe04..9b525b657fca29 100644 --- a/Documentation/devicetree/bindings/regulator/regulator.txt +++ b/Documentation/devicetree/bindings/regulator/regulator.txt @@ -76,8 +76,9 @@ Optional properties: - regulator-coupled-with: Regulators with which the regulator is coupled. The linkage is 2-way - all coupled regulators should be linked with each other. A regulator should not be coupled with its supplier. -- regulator-coupled-max-spread: Max spread between voltages of coupled regulators - in microvolts. +- regulator-coupled-max-spread: Array of maximum spread between voltages of + coupled regulators in microvolts, each value in the array relates to the + corresponding couple specified by the regulator-coupled-with property. Deprecated properties: - regulator-compatible: If a regulator chip contains multiple From 40c223efaa17e9bc3d964ee285967ebbe09c3e12 Mon Sep 17 00:00:00 2001 From: Dmitry Osipenko Date: Fri, 5 Oct 2018 18:36:33 +0300 Subject: [PATCH 09/85] regulator: core: Limit regulators coupling to a single couple Device tree binding was changed in a way that now max-spread values must be defied per regulator pair. Limit number of pairs in order to adapt to the new binding without changing regulators code. Signed-off-by: Dmitry Osipenko Signed-off-by: Mark Brown --- include/linux/regulator/driver.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/linux/regulator/driver.h b/include/linux/regulator/driver.h index a9c030192147df..a05d37d0efa14d 100644 --- a/include/linux/regulator/driver.h +++ b/include/linux/regulator/driver.h @@ -15,7 +15,7 @@ #ifndef __LINUX_REGULATOR_DRIVER_H_ #define __LINUX_REGULATOR_DRIVER_H_ -#define MAX_COUPLED 4 +#define MAX_COUPLED 2 #include #include From a2dfe7c7dfb65c814cfa2777b46e687db1820cc4 Mon Sep 17 00:00:00 2001 From: Dmitry Osipenko Date: Fri, 5 Oct 2018 18:36:34 +0300 Subject: [PATCH 10/85] regulator: Document new regulator-max-step-microvolt property Certain hardware may require supply voltage to be changed in steps. Define new property that allow to describe such hardware. Signed-off-by: Dmitry Osipenko Reviewed-by: Rob Herring Signed-off-by: Mark Brown --- Documentation/devicetree/bindings/regulator/regulator.txt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Documentation/devicetree/bindings/regulator/regulator.txt b/Documentation/devicetree/bindings/regulator/regulator.txt index 9b525b657fca29..0c3a243c95df8f 100644 --- a/Documentation/devicetree/bindings/regulator/regulator.txt +++ b/Documentation/devicetree/bindings/regulator/regulator.txt @@ -79,6 +79,8 @@ Optional properties: - regulator-coupled-max-spread: Array of maximum spread between voltages of coupled regulators in microvolts, each value in the array relates to the corresponding couple specified by the regulator-coupled-with property. +- regulator-max-step-microvolt: Maximum difference between current and target + voltages that can be changed safely in a single step. Deprecated properties: - regulator-compatible: If a regulator chip contains multiple From 85254bcf394f93a8955814da1eef4d477b63eb84 Mon Sep 17 00:00:00 2001 From: Dmitry Osipenko Date: Fri, 5 Oct 2018 18:36:35 +0300 Subject: [PATCH 11/85] regulator: core: Add new max_uV_step constraint On NVIDIA Tegra30 there is a requirement for regulator "A" to have voltage higher than voltage of regulator "B" by N microvolts, the N value changes depending on the voltage of regulator "B". This is similar to min-spread between voltages of regulators, the difference is that the spread value isn't fixed. This means that extra carefulness is required for regulator "A" to drop its voltage without violating the requirement, hence its voltage should be changed in steps so that its couple "B" could follow (there is also max-spread requirement). Add new "max_uV_step" constraint that breaks voltage change into several steps, each step is limited by the max_uV_step value. Signed-off-by: Dmitry Osipenko Signed-off-by: Mark Brown --- drivers/regulator/core.c | 41 +++++++++++++++++++++++++++++++ drivers/regulator/of_regulator.c | 4 +++ include/linux/regulator/machine.h | 3 +++ 3 files changed, 48 insertions(+) diff --git a/drivers/regulator/core.c b/drivers/regulator/core.c index 089e8ad8ef57ec..ba03bdf3716f66 100644 --- a/drivers/regulator/core.c +++ b/drivers/regulator/core.c @@ -3191,6 +3191,36 @@ static int regulator_set_voltage_rdev(struct regulator_dev *rdev, int min_uV, return ret; } +static int regulator_limit_voltage_step(struct regulator_dev *rdev, + int *current_uV, int *min_uV) +{ + struct regulation_constraints *constraints = rdev->constraints; + + /* Limit voltage change only if necessary */ + if (!constraints->max_uV_step || !_regulator_is_enabled(rdev)) + return 1; + + if (*current_uV < 0) { + *current_uV = _regulator_get_voltage(rdev); + + if (*current_uV < 0) + return *current_uV; + } + + if (abs(*current_uV - *min_uV) <= constraints->max_uV_step) + return 1; + + /* Clamp target voltage within the given step */ + if (*current_uV < *min_uV) + *min_uV = min(*current_uV + constraints->max_uV_step, + *min_uV); + else + *min_uV = max(*current_uV - constraints->max_uV_step, + *min_uV); + + return 0; +} + static int regulator_get_optimal_voltage(struct regulator_dev *rdev, int *current_uV, int *min_uV, int *max_uV, @@ -3302,6 +3332,17 @@ static int regulator_get_optimal_voltage(struct regulator_dev *rdev, desired_min_uV = possible_uV; finish: + /* Apply max_uV_step constraint if necessary */ + if (state == PM_SUSPEND_ON) { + ret = regulator_limit_voltage_step(rdev, current_uV, + &desired_min_uV); + if (ret < 0) + return ret; + + if (ret == 0) + done = false; + } + /* Set current_uV if wasn't done earlier in the code and if necessary */ if (n_coupled > 1 && *current_uV == -1) { diff --git a/drivers/regulator/of_regulator.c b/drivers/regulator/of_regulator.c index c4223b3e0dffa6..a732f09d207bb0 100644 --- a/drivers/regulator/of_regulator.c +++ b/drivers/regulator/of_regulator.c @@ -170,6 +170,10 @@ static void of_get_regulation_constraints(struct device_node *np, &pval)) constraints->max_spread = pval; + if (!of_property_read_u32(np, "regulator-max-step-microvolt", + &pval)) + constraints->max_uV_step = pval; + constraints->over_current_protection = of_property_read_bool(np, "regulator-over-current-protection"); diff --git a/include/linux/regulator/machine.h b/include/linux/regulator/machine.h index a459a5e973a729..1d34a70ffda27e 100644 --- a/include/linux/regulator/machine.h +++ b/include/linux/regulator/machine.h @@ -158,6 +158,9 @@ struct regulation_constraints { /* used for coupled regulators */ int max_spread; + /* used for changing voltage in steps */ + int max_uV_step; + /* valid regulator operating modes for this machine */ unsigned int valid_modes_mask; From 6303f3e78b6fd3f84dbe7f2fa4f108d47061d1fb Mon Sep 17 00:00:00 2001 From: Dmitry Osipenko Date: Fri, 5 Oct 2018 18:36:36 +0300 Subject: [PATCH 12/85] regulator: core: Decouple regulators on regulator_unregister() Regulators shall be uncoupled if one of the couples disappear. Signed-off-by: Dmitry Osipenko Signed-off-by: Mark Brown --- drivers/regulator/core.c | 38 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) diff --git a/drivers/regulator/core.c b/drivers/regulator/core.c index ba03bdf3716f66..783ec9c7410465 100644 --- a/drivers/regulator/core.c +++ b/drivers/regulator/core.c @@ -4518,6 +4518,43 @@ static void regulator_resolve_coupling(struct regulator_dev *rdev) } } +static void regulator_remove_coupling(struct regulator_dev *rdev) +{ + struct coupling_desc *__c_desc, *c_desc = &rdev->coupling_desc; + struct regulator_dev *__c_rdev, *c_rdev; + unsigned int __n_coupled, n_coupled; + int i, k; + + n_coupled = c_desc->n_coupled; + + for (i = 1; i < n_coupled; i++) { + c_rdev = c_desc->coupled_rdevs[i]; + + if (!c_rdev) + continue; + + regulator_lock(c_rdev); + + __c_desc = &c_rdev->coupling_desc; + __n_coupled = __c_desc->n_coupled; + + for (k = 1; k < __n_coupled; k++) { + __c_rdev = __c_desc->coupled_rdevs[k]; + + if (__c_rdev == rdev) { + __c_desc->coupled_rdevs[k] = NULL; + __c_desc->n_resolved--; + break; + } + } + + regulator_unlock(c_rdev); + + c_desc->coupled_rdevs[i] = NULL; + c_desc->n_resolved--; + } +} + static int regulator_init_coupling(struct regulator_dev *rdev) { int n_phandles; @@ -4776,6 +4813,7 @@ void regulator_unregister(struct regulator_dev *rdev) debugfs_remove_recursive(rdev->debugfs); flush_work(&rdev->disable_work.work); WARN_ON(rdev->open_count); + regulator_remove_coupling(rdev); unset_regulator_supplies(rdev); list_del(&rdev->list); regulator_ena_gpio_free(rdev); From 5279e96ff8033500b6008be5925ae2d20f42c434 Mon Sep 17 00:00:00 2001 From: "Andrei.Stefanescu@microchip.com" Date: Tue, 13 Nov 2018 14:47:33 +0000 Subject: [PATCH 13/85] regulator: dt-bindings: add regulator-state-standby bindings For state-mem and state-disk regulators can have various parameters applied such as enabled/disabled, current mode, voltage etc. This patch adds documentation on how to set these parameters in the device tree for the standby state. Signed-off-by: Andrei Stefanescu Signed-off-by: Mark Brown --- Documentation/devicetree/bindings/regulator/regulator.txt | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/Documentation/devicetree/bindings/regulator/regulator.txt b/Documentation/devicetree/bindings/regulator/regulator.txt index a7cd36877bfe04..ca96f70bd2def2 100644 --- a/Documentation/devicetree/bindings/regulator/regulator.txt +++ b/Documentation/devicetree/bindings/regulator/regulator.txt @@ -33,13 +33,16 @@ Optional properties: decreases of any level. This is useful for regulators with exponential voltage changes. - regulator-soft-start: Enable soft start so that voltage ramps slowly +- regulator-state-standby sub-root node for Standby mode + : equivalent with standby Linux sleep state, which provides energy savings + with a relatively quick transition back time. - regulator-state-mem sub-root node for Suspend-to-RAM mode : suspend to memory, the device goes to sleep, but all data stored in memory, only some external interrupt can wake the device. - regulator-state-disk sub-root node for Suspend-to-DISK mode : suspend to disk, this state operates similarly to Suspend-to-RAM, but includes a final step of writing memory contents to disk. -- regulator-state-[mem/disk] node has following common properties: +- regulator-state-[mem/disk/standby] node has following common properties: - regulator-on-in-suspend: regulator should be on in suspend state. - regulator-off-in-suspend: regulator should be off in suspend state. - regulator-suspend-min-microvolt: minimum voltage may be set in From f2b4076988a9c229dab373470b4b108ef0e106c8 Mon Sep 17 00:00:00 2001 From: "Andrei.Stefanescu@microchip.com" Date: Tue, 13 Nov 2018 14:47:37 +0000 Subject: [PATCH 14/85] regulator: of: add support for parsing regulator-state-standby Set the according constraints for PM_SUSPEND_STANDBY case. Previously, only suspend to mem/disk were taken into consideration. Signed-off-by: Andrei Stefanescu Signed-off-by: Mark Brown --- drivers/regulator/of_regulator.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/drivers/regulator/of_regulator.c b/drivers/regulator/of_regulator.c index c4223b3e0dffa6..82fb1228ca2c47 100644 --- a/drivers/regulator/of_regulator.c +++ b/drivers/regulator/of_regulator.c @@ -20,6 +20,7 @@ #include "internal.h" static const char *const regulator_states[PM_SUSPEND_MAX + 1] = { + [PM_SUSPEND_STANDBY] = "regulator-state-standby", [PM_SUSPEND_MEM] = "regulator-state-mem", [PM_SUSPEND_MAX] = "regulator-state-disk", }; @@ -181,9 +182,11 @@ static void of_get_regulation_constraints(struct device_node *np, case PM_SUSPEND_MAX: suspend_state = &constraints->state_disk; break; + case PM_SUSPEND_STANDBY: + suspend_state = &constraints->state_standby; + break; case PM_SUSPEND_ON: case PM_SUSPEND_TO_IDLE: - case PM_SUSPEND_STANDBY: default: continue; } From 2e61286dc6ce10ebce17825cd61031c5fd0289db Mon Sep 17 00:00:00 2001 From: Axel Lin Date: Sat, 10 Nov 2018 11:50:03 +0800 Subject: [PATCH 15/85] regulator: bd718x7: Use regulator_map_voltage_ascend for buck5 and buck7 The voltages in bd718xx_3rd_nodvs_buck_volts are in ascendant order, so use regulator_map_voltage_ascend. Signed-off-by: Axel Lin Signed-off-by: Mark Brown --- drivers/regulator/bd718x7-regulator.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/regulator/bd718x7-regulator.c b/drivers/regulator/bd718x7-regulator.c index fff5bc4faa2c99..512c936ae021eb 100644 --- a/drivers/regulator/bd718x7-regulator.c +++ b/drivers/regulator/bd718x7-regulator.c @@ -130,6 +130,7 @@ static struct regulator_ops bd718xx_buck_regulator_nolinear_ops = { .disable = regulator_disable_regmap, .is_enabled = regulator_is_enabled_regmap, .list_voltage = regulator_list_voltage_table, + .map_voltage = regulator_map_voltage_ascend, .set_voltage_sel = bd718xx_set_voltage_sel_restricted, .get_voltage_sel = regulator_get_voltage_sel_regmap, .set_voltage_time_sel = regulator_set_voltage_time_sel, From 49f539518b333ee9da27cf2ddd5345484a13cc22 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Thu, 8 Nov 2018 06:44:19 +0000 Subject: [PATCH 16/85] regulator: bd9571mwv: convert to SPDX identifiers This patch updates license to use SPDX-License-Identifier instead of verbose license text. Signed-off-by: Kuninori Morimoto Signed-off-by: Mark Brown --- drivers/regulator/bd9571mwv-regulator.c | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/drivers/regulator/bd9571mwv-regulator.c b/drivers/regulator/bd9571mwv-regulator.c index 274c5ed7cd737e..e12dd1f750f349 100644 --- a/drivers/regulator/bd9571mwv-regulator.c +++ b/drivers/regulator/bd9571mwv-regulator.c @@ -1,17 +1,9 @@ +// SPDX-License-Identifier: GPL-2.0 /* * ROHM BD9571MWV-M regulator driver * * Copyright (C) 2017 Marek Vasut * - * 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. - * - * This program is distributed "as is" WITHOUT ANY WARRANTY of any - * kind, whether expressed or implied; without even the implied warranty - * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License version 2 for more details. - * * Based on the TPS65086 driver * * NOTE: VD09 is missing From ef4de050b4c796ef13421032403ad986c00423d3 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Thu, 8 Nov 2018 06:45:01 +0000 Subject: [PATCH 17/85] regulator: as3711: convert to SPDX identifiers This patch updates license to use SPDX-License-Identifier instead of verbose license text. Signed-off-by: Kuninori Morimoto Signed-off-by: Mark Brown --- drivers/regulator/as3711-regulator.c | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/drivers/regulator/as3711-regulator.c b/drivers/regulator/as3711-regulator.c index 565a71343a8e60..f7fe218bb3e4e3 100644 --- a/drivers/regulator/as3711-regulator.c +++ b/drivers/regulator/as3711-regulator.c @@ -1,12 +1,9 @@ +// SPDX-License-Identifier: GPL-2.0 /* * AS3711 PMIC regulator driver, using DCDC Step Down and LDO supplies * * Copyright (C) 2012 Renesas Electronics Corporation * Author: Guennadi Liakhovetski, - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the version 2 of the GNU General Public License as - * published by the Free Software Foundation */ #include From e770b18bbbaecebfb37389b45bf82974a17488d7 Mon Sep 17 00:00:00 2001 From: Matti Vaittinen Date: Wed, 7 Nov 2018 15:41:26 +0200 Subject: [PATCH 18/85] regulator: bd718x7: Change next state after poweroff to ready BD71837 and BD71847 have a HW functionality which leave power rails OFF after powerof state: - if they have been controlled by SW. - if state transition from poweroff is done to SNVS BD71837 can after reset transition from power-off to SNVS or READY state depending on reset reason. By default only wathcdog reset changes state from poweroff to ready. Change PMIC configuration to always transition to READY in order to avoid crucial power rails being OFF after reset. If SNVS is required the crucial power rails should not be controlled by SW - eg corresponding regulator control register should have SEL bit kept zero. Currently the driver assumes all regulators to be controlled by SW so it sets all SEL bits to 1. Signed-off-by: Matti Vaittinen Signed-off-by: Mark Brown --- drivers/regulator/bd718x7-regulator.c | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/drivers/regulator/bd718x7-regulator.c b/drivers/regulator/bd718x7-regulator.c index 512c936ae021eb..e63581651d6939 100644 --- a/drivers/regulator/bd718x7-regulator.c +++ b/drivers/regulator/bd718x7-regulator.c @@ -1054,6 +1054,29 @@ static int bd718xx_probe(struct platform_device *pdev) BD718XX_REG_REGLOCK); } + /* At poweroff transition PMIC HW disables EN bit for regulators but + * leaves SEL bit untouched. So if state transition from POWEROFF + * is done to SNVS - then all power rails controlled by SW (having + * SEL bit set) stay disabled as EN is cleared. This may result boot + * failure if any crucial systems are powered by these rails. + * + * Change the next stage from poweroff to be READY instead of SNVS + * for all reset types because OTP loading at READY will clear SEL + * bit allowing HW defaults for power rails to be used + */ + err = regmap_update_bits(mfd->regmap, BD718XX_REG_TRANS_COND1, + BD718XX_ON_REQ_POWEROFF_MASK | + BD718XX_SWRESET_POWEROFF_MASK | + BD718XX_WDOG_POWEROFF_MASK | + BD718XX_KEY_L_POWEROFF_MASK, + BD718XX_POWOFF_TO_RDY); + if (err) { + dev_err(&pdev->dev, "Failed to change reset target\n"); + goto err; + } else { + dev_dbg(&pdev->dev, "Changed all resets from SVNS to READY\n"); + } + for (i = 0; i < pmic_regulators[mfd->chip_type].r_amount; i++) { const struct regulator_desc *desc; From 1c984942f0a480ed41635528234b8bc672f6bac7 Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Thu, 15 Nov 2018 09:01:17 +0100 Subject: [PATCH 19/85] regulator: s2mps11: Pass descriptor instead of GPIO number Instead of passing a global GPIO number for the enable GPIO, pass a descriptor looked up with the standard devm_gpiod_get_optional() call. This regulator supports passing platform data, but enable/sleep regulators are looked up from the device tree exclusively, so we can need not touch other files. Tested on Odroid XU3 (with s2mps11 although not using any GPIOs for regulators, so at least default paths are not broken). Cc: Sangbeom Kim Cc: Chanwoo Choi Cc: Bartlomiej Zolnierkiewicz Cc: Krzysztof Kozlowski Reviewed-by: Krzysztof Kozlowski Tested-by: Krzysztof Kozlowski Signed-off-by: Linus Walleij Signed-off-by: Mark Brown --- drivers/regulator/s2mps11.c | 46 ++++++++++++++++++------------------- 1 file changed, 23 insertions(+), 23 deletions(-) diff --git a/drivers/regulator/s2mps11.c b/drivers/regulator/s2mps11.c index 5bb6f4ca48db8e..6fec4589719410 100644 --- a/drivers/regulator/s2mps11.c +++ b/drivers/regulator/s2mps11.c @@ -5,7 +5,7 @@ #include #include -#include +#include #include #include #include @@ -14,7 +14,6 @@ #include #include #include -#include #include #include #include @@ -44,7 +43,7 @@ struct s2mps11_info { * Array (size: number of regulators) with GPIO-s for external * sleep control. */ - int *ext_control_gpio; + struct gpio_desc **ext_control_gpiod; }; static int get_ramp_delay(int ramp_delay) @@ -511,7 +510,7 @@ static int s2mps14_regulator_enable(struct regulator_dev *rdev) case S2MPS14X: if (test_bit(rdev_get_id(rdev), s2mps11->suspend_state)) val = S2MPS14_ENABLE_SUSPEND; - else if (gpio_is_valid(s2mps11->ext_control_gpio[rdev_get_id(rdev)])) + else if (s2mps11->ext_control_gpiod[rdev_get_id(rdev)]) val = S2MPS14_ENABLE_EXT_CONTROL; else val = rdev->desc->enable_mask; @@ -805,7 +804,7 @@ static int s2mps14_pmic_enable_ext_control(struct s2mps11_info *s2mps11, static void s2mps14_pmic_dt_parse_ext_control_gpio(struct platform_device *pdev, struct of_regulator_match *rdata, struct s2mps11_info *s2mps11) { - int *gpio = s2mps11->ext_control_gpio; + struct gpio_desc **gpio = s2mps11->ext_control_gpiod; unsigned int i; unsigned int valid_regulators[3] = { S2MPS14_LDO10, S2MPS14_LDO11, S2MPS14_LDO12 }; @@ -816,11 +815,20 @@ static void s2mps14_pmic_dt_parse_ext_control_gpio(struct platform_device *pdev, if (!rdata[reg].init_data || !rdata[reg].of_node) continue; - gpio[reg] = of_get_named_gpio(rdata[reg].of_node, - "samsung,ext-control-gpios", 0); - if (gpio_is_valid(gpio[reg])) - dev_dbg(&pdev->dev, "Using GPIO %d for ext-control over %d/%s\n", - gpio[reg], reg, rdata[reg].name); + gpio[reg] = devm_gpiod_get_from_of_node(&pdev->dev, + rdata[reg].of_node, + "samsung,ext-control-gpios", + 0, + GPIOD_OUT_HIGH | GPIOD_FLAGS_BIT_NONEXCLUSIVE, + "s2mps11-regulator"); + if (IS_ERR(gpio[reg])) { + dev_err(&pdev->dev, "Failed to get control GPIO for %d/%s\n", + reg, rdata[reg].name); + continue; + } + if (gpio[reg]) + dev_dbg(&pdev->dev, "Using GPIO for ext-control over %d/%s\n", + reg, rdata[reg].name); } } @@ -1126,17 +1134,11 @@ static int s2mps11_pmic_probe(struct platform_device *pdev) return -EINVAL; } - s2mps11->ext_control_gpio = devm_kmalloc_array(&pdev->dev, - rdev_num, sizeof(*s2mps11->ext_control_gpio), + s2mps11->ext_control_gpiod = devm_kmalloc_array(&pdev->dev, + rdev_num, sizeof(*s2mps11->ext_control_gpiod), GFP_KERNEL); - if (!s2mps11->ext_control_gpio) + if (!s2mps11->ext_control_gpiod) return -ENOMEM; - /* - * 0 is a valid GPIO so initialize all GPIO-s to negative value - * to indicate that external control won't be used for this regulator. - */ - for (i = 0; i < rdev_num; i++) - s2mps11->ext_control_gpio[i] = -EINVAL; if (!iodev->dev->of_node) { if (iodev->pdata) { @@ -1166,8 +1168,6 @@ static int s2mps11_pmic_probe(struct platform_device *pdev) config.dev = &pdev->dev; config.regmap = iodev->regmap_pmic; config.driver_data = s2mps11; - config.ena_gpio_flags = GPIOF_OUT_INIT_HIGH; - config.ena_gpio_initialized = true; for (i = 0; i < rdev_num; i++) { struct regulator_dev *regulator; @@ -1178,7 +1178,7 @@ static int s2mps11_pmic_probe(struct platform_device *pdev) config.init_data = rdata[i].init_data; config.of_node = rdata[i].of_node; } - config.ena_gpio = s2mps11->ext_control_gpio[i]; + config.ena_gpiod = s2mps11->ext_control_gpiod[i]; regulator = devm_regulator_register(&pdev->dev, ®ulators[i], &config); @@ -1189,7 +1189,7 @@ static int s2mps11_pmic_probe(struct platform_device *pdev) goto out; } - if (gpio_is_valid(s2mps11->ext_control_gpio[i])) { + if (s2mps11->ext_control_gpiod[i]) { ret = s2mps14_pmic_enable_ext_control(s2mps11, regulator); if (ret < 0) { From 1d2f46814d20a55c45ac171739b6885826e0c793 Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Thu, 15 Nov 2018 09:01:18 +0100 Subject: [PATCH 20/85] regulator: wm8994: Pass descriptor instead of GPIO number Instead of passing a global GPIO number for the enable GPIO, pass a descriptor looked up from the device tree node or the board file decriptor table for the regulator. There is a single board file passing the GPIOs for LDO1 and LDO2 through platform data, so augment this to pass descriptors associated with the i2c device as well. The special GPIO enable DT property for the enable GPIO is nonstandard but this was accomodated in commit 6a537d48461deacc57c07ed86d9915e5aa4b3539 "gpio: of: Support regulator nonstandard GPIO properties". Cc: patches@opensource.cirrus.com Acked-by: Charles Keepax Acked-by: Lee Jones Signed-off-by: Linus Walleij Signed-off-by: Mark Brown --- arch/arm/mach-s3c64xx/mach-crag6410-module.c | 17 +++++++++++++++-- drivers/mfd/wm8994-core.c | 9 --------- drivers/regulator/wm8994-regulator.c | 20 ++++++++++++-------- include/linux/mfd/wm8994/pdata.h | 3 --- 4 files changed, 27 insertions(+), 22 deletions(-) diff --git a/arch/arm/mach-s3c64xx/mach-crag6410-module.c b/arch/arm/mach-s3c64xx/mach-crag6410-module.c index 5aa472892465ad..76c4855a03bce2 100644 --- a/arch/arm/mach-s3c64xx/mach-crag6410-module.c +++ b/arch/arm/mach-s3c64xx/mach-crag6410-module.c @@ -194,8 +194,8 @@ static struct wm8994_pdata wm8994_pdata = { 0x3, /* IRQ out, active high, CMOS */ }, .ldo = { - { .enable = S3C64XX_GPN(6), .init_data = &wm8994_ldo1, }, - { .enable = S3C64XX_GPN(4), .init_data = &wm8994_ldo2, }, + { .init_data = &wm8994_ldo1, }, + { .init_data = &wm8994_ldo2, }, }, }; @@ -203,6 +203,18 @@ static const struct i2c_board_info wm1277_devs[] = { { I2C_BOARD_INFO("wm8958", 0x1a), /* WM8958 is the superset */ .platform_data = &wm8994_pdata, .irq = GLENFARCLAS_PMIC_IRQ_BASE + WM831X_IRQ_GPIO_2, + .dev_name = "wm8958", + }, +}; + +static struct gpiod_lookup_table wm8994_gpiod_table = { + .dev_id = "i2c-wm8958", /* I2C device name */ + .table = { + GPIO_LOOKUP("GPION", 6, + "wlf,ldo1ena", GPIO_ACTIVE_HIGH), + GPIO_LOOKUP("GPION", 4, + "wlf,ldo2ena", GPIO_ACTIVE_HIGH), + { }, }, }; @@ -381,6 +393,7 @@ static int wlf_gf_module_probe(struct i2c_client *i2c, gpiod_add_lookup_table(&wm5102_reva_gpiod_table); gpiod_add_lookup_table(&wm5102_gpiod_table); + gpiod_add_lookup_table(&wm8994_gpiod_table); if (i < ARRAY_SIZE(gf_mods)) { dev_info(&i2c->dev, "%s revision %d\n", diff --git a/drivers/mfd/wm8994-core.c b/drivers/mfd/wm8994-core.c index 22bd6525e09cfb..04a177efd24514 100644 --- a/drivers/mfd/wm8994-core.c +++ b/drivers/mfd/wm8994-core.c @@ -21,7 +21,6 @@ #include #include #include -#include #include #include #include @@ -306,14 +305,6 @@ static int wm8994_set_pdata_from_of(struct wm8994 *wm8994) pdata->csnaddr_pd = of_property_read_bool(np, "wlf,csnaddr-pd"); - pdata->ldo[0].enable = of_get_named_gpio(np, "wlf,ldo1ena", 0); - if (pdata->ldo[0].enable < 0) - pdata->ldo[0].enable = 0; - - pdata->ldo[1].enable = of_get_named_gpio(np, "wlf,ldo2ena", 0); - if (pdata->ldo[1].enable < 0) - pdata->ldo[1].enable = 0; - return 0; } #else diff --git a/drivers/regulator/wm8994-regulator.c b/drivers/regulator/wm8994-regulator.c index 7a4ce6df4f22a9..d7fec533c40322 100644 --- a/drivers/regulator/wm8994-regulator.c +++ b/drivers/regulator/wm8994-regulator.c @@ -19,7 +19,7 @@ #include #include #include -#include +#include #include #include @@ -129,6 +129,7 @@ static int wm8994_ldo_probe(struct platform_device *pdev) int id = pdev->id % ARRAY_SIZE(pdata->ldo); struct regulator_config config = { }; struct wm8994_ldo *ldo; + struct gpio_desc *gpiod; int ret; dev_dbg(&pdev->dev, "Probing LDO%d\n", id + 1); @@ -145,12 +146,15 @@ static int wm8994_ldo_probe(struct platform_device *pdev) config.driver_data = ldo; config.regmap = wm8994->regmap; config.init_data = &ldo->init_data; - if (pdata) { - config.ena_gpio = pdata->ldo[id].enable; - } else if (wm8994->dev->of_node) { - config.ena_gpio = wm8994->pdata.ldo[id].enable; - config.ena_gpio_initialized = true; - } + + /* Look up LDO enable GPIO from the parent device node */ + gpiod = devm_gpiod_get_optional(pdev->dev.parent, + id ? "wlf,ldo2ena" : "wlf,ldo1ena", + GPIOD_OUT_LOW | + GPIOD_FLAGS_BIT_NONEXCLUSIVE); + if (IS_ERR(gpiod)) + return PTR_ERR(gpiod); + config.ena_gpiod = gpiod; /* Use default constraints if none set up */ if (!pdata || !pdata->ldo[id].init_data || wm8994->dev->of_node) { @@ -159,7 +163,7 @@ static int wm8994_ldo_probe(struct platform_device *pdev) ldo->init_data = wm8994_ldo_default[id]; ldo->init_data.consumer_supplies = &ldo->supply; - if (!config.ena_gpio) + if (!gpiod) ldo->init_data.constraints.valid_ops_mask = 0; } else { ldo->init_data = *pdata->ldo[id].init_data; diff --git a/include/linux/mfd/wm8994/pdata.h b/include/linux/mfd/wm8994/pdata.h index b19c370fe81a07..f346167c0e0016 100644 --- a/include/linux/mfd/wm8994/pdata.h +++ b/include/linux/mfd/wm8994/pdata.h @@ -20,9 +20,6 @@ #define WM8994_NUM_AIF 3 struct wm8994_ldo_pdata { - /** GPIOs to enable regulator, 0 or less if not available */ - int enable; - const struct regulator_init_data *init_data; }; From 96392c3d8ca47f6654dfa40680c926b1a3419754 Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Thu, 15 Nov 2018 09:01:19 +0100 Subject: [PATCH 21/85] regulator: max77686: Pass descriptor instead of GPIO number Instead of passing a global GPIO number, pass a descriptor looked up from the device tree configuration node. Tested on Odroid U3 (with max77686 although not using any GPIOs for regulators, so at least default paths are not broken). Cc: Chanwoo Choi Cc: Krzysztof Kozlowski Cc: Bartlomiej Zolnierkiewicz Reviewed-by: Krzysztof Kozlowski Tested-by: Krzysztof Kozlowski Signed-off-by: Linus Walleij Signed-off-by: Mark Brown --- drivers/regulator/max77686-regulator.c | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/drivers/regulator/max77686-regulator.c b/drivers/regulator/max77686-regulator.c index bee060937f5608..f5cee17759053c 100644 --- a/drivers/regulator/max77686-regulator.c +++ b/drivers/regulator/max77686-regulator.c @@ -11,8 +11,7 @@ #include #include #include -#include -#include +#include #include #include #include @@ -76,6 +75,7 @@ enum max77686_ramp_rate { }; struct max77686_data { + struct device *dev; DECLARE_BITMAP(gpio_enabled, MAX77686_REGULATORS); /* Array indexed by regulator id */ @@ -255,16 +255,20 @@ static int max77686_of_parse_cb(struct device_node *np, case MAX77686_BUCK8: case MAX77686_BUCK9: case MAX77686_LDO20 ... MAX77686_LDO22: - config->ena_gpio = of_get_named_gpio(np, - "maxim,ena-gpios", 0); - config->ena_gpio_flags = GPIOF_OUT_INIT_HIGH; - config->ena_gpio_initialized = true; + config->ena_gpiod = devm_gpiod_get_from_of_node(max77686->dev, + np, + "maxim,ena", + 0, + GPIOD_OUT_HIGH | GPIOD_FLAGS_BIT_NONEXCLUSIVE, + "max77686-regulator"); + if (IS_ERR(config->ena_gpiod)) + config->ena_gpiod = NULL; break; default: return 0; } - if (gpio_is_valid(config->ena_gpio)) { + if (config->ena_gpiod) { set_bit(desc->id, max77686->gpio_enabled); return regmap_update_bits(config->regmap, desc->enable_reg, @@ -507,6 +511,7 @@ static int max77686_pmic_probe(struct platform_device *pdev) if (!max77686) return -ENOMEM; + max77686->dev = &pdev->dev; config.dev = iodev->dev; config.regmap = iodev->regmap; config.driver_data = max77686; From fe06051dbf8abf5962d9258c4a863056bdfa6eae Mon Sep 17 00:00:00 2001 From: zoro Date: Wed, 14 Nov 2018 17:38:22 +0800 Subject: [PATCH 22/85] regulator/of_get_regulator: add child path to find the regulator supplier when the VIR_LDO1 regulator supplier is it's brother, we can't find the supplier. example code : &vir_regulator { ldo0_vir: ldo0-virtual { regulator-compatible = "VIR_LDO0"; regulator-name= "VIR_LDO0"; regulator-min-microvolt = <1000000>; regulator-max-microvolt = <2000000>; }; ldo1_vir: ldo1-virtual { regulator-compatible = "VIR_LDO1"; regulator-name= "VIR_LDO1"; regulator-min-microvolt = <1000000>; regulator-max-microvolt = <3000000>; ldo1-supply = <&ldo0_vir>; }; ... } so we add the child ptah to find the suppier. Signed-off-by: zoro Signed-off-by: Mark Brown --- drivers/regulator/core.c | 35 +++++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/drivers/regulator/core.c b/drivers/regulator/core.c index 2c66b528aedec0..55207264c212b9 100644 --- a/drivers/regulator/core.c +++ b/drivers/regulator/core.c @@ -227,6 +227,37 @@ static void regulator_unlock_supply(struct regulator_dev *rdev) } } +/** + * of_get_child_regulator - get a child regulator device node + * based on supply name + * @parent: Parent device node + * @prop_name: Combination regulator supply name and "-supply" + * + * Traverse all child nodes. + * Extract the child regulator device node corresponding to the supply name. + * returns the device node corresponding to the regulator if found, else + * returns NULL. + */ +static struct device_node *of_get_child_regulator(struct device_node *parent, + const char *prop_name) +{ + struct device_node *regnode = NULL; + struct device_node *child = NULL; + + for_each_child_of_node(parent, child) { + regnode = of_parse_phandle(child, prop_name, 0); + + if (!regnode) { + regnode = of_get_child_regulator(child, prop_name); + if (regnode) + return regnode; + } else { + return regnode; + } + } + return NULL; +} + /** * of_get_regulator - get a regulator device node based on supply name * @dev: Device pointer for the consumer (of regulator) device @@ -247,6 +278,10 @@ static struct device_node *of_get_regulator(struct device *dev, const char *supp regnode = of_parse_phandle(dev->of_node, prop_name, 0); if (!regnode) { + regnode = of_get_child_regulator(dev->of_node, prop_name); + if (regnode) + return regnode; + dev_dbg(dev, "Looking up %s property in node %pOF failed\n", prop_name, dev->of_node); return NULL; From f8702f9e4aa7b45131af3df5531d6e3835269141 Mon Sep 17 00:00:00 2001 From: Dmitry Osipenko Date: Mon, 19 Nov 2018 00:56:17 +0300 Subject: [PATCH 23/85] regulator: core: Use ww_mutex for regulators locking Wait/wound mutex shall be used in order to avoid lockups on locking of coupled regulators. Signed-off-by: Dmitry Osipenko Suggested-by: Lucas Stach Signed-off-by: Mark Brown --- drivers/regulator/core.c | 403 +++++++++++++++++++------- drivers/regulator/da9210-regulator.c | 4 +- drivers/regulator/stpmic1_regulator.c | 4 +- drivers/regulator/wm8350-regulator.c | 4 +- include/linux/regulator/driver.h | 6 +- 5 files changed, 317 insertions(+), 104 deletions(-) diff --git a/drivers/regulator/core.c b/drivers/regulator/core.c index 783ec9c7410465..47ccd35c79658c 100644 --- a/drivers/regulator/core.c +++ b/drivers/regulator/core.c @@ -50,6 +50,8 @@ #define rdev_dbg(rdev, fmt, ...) \ pr_debug("%s: " fmt, rdev_get_name(rdev), ##__VA_ARGS__) +static DEFINE_WW_CLASS(regulator_ww_class); +static DEFINE_MUTEX(regulator_nesting_mutex); static DEFINE_MUTEX(regulator_list_mutex); static LIST_HEAD(regulator_map_list); static LIST_HEAD(regulator_ena_gpio_list); @@ -154,7 +156,7 @@ static inline struct regulator_dev *rdev_get_supply(struct regulator_dev *rdev) /** * regulator_lock_nested - lock a single regulator * @rdev: regulator source - * @subclass: mutex subclass used for lockdep + * @ww_ctx: w/w mutex acquire context * * This function can be called many times by one task on * a single regulator and its mutex will be locked only @@ -162,24 +164,52 @@ static inline struct regulator_dev *rdev_get_supply(struct regulator_dev *rdev) * than the one, which initially locked the mutex, it will * wait on mutex. */ -static void regulator_lock_nested(struct regulator_dev *rdev, - unsigned int subclass) +static inline int regulator_lock_nested(struct regulator_dev *rdev, + struct ww_acquire_ctx *ww_ctx) { - if (!mutex_trylock(&rdev->mutex)) { - if (rdev->mutex_owner == current) { + bool lock = false; + int ret = 0; + + mutex_lock(®ulator_nesting_mutex); + + if (ww_ctx || !ww_mutex_trylock(&rdev->mutex)) { + if (rdev->mutex_owner == current) rdev->ref_cnt++; - return; + else + lock = true; + + if (lock) { + mutex_unlock(®ulator_nesting_mutex); + ret = ww_mutex_lock(&rdev->mutex, ww_ctx); + mutex_lock(®ulator_nesting_mutex); } - mutex_lock_nested(&rdev->mutex, subclass); + } else { + lock = true; } - rdev->ref_cnt = 1; - rdev->mutex_owner = current; + if (lock && ret != -EDEADLK) { + rdev->ref_cnt++; + rdev->mutex_owner = current; + } + + mutex_unlock(®ulator_nesting_mutex); + + return ret; } -static inline void regulator_lock(struct regulator_dev *rdev) +/** + * regulator_lock - lock a single regulator + * @rdev: regulator source + * + * This function can be called many times by one task on + * a single regulator and its mutex will be locked only + * once. If a task, which is calling this function is other + * than the one, which initially locked the mutex, it will + * wait on mutex. + */ +void regulator_lock(struct regulator_dev *rdev) { - regulator_lock_nested(rdev, 0); + regulator_lock_nested(rdev, NULL); } /** @@ -189,52 +219,48 @@ static inline void regulator_lock(struct regulator_dev *rdev) * This function unlocks the mutex when the * reference counter reaches 0. */ -static void regulator_unlock(struct regulator_dev *rdev) +void regulator_unlock(struct regulator_dev *rdev) { - if (rdev->ref_cnt != 0) { - rdev->ref_cnt--; + mutex_lock(®ulator_nesting_mutex); - if (!rdev->ref_cnt) { - rdev->mutex_owner = NULL; - mutex_unlock(&rdev->mutex); - } + if (--rdev->ref_cnt == 0) { + rdev->mutex_owner = NULL; + ww_mutex_unlock(&rdev->mutex); } + + WARN_ON_ONCE(rdev->ref_cnt < 0); + + mutex_unlock(®ulator_nesting_mutex); } -static int regulator_lock_recursive(struct regulator_dev *rdev, - unsigned int subclass) +static void regulator_unlock_recursive(struct regulator_dev *rdev, + unsigned int n_coupled) { struct regulator_dev *c_rdev; int i; - for (i = 0; i < rdev->coupling_desc.n_coupled; i++) { - c_rdev = rdev->coupling_desc.coupled_rdevs[i]; + for (i = n_coupled; i > 0; i--) { + c_rdev = rdev->coupling_desc.coupled_rdevs[i - 1]; if (!c_rdev) continue; - regulator_lock_nested(c_rdev, subclass++); - if (c_rdev->supply) - subclass = - regulator_lock_recursive(c_rdev->supply->rdev, - subclass); - } + regulator_unlock_recursive( + c_rdev->supply->rdev, + c_rdev->coupling_desc.n_coupled); - return subclass; + regulator_unlock(c_rdev); + } } -/** - * regulator_unlock_dependent - unlock regulator's suppliers and coupled - * regulators - * @rdev: regulator source - * - * Unlock all regulators related with rdev by coupling or suppling. - */ -static void regulator_unlock_dependent(struct regulator_dev *rdev) +static int regulator_lock_recursive(struct regulator_dev *rdev, + struct regulator_dev **new_contended_rdev, + struct regulator_dev **old_contended_rdev, + struct ww_acquire_ctx *ww_ctx) { struct regulator_dev *c_rdev; - int i; + int i, err; for (i = 0; i < rdev->coupling_desc.n_coupled; i++) { c_rdev = rdev->coupling_desc.coupled_rdevs[i]; @@ -242,23 +268,95 @@ static void regulator_unlock_dependent(struct regulator_dev *rdev) if (!c_rdev) continue; - regulator_unlock(c_rdev); + if (c_rdev != *old_contended_rdev) { + err = regulator_lock_nested(c_rdev, ww_ctx); + if (err) { + if (err == -EDEADLK) { + *new_contended_rdev = c_rdev; + goto err_unlock; + } - if (c_rdev->supply) - regulator_unlock_dependent(c_rdev->supply->rdev); + /* shouldn't happen */ + WARN_ON_ONCE(err != -EALREADY); + } + } else { + *old_contended_rdev = NULL; + } + + if (c_rdev->supply) { + err = regulator_lock_recursive(c_rdev->supply->rdev, + new_contended_rdev, + old_contended_rdev, + ww_ctx); + if (err) { + regulator_unlock(c_rdev); + goto err_unlock; + } + } } + + return 0; + +err_unlock: + regulator_unlock_recursive(rdev, i); + + return err; +} + +/** + * regulator_unlock_dependent - unlock regulator's suppliers and coupled + * regulators + * @rdev: regulator source + * @ww_ctx: w/w mutex acquire context + * + * Unlock all regulators related with rdev by coupling or suppling. + */ +static void regulator_unlock_dependent(struct regulator_dev *rdev, + struct ww_acquire_ctx *ww_ctx) +{ + regulator_unlock_recursive(rdev, rdev->coupling_desc.n_coupled); + ww_acquire_fini(ww_ctx); } /** * regulator_lock_dependent - lock regulator's suppliers and coupled regulators * @rdev: regulator source + * @ww_ctx: w/w mutex acquire context * * This function as a wrapper on regulator_lock_recursive(), which locks * all regulators related with rdev by coupling or suppling. */ -static inline void regulator_lock_dependent(struct regulator_dev *rdev) +static void regulator_lock_dependent(struct regulator_dev *rdev, + struct ww_acquire_ctx *ww_ctx) { - regulator_lock_recursive(rdev, 0); + struct regulator_dev *new_contended_rdev = NULL; + struct regulator_dev *old_contended_rdev = NULL; + int err; + + mutex_lock(®ulator_list_mutex); + + ww_acquire_init(ww_ctx, ®ulator_ww_class); + + do { + if (new_contended_rdev) { + ww_mutex_lock_slow(&new_contended_rdev->mutex, ww_ctx); + old_contended_rdev = new_contended_rdev; + old_contended_rdev->ref_cnt++; + } + + err = regulator_lock_recursive(rdev, + &new_contended_rdev, + &old_contended_rdev, + ww_ctx); + + if (old_contended_rdev) + regulator_unlock(old_contended_rdev); + + } while (err == -EDEADLK); + + ww_acquire_done(ww_ctx); + + mutex_unlock(®ulator_list_mutex); } /** @@ -772,7 +870,7 @@ static int drms_uA_update(struct regulator_dev *rdev) int current_uA = 0, output_uV, input_uV, err; unsigned int mode; - lockdep_assert_held_once(&rdev->mutex); + lockdep_assert_held_once(&rdev->mutex.base); /* * first check to see if we can set modes at all, otherwise just @@ -2274,7 +2372,20 @@ static int _regulator_enable(struct regulator_dev *rdev) { int ret; - lockdep_assert_held_once(&rdev->mutex); + lockdep_assert_held_once(&rdev->mutex.base); + + if (rdev->supply) { + ret = _regulator_enable(rdev->supply->rdev); + if (ret < 0) + return ret; + } + + /* balance only if there are regulators coupled */ + if (rdev->coupling_desc.n_coupled > 1) { + ret = regulator_balance_voltage(rdev, PM_SUSPEND_ON); + if (ret < 0) + goto err_disable_supply; + } /* check voltage and requested load before enabling */ if (regulator_ops_is_valid(rdev, REGULATOR_CHANGE_DRMS)) @@ -2285,18 +2396,20 @@ static int _regulator_enable(struct regulator_dev *rdev) ret = _regulator_is_enabled(rdev); if (ret == -EINVAL || ret == 0) { if (!regulator_ops_is_valid(rdev, - REGULATOR_CHANGE_STATUS)) - return -EPERM; + REGULATOR_CHANGE_STATUS)) { + ret = -EPERM; + goto err_disable_supply; + } ret = _regulator_do_enable(rdev); if (ret < 0) - return ret; + goto err_disable_supply; _notifier_call_chain(rdev, REGULATOR_EVENT_ENABLE, NULL); } else if (ret < 0) { rdev_err(rdev, "is_enabled() failed: %d\n", ret); - return ret; + goto err_disable_supply; } /* Fallthrough on positive return values - already enabled */ } @@ -2304,6 +2417,12 @@ static int _regulator_enable(struct regulator_dev *rdev) rdev->use_count++; return 0; + +err_disable_supply: + if (rdev->supply) + _regulator_disable(rdev->supply->rdev); + + return ret; } /** @@ -2320,30 +2439,15 @@ static int _regulator_enable(struct regulator_dev *rdev) int regulator_enable(struct regulator *regulator) { struct regulator_dev *rdev = regulator->rdev; + struct ww_acquire_ctx ww_ctx; int ret = 0; if (regulator->always_on) return 0; - if (rdev->supply) { - ret = regulator_enable(rdev->supply); - if (ret != 0) - return ret; - } - - regulator_lock_dependent(rdev); - /* balance only if there are regulators coupled */ - if (rdev->coupling_desc.n_coupled > 1) { - ret = regulator_balance_voltage(rdev, PM_SUSPEND_ON); - if (ret != 0) - goto unlock; - } + regulator_lock_dependent(rdev, &ww_ctx); ret = _regulator_enable(rdev); -unlock: - regulator_unlock_dependent(rdev); - - if (ret != 0 && rdev->supply) - regulator_disable(rdev->supply); + regulator_unlock_dependent(rdev, &ww_ctx); return ret; } @@ -2385,7 +2489,7 @@ static int _regulator_disable(struct regulator_dev *rdev) { int ret = 0; - lockdep_assert_held_once(&rdev->mutex); + lockdep_assert_held_once(&rdev->mutex.base); if (WARN(rdev->use_count <= 0, "unbalanced disables for %s\n", rdev_get_name(rdev))) @@ -2423,6 +2527,12 @@ static int _regulator_disable(struct regulator_dev *rdev) rdev->use_count--; } + if (ret == 0 && rdev->coupling_desc.n_coupled > 1) + ret = regulator_balance_voltage(rdev, PM_SUSPEND_ON); + + if (ret == 0 && rdev->supply) + ret = _regulator_disable(rdev->supply->rdev); + return ret; } @@ -2441,19 +2551,15 @@ static int _regulator_disable(struct regulator_dev *rdev) int regulator_disable(struct regulator *regulator) { struct regulator_dev *rdev = regulator->rdev; + struct ww_acquire_ctx ww_ctx; int ret = 0; if (regulator->always_on) return 0; - regulator_lock_dependent(rdev); + regulator_lock_dependent(rdev, &ww_ctx); ret = _regulator_disable(rdev); - if (rdev->coupling_desc.n_coupled > 1) - regulator_balance_voltage(rdev, PM_SUSPEND_ON); - regulator_unlock_dependent(rdev); - - if (ret == 0 && rdev->supply) - regulator_disable(rdev->supply); + regulator_unlock_dependent(rdev, &ww_ctx); return ret; } @@ -2464,7 +2570,7 @@ static int _regulator_force_disable(struct regulator_dev *rdev) { int ret = 0; - lockdep_assert_held_once(&rdev->mutex); + lockdep_assert_held_once(&rdev->mutex.base); ret = _notifier_call_chain(rdev, REGULATOR_EVENT_FORCE_DISABLE | REGULATOR_EVENT_PRE_DISABLE, NULL); @@ -2497,14 +2603,15 @@ static int _regulator_force_disable(struct regulator_dev *rdev) int regulator_force_disable(struct regulator *regulator) { struct regulator_dev *rdev = regulator->rdev; + struct ww_acquire_ctx ww_ctx; int ret; - regulator_lock_dependent(rdev); + regulator_lock_dependent(rdev, &ww_ctx); regulator->uA_load = 0; ret = _regulator_force_disable(regulator->rdev); if (rdev->coupling_desc.n_coupled > 1) regulator_balance_voltage(rdev, PM_SUSPEND_ON); - regulator_unlock_dependent(rdev); + regulator_unlock_dependent(rdev, &ww_ctx); if (rdev->supply) while (rdev->open_count--) @@ -2518,9 +2625,10 @@ static void regulator_disable_work(struct work_struct *work) { struct regulator_dev *rdev = container_of(work, struct regulator_dev, disable_work.work); + struct ww_acquire_ctx ww_ctx; int count, i, ret; - regulator_lock(rdev); + regulator_lock_dependent(rdev, &ww_ctx); BUG_ON(!rdev->deferred_disables); @@ -2541,7 +2649,10 @@ static void regulator_disable_work(struct work_struct *work) rdev_err(rdev, "Deferred disable failed: %d\n", ret); } - regulator_unlock(rdev); + if (rdev->coupling_desc.n_coupled > 1) + regulator_balance_voltage(rdev, PM_SUSPEND_ON); + + regulator_unlock_dependent(rdev, &ww_ctx); if (rdev->supply) { for (i = 0; i < count; i++) { @@ -2652,9 +2763,9 @@ int regulator_is_enabled(struct regulator *regulator) if (regulator->always_on) return 1; - regulator_lock_dependent(regulator->rdev); + regulator_lock(regulator->rdev); ret = _regulator_is_enabled(regulator->rdev); - regulator_unlock_dependent(regulator->rdev); + regulator_unlock(regulator->rdev); return ret; } @@ -3268,7 +3379,7 @@ static int regulator_get_optimal_voltage(struct regulator_dev *rdev, int tmp_min = 0; int tmp_max = INT_MAX; - lockdep_assert_held_once(&c_rdevs[i]->mutex); + lockdep_assert_held_once(&c_rdevs[i]->mutex.base); ret = regulator_check_consumers(c_rdevs[i], &tmp_min, @@ -3479,14 +3590,15 @@ static int regulator_balance_voltage(struct regulator_dev *rdev, */ int regulator_set_voltage(struct regulator *regulator, int min_uV, int max_uV) { - int ret = 0; + struct ww_acquire_ctx ww_ctx; + int ret; - regulator_lock_dependent(regulator->rdev); + regulator_lock_dependent(regulator->rdev, &ww_ctx); ret = regulator_set_voltage_unlocked(regulator, min_uV, max_uV, PM_SUSPEND_ON); - regulator_unlock_dependent(regulator->rdev); + regulator_unlock_dependent(regulator->rdev, &ww_ctx); return ret; } @@ -3558,18 +3670,19 @@ static int _regulator_set_suspend_voltage(struct regulator *regulator, int regulator_set_suspend_voltage(struct regulator *regulator, int min_uV, int max_uV, suspend_state_t state) { - int ret = 0; + struct ww_acquire_ctx ww_ctx; + int ret; /* PM_SUSPEND_ON is handled by regulator_set_voltage() */ if (regulator_check_states(state) || state == PM_SUSPEND_ON) return -EINVAL; - regulator_lock_dependent(regulator->rdev); + regulator_lock_dependent(regulator->rdev, &ww_ctx); ret = _regulator_set_suspend_voltage(regulator, min_uV, max_uV, state); - regulator_unlock_dependent(regulator->rdev); + regulator_unlock_dependent(regulator->rdev, &ww_ctx); return ret; } @@ -3759,13 +3872,12 @@ static int _regulator_get_voltage(struct regulator_dev *rdev) */ int regulator_get_voltage(struct regulator *regulator) { + struct ww_acquire_ctx ww_ctx; int ret; - regulator_lock_dependent(regulator->rdev); - + regulator_lock_dependent(regulator->rdev, &ww_ctx); ret = _regulator_get_voltage(regulator->rdev); - - regulator_unlock_dependent(regulator->rdev); + regulator_unlock_dependent(regulator->rdev, &ww_ctx); return ret; } @@ -4301,7 +4413,7 @@ EXPORT_SYMBOL_GPL(regulator_bulk_free); int regulator_notifier_call_chain(struct regulator_dev *rdev, unsigned long event, void *data) { - lockdep_assert_held_once(&rdev->mutex); + lockdep_assert_held_once(&rdev->mutex.base); _notifier_call_chain(rdev, event, data); return NOTIFY_DONE; @@ -4669,7 +4781,7 @@ regulator_register(const struct regulator_desc *regulator_desc, rdev->dev.of_node = of_node_get(config->of_node); } - mutex_init(&rdev->mutex); + ww_mutex_init(&rdev->mutex, ®ulator_ww_class); rdev->reg_data = config->driver_data; rdev->owner = regulator_desc->owner; rdev->desc = regulator_desc; @@ -5026,8 +5138,6 @@ static void regulator_summary_show_subtree(struct seq_file *s, if (!rdev) return; - regulator_lock_nested(rdev, level); - opmode = _regulator_get_mode_unlocked(rdev); seq_printf(s, "%*s%-*s %3d %4d %6d %7s ", level * 3 + 1, "", @@ -5084,8 +5194,101 @@ static void regulator_summary_show_subtree(struct seq_file *s, class_for_each_device(®ulator_class, NULL, &summary_data, regulator_summary_show_children); +} + +struct summary_lock_data { + struct ww_acquire_ctx *ww_ctx; + struct regulator_dev **new_contended_rdev; + struct regulator_dev **old_contended_rdev; +}; + +static int regulator_summary_lock_one(struct device *dev, void *data) +{ + struct regulator_dev *rdev = dev_to_rdev(dev); + struct summary_lock_data *lock_data = data; + int ret = 0; + + if (rdev != *lock_data->old_contended_rdev) { + ret = regulator_lock_nested(rdev, lock_data->ww_ctx); + + if (ret == -EDEADLK) + *lock_data->new_contended_rdev = rdev; + else + WARN_ON_ONCE(ret); + } else { + *lock_data->old_contended_rdev = NULL; + } + + return ret; +} + +static int regulator_summary_unlock_one(struct device *dev, void *data) +{ + struct regulator_dev *rdev = dev_to_rdev(dev); + struct summary_lock_data *lock_data = data; + + if (lock_data) { + if (rdev == *lock_data->new_contended_rdev) + return -EDEADLK; + } regulator_unlock(rdev); + + return 0; +} + +static int regulator_summary_lock_all(struct ww_acquire_ctx *ww_ctx, + struct regulator_dev **new_contended_rdev, + struct regulator_dev **old_contended_rdev) +{ + struct summary_lock_data lock_data; + int ret; + + lock_data.ww_ctx = ww_ctx; + lock_data.new_contended_rdev = new_contended_rdev; + lock_data.old_contended_rdev = old_contended_rdev; + + ret = class_for_each_device(®ulator_class, NULL, &lock_data, + regulator_summary_lock_one); + if (ret) + class_for_each_device(®ulator_class, NULL, &lock_data, + regulator_summary_unlock_one); + + return ret; +} + +static void regulator_summary_lock(struct ww_acquire_ctx *ww_ctx) +{ + struct regulator_dev *new_contended_rdev = NULL; + struct regulator_dev *old_contended_rdev = NULL; + int err; + + ww_acquire_init(ww_ctx, ®ulator_ww_class); + + do { + if (new_contended_rdev) { + ww_mutex_lock_slow(&new_contended_rdev->mutex, ww_ctx); + old_contended_rdev = new_contended_rdev; + old_contended_rdev->ref_cnt++; + } + + err = regulator_summary_lock_all(ww_ctx, + &new_contended_rdev, + &old_contended_rdev); + + if (old_contended_rdev) + regulator_unlock(old_contended_rdev); + + } while (err == -EDEADLK); + + ww_acquire_done(ww_ctx); +} + +static void regulator_summary_unlock(struct ww_acquire_ctx *ww_ctx) +{ + class_for_each_device(®ulator_class, NULL, NULL, + regulator_summary_unlock_one); + ww_acquire_fini(ww_ctx); } static int regulator_summary_show_roots(struct device *dev, void *data) @@ -5101,12 +5304,18 @@ static int regulator_summary_show_roots(struct device *dev, void *data) static int regulator_summary_show(struct seq_file *s, void *data) { + struct ww_acquire_ctx ww_ctx; + seq_puts(s, " regulator use open bypass opmode voltage current min max\n"); seq_puts(s, "---------------------------------------------------------------------------------------\n"); + regulator_summary_lock(&ww_ctx); + class_for_each_device(®ulator_class, NULL, s, regulator_summary_show_roots); + regulator_summary_unlock(&ww_ctx); + return 0; } diff --git a/drivers/regulator/da9210-regulator.c b/drivers/regulator/da9210-regulator.c index d0496d6b09346b..84dba64ed11eca 100644 --- a/drivers/regulator/da9210-regulator.c +++ b/drivers/regulator/da9210-regulator.c @@ -131,7 +131,7 @@ static irqreturn_t da9210_irq_handler(int irq, void *data) if (error < 0) goto error_i2c; - mutex_lock(&chip->rdev->mutex); + regulator_lock(chip->rdev); if (val & DA9210_E_OVCURR) { regulator_notifier_call_chain(chip->rdev, @@ -157,7 +157,7 @@ static irqreturn_t da9210_irq_handler(int irq, void *data) handled |= DA9210_E_VMAX; } - mutex_unlock(&chip->rdev->mutex); + regulator_unlock(chip->rdev); if (handled) { /* Clear handled events */ diff --git a/drivers/regulator/stpmic1_regulator.c b/drivers/regulator/stpmic1_regulator.c index e15634edb8cef8..eac0848a78c7f6 100644 --- a/drivers/regulator/stpmic1_regulator.c +++ b/drivers/regulator/stpmic1_regulator.c @@ -489,14 +489,14 @@ static irqreturn_t stpmic1_curlim_irq_handler(int irq, void *data) { struct regulator_dev *rdev = (struct regulator_dev *)data; - mutex_lock(&rdev->mutex); + regulator_lock(rdev, NULL); /* Send an overcurrent notification */ regulator_notifier_call_chain(rdev, REGULATOR_EVENT_OVER_CURRENT, NULL); - mutex_unlock(&rdev->mutex); + regulator_unlock(rdev); return IRQ_HANDLED; } diff --git a/drivers/regulator/wm8350-regulator.c b/drivers/regulator/wm8350-regulator.c index 8ad11b074b4920..a1c7dfee5c37d7 100644 --- a/drivers/regulator/wm8350-regulator.c +++ b/drivers/regulator/wm8350-regulator.c @@ -1153,7 +1153,7 @@ static irqreturn_t pmic_uv_handler(int irq, void *data) { struct regulator_dev *rdev = (struct regulator_dev *)data; - mutex_lock(&rdev->mutex); + regulator_lock(rdev); if (irq == WM8350_IRQ_CS1 || irq == WM8350_IRQ_CS2) regulator_notifier_call_chain(rdev, REGULATOR_EVENT_REGULATION_OUT, @@ -1162,7 +1162,7 @@ static irqreturn_t pmic_uv_handler(int irq, void *data) regulator_notifier_call_chain(rdev, REGULATOR_EVENT_UNDER_VOLTAGE, NULL); - mutex_unlock(&rdev->mutex); + regulator_unlock(rdev); return IRQ_HANDLED; } diff --git a/include/linux/regulator/driver.h b/include/linux/regulator/driver.h index a05d37d0efa14d..7065031f084657 100644 --- a/include/linux/regulator/driver.h +++ b/include/linux/regulator/driver.h @@ -20,6 +20,7 @@ #include #include #include +#include struct gpio_desc; struct regmap; @@ -462,7 +463,7 @@ struct regulator_dev { struct coupling_desc coupling_desc; struct blocking_notifier_head notifier; - struct mutex mutex; /* consumer lock */ + struct ww_mutex mutex; /* consumer lock */ struct task_struct *mutex_owner; int ref_cnt; struct module *owner; @@ -545,4 +546,7 @@ int regulator_set_active_discharge_regmap(struct regulator_dev *rdev, bool enable); void *regulator_get_init_drvdata(struct regulator_init_data *reg_init_data); +void regulator_lock(struct regulator_dev *rdev); +void regulator_unlock(struct regulator_dev *rdev); + #endif From 089e2cc2e1861ad13a56811aa538419989346089 Mon Sep 17 00:00:00 2001 From: Dmitry Osipenko Date: Mon, 19 Nov 2018 00:56:18 +0300 Subject: [PATCH 24/85] regulator: core: Properly handle case where supply is the couple Check whether supply regulator is the couple to avoid infinite recursion during of locking. Signed-off-by: Dmitry Osipenko Signed-off-by: Mark Brown --- drivers/regulator/core.c | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/drivers/regulator/core.c b/drivers/regulator/core.c index 47ccd35c79658c..e46e53aa43dba0 100644 --- a/drivers/regulator/core.c +++ b/drivers/regulator/core.c @@ -233,6 +233,21 @@ void regulator_unlock(struct regulator_dev *rdev) mutex_unlock(®ulator_nesting_mutex); } +static bool regulator_supply_is_couple(struct regulator_dev *rdev) +{ + struct regulator_dev *c_rdev; + int i; + + for (i = 1; i < rdev->coupling_desc.n_coupled; i++) { + c_rdev = rdev->coupling_desc.coupled_rdevs[i]; + + if (rdev->supply->rdev == c_rdev) + return true; + } + + return false; +} + static void regulator_unlock_recursive(struct regulator_dev *rdev, unsigned int n_coupled) { @@ -245,7 +260,7 @@ static void regulator_unlock_recursive(struct regulator_dev *rdev, if (!c_rdev) continue; - if (c_rdev->supply) + if (c_rdev->supply && !regulator_supply_is_couple(c_rdev)) regulator_unlock_recursive( c_rdev->supply->rdev, c_rdev->coupling_desc.n_coupled); @@ -283,7 +298,7 @@ static int regulator_lock_recursive(struct regulator_dev *rdev, *old_contended_rdev = NULL; } - if (c_rdev->supply) { + if (c_rdev->supply && !regulator_supply_is_couple(c_rdev)) { err = regulator_lock_recursive(c_rdev->supply->rdev, new_contended_rdev, old_contended_rdev, From ff9b34b615a37a3f4d359bf82fc97f5e0310b301 Mon Sep 17 00:00:00 2001 From: Dmitry Osipenko Date: Mon, 19 Nov 2018 00:56:19 +0300 Subject: [PATCH 25/85] regulator: core: Keep regulators-list locked while traversing the list It's unlikely that regulators may disappear/appear while regulators debug-summary is being prepared, but let's be consistent and avoid that situation. Signed-off-by: Dmitry Osipenko Signed-off-by: Mark Brown --- drivers/regulator/core.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/drivers/regulator/core.c b/drivers/regulator/core.c index e46e53aa43dba0..fd237bc0fa7cdc 100644 --- a/drivers/regulator/core.c +++ b/drivers/regulator/core.c @@ -4936,7 +4936,9 @@ void regulator_unregister(struct regulator_dev *rdev) regulator_disable(rdev->supply); regulator_put(rdev->supply); } + mutex_lock(®ulator_list_mutex); + debugfs_remove_recursive(rdev->debugfs); flush_work(&rdev->disable_work.work); WARN_ON(rdev->open_count); @@ -4944,8 +4946,9 @@ void regulator_unregister(struct regulator_dev *rdev) unset_regulator_supplies(rdev); list_del(&rdev->list); regulator_ena_gpio_free(rdev); - mutex_unlock(®ulator_list_mutex); device_unregister(&rdev->dev); + + mutex_unlock(®ulator_list_mutex); } EXPORT_SYMBOL_GPL(regulator_unregister); @@ -5278,6 +5281,8 @@ static void regulator_summary_lock(struct ww_acquire_ctx *ww_ctx) struct regulator_dev *old_contended_rdev = NULL; int err; + mutex_lock(®ulator_list_mutex); + ww_acquire_init(ww_ctx, ®ulator_ww_class); do { @@ -5304,6 +5309,8 @@ static void regulator_summary_unlock(struct ww_acquire_ctx *ww_ctx) class_for_each_device(®ulator_class, NULL, NULL, regulator_summary_unlock_one); ww_acquire_fini(ww_ctx); + + mutex_unlock(®ulator_list_mutex); } static int regulator_summary_show_roots(struct device *dev, void *data) From a8d8ee437ea0bf21f840f33ca87f8e1641727aae Mon Sep 17 00:00:00 2001 From: Dmitry Osipenko Date: Mon, 19 Nov 2018 00:56:16 +0300 Subject: [PATCH 26/85] regulator: Change regulator-coupled-max-spread property Redefine binding for regulator-coupled-max-spread property in a way that max-spread values are defined per regulator couple instead of defining single max-spread for the whole group of coupled regulators. With that change the following regulators coupling configuration will be possible: regA: regulatorA { regulator-coupled-with = <®B ®C>; regulator-coupled-max-spread = <100000 300000>; }; regB: regulatorB { regulator-coupled-with = <®A ®C>; regulator-coupled-max-spread = <100000 200000>; }; regC: regulatorC { regulator-coupled-with = <®A ®B>; regulator-coupled-max-spread = <300000 200000>; }; Note that the regulator-coupled-max-spread property does not have any users yet, hence it's okay to change the binding. Signed-off-by: Dmitry Osipenko Signed-off-by: Mark Brown --- Documentation/devicetree/bindings/regulator/regulator.txt | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/Documentation/devicetree/bindings/regulator/regulator.txt b/Documentation/devicetree/bindings/regulator/regulator.txt index ca96f70bd2def2..e3a7a7a0316b4d 100644 --- a/Documentation/devicetree/bindings/regulator/regulator.txt +++ b/Documentation/devicetree/bindings/regulator/regulator.txt @@ -79,8 +79,9 @@ Optional properties: - regulator-coupled-with: Regulators with which the regulator is coupled. The linkage is 2-way - all coupled regulators should be linked with each other. A regulator should not be coupled with its supplier. -- regulator-coupled-max-spread: Max spread between voltages of coupled regulators - in microvolts. +- regulator-coupled-max-spread: Array of maximum spread between voltages of + coupled regulators in microvolts, each value in the array relates to the + corresponding couple specified by the regulator-coupled-with property. Deprecated properties: - regulator-compatible: If a regulator chip contains multiple From d7c7fc442fa6f6f0cd0115d7b8dbdf6fe2eeea44 Mon Sep 17 00:00:00 2001 From: Marek Szyprowski Date: Tue, 20 Nov 2018 13:38:44 +0100 Subject: [PATCH 27/85] regulator: s2mps11: Fix GPIO descriptor initialization GPIO descriptor array must be zero initialized to ensure that core will properly handle also the case when no external GPIO pin is defined. Fixes: 1c984942f0a4 ("regulator: s2mps11: Pass descriptor instead of GPIO number") Signed-off-by: Marek Szyprowski Reviewed-by: Krzysztof Kozlowski Signed-off-by: Mark Brown --- drivers/regulator/s2mps11.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/drivers/regulator/s2mps11.c b/drivers/regulator/s2mps11.c index 6fec4589719410..63e66f485cc084 100644 --- a/drivers/regulator/s2mps11.c +++ b/drivers/regulator/s2mps11.c @@ -1134,9 +1134,8 @@ static int s2mps11_pmic_probe(struct platform_device *pdev) return -EINVAL; } - s2mps11->ext_control_gpiod = devm_kmalloc_array(&pdev->dev, - rdev_num, sizeof(*s2mps11->ext_control_gpiod), - GFP_KERNEL); + s2mps11->ext_control_gpiod = devm_kcalloc(&pdev->dev, rdev_num, + sizeof(*s2mps11->ext_control_gpiod), GFP_KERNEL); if (!s2mps11->ext_control_gpiod) return -ENOMEM; From d90acbc4e3ddd72ccccebcaf9cdc7ed8da669132 Mon Sep 17 00:00:00 2001 From: Charles Keepax Date: Tue, 20 Nov 2018 14:16:24 +0000 Subject: [PATCH 28/85] regulator: lochnagar: Move driver to binding from DT Based on review comments on the MFD driver, move the child drivers for the Lochnagar MFD over to binding through device tree. Signed-off-by: Charles Keepax Signed-off-by: Mark Brown --- drivers/regulator/lochnagar-regulator.c | 48 +++++++++++++++++++------ 1 file changed, 37 insertions(+), 11 deletions(-) diff --git a/drivers/regulator/lochnagar-regulator.c b/drivers/regulator/lochnagar-regulator.c index 182198246479fa..5a89e6d4b9a6bd 100644 --- a/drivers/regulator/lochnagar-regulator.c +++ b/drivers/regulator/lochnagar-regulator.c @@ -13,6 +13,7 @@ #include #include #include +#include #include #include #include @@ -214,28 +215,52 @@ static const struct regulator_desc lochnagar_regulators[] = { }, }; +static const struct of_device_id lochnagar_of_match[] = { + { + .compatible = "cirrus,lochnagar2-micvdd", + .data = &lochnagar_regulators[LOCHNAGAR_MICVDD], + }, + { + .compatible = "cirrus,lochnagar2-mic1vdd", + .data = &lochnagar_regulators[LOCHNAGAR_MIC1VDD], + }, + { + .compatible = "cirrus,lochnagar2-mic2vdd", + .data = &lochnagar_regulators[LOCHNAGAR_MIC1VDD], + }, + { + .compatible = "cirrus,lochnagar2-vddcore", + .data = &lochnagar_regulators[LOCHNAGAR_VDDCORE], + }, + {}, +}; + 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 = { }; + const struct of_device_id *of_id; + const struct regulator_desc *desc; struct regulator_dev *rdev; - int ret, i; + int ret; - config.dev = lochnagar->dev; + config.dev = 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]; + of_id = of_match_device(lochnagar_of_match, dev); + if (!of_id) + return -EINVAL; - 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; - } + desc = of_id->data; + + 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; @@ -244,6 +269,7 @@ static int lochnagar_regulator_probe(struct platform_device *pdev) static struct platform_driver lochnagar_regulator_driver = { .driver = { .name = "lochnagar-regulator", + .of_match_table = of_match_ptr(lochnagar_of_match), }, .probe = lochnagar_regulator_probe, From 9e6d9c9b737326d880e52ebe5a845f4afde378ea Mon Sep 17 00:00:00 2001 From: Charles Keepax Date: Tue, 20 Nov 2018 14:16:28 +0000 Subject: [PATCH 29/85] regulator: lochnagar: Add initial binding documentation Lochnagar is an evaluation and development board for Cirrus Logic Smart CODEC and Amp devices. It allows the connection of most Cirrus Logic devices on mini-cards, as well as allowing connection of various application processor systems to provide a full evaluation platform. This driver supports the board controller chip on the Lochnagar board. Signed-off-by: Charles Keepax Signed-off-by: Mark Brown --- .../bindings/regulator/cirrus,lochnagar.txt | 82 +++++++++++++++++++ 1 file changed, 82 insertions(+) create mode 100644 Documentation/devicetree/bindings/regulator/cirrus,lochnagar.txt diff --git a/Documentation/devicetree/bindings/regulator/cirrus,lochnagar.txt b/Documentation/devicetree/bindings/regulator/cirrus,lochnagar.txt new file mode 100644 index 00000000000000..91974e6ee251b3 --- /dev/null +++ b/Documentation/devicetree/bindings/regulator/cirrus,lochnagar.txt @@ -0,0 +1,82 @@ +Cirrus Logic Lochnagar Audio Development Board + +Lochnagar is an evaluation and development board for Cirrus Logic +Smart CODEC and Amp devices. It allows the connection of most Cirrus +Logic devices on mini-cards, as well as allowing connection of +various application processor systems to provide a full evaluation +platform. Audio system topology, clocking and power can all be +controlled through the Lochnagar, allowing the device under test +to be used in a variety of possible use cases. + +This binding document describes the binding for the regulator portion +of the driver. + +Also see these documents for generic binding information: + [1] Regulator: ../regulator/regulator.txt + +This binding must be part of the Lochnagar MFD binding: + [2] ../mfd/cirrus,lochnagar.txt + +Optional sub-nodes: + + - VDDCORE : Initialisation data for the VDDCORE regulator, which + supplies the CODECs digital core if it has no build regulator for that + purpose. + Required Properties: + - compatible : One of the following strings: + "cirrus,lochnagar2-vddcore" + - SYSVDD-supply: Primary power supply for the Lochnagar. + + - MICVDD : Initialisation data for the MICVDD regulator, which + supplies the CODECs MICVDD. + Required Properties: + - compatible : One of the following strings: + "cirrus,lochnagar2-micvdd" + - SYSVDD-supply: Primary power supply for the Lochnagar. + + - MIC1VDD, MIC2VDD : Initialisation data for the MICxVDD supplies. + Required Properties: + - compatible : One of the following strings: + "cirrus,lochnagar2-mic1vdd", "cirrus,lochnagar2-mic2vdd" + Optional Properties: + - cirrus,micbias-input : A property selecting which of the CODEC + minicard micbias outputs should be used, valid values are 1 - 4. + - MICBIAS1-supply, MICBIAS2-supply: Regulator supplies for the + MICxVDD outputs, supplying the digital microphones, normally + supplied from the attached CODEC. + + - VDD1V8 : Recommended fixed regulator for the VDD1V8 regulator, which supplies the + CODECs analog and 1.8V digital supplies. + Required Properties: + - compatible : Should be set to "regulator-fixed" + - regulator-min-microvolt : Should be set to 1.8V + - regulator-max-microvolt : Should be set to 1.8V + - regulator-boot-on + - regulator-always-on + - vin-supply : Should be set to same supply as SYSVDD + +Example: + +lochnagar { + lochnagar-micvdd: MICVDD { + compatible = "cirrus,lochnagar2-micvdd"; + + SYSVDD-supply = <&wallvdd>; + + regulator-min-microvolt = <3300000>; + regulator-max-microvolt = <3300000>; + }; + + lochnagar-vdd1v8: VDD1V8 { + compatible = "regulator-fixed"; + + regulator-name = "VDD1V8"; + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1800000>; + regulator-boot-on; + regulator-always-on; + + vin-supply = <&wallvdd>; + }; +}; + From 7b51a821211eb6217f60962c31f30cb73e30a0c7 Mon Sep 17 00:00:00 2001 From: Douglas Anderson Date: Mon, 19 Nov 2018 16:26:48 -0800 Subject: [PATCH 30/85] regulator: core: Properly expose requested_microamps in sysfs The "requested_microamps" sysfs attribute was only being exposed for "current" regulators. This didn't make sense. Allow it to be exposed always. Signed-off-by: Douglas Anderson Signed-off-by: Mark Brown --- drivers/regulator/core.c | 4 ---- 1 file changed, 4 deletions(-) diff --git a/drivers/regulator/core.c b/drivers/regulator/core.c index d7ffd7b1247228..ff5ca185bb8f56 100644 --- a/drivers/regulator/core.c +++ b/drivers/regulator/core.c @@ -4565,10 +4565,6 @@ static umode_t regulator_attr_is_visible(struct kobject *kobj, if (attr == &dev_attr_bypass.attr) return ops->get_bypass ? mode : 0; - /* some attributes are type-specific */ - if (attr == &dev_attr_requested_microamps.attr) - return rdev->desc->type == REGULATOR_CURRENT ? mode : 0; - /* constraints need specific supporting methods */ if (attr == &dev_attr_min_microvolts.attr || attr == &dev_attr_max_microvolts.attr) From 466affa06703d5668b06b25fe34942a784506b62 Mon Sep 17 00:00:00 2001 From: Charles Keepax Date: Wed, 21 Nov 2018 10:13:16 +0000 Subject: [PATCH 31/85] regulator: wm8994: Don't use devres for enable GPIOs The regulator core takes over managing the lifetime of the enable GPIO once the regulator is registered. As such we shouldn't register the enable GPIO using devm, or it will be freed twice. Reported-by: Marek Szyprowski Signed-off-by: Charles Keepax Tested-by: Marek Szyprowski Signed-off-by: Mark Brown --- drivers/regulator/wm8994-regulator.c | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/drivers/regulator/wm8994-regulator.c b/drivers/regulator/wm8994-regulator.c index d7fec533c40322..46e6b4ee1491aa 100644 --- a/drivers/regulator/wm8994-regulator.c +++ b/drivers/regulator/wm8994-regulator.c @@ -147,11 +147,14 @@ static int wm8994_ldo_probe(struct platform_device *pdev) config.regmap = wm8994->regmap; config.init_data = &ldo->init_data; - /* Look up LDO enable GPIO from the parent device node */ - gpiod = devm_gpiod_get_optional(pdev->dev.parent, - id ? "wlf,ldo2ena" : "wlf,ldo1ena", - GPIOD_OUT_LOW | - GPIOD_FLAGS_BIT_NONEXCLUSIVE); + /* + * Look up LDO enable GPIO from the parent device node, we don't + * use devm because the regulator core will free the GPIO + */ + gpiod = gpiod_get_optional(pdev->dev.parent, + id ? "wlf,ldo2ena" : "wlf,ldo1ena", + GPIOD_OUT_LOW | + GPIOD_FLAGS_BIT_NONEXCLUSIVE); if (IS_ERR(gpiod)) return PTR_ERR(gpiod); config.ena_gpiod = gpiod; @@ -184,6 +187,7 @@ static int wm8994_ldo_probe(struct platform_device *pdev) return 0; err: + gpiod_put(gpiod); return ret; } From 5451781dadf85000665e0e2c3288e9e0f34b860a Mon Sep 17 00:00:00 2001 From: Douglas Anderson Date: Tue, 20 Nov 2018 09:52:53 -0800 Subject: [PATCH 32/85] regulator: core: Only count load for enabled consumers In general when the consumer of a regulator requests that the regulator be disabled it no longer will be drawing much load from the regulator--it should just be the leakage current and that should be very close to 0. Up to this point the regulator framework has continued to count a consumer's load request for disabled regulators. This has led to code patterns that look like this: enable_my_thing(): regular_set_load(reg, load_uA) regulator_enable(reg) disable_my_thing(): regulator_disable(reg) regulator_set_load(reg, 0) Sometimes disable_my_thing() sets a nominal (<= 100 uA) load instead of setting a 0 uA load. I will make the assertion that nearly all (if not all) places where we set a nominal load of 100 uA or less we end up with a result that is the same as if we had set a load of 0 uA. Specifically: - The whole point of setting the load is to help set the operating mode of the regulator. Higher loads may need less efficient operating modes. - The only time this matters at all is if there is another consumer of the regulator that wants the regulator on. If there are no other consumers of the regulator then the regulator will turn off and we don't care about the operating mode. - If there's another consumer that actually wants the regulator on then presumably it is requesting a load that makes our nominal <= 100 uA load insignificant. A quick survey of the existing callers to regulator_set_load() to see how everyone uses it: Signed-off-by: Douglas Anderson Signed-off-by: Mark Brown --- drivers/regulator/core.c | 193 +++++++++++++++++++++++-------- drivers/regulator/internal.h | 2 + include/linux/regulator/driver.h | 1 - 3 files changed, 144 insertions(+), 52 deletions(-) diff --git a/drivers/regulator/core.c b/drivers/regulator/core.c index ff5ca185bb8f56..26a0c523ed86cb 100644 --- a/drivers/regulator/core.c +++ b/drivers/regulator/core.c @@ -99,7 +99,7 @@ struct regulator_supply_alias { }; static int _regulator_is_enabled(struct regulator_dev *rdev); -static int _regulator_disable(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); @@ -764,8 +764,10 @@ static ssize_t regulator_total_uA_show(struct device *dev, int uA = 0; regulator_lock(rdev); - list_for_each_entry(regulator, &rdev->consumer_list, list) - uA += regulator->uA_load; + list_for_each_entry(regulator, &rdev->consumer_list, list) { + if (regulator->enable_count) + uA += regulator->uA_load; + } regulator_unlock(rdev); return sprintf(buf, "%d\n", uA); } @@ -938,8 +940,10 @@ static int drms_uA_update(struct regulator_dev *rdev) return -EINVAL; /* calc total requested load */ - list_for_each_entry(sibling, &rdev->consumer_list, list) - current_uA += sibling->uA_load; + list_for_each_entry(sibling, &rdev->consumer_list, list) { + if (sibling->enable_count) + current_uA += sibling->uA_load; + } current_uA += rdev->constraints->system_load; @@ -2024,6 +2028,9 @@ static void _regulator_put(struct regulator *regulator) lockdep_assert_held_once(®ulator_list_mutex); + /* Docs say you must disable before calling regulator_put() */ + WARN_ON(regulator->enable_count); + rdev = regulator->rdev; debugfs_remove_recursive(regulator->debugfs); @@ -2417,15 +2424,75 @@ static int _regulator_do_enable(struct regulator_dev *rdev) return 0; } +/** + * _regulator_handle_consumer_enable - handle that a consumer enabled + * @regulator: regulator source + * + * Some things on a regulator consumer (like the contribution towards total + * load on the regulator) only have an effect when the consumer wants the + * regulator enabled. Explained in example with two consumers of the same + * regulator: + * consumer A: set_load(100); => total load = 0 + * consumer A: regulator_enable(); => total load = 100 + * consumer B: set_load(1000); => total load = 100 + * consumer B: regulator_enable(); => total load = 1100 + * consumer A: regulator_disable(); => total_load = 1000 + * + * This function (together with _regulator_handle_consumer_disable) is + * responsible for keeping track of the refcount for a given regulator consumer + * and applying / unapplying these things. + * + * Returns 0 upon no error; -error upon error. + */ +static int _regulator_handle_consumer_enable(struct regulator *regulator) +{ + struct regulator_dev *rdev = regulator->rdev; + + lockdep_assert_held_once(&rdev->mutex.base); + + regulator->enable_count++; + if (regulator->uA_load && regulator->enable_count == 1) + return drms_uA_update(rdev); + + return 0; +} + +/** + * _regulator_handle_consumer_disable - handle that a consumer disabled + * @regulator: regulator source + * + * The opposite of _regulator_handle_consumer_enable(). + * + * Returns 0 upon no error; -error upon error. + */ +static int _regulator_handle_consumer_disable(struct regulator *regulator) +{ + struct regulator_dev *rdev = regulator->rdev; + + lockdep_assert_held_once(&rdev->mutex.base); + + if (!regulator->enable_count) { + rdev_err(rdev, "Underflow of regulator enable count\n"); + return -EINVAL; + } + + regulator->enable_count--; + if (regulator->uA_load && regulator->enable_count == 0) + return drms_uA_update(rdev); + + return 0; +} + /* locks held by regulator_enable() */ -static int _regulator_enable(struct regulator_dev *rdev) +static int _regulator_enable(struct regulator *regulator) { + struct regulator_dev *rdev = regulator->rdev; int ret; lockdep_assert_held_once(&rdev->mutex.base); if (rdev->supply) { - ret = _regulator_enable(rdev->supply->rdev); + ret = _regulator_enable(rdev->supply); if (ret < 0) return ret; } @@ -2437,9 +2504,9 @@ static int _regulator_enable(struct regulator_dev *rdev) goto err_disable_supply; } - /* check voltage and requested load before enabling */ - if (regulator_ops_is_valid(rdev, REGULATOR_CHANGE_DRMS)) - drms_uA_update(rdev); + ret = _regulator_handle_consumer_enable(regulator); + if (ret < 0) + goto err_disable_supply; if (rdev->use_count == 0) { /* The regulator may on if it's not switchable or left on */ @@ -2448,18 +2515,18 @@ static int _regulator_enable(struct regulator_dev *rdev) if (!regulator_ops_is_valid(rdev, REGULATOR_CHANGE_STATUS)) { ret = -EPERM; - goto err_disable_supply; + goto err_consumer_disable; } ret = _regulator_do_enable(rdev); if (ret < 0) - goto err_disable_supply; + goto err_consumer_disable; _notifier_call_chain(rdev, REGULATOR_EVENT_ENABLE, NULL); } else if (ret < 0) { rdev_err(rdev, "is_enabled() failed: %d\n", ret); - goto err_disable_supply; + goto err_consumer_disable; } /* Fallthrough on positive return values - already enabled */ } @@ -2468,9 +2535,12 @@ static int _regulator_enable(struct regulator_dev *rdev) return 0; +err_consumer_disable: + _regulator_handle_consumer_disable(regulator); + err_disable_supply: if (rdev->supply) - _regulator_disable(rdev->supply->rdev); + _regulator_disable(rdev->supply); return ret; } @@ -2490,13 +2560,10 @@ int regulator_enable(struct regulator *regulator) { struct regulator_dev *rdev = regulator->rdev; struct ww_acquire_ctx ww_ctx; - int ret = 0; - - if (regulator->always_on) - return 0; + int ret; regulator_lock_dependent(rdev, &ww_ctx); - ret = _regulator_enable(rdev); + ret = _regulator_enable(regulator); regulator_unlock_dependent(rdev, &ww_ctx); return ret; @@ -2535,8 +2602,9 @@ static int _regulator_do_disable(struct regulator_dev *rdev) } /* locks held by regulator_disable() */ -static int _regulator_disable(struct regulator_dev *rdev) +static int _regulator_disable(struct regulator *regulator) { + struct regulator_dev *rdev = regulator->rdev; int ret = 0; lockdep_assert_held_once(&rdev->mutex.base); @@ -2571,17 +2639,17 @@ static int _regulator_disable(struct regulator_dev *rdev) rdev->use_count = 0; } else if (rdev->use_count > 1) { - if (regulator_ops_is_valid(rdev, REGULATOR_CHANGE_DRMS)) - drms_uA_update(rdev); - rdev->use_count--; } + if (ret == 0) + ret = _regulator_handle_consumer_disable(regulator); + if (ret == 0 && rdev->coupling_desc.n_coupled > 1) ret = regulator_balance_voltage(rdev, PM_SUSPEND_ON); if (ret == 0 && rdev->supply) - ret = _regulator_disable(rdev->supply->rdev); + ret = _regulator_disable(rdev->supply); return ret; } @@ -2602,13 +2670,10 @@ int regulator_disable(struct regulator *regulator) { struct regulator_dev *rdev = regulator->rdev; struct ww_acquire_ctx ww_ctx; - int ret = 0; - - if (regulator->always_on) - return 0; + int ret; regulator_lock_dependent(rdev, &ww_ctx); - ret = _regulator_disable(rdev); + ret = _regulator_disable(regulator); regulator_unlock_dependent(rdev, &ww_ctx); return ret; @@ -2657,10 +2722,17 @@ int regulator_force_disable(struct regulator *regulator) int ret; regulator_lock_dependent(rdev, &ww_ctx); - regulator->uA_load = 0; + ret = _regulator_force_disable(regulator->rdev); + if (rdev->coupling_desc.n_coupled > 1) regulator_balance_voltage(rdev, PM_SUSPEND_ON); + + if (regulator->uA_load) { + regulator->uA_load = 0; + ret = drms_uA_update(rdev); + } + regulator_unlock_dependent(rdev, &ww_ctx); if (rdev->supply) @@ -2677,14 +2749,11 @@ static void regulator_disable_work(struct work_struct *work) disable_work.work); struct ww_acquire_ctx ww_ctx; int count, i, ret; + struct regulator *regulator; + int total_count = 0; regulator_lock_dependent(rdev, &ww_ctx); - BUG_ON(!rdev->deferred_disables); - - count = rdev->deferred_disables; - rdev->deferred_disables = 0; - /* * Workqueue functions queue the new work instance while the previous * work instance is being processed. Cancel the queued work instance @@ -2693,11 +2762,22 @@ static void regulator_disable_work(struct work_struct *work) */ cancel_delayed_work(&rdev->disable_work); - for (i = 0; i < count; i++) { - ret = _regulator_disable(rdev); - if (ret != 0) - rdev_err(rdev, "Deferred disable failed: %d\n", ret); + list_for_each_entry(regulator, &rdev->consumer_list, list) { + count = regulator->deferred_disables; + + if (!count) + continue; + + total_count += count; + regulator->deferred_disables = 0; + + for (i = 0; i < count; i++) { + ret = _regulator_disable(regulator); + if (ret != 0) + rdev_err(rdev, "Deferred disable failed: %d\n", ret); + } } + WARN_ON(!total_count); if (rdev->coupling_desc.n_coupled > 1) regulator_balance_voltage(rdev, PM_SUSPEND_ON); @@ -2731,14 +2811,11 @@ int regulator_disable_deferred(struct regulator *regulator, int ms) { struct regulator_dev *rdev = regulator->rdev; - if (regulator->always_on) - return 0; - if (!ms) return regulator_disable(regulator); regulator_lock(rdev); - rdev->deferred_disables++; + regulator->deferred_disables++; mod_delayed_work(system_power_efficient_wq, &rdev->disable_work, msecs_to_jiffies(ms)); regulator_unlock(rdev); @@ -4145,16 +4222,30 @@ EXPORT_SYMBOL_GPL(regulator_get_error_flags); * DRMS will sum the total requested load on the regulator and change * to the most efficient operating mode if platform constraints allow. * + * NOTE: when a regulator consumer requests to have a regulator + * disabled then any load that consumer requested no longer counts + * toward the total requested load. If the regulator is re-enabled + * then the previously requested load will start counting again. + * + * If a regulator is an always-on regulator then an individual consumer's + * load will still be removed if that consumer is fully disabled. + * * On error a negative errno is returned. */ int regulator_set_load(struct regulator *regulator, int uA_load) { struct regulator_dev *rdev = regulator->rdev; - int ret; + int old_uA_load; + int ret = 0; regulator_lock(rdev); + old_uA_load = regulator->uA_load; regulator->uA_load = uA_load; - ret = drms_uA_update(rdev); + if (regulator->enable_count && old_uA_load != uA_load) { + ret = drms_uA_update(rdev); + if (ret < 0) + regulator->uA_load = old_uA_load; + } regulator_unlock(rdev); return ret; @@ -4325,11 +4416,8 @@ int regulator_bulk_enable(int num_consumers, int ret = 0; for (i = 0; i < num_consumers; i++) { - if (consumers[i].consumer->always_on) - consumers[i].ret = 0; - else - async_schedule_domain(regulator_bulk_enable_async, - &consumers[i], &async_domain); + async_schedule_domain(regulator_bulk_enable_async, + &consumers[i], &async_domain); } async_synchronize_full_domain(&async_domain); @@ -5225,8 +5313,11 @@ static void regulator_summary_show_subtree(struct seq_file *s, switch (rdev->desc->type) { case REGULATOR_VOLTAGE: - seq_printf(s, "%37dmA %5dmV %5dmV", + seq_printf(s, "%3d %33dmA%c%5dmV %5dmV", + consumer->enable_count, consumer->uA_load / 1000, + consumer->uA_load && !consumer->enable_count ? + '*' : ' ', consumer->voltage[PM_SUSPEND_ON].min_uV / 1000, consumer->voltage[PM_SUSPEND_ON].max_uV / 1000); break; diff --git a/drivers/regulator/internal.h b/drivers/regulator/internal.h index 943926a156f201..6017f15c5d7510 100644 --- a/drivers/regulator/internal.h +++ b/drivers/regulator/internal.h @@ -42,6 +42,8 @@ struct regulator { unsigned int always_on:1; unsigned int bypass:1; int uA_load; + unsigned int enable_count; + unsigned int deferred_disables; struct regulator_voltage voltage[REGULATOR_STATES_NUM]; const char *supply_name; struct device_attribute dev_attr; diff --git a/include/linux/regulator/driver.h b/include/linux/regulator/driver.h index 7065031f084657..389bcaf7900f6a 100644 --- a/include/linux/regulator/driver.h +++ b/include/linux/regulator/driver.h @@ -474,7 +474,6 @@ struct regulator_dev { struct regmap *regmap; struct delayed_work disable_work; - int deferred_disables; void *reg_data; /* regulator_dev data */ From 1fc12b05895ea50386f0e666f84053f0c8307943 Mon Sep 17 00:00:00 2001 From: Douglas Anderson Date: Tue, 20 Nov 2018 09:52:54 -0800 Subject: [PATCH 33/85] regulator: core: Avoid propagating to supplies when possible When we called regulator_enable() on a regulator we'd end up propagating that call all the way up the chain every time. This is a bit of a waste of time. A child regulator already refcounts its own enables so it should avoid passing on to its parent unless the refcount transitioned between 0 and 1. Historically this hasn't been a huge problem since we skipped dealing with enable for always-on regulators. In a previous patch, however, we removed the always-on optimization. On one system, the debugfs regulator_summary was now showing a "use_count" of 33 for a top-level regulator. Let's implement this optimization. This turns out to be fairly trivial with the recent reorganization of the regulator core. NOTE: as part of this patch I'll make "always-on" regulators start with a use count of 1. This keeps the counts clean when recursively resolving regulators. ALSO NOTE: this commit also contains somewhat of a bug fix to regulator_force_disable(). It was incorrectly looping over "rdev->open_count" when it should have been looping over use_count. We have to touch that code anyway (since we should no longer loop at all), so we'll fix it together in one patch. Also: since this comes after commit f8702f9e4aa7 ("regulator: core: Use ww_mutex for regulators locking") we can now move to use _regulator_disable() for our supply and keep it in the lock. Signed-off-by: Douglas Anderson Signed-off-by: Mark Brown --- drivers/regulator/core.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/drivers/regulator/core.c b/drivers/regulator/core.c index 26a0c523ed86cb..03a03763457c15 100644 --- a/drivers/regulator/core.c +++ b/drivers/regulator/core.c @@ -1822,6 +1822,7 @@ static int regulator_resolve_supply(struct regulator_dev *rdev) rdev->supply = NULL; return ret; } + rdev->use_count = 1; } return 0; @@ -2491,7 +2492,7 @@ static int _regulator_enable(struct regulator *regulator) lockdep_assert_held_once(&rdev->mutex.base); - if (rdev->supply) { + if (rdev->use_count == 0 && rdev->supply) { ret = _regulator_enable(rdev->supply); if (ret < 0) return ret; @@ -2539,7 +2540,7 @@ static int _regulator_enable(struct regulator *regulator) _regulator_handle_consumer_disable(regulator); err_disable_supply: - if (rdev->supply) + if (rdev->use_count == 0 && rdev->supply) _regulator_disable(rdev->supply); return ret; @@ -2648,7 +2649,7 @@ static int _regulator_disable(struct regulator *regulator) if (ret == 0 && rdev->coupling_desc.n_coupled > 1) ret = regulator_balance_voltage(rdev, PM_SUSPEND_ON); - if (ret == 0 && rdev->supply) + if (ret == 0 && rdev->use_count == 0 && rdev->supply) ret = _regulator_disable(rdev->supply); return ret; @@ -2733,11 +2734,10 @@ int regulator_force_disable(struct regulator *regulator) ret = drms_uA_update(rdev); } - regulator_unlock_dependent(rdev, &ww_ctx); + if (rdev->use_count != 0 && rdev->supply) + _regulator_disable(rdev->supply); - if (rdev->supply) - while (rdev->open_count--) - regulator_disable(rdev->supply); + regulator_unlock_dependent(rdev, &ww_ctx); return ret; } From 478652f3d45cc6f0ef9be3cda8b13fcd85076fa6 Mon Sep 17 00:00:00 2001 From: Ryan Case Date: Mon, 22 Oct 2018 18:07:07 -0700 Subject: [PATCH 34/85] spi: spi-qcom-qspi: Fix remaining driver nits Address remaining comments from original driver patch series * Move RD_FIFO_CFG to be ordered corretly * Expand spinlock comment Signed-off-by: Ryan Case Reviewed-by: Stephen Boyd Reviewed-by: Douglas Anderson Signed-off-by: Mark Brown --- drivers/spi/spi-qcom-qspi.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/spi/spi-qcom-qspi.c b/drivers/spi/spi-qcom-qspi.c index b8163b40bb92c5..e0f061139c8f5b 100644 --- a/drivers/spi/spi-qcom-qspi.c +++ b/drivers/spi/spi-qcom-qspi.c @@ -90,6 +90,9 @@ #define PIO_DATAOUT_1B 0x0020 #define PIO_DATAOUT_4B 0x0024 +#define RD_FIFO_CFG 0x0028 +#define CONTINUOUS_MODE BIT(0) + #define RD_FIFO_STATUS 0x002c #define FIFO_EMPTY BIT(11) #define WR_CNTS_MSK 0x7f0 @@ -99,9 +102,6 @@ #define RDY_16BYTE BIT(1) #define FIFO_RDY BIT(0) -#define RD_FIFO_CFG 0x0028 -#define CONTINUOUS_MODE BIT(0) - #define RD_FIFO_RESET 0x0030 #define RESET_FIFO BIT(0) @@ -139,7 +139,7 @@ struct qcom_qspi { struct device *dev; struct clk_bulk_data clks[QSPI_NUM_CLKS]; struct qspi_xfer xfer; - /* Lock to protect data accessed by IRQs */ + /* Lock to protect xfer and IRQ accessed registers */ spinlock_t lock; }; From fa94e48e13a1aaf814b9e956d8e5a52ef303b569 Mon Sep 17 00:00:00 2001 From: Douglas Anderson Date: Mon, 26 Nov 2018 09:08:27 -0800 Subject: [PATCH 35/85] regulator: core: Apply system load even if no consumer loads Prior to commit 5451781dadf8 ("regulator: core: Only count load for enabled consumers") we used to always add up the total load on every enable in _regulator_enable(). After that commit we only updated the total load when enabling / disabling a regulator where a consumer specified a load or when changing the consumer load on an enabled regulator. The problem with the new scheme is that if there is a system load specified for a regulator but no consumers specify a load then we never account for it. Let's account for the system load in set_machine_constraints(). NOTE: with the new scheme we end up with a bit of a quandry. What if someone specifies _both_ an initial mode and a system load? If we take the system load into account right at init time then it will effectively clobber the initial mode. We'll resolve this by saying that if both are specified then the initial mode will win. The system load will then only take effect if/when a consumer specifies a load. If no consumers ever specify a load then the initial mode will persist and the system load will have no effect. Fixes: 5451781dadf8 ("regulator: core: Only count load for enabled consumers") Reported-by: Brian Masney Signed-off-by: Douglas Anderson Tested-by: Brian Masney Signed-off-by: Mark Brown --- drivers/regulator/core.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/drivers/regulator/core.c b/drivers/regulator/core.c index 03a03763457c15..757619878068c9 100644 --- a/drivers/regulator/core.c +++ b/drivers/regulator/core.c @@ -1342,6 +1342,12 @@ static int set_machine_constraints(struct regulator_dev *rdev, rdev_err(rdev, "failed to set initial mode: %d\n", ret); return ret; } + } else if (rdev->constraints->system_load) { + /* + * We'll only apply the initial system load if an + * initial mode wasn't specified. + */ + drms_uA_update(rdev); } /* If the constraints say the regulator should be on at this point From db4a555f7c4cfcca26ca3a4ff5f9ee9bd6a78f98 Mon Sep 17 00:00:00 2001 From: Olliver Schinagl Date: Mon, 26 Nov 2018 17:27:42 +0200 Subject: [PATCH 36/85] regulator: axp20x: use defines for masks The AXP20X driver currently has several masks defined throughout the code. Use nice defines to make them clean and more descriptive. Additionally include bitops.h, which was missing before, and sort headers. Signed-off-by: Olliver Schinagl Signed-off-by: Priit Laes Signed-off-by: Mark Brown --- drivers/regulator/axp20x-regulator.c | 733 ++++++++++++++++++++------- 1 file changed, 555 insertions(+), 178 deletions(-) diff --git a/drivers/regulator/axp20x-regulator.c b/drivers/regulator/axp20x-regulator.c index a3734039a86a78..9a2db28effb528 100644 --- a/drivers/regulator/axp20x-regulator.c +++ b/drivers/regulator/axp20x-regulator.c @@ -13,31 +13,249 @@ * GNU General Public License for more details. */ +#include #include #include +#include #include #include #include #include #include -#include #include #include +#define AXP20X_GPIO0_FUNC_MASK GENMASK(3, 0) +#define AXP20X_GPIO1_FUNC_MASK GENMASK(3, 0) + #define AXP20X_IO_ENABLED 0x03 #define AXP20X_IO_DISABLED 0x07 +#define AXP20X_WORKMODE_DCDC2_MASK BIT_MASK(2) +#define AXP20X_WORKMODE_DCDC3_MASK BIT_MASK(1) + +#define AXP20X_FREQ_DCDC_MASK GENMASK(3, 0) + +#define AXP20X_VBUS_IPSOUT_MGMT_MASK BIT_MASK(2) + +#define AXP20X_DCDC2_V_OUT_MASK GENMASK(5, 0) +#define AXP20X_DCDC3_V_OUT_MASK GENMASK(7, 0) +#define AXP20X_LDO24_V_OUT_MASK GENMASK(7, 4) +#define AXP20X_LDO3_V_OUT_MASK GENMASK(6, 0) +#define AXP20X_LDO5_V_OUT_MASK GENMASK(7, 4) + +#define AXP20X_PWR_OUT_EXTEN_MASK BIT_MASK(0) +#define AXP20X_PWR_OUT_DCDC3_MASK BIT_MASK(1) +#define AXP20X_PWR_OUT_LDO2_MASK BIT_MASK(2) +#define AXP20X_PWR_OUT_LDO4_MASK BIT_MASK(3) +#define AXP20X_PWR_OUT_DCDC2_MASK BIT_MASK(4) +#define AXP20X_PWR_OUT_LDO3_MASK BIT_MASK(6) + +#define AXP20X_LDO4_V_OUT_1250mV_START 0x0 +#define AXP20X_LDO4_V_OUT_1250mV_STEPS 0 +#define AXP20X_LDO4_V_OUT_1250mV_END \ + (AXP20X_LDO4_V_OUT_1250mV_START + AXP20X_LDO4_V_OUT_1250mV_STEPS) +#define AXP20X_LDO4_V_OUT_1300mV_START 0x1 +#define AXP20X_LDO4_V_OUT_1300mV_STEPS 7 +#define AXP20X_LDO4_V_OUT_1300mV_END \ + (AXP20X_LDO4_V_OUT_1300mV_START + AXP20X_LDO4_V_OUT_1300mV_STEPS) +#define AXP20X_LDO4_V_OUT_2500mV_START 0x9 +#define AXP20X_LDO4_V_OUT_2500mV_STEPS 0 +#define AXP20X_LDO4_V_OUT_2500mV_END \ + (AXP20X_LDO4_V_OUT_2500mV_START + AXP20X_LDO4_V_OUT_2500mV_STEPS) +#define AXP20X_LDO4_V_OUT_2700mV_START 0xa +#define AXP20X_LDO4_V_OUT_2700mV_STEPS 1 +#define AXP20X_LDO4_V_OUT_2700mV_END \ + (AXP20X_LDO4_V_OUT_2700mV_START + AXP20X_LDO4_V_OUT_2700mV_STEPS) +#define AXP20X_LDO4_V_OUT_3000mV_START 0xc +#define AXP20X_LDO4_V_OUT_3000mV_STEPS 3 +#define AXP20X_LDO4_V_OUT_3000mV_END \ + (AXP20X_LDO4_V_OUT_3000mV_START + AXP20X_LDO4_V_OUT_3000mV_STEPS) +#define AXP20X_LDO4_V_OUT_NUM_VOLTAGES 16 + #define AXP22X_IO_ENABLED 0x03 #define AXP22X_IO_DISABLED 0x04 -#define AXP20X_WORKMODE_DCDC2_MASK BIT(2) -#define AXP20X_WORKMODE_DCDC3_MASK BIT(1) -#define AXP22X_WORKMODE_DCDCX_MASK(x) BIT(x) - -#define AXP20X_FREQ_DCDC_MASK 0x0f +#define AXP22X_WORKMODE_DCDCX_MASK(x) BIT_MASK(x) #define AXP22X_MISC_N_VBUSEN_FUNC BIT(4) +#define AXP22X_DCDC1_V_OUT_MASK GENMASK(4, 0) +#define AXP22X_DCDC2_V_OUT_MASK GENMASK(5, 0) +#define AXP22X_DCDC3_V_OUT_MASK GENMASK(5, 0) +#define AXP22X_DCDC4_V_OUT_MASK GENMASK(5, 0) +#define AXP22X_DCDC5_V_OUT_MASK GENMASK(4, 0) +#define AXP22X_DC5LDO_V_OUT_MASK GENMASK(2, 0) +#define AXP22X_ALDO1_V_OUT_MASK GENMASK(4, 0) +#define AXP22X_ALDO2_V_OUT_MASK GENMASK(4, 0) +#define AXP22X_ALDO3_V_OUT_MASK GENMASK(4, 0) +#define AXP22X_DLDO1_V_OUT_MASK GENMASK(4, 0) +#define AXP22X_DLDO2_V_OUT_MASK GENMASK(4, 0) +#define AXP22X_DLDO3_V_OUT_MASK GENMASK(4, 0) +#define AXP22X_DLDO4_V_OUT_MASK GENMASK(4, 0) +#define AXP22X_ELDO1_V_OUT_MASK GENMASK(4, 0) +#define AXP22X_ELDO2_V_OUT_MASK GENMASK(4, 0) +#define AXP22X_ELDO3_V_OUT_MASK GENMASK(4, 0) +#define AXP22X_LDO_IO0_V_OUT_MASK GENMASK(4, 0) +#define AXP22X_LDO_IO1_V_OUT_MASK GENMASK(4, 0) + +#define AXP22X_PWR_OUT_DC5LDO_MASK BIT_MASK(0) +#define AXP22X_PWR_OUT_DCDC1_MASK BIT_MASK(1) +#define AXP22X_PWR_OUT_DCDC2_MASK BIT_MASK(2) +#define AXP22X_PWR_OUT_DCDC3_MASK BIT_MASK(3) +#define AXP22X_PWR_OUT_DCDC4_MASK BIT_MASK(4) +#define AXP22X_PWR_OUT_DCDC5_MASK BIT_MASK(5) +#define AXP22X_PWR_OUT_ALDO1_MASK BIT_MASK(6) +#define AXP22X_PWR_OUT_ALDO2_MASK BIT_MASK(7) + +#define AXP22X_PWR_OUT_SW_MASK BIT_MASK(6) +#define AXP22X_PWR_OUT_DC1SW_MASK BIT_MASK(7) + +#define AXP22X_PWR_OUT_ELDO1_MASK BIT_MASK(0) +#define AXP22X_PWR_OUT_ELDO2_MASK BIT_MASK(1) +#define AXP22X_PWR_OUT_ELDO3_MASK BIT_MASK(2) +#define AXP22X_PWR_OUT_DLDO1_MASK BIT_MASK(3) +#define AXP22X_PWR_OUT_DLDO2_MASK BIT_MASK(4) +#define AXP22X_PWR_OUT_DLDO3_MASK BIT_MASK(5) +#define AXP22X_PWR_OUT_DLDO4_MASK BIT_MASK(6) +#define AXP22X_PWR_OUT_ALDO3_MASK BIT_MASK(7) + +#define AXP803_PWR_OUT_DCDC1_MASK BIT_MASK(0) +#define AXP803_PWR_OUT_DCDC2_MASK BIT_MASK(1) +#define AXP803_PWR_OUT_DCDC3_MASK BIT_MASK(2) +#define AXP803_PWR_OUT_DCDC4_MASK BIT_MASK(3) +#define AXP803_PWR_OUT_DCDC5_MASK BIT_MASK(4) +#define AXP803_PWR_OUT_DCDC6_MASK BIT_MASK(5) + +#define AXP803_PWR_OUT_FLDO1_MASK BIT_MASK(2) +#define AXP803_PWR_OUT_FLDO2_MASK BIT_MASK(3) + +#define AXP803_DCDC1_V_OUT_MASK GENMASK(4, 0) +#define AXP803_DCDC2_V_OUT_MASK GENMASK(6, 0) +#define AXP803_DCDC3_V_OUT_MASK GENMASK(6, 0) +#define AXP803_DCDC4_V_OUT_MASK GENMASK(6, 0) +#define AXP803_DCDC5_V_OUT_MASK GENMASK(6, 0) +#define AXP803_DCDC6_V_OUT_MASK GENMASK(6, 0) + +#define AXP803_FLDO1_V_OUT_MASK GENMASK(3, 0) +#define AXP803_FLDO2_V_OUT_MASK GENMASK(3, 0) + +#define AXP803_DCDC23_POLYPHASE_DUAL BIT(6) +#define AXP803_DCDC56_POLYPHASE_DUAL BIT(5) + +#define AXP803_DCDC234_500mV_START 0x00 +#define AXP803_DCDC234_500mV_STEPS 70 +#define AXP803_DCDC234_500mV_END \ + (AXP803_DCDC234_500mV_START + AXP803_DCDC234_500mV_STEPS) +#define AXP803_DCDC234_1220mV_START 0x47 +#define AXP803_DCDC234_1220mV_STEPS 4 +#define AXP803_DCDC234_1220mV_END \ + (AXP803_DCDC234_1220mV_START + AXP803_DCDC234_1220mV_STEPS) +#define AXP803_DCDC234_NUM_VOLTAGES 76 + +#define AXP803_DCDC5_800mV_START 0x00 +#define AXP803_DCDC5_800mV_STEPS 32 +#define AXP803_DCDC5_800mV_END \ + (AXP803_DCDC5_800mV_START + AXP803_DCDC5_800mV_STEPS) +#define AXP803_DCDC5_1140mV_START 0x21 +#define AXP803_DCDC5_1140mV_STEPS 35 +#define AXP803_DCDC5_1140mV_END \ + (AXP803_DCDC5_1140mV_START + AXP803_DCDC5_1140mV_STEPS) +#define AXP803_DCDC5_NUM_VOLTAGES 68 + +#define AXP803_DCDC6_600mV_START 0x00 +#define AXP803_DCDC6_600mV_STEPS 50 +#define AXP803_DCDC6_600mV_END \ + (AXP803_DCDC6_600mV_START + AXP803_DCDC6_600mV_STEPS) +#define AXP803_DCDC6_1120mV_START 0x33 +#define AXP803_DCDC6_1120mV_STEPS 14 +#define AXP803_DCDC6_1120mV_END \ + (AXP803_DCDC6_1120mV_START + AXP803_DCDC6_1120mV_STEPS) +#define AXP803_DCDC6_NUM_VOLTAGES 72 + +#define AXP803_DLDO2_700mV_START 0x00 +#define AXP803_DLDO2_700mV_STEPS 26 +#define AXP803_DLDO2_700mV_END \ + (AXP803_DLDO2_700mV_START + AXP803_DLDO2_700mV_STEPS) +#define AXP803_DLDO2_3400mV_START 0x1b +#define AXP803_DLDO2_3400mV_STEPS 4 +#define AXP803_DLDO2_3400mV_END \ + (AXP803_DLDO2_3400mV_START + AXP803_DLDO2_3400mV_STEPS) +#define AXP803_DLDO2_NUM_VOLTAGES 32 + +#define AXP806_DCDCA_V_CTRL_MASK GENMASK(6, 0) +#define AXP806_DCDCB_V_CTRL_MASK GENMASK(4, 0) +#define AXP806_DCDCC_V_CTRL_MASK GENMASK(6, 0) +#define AXP806_DCDCD_V_CTRL_MASK GENMASK(5, 0) +#define AXP806_DCDCE_V_CTRL_MASK GENMASK(4, 0) +#define AXP806_ALDO1_V_CTRL_MASK GENMASK(4, 0) +#define AXP806_ALDO2_V_CTRL_MASK GENMASK(4, 0) +#define AXP806_ALDO3_V_CTRL_MASK GENMASK(4, 0) +#define AXP806_BLDO1_V_CTRL_MASK GENMASK(3, 0) +#define AXP806_BLDO2_V_CTRL_MASK GENMASK(3, 0) +#define AXP806_BLDO3_V_CTRL_MASK GENMASK(3, 0) +#define AXP806_BLDO4_V_CTRL_MASK GENMASK(3, 0) +#define AXP806_CLDO1_V_CTRL_MASK GENMASK(4, 0) +#define AXP806_CLDO2_V_CTRL_MASK GENMASK(4, 0) +#define AXP806_CLDO3_V_CTRL_MASK GENMASK(4, 0) + +#define AXP806_PWR_OUT_DCDCA_MASK BIT_MASK(0) +#define AXP806_PWR_OUT_DCDCB_MASK BIT_MASK(1) +#define AXP806_PWR_OUT_DCDCC_MASK BIT_MASK(2) +#define AXP806_PWR_OUT_DCDCD_MASK BIT_MASK(3) +#define AXP806_PWR_OUT_DCDCE_MASK BIT_MASK(4) +#define AXP806_PWR_OUT_ALDO1_MASK BIT_MASK(5) +#define AXP806_PWR_OUT_ALDO2_MASK BIT_MASK(6) +#define AXP806_PWR_OUT_ALDO3_MASK BIT_MASK(7) +#define AXP806_PWR_OUT_BLDO1_MASK BIT_MASK(0) +#define AXP806_PWR_OUT_BLDO2_MASK BIT_MASK(1) +#define AXP806_PWR_OUT_BLDO3_MASK BIT_MASK(2) +#define AXP806_PWR_OUT_BLDO4_MASK BIT_MASK(3) +#define AXP806_PWR_OUT_CLDO1_MASK BIT_MASK(4) +#define AXP806_PWR_OUT_CLDO2_MASK BIT_MASK(5) +#define AXP806_PWR_OUT_CLDO3_MASK BIT_MASK(6) +#define AXP806_PWR_OUT_SW_MASK BIT_MASK(7) + +#define AXP806_DCDCAB_POLYPHASE_DUAL 0x40 +#define AXP806_DCDCABC_POLYPHASE_TRI 0x80 +#define AXP806_DCDCABC_POLYPHASE_MASK GENMASK(7, 6) + +#define AXP806_DCDCDE_POLYPHASE_DUAL BIT(5) + +#define AXP806_DCDCA_600mV_START 0x00 +#define AXP806_DCDCA_600mV_STEPS 50 +#define AXP806_DCDCA_600mV_END \ + (AXP806_DCDCA_600mV_START + AXP806_DCDCA_600mV_STEPS) +#define AXP806_DCDCA_1120mV_START 0x33 +#define AXP806_DCDCA_1120mV_STEPS 14 +#define AXP806_DCDCA_1120mV_END \ + (AXP806_DCDCA_1120mV_START + AXP806_DCDCA_1120mV_STEPS) +#define AXP806_DCDCA_NUM_VOLTAGES 72 + +#define AXP806_DCDCD_600mV_START 0x00 +#define AXP806_DCDCD_600mV_STEPS 45 +#define AXP806_DCDCD_600mV_END \ + (AXP806_DCDCD_600mV_START + AXP806_DCDCD_600mV_STEPS) +#define AXP806_DCDCD_1600mV_START 0x2e +#define AXP806_DCDCD_1600mV_STEPS 17 +#define AXP806_DCDCD_1600mV_END \ + (AXP806_DCDCD_1600mV_START + AXP806_DCDCD_1600mV_STEPS) +#define AXP806_DCDCD_NUM_VOLTAGES 64 + +#define AXP809_DCDC4_600mV_START 0x00 +#define AXP809_DCDC4_600mV_STEPS 47 +#define AXP809_DCDC4_600mV_END \ + (AXP809_DCDC4_600mV_START + AXP809_DCDC4_600mV_STEPS) +#define AXP809_DCDC4_1800mV_START 0x30 +#define AXP809_DCDC4_1800mV_STEPS 8 +#define AXP809_DCDC4_1800mV_END \ + (AXP809_DCDC4_1800mV_START + AXP809_DCDC4_1800mV_STEPS) +#define AXP809_DCDC4_NUM_VOLTAGES 57 + +#define AXP813_DCDC7_V_OUT_MASK GENMASK(6, 0) + +#define AXP813_PWR_OUT_DCDC7_MASK BIT_MASK(6) + #define AXP_DESC_IO(_family, _id, _match, _supply, _min, _max, _step, _vreg, \ _vmask, _ereg, _emask, _enable_val, _disable_val) \ [_family##_##_id] = { \ @@ -157,77 +375,116 @@ static const struct regulator_ops axp20x_ops_sw = { }; static const struct regulator_linear_range axp20x_ldo4_ranges[] = { - REGULATOR_LINEAR_RANGE(1250000, 0x0, 0x0, 0), - REGULATOR_LINEAR_RANGE(1300000, 0x1, 0x8, 100000), - REGULATOR_LINEAR_RANGE(2500000, 0x9, 0x9, 0), - REGULATOR_LINEAR_RANGE(2700000, 0xa, 0xb, 100000), - REGULATOR_LINEAR_RANGE(3000000, 0xc, 0xf, 100000), + REGULATOR_LINEAR_RANGE(1250000, + AXP20X_LDO4_V_OUT_1250mV_START, + AXP20X_LDO4_V_OUT_1250mV_END, + 0), + REGULATOR_LINEAR_RANGE(1300000, + AXP20X_LDO4_V_OUT_1300mV_START, + AXP20X_LDO4_V_OUT_1300mV_END, + 100000), + REGULATOR_LINEAR_RANGE(2500000, + AXP20X_LDO4_V_OUT_2500mV_START, + AXP20X_LDO4_V_OUT_2500mV_END, + 0), + REGULATOR_LINEAR_RANGE(2700000, + AXP20X_LDO4_V_OUT_2700mV_START, + AXP20X_LDO4_V_OUT_2700mV_END, + 100000), + REGULATOR_LINEAR_RANGE(3000000, + AXP20X_LDO4_V_OUT_3000mV_START, + AXP20X_LDO4_V_OUT_3000mV_END, + 100000), }; static const struct regulator_desc axp20x_regulators[] = { AXP_DESC(AXP20X, DCDC2, "dcdc2", "vin2", 700, 2275, 25, - AXP20X_DCDC2_V_OUT, 0x3f, AXP20X_PWR_OUT_CTRL, 0x10), + AXP20X_DCDC2_V_OUT, AXP20X_DCDC2_V_OUT_MASK, + AXP20X_PWR_OUT_CTRL, AXP20X_PWR_OUT_DCDC2_MASK), AXP_DESC(AXP20X, DCDC3, "dcdc3", "vin3", 700, 3500, 25, - AXP20X_DCDC3_V_OUT, 0x7f, AXP20X_PWR_OUT_CTRL, 0x02), + AXP20X_DCDC3_V_OUT, AXP20X_DCDC3_V_OUT_MASK, + AXP20X_PWR_OUT_CTRL, AXP20X_PWR_OUT_DCDC3_MASK), AXP_DESC_FIXED(AXP20X, LDO1, "ldo1", "acin", 1300), AXP_DESC(AXP20X, LDO2, "ldo2", "ldo24in", 1800, 3300, 100, - AXP20X_LDO24_V_OUT, 0xf0, AXP20X_PWR_OUT_CTRL, 0x04), + AXP20X_LDO24_V_OUT, AXP20X_LDO24_V_OUT_MASK, + AXP20X_PWR_OUT_CTRL, AXP20X_PWR_OUT_LDO2_MASK), AXP_DESC(AXP20X, LDO3, "ldo3", "ldo3in", 700, 3500, 25, - AXP20X_LDO3_V_OUT, 0x7f, AXP20X_PWR_OUT_CTRL, 0x40), - AXP_DESC_RANGES(AXP20X, LDO4, "ldo4", "ldo24in", axp20x_ldo4_ranges, - 16, AXP20X_LDO24_V_OUT, 0x0f, AXP20X_PWR_OUT_CTRL, - 0x08), + AXP20X_LDO3_V_OUT, AXP20X_LDO3_V_OUT_MASK, + AXP20X_PWR_OUT_CTRL, AXP20X_PWR_OUT_LDO3_MASK), + AXP_DESC_RANGES(AXP20X, LDO4, "ldo4", "ldo24in", + axp20x_ldo4_ranges, AXP20X_LDO4_V_OUT_NUM_VOLTAGES, + AXP20X_LDO24_V_OUT, AXP20X_LDO24_V_OUT_MASK, + AXP20X_PWR_OUT_CTRL, AXP20X_PWR_OUT_LDO4_MASK), AXP_DESC_IO(AXP20X, LDO5, "ldo5", "ldo5in", 1800, 3300, 100, - AXP20X_LDO5_V_OUT, 0xf0, AXP20X_GPIO0_CTRL, 0x07, + AXP20X_LDO5_V_OUT, AXP20X_LDO5_V_OUT_MASK, + AXP20X_GPIO0_CTRL, AXP20X_GPIO0_FUNC_MASK, AXP20X_IO_ENABLED, AXP20X_IO_DISABLED), }; static const struct regulator_desc axp22x_regulators[] = { AXP_DESC(AXP22X, DCDC1, "dcdc1", "vin1", 1600, 3400, 100, - AXP22X_DCDC1_V_OUT, 0x1f, AXP22X_PWR_OUT_CTRL1, BIT(1)), + AXP22X_DCDC1_V_OUT, AXP22X_DCDC1_V_OUT_MASK, + AXP22X_PWR_OUT_CTRL1, AXP22X_PWR_OUT_DCDC1_MASK), AXP_DESC(AXP22X, DCDC2, "dcdc2", "vin2", 600, 1540, 20, - AXP22X_DCDC2_V_OUT, 0x3f, AXP22X_PWR_OUT_CTRL1, BIT(2)), + AXP22X_DCDC2_V_OUT, AXP22X_DCDC2_V_OUT_MASK, + AXP22X_PWR_OUT_CTRL1, AXP22X_PWR_OUT_DCDC2_MASK), AXP_DESC(AXP22X, DCDC3, "dcdc3", "vin3", 600, 1860, 20, - AXP22X_DCDC3_V_OUT, 0x3f, AXP22X_PWR_OUT_CTRL1, BIT(3)), + AXP22X_DCDC3_V_OUT, AXP22X_DCDC3_V_OUT_MASK, + AXP22X_PWR_OUT_CTRL1, AXP22X_PWR_OUT_DCDC3_MASK), AXP_DESC(AXP22X, DCDC4, "dcdc4", "vin4", 600, 1540, 20, - AXP22X_DCDC4_V_OUT, 0x3f, AXP22X_PWR_OUT_CTRL1, BIT(4)), + AXP22X_DCDC4_V_OUT, AXP22X_DCDC4_V_OUT, + AXP22X_PWR_OUT_CTRL1, AXP22X_PWR_OUT_DCDC4_MASK), AXP_DESC(AXP22X, DCDC5, "dcdc5", "vin5", 1000, 2550, 50, - AXP22X_DCDC5_V_OUT, 0x1f, AXP22X_PWR_OUT_CTRL1, BIT(5)), + AXP22X_DCDC5_V_OUT, AXP22X_DCDC5_V_OUT_MASK, + AXP22X_PWR_OUT_CTRL1, AXP22X_PWR_OUT_DCDC5_MASK), /* secondary switchable output of DCDC1 */ - AXP_DESC_SW(AXP22X, DC1SW, "dc1sw", NULL, AXP22X_PWR_OUT_CTRL2, - BIT(7)), + AXP_DESC_SW(AXP22X, DC1SW, "dc1sw", NULL, + AXP22X_PWR_OUT_CTRL2, AXP22X_PWR_OUT_DC1SW_MASK), /* LDO regulator internally chained to DCDC5 */ AXP_DESC(AXP22X, DC5LDO, "dc5ldo", NULL, 700, 1400, 100, - AXP22X_DC5LDO_V_OUT, 0x7, AXP22X_PWR_OUT_CTRL1, BIT(0)), + AXP22X_DC5LDO_V_OUT, AXP22X_DC5LDO_V_OUT_MASK, + AXP22X_PWR_OUT_CTRL1, AXP22X_PWR_OUT_DC5LDO_MASK), AXP_DESC(AXP22X, ALDO1, "aldo1", "aldoin", 700, 3300, 100, - AXP22X_ALDO1_V_OUT, 0x1f, AXP22X_PWR_OUT_CTRL1, BIT(6)), + AXP22X_ALDO1_V_OUT, AXP22X_ALDO1_V_OUT_MASK, + AXP22X_PWR_OUT_CTRL1, AXP22X_PWR_OUT_ALDO1_MASK), AXP_DESC(AXP22X, ALDO2, "aldo2", "aldoin", 700, 3300, 100, - AXP22X_ALDO2_V_OUT, 0x1f, AXP22X_PWR_OUT_CTRL1, BIT(7)), + AXP22X_ALDO2_V_OUT, AXP22X_ALDO2_V_OUT_MASK, + AXP22X_PWR_OUT_CTRL1, AXP22X_PWR_OUT_ALDO2_MASK), AXP_DESC(AXP22X, ALDO3, "aldo3", "aldoin", 700, 3300, 100, - AXP22X_ALDO3_V_OUT, 0x1f, AXP22X_PWR_OUT_CTRL3, BIT(7)), + AXP22X_ALDO3_V_OUT, AXP22X_ALDO3_V_OUT_MASK, + AXP22X_PWR_OUT_CTRL3, AXP22X_PWR_OUT_ALDO3_MASK), AXP_DESC(AXP22X, DLDO1, "dldo1", "dldoin", 700, 3300, 100, - AXP22X_DLDO1_V_OUT, 0x1f, AXP22X_PWR_OUT_CTRL2, BIT(3)), + AXP22X_DLDO1_V_OUT, AXP22X_DLDO1_V_OUT_MASK, + AXP22X_PWR_OUT_CTRL2, AXP22X_PWR_OUT_DLDO1_MASK), AXP_DESC(AXP22X, DLDO2, "dldo2", "dldoin", 700, 3300, 100, - AXP22X_DLDO2_V_OUT, 0x1f, AXP22X_PWR_OUT_CTRL2, BIT(4)), + AXP22X_DLDO2_V_OUT, AXP22X_PWR_OUT_DLDO2_MASK, + AXP22X_PWR_OUT_CTRL2, AXP22X_PWR_OUT_DLDO2_MASK), AXP_DESC(AXP22X, DLDO3, "dldo3", "dldoin", 700, 3300, 100, - AXP22X_DLDO3_V_OUT, 0x1f, AXP22X_PWR_OUT_CTRL2, BIT(5)), + AXP22X_DLDO3_V_OUT, AXP22X_DLDO3_V_OUT_MASK, + AXP22X_PWR_OUT_CTRL2, AXP22X_PWR_OUT_DLDO3_MASK), AXP_DESC(AXP22X, DLDO4, "dldo4", "dldoin", 700, 3300, 100, - AXP22X_DLDO4_V_OUT, 0x1f, AXP22X_PWR_OUT_CTRL2, BIT(6)), + AXP22X_DLDO4_V_OUT, AXP22X_DLDO4_V_OUT_MASK, + AXP22X_PWR_OUT_CTRL2, AXP22X_PWR_OUT_DLDO4_MASK), AXP_DESC(AXP22X, ELDO1, "eldo1", "eldoin", 700, 3300, 100, - AXP22X_ELDO1_V_OUT, 0x1f, AXP22X_PWR_OUT_CTRL2, BIT(0)), + AXP22X_ELDO1_V_OUT, AXP22X_ELDO1_V_OUT_MASK, + AXP22X_PWR_OUT_CTRL2, AXP22X_PWR_OUT_ELDO1_MASK), AXP_DESC(AXP22X, ELDO2, "eldo2", "eldoin", 700, 3300, 100, - AXP22X_ELDO2_V_OUT, 0x1f, AXP22X_PWR_OUT_CTRL2, BIT(1)), + AXP22X_ELDO2_V_OUT, AXP22X_ELDO2_V_OUT_MASK, + AXP22X_PWR_OUT_CTRL2, AXP22X_PWR_OUT_ELDO1_MASK), AXP_DESC(AXP22X, ELDO3, "eldo3", "eldoin", 700, 3300, 100, - AXP22X_ELDO3_V_OUT, 0x1f, AXP22X_PWR_OUT_CTRL2, BIT(2)), + AXP22X_ELDO3_V_OUT, AXP22X_ELDO3_V_OUT_MASK, + AXP22X_PWR_OUT_CTRL2, AXP22X_PWR_OUT_ELDO3_MASK), /* Note the datasheet only guarantees reliable operation up to * 3.3V, this needs to be enforced via dts provided constraints */ AXP_DESC_IO(AXP22X, LDO_IO0, "ldo_io0", "ips", 700, 3800, 100, - AXP22X_LDO_IO0_V_OUT, 0x1f, AXP20X_GPIO0_CTRL, 0x07, + AXP22X_LDO_IO0_V_OUT, AXP22X_LDO_IO0_V_OUT_MASK, + AXP20X_GPIO0_CTRL, AXP20X_GPIO0_FUNC_MASK, AXP22X_IO_ENABLED, AXP22X_IO_DISABLED), /* Note the datasheet only guarantees reliable operation up to * 3.3V, this needs to be enforced via dts provided constraints */ AXP_DESC_IO(AXP22X, LDO_IO1, "ldo_io1", "ips", 700, 3800, 100, - AXP22X_LDO_IO1_V_OUT, 0x1f, AXP20X_GPIO1_CTRL, 0x07, + AXP22X_LDO_IO1_V_OUT, AXP22X_LDO_IO1_V_OUT_MASK, + AXP20X_GPIO1_CTRL, AXP20X_GPIO1_FUNC_MASK, AXP22X_IO_ENABLED, AXP22X_IO_DISABLED), AXP_DESC_FIXED(AXP22X, RTC_LDO, "rtc_ldo", "ips", 3000), }; @@ -240,240 +497,354 @@ static const struct regulator_desc axp22x_drivevbus_regulator = { .type = REGULATOR_VOLTAGE, .owner = THIS_MODULE, .enable_reg = AXP20X_VBUS_IPSOUT_MGMT, - .enable_mask = BIT(2), + .enable_mask = AXP20X_VBUS_IPSOUT_MGMT_MASK, .ops = &axp20x_ops_sw, }; /* DCDC ranges shared with AXP813 */ static const struct regulator_linear_range axp803_dcdc234_ranges[] = { - REGULATOR_LINEAR_RANGE(500000, 0x0, 0x46, 10000), - REGULATOR_LINEAR_RANGE(1220000, 0x47, 0x4b, 20000), + REGULATOR_LINEAR_RANGE(500000, + AXP803_DCDC234_500mV_START, + AXP803_DCDC234_500mV_END, + 10000), + REGULATOR_LINEAR_RANGE(1220000, + AXP803_DCDC234_1220mV_START, + AXP803_DCDC234_1220mV_END, + 20000), }; static const struct regulator_linear_range axp803_dcdc5_ranges[] = { - REGULATOR_LINEAR_RANGE(800000, 0x0, 0x20, 10000), - REGULATOR_LINEAR_RANGE(1140000, 0x21, 0x44, 20000), + REGULATOR_LINEAR_RANGE(800000, + AXP803_DCDC5_800mV_START, + AXP803_DCDC5_800mV_END, + 10000), + REGULATOR_LINEAR_RANGE(1140000, + AXP803_DCDC5_1140mV_START, + AXP803_DCDC5_1140mV_END, + 20000), }; static const struct regulator_linear_range axp803_dcdc6_ranges[] = { - REGULATOR_LINEAR_RANGE(600000, 0x0, 0x32, 10000), - REGULATOR_LINEAR_RANGE(1120000, 0x33, 0x47, 20000), + REGULATOR_LINEAR_RANGE(600000, + AXP803_DCDC6_600mV_START, + AXP803_DCDC6_600mV_END, + 10000), + REGULATOR_LINEAR_RANGE(1120000, + AXP803_DCDC6_1120mV_START, + AXP803_DCDC6_1120mV_END, + 20000), }; -/* AXP806's CLDO2 and AXP809's DLDO1 shares the same range */ +/* AXP806's CLDO2 and AXP809's DLDO1 share the same range */ static const struct regulator_linear_range axp803_dldo2_ranges[] = { - REGULATOR_LINEAR_RANGE(700000, 0x0, 0x1a, 100000), - REGULATOR_LINEAR_RANGE(3400000, 0x1b, 0x1f, 200000), + REGULATOR_LINEAR_RANGE(700000, + AXP803_DLDO2_700mV_START, + AXP803_DLDO2_700mV_END, + 100000), + REGULATOR_LINEAR_RANGE(3400000, + AXP803_DLDO2_3400mV_START, + AXP803_DLDO2_3400mV_END, + 200000), }; static const struct regulator_desc axp803_regulators[] = { AXP_DESC(AXP803, DCDC1, "dcdc1", "vin1", 1600, 3400, 100, - AXP803_DCDC1_V_OUT, 0x1f, AXP22X_PWR_OUT_CTRL1, BIT(0)), - AXP_DESC_RANGES(AXP803, DCDC2, "dcdc2", "vin2", axp803_dcdc234_ranges, - 76, AXP803_DCDC2_V_OUT, 0x7f, AXP22X_PWR_OUT_CTRL1, - BIT(1)), - AXP_DESC_RANGES(AXP803, DCDC3, "dcdc3", "vin3", axp803_dcdc234_ranges, - 76, AXP803_DCDC3_V_OUT, 0x7f, AXP22X_PWR_OUT_CTRL1, - BIT(2)), - AXP_DESC_RANGES(AXP803, DCDC4, "dcdc4", "vin4", axp803_dcdc234_ranges, - 76, AXP803_DCDC4_V_OUT, 0x7f, AXP22X_PWR_OUT_CTRL1, - BIT(3)), - AXP_DESC_RANGES(AXP803, DCDC5, "dcdc5", "vin5", axp803_dcdc5_ranges, - 68, AXP803_DCDC5_V_OUT, 0x7f, AXP22X_PWR_OUT_CTRL1, - BIT(4)), - AXP_DESC_RANGES(AXP803, DCDC6, "dcdc6", "vin6", axp803_dcdc6_ranges, - 72, AXP803_DCDC6_V_OUT, 0x7f, AXP22X_PWR_OUT_CTRL1, - BIT(5)), + AXP803_DCDC1_V_OUT, AXP803_DCDC1_V_OUT_MASK, + AXP22X_PWR_OUT_CTRL1, AXP803_PWR_OUT_DCDC1_MASK), + AXP_DESC_RANGES(AXP803, DCDC2, "dcdc2", "vin2", + axp803_dcdc234_ranges, AXP803_DCDC234_NUM_VOLTAGES, + AXP803_DCDC2_V_OUT, AXP803_DCDC2_V_OUT_MASK, + AXP22X_PWR_OUT_CTRL1, AXP803_PWR_OUT_DCDC2_MASK), + AXP_DESC_RANGES(AXP803, DCDC3, "dcdc3", "vin3", + axp803_dcdc234_ranges, AXP803_DCDC234_NUM_VOLTAGES, + AXP803_DCDC3_V_OUT, AXP803_DCDC3_V_OUT_MASK, + AXP22X_PWR_OUT_CTRL1, AXP803_PWR_OUT_DCDC3_MASK), + AXP_DESC_RANGES(AXP803, DCDC4, "dcdc4", "vin4", + axp803_dcdc234_ranges, AXP803_DCDC234_NUM_VOLTAGES, + AXP803_DCDC4_V_OUT, AXP803_DCDC4_V_OUT_MASK, + AXP22X_PWR_OUT_CTRL1, AXP803_PWR_OUT_DCDC4_MASK), + AXP_DESC_RANGES(AXP803, DCDC5, "dcdc5", "vin5", + axp803_dcdc5_ranges, AXP803_DCDC5_NUM_VOLTAGES, + AXP803_DCDC5_V_OUT, AXP803_DCDC5_V_OUT_MASK, + AXP22X_PWR_OUT_CTRL1, AXP803_PWR_OUT_DCDC5_MASK), + AXP_DESC_RANGES(AXP803, DCDC6, "dcdc6", "vin6", + axp803_dcdc6_ranges, AXP803_DCDC6_NUM_VOLTAGES, + AXP803_DCDC6_V_OUT, AXP803_DCDC6_V_OUT_MASK, + AXP22X_PWR_OUT_CTRL1, AXP803_PWR_OUT_DCDC6_MASK), /* secondary switchable output of DCDC1 */ - AXP_DESC_SW(AXP803, DC1SW, "dc1sw", NULL, AXP22X_PWR_OUT_CTRL2, - BIT(7)), + AXP_DESC_SW(AXP803, DC1SW, "dc1sw", NULL, + AXP22X_PWR_OUT_CTRL2, AXP22X_PWR_OUT_DC1SW_MASK), AXP_DESC(AXP803, ALDO1, "aldo1", "aldoin", 700, 3300, 100, - AXP22X_ALDO1_V_OUT, 0x1f, AXP22X_PWR_OUT_CTRL3, BIT(5)), + AXP22X_ALDO1_V_OUT, AXP22X_ALDO1_V_OUT_MASK, + AXP22X_PWR_OUT_CTRL3, AXP806_PWR_OUT_ALDO1_MASK), AXP_DESC(AXP803, ALDO2, "aldo2", "aldoin", 700, 3300, 100, - AXP22X_ALDO2_V_OUT, 0x1f, AXP22X_PWR_OUT_CTRL3, BIT(6)), + AXP22X_ALDO2_V_OUT, AXP22X_ALDO2_V_OUT, + AXP22X_PWR_OUT_CTRL3, AXP806_PWR_OUT_ALDO2_MASK), AXP_DESC(AXP803, ALDO3, "aldo3", "aldoin", 700, 3300, 100, - AXP22X_ALDO3_V_OUT, 0x1f, AXP22X_PWR_OUT_CTRL3, BIT(7)), + AXP22X_ALDO3_V_OUT, AXP22X_ALDO3_V_OUT_MASK, + AXP22X_PWR_OUT_CTRL3, AXP806_PWR_OUT_ALDO3_MASK), AXP_DESC(AXP803, DLDO1, "dldo1", "dldoin", 700, 3300, 100, - AXP22X_DLDO1_V_OUT, 0x1f, AXP22X_PWR_OUT_CTRL2, BIT(3)), - AXP_DESC_RANGES(AXP803, DLDO2, "dldo2", "dldoin", axp803_dldo2_ranges, - 32, AXP22X_DLDO2_V_OUT, 0x1f, AXP22X_PWR_OUT_CTRL2, - BIT(4)), + AXP22X_DLDO1_V_OUT, AXP22X_DLDO1_V_OUT_MASK, + AXP22X_PWR_OUT_CTRL2, AXP22X_PWR_OUT_DLDO1_MASK), + AXP_DESC_RANGES(AXP803, DLDO2, "dldo2", "dldoin", + axp803_dldo2_ranges, AXP803_DLDO2_NUM_VOLTAGES, + AXP22X_DLDO2_V_OUT, AXP22X_DLDO2_V_OUT, + AXP22X_PWR_OUT_CTRL2, AXP22X_PWR_OUT_DLDO2_MASK), AXP_DESC(AXP803, DLDO3, "dldo3", "dldoin", 700, 3300, 100, - AXP22X_DLDO3_V_OUT, 0x1f, AXP22X_PWR_OUT_CTRL2, BIT(5)), + AXP22X_DLDO3_V_OUT, AXP22X_DLDO3_V_OUT_MASK, + AXP22X_PWR_OUT_CTRL2, AXP22X_PWR_OUT_DLDO3_MASK), AXP_DESC(AXP803, DLDO4, "dldo4", "dldoin", 700, 3300, 100, - AXP22X_DLDO4_V_OUT, 0x1f, AXP22X_PWR_OUT_CTRL2, BIT(6)), + AXP22X_DLDO4_V_OUT, AXP22X_DLDO4_V_OUT_MASK, + AXP22X_PWR_OUT_CTRL2, AXP22X_PWR_OUT_DLDO4_MASK), AXP_DESC(AXP803, ELDO1, "eldo1", "eldoin", 700, 1900, 50, - AXP22X_ELDO1_V_OUT, 0x1f, AXP22X_PWR_OUT_CTRL2, BIT(0)), + AXP22X_ELDO1_V_OUT, AXP22X_ELDO1_V_OUT_MASK, + AXP22X_PWR_OUT_CTRL2, AXP22X_PWR_OUT_ELDO1_MASK), AXP_DESC(AXP803, ELDO2, "eldo2", "eldoin", 700, 1900, 50, - AXP22X_ELDO2_V_OUT, 0x1f, AXP22X_PWR_OUT_CTRL2, BIT(1)), + AXP22X_ELDO2_V_OUT, AXP22X_ELDO2_V_OUT_MASK, + AXP22X_PWR_OUT_CTRL2, AXP22X_PWR_OUT_ELDO2_MASK), AXP_DESC(AXP803, ELDO3, "eldo3", "eldoin", 700, 1900, 50, - AXP22X_ELDO3_V_OUT, 0x1f, AXP22X_PWR_OUT_CTRL2, BIT(2)), + AXP22X_ELDO3_V_OUT, AXP22X_ELDO3_V_OUT, + AXP22X_PWR_OUT_CTRL2, AXP22X_PWR_OUT_ELDO3_MASK), AXP_DESC(AXP803, FLDO1, "fldo1", "fldoin", 700, 1450, 50, - AXP803_FLDO1_V_OUT, 0x0f, AXP22X_PWR_OUT_CTRL3, BIT(2)), + AXP803_FLDO1_V_OUT, AXP803_FLDO1_V_OUT_MASK, + AXP22X_PWR_OUT_CTRL3, AXP803_PWR_OUT_FLDO1_MASK), AXP_DESC(AXP803, FLDO2, "fldo2", "fldoin", 700, 1450, 50, - AXP803_FLDO2_V_OUT, 0x0f, AXP22X_PWR_OUT_CTRL3, BIT(3)), + AXP803_FLDO2_V_OUT, AXP803_FLDO2_V_OUT_MASK, + AXP22X_PWR_OUT_CTRL3, AXP803_PWR_OUT_FLDO2_MASK), AXP_DESC_IO(AXP803, LDO_IO0, "ldo-io0", "ips", 700, 3300, 100, - AXP22X_LDO_IO0_V_OUT, 0x1f, AXP20X_GPIO0_CTRL, 0x07, + AXP22X_LDO_IO0_V_OUT, AXP22X_LDO_IO0_V_OUT_MASK, + AXP20X_GPIO0_CTRL, AXP20X_GPIO0_FUNC_MASK, AXP22X_IO_ENABLED, AXP22X_IO_DISABLED), AXP_DESC_IO(AXP803, LDO_IO1, "ldo-io1", "ips", 700, 3300, 100, - AXP22X_LDO_IO1_V_OUT, 0x1f, AXP20X_GPIO1_CTRL, 0x07, + AXP22X_LDO_IO1_V_OUT, AXP22X_LDO_IO1_V_OUT_MASK, + AXP20X_GPIO1_CTRL, AXP20X_GPIO1_FUNC_MASK, AXP22X_IO_ENABLED, AXP22X_IO_DISABLED), AXP_DESC_FIXED(AXP803, RTC_LDO, "rtc-ldo", "ips", 3000), }; static const struct regulator_linear_range axp806_dcdca_ranges[] = { - REGULATOR_LINEAR_RANGE(600000, 0x0, 0x32, 10000), - REGULATOR_LINEAR_RANGE(1120000, 0x33, 0x47, 20000), + REGULATOR_LINEAR_RANGE(600000, + AXP806_DCDCA_600mV_START, + AXP806_DCDCA_600mV_END, + 10000), + REGULATOR_LINEAR_RANGE(1120000, + AXP806_DCDCA_1120mV_START, + AXP806_DCDCA_1120mV_END, + 20000), }; static const struct regulator_linear_range axp806_dcdcd_ranges[] = { - REGULATOR_LINEAR_RANGE(600000, 0x0, 0x2d, 20000), - REGULATOR_LINEAR_RANGE(1600000, 0x2e, 0x3f, 100000), + REGULATOR_LINEAR_RANGE(600000, + AXP806_DCDCD_600mV_START, + AXP806_DCDCD_600mV_END, + 20000), + REGULATOR_LINEAR_RANGE(1600000, + AXP806_DCDCD_600mV_START, + AXP806_DCDCD_600mV_END, + 100000), }; static const struct regulator_desc axp806_regulators[] = { - AXP_DESC_RANGES(AXP806, DCDCA, "dcdca", "vina", axp806_dcdca_ranges, - 72, AXP806_DCDCA_V_CTRL, 0x7f, AXP806_PWR_OUT_CTRL1, - BIT(0)), + AXP_DESC_RANGES(AXP806, DCDCA, "dcdca", "vina", + axp806_dcdca_ranges, AXP806_DCDCA_NUM_VOLTAGES, + AXP806_DCDCA_V_CTRL, AXP806_DCDCA_V_CTRL_MASK, + AXP806_PWR_OUT_CTRL1, AXP806_PWR_OUT_DCDCA_MASK), AXP_DESC(AXP806, DCDCB, "dcdcb", "vinb", 1000, 2550, 50, - AXP806_DCDCB_V_CTRL, 0x1f, AXP806_PWR_OUT_CTRL1, BIT(1)), - AXP_DESC_RANGES(AXP806, DCDCC, "dcdcc", "vinc", axp806_dcdca_ranges, - 72, AXP806_DCDCC_V_CTRL, 0x7f, AXP806_PWR_OUT_CTRL1, - BIT(2)), - AXP_DESC_RANGES(AXP806, DCDCD, "dcdcd", "vind", axp806_dcdcd_ranges, - 64, AXP806_DCDCD_V_CTRL, 0x3f, AXP806_PWR_OUT_CTRL1, - BIT(3)), + AXP806_DCDCB_V_CTRL, AXP806_DCDCB_V_CTRL, + AXP806_PWR_OUT_CTRL1, AXP806_PWR_OUT_DCDCB_MASK), + AXP_DESC_RANGES(AXP806, DCDCC, "dcdcc", "vinc", + axp806_dcdca_ranges, AXP806_DCDCA_NUM_VOLTAGES, + AXP806_DCDCC_V_CTRL, AXP806_DCDCC_V_CTRL_MASK, + AXP806_PWR_OUT_CTRL1, AXP806_PWR_OUT_DCDCC_MASK), + AXP_DESC_RANGES(AXP806, DCDCD, "dcdcd", "vind", + axp806_dcdcd_ranges, AXP806_DCDCD_NUM_VOLTAGES, + AXP806_DCDCD_V_CTRL, AXP806_DCDCD_V_CTRL_MASK, + AXP806_PWR_OUT_CTRL1, AXP806_PWR_OUT_DCDCD_MASK), AXP_DESC(AXP806, DCDCE, "dcdce", "vine", 1100, 3400, 100, - AXP806_DCDCE_V_CTRL, 0x1f, AXP806_PWR_OUT_CTRL1, BIT(4)), + AXP806_DCDCE_V_CTRL, AXP806_DCDCE_V_CTRL_MASK, + AXP806_PWR_OUT_CTRL1, AXP806_PWR_OUT_DCDCE_MASK), AXP_DESC(AXP806, ALDO1, "aldo1", "aldoin", 700, 3300, 100, - AXP806_ALDO1_V_CTRL, 0x1f, AXP806_PWR_OUT_CTRL1, BIT(5)), + AXP806_ALDO1_V_CTRL, AXP806_ALDO1_V_CTRL_MASK, + AXP806_PWR_OUT_CTRL1, AXP806_PWR_OUT_ALDO1_MASK), AXP_DESC(AXP806, ALDO2, "aldo2", "aldoin", 700, 3400, 100, - AXP806_ALDO2_V_CTRL, 0x1f, AXP806_PWR_OUT_CTRL1, BIT(6)), + AXP806_ALDO2_V_CTRL, AXP806_ALDO2_V_CTRL_MASK, + AXP806_PWR_OUT_CTRL1, AXP806_PWR_OUT_ALDO2_MASK), AXP_DESC(AXP806, ALDO3, "aldo3", "aldoin", 700, 3300, 100, - AXP806_ALDO3_V_CTRL, 0x1f, AXP806_PWR_OUT_CTRL1, BIT(7)), + AXP806_ALDO3_V_CTRL, AXP806_ALDO3_V_CTRL_MASK, + AXP806_PWR_OUT_CTRL1, AXP806_PWR_OUT_ALDO3_MASK), AXP_DESC(AXP806, BLDO1, "bldo1", "bldoin", 700, 1900, 100, - AXP806_BLDO1_V_CTRL, 0x0f, AXP806_PWR_OUT_CTRL2, BIT(0)), + AXP806_BLDO1_V_CTRL, AXP806_BLDO1_V_CTRL_MASK, + AXP806_PWR_OUT_CTRL2, AXP806_PWR_OUT_BLDO1_MASK), AXP_DESC(AXP806, BLDO2, "bldo2", "bldoin", 700, 1900, 100, - AXP806_BLDO2_V_CTRL, 0x0f, AXP806_PWR_OUT_CTRL2, BIT(1)), + AXP806_BLDO2_V_CTRL, AXP806_BLDO2_V_CTRL, + AXP806_PWR_OUT_CTRL2, AXP806_PWR_OUT_BLDO2_MASK), AXP_DESC(AXP806, BLDO3, "bldo3", "bldoin", 700, 1900, 100, - AXP806_BLDO3_V_CTRL, 0x0f, AXP806_PWR_OUT_CTRL2, BIT(2)), + AXP806_BLDO3_V_CTRL, AXP806_BLDO3_V_CTRL_MASK, + AXP806_PWR_OUT_CTRL2, AXP806_PWR_OUT_BLDO3_MASK), AXP_DESC(AXP806, BLDO4, "bldo4", "bldoin", 700, 1900, 100, - AXP806_BLDO4_V_CTRL, 0x0f, AXP806_PWR_OUT_CTRL2, BIT(3)), + AXP806_BLDO4_V_CTRL, AXP806_BLDO4_V_CTRL_MASK, + AXP806_PWR_OUT_CTRL2, AXP806_PWR_OUT_BLDO4_MASK), AXP_DESC(AXP806, CLDO1, "cldo1", "cldoin", 700, 3300, 100, - AXP806_CLDO1_V_CTRL, 0x1f, AXP806_PWR_OUT_CTRL2, BIT(4)), - AXP_DESC_RANGES(AXP806, CLDO2, "cldo2", "cldoin", axp803_dldo2_ranges, - 32, AXP806_CLDO2_V_CTRL, 0x1f, AXP806_PWR_OUT_CTRL2, - BIT(5)), + AXP806_CLDO1_V_CTRL, AXP806_CLDO1_V_CTRL_MASK, + AXP806_PWR_OUT_CTRL2, AXP806_PWR_OUT_CLDO1_MASK), + AXP_DESC_RANGES(AXP806, CLDO2, "cldo2", "cldoin", + axp803_dldo2_ranges, AXP803_DLDO2_NUM_VOLTAGES, + AXP806_CLDO2_V_CTRL, AXP806_CLDO2_V_CTRL_MASK, + AXP806_PWR_OUT_CTRL2, AXP806_PWR_OUT_CLDO2_MASK), AXP_DESC(AXP806, CLDO3, "cldo3", "cldoin", 700, 3300, 100, - AXP806_CLDO3_V_CTRL, 0x1f, AXP806_PWR_OUT_CTRL2, BIT(6)), - AXP_DESC_SW(AXP806, SW, "sw", "swin", AXP806_PWR_OUT_CTRL2, BIT(7)), + AXP806_CLDO3_V_CTRL, AXP806_CLDO3_V_CTRL_MASK, + AXP806_PWR_OUT_CTRL2, AXP806_PWR_OUT_CLDO3_MASK), + AXP_DESC_SW(AXP806, SW, "sw", "swin", + AXP806_PWR_OUT_CTRL2, AXP806_PWR_OUT_SW_MASK), }; static const struct regulator_linear_range axp809_dcdc4_ranges[] = { - REGULATOR_LINEAR_RANGE(600000, 0x0, 0x2f, 20000), - REGULATOR_LINEAR_RANGE(1800000, 0x30, 0x38, 100000), + REGULATOR_LINEAR_RANGE(600000, + AXP809_DCDC4_600mV_START, + AXP809_DCDC4_600mV_END, + 20000), + REGULATOR_LINEAR_RANGE(1800000, + AXP809_DCDC4_1800mV_START, + AXP809_DCDC4_1800mV_END, + 100000), }; static const struct regulator_desc axp809_regulators[] = { AXP_DESC(AXP809, DCDC1, "dcdc1", "vin1", 1600, 3400, 100, - AXP22X_DCDC1_V_OUT, 0x1f, AXP22X_PWR_OUT_CTRL1, BIT(1)), + AXP22X_DCDC1_V_OUT, AXP22X_DCDC1_V_OUT_MASK, + AXP22X_PWR_OUT_CTRL1, AXP22X_PWR_OUT_DCDC1_MASK), AXP_DESC(AXP809, DCDC2, "dcdc2", "vin2", 600, 1540, 20, - AXP22X_DCDC2_V_OUT, 0x3f, AXP22X_PWR_OUT_CTRL1, BIT(2)), + AXP22X_DCDC2_V_OUT, AXP22X_DCDC2_V_OUT_MASK, + AXP22X_PWR_OUT_CTRL1, AXP22X_PWR_OUT_DCDC2_MASK), AXP_DESC(AXP809, DCDC3, "dcdc3", "vin3", 600, 1860, 20, - AXP22X_DCDC3_V_OUT, 0x3f, AXP22X_PWR_OUT_CTRL1, BIT(3)), - AXP_DESC_RANGES(AXP809, DCDC4, "dcdc4", "vin4", axp809_dcdc4_ranges, - 57, AXP22X_DCDC4_V_OUT, 0x3f, AXP22X_PWR_OUT_CTRL1, - BIT(4)), + AXP22X_DCDC3_V_OUT, AXP22X_DCDC3_V_OUT_MASK, + AXP22X_PWR_OUT_CTRL1, AXP22X_PWR_OUT_DCDC3_MASK), + AXP_DESC_RANGES(AXP809, DCDC4, "dcdc4", "vin4", + axp809_dcdc4_ranges, AXP809_DCDC4_NUM_VOLTAGES, + AXP22X_DCDC4_V_OUT, AXP22X_DCDC4_V_OUT_MASK, + AXP22X_PWR_OUT_CTRL1, AXP22X_PWR_OUT_DCDC4_MASK), AXP_DESC(AXP809, DCDC5, "dcdc5", "vin5", 1000, 2550, 50, - AXP22X_DCDC5_V_OUT, 0x1f, AXP22X_PWR_OUT_CTRL1, BIT(5)), + AXP22X_DCDC5_V_OUT, AXP22X_DCDC5_V_OUT_MASK, + AXP22X_PWR_OUT_CTRL1, AXP22X_PWR_OUT_DCDC5_MASK), /* secondary switchable output of DCDC1 */ - AXP_DESC_SW(AXP809, DC1SW, "dc1sw", NULL, AXP22X_PWR_OUT_CTRL2, - BIT(7)), + AXP_DESC_SW(AXP809, DC1SW, "dc1sw", NULL, + AXP22X_PWR_OUT_CTRL2, AXP22X_PWR_OUT_DC1SW_MASK), /* LDO regulator internally chained to DCDC5 */ AXP_DESC(AXP809, DC5LDO, "dc5ldo", NULL, 700, 1400, 100, - AXP22X_DC5LDO_V_OUT, 0x7, AXP22X_PWR_OUT_CTRL1, BIT(0)), + AXP22X_DC5LDO_V_OUT, AXP22X_DC5LDO_V_OUT_MASK, + AXP22X_PWR_OUT_CTRL1, AXP22X_PWR_OUT_DC5LDO_MASK), AXP_DESC(AXP809, ALDO1, "aldo1", "aldoin", 700, 3300, 100, - AXP22X_ALDO1_V_OUT, 0x1f, AXP22X_PWR_OUT_CTRL1, BIT(6)), + AXP22X_ALDO1_V_OUT, AXP22X_ALDO1_V_OUT_MASK, + AXP22X_PWR_OUT_CTRL1, AXP22X_PWR_OUT_ALDO1_MASK), AXP_DESC(AXP809, ALDO2, "aldo2", "aldoin", 700, 3300, 100, - AXP22X_ALDO2_V_OUT, 0x1f, AXP22X_PWR_OUT_CTRL1, BIT(7)), + AXP22X_ALDO2_V_OUT, AXP22X_ALDO2_V_OUT_MASK, + AXP22X_PWR_OUT_CTRL1, AXP22X_PWR_OUT_ALDO2_MASK), AXP_DESC(AXP809, ALDO3, "aldo3", "aldoin", 700, 3300, 100, - AXP22X_ALDO3_V_OUT, 0x1f, AXP22X_PWR_OUT_CTRL2, BIT(5)), - AXP_DESC_RANGES(AXP809, DLDO1, "dldo1", "dldoin", axp803_dldo2_ranges, - 32, AXP22X_DLDO1_V_OUT, 0x1f, AXP22X_PWR_OUT_CTRL2, - BIT(3)), + AXP22X_ALDO3_V_OUT, AXP22X_ALDO3_V_OUT_MASK, + AXP22X_PWR_OUT_CTRL2, AXP22X_PWR_OUT_ALDO3_MASK), + AXP_DESC_RANGES(AXP809, DLDO1, "dldo1", "dldoin", + axp803_dldo2_ranges, AXP803_DLDO2_NUM_VOLTAGES, + AXP22X_DLDO1_V_OUT, AXP22X_DLDO1_V_OUT_MASK, + AXP22X_PWR_OUT_CTRL2, AXP22X_PWR_OUT_DLDO1_MASK), AXP_DESC(AXP809, DLDO2, "dldo2", "dldoin", 700, 3300, 100, - AXP22X_DLDO2_V_OUT, 0x1f, AXP22X_PWR_OUT_CTRL2, BIT(4)), + AXP22X_DLDO2_V_OUT, AXP22X_DLDO2_V_OUT_MASK, + AXP22X_PWR_OUT_CTRL2, AXP22X_PWR_OUT_DLDO2_MASK), AXP_DESC(AXP809, ELDO1, "eldo1", "eldoin", 700, 3300, 100, - AXP22X_ELDO1_V_OUT, 0x1f, AXP22X_PWR_OUT_CTRL2, BIT(0)), + AXP22X_ELDO1_V_OUT, AXP22X_ELDO1_V_OUT_MASK, + AXP22X_PWR_OUT_CTRL2, AXP22X_PWR_OUT_ELDO1_MASK), AXP_DESC(AXP809, ELDO2, "eldo2", "eldoin", 700, 3300, 100, - AXP22X_ELDO2_V_OUT, 0x1f, AXP22X_PWR_OUT_CTRL2, BIT(1)), + AXP22X_ELDO2_V_OUT, AXP22X_ELDO2_V_OUT_MASK, + AXP22X_PWR_OUT_CTRL2, AXP22X_PWR_OUT_ELDO2_MASK), AXP_DESC(AXP809, ELDO3, "eldo3", "eldoin", 700, 3300, 100, - AXP22X_ELDO3_V_OUT, 0x1f, AXP22X_PWR_OUT_CTRL2, BIT(2)), + AXP22X_ELDO3_V_OUT, AXP22X_ELDO3_V_OUT_MASK, + AXP22X_PWR_OUT_CTRL2, AXP22X_PWR_OUT_ELDO3_MASK), /* * Note the datasheet only guarantees reliable operation up to * 3.3V, this needs to be enforced via dts provided constraints */ AXP_DESC_IO(AXP809, LDO_IO0, "ldo_io0", "ips", 700, 3800, 100, - AXP22X_LDO_IO0_V_OUT, 0x1f, AXP20X_GPIO0_CTRL, 0x07, + AXP22X_LDO_IO0_V_OUT, AXP22X_LDO_IO0_V_OUT_MASK, + AXP20X_GPIO0_CTRL, AXP20X_GPIO0_FUNC_MASK, AXP22X_IO_ENABLED, AXP22X_IO_DISABLED), /* * Note the datasheet only guarantees reliable operation up to * 3.3V, this needs to be enforced via dts provided constraints */ AXP_DESC_IO(AXP809, LDO_IO1, "ldo_io1", "ips", 700, 3800, 100, - AXP22X_LDO_IO1_V_OUT, 0x1f, AXP20X_GPIO1_CTRL, 0x07, + AXP22X_LDO_IO1_V_OUT, AXP22X_LDO_IO1_V_OUT_MASK, + AXP20X_GPIO1_CTRL, AXP20X_GPIO1_FUNC_MASK, AXP22X_IO_ENABLED, AXP22X_IO_DISABLED), AXP_DESC_FIXED(AXP809, RTC_LDO, "rtc_ldo", "ips", 1800), - AXP_DESC_SW(AXP809, SW, "sw", "swin", AXP22X_PWR_OUT_CTRL2, BIT(6)), + AXP_DESC_SW(AXP809, SW, "sw", "swin", + AXP22X_PWR_OUT_CTRL2, AXP22X_PWR_OUT_SW_MASK), }; static const struct regulator_desc axp813_regulators[] = { AXP_DESC(AXP813, DCDC1, "dcdc1", "vin1", 1600, 3400, 100, - AXP803_DCDC1_V_OUT, 0x1f, AXP22X_PWR_OUT_CTRL1, BIT(0)), - AXP_DESC_RANGES(AXP813, DCDC2, "dcdc2", "vin2", axp803_dcdc234_ranges, - 76, AXP803_DCDC2_V_OUT, 0x7f, AXP22X_PWR_OUT_CTRL1, - BIT(1)), - AXP_DESC_RANGES(AXP813, DCDC3, "dcdc3", "vin3", axp803_dcdc234_ranges, - 76, AXP803_DCDC3_V_OUT, 0x7f, AXP22X_PWR_OUT_CTRL1, - BIT(2)), - AXP_DESC_RANGES(AXP813, DCDC4, "dcdc4", "vin4", axp803_dcdc234_ranges, - 76, AXP803_DCDC4_V_OUT, 0x7f, AXP22X_PWR_OUT_CTRL1, - BIT(3)), - AXP_DESC_RANGES(AXP813, DCDC5, "dcdc5", "vin5", axp803_dcdc5_ranges, - 68, AXP803_DCDC5_V_OUT, 0x7f, AXP22X_PWR_OUT_CTRL1, - BIT(4)), - AXP_DESC_RANGES(AXP813, DCDC6, "dcdc6", "vin6", axp803_dcdc6_ranges, - 72, AXP803_DCDC6_V_OUT, 0x7f, AXP22X_PWR_OUT_CTRL1, - BIT(5)), - AXP_DESC_RANGES(AXP813, DCDC7, "dcdc7", "vin7", axp803_dcdc6_ranges, - 72, AXP813_DCDC7_V_OUT, 0x7f, AXP22X_PWR_OUT_CTRL1, - BIT(6)), + AXP803_DCDC1_V_OUT, AXP803_DCDC1_V_OUT_MASK, + AXP22X_PWR_OUT_CTRL1, AXP803_PWR_OUT_DCDC1_MASK), + AXP_DESC_RANGES(AXP813, DCDC2, "dcdc2", "vin2", + axp803_dcdc234_ranges, AXP803_DCDC234_NUM_VOLTAGES, + AXP803_DCDC2_V_OUT, AXP803_DCDC2_V_OUT_MASK, + AXP22X_PWR_OUT_CTRL1, AXP803_PWR_OUT_DCDC2_MASK), + AXP_DESC_RANGES(AXP813, DCDC3, "dcdc3", "vin3", + axp803_dcdc234_ranges, AXP803_DCDC234_NUM_VOLTAGES, + AXP803_DCDC3_V_OUT, AXP803_DCDC3_V_OUT_MASK, + AXP22X_PWR_OUT_CTRL1, AXP803_PWR_OUT_DCDC3_MASK), + AXP_DESC_RANGES(AXP813, DCDC4, "dcdc4", "vin4", + axp803_dcdc234_ranges, AXP803_DCDC234_NUM_VOLTAGES, + AXP803_DCDC4_V_OUT, AXP803_DCDC4_V_OUT_MASK, + AXP22X_PWR_OUT_CTRL1, AXP803_PWR_OUT_DCDC4_MASK), + AXP_DESC_RANGES(AXP813, DCDC5, "dcdc5", "vin5", + axp803_dcdc5_ranges, AXP803_DCDC5_NUM_VOLTAGES, + AXP803_DCDC5_V_OUT, AXP803_DCDC5_V_OUT_MASK, + AXP22X_PWR_OUT_CTRL1, AXP803_PWR_OUT_DCDC5_MASK), + AXP_DESC_RANGES(AXP813, DCDC6, "dcdc6", "vin6", + axp803_dcdc6_ranges, AXP803_DCDC6_NUM_VOLTAGES, + AXP803_DCDC6_V_OUT, AXP803_DCDC6_V_OUT_MASK, + AXP22X_PWR_OUT_CTRL1, AXP803_PWR_OUT_DCDC6_MASK), + AXP_DESC_RANGES(AXP813, DCDC7, "dcdc7", "vin7", + axp803_dcdc6_ranges, AXP803_DCDC6_NUM_VOLTAGES, + AXP813_DCDC7_V_OUT, AXP813_DCDC7_V_OUT_MASK, + AXP22X_PWR_OUT_CTRL1, AXP813_PWR_OUT_DCDC7_MASK), AXP_DESC(AXP813, ALDO1, "aldo1", "aldoin", 700, 3300, 100, - AXP22X_ALDO1_V_OUT, 0x1f, AXP22X_PWR_OUT_CTRL3, BIT(5)), + AXP22X_ALDO1_V_OUT, AXP22X_ALDO1_V_OUT_MASK, + AXP22X_PWR_OUT_CTRL3, AXP806_PWR_OUT_ALDO1_MASK), AXP_DESC(AXP813, ALDO2, "aldo2", "aldoin", 700, 3300, 100, - AXP22X_ALDO2_V_OUT, 0x1f, AXP22X_PWR_OUT_CTRL3, BIT(6)), + AXP22X_ALDO2_V_OUT, AXP22X_ALDO2_V_OUT, + AXP22X_PWR_OUT_CTRL3, AXP806_PWR_OUT_ALDO2_MASK), AXP_DESC(AXP813, ALDO3, "aldo3", "aldoin", 700, 3300, 100, - AXP22X_ALDO3_V_OUT, 0x1f, AXP22X_PWR_OUT_CTRL3, BIT(7)), + AXP22X_ALDO3_V_OUT, AXP22X_ALDO3_V_OUT_MASK, + AXP22X_PWR_OUT_CTRL3, AXP806_PWR_OUT_ALDO3_MASK), AXP_DESC(AXP813, DLDO1, "dldo1", "dldoin", 700, 3300, 100, - AXP22X_DLDO1_V_OUT, 0x1f, AXP22X_PWR_OUT_CTRL2, BIT(3)), - AXP_DESC_RANGES(AXP813, DLDO2, "dldo2", "dldoin", axp803_dldo2_ranges, - 32, AXP22X_DLDO2_V_OUT, 0x1f, AXP22X_PWR_OUT_CTRL2, - BIT(4)), + AXP22X_DLDO1_V_OUT, AXP22X_DLDO1_V_OUT_MASK, + AXP22X_PWR_OUT_CTRL2, AXP22X_PWR_OUT_DLDO1_MASK), + AXP_DESC_RANGES(AXP813, DLDO2, "dldo2", "dldoin", + axp803_dldo2_ranges, AXP803_DLDO2_NUM_VOLTAGES, + AXP22X_DLDO2_V_OUT, AXP22X_DLDO2_V_OUT, + AXP22X_PWR_OUT_CTRL2, AXP22X_PWR_OUT_DLDO2_MASK), AXP_DESC(AXP813, DLDO3, "dldo3", "dldoin", 700, 3300, 100, - AXP22X_DLDO3_V_OUT, 0x1f, AXP22X_PWR_OUT_CTRL2, BIT(5)), + AXP22X_DLDO3_V_OUT, AXP22X_DLDO3_V_OUT_MASK, + AXP22X_PWR_OUT_CTRL2, AXP22X_PWR_OUT_DLDO3_MASK), AXP_DESC(AXP813, DLDO4, "dldo4", "dldoin", 700, 3300, 100, - AXP22X_DLDO4_V_OUT, 0x1f, AXP22X_PWR_OUT_CTRL2, BIT(6)), + AXP22X_DLDO4_V_OUT, AXP22X_DLDO4_V_OUT_MASK, + AXP22X_PWR_OUT_CTRL2, AXP22X_PWR_OUT_DLDO4_MASK), AXP_DESC(AXP813, ELDO1, "eldo1", "eldoin", 700, 1900, 50, - AXP22X_ELDO1_V_OUT, 0x1f, AXP22X_PWR_OUT_CTRL2, BIT(0)), + AXP22X_ELDO1_V_OUT, AXP22X_ELDO1_V_OUT_MASK, + AXP22X_PWR_OUT_CTRL2, AXP22X_PWR_OUT_ELDO1_MASK), AXP_DESC(AXP813, ELDO2, "eldo2", "eldoin", 700, 1900, 50, - AXP22X_ELDO2_V_OUT, 0x1f, AXP22X_PWR_OUT_CTRL2, BIT(1)), + AXP22X_ELDO2_V_OUT, AXP22X_ELDO2_V_OUT_MASK, + AXP22X_PWR_OUT_CTRL2, AXP22X_PWR_OUT_ELDO2_MASK), AXP_DESC(AXP813, ELDO3, "eldo3", "eldoin", 700, 1900, 50, - AXP22X_ELDO3_V_OUT, 0x1f, AXP22X_PWR_OUT_CTRL2, BIT(2)), + AXP22X_ELDO3_V_OUT, AXP22X_ELDO3_V_OUT, + AXP22X_PWR_OUT_CTRL2, AXP22X_PWR_OUT_ELDO3_MASK), /* to do / check ... */ AXP_DESC(AXP813, FLDO1, "fldo1", "fldoin", 700, 1450, 50, - AXP803_FLDO1_V_OUT, 0x0f, AXP22X_PWR_OUT_CTRL3, BIT(2)), + AXP803_FLDO1_V_OUT, AXP803_FLDO1_V_OUT_MASK, + AXP22X_PWR_OUT_CTRL3, AXP803_PWR_OUT_FLDO1_MASK), AXP_DESC(AXP813, FLDO2, "fldo2", "fldoin", 700, 1450, 50, - AXP803_FLDO2_V_OUT, 0x0f, AXP22X_PWR_OUT_CTRL3, BIT(3)), + AXP803_FLDO2_V_OUT, AXP803_FLDO2_V_OUT_MASK, + AXP22X_PWR_OUT_CTRL3, AXP803_PWR_OUT_FLDO2_MASK), /* * TODO: FLDO3 = {DCDC5, FLDOIN} / 2 * @@ -482,12 +853,15 @@ static const struct regulator_desc axp813_regulators[] = { */ AXP_DESC_FIXED(AXP813, RTC_LDO, "rtc-ldo", "ips", 1800), AXP_DESC_IO(AXP813, LDO_IO0, "ldo-io0", "ips", 700, 3300, 100, - AXP22X_LDO_IO0_V_OUT, 0x1f, AXP20X_GPIO0_CTRL, 0x07, + AXP22X_LDO_IO0_V_OUT, AXP22X_LDO_IO0_V_OUT_MASK, + AXP20X_GPIO0_CTRL, AXP20X_GPIO0_FUNC_MASK, AXP22X_IO_ENABLED, AXP22X_IO_DISABLED), AXP_DESC_IO(AXP813, LDO_IO1, "ldo-io1", "ips", 700, 3300, 100, - AXP22X_LDO_IO1_V_OUT, 0x1f, AXP20X_GPIO1_CTRL, 0x07, + AXP22X_LDO_IO1_V_OUT, AXP22X_LDO_IO1_V_OUT_MASK, + AXP20X_GPIO1_CTRL, AXP20X_GPIO1_FUNC_MASK, AXP22X_IO_ENABLED, AXP22X_IO_DISABLED), - AXP_DESC_SW(AXP813, SW, "sw", "swin", AXP22X_PWR_OUT_CTRL2, BIT(7)), + AXP_DESC_SW(AXP813, SW, "sw", "swin", + AXP22X_PWR_OUT_CTRL2, AXP22X_PWR_OUT_DC1SW_MASK), }; static int axp20x_set_dcdc_freq(struct platform_device *pdev, u32 dcdcfreq) @@ -663,9 +1037,9 @@ static bool axp20x_is_polyphase_slave(struct axp20x_dev *axp20x, int id) switch (id) { case AXP803_DCDC3: - return !!(reg & BIT(6)); + return !!(reg & AXP803_DCDC23_POLYPHASE_DUAL); case AXP803_DCDC6: - return !!(reg & BIT(5)); + return !!(reg & AXP803_DCDC56_POLYPHASE_DUAL); } break; @@ -674,12 +1048,15 @@ static bool axp20x_is_polyphase_slave(struct axp20x_dev *axp20x, int id) switch (id) { case AXP806_DCDCB: - return (((reg & GENMASK(7, 6)) == BIT(6)) || - ((reg & GENMASK(7, 6)) == BIT(7))); + return (((reg & AXP806_DCDCABC_POLYPHASE_MASK) == + AXP806_DCDCAB_POLYPHASE_DUAL) || + ((reg & AXP806_DCDCABC_POLYPHASE_MASK) == + AXP806_DCDCABC_POLYPHASE_TRI)); case AXP806_DCDCC: - return ((reg & GENMASK(7, 6)) == BIT(7)); + return ((reg & AXP806_DCDCABC_POLYPHASE_MASK) == + AXP806_DCDCABC_POLYPHASE_TRI); case AXP806_DCDCE: - return !!(reg & BIT(5)); + return !!(reg & AXP806_DCDCDE_POLYPHASE_DUAL); } break; From db6e6244cb714029959f26caa58522e3c20d4c61 Mon Sep 17 00:00:00 2001 From: Pascal PAILLET-LME Date: Fri, 30 Nov 2018 09:55:51 +0000 Subject: [PATCH 37/85] regulator: stpmic1: fix regulator_lock usage fix a compilation issue due to regulator_lock usage. Signed-off-by: Pascal Paillet Signed-off-by: Mark Brown --- drivers/regulator/stpmic1_regulator.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/regulator/stpmic1_regulator.c b/drivers/regulator/stpmic1_regulator.c index eac0848a78c7f6..16ba0297f709b1 100644 --- a/drivers/regulator/stpmic1_regulator.c +++ b/drivers/regulator/stpmic1_regulator.c @@ -489,7 +489,7 @@ static irqreturn_t stpmic1_curlim_irq_handler(int irq, void *data) { struct regulator_dev *rdev = (struct regulator_dev *)data; - regulator_lock(rdev, NULL); + regulator_lock(rdev); /* Send an overcurrent notification */ regulator_notifier_call_chain(rdev, From 925c85e21ed8773c8b0ab01d60618b862847a10f Mon Sep 17 00:00:00 2001 From: Charles Keepax Date: Thu, 29 Nov 2018 10:28:20 +0000 Subject: [PATCH 38/85] regulator: Factor out location of init data OF node To support future additions factor out the location of the OF node containing the init data for the regulator from the code that parses the init data. Signed-off-by: Charles Keepax Signed-off-by: Mark Brown --- drivers/regulator/of_regulator.c | 64 ++++++++++++++++++-------------- 1 file changed, 37 insertions(+), 27 deletions(-) diff --git a/drivers/regulator/of_regulator.c b/drivers/regulator/of_regulator.c index c711a0a2bc4bd1..4bb8928bdb3f6a 100644 --- a/drivers/regulator/of_regulator.c +++ b/drivers/regulator/of_regulator.c @@ -371,13 +371,10 @@ int of_regulator_match(struct device *dev, struct device_node *node, } EXPORT_SYMBOL_GPL(of_regulator_match); -struct regulator_init_data *regulator_of_get_init_data(struct device *dev, - const struct regulator_desc *desc, - struct regulator_config *config, - struct device_node **node) +struct device_node *regulator_of_get_init_node(struct device *dev, + const struct regulator_desc *desc) { struct device_node *search, *child; - struct regulator_init_data *init_data = NULL; const char *name; if (!dev->of_node || !desc->of_match) @@ -400,35 +397,48 @@ struct regulator_init_data *regulator_of_get_init_data(struct device *dev, if (!name) name = child->name; - if (strcmp(desc->of_match, name)) - continue; + if (!strcmp(desc->of_match, name)) + return of_node_get(child); + } - init_data = of_get_regulator_init_data(dev, child, desc); - if (!init_data) { - dev_err(dev, - "failed to parse DT for regulator %pOFn\n", - child); - break; - } + of_node_put(search); - if (desc->of_parse_cb) { - if (desc->of_parse_cb(child, desc, config)) { - dev_err(dev, - "driver callback failed to parse DT for regulator %pOFn\n", - child); - init_data = NULL; - break; - } - } + return NULL; +} - of_node_get(child); - *node = child; - break; +struct regulator_init_data *regulator_of_get_init_data(struct device *dev, + const struct regulator_desc *desc, + struct regulator_config *config, + struct device_node **node) +{ + struct device_node *child; + struct regulator_init_data *init_data = NULL; + + child = regulator_of_get_init_node(dev, desc); + if (!child) + return NULL; + + init_data = of_get_regulator_init_data(dev, child, desc); + if (!init_data) { + dev_err(dev, "failed to parse DT for regulator %pOFn\n", child); + goto error; } - of_node_put(search); + if (desc->of_parse_cb && desc->of_parse_cb(child, desc, config)) { + dev_err(dev, + "driver callback failed to parse DT for regulator %pOFn\n", + child); + goto error; + } + + *node = child; return init_data; + +error: + of_node_put(child); + + return NULL; } static int of_node_match(struct device *dev, const void *data) From eba9473f6761d8df6cabf278b272cc5466733737 Mon Sep 17 00:00:00 2001 From: Charles Keepax Date: Thu, 29 Nov 2018 10:28:21 +0000 Subject: [PATCH 39/85] regulator: Allow regulator nodes to contain their own init data Currently it is expected that regulator init data will be defined as a series of sub-nodes from the node that bound in the driver. Add support for a node to both bind in a driver and contain init data for that regulator. Signed-off-by: Charles Keepax Signed-off-by: Mark Brown --- drivers/regulator/of_regulator.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/drivers/regulator/of_regulator.c b/drivers/regulator/of_regulator.c index 4bb8928bdb3f6a..ffa5fc3724e4f2 100644 --- a/drivers/regulator/of_regulator.c +++ b/drivers/regulator/of_regulator.c @@ -380,12 +380,16 @@ struct device_node *regulator_of_get_init_node(struct device *dev, if (!dev->of_node || !desc->of_match) return NULL; - if (desc->regulators_node) + if (desc->regulators_node) { search = of_get_child_by_name(dev->of_node, desc->regulators_node); - else + } else { search = of_node_get(dev->of_node); + if (!strcmp(desc->of_match, search->name)) + return search; + } + if (!search) { dev_dbg(dev, "Failed to find regulator container node '%s'\n", desc->regulators_node); From c32569e358adea225f053ef9d97a4c8c18be8743 Mon Sep 17 00:00:00 2001 From: Rob Herring Date: Wed, 5 Dec 2018 13:50:37 -0600 Subject: [PATCH 40/85] regulator: Use of_node_name_eq for node name comparisons Convert string compares of DT node names to use of_node_name_eq helper instead. This removes direct access to the node name pointer. For instances using of_node_cmp, this has the side effect of now using case sensitive comparisons. This should not matter for any FDT based system which all of these are. Cc: Liam Girdwood Cc: Mark Brown Cc: Support Opensource Cc: Sangbeom Kim Cc: Krzysztof Kozlowski Cc: Bartlomiej Zolnierkiewicz Cc: linux-samsung-soc@vger.kernel.org Signed-off-by: Rob Herring Acked-by: Adam Thomson Signed-off-by: Mark Brown --- drivers/regulator/88pm8607.c | 2 +- drivers/regulator/da9052-regulator.c | 2 +- drivers/regulator/max8997-regulator.c | 2 +- drivers/regulator/mc13xxx-regulator-core.c | 2 +- drivers/regulator/qcom-rpmh-regulator.c | 2 +- drivers/regulator/s5m8767.c | 2 +- 6 files changed, 6 insertions(+), 6 deletions(-) diff --git a/drivers/regulator/88pm8607.c b/drivers/regulator/88pm8607.c index fd86446e499b23..28f55248eb9033 100644 --- a/drivers/regulator/88pm8607.c +++ b/drivers/regulator/88pm8607.c @@ -328,7 +328,7 @@ static int pm8607_regulator_dt_init(struct platform_device *pdev, return -ENODEV; } for_each_child_of_node(nproot, np) { - if (!of_node_cmp(np->name, info->desc.name)) { + if (of_node_name_eq(np, info->desc.name)) { config->init_data = of_get_regulator_init_data(&pdev->dev, np, &info->desc); diff --git a/drivers/regulator/da9052-regulator.c b/drivers/regulator/da9052-regulator.c index 37e4025203e349..207cb3859dcc66 100644 --- a/drivers/regulator/da9052-regulator.c +++ b/drivers/regulator/da9052-regulator.c @@ -435,7 +435,7 @@ static int da9052_regulator_probe(struct platform_device *pdev) return -ENODEV; for_each_child_of_node(nproot, np) { - if (!of_node_cmp(np->name, + if (of_node_name_eq(np, regulator->info->reg_desc.name)) { config.init_data = of_get_regulator_init_data( &pdev->dev, np, diff --git a/drivers/regulator/max8997-regulator.c b/drivers/regulator/max8997-regulator.c index 3bf5ddfaaea89d..4d2487279a0a01 100644 --- a/drivers/regulator/max8997-regulator.c +++ b/drivers/regulator/max8997-regulator.c @@ -925,7 +925,7 @@ static int max8997_pmic_dt_parse_pdata(struct platform_device *pdev, pdata->regulators = rdata; for_each_child_of_node(regulators_np, reg_np) { for (i = 0; i < ARRAY_SIZE(regulators); i++) - if (!of_node_cmp(reg_np->name, regulators[i].name)) + if (of_node_name_eq(reg_np, regulators[i].name)) break; if (i == ARRAY_SIZE(regulators)) { diff --git a/drivers/regulator/mc13xxx-regulator-core.c b/drivers/regulator/mc13xxx-regulator-core.c index 65eb1e0350cfd3..2243138d8a5806 100644 --- a/drivers/regulator/mc13xxx-regulator-core.c +++ b/drivers/regulator/mc13xxx-regulator-core.c @@ -186,7 +186,7 @@ struct mc13xxx_regulator_init_data *mc13xxx_parse_regulators_dt( for (i = 0; i < num_regulators; i++) { if (!regulators[i].desc.name) continue; - if (!of_node_cmp(child->name, + if (of_node_name_eq(child, regulators[i].desc.name)) { p->id = i; p->init_data = of_get_regulator_init_data( diff --git a/drivers/regulator/qcom-rpmh-regulator.c b/drivers/regulator/qcom-rpmh-regulator.c index 39ccf53fdeb38f..b2c2d01d163706 100644 --- a/drivers/regulator/qcom-rpmh-regulator.c +++ b/drivers/regulator/qcom-rpmh-regulator.c @@ -410,7 +410,7 @@ static int rpmh_regulator_init_vreg(struct rpmh_vreg *vreg, struct device *dev, vreg->dev = dev; for (rpmh_data = pmic_rpmh_data; rpmh_data->name; rpmh_data++) - if (!strcmp(rpmh_data->name, node->name)) + if (of_node_name_eq(node, rpmh_data->name)) break; if (!rpmh_data->name) { diff --git a/drivers/regulator/s5m8767.c b/drivers/regulator/s5m8767.c index 219b9afda0cb93..654f65abcb5a0c 100644 --- a/drivers/regulator/s5m8767.c +++ b/drivers/regulator/s5m8767.c @@ -561,7 +561,7 @@ static int s5m8767_pmic_dt_parse_pdata(struct platform_device *pdev, pdata->opmode = rmode; for_each_child_of_node(regulators_np, reg_np) { for (i = 0; i < ARRAY_SIZE(regulators); i++) - if (!of_node_cmp(reg_np->name, regulators[i].name)) + if (of_node_name_eq(reg_np, regulators[i].name)) break; if (i == ARRAY_SIZE(regulators)) { From 0edb040d416ab350c02ac710233349aeaf8c2a26 Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Thu, 6 Dec 2018 13:43:37 +0100 Subject: [PATCH 41/85] regulator: core: Track dangling GPIO descriptors If a GPIO descriptor is passed to the regulator_register() function inside the config->ena_gpiod callers must be sure that once they call this API the regulator core owns that descriptor and will make sure to issue gpiod_put() on it, no matter whether the call is successful or not. For device tree regulators, the regulator core will automatically set up regulator init data from the device tree when registering a regulator by calling regulator_of_get_init_data() which in turn calls down to the regulator driver's .of_parse_cb() callback. This callback (in drivers such as for max77686) may also choose to fill in the config->ena_gpiod field with a GPIO descriptor. Harden the errorpath of regulator_register() to properly gpiod_put() any passed in cfg->ena_gpiod or any gpiod coming from the device tree on any type of error. Signed-off-by: Linus Walleij Reviewed-by: Marek Szyprowski Tested-by: Marek Szyprowski Reviewed-by: Charles Keepax Signed-off-by: Mark Brown --- drivers/regulator/core.c | 55 ++++++++++++++++++++++++++++++++-------- 1 file changed, 45 insertions(+), 10 deletions(-) diff --git a/drivers/regulator/core.c b/drivers/regulator/core.c index 757619878068c9..79cb090ff22f61 100644 --- a/drivers/regulator/core.c +++ b/drivers/regulator/core.c @@ -4868,21 +4868,33 @@ regulator_register(const struct regulator_desc *regulator_desc, struct regulator_config *config = NULL; static atomic_t regulator_no = ATOMIC_INIT(-1); struct regulator_dev *rdev; + bool dangling_cfg_gpiod = false; + bool dangling_of_gpiod = false; struct device *dev; int ret, i; - if (regulator_desc == NULL || cfg == NULL) + if (cfg == NULL) return ERR_PTR(-EINVAL); + if (cfg->ena_gpiod) + dangling_cfg_gpiod = true; + if (regulator_desc == NULL) { + ret = -EINVAL; + goto rinse; + } dev = cfg->dev; WARN_ON(!dev); - if (regulator_desc->name == NULL || regulator_desc->ops == NULL) - return ERR_PTR(-EINVAL); + if (regulator_desc->name == NULL || regulator_desc->ops == NULL) { + ret = -EINVAL; + goto rinse; + } if (regulator_desc->type != REGULATOR_VOLTAGE && - regulator_desc->type != REGULATOR_CURRENT) - return ERR_PTR(-EINVAL); + regulator_desc->type != REGULATOR_CURRENT) { + ret = -EINVAL; + goto rinse; + } /* Only one of each should be implemented */ WARN_ON(regulator_desc->ops->get_voltage && @@ -4893,16 +4905,20 @@ regulator_register(const struct regulator_desc *regulator_desc, /* If we're using selectors we must implement list_voltage. */ if (regulator_desc->ops->get_voltage_sel && !regulator_desc->ops->list_voltage) { - return ERR_PTR(-EINVAL); + ret = -EINVAL; + goto rinse; } if (regulator_desc->ops->set_voltage_sel && !regulator_desc->ops->list_voltage) { - return ERR_PTR(-EINVAL); + ret = -EINVAL; + goto rinse; } rdev = kzalloc(sizeof(struct regulator_dev), GFP_KERNEL); - if (rdev == NULL) - return ERR_PTR(-ENOMEM); + if (rdev == NULL) { + ret = -ENOMEM; + goto rinse; + } /* * Duplicate the config so the driver could override it after @@ -4911,11 +4927,22 @@ regulator_register(const struct regulator_desc *regulator_desc, config = kmemdup(cfg, sizeof(*cfg), GFP_KERNEL); if (config == NULL) { kfree(rdev); - return ERR_PTR(-ENOMEM); + ret = -ENOMEM; + goto rinse; } init_data = regulator_of_get_init_data(dev, regulator_desc, config, &rdev->dev.of_node); + /* + * We need to keep track of any GPIO descriptor coming from the + * device tree until we have handled it over to the core. If the + * config that was passed in to this function DOES NOT contain + * a descriptor, and the config after this call DOES contain + * a descriptor, we definately got one from parsing the device + * tree. + */ + if (!cfg->ena_gpiod && config->ena_gpiod) + dangling_of_gpiod = true; if (!init_data) { init_data = config->init_data; rdev->dev.of_node = of_node_get(config->of_node); @@ -4954,6 +4981,9 @@ regulator_register(const struct regulator_desc *regulator_desc, config->ena_gpio, ret); goto clean; } + /* The regulator core took over the GPIO descriptor */ + dangling_cfg_gpiod = false; + dangling_of_gpiod = false; } /* register with sysfs */ @@ -5039,8 +5069,13 @@ regulator_register(const struct regulator_desc *regulator_desc, regulator_ena_gpio_free(rdev); mutex_unlock(®ulator_list_mutex); clean: + if (dangling_of_gpiod) + gpiod_put(config->ena_gpiod); kfree(rdev); kfree(config); +rinse: + if (dangling_cfg_gpiod) + gpiod_put(cfg->ena_gpiod); return ERR_PTR(ret); } EXPORT_SYMBOL_GPL(regulator_register); From 5e6f3ae5c13b12ecd440da2f511fc5fd3c8de520 Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Thu, 6 Dec 2018 13:43:38 +0100 Subject: [PATCH 42/85] regulator: fixed: Let core handle GPIO descriptor Use the gpiod_get() rather than the devm_* version so that the regulator core can handle the lifecycle of these descriptors. Fixes: efdfeb079cc3 ("regulator: fixed: Convert to use GPIO descriptor only") Signed-off-by: Linus Walleij Reviewed-by: Marek Szyprowski Tested-by: Marek Szyprowski Reviewed-by: Charles Keepax Signed-off-by: Mark Brown --- drivers/regulator/fixed.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/drivers/regulator/fixed.c b/drivers/regulator/fixed.c index ccc29038f19a98..9abdb91307662a 100644 --- a/drivers/regulator/fixed.c +++ b/drivers/regulator/fixed.c @@ -183,7 +183,11 @@ static int reg_fixed_voltage_probe(struct platform_device *pdev) */ gflags |= GPIOD_FLAGS_BIT_NONEXCLUSIVE; - cfg.ena_gpiod = devm_gpiod_get_optional(&pdev->dev, NULL, gflags); + /* + * Do not use devm* here: the regulator core takes over the + * lifecycle management of the GPIO descriptor. + */ + cfg.ena_gpiod = gpiod_get_optional(&pdev->dev, NULL, gflags); if (IS_ERR(cfg.ena_gpiod)) return PTR_ERR(cfg.ena_gpiod); From e8a33aa0e3bc22134bf2bf3ca6339d6a1b7ffe16 Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Thu, 6 Dec 2018 13:43:39 +0100 Subject: [PATCH 43/85] regulator: lm363x: Let core handle GPIO descriptor Use the gpiod_get() rather than the devm_* version so that the regulator core can handle the lifecycle of these descriptors. Fixes: b2d751b7f69b ("regulator: lm363x: Pass descriptor instead of GPIO number") Signed-off-by: Linus Walleij Reviewed-by: Marek Szyprowski Reviewed-by: Charles Keepax Signed-off-by: Mark Brown --- drivers/regulator/lm363x-regulator.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/drivers/regulator/lm363x-regulator.c b/drivers/regulator/lm363x-regulator.c index bbedb08d257b60..8c0e8419c43f08 100644 --- a/drivers/regulator/lm363x-regulator.c +++ b/drivers/regulator/lm363x-regulator.c @@ -224,13 +224,15 @@ static struct gpio_desc *lm363x_regulator_of_get_enable_gpio(struct device *dev, /* * Check LCM_EN1/2_GPIO is configured. * Those pins are used for enabling VPOS/VNEG LDOs. + * Do not use devm* here: the regulator core takes over the + * lifecycle management of the GPIO descriptor. */ switch (id) { case LM3632_LDO_POS: - return devm_gpiod_get_index_optional(dev, "enable", 0, + return 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, + return gpiod_get_index_optional(dev, "enable", 1, GPIOD_OUT_LOW | GPIOD_FLAGS_BIT_NONEXCLUSIVE); default: return NULL; @@ -263,6 +265,8 @@ static int lm363x_regulator_probe(struct platform_device *pdev) LM3632_EXT_EN_MASK, LM3632_EXT_EN_MASK); if (ret) { + if (gpiod) + gpiod_put(gpiod); dev_err(dev, "External pin err: %d\n", ret); return ret; } From 2bb8ede0b14187c19b5eedc62cf0b18b3bf00d3b Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Thu, 6 Dec 2018 13:43:40 +0100 Subject: [PATCH 44/85] regulator: lp8788-ldo: Let core handle GPIO descriptor Use the gpiod_get() rather than the devm_* version so that the regulator core can handle the lifecycle of these descriptors. Fixes: 2468f0d51548 ("regulator: lp8788-ldo: Pass descriptor instead of GPIO number") Signed-off-by: Linus Walleij Reviewed-by: Marek Szyprowski Reviewed-by: Charles Keepax Signed-off-by: Mark Brown --- drivers/regulator/lp8788-ldo.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/drivers/regulator/lp8788-ldo.c b/drivers/regulator/lp8788-ldo.c index 553b4790050f66..2ee22e7ea67567 100644 --- a/drivers/regulator/lp8788-ldo.c +++ b/drivers/regulator/lp8788-ldo.c @@ -501,8 +501,12 @@ static int lp8788_config_ldo_enable_mode(struct platform_device *pdev, return 0; } - /* FIXME: check default mode for GPIO here: high or low? */ - ldo->ena_gpiod = devm_gpiod_get_index_optional(&pdev->dev, + /* + * Do not use devm* here: the regulator core takes over the + * lifecycle management of the GPIO descriptor. + * FIXME: check default mode for GPIO here: high or low? + */ + ldo->ena_gpiod = gpiod_get_index_optional(&pdev->dev, "enable", enable_id, GPIOD_OUT_HIGH | From 894077d5c871f86e6c88a70ac016b6811a910b23 Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Thu, 6 Dec 2018 13:43:41 +0100 Subject: [PATCH 45/85] regulator: max8952: Let core handle GPIO descriptor Use the gpiod_get() rather than the devm_* version so that the regulator core can handle the lifecycle of these descriptors. Fixes: d7a261c2d1f2 ("regulator: max8952: Pass descriptor instead of GPIO number") Signed-off-by: Linus Walleij Reviewed-by: Marek Szyprowski Tested-by: Marek Szyprowski Reviewed-by: Charles Keepax Signed-off-by: Mark Brown --- drivers/regulator/max8952.c | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/drivers/regulator/max8952.c b/drivers/regulator/max8952.c index 6c39fff73b8acd..cf2a2912cb1bcd 100644 --- a/drivers/regulator/max8952.c +++ b/drivers/regulator/max8952.c @@ -231,9 +231,13 @@ static int max8952_pmic_probe(struct i2c_client *client, else gflags = GPIOD_OUT_LOW; gflags |= GPIOD_FLAGS_BIT_NONEXCLUSIVE; - gpiod = devm_gpiod_get_optional(&client->dev, - "max8952,en", - gflags); + /* + * Do not use devm* here: the regulator core takes over the + * lifecycle management of the GPIO descriptor. + */ + gpiod = gpiod_get_optional(&client->dev, + "max8952,en", + gflags); if (IS_ERR(gpiod)) return PTR_ERR(gpiod); if (gpiod) From fe6c473e3e41114301bfbf5710be56bf0eb233dc Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Thu, 6 Dec 2018 13:43:42 +0100 Subject: [PATCH 46/85] gpio: Export gpiod_get_from_of_node() This function already exist inside gpiolib, we were just reluctant to make it available to the kernel at large as the devm_* seemed to be enough for anyone. However we found out that regulators need to do their own lifecycle/refcounting on GPIO descriptors and explicitly call gpiod_put() when done with a descriptor, so export this function so we can hand the refcounting over to the regulator core for these descriptors after retrieveal. Signed-off-by: Linus Walleij Reviewed-by: Marek Szyprowski Tested-by: Marek Szyprowski Reviewed-by: Charles Keepax Signed-off-by: Mark Brown --- drivers/gpio/gpiolib.h | 6 ------ include/linux/gpio/consumer.h | 13 +++++++++++++ 2 files changed, 13 insertions(+), 6 deletions(-) diff --git a/drivers/gpio/gpiolib.h b/drivers/gpio/gpiolib.h index 087d865286a0c8..bc57f0dc59532f 100644 --- a/drivers/gpio/gpiolib.h +++ b/drivers/gpio/gpiolib.h @@ -201,12 +201,6 @@ int gpiod_set_array_value_complex(bool raw, bool can_sleep, struct gpio_array *array_info, unsigned long *value_bitmap); -/* This is just passed between gpiolib and devres */ -struct gpio_desc *gpiod_get_from_of_node(struct device_node *node, - const char *propname, int index, - enum gpiod_flags dflags, - const char *label); - extern struct spinlock gpio_lock; extern struct list_head gpio_devices; diff --git a/include/linux/gpio/consumer.h b/include/linux/gpio/consumer.h index f2f887795d43e9..348885f2f3d3b9 100644 --- a/include/linux/gpio/consumer.h +++ b/include/linux/gpio/consumer.h @@ -172,6 +172,10 @@ int desc_to_gpio(const struct gpio_desc *desc); struct device_node; struct fwnode_handle; +struct gpio_desc *gpiod_get_from_of_node(struct device_node *node, + const char *propname, int index, + enum gpiod_flags dflags, + const char *label); struct gpio_desc *devm_gpiod_get_from_of_node(struct device *dev, struct device_node *node, const char *propname, int index, @@ -517,6 +521,15 @@ static inline int desc_to_gpio(const struct gpio_desc *desc) struct device_node; struct fwnode_handle; +static inline +struct gpio_desc *gpiod_get_from_of_node(struct device_node *node, + const char *propname, int index, + enum gpiod_flags dflags, + const char *label) +{ + return ERR_PTR(-ENOSYS); +} + static inline struct gpio_desc *devm_gpiod_get_from_of_node(struct device *dev, struct device_node *node, From d03c63dbcaa1ee570d0435a7f79c7083ffb59245 Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Thu, 6 Dec 2018 13:43:43 +0100 Subject: [PATCH 47/85] regulator: max77686: Let core handle GPIO descriptor Use the gpiod_get_from_of_node() rather than the devm_* version so that the regulator core can handle the lifecycle of these descriptors. Fix up the errorpath so that we free this descriptor if an error occurs in the callback. Rely on the regulator core to deal with it after this point: a previous patch fixed up the regulator core to properly dispose any GPIO descriptors once you call regulator_register(). Fixes: 96392c3d8ca4 ("regulator: max77686: Pass descriptor instead of GPIO number") Signed-off-by: Linus Walleij Reviewed-by: Marek Szyprowski Tested-by: Marek Szyprowski Reviewed-by: Charles Keepax Signed-off-by: Mark Brown --- drivers/regulator/max77686-regulator.c | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/drivers/regulator/max77686-regulator.c b/drivers/regulator/max77686-regulator.c index f5cee17759053c..8020eb57374a1b 100644 --- a/drivers/regulator/max77686-regulator.c +++ b/drivers/regulator/max77686-regulator.c @@ -250,13 +250,13 @@ static int max77686_of_parse_cb(struct device_node *np, struct regulator_config *config) { struct max77686_data *max77686 = config->driver_data; + int ret; switch (desc->id) { case MAX77686_BUCK8: case MAX77686_BUCK9: case MAX77686_LDO20 ... MAX77686_LDO22: - config->ena_gpiod = devm_gpiod_get_from_of_node(max77686->dev, - np, + config->ena_gpiod = gpiod_get_from_of_node(np, "maxim,ena", 0, GPIOD_OUT_HIGH | GPIOD_FLAGS_BIT_NONEXCLUSIVE, @@ -271,9 +271,13 @@ static int max77686_of_parse_cb(struct device_node *np, if (config->ena_gpiod) { set_bit(desc->id, max77686->gpio_enabled); - return regmap_update_bits(config->regmap, desc->enable_reg, - desc->enable_mask, - MAX77686_GPIO_CONTROL); + ret = regmap_update_bits(config->regmap, desc->enable_reg, + desc->enable_mask, + MAX77686_GPIO_CONTROL); + if (ret) { + gpiod_put(config->ena_gpiod); + config->ena_gpiod = NULL; + } } return 0; From ec757001c818c175e6b610e8ef80c2a25d1ed1a5 Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Thu, 6 Dec 2018 13:43:44 +0100 Subject: [PATCH 48/85] gpio: Enable nonexclusive gpiods from DT nodes This makes gpiod_get_from_of_node() respect the GPIOD_FLAGS_BIT_NONEXCLUSIVE flag which is especially nice when getting regulator GPIOs right out of device tree nodes. Suggested-by: Marek Szyprowski Fixes: b0ce7b29bfcd ("regulator/gpio: Allow nonexclusive GPIO access") Signed-off-by: Linus Walleij Reviewed-by: Marek Szyprowski Tested-by: Marek Szyprowski Reviewed-by: Charles Keepax Signed-off-by: Mark Brown --- drivers/gpio/gpiolib.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c index 230e41562462b2..a7e3fd512e2d87 100644 --- a/drivers/gpio/gpiolib.c +++ b/drivers/gpio/gpiolib.c @@ -4204,6 +4204,8 @@ struct gpio_desc *gpiod_get_from_of_node(struct device_node *node, transitory = flags & OF_GPIO_TRANSITORY; ret = gpiod_request(desc, label); + if (ret == -EBUSY && (flags & GPIOD_FLAGS_BIT_NONEXCLUSIVE)) + return desc; if (ret) return ERR_PTR(ret); From cb28ee388e465a956b05ada682f9ef90e776a9b7 Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Thu, 6 Dec 2018 13:43:45 +0100 Subject: [PATCH 49/85] gpio: devres: Handle nonexclusive GPIOs When we get a nonexeclusive GPIO descriptor using managed resources, we should only add it to the list of managed resources once: on the first user. Augment the devm_gpiod_get_index() and devm_gpiod_get_from_of_node() calls to account for this by checking if the descriptor is already resource managed before we proceed to allocate a new resource management struct. Fixes: b0ce7b29bfcd ("regulator/gpio: Allow nonexclusive GPIO access") Reported-by: Marek Szyprowski Signed-off-by: Linus Walleij Reviewed-by: Marek Szyprowski Tested-by: Marek Szyprowski Reviewed-by: Charles Keepax Signed-off-by: Mark Brown --- drivers/gpio/gpiolib-devres.c | 50 ++++++++++++++++++++++++++--------- 1 file changed, 38 insertions(+), 12 deletions(-) diff --git a/drivers/gpio/gpiolib-devres.c b/drivers/gpio/gpiolib-devres.c index 01959369360bd7..f9591b5c974856 100644 --- a/drivers/gpio/gpiolib-devres.c +++ b/drivers/gpio/gpiolib-devres.c @@ -98,15 +98,28 @@ struct gpio_desc *__must_check devm_gpiod_get_index(struct device *dev, struct gpio_desc **dr; struct gpio_desc *desc; + desc = gpiod_get_index(dev, con_id, idx, flags); + if (IS_ERR(desc)) + return desc; + + /* + * For non-exclusive GPIO descriptors, check if this descriptor is + * already under resource management by this device. + */ + if (flags & GPIOD_FLAGS_BIT_NONEXCLUSIVE) { + struct devres *dres; + + dres = devres_find(dev, devm_gpiod_release, + devm_gpiod_match, &desc); + if (dres) + return desc; + } + dr = devres_alloc(devm_gpiod_release, sizeof(struct gpio_desc *), GFP_KERNEL); - if (!dr) + if (!dr) { + gpiod_put(desc); return ERR_PTR(-ENOMEM); - - desc = gpiod_get_index(dev, con_id, idx, flags); - if (IS_ERR(desc)) { - devres_free(dr); - return desc; } *dr = desc; @@ -140,15 +153,28 @@ struct gpio_desc *devm_gpiod_get_from_of_node(struct device *dev, struct gpio_desc **dr; struct gpio_desc *desc; + desc = gpiod_get_from_of_node(node, propname, index, dflags, label); + if (IS_ERR(desc)) + return desc; + + /* + * For non-exclusive GPIO descriptors, check if this descriptor is + * already under resource management by this device. + */ + if (dflags & GPIOD_FLAGS_BIT_NONEXCLUSIVE) { + struct devres *dres; + + dres = devres_find(dev, devm_gpiod_release, + devm_gpiod_match, &desc); + if (dres) + return desc; + } + dr = devres_alloc(devm_gpiod_release, sizeof(struct gpio_desc *), GFP_KERNEL); - if (!dr) + if (!dr) { + gpiod_put(desc); return ERR_PTR(-ENOMEM); - - desc = gpiod_get_from_of_node(node, propname, index, dflags, label); - if (IS_ERR(desc)) { - devres_free(dr); - return desc; } *dr = desc; From 891ddbc79a61eb5b919cf56202ecaf7259878cb2 Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Thu, 6 Dec 2018 13:43:46 +0100 Subject: [PATCH 50/85] gpio: Add devm_gpiod_unhinge() This adds a function named devm_gpiod_unhinge() that removes the resource management from a GPIO descriptor. I am not sure if this is the best anglosaxon name for the function, no other managed resources have an equivalent currently, but I chose "unhinge" as the closest intuitive thing I could imagine that fits Rusty Russell's API design criterions "the obvious use is the correct one" and "the name tells you how to use it". The idea came out of a remark from Mark Brown that it should be possible to handle over management of a resource from devres to the regulator core, and indeed we can do that. Signed-off-by: Linus Walleij Reviewed-by: Marek Szyprowski Tested-by: Marek Szyprowski Reviewed-by: Charles Keepax Signed-off-by: Mark Brown --- Documentation/driver-model/devres.txt | 1 + drivers/gpio/gpiolib-devres.c | 30 +++++++++++++++++++++++++++ include/linux/gpio/consumer.h | 10 +++++++++ 3 files changed, 41 insertions(+) diff --git a/Documentation/driver-model/devres.txt b/Documentation/driver-model/devres.txt index 43681ca0837f8d..fc4cc24dfb9767 100644 --- a/Documentation/driver-model/devres.txt +++ b/Documentation/driver-model/devres.txt @@ -254,6 +254,7 @@ GPIO devm_gpiod_get_index_optional() devm_gpiod_get_optional() devm_gpiod_put() + devm_gpiod_unhinge() devm_gpiochip_add_data() devm_gpiochip_remove() devm_gpio_request() diff --git a/drivers/gpio/gpiolib-devres.c b/drivers/gpio/gpiolib-devres.c index f9591b5c974856..0acc2cc6e868fd 100644 --- a/drivers/gpio/gpiolib-devres.c +++ b/drivers/gpio/gpiolib-devres.c @@ -346,6 +346,36 @@ void devm_gpiod_put(struct device *dev, struct gpio_desc *desc) } EXPORT_SYMBOL(devm_gpiod_put); +/** + * devm_gpiod_unhinge - Remove resource management from a gpio descriptor + * @dev: GPIO consumer + * @desc: GPIO descriptor to remove resource management from + * + * Remove resource management from a GPIO descriptor. This is needed when + * you want to hand over lifecycle management of a descriptor to another + * mechanism. + */ + +void devm_gpiod_unhinge(struct device *dev, struct gpio_desc *desc) +{ + int ret; + + if (IS_ERR_OR_NULL(desc)) + return; + ret = devres_destroy(dev, devm_gpiod_release, + devm_gpiod_match, &desc); + /* + * If the GPIO descriptor is requested as nonexclusive, we + * may call this function several times on the same descriptor + * so it is OK if devres_destroy() returns -ENOENT. + */ + if (ret == -ENOENT) + return; + /* Anything else we should warn about */ + WARN_ON(ret); +} +EXPORT_SYMBOL(devm_gpiod_unhinge); + /** * devm_gpiod_put_array - Resource-managed gpiod_put_array() * @dev: GPIO consumer diff --git a/include/linux/gpio/consumer.h b/include/linux/gpio/consumer.h index 348885f2f3d3b9..8aebcf822082ea 100644 --- a/include/linux/gpio/consumer.h +++ b/include/linux/gpio/consumer.h @@ -104,6 +104,7 @@ struct gpio_descs *__must_check devm_gpiod_get_array_optional(struct device *dev, const char *con_id, enum gpiod_flags flags); void devm_gpiod_put(struct device *dev, struct gpio_desc *desc); +void devm_gpiod_unhinge(struct device *dev, struct gpio_desc *desc); void devm_gpiod_put_array(struct device *dev, struct gpio_descs *descs); int gpiod_get_direction(struct gpio_desc *desc); @@ -249,6 +250,15 @@ static inline void gpiod_put(struct gpio_desc *desc) WARN_ON(1); } +static inline void devm_gpiod_unhinge(struct device *dev, + struct gpio_desc *desc) +{ + might_sleep(); + + /* GPIO can never have been requested */ + WARN_ON(1); +} + static inline void gpiod_put_array(struct gpio_descs *descs) { might_sleep(); From 48bd226f0ab3b91a837213cbfa15319f377effad Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Thu, 6 Dec 2018 13:43:47 +0100 Subject: [PATCH 51/85] regulator: max8973: Let core handle GPIO descriptor The probe path of this driver is a bit complex: sometimes the GPIO descriptor is passed to the regulator core, sometimes it is not. To handle it in a simple way: stick with the devm_* resource management and unhinge the GPIO descriptor devres handling right before passing it to the regulator core, if we pass it to the regulator core. Fixes: e7d2be696faa ("regulator: max8973: Pass descriptor instead of GPIO number") Signed-off-by: Linus Walleij Reviewed-by: Marek Szyprowski Reviewed-by: Charles Keepax Signed-off-by: Mark Brown --- drivers/regulator/max8973-regulator.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/drivers/regulator/max8973-regulator.c b/drivers/regulator/max8973-regulator.c index e7a58b50903203..9aee1444181dad 100644 --- a/drivers/regulator/max8973-regulator.c +++ b/drivers/regulator/max8973-regulator.c @@ -808,7 +808,13 @@ static int max8973_probe(struct i2c_client *client, config.of_node = client->dev.of_node; config.regmap = max->regmap; - /* Register the regulators */ + /* + * Register the regulators + * Turn the GPIO descriptor over to the regulator core for + * lifecycle management if we pass an ena_gpiod. + */ + if (config.ena_gpiod) + devm_gpiod_unhinge(&client->dev, config.ena_gpiod); rdev = devm_regulator_register(&client->dev, &max->desc, &config); if (IS_ERR(rdev)) { ret = PTR_ERR(rdev); From b23328d76dba826265f4b0fc625280865eec9782 Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Thu, 6 Dec 2018 13:43:48 +0100 Subject: [PATCH 52/85] regulator: da9211: Hand over GPIO to regulator core The GPIO descriptors used by the DA9211 driver are retrieved during probe() and it is really helpful to have those under devres management because of all the errorpaths in the intialization. Using the new dev_gpiod_unhinge() call we can remove the devres management of the descriptor right before handing it over to the regulators core. Signed-off-by: Linus Walleij Reviewed-by: Marek Szyprowski Reviewed-by: Charles Keepax Signed-off-by: Mark Brown --- drivers/regulator/da9211-regulator.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/drivers/regulator/da9211-regulator.c b/drivers/regulator/da9211-regulator.c index 8f68c7a05d2796..109ee12d43626c 100644 --- a/drivers/regulator/da9211-regulator.c +++ b/drivers/regulator/da9211-regulator.c @@ -389,6 +389,12 @@ static int da9211_regulator_init(struct da9211 *chip) else config.ena_gpiod = NULL; + /* + * Hand the GPIO descriptor management over to the regulator + * core, remove it from GPIO devres management. + */ + if (config.ena_gpiod) + devm_gpiod_unhinge(chip->dev, config.ena_gpiod); chip->rdev[i] = devm_regulator_register(chip->dev, &da9211_regulators[i], &config); if (IS_ERR(chip->rdev[i])) { From 1f5163fcf1523e2890d3e8909daed777661765ab Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Thu, 6 Dec 2018 13:43:49 +0100 Subject: [PATCH 53/85] regulator: s5m8767: Hand over GPIO to regulator core The GPIO descriptors used by the S5M8767 driver are retrieved during probe() and it is really helpful to have those under devres management because of all the errorpaths in the intialization. Using the new dev_gpiod_unhinge() call we can remove the devres management of the descriptor right before handing it over to the regulators core. Signed-off-by: Linus Walleij Reviewed-by: Marek Szyprowski Tested-by: Marek Szyprowski Reviewed-by: Charles Keepax Signed-off-by: Mark Brown --- drivers/regulator/s5m8767.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/drivers/regulator/s5m8767.c b/drivers/regulator/s5m8767.c index 654f65abcb5a0c..b581f01f339576 100644 --- a/drivers/regulator/s5m8767.c +++ b/drivers/regulator/s5m8767.c @@ -956,10 +956,17 @@ static int s5m8767_pmic_probe(struct platform_device *pdev) config.regmap = iodev->regmap_pmic; config.of_node = pdata->regulators[i].reg_node; config.ena_gpiod = NULL; - if (pdata->regulators[i].ext_control_gpiod) + if (pdata->regulators[i].ext_control_gpiod) { + /* Assigns config.ena_gpiod */ s5m8767_regulator_config_ext_control(s5m8767, &pdata->regulators[i], &config); + /* + * Hand the GPIO descriptor management over to the + * regulator core, remove it from devres management. + */ + devm_gpiod_unhinge(s5m8767->dev, config.ena_gpiod); + } rdev = devm_regulator_register(&pdev->dev, ®ulators[id], &config); if (IS_ERR(rdev)) { From 870311e52bbf53838df937a5099ec92a657c1db5 Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Thu, 6 Dec 2018 13:43:50 +0100 Subject: [PATCH 54/85] regulator: tps65090: Hand over GPIO to regulator core The GPIO descriptors used by the TPS65090 driver are retrieved during probe() and it is really helpful to have those under devres management because of all the errorpaths in the intialization. Using the new dev_gpiod_unhinge() call we can remove the devres management of the descriptor right before handing it over to the regulators core. Signed-off-by: Linus Walleij Reviewed-by: Marek Szyprowski Tested-by: Marek Szyprowski Reviewed-by: Charles Keepax Signed-off-by: Mark Brown --- drivers/regulator/tps65090-regulator.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/drivers/regulator/tps65090-regulator.c b/drivers/regulator/tps65090-regulator.c index db714d5edafc88..0614551796a19a 100644 --- a/drivers/regulator/tps65090-regulator.c +++ b/drivers/regulator/tps65090-regulator.c @@ -480,6 +480,12 @@ static int tps65090_regulator_probe(struct platform_device *pdev) else config.of_node = NULL; + /* + * Hand the GPIO descriptor management over to the regulator + * core, remove it from devres management. + */ + if (config.ena_gpiod) + devm_gpiod_unhinge(&pdev->dev, config.ena_gpiod); rdev = devm_regulator_register(&pdev->dev, ri->desc, &config); if (IS_ERR(rdev)) { dev_err(&pdev->dev, "failed to register regulator %s\n", From 2b96edb570709d6ca2e06842941c4bdf04b55f47 Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Thu, 6 Dec 2018 13:43:51 +0100 Subject: [PATCH 55/85] regulator: s2mps11: Hand over GPIO to regulator core The GPIO descriptors used by the S2MPS11 driver are retrieved during probe() and it is really helpful to have those under devres management because of all the errorpaths in the intialization. Using the new dev_gpiod_unhinge() call we can remove the devres management of the descriptor right before handing it over to the regulators core. Signed-off-by: Linus Walleij Reviewed-by: Marek Szyprowski Tested-by: Marek Szyprowski Reviewed-by: Charles Keepax Signed-off-by: Mark Brown --- drivers/regulator/s2mps11.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/drivers/regulator/s2mps11.c b/drivers/regulator/s2mps11.c index 63e66f485cc084..ee4a23ab06639a 100644 --- a/drivers/regulator/s2mps11.c +++ b/drivers/regulator/s2mps11.c @@ -1178,7 +1178,12 @@ static int s2mps11_pmic_probe(struct platform_device *pdev) config.of_node = rdata[i].of_node; } config.ena_gpiod = s2mps11->ext_control_gpiod[i]; - + /* + * Hand the GPIO descriptor management over to the regulator + * core, remove it from devres management. + */ + if (config.ena_gpiod) + devm_gpiod_unhinge(&pdev->dev, config.ena_gpiod); regulator = devm_regulator_register(&pdev->dev, ®ulators[i], &config); if (IS_ERR(regulator)) { From 05f224ca669398b567d09feb6e2ceefcb7d7f945 Mon Sep 17 00:00:00 2001 From: Douglas Anderson Date: Thu, 6 Dec 2018 14:23:18 -0800 Subject: [PATCH 56/85] regulator: core: Clean enabling always-on regulators + their supplies At the end of regulator_resolve_supply() we have historically turned on our supply in some cases. This could be for one of two reasons: 1. If resolving supplies was happening before the call to set_machine_constraints() we needed to predict if set_machine_constraints() was going to turn the regulator on and we needed to preemptively turn the supply on. 2. Maybe set_machine_constraints() happened before we could resolve supplies (because we failed the first time to resolve) and thus we might need to propagate an enable that already happened up to our supply. Historically regulator_resolve_supply() used _regulator_is_enabled() to decide whether to turn on the supply. Let's change things a little bit. Specifically: 1. Let's try to enable the supply and the regulator in the same place, both in set_machine_constraints(). This means that we have exactly the same logic for enabling the supply and the regulator. 2. Let's properly set use_count when we enable always-on or boot-on regulators even for those that don't have supplies. The previous commit 1fc12b05895e ("regulator: core: Avoid propagating to supplies when possible") only did this right for regulators with supplies. 3. Let's make it clear that the only time we need to enable the supply in regulator_resolve_supply() is if the main regulator is currently in use. By using use_count (like the rest of the code) to decide if we're going to enable our supply we keep everything consistent. Overall the new scheme should be cleaner and easier to reason about. In addition to fixing regulator_summary to be more correct (because of the more correct use_count), this change also has the effect of no longer using _regulator_is_enabled() in this code path. _regulator_is_enabled() could return an error code for some regulators at bootup (like RPMh) that can't read their initial state. While one can argue that the design of those regulators is sub-optimal, the new logic sidesteps this brokenness. This fix in particular fixes observed problems on Qualcomm sdm845 boards which use the above-mentioned RPMh regulator. Those problems were made worse by commit 1fc12b05895e ("regulator: core: Avoid propagating to supplies when possible") because now we'd think at bootup that the SD regulators were already enabled and we'd never try them again. Fixes: 1fc12b05895e ("regulator: core: Avoid propagating to supplies when possible") Reported-by: Evan Green Signed-off-by: Douglas Anderson Signed-off-by: Mark Brown --- drivers/regulator/core.c | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/drivers/regulator/core.c b/drivers/regulator/core.c index ae0a559d197831..f211aa7da44e5d 100644 --- a/drivers/regulator/core.c +++ b/drivers/regulator/core.c @@ -1399,11 +1399,21 @@ static int set_machine_constraints(struct regulator_dev *rdev, * and we have control then make sure it is enabled. */ if (rdev->constraints->always_on || rdev->constraints->boot_on) { + if (rdev->supply) { + ret = regulator_enable(rdev->supply); + if (ret < 0) { + _regulator_put(rdev->supply); + rdev->supply = NULL; + return ret; + } + } + ret = _regulator_do_enable(rdev); if (ret < 0 && ret != -EINVAL) { rdev_err(rdev, "failed to enable\n"); return ret; } + rdev->use_count++; } print_constraints(rdev); @@ -1820,15 +1830,18 @@ static int regulator_resolve_supply(struct regulator_dev *rdev) return ret; } - /* Cascade always-on state to supply */ - if (_regulator_is_enabled(rdev)) { + /* + * In set_machine_constraints() we may have turned this regulator on + * but we couldn't propagate to the supply if it hadn't been resolved + * yet. Do it now. + */ + if (rdev->use_count) { ret = regulator_enable(rdev->supply); if (ret < 0) { _regulator_put(rdev->supply); rdev->supply = NULL; return ret; } - rdev->use_count = 1; } return 0; From daa531db8a09dc00084a6030c81f2555a8de70ac Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Tue, 11 Dec 2018 10:53:18 +0100 Subject: [PATCH 57/85] regulator: wm8994: Rely on core to handle GPIO descriptor After making sure that the regulator core always take over handling of the GPIO descriptors, the gpiod_put() on the errorpath of the wm8994 driver becomes redundant. Reported-by: Charles Keepax Signed-off-by: Linus Walleij Acked-by: Charles Keepax Signed-off-by: Mark Brown --- drivers/regulator/wm8994-regulator.c | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/drivers/regulator/wm8994-regulator.c b/drivers/regulator/wm8994-regulator.c index 46e6b4ee1491aa..38928cdcb6e658 100644 --- a/drivers/regulator/wm8994-regulator.c +++ b/drivers/regulator/wm8994-regulator.c @@ -172,6 +172,11 @@ static int wm8994_ldo_probe(struct platform_device *pdev) ldo->init_data = *pdata->ldo[id].init_data; } + /* + * At this point the GPIO descriptor is handled over to the + * regulator core and we need not worry about it on the + * error path. + */ ldo->regulator = devm_regulator_register(&pdev->dev, &wm8994_ldo_desc[id], &config); @@ -179,16 +184,12 @@ static int wm8994_ldo_probe(struct platform_device *pdev) ret = PTR_ERR(ldo->regulator); dev_err(wm8994->dev, "Failed to register LDO%d: %d\n", id + 1, ret); - goto err; + return ret; } platform_set_drvdata(pdev, ldo); return 0; - -err: - gpiod_put(gpiod); - return ret; } static struct platform_driver wm8994_ldo_driver = { From f306d76082358fa9a3fb3e66674a5852bb48845f Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Tue, 11 Dec 2018 10:53:19 +0100 Subject: [PATCH 58/85] regulator: arizona-ldo1: Rely on core to handle GPIO descriptor After making sure that the regulator core always take over handling of the GPIO descriptors, the gpiod_put() on the errorpath of the Arizona LDO1 driver becomes redundant. Reported-by: Charles Keepax Signed-off-by: Linus Walleij Acked-by: Charles Keepax Signed-off-by: Mark Brown --- drivers/regulator/arizona-ldo1.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/drivers/regulator/arizona-ldo1.c b/drivers/regulator/arizona-ldo1.c index 9a72eae4926d5c..b9a93049e41ee7 100644 --- a/drivers/regulator/arizona-ldo1.c +++ b/drivers/regulator/arizona-ldo1.c @@ -283,9 +283,6 @@ static int arizona_ldo1_common_init(struct platform_device *pdev, of_node_put(config.of_node); if (IS_ERR(ldo1->regulator)) { - if (config.ena_gpiod) - gpiod_put(config.ena_gpiod); - ret = PTR_ERR(ldo1->regulator); dev_err(&pdev->dev, "Failed to register LDO1 supply: %d\n", ret); From 90bc8ac220aab82df91322e67587d0418ec3b251 Mon Sep 17 00:00:00 2001 From: Claudiu Beznea Date: Tue, 11 Dec 2018 15:56:36 +0000 Subject: [PATCH 59/85] regulator: act8945a-regulator: unlock expert registers Unlock expert registers for act8945a. This is based on orginal work of Boris Brezillon at [1]. [1] https://www.spinics.net/lists/kernel/msg2942960.html Signed-off-by: Claudiu Beznea Signed-off-by: Mark Brown --- drivers/regulator/act8945a-regulator.c | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/drivers/regulator/act8945a-regulator.c b/drivers/regulator/act8945a-regulator.c index 43fda8b4455acd..8d71d9893d0d70 100644 --- a/drivers/regulator/act8945a-regulator.c +++ b/drivers/regulator/act8945a-regulator.c @@ -15,6 +15,7 @@ #include #include #include +#include #include #include @@ -23,6 +24,7 @@ */ #define ACT8945A_SYS_MODE 0x00 #define ACT8945A_SYS_CTRL 0x01 +#define ACT8945A_SYS_UNLK_REGS 0x0b #define ACT8945A_DCDC1_VSET1 0x20 #define ACT8945A_DCDC1_VSET2 0x21 #define ACT8945A_DCDC1_CTRL 0x22 @@ -123,9 +125,17 @@ static int act8945a_pmic_probe(struct platform_device *pdev) struct regulator_config config = { }; const struct regulator_desc *regulators; struct regulator_dev *rdev; + struct regmap *regmap; int i, num_regulators; bool voltage_select; + regmap = dev_get_regmap(pdev->dev.parent, NULL); + if (!regmap) { + dev_err(&pdev->dev, + "could not retrieve regmap from parent device\n"); + return -EINVAL; + } + voltage_select = of_property_read_bool(pdev->dev.parent->of_node, "active-semi,vsel-high"); @@ -149,7 +159,8 @@ static int act8945a_pmic_probe(struct platform_device *pdev) } } - return 0; + /* Unlock expert registers. */ + return regmap_write(regmap, ACT8945A_SYS_UNLK_REGS, 0xef); } static struct platform_driver act8945a_pmic_driver = { From 7482d6ecc68e16a2b8272b0c9cbcc5c50edf013f Mon Sep 17 00:00:00 2001 From: Boris Brezillon Date: Tue, 11 Dec 2018 15:56:41 +0000 Subject: [PATCH 60/85] regulator: act8945a-regulator: Implement PM functionalities The regulator supports a dedicated suspend mode. Implement the appropriate ->set_suspend_xx() hooks, add support for ->set_mode(), and provide basic PM ops functionalities to setup the regulator in a suspend state when the system is entering suspend. Signed-off-by: Boris Brezillon [claudiu.beznea@microchip.com: remove shutdown function, use dev_pm_ops, fix checkpatch warning, adapt commit message, add LDO modes support, move modes constants to active-semi,8945a-regulator.h, remove rdevs from struct act8945a_pmic, add op_mode to act8945a_pmic] Signed-off-by: Claudiu Beznea Signed-off-by: Mark Brown --- drivers/regulator/act8945a-regulator.c | 186 +++++++++++++++++- .../regulator/active-semi,8945a-regulator.h | 30 +++ 2 files changed, 211 insertions(+), 5 deletions(-) create mode 100644 include/dt-bindings/regulator/active-semi,8945a-regulator.h diff --git a/drivers/regulator/act8945a-regulator.c b/drivers/regulator/act8945a-regulator.c index 8d71d9893d0d70..f37d1a8604079c 100644 --- a/drivers/regulator/act8945a-regulator.c +++ b/drivers/regulator/act8945a-regulator.c @@ -18,6 +18,7 @@ #include #include #include +#include /** * ACT8945A Global Register Map. @@ -28,20 +29,27 @@ #define ACT8945A_DCDC1_VSET1 0x20 #define ACT8945A_DCDC1_VSET2 0x21 #define ACT8945A_DCDC1_CTRL 0x22 +#define ACT8945A_DCDC1_SUS 0x24 #define ACT8945A_DCDC2_VSET1 0x30 #define ACT8945A_DCDC2_VSET2 0x31 #define ACT8945A_DCDC2_CTRL 0x32 +#define ACT8945A_DCDC2_SUS 0x34 #define ACT8945A_DCDC3_VSET1 0x40 #define ACT8945A_DCDC3_VSET2 0x41 #define ACT8945A_DCDC3_CTRL 0x42 +#define ACT8945A_DCDC3_SUS 0x44 #define ACT8945A_LDO1_VSET 0x50 #define ACT8945A_LDO1_CTRL 0x51 +#define ACT8945A_LDO1_SUS 0x52 #define ACT8945A_LDO2_VSET 0x54 #define ACT8945A_LDO2_CTRL 0x55 +#define ACT8945A_LDO2_SUS 0x56 #define ACT8945A_LDO3_VSET 0x60 #define ACT8945A_LDO3_CTRL 0x61 +#define ACT8945A_LDO3_SUS 0x62 #define ACT8945A_LDO4_VSET 0x64 #define ACT8945A_LDO4_CTRL 0x65 +#define ACT8945A_LDO4_SUS 0x66 /** * Field Definitions. @@ -62,7 +70,12 @@ enum { ACT8945A_ID_LDO2, ACT8945A_ID_LDO3, ACT8945A_ID_LDO4, - ACT8945A_REG_NUM, + ACT8945A_ID_MAX, +}; + +struct act8945a_pmic { + struct regmap *regmap; + u32 op_mode[ACT8945A_ID_MAX]; }; static const struct regulator_linear_range act8945a_voltage_ranges[] = { @@ -71,6 +84,143 @@ static const struct regulator_linear_range act8945a_voltage_ranges[] = { REGULATOR_LINEAR_RANGE(2400000, 48, 63, 100000), }; +static int act8945a_set_suspend_state(struct regulator_dev *rdev, bool enable) +{ + struct regmap *regmap = rdev->regmap; + int id = rdev->desc->id, reg, val; + + switch (id) { + case ACT8945A_ID_DCDC1: + reg = ACT8945A_DCDC1_SUS; + val = 0xa8; + break; + case ACT8945A_ID_DCDC2: + reg = ACT8945A_DCDC2_SUS; + val = 0xa8; + break; + case ACT8945A_ID_DCDC3: + reg = ACT8945A_DCDC3_SUS; + val = 0xa8; + break; + case ACT8945A_ID_LDO1: + reg = ACT8945A_LDO1_SUS; + val = 0xe8; + break; + case ACT8945A_ID_LDO2: + reg = ACT8945A_LDO2_SUS; + val = 0xe8; + break; + case ACT8945A_ID_LDO3: + reg = ACT8945A_LDO3_SUS; + val = 0xe8; + break; + case ACT8945A_ID_LDO4: + reg = ACT8945A_LDO4_SUS; + val = 0xe8; + break; + default: + return -EINVAL; + } + + if (enable) + val |= BIT(4); + + /* + * Ask the PMIC to enable/disable this output when entering hibernate + * mode. + */ + return regmap_write(regmap, reg, val); +} + +static int act8945a_set_suspend_enable(struct regulator_dev *rdev) +{ + return act8945a_set_suspend_state(rdev, true); +} + +static int act8945a_set_suspend_disable(struct regulator_dev *rdev) +{ + return act8945a_set_suspend_state(rdev, false); +} + +static unsigned int act8945a_of_map_mode(unsigned int mode) +{ + switch (mode) { + case ACT8945A_REGULATOR_MODE_FIXED: + case ACT8945A_REGULATOR_MODE_NORMAL: + return REGULATOR_MODE_NORMAL; + case ACT8945A_REGULATOR_MODE_LOWPOWER: + return REGULATOR_MODE_STANDBY; + default: + return REGULATOR_MODE_INVALID; + } +} + +static int act8945a_set_mode(struct regulator_dev *rdev, unsigned int mode) +{ + struct act8945a_pmic *act8945a = rdev_get_drvdata(rdev); + struct regmap *regmap = rdev->regmap; + int id = rdev->desc->id; + int reg, ret, val = 0; + + switch (id) { + case ACT8945A_ID_DCDC1: + reg = ACT8945A_DCDC1_CTRL; + break; + case ACT8945A_ID_DCDC2: + reg = ACT8945A_DCDC2_CTRL; + break; + case ACT8945A_ID_DCDC3: + reg = ACT8945A_DCDC3_CTRL; + break; + case ACT8945A_ID_LDO1: + reg = ACT8945A_LDO1_SUS; + break; + case ACT8945A_ID_LDO2: + reg = ACT8945A_LDO2_SUS; + break; + case ACT8945A_ID_LDO3: + reg = ACT8945A_LDO3_SUS; + break; + case ACT8945A_ID_LDO4: + reg = ACT8945A_LDO4_SUS; + break; + default: + return -EINVAL; + } + + switch (mode) { + case REGULATOR_MODE_STANDBY: + if (rdev->desc->id > ACT8945A_ID_DCDC3) + val = BIT(5); + break; + case REGULATOR_MODE_NORMAL: + if (rdev->desc->id <= ACT8945A_ID_DCDC3) + val = BIT(5); + break; + default: + return -EINVAL; + } + + ret = regmap_update_bits(regmap, reg, BIT(5), val); + if (ret) + return ret; + + act8945a->op_mode[id] = mode; + + return 0; +} + +static unsigned int act8945a_get_mode(struct regulator_dev *rdev) +{ + struct act8945a_pmic *act8945a = rdev_get_drvdata(rdev); + int id = rdev->desc->id; + + if (id < ACT8945A_ID_DCDC1 || id >= ACT8945A_ID_MAX) + return -EINVAL; + + return act8945a->op_mode[id]; +} + static const struct regulator_ops act8945a_ops = { .list_voltage = regulator_list_voltage_linear_range, .map_voltage = regulator_map_voltage_linear_range, @@ -78,7 +228,11 @@ static const struct regulator_ops act8945a_ops = { .set_voltage_sel = regulator_set_voltage_sel_regmap, .enable = regulator_enable_regmap, .disable = regulator_disable_regmap, + .set_mode = act8945a_set_mode, + .get_mode = act8945a_get_mode, .is_enabled = regulator_is_enabled_regmap, + .set_suspend_enable = act8945a_set_suspend_enable, + .set_suspend_disable = act8945a_set_suspend_disable, }; #define ACT89xx_REG(_name, _family, _id, _vsel_reg, _supply) \ @@ -86,6 +240,7 @@ static const struct regulator_ops act8945a_ops = { .name = _name, \ .supply_name = _supply, \ .of_match = of_match_ptr("REG_"#_id), \ + .of_map_mode = act8945a_of_map_mode, \ .regulators_node = of_match_ptr("regulators"), \ .id = _family##_ID_##_id, \ .type = REGULATOR_VOLTAGE, \ @@ -124,13 +279,17 @@ static int act8945a_pmic_probe(struct platform_device *pdev) { struct regulator_config config = { }; const struct regulator_desc *regulators; + struct act8945a_pmic *act8945a; struct regulator_dev *rdev; - struct regmap *regmap; int i, num_regulators; bool voltage_select; - regmap = dev_get_regmap(pdev->dev.parent, NULL); - if (!regmap) { + act8945a = devm_kzalloc(&pdev->dev, sizeof(*act8945a), GFP_KERNEL); + if (!act8945a) + return -ENOMEM; + + act8945a->regmap = dev_get_regmap(pdev->dev.parent, NULL); + if (!act8945a->regmap) { dev_err(&pdev->dev, "could not retrieve regmap from parent device\n"); return -EINVAL; @@ -149,6 +308,7 @@ static int act8945a_pmic_probe(struct platform_device *pdev) config.dev = &pdev->dev; config.dev->of_node = pdev->dev.parent->of_node; + config.driver_data = act8945a; for (i = 0; i < num_regulators; i++) { rdev = devm_regulator_register(&pdev->dev, ®ulators[i], &config); if (IS_ERR(rdev)) { @@ -159,13 +319,29 @@ static int act8945a_pmic_probe(struct platform_device *pdev) } } + platform_set_drvdata(pdev, act8945a); + /* Unlock expert registers. */ - return regmap_write(regmap, ACT8945A_SYS_UNLK_REGS, 0xef); + return regmap_write(act8945a->regmap, ACT8945A_SYS_UNLK_REGS, 0xef); } +static int act8945a_suspend(struct device *pdev) +{ + struct act8945a_pmic *act8945a = dev_get_drvdata(pdev); + + /* + * Ask the PMIC to enter the suspend mode on the next PWRHLD + * transition. + */ + return regmap_write(act8945a->regmap, ACT8945A_SYS_CTRL, 0x42); +} + +SIMPLE_DEV_PM_OPS(act8945a_pm, act8945a_suspend, NULL); + static struct platform_driver act8945a_pmic_driver = { .driver = { .name = "act8945a-regulator", + .pm = &act8945a_pm, }, .probe = act8945a_pmic_probe, }; diff --git a/include/dt-bindings/regulator/active-semi,8945a-regulator.h b/include/dt-bindings/regulator/active-semi,8945a-regulator.h new file mode 100644 index 00000000000000..9bdba5e3141aaa --- /dev/null +++ b/include/dt-bindings/regulator/active-semi,8945a-regulator.h @@ -0,0 +1,30 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (c) 2018 Microchip Technology, Inc. All rights reserved. + * + * Device Tree binding constants for the ACT8945A PMIC regulators + */ + +#ifndef _DT_BINDINGS_REGULATOR_ACT8945A_H +#define _DT_BINDINGS_REGULATOR_ACT8945A_H + +/* + * These constants should be used to specify regulator modes in device tree for + * ACT8945A regulators as follows: + * ACT8945A_REGULATOR_MODE_FIXED: It is specific to DCDC regulators and it + * specifies the usage of fixed-frequency + * PWM. + * + * ACT8945A_REGULATOR_MODE_NORMAL: It is specific to LDO regulators and it + * specifies the usage of normal mode. + * + * ACT8945A_REGULATOR_MODE_LOWPOWER: For DCDC and LDO regulators; it specify + * the usage of proprietary power-saving + * mode. + */ + +#define ACT8945A_REGULATOR_MODE_FIXED 1 +#define ACT8945A_REGULATOR_MODE_NORMAL 2 +#define ACT8945A_REGULATOR_MODE_LOWPOWER 3 + +#endif From 03ffbf9c0891caf8723c81967ceea5faf5d099d5 Mon Sep 17 00:00:00 2001 From: Claudiu Beznea Date: Tue, 11 Dec 2018 15:56:45 +0000 Subject: [PATCH 61/85] regulator: act8945a-regulator: fix line over 80 chars warning Fix line over 80 chars checkpatch.pl warning. Signed-off-by: Claudiu Beznea Signed-off-by: Mark Brown --- drivers/regulator/act8945a-regulator.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/regulator/act8945a-regulator.c b/drivers/regulator/act8945a-regulator.c index f37d1a8604079c..f4d60743efb31f 100644 --- a/drivers/regulator/act8945a-regulator.c +++ b/drivers/regulator/act8945a-regulator.c @@ -310,7 +310,8 @@ static int act8945a_pmic_probe(struct platform_device *pdev) config.dev->of_node = pdev->dev.parent->of_node; config.driver_data = act8945a; for (i = 0; i < num_regulators; i++) { - rdev = devm_regulator_register(&pdev->dev, ®ulators[i], &config); + rdev = devm_regulator_register(&pdev->dev, ®ulators[i], + &config); if (IS_ERR(rdev)) { dev_err(&pdev->dev, "failed to register %s regulator\n", From b5ebba46e694a6e8e4322a80bb2a5a448f8f8b04 Mon Sep 17 00:00:00 2001 From: Claudiu Beznea Date: Tue, 11 Dec 2018 15:56:50 +0000 Subject: [PATCH 62/85] regulator: act8945a-regulator: add shutdown function Implement shutdown method to make sure the PMIC will not enter the suspend state when the system is shutdown. This work is based on work done by Borris Brezillon on [1]. [1] https://www.spinics.net/lists/kernel/msg2942960.html Signed-off-by: Claudiu Beznea Signed-off-by: Mark Brown --- drivers/regulator/act8945a-regulator.c | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/drivers/regulator/act8945a-regulator.c b/drivers/regulator/act8945a-regulator.c index f4d60743efb31f..90572b6370c696 100644 --- a/drivers/regulator/act8945a-regulator.c +++ b/drivers/regulator/act8945a-regulator.c @@ -339,12 +339,23 @@ static int act8945a_suspend(struct device *pdev) SIMPLE_DEV_PM_OPS(act8945a_pm, act8945a_suspend, NULL); +static void act8945a_pmic_shutdown(struct platform_device *pdev) +{ + struct act8945a_pmic *act8945a = platform_get_drvdata(pdev); + + /* + * Ask the PMIC to shutdown everything on the next PWRHLD transition. + */ + regmap_write(act8945a->regmap, ACT8945A_SYS_CTRL, 0x0); +} + static struct platform_driver act8945a_pmic_driver = { .driver = { .name = "act8945a-regulator", .pm = &act8945a_pm, }, .probe = act8945a_pmic_probe, + .shutdown = act8945a_pmic_shutdown, }; module_platform_driver(act8945a_pmic_driver); From 8a582fa9429108c247ff56dfbc4e3ead08a76956 Mon Sep 17 00:00:00 2001 From: Claudiu Beznea Date: Tue, 11 Dec 2018 15:56:59 +0000 Subject: [PATCH 63/85] regulator: add documentation for regulator modes and suspend states Add documentation for regulator modes and suspend states. Signed-off-by: Claudiu Beznea Signed-off-by: Mark Brown --- .../bindings/regulator/act8945a-regulator.txt | 34 +++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/Documentation/devicetree/bindings/regulator/act8945a-regulator.txt b/Documentation/devicetree/bindings/regulator/act8945a-regulator.txt index ac955dea00d16c..4017527619ab2d 100644 --- a/Documentation/devicetree/bindings/regulator/act8945a-regulator.txt +++ b/Documentation/devicetree/bindings/regulator/act8945a-regulator.txt @@ -15,11 +15,17 @@ Optional input supply properties: - inl67-supply: The input supply for REG_LDO3 and REG_LDO4 Any standard regulator properties can be used to configure the single regulator. +regulator-initial-mode, regulator-allowed-modes and regulator-mode could be +specified using mode values from dt-bindings/regulator/active-semi,8945a-regulator.h +file. The valid names for regulators are: REG_DCDC1, REG_DCDC2, REG_DCDC3, REG_LDO1, REG_LDO2, REG_LDO3, REG_LDO4. Example: + +#include + pmic@5b { compatible = "active-semi,act8945a"; reg = <0x5b>; @@ -32,6 +38,18 @@ Example: regulator-min-microvolt = <1350000>; regulator-max-microvolt = <1350000>; regulator-always-on; + + regulator-allowed-modes = , + ; + regulator-initial-mode = ; + + regulator-state-mem { + regulator-on-in-suspend; + regulator-suspend-min-microvolt=<1400000>; + regulator-suspend-max-microvolt=<1400000>; + regulator-changeable-in-suspend; + regulator-mode=; + }; }; vdd_1v2_reg: REG_DCDC2 { @@ -39,6 +57,14 @@ Example: regulator-min-microvolt = <1100000>; regulator-max-microvolt = <1300000>; regulator-always-on; + + regulator-allowed-modes = , + ; + regulator-initial-mode = ; + + regulator-state-mem { + regulator-off-in-suspend; + }; }; vdd_3v3_reg: REG_DCDC3 { @@ -53,6 +79,14 @@ Example: regulator-min-microvolt = <2500000>; regulator-max-microvolt = <2500000>; regulator-always-on; + + regulator-allowed-modes = , + ; + regulator-initial-mode = ; + + regulator-state-mem { + regulator-off-in-suspend; + }; }; vdd_3v3_lp_reg: REG_LDO2 { From 96f3fb86d70fe64ba88a7f567bafe9c504b87d25 Mon Sep 17 00:00:00 2001 From: "Andrei.Stefanescu@microchip.com" Date: Wed, 12 Dec 2018 18:31:08 +0000 Subject: [PATCH 64/85] regulator: dt-bindings: add MCP16502 regulator bindings This patch describes the compatible and the device tree bindings necessary for the MCP16502 PMIC. Signed-off-by: Andrei Stefanescu Reviewed-by: Rob Herring Signed-off-by: Mark Brown --- .../bindings/regulator/mcp16502-regulator.txt | 143 ++++++++++++++++++ 1 file changed, 143 insertions(+) create mode 100644 Documentation/devicetree/bindings/regulator/mcp16502-regulator.txt diff --git a/Documentation/devicetree/bindings/regulator/mcp16502-regulator.txt b/Documentation/devicetree/bindings/regulator/mcp16502-regulator.txt new file mode 100644 index 00000000000000..b8f843fa609263 --- /dev/null +++ b/Documentation/devicetree/bindings/regulator/mcp16502-regulator.txt @@ -0,0 +1,143 @@ +MCP16502 PMIC + +Required properties: +- compatible: "microchip,mcp16502" +- reg: I2C slave address +- lpm-gpios: GPIO for LPM pin. Note that this GPIO *must* remain high during + suspend-to-ram, keeping the PMIC into HIBERNATE mode. +- regulators: A node that houses a sub-node for each regulator within + the device. Each sub-node is identified using the node's + name. The content of each sub-node is defined by the + standard binding for regulators; see regulator.txt. + +Regualtors of MCP16502 PMIC: +1) VDD_IO - Buck (1.2 - 3.7 V) +2) VDD_DDR - Buck (0.6 - 1.85 V) +3) VDD_CORE - Buck (0.6 - 1.85 V) +4) VDD_OTHER - BUCK (0.6 - 1.85 V) +5) LDO1 - LDO (1.2 - 3.7 V) +6) LDO2 - LDO (1.2 - 3.7 V) + +Regulator modes: +2 - FPWM: higher precision, higher consumption +4 - AutoPFM: lower precision, lower consumption + +Each regulator is defined using the standard binding for regulators. + +Example: + +mcp16502@5b { + compatible = "microchip,mcp16502"; + reg = <0x5b>; + status = "okay"; + lpm-gpios = <&pioBU 7 GPIO_ACTIVE_HIGH>; + + regulators { + VDD_IO { + regulator-name = "VDD_IO"; + regulator-min-microvolt = <1200000>; + regulator-max-microvolt = <3700000>; + regulator-initial-mode = <2>; + regulator-allowed-modes = <2>, <4>; + regulator-always-on; + + regulator-state-standby { + regulator-on-in-suspend; + regulator-mode = <4>; + }; + + regulator-state-mem { + regulator-off-in-suspend; + regulator-mode = <4>; + }; + }; + + VDD_DDR { + regulator-name = "VDD_DDR"; + regulator-min-microvolt = <600000>; + regulator-max-microvolt = <1850000>; + regulator-initial-mode = <2>; + regulator-allowed-modes = <2>, <4>; + regulator-always-on; + + regulator-state-standby { + regulator-on-in-suspend; + regulator-mode = <4>; + }; + + regulator-state-mem { + regulator-on-in-suspend; + regulator-mode = <4>; + }; + }; + + VDD_CORE { + regulator-name = "VDD_CORE"; + regulator-min-microvolt = <600000>; + regulator-max-microvolt = <1850000>; + regulator-initial-mode = <2>; + regulator-allowed-modes = <2>, <4>; + regulator-always-on; + + regulator-state-standby { + regulator-on-in-suspend; + regulator-mode = <4>; + }; + + regulator-state-mem { + regulator-off-in-suspend; + regulator-mode = <4>; + }; + }; + + VDD_OTHER { + regulator-name = "VDD_OTHER"; + regulator-min-microvolt = <600000>; + regulator-max-microvolt = <1850000>; + regulator-initial-mode = <2>; + regulator-allowed-modes = <2>, <4>; + regulator-always-on; + + regulator-state-standby { + regulator-on-in-suspend; + regulator-mode = <4>; + }; + + regulator-state-mem { + regulator-off-in-suspend; + regulator-mode = <4>; + }; + }; + + LDO1 { + regulator-name = "LDO1"; + regulator-min-microvolt = <1200000>; + regulator-max-microvolt = <3700000>; + regulator-always-on; + + regulator-state-standby { + regulator-on-in-suspend; + }; + + regulator-state-mem { + regulator-off-in-suspend; + }; + }; + + LDO2 { + regulator-name = "LDO2"; + regulator-min-microvolt = <1200000>; + regulator-max-microvolt = <3700000>; + regulator-always-on; + + regulator-state-standby { + regulator-on-in-suspend; + }; + + regulator-state-mem { + regulator-off-in-suspend; + }; + }; + + }; +}; From fef9f28a2ea4eff1b203e3ad6629778be20b4714 Mon Sep 17 00:00:00 2001 From: "Andrei.Stefanescu@microchip.com" Date: Wed, 12 Dec 2018 18:31:11 +0000 Subject: [PATCH 65/85] MAINTAINERS: add maintainer for MCP16502 PMIC driver This patch adds a maintainer for the MCP16502 PMIC driver. Signed-off-by: Andrei Stefanescu Signed-off-by: Mark Brown --- MAINTAINERS | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/MAINTAINERS b/MAINTAINERS index 8119141a926f3a..d1ec97634e16e8 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -9813,6 +9813,13 @@ M: Ludovic Desroches S: Maintained F: drivers/mmc/host/atmel-mci.c +MICROCHIP MCP16502 PMIC DRIVER +M: Andrei Stefanescu +L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers) +S: Maintained +F: Documentation/devicetree/bindings/regulator/mcp16502-regulator.txt +F: drivers/regulator/mcp16502.c + MICROCHIP MCP3911 ADC DRIVER M: Marcus Folkesson M: Kent Gustavsson From 919261c03e7cae9854f25812d4b529fbfbdb4c14 Mon Sep 17 00:00:00 2001 From: "Andrei.Stefanescu@microchip.com" Date: Wed, 12 Dec 2018 18:31:18 +0000 Subject: [PATCH 66/85] regulator: mcp16502: add regulator driver for MCP16502 This patch adds a regulator driver for the MCP16502 PMIC. This drivers supports basic operations through the regulator interface such as: - setting/reading voltage - setting/reading operating mode - reading current status Signed-off-by: Andrei Stefanescu Signed-off-by: Mark Brown --- drivers/regulator/Kconfig | 9 + drivers/regulator/Makefile | 1 + drivers/regulator/mcp16502.c | 520 +++++++++++++++++++++++++++++++++++ 3 files changed, 530 insertions(+) create mode 100644 drivers/regulator/mcp16502.c diff --git a/drivers/regulator/Kconfig b/drivers/regulator/Kconfig index 926cee0d0b5f98..719d9d660e5662 100644 --- a/drivers/regulator/Kconfig +++ b/drivers/regulator/Kconfig @@ -567,6 +567,15 @@ config REGULATOR_MC13892 Say y here to support the regulators found on the Freescale MC13892 PMIC. +config REGULATOR_MCP16502 + tristate "Microchip MCP16502 PMIC" + depends on I2C && OF + help + Say y here to support the MCP16502 PMIC. This driver supports + basic operations (get/set voltage, get/set operating mode) + through the regulator interface. In addition it enables + suspend-to-ram/standby transition. + config REGULATOR_MT6311 tristate "MediaTek MT6311 PMIC" depends on I2C diff --git a/drivers/regulator/Makefile b/drivers/regulator/Makefile index 72488ef11b8ad4..b12e1c9b211875 100644 --- a/drivers/regulator/Makefile +++ b/drivers/regulator/Makefile @@ -74,6 +74,7 @@ obj-$(CONFIG_REGULATOR_MAX77802) += max77802-regulator.o obj-$(CONFIG_REGULATOR_MC13783) += mc13783-regulator.o obj-$(CONFIG_REGULATOR_MC13892) += mc13892-regulator.o obj-$(CONFIG_REGULATOR_MC13XXX_CORE) += mc13xxx-regulator-core.o +obj-$(CONFIG_REGULATOR_MCP16502) += mcp16502.o obj-$(CONFIG_REGULATOR_MT6311) += mt6311-regulator.o obj-$(CONFIG_REGULATOR_MT6323) += mt6323-regulator.o obj-$(CONFIG_REGULATOR_MT6380) += mt6380-regulator.o diff --git a/drivers/regulator/mcp16502.c b/drivers/regulator/mcp16502.c new file mode 100644 index 00000000000000..7cbc96f7afcf98 --- /dev/null +++ b/drivers/regulator/mcp16502.c @@ -0,0 +1,520 @@ +// SPDX-License-Identifier: GPL-2.0 +// +// MCP16502 PMIC driver +// +// Copyright (C) 2018 Microchip Technology Inc. and its subsidiaries +// +// Author: Andrei Stefanescu +// +// Inspired from tps65086-regulator.c + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define VDD_LOW_SEL 0x0D +#define VDD_HIGH_SEL 0x3F + +#define MCP16502_FLT BIT(7) +#define MCP16502_ENS BIT(0) + +/* + * The PMIC has four sets of registers corresponding to four power modes: + * Performance, Active, Low-power, Hibernate. + * + * Registers: + * Each regulator has a register for each power mode. To access a register + * for a specific regulator and mode BASE_* and OFFSET_* need to be added. + * + * Operating modes: + * In order for the PMIC to transition to operating modes it has to be + * controlled via GPIO lines called LPM and HPM. + * + * The registers are fully configurable such that you can put all regulators in + * a low-power state while the PMIC is in Active mode. They are supposed to be + * configured at startup and then simply transition to/from a global low-power + * state by setting the GPIO lpm pin high/low. + * + * This driver keeps the PMIC in Active mode, Low-power state is set for the + * regulators by enabling/disabling operating mode (FPWM or Auto PFM). + * + * The PMIC's Low-power and Hibernate modes are used during standby/suspend. + * To enter standby/suspend the PMIC will go to Low-power mode. From there, it + * will transition to Hibernate when the PWRHLD line is set to low by the MPU. + */ + +/* + * This function is useful for iterating over all regulators and accessing their + * registers in a generic way or accessing a regulator device by its id. + */ +#define MCP16502_BASE(i) (((i) + 1) << 4) +#define MCP16502_STAT_BASE(i) ((i) + 5) + +#define MCP16502_OFFSET_MODE_A 0 +#define MCP16502_OFFSET_MODE_LPM 1 +#define MCP16502_OFFSET_MODE_HIB 2 + +#define MCP16502_OPMODE_ACTIVE REGULATOR_MODE_NORMAL +#define MCP16502_OPMODE_LPM REGULATOR_MODE_IDLE +#define MCP16502_OPMODE_HIB REGULATOR_MODE_STANDBY + +#define MCP16502_MODE_AUTO_PFM 0 +#define MCP16502_MODE_FPWM BIT(6) + +#define MCP16502_VSEL 0x3F +#define MCP16502_EN BIT(7) +#define MCP16502_MODE BIT(6) + +#define MCP16502_MIN_REG 0x0 +#define MCP16502_MAX_REG 0x65 + +static unsigned int mcp16502_of_map_mode(unsigned int mode) +{ + if (mode == REGULATOR_MODE_NORMAL || mode == REGULATOR_MODE_IDLE) + return mode; + + return REGULATOR_MODE_INVALID; +} + +#define MCP16502_REGULATOR(_name, _id, _ranges, _ops) \ + [_id] = { \ + .name = _name, \ + .regulators_node = of_match_ptr("regulators"), \ + .id = _id, \ + .ops = &(_ops), \ + .type = REGULATOR_VOLTAGE, \ + .owner = THIS_MODULE, \ + .linear_ranges = _ranges, \ + .n_linear_ranges = ARRAY_SIZE(_ranges), \ + .of_match = of_match_ptr(_name), \ + .of_map_mode = mcp16502_of_map_mode, \ + .vsel_reg = (((_id) + 1) << 4), \ + .vsel_mask = MCP16502_VSEL, \ + .enable_reg = (((_id) + 1) << 4), \ + .enable_mask = MCP16502_EN, \ + } + +enum { + BUCK1 = 0, + BUCK2, + BUCK3, + BUCK4, + LDO1, + LDO2, + NUM_REGULATORS +}; + +/* + * struct mcp16502 - PMIC representation + * @rdev: the regulators belonging to this chip + * @rmap: regmap to be used for I2C communication + * @lpm: LPM GPIO descriptor + */ +struct mcp16502 { + struct regulator_dev *rdev[NUM_REGULATORS]; + struct regmap *rmap; + struct gpio_desc *lpm; +}; + +/* + * mcp16502_gpio_set_mode() - set the GPIO corresponding value + * + * Used to prepare transitioning into hibernate or resuming from it. + */ +static void mcp16502_gpio_set_mode(struct mcp16502 *mcp, int mode) +{ + switch (mode) { + case MCP16502_OPMODE_ACTIVE: + gpiod_set_value(mcp->lpm, 0); + break; + case MCP16502_OPMODE_LPM: + case MCP16502_OPMODE_HIB: + gpiod_set_value(mcp->lpm, 1); + break; + default: + pr_err("%s: %d invalid\n", __func__, mode); + } +} + +/* + * mcp16502_get_reg() - get the PMIC's configuration register for opmode + * + * @rdev: the regulator whose register we are searching + * @opmode: the PMIC's operating mode ACTIVE, Low-power, Hibernate + */ +static int mcp16502_get_reg(struct regulator_dev *rdev, int opmode) +{ + int reg = MCP16502_BASE(rdev_get_id(rdev)); + + switch (opmode) { + case MCP16502_OPMODE_ACTIVE: + return reg + MCP16502_OFFSET_MODE_A; + case MCP16502_OPMODE_LPM: + return reg + MCP16502_OFFSET_MODE_LPM; + case MCP16502_OPMODE_HIB: + return reg + MCP16502_OFFSET_MODE_HIB; + default: + return -EINVAL; + } +} + +/* + * mcp16502_get_mode() - return the current operating mode of a regulator + * + * Note: all functions that are not part of entering/exiting standby/suspend + * use the Active mode registers. + * + * Note: this is different from the PMIC's operatig mode, it is the + * MODE bit from the regulator's register. + */ +static unsigned int mcp16502_get_mode(struct regulator_dev *rdev) +{ + unsigned int val; + int ret, reg; + struct mcp16502 *mcp = rdev_get_drvdata(rdev); + + reg = mcp16502_get_reg(rdev, MCP16502_OPMODE_ACTIVE); + if (reg < 0) + return reg; + + ret = regmap_read(mcp->rmap, reg, &val); + if (ret) + return ret; + + switch (val & MCP16502_MODE) { + case MCP16502_MODE_FPWM: + return REGULATOR_MODE_NORMAL; + case MCP16502_MODE_AUTO_PFM: + return REGULATOR_MODE_IDLE; + default: + return REGULATOR_MODE_INVALID; + } +} + +/* + * _mcp16502_set_mode() - helper for set_mode and set_suspend_mode + * + * @rdev: the regulator for which we are setting the mode + * @mode: the regulator's mode (the one from MODE bit) + * @opmode: the PMIC's operating mode: Active/Low-power/Hibernate + */ +static int _mcp16502_set_mode(struct regulator_dev *rdev, unsigned int mode, + unsigned int op_mode) +{ + int val; + int reg; + struct mcp16502 *mcp = rdev_get_drvdata(rdev); + + reg = mcp16502_get_reg(rdev, op_mode); + if (reg < 0) + return reg; + + switch (mode) { + case REGULATOR_MODE_NORMAL: + val = MCP16502_MODE_FPWM; + break; + case REGULATOR_MODE_IDLE: + val = MCP16502_MODE_AUTO_PFM; + break; + default: + return -EINVAL; + } + + reg = regmap_update_bits(mcp->rmap, reg, MCP16502_MODE, val); + return reg; +} + +/* + * mcp16502_set_mode() - regulator_ops set_mode + */ +static int mcp16502_set_mode(struct regulator_dev *rdev, unsigned int mode) +{ + return _mcp16502_set_mode(rdev, mode, MCP16502_OPMODE_ACTIVE); +} + +/* + * mcp16502_get_status() - regulator_ops get_status + */ +static int mcp16502_get_status(struct regulator_dev *rdev) +{ + int ret; + unsigned int val; + struct mcp16502 *mcp = rdev_get_drvdata(rdev); + + ret = regmap_read(mcp->rmap, MCP16502_STAT_BASE(rdev_get_id(rdev)), + &val); + if (ret) + return ret; + + if (val & MCP16502_FLT) + return REGULATOR_STATUS_ERROR; + else if (val & MCP16502_ENS) + return REGULATOR_STATUS_ON; + else if (!(val & MCP16502_ENS)) + return REGULATOR_STATUS_OFF; + + return REGULATOR_STATUS_UNDEFINED; +} + +#ifdef CONFIG_SUSPEND +/* + * mcp16502_suspend_get_target_reg() - get the reg of the target suspend PMIC + * mode + */ +static int mcp16502_suspend_get_target_reg(struct regulator_dev *rdev) +{ + switch (pm_suspend_target_state) { + case PM_SUSPEND_STANDBY: + return mcp16502_get_reg(rdev, MCP16502_OPMODE_LPM); + case PM_SUSPEND_ON: + case PM_SUSPEND_MEM: + return mcp16502_get_reg(rdev, MCP16502_OPMODE_HIB); + default: + dev_err(&rdev->dev, "invalid suspend target: %d\n", + pm_suspend_target_state); + } + + return -EINVAL; +} + +/* + * mcp16502_set_suspend_voltage() - regulator_ops set_suspend_voltage + */ +static int mcp16502_set_suspend_voltage(struct regulator_dev *rdev, int uV) +{ + struct mcp16502 *mcp = rdev_get_drvdata(rdev); + int sel = regulator_map_voltage_linear_range(rdev, uV, uV); + int reg = mcp16502_suspend_get_target_reg(rdev); + + if (sel < 0) + return sel; + + if (reg < 0) + return reg; + + return regmap_update_bits(mcp->rmap, reg, MCP16502_VSEL, sel); +} + +/* + * mcp16502_set_suspend_mode() - regulator_ops set_suspend_mode + */ +static int mcp16502_set_suspend_mode(struct regulator_dev *rdev, + unsigned int mode) +{ + switch (pm_suspend_target_state) { + case PM_SUSPEND_STANDBY: + return _mcp16502_set_mode(rdev, mode, MCP16502_OPMODE_LPM); + case PM_SUSPEND_ON: + case PM_SUSPEND_MEM: + return _mcp16502_set_mode(rdev, mode, MCP16502_OPMODE_HIB); + default: + dev_err(&rdev->dev, "invalid suspend target: %d\n", + pm_suspend_target_state); + } + + return -EINVAL; +} + +/* + * mcp16502_set_suspend_enable() - regulator_ops set_suspend_enable + */ +static int mcp16502_set_suspend_enable(struct regulator_dev *rdev) +{ + struct mcp16502 *mcp = rdev_get_drvdata(rdev); + int reg = mcp16502_suspend_get_target_reg(rdev); + + if (reg < 0) + return reg; + + return regmap_update_bits(mcp->rmap, reg, MCP16502_EN, MCP16502_EN); +} + +/* + * mcp16502_set_suspend_disable() - regulator_ops set_suspend_disable + */ +static int mcp16502_set_suspend_disable(struct regulator_dev *rdev) +{ + struct mcp16502 *mcp = rdev_get_drvdata(rdev); + int reg = mcp16502_suspend_get_target_reg(rdev); + + if (reg < 0) + return reg; + + return regmap_update_bits(mcp->rmap, reg, MCP16502_EN, 0); +} +#endif /* CONFIG_SUSPEND */ + +static const struct regulator_ops mcp16502_buck_ops = { + .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, + .enable = regulator_enable_regmap, + .disable = regulator_disable_regmap, + .is_enabled = regulator_is_enabled_regmap, + .get_status = mcp16502_get_status, + + .set_mode = mcp16502_set_mode, + .get_mode = mcp16502_get_mode, + +#ifdef CONFIG_SUSPEND + .set_suspend_voltage = mcp16502_set_suspend_voltage, + .set_suspend_mode = mcp16502_set_suspend_mode, + .set_suspend_enable = mcp16502_set_suspend_enable, + .set_suspend_disable = mcp16502_set_suspend_disable, +#endif /* CONFIG_SUSPEND */ +}; + +/* + * LDOs cannot change operating modes. + */ +static const struct regulator_ops mcp16502_ldo_ops = { + .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, + .enable = regulator_enable_regmap, + .disable = regulator_disable_regmap, + .is_enabled = regulator_is_enabled_regmap, + .get_status = mcp16502_get_status, + +#ifdef CONFIG_SUSPEND + .set_suspend_voltage = mcp16502_set_suspend_voltage, + .set_suspend_enable = mcp16502_set_suspend_enable, + .set_suspend_disable = mcp16502_set_suspend_disable, +#endif /* CONFIG_SUSPEND */ +}; + +static const struct of_device_id mcp16502_ids[] = { + { .compatible = "microchip,mcp16502", }, + {} +}; +MODULE_DEVICE_TABLE(of, mcp16502_ids); + +static const struct regulator_linear_range b1l12_ranges[] = { + REGULATOR_LINEAR_RANGE(1200000, VDD_LOW_SEL, VDD_HIGH_SEL, 50000), +}; + +static const struct regulator_linear_range b234_ranges[] = { + REGULATOR_LINEAR_RANGE(600000, VDD_LOW_SEL, VDD_HIGH_SEL, 25000), +}; + +static const struct regulator_desc mcp16502_desc[] = { + /* MCP16502_REGULATOR(_name, _id, ranges, regulator_ops) */ + MCP16502_REGULATOR("VDD_IO", BUCK1, b1l12_ranges, mcp16502_buck_ops), + MCP16502_REGULATOR("VDD_DDR", BUCK2, b234_ranges, mcp16502_buck_ops), + MCP16502_REGULATOR("VDD_CORE", BUCK3, b234_ranges, mcp16502_buck_ops), + MCP16502_REGULATOR("VDD_OTHER", BUCK4, b234_ranges, mcp16502_buck_ops), + MCP16502_REGULATOR("LDO1", LDO1, b1l12_ranges, mcp16502_ldo_ops), + MCP16502_REGULATOR("LDO2", LDO2, b1l12_ranges, mcp16502_ldo_ops) +}; + +static const struct regmap_range mcp16502_ranges[] = { + regmap_reg_range(MCP16502_MIN_REG, MCP16502_MAX_REG) +}; + +static const struct regmap_access_table mcp16502_yes_reg_table = { + .yes_ranges = mcp16502_ranges, + .n_yes_ranges = ARRAY_SIZE(mcp16502_ranges), +}; + +static const struct regmap_config mcp16502_regmap_config = { + .reg_bits = 8, + .val_bits = 8, + .max_register = MCP16502_MAX_REG, + .cache_type = REGCACHE_NONE, + .rd_table = &mcp16502_yes_reg_table, + .wr_table = &mcp16502_yes_reg_table, +}; + +/* + * set_up_regulators() - initialize all regulators + */ +static int setup_regulators(struct mcp16502 *mcp, struct device *dev, + struct regulator_config config) +{ + int i; + + for (i = 0; i < NUM_REGULATORS; i++) { + mcp->rdev[i] = devm_regulator_register(dev, + &mcp16502_desc[i], + &config); + if (IS_ERR(mcp->rdev[i])) { + dev_err(dev, + "failed to register %s regulator %ld\n", + mcp16502_desc[i].name, PTR_ERR(mcp->rdev[i])); + return PTR_ERR(mcp->rdev[i]); + } + } + + return 0; +} + +static int mcp16502_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct regulator_config config = { }; + struct device *dev; + struct mcp16502 *mcp; + int ret = 0; + + dev = &client->dev; + config.dev = dev; + + mcp = devm_kzalloc(dev, sizeof(*mcp), GFP_KERNEL); + if (!mcp) + return -ENOMEM; + + mcp->rmap = devm_regmap_init_i2c(client, &mcp16502_regmap_config); + if (IS_ERR(mcp->rmap)) { + ret = PTR_ERR(mcp->rmap); + dev_err(dev, "regmap init failed: %d\n", ret); + return ret; + } + + i2c_set_clientdata(client, mcp); + config.regmap = mcp->rmap; + config.driver_data = mcp; + + mcp->lpm = devm_gpiod_get(dev, "lpm", GPIOD_OUT_LOW); + if (IS_ERR(mcp->lpm)) { + dev_err(dev, "failed to get lpm pin: %ld\n", PTR_ERR(mcp->lpm)); + return PTR_ERR(mcp->lpm); + } + + ret = setup_regulators(mcp, dev, config); + if (ret != 0) + return ret; + + mcp16502_gpio_set_mode(mcp, MCP16502_OPMODE_ACTIVE); + + return 0; +} + +static const struct i2c_device_id mcp16502_i2c_id[] = { + { "mcp16502", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, mcp16502_i2c_id); + +static struct i2c_driver mcp16502_drv = { + .probe = mcp16502_probe, + .driver = { + .name = "mcp16502-regulator", + .of_match_table = of_match_ptr(mcp16502_ids), + }, + .id_table = mcp16502_i2c_id, +}; + +module_i2c_driver(mcp16502_drv); + +MODULE_VERSION("1.0"); +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("MCP16502 PMIC driver"); +MODULE_AUTHOR("Andrei Stefanescu andrei.stefanescu@microchip.com"); From 16aa70e95947e0870ec9e5bf7c7db33fcbacb957 Mon Sep 17 00:00:00 2001 From: Olliver Schinagl Date: Tue, 11 Dec 2018 17:17:05 +0200 Subject: [PATCH 67/85] mfd: axp20x: name voltage ramping define properly The current axp20x names the ramping register 'scal' which probably means scaling. Since the register really has nothing to do with scaling, but really is the voltage ramp we rename it appropriately. Signed-off-by: Olliver Schinagl Signed-off-by: Priit Laes Acked-by: Lee Jones Signed-off-by: Mark Brown --- include/linux/mfd/axp20x.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/include/linux/mfd/axp20x.h b/include/linux/mfd/axp20x.h index 517e60eecbcbea..1293695245dfe7 100644 --- a/include/linux/mfd/axp20x.h +++ b/include/linux/mfd/axp20x.h @@ -35,7 +35,7 @@ enum axp20x_variants { #define AXP152_ALDO_OP_MODE 0x13 #define AXP152_LDO0_CTRL 0x15 #define AXP152_DCDC2_V_OUT 0x23 -#define AXP152_DCDC2_V_SCAL 0x25 +#define AXP152_DCDC2_V_RAMP 0x25 #define AXP152_DCDC1_V_OUT 0x26 #define AXP152_DCDC3_V_OUT 0x27 #define AXP152_ALDO12_V_OUT 0x28 @@ -53,7 +53,7 @@ enum axp20x_variants { #define AXP20X_USB_OTG_STATUS 0x02 #define AXP20X_PWR_OUT_CTRL 0x12 #define AXP20X_DCDC2_V_OUT 0x23 -#define AXP20X_DCDC2_LDO3_V_SCAL 0x25 +#define AXP20X_DCDC2_LDO3_V_RAMP 0x25 #define AXP20X_DCDC3_V_OUT 0x27 #define AXP20X_LDO24_V_OUT 0x28 #define AXP20X_LDO3_V_OUT 0x29 From d29f54df8b167be6174881f265b12453a558e48a Mon Sep 17 00:00:00 2001 From: Olliver Schinagl Date: Tue, 11 Dec 2018 17:17:06 +0200 Subject: [PATCH 68/85] regulator: axp20x: add support for set_ramp_delay for AXP209 The AXP209 supports ramping up voltages on several regulators such as DCDC2 and LDO3. This patch adds preliminary support for the regulator-ramp-delay property for these 2 regulators. Note that the voltage ramp only works when regulator is already enabled. E.g. when going from say 0.7 V to 3.6 V. When turning on the regulator, no voltage ramp is performed in hardware. What this means, is that if the bootloader brings up the voltage at 0.7 V, the ramp delay property is properly applied. If however, the bootloader leaves the power off, no ramp delay is applied when the power is enabled by the regulator framework. Signed-off-by: Olliver Schinagl Signed-off-by: Priit Laes Signed-off-by: Mark Brown --- drivers/regulator/axp20x-regulator.c | 85 ++++++++++++++++++++++++++++ 1 file changed, 85 insertions(+) diff --git a/drivers/regulator/axp20x-regulator.c b/drivers/regulator/axp20x-regulator.c index 9a2db28effb528..1d9fa6238aec10 100644 --- a/drivers/regulator/axp20x-regulator.c +++ b/drivers/regulator/axp20x-regulator.c @@ -51,6 +51,17 @@ #define AXP20X_PWR_OUT_DCDC2_MASK BIT_MASK(4) #define AXP20X_PWR_OUT_LDO3_MASK BIT_MASK(6) +#define AXP20X_DCDC2_LDO3_V_RAMP_DCDC2_RATE_MASK BIT_MASK(0) +#define AXP20X_DCDC2_LDO3_V_RAMP_DCDC2_RATE(x) \ + ((x) << 0) +#define AXP20X_DCDC2_LDO3_V_RAMP_LDO3_RATE_MASK BIT_MASK(1) +#define AXP20X_DCDC2_LDO3_V_RAMP_LDO3_RATE(x) \ + ((x) << 1) +#define AXP20X_DCDC2_LDO3_V_RAMP_DCDC2_EN_MASK BIT_MASK(2) +#define AXP20X_DCDC2_LDO3_V_RAMP_DCDC2_EN BIT(2) +#define AXP20X_DCDC2_LDO3_V_RAMP_LDO3_EN_MASK BIT_MASK(3) +#define AXP20X_DCDC2_LDO3_V_RAMP_LDO3_EN BIT(3) + #define AXP20X_LDO4_V_OUT_1250mV_START 0x0 #define AXP20X_LDO4_V_OUT_1250mV_STEPS 0 #define AXP20X_LDO4_V_OUT_1250mV_END \ @@ -346,6 +357,79 @@ .ops = &axp20x_ops_range, \ } +static const int axp209_dcdc2_ldo3_slew_rates[] = { + 1600, + 800, +}; + +static int axp20x_set_ramp_delay(struct regulator_dev *rdev, int ramp) +{ + struct axp20x_dev *axp20x = rdev_get_drvdata(rdev); + const struct regulator_desc *desc = rdev->desc; + u8 reg, mask, enable, cfg = 0xff; + const int *slew_rates; + int rate_count = 0; + + if (!rdev) + return -EINVAL; + + switch (axp20x->variant) { + case AXP209_ID: + if (desc->id == AXP20X_DCDC2) { + rate_count = ARRAY_SIZE(axp209_dcdc2_ldo3_slew_rates); + reg = AXP20X_DCDC2_LDO3_V_RAMP; + mask = AXP20X_DCDC2_LDO3_V_RAMP_DCDC2_RATE_MASK | + AXP20X_DCDC2_LDO3_V_RAMP_DCDC2_EN_MASK; + enable = (ramp > 0) ? + AXP20X_DCDC2_LDO3_V_RAMP_DCDC2_EN : + !AXP20X_DCDC2_LDO3_V_RAMP_DCDC2_EN; + break; + } + + if (desc->id == AXP20X_LDO3) { + slew_rates = axp209_dcdc2_ldo3_slew_rates; + rate_count = ARRAY_SIZE(axp209_dcdc2_ldo3_slew_rates); + reg = AXP20X_DCDC2_LDO3_V_RAMP; + mask = AXP20X_DCDC2_LDO3_V_RAMP_LDO3_RATE_MASK | + AXP20X_DCDC2_LDO3_V_RAMP_LDO3_EN_MASK; + enable = (ramp > 0) ? + AXP20X_DCDC2_LDO3_V_RAMP_LDO3_EN : + !AXP20X_DCDC2_LDO3_V_RAMP_LDO3_EN; + break; + } + + if (rate_count > 0) + break; + + /* fall through */ + default: + /* Not supported for this regulator */ + return -ENOTSUPP; + } + + if (ramp == 0) { + cfg = enable; + } else { + int i; + + for (i = 0; i < rate_count; i++) { + if (ramp <= slew_rates[i]) + cfg = AXP20X_DCDC2_LDO3_V_RAMP_LDO3_RATE(i); + else + break; + } + + if (cfg == 0xff) { + dev_err(axp20x->dev, "unsupported ramp value %d", ramp); + return -EINVAL; + } + + cfg |= enable; + } + + return regmap_update_bits(axp20x->regmap, reg, mask, cfg); +} + static const struct regulator_ops axp20x_ops_fixed = { .list_voltage = regulator_list_voltage_linear, }; @@ -366,6 +450,7 @@ static const struct regulator_ops axp20x_ops = { .enable = regulator_enable_regmap, .disable = regulator_disable_regmap, .is_enabled = regulator_is_enabled_regmap, + .set_ramp_delay = axp20x_set_ramp_delay, }; static const struct regulator_ops axp20x_ops_sw = { From fdb0436c35b86759da6d7fce27323e3cdced0e13 Mon Sep 17 00:00:00 2001 From: Olliver Schinagl Date: Tue, 11 Dec 2018 17:17:07 +0200 Subject: [PATCH 69/85] dt-bindings: mfd: axp20x: add support for regulator-ramp-delay for AXP209 The AXP209 supports ramping up voltages on several regulators such as DCDC2 and LDO3, therefore we can use the standard 'regulator-ramp-delay' property for those 2 regulators. Note that the voltage ramp only works when the regulator is already enabled. E.g. when going from say 0.7 V to 3.6 V. When turning on the regulator, no voltage ramp is performed in hardware. What this means, is that if the bootloader brings up the voltage at 0.7 V, the ramp delay property is properly applied. If however, the bootloader leaves the power off, no ramp delay is applied when the power is enabled by the regulator framework. Signed-off-by: Olliver Schinagl Signed-off-by: Priit Laes Acked-for-MFD-by: Lee Jones Signed-off-by: Mark Brown --- Documentation/devicetree/bindings/mfd/axp20x.txt | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/Documentation/devicetree/bindings/mfd/axp20x.txt b/Documentation/devicetree/bindings/mfd/axp20x.txt index 188f0373d441bc..c6266052d23f16 100644 --- a/Documentation/devicetree/bindings/mfd/axp20x.txt +++ b/Documentation/devicetree/bindings/mfd/axp20x.txt @@ -32,6 +32,12 @@ Required properties: - interrupt-controller: The PMIC has its own internal IRQs - #interrupt-cells: Should be set to 1 +Supported common regulator properties, see ../regulator/regulator.txt for +more information: +- regulator-ramp-delay: sets the ramp up delay in uV/us + AXP20x/DCDC2: 1600, 800 + AXP20x/LDO3: 1600, 800 + Optional properties: - x-powers,dcdc-freq: defines the work frequency of DC-DC in KHz AXP152/20X: range: 750-1875, Default: 1.5 MHz From 77e3e3b165db94dc48c0de30498f51fa38f19d4d Mon Sep 17 00:00:00 2001 From: Olliver Schinagl Date: Tue, 11 Dec 2018 17:17:08 +0200 Subject: [PATCH 70/85] regulator: axp20x: add software based soft_start for AXP209 LDO3 In the past, there have been words on various lists that if LDO3 is disabled in u-boot, but enabled in the DTS, the axp209 driver would fail to continue/hang. Several enable/disable patches have been issues to devicetree's in both the kernel and u-boot to address this issue. What really happened however, was that the AXP209 shuts down without a notice and without setting an interrupt. This is caused when LDO3 gets overloaded, for example with large capacitors on the LDO3 output. Normally, we would expect that AXP209 would source 200 mA as per datasheet and set and trigger an interrupt when being overloaded. For some reason however, this does not happen. As a work-around, we use the soft-start constraint of the regulator node to first bring up the LDO3 to the lowest possible voltage and then enable the LDO. After that, we can set the requested voltage as usual. Combining this setting with the regulator-ramp-delay allows LDO3 to enable voltage slowly and staggered, potentially reducing overall inrush current. Signed-off-by: Olliver Schinagl Signed-off-by: Priit Laes Signed-off-by: Mark Brown --- drivers/regulator/axp20x-regulator.c | 57 +++++++++++++++++++++++++++- 1 file changed, 56 insertions(+), 1 deletion(-) diff --git a/drivers/regulator/axp20x-regulator.c b/drivers/regulator/axp20x-regulator.c index 1d9fa6238aec10..e8a895b81c903e 100644 --- a/drivers/regulator/axp20x-regulator.c +++ b/drivers/regulator/axp20x-regulator.c @@ -14,6 +14,7 @@ */ #include +#include #include #include #include @@ -23,6 +24,7 @@ #include #include #include +#include #include #define AXP20X_GPIO0_FUNC_MASK GENMASK(3, 0) @@ -430,6 +432,59 @@ static int axp20x_set_ramp_delay(struct regulator_dev *rdev, int ramp) return regmap_update_bits(axp20x->regmap, reg, mask, cfg); } +static int axp20x_regulator_enable_regmap(struct regulator_dev *rdev) +{ + struct axp20x_dev *axp20x = rdev_get_drvdata(rdev); + const struct regulator_desc *desc = rdev->desc; + + if (!rdev) + return -EINVAL; + + switch (axp20x->variant) { + case AXP209_ID: + if ((desc->id == AXP20X_LDO3) && + rdev->constraints && rdev->constraints->soft_start) { + int v_out; + int ret; + + /* + * On some boards, the LDO3 can be overloaded when + * turning on, causing the entire PMIC to shutdown + * without warning. Turning it on at the minimal voltage + * and then setting the voltage to the requested value + * works reliably. + */ + if (regulator_is_enabled_regmap(rdev)) + break; + + v_out = regulator_get_voltage_sel_regmap(rdev); + if (v_out < 0) + return v_out; + + if (v_out == 0) + break; + + ret = regulator_set_voltage_sel_regmap(rdev, 0x00); + /* + * A small pause is needed between + * setting the voltage and enabling the LDO to give the + * internal state machine time to process the request. + */ + usleep_range(1000, 5000); + ret |= regulator_enable_regmap(rdev); + ret |= regulator_set_voltage_sel_regmap(rdev, v_out); + + return ret; + } + break; + default: + /* No quirks */ + break; + } + + return regulator_enable_regmap(rdev); +}; + static const struct regulator_ops axp20x_ops_fixed = { .list_voltage = regulator_list_voltage_linear, }; @@ -447,7 +502,7 @@ static const struct regulator_ops axp20x_ops = { .set_voltage_sel = regulator_set_voltage_sel_regmap, .get_voltage_sel = regulator_get_voltage_sel_regmap, .list_voltage = regulator_list_voltage_linear, - .enable = regulator_enable_regmap, + .enable = axp20x_regulator_enable_regmap, .disable = regulator_disable_regmap, .is_enabled = regulator_is_enabled_regmap, .set_ramp_delay = axp20x_set_ramp_delay, From eb51c6eb9775d5e7ef810327dbc84bb0d15f72b5 Mon Sep 17 00:00:00 2001 From: Olliver Schinagl Date: Tue, 11 Dec 2018 17:17:09 +0200 Subject: [PATCH 71/85] dt-bindings: mfd: axp20x: Add software based soft_start for AXP209 LDO3 In the past, there have been words on various lists that if LDO3 is disabled in u-boot, but enabled in the DTS, the axp209 driver would fail to continue/hang. Several enable/disable patches have been issues to devicetree's in both the kernel and u-boot to address this issue. What really happened however, was that the AXP209 shuts down without a notice and without setting an interrupt. This is caused when LDO3 gets overloaded, for example with large capacitors on the LDO3 output. Normally, we would expect that AXP209 would source 200 mA as per datasheet and set and trigger an interrupt when being overloaded. For some reason however, this does not happen. As a work-around, implement software-based 'regulator-soft-start' property for AXP209 LDO3 regulator, which is used to first bring up the LDO3 to the lowest possible voltage and then enable the LDO. After that, we can set the requested voltage as usual. Combining this setting with the regulator-ramp-delay allows LDO3 to come up slowly and staggered, potentially reducing overall inrush current. Signed-off-by: Olliver Schinagl Signed-off-by: Priit Laes Signed-off-by: Mark Brown --- Documentation/devicetree/bindings/mfd/axp20x.txt | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Documentation/devicetree/bindings/mfd/axp20x.txt b/Documentation/devicetree/bindings/mfd/axp20x.txt index c6266052d23f16..2af4ff95d6bc7f 100644 --- a/Documentation/devicetree/bindings/mfd/axp20x.txt +++ b/Documentation/devicetree/bindings/mfd/axp20x.txt @@ -37,6 +37,9 @@ more information: - regulator-ramp-delay: sets the ramp up delay in uV/us AXP20x/DCDC2: 1600, 800 AXP20x/LDO3: 1600, 800 +- regulator-soft-start: enable the output at the lowest possible voltage and + only then set the desired voltage + AXP20x/LDO3: software-based implementation Optional properties: - x-powers,dcdc-freq: defines the work frequency of DC-DC in KHz From c1132b006785b14d01ccf40922493278984555d5 Mon Sep 17 00:00:00 2001 From: Olliver Schinagl Date: Tue, 11 Dec 2018 17:17:10 +0200 Subject: [PATCH 72/85] regulator: dts: enable soft-start and ramp delay for the OLinuXino Lime2 The OLinuXino Lime2 has a big capacitor on its LDO3 output. It is actually too large, causing the PMIC to shutdown when toggling the LDO3. By enabling soft-start and ramp delay we increase the time for the capacitor to charge lowering the current drain on the power regulator. Signed-off-by: Olliver Schinagl Signed-off-by: Priit Laes Signed-off-by: Mark Brown --- arch/arm/boot/dts/sun7i-a20-olinuxino-lime2.dts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/arch/arm/boot/dts/sun7i-a20-olinuxino-lime2.dts b/arch/arm/boot/dts/sun7i-a20-olinuxino-lime2.dts index b828677f331d5d..ffafe9720b3590 100644 --- a/arch/arm/boot/dts/sun7i-a20-olinuxino-lime2.dts +++ b/arch/arm/boot/dts/sun7i-a20-olinuxino-lime2.dts @@ -245,6 +245,8 @@ regulator-min-microvolt = <2800000>; regulator-max-microvolt = <2800000>; regulator-name = "vddio-csi0"; + regulator-soft-start; + regulator-ramp-delay = <1600>; }; ®_ldo4 { From dcea4d5c0a349034686ef9f8ba8a9910e0a1c14b Mon Sep 17 00:00:00 2001 From: Olliver Schinagl Date: Tue, 11 Dec 2018 17:17:11 +0200 Subject: [PATCH 73/85] mfd: axp20x: Clean up included headers Add the bitops.h header as we need it, alphabetize header order. Signed-off-by: Olliver Schinagl Signed-off-by: Priit Laes Acked-by: Lee Jones Signed-off-by: Mark Brown --- drivers/mfd/axp20x.c | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/drivers/mfd/axp20x.c b/drivers/mfd/axp20x.c index 0be511dd93d012..da54399dc7b37e 100644 --- a/drivers/mfd/axp20x.c +++ b/drivers/mfd/axp20x.c @@ -16,18 +16,19 @@ * published by the Free Software Foundation. */ -#include +#include +#include #include +#include #include #include +#include +#include #include +#include #include #include #include -#include -#include -#include -#include #define AXP20X_OFF 0x80 From 82b4d99725aedc3de731faa4e23f256fbe2821cf Mon Sep 17 00:00:00 2001 From: Olliver Schinagl Date: Tue, 11 Dec 2018 17:17:12 +0200 Subject: [PATCH 74/85] mfd: axp20x: use explicit bit defines The AXP20X_OFF define is an actual specific bit, define it as such. Signed-off-by: Olliver Schinagl Signed-off-by: Priit Laes Acked-by: Lee Jones Signed-off-by: Mark Brown --- drivers/mfd/axp20x.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/mfd/axp20x.c b/drivers/mfd/axp20x.c index da54399dc7b37e..e1450a56fc070c 100644 --- a/drivers/mfd/axp20x.c +++ b/drivers/mfd/axp20x.c @@ -30,7 +30,7 @@ #include #include -#define AXP20X_OFF 0x80 +#define AXP20X_OFF BIT(7) #define AXP806_REG_ADDR_EXT_ADDR_MASTER_MODE 0 #define AXP806_REG_ADDR_EXT_ADDR_SLAVE_MODE BIT(4) From fc401cb98a736fa010aeaf863618a904ca70ba2c Mon Sep 17 00:00:00 2001 From: "Andrei.Stefanescu@microchip.com" Date: Fri, 14 Dec 2018 08:09:31 +0000 Subject: [PATCH 75/85] regulator: mcp16502: add support for suspend This patch adds support for entering/resuming suspend states. It does this by setting the LPM pin of the PMIC. When suspending the PMIC will enter the Low-power mode when the LPM pin will be set to high. If the suspend target state is suspend-to-mem, the PMIC will transition to Hibernate mode, otherwise, if it is standby, it will remain in Low-power mode. Signed-off-by: Andrei Stefanescu Signed-off-by: Mark Brown --- drivers/regulator/mcp16502.c | 34 ++++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/drivers/regulator/mcp16502.c b/drivers/regulator/mcp16502.c index 7cbc96f7afcf98..25d7b433fafd82 100644 --- a/drivers/regulator/mcp16502.c +++ b/drivers/regulator/mcp16502.c @@ -497,6 +497,37 @@ static int mcp16502_probe(struct i2c_client *client, return 0; } +#ifdef CONFIG_SUSPEND +static int mcp16502_suspend_noirq(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct mcp16502 *mcp = i2c_get_clientdata(client); + + mcp16502_gpio_set_mode(mcp, MCP16502_OPMODE_LPM); + + return 0; +} + +static int mcp16502_resume_noirq(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct mcp16502 *mcp = i2c_get_clientdata(client); + + mcp16502_gpio_set_mode(mcp, MCP16502_OPMODE_ACTIVE); + + return 0; +} +#else /* !CONFIG_SUSPEND */ +#define mcp16502_suspend NULL +#define mcp16502_resume NULL +#endif /* !CONFIG_SUSPEND */ + +#ifdef CONFIG_PM +static const struct dev_pm_ops mcp16502_pm_ops = { + SET_NOIRQ_SYSTEM_SLEEP_PM_OPS(mcp16502_suspend_noirq, + mcp16502_resume_noirq) +}; +#endif static const struct i2c_device_id mcp16502_i2c_id[] = { { "mcp16502", 0 }, { } @@ -508,6 +539,9 @@ static struct i2c_driver mcp16502_drv = { .driver = { .name = "mcp16502-regulator", .of_match_table = of_match_ptr(mcp16502_ids), +#ifdef CONFIG_PM + .pm = &mcp16502_pm_ops, +#endif }, .id_table = mcp16502_i2c_id, }; From 918446c96595636be81a224bf663b0f4e669aad7 Mon Sep 17 00:00:00 2001 From: Priit Laes Date: Fri, 14 Dec 2018 22:54:07 +0200 Subject: [PATCH 76/85] regulator: axp20x: fix set_ramp_delay for AXP209/dcdc2 Initial commit of set_ramp_delay feature was missing an assignment which should have populated slew_rate table for dcdc2 regulator. Add it. Fixes: d29f54df8b16 ("regulator: axp20x: add support for set_ramp_delay for AXP209") Signed-off-by: Priit Laes Signed-off-by: Mark Brown --- drivers/regulator/axp20x-regulator.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/regulator/axp20x-regulator.c b/drivers/regulator/axp20x-regulator.c index e8a895b81c903e..48af859fd05344 100644 --- a/drivers/regulator/axp20x-regulator.c +++ b/drivers/regulator/axp20x-regulator.c @@ -378,6 +378,7 @@ static int axp20x_set_ramp_delay(struct regulator_dev *rdev, int ramp) switch (axp20x->variant) { case AXP209_ID: if (desc->id == AXP20X_DCDC2) { + slew_rates = axp209_dcdc2_ldo3_slew_rates; rate_count = ARRAY_SIZE(axp209_dcdc2_ldo3_slew_rates); reg = AXP20X_DCDC2_LDO3_V_RAMP; mask = AXP20X_DCDC2_LDO3_V_RAMP_DCDC2_RATE_MASK | From 22a5a4a44c43e2993e96183039aef71566e1a514 Mon Sep 17 00:00:00 2001 From: Claudiu Beznea Date: Mon, 17 Dec 2018 10:37:51 +0000 Subject: [PATCH 77/85] regulator: act8945a-regulator: fix 'defined but not used' compiler warning Fix 'defined but not used' compiler warning for act8945a_suspend() function in case CONFIG_PM_SLEEP is not defined. Fixes: b5ebba46e694 ("regulator: act8945a-regulator: add shutdown function") Signed-off-by: Claudiu Beznea Reported-by: Andrei Stefanescu Signed-off-by: Mark Brown --- drivers/regulator/act8945a-regulator.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/regulator/act8945a-regulator.c b/drivers/regulator/act8945a-regulator.c index 90572b6370c696..1572725361222c 100644 --- a/drivers/regulator/act8945a-regulator.c +++ b/drivers/regulator/act8945a-regulator.c @@ -326,7 +326,7 @@ static int act8945a_pmic_probe(struct platform_device *pdev) return regmap_write(act8945a->regmap, ACT8945A_SYS_UNLK_REGS, 0xef); } -static int act8945a_suspend(struct device *pdev) +static int __maybe_unused act8945a_suspend(struct device *pdev) { struct act8945a_pmic *act8945a = dev_get_drvdata(pdev); From 966e927bf8cc6a44f8b72582a1d6d3ffc73b12ad Mon Sep 17 00:00:00 2001 From: Kangjie Lu Date: Tue, 18 Dec 2018 23:04:13 -0600 Subject: [PATCH 78/85] drivers/regulator: fix a missing check of return value If palmas_smps_read() fails, we should not use the read data in "reg" which may contain random value. The fix inserts a check for the return value of palmas_smps_read(): If it fails, we return the error code upstream and stop using "reg". Signed-off-by: Kangjie Lu Signed-off-by: Mark Brown --- drivers/regulator/palmas-regulator.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/drivers/regulator/palmas-regulator.c b/drivers/regulator/palmas-regulator.c index bb5ab7d78895b8..c2cc392a27d407 100644 --- a/drivers/regulator/palmas-regulator.c +++ b/drivers/regulator/palmas-regulator.c @@ -443,13 +443,16 @@ static int palmas_ldo_write(struct palmas *palmas, unsigned int reg, static int palmas_set_mode_smps(struct regulator_dev *dev, unsigned int mode) { int id = rdev_get_id(dev); + int ret; struct palmas_pmic *pmic = rdev_get_drvdata(dev); struct palmas_pmic_driver_data *ddata = pmic->palmas->pmic_ddata; struct palmas_regs_info *rinfo = &ddata->palmas_regs_info[id]; unsigned int reg; bool rail_enable = true; - palmas_smps_read(pmic->palmas, rinfo->ctrl_addr, ®); + ret = palmas_smps_read(pmic->palmas, rinfo->ctrl_addr, ®); + if (ret) + return ret; reg &= ~PALMAS_SMPS12_CTRL_MODE_ACTIVE_MASK; From 78e6fbf321a9b4cb46b17d83f25abcd318859c70 Mon Sep 17 00:00:00 2001 From: Wei Yongjun Date: Tue, 18 Dec 2018 07:10:21 +0000 Subject: [PATCH 79/85] regulator: act8945a-regulator: make symbol act8945a_pm static Fixes the following sparse warning: drivers/regulator/act8945a-regulator.c:340:1: warning: symbol 'act8945a_pm' was not declared. Should it be static? Fixes: 7482d6ecc68e ("regulator: act8945a-regulator: Implement PM functionalities") Signed-off-by: Wei Yongjun Signed-off-by: Mark Brown --- drivers/regulator/act8945a-regulator.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/regulator/act8945a-regulator.c b/drivers/regulator/act8945a-regulator.c index 1572725361222c..603db77723b66d 100644 --- a/drivers/regulator/act8945a-regulator.c +++ b/drivers/regulator/act8945a-regulator.c @@ -337,7 +337,7 @@ static int __maybe_unused act8945a_suspend(struct device *pdev) return regmap_write(act8945a->regmap, ACT8945A_SYS_CTRL, 0x42); } -SIMPLE_DEV_PM_OPS(act8945a_pm, act8945a_suspend, NULL); +static SIMPLE_DEV_PM_OPS(act8945a_pm, act8945a_suspend, NULL); static void act8945a_pmic_shutdown(struct platform_device *pdev) { From 308144ce8e51bb6f9bed0ff0e72e7864f5f405f3 Mon Sep 17 00:00:00 2001 From: "Andrei.Stefanescu@microchip.com" Date: Mon, 17 Dec 2018 11:44:59 +0000 Subject: [PATCH 80/85] regulator: mcp16502: code cleanup This patch does the following: - align parameter with parenthesis - fix compile error If CONFIG_SUSPEND is not set the dummy pm_ops callbacks are named mcp16502_suspend and mcp16502_resume instead of mcp16502_suspend_noirq and mcp16502_resume_noirq. Excerpt from compile log (kbuild test robot): In file included from include/linux/device.h:23:0, from include/linux/gpio/driver.h:5, from include/asm-generic/gpio.h:13, from include/linux/gpio.h:62, from drivers/regulator/mcp16502.c:11: >> drivers/regulator/mcp16502.c:527:32: error: 'mcp16502_suspend_noirq' undeclared here (not in a function); did you mean 'mcp16502_suspend'? SET_NOIRQ_SYSTEM_SLEEP_PM_OPS(mcp16502_suspend_noirq, >> drivers/regulator/mcp16502.c:528:10: error: 'mcp16502_resume_noirq' undeclared here (not in a function); did you mean 'mcp16502_suspend_noirq'? mcp16502_resume_noirq) vim +527 drivers/regulator/mcp16502.c 524 525 #ifdef CONFIG_PM 526 static const struct dev_pm_ops mcp16502_pm_ops = { > 527 SET_NOIRQ_SYSTEM_SLEEP_PM_OPS(mcp16502_suspend_noirq, > 528 mcp16502_resume_noirq) 529 }; 530 #endif 531 static const struct i2c_device_id mcp16502_i2c_id[] = { 532 { "mcp16502", 0 }, 533 { } 534 }; 535 MODULE_DEVICE_TABLE(i2c, mcp16502_i2c_id); 536 Signed-off-by: Andrei Stefanescu Signed-off-by: Mark Brown --- drivers/regulator/mcp16502.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/regulator/mcp16502.c b/drivers/regulator/mcp16502.c index 25d7b433fafd82..809a664733a447 100644 --- a/drivers/regulator/mcp16502.c +++ b/drivers/regulator/mcp16502.c @@ -518,14 +518,14 @@ static int mcp16502_resume_noirq(struct device *dev) return 0; } #else /* !CONFIG_SUSPEND */ -#define mcp16502_suspend NULL -#define mcp16502_resume NULL +#define mcp16502_suspend_noirq NULL +#define mcp16502_resume_noirq NULL #endif /* !CONFIG_SUSPEND */ #ifdef CONFIG_PM static const struct dev_pm_ops mcp16502_pm_ops = { SET_NOIRQ_SYSTEM_SLEEP_PM_OPS(mcp16502_suspend_noirq, - mcp16502_resume_noirq) + mcp16502_resume_noirq) }; #endif static const struct i2c_device_id mcp16502_i2c_id[] = { From 4906d09164c00979dcbb28060231d9ce46ab2061 Mon Sep 17 00:00:00 2001 From: Axel Lin Date: Thu, 20 Dec 2018 21:14:13 +0800 Subject: [PATCH 81/85] regulator: mcp16502: Use #ifdef CONFIG_PM_SLEEP around mcp16502_suspend/resume_noirq mcp16502_suspend/resume_noirq is only used by SET_NOIRQ_SYSTEM_SLEEP_PM_OPS when CONFIG_PM_SLEEP is defined. So use #ifdef CONFIG_PM_SLEEP instead CONFIG_SUSPEND guard. Signed-off-by: Axel Lin Signed-off-by: Mark Brown --- drivers/regulator/mcp16502.c | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/drivers/regulator/mcp16502.c b/drivers/regulator/mcp16502.c index 809a664733a447..cb11fb206899af 100644 --- a/drivers/regulator/mcp16502.c +++ b/drivers/regulator/mcp16502.c @@ -497,7 +497,7 @@ static int mcp16502_probe(struct i2c_client *client, return 0; } -#ifdef CONFIG_SUSPEND +#ifdef CONFIG_PM_SLEEP static int mcp16502_suspend_noirq(struct device *dev) { struct i2c_client *client = to_i2c_client(dev); @@ -517,10 +517,7 @@ static int mcp16502_resume_noirq(struct device *dev) return 0; } -#else /* !CONFIG_SUSPEND */ -#define mcp16502_suspend_noirq NULL -#define mcp16502_resume_noirq NULL -#endif /* !CONFIG_SUSPEND */ +#endif #ifdef CONFIG_PM static const struct dev_pm_ops mcp16502_pm_ops = { From f4c8f980283b4832dad4d9ff4d5ecb3d7763efae Mon Sep 17 00:00:00 2001 From: Axel Lin Date: Wed, 19 Dec 2018 16:58:04 +0800 Subject: [PATCH 82/85] regulator: mcp16502: Fix missing n_voltages setting The n_voltages setting is not set, fix it. Signed-off-by: Axel Lin Signed-off-by: Mark Brown --- drivers/regulator/mcp16502.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/regulator/mcp16502.c b/drivers/regulator/mcp16502.c index cb11fb206899af..3479ae009b0b26 100644 --- a/drivers/regulator/mcp16502.c +++ b/drivers/regulator/mcp16502.c @@ -90,6 +90,7 @@ static unsigned int mcp16502_of_map_mode(unsigned int mode) .ops = &(_ops), \ .type = REGULATOR_VOLTAGE, \ .owner = THIS_MODULE, \ + .n_voltages = MCP16502_VSEL + 1, \ .linear_ranges = _ranges, \ .n_linear_ranges = ARRAY_SIZE(_ranges), \ .of_match = of_match_ptr(_name), \ From 3e60b4fc86710cd2095628c67b296e7cc4b239c1 Mon Sep 17 00:00:00 2001 From: Yangtao Li Date: Sat, 15 Dec 2018 03:21:48 -0500 Subject: [PATCH 83/85] regulator: convert to DEFINE_SHOW_ATTRIBUTE Use DEFINE_SHOW_ATTRIBUTE macro to simplify the code. Signed-off-by: Yangtao Li Signed-off-by: Mark Brown --- drivers/regulator/core.c | 37 +++++--------------------------- drivers/regulator/dbx500-prcmu.c | 35 ++++-------------------------- 2 files changed, 9 insertions(+), 63 deletions(-) diff --git a/drivers/regulator/core.c b/drivers/regulator/core.c index f211aa7da44e5d..0e402606e8bea6 100644 --- a/drivers/regulator/core.c +++ b/drivers/regulator/core.c @@ -5278,23 +5278,8 @@ static int supply_map_show(struct seq_file *sf, void *data) return 0; } +DEFINE_SHOW_ATTRIBUTE(supply_map); -static int supply_map_open(struct inode *inode, struct file *file) -{ - return single_open(file, supply_map_show, inode->i_private); -} -#endif - -static const struct file_operations supply_map_fops = { -#ifdef CONFIG_DEBUG_FS - .open = supply_map_open, - .read = seq_read, - .llseek = seq_lseek, - .release = single_release, -#endif -}; - -#ifdef CONFIG_DEBUG_FS struct summary_data { struct seq_file *s; struct regulator_dev *parent; @@ -5516,21 +5501,8 @@ static int regulator_summary_show(struct seq_file *s, void *data) return 0; } - -static int regulator_summary_open(struct inode *inode, struct file *file) -{ - return single_open(file, regulator_summary_show, inode->i_private); -} -#endif - -static const struct file_operations regulator_summary_fops = { -#ifdef CONFIG_DEBUG_FS - .open = regulator_summary_open, - .read = seq_read, - .llseek = seq_lseek, - .release = single_release, -#endif -}; +DEFINE_SHOW_ATTRIBUTE(regulator_summary); +#endif /* CONFIG_DEBUG_FS */ static int __init regulator_init(void) { @@ -5542,12 +5514,13 @@ static int __init regulator_init(void) if (!debugfs_root) pr_warn("regulator: Failed to create debugfs directory\n"); +#ifdef CONFIG_DEBUG_FS debugfs_create_file("supply_map", 0444, debugfs_root, NULL, &supply_map_fops); debugfs_create_file("regulator_summary", 0444, debugfs_root, NULL, ®ulator_summary_fops); - +#endif regulator_dummy_init(); return ret; diff --git a/drivers/regulator/dbx500-prcmu.c b/drivers/regulator/dbx500-prcmu.c index 8976141c1438ac..308e3ff0a1bd7c 100644 --- a/drivers/regulator/dbx500-prcmu.c +++ b/drivers/regulator/dbx500-prcmu.c @@ -75,7 +75,7 @@ static struct ux500_regulator_debug { u8 *state_after_suspend; } rdebug; -static int ux500_regulator_power_state_cnt_print(struct seq_file *s, void *p) +static int ux500_regulator_power_state_cnt_show(struct seq_file *s, void *p) { /* print power state count */ seq_printf(s, "ux500-regulator power state count: %i\n", @@ -83,23 +83,9 @@ static int ux500_regulator_power_state_cnt_print(struct seq_file *s, void *p) return 0; } +DEFINE_SHOW_ATTRIBUTE(ux500_regulator_power_state_cnt); -static int ux500_regulator_power_state_cnt_open(struct inode *inode, - struct file *file) -{ - return single_open(file, ux500_regulator_power_state_cnt_print, - inode->i_private); -} - -static const struct file_operations ux500_regulator_power_state_cnt_fops = { - .open = ux500_regulator_power_state_cnt_open, - .read = seq_read, - .llseek = seq_lseek, - .release = single_release, - .owner = THIS_MODULE, -}; - -static int ux500_regulator_status_print(struct seq_file *s, void *p) +static int ux500_regulator_status_show(struct seq_file *s, void *p) { int i; @@ -122,20 +108,7 @@ static int ux500_regulator_status_print(struct seq_file *s, void *p) return 0; } - -static int ux500_regulator_status_open(struct inode *inode, struct file *file) -{ - return single_open(file, ux500_regulator_status_print, - inode->i_private); -} - -static const struct file_operations ux500_regulator_status_fops = { - .open = ux500_regulator_status_open, - .read = seq_read, - .llseek = seq_lseek, - .release = single_release, - .owner = THIS_MODULE, -}; +DEFINE_SHOW_ATTRIBUTE(ux500_regulator_status); int __attribute__((weak)) dbx500_regulator_testcase( struct dbx500_regulator_info *regulator_info, From 77ea906082dcfa63ad8a07ace6d5033d30c4175b Mon Sep 17 00:00:00 2001 From: Axel Lin Date: Fri, 21 Dec 2018 10:49:29 +0800 Subject: [PATCH 84/85] regulator: mcp16502: Select REGMAP_I2C to fix build error Fix build error when CONFIG_REGMAP_I2C=m && CONFIG_REGULATOR_MCP16502=y. drivers/regulator/mcp16502.o: In function `mcp16502_probe': mcp16502.c:(.text+0xca): undefined reference to `__devm_regmap_init_i2c' Signed-off-by: Axel Lin Signed-off-by: Mark Brown --- drivers/regulator/Kconfig | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/regulator/Kconfig b/drivers/regulator/Kconfig index 719d9d660e5662..ee60a222f5ebb0 100644 --- a/drivers/regulator/Kconfig +++ b/drivers/regulator/Kconfig @@ -570,6 +570,7 @@ config REGULATOR_MC13892 config REGULATOR_MCP16502 tristate "Microchip MCP16502 PMIC" depends on I2C && OF + select REGMAP_I2C help Say y here to support the MCP16502 PMIC. This driver supports basic operations (get/set voltage, get/set operating mode) From cd07e3701fa6a4c68f8493ee1d12caa18d46ec6a Mon Sep 17 00:00:00 2001 From: Kangjie Lu Date: Fri, 21 Dec 2018 00:29:19 -0600 Subject: [PATCH 85/85] regulator: tps65910: fix a missing check of return value tps65910_reg_set_bits() may fail. The fix checks if it fails, and if so, returns with its error code. Signed-off-by: Kangjie Lu Signed-off-by: Mark Brown --- drivers/regulator/tps65910-regulator.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/regulator/tps65910-regulator.c b/drivers/regulator/tps65910-regulator.c index 02ccdaa226a73f..5ebb6ee73f0770 100644 --- a/drivers/regulator/tps65910-regulator.c +++ b/drivers/regulator/tps65910-regulator.c @@ -1102,8 +1102,10 @@ static int tps65910_probe(struct platform_device *pdev) platform_set_drvdata(pdev, pmic); /* Give control of all register to control port */ - tps65910_reg_set_bits(pmic->mfd, TPS65910_DEVCTRL, + err = tps65910_reg_set_bits(pmic->mfd, TPS65910_DEVCTRL, DEVCTRL_SR_CTL_I2C_SEL_MASK); + if (err < 0) + return err; switch (tps65910_chip_id(tps65910)) { case TPS65910: