Continuing the work on coupled regulators
Subsystems (3)
cover Continuing the work on coupled regulators
Hello,
Maciej moved on to work on something else and kindly allowed me to take over the coupled regulators patches [0] which I'll try to finalize.
I recently started to work on adding DVFS support to the CPUFreq driver of NVIDIA Tegra20/30 which require the coupled regulators functionality. Both Tegra20 and Tegra30 have the voltage "max-spread" constraint. Tegra30 has an additional constraint, the "max-step" voltage.
What is done in this series:
1. Re-worked the original "Add voltage balancing mechanism" patch from Maciej by:
1) Fixing infinite loop within regulator_balance_voltage().
2) Handling suspend_state_t properly.
3) Fixing broken compilation of the patch.
2. Added pair of patches to clean up regulator-couple resolving.
3. Changed device tree binding of the regulator-coupled-max-spread property to make it more flexible.
4. Added new device tree property, the regulator-max-step-microvolt.
5. Implemented wait/wound mutex which was suggested by Lucas Stach in the review comment to [0]. I've tested w/w locking extensively (including CONFIG_DEBUG_WW_MUTEX_SLOWPATH) and quite confident that everything is okay.
6. Patches were tested with KASAN on Tegra20 / Tegra30, I tried to cover various cases and verified that voltage constraints not violated under different circumstances.
Please review, thanks.
[0] https://lkml.org/lkml/2018/4/23/619
Dmitry Osipenko (9):
regulator: core: Mutually resolve regulators coupling
regulator: core: Don't allow to get regulator until all couples
resolved
dt-bindings: regulator: Change regulator-coupled-max-spread property
regulator: core: Limit regulators coupling to a single couple
dt-bindings: regulator: Document new regulator-max-step-microvolt
property
regulator: core: Add new max_uV_step constraint
regulator: core: Decouple regulators on regulator_unregister()
regulator: core: Use ww_mutex for regulators locking
regulator: core: Properly handle case where supply is the couple
Maciej Purski (2):
regulator: core: Add voltage balancing mechanism
regulator: core: Change voltage setting path
.../bindings/regulator/regulator.txt | 7 +- drivers/regulator/core.c | 821 +++++++++++++++--- drivers/regulator/of_regulator.c | 4 + include/linux/regulator/driver.h | 5 +- include/linux/regulator/machine.h | 3 + 5 files changed, 713 insertions(+), 127 deletions(-)
Signature · 1 line
2.19.0
PATCH 1/11 regulator: core: Add voltage balancing mechanism
From: Maciej Purski <[email protected]>
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.
--- drivers/regulator/core.c | 229 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 229 insertions(+)
drivers/regulator/core.c +229 -0
@@ -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
Signature · 1 line
2.19.0
PATCH 2/11 regulator: core: Change voltage setting path
In reply to Dmitry OsipenkoFrom: Maciej Purski <[email protected]>
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.
--- drivers/regulator/core.c | 145 ++++++++++++++++++++++++++------------- 1 file changed, 99 insertions(+), 46 deletions(-)
drivers/regulator/core.c +99 -46
@@ -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; }
Signature · 1 line
2.19.0
PATCH 3/11 regulator: core: Mutually resolve regulators coupling
In reply to Dmitry OsipenkoIf 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.
--- drivers/regulator/core.c | 54 +++++++++++++--------------------------- 1 file changed, 17 insertions(+), 37 deletions(-)
drivers/regulator/core.c +17 -37
@@ -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);
Signature · 1 line
2.19.0
PATCH 4/11 regulator: core: Don't allow to get regulator until all couples resolved
In reply to Dmitry OsipenkoDon't allow to get regulator until all of its couples resolved because consumer will get EPERM and coupling shall be transparent for the drivers.
--- drivers/regulator/core.c | 10 ++++++++++ 1 file changed, 10 insertions(+)
drivers/regulator/core.c +10 -0
@@ -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);
Signature · 1 line
2.19.0
PATCH 5/11 dt-bindings: regulator: Change regulator-coupled-max-spread property
In reply to Dmitry OsipenkoRedefine 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>;
};--- Documentation/devicetree/bindings/regulator/regulator.txt | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-)
Documentation/devicetree/bindings/regulator/regulator.txt +3 -2
@@ -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
Signature · 1 line
2.19.0
Rob Herring
maintainerRe: [PATCH v1 05/11] dt-bindings: regulator: Change regulator-coupled-max-spread property
Quoted 32 lines · Dmitry Osipenko
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 <[email protected]>--- Documentation/devicetree/bindings/regulator/regulator.txt | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-)Documentation/devicetree/bindings/regulator/regulator.txt +0
-0
Documentation/devicetree/bindings/regulator/regulator.txt Optional properties: · line 81
Quoted 8 lines
Documentation/devicetree/bindings/regulator/regulator.txt +3
-2@@ -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.
Is this in use already? How is a single entry handled?
Rob
Re: [PATCH v1 05/11] dt-bindings: regulator: Change regulator-coupled-max-spread property
Quoted 28 lines · Rob Herring
Quoted 27 lines · Dmitry Osipenko
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 <[email protected]>--- Documentation/devicetree/bindings/regulator/regulator.txt | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-)
Quoted 15 lines
Documentation/devicetree/bindings/regulator/regulator.txt Optional properties: · line 81
Quoted 13 lines
Documentation/devicetree/bindings/regulator/regulator.txt +3
-2@@ -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.Is this in use already? How is a single entry handled?
No, it is not in use yet. Only some of the prerequisite patches were applied.
Initially single entry was supposed to be re-used by all of regulator couples. Now single entry describes a single couple only.
Rob Herring
maintainerRe: [PATCH v1 05/11] dt-bindings: regulator: Change regulator-coupled-max-spread property
Quoted 46 lines · Dmitry Osipenko
Quoted 28 lines · Rob Herring
Quoted 27 lines · Dmitry Osipenko
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 <[email protected]>--- Documentation/devicetree/bindings/regulator/regulator.txt | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-)Quoted 15 lines
Documentation/devicetree/bindings/regulator/regulator.txt Optional properties: · line 81
Quoted 13 lines
Documentation/devicetree/bindings/regulator/regulator.txt +3
-2@@ -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.Is this in use already? How is a single entry handled?
Quoted 1 line
No, it is not in use yet. Only some of the prerequisite patches were applied.
Please make that clear in the commit message.
Re: [PATCH v1 05/11] dt-bindings: regulator: Change regulator-coupled-max-spread property
Quoted 47 lines · Rob Herring
Quoted 46 lines · Dmitry Osipenko
Quoted 43 lines · Rob Herring
Quoted 32 lines · Dmitry Osipenko
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 <[email protected]>--- Documentation/devicetree/bindings/regulator/regulator.txt | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-)Documentation/devicetree/bindings/regulator/regulator.txt +0
-0Documentation/devicetree/bindings/regulator/regulator.txt Optional properties: · line 81
Quoted 8 lines
Documentation/devicetree/bindings/regulator/regulator.txt +3
-2@@ -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.Is this in use already? How is a single entry handled?
Quoted 3 lines
Quoted 1 line
No, it is not in use yet. Only some of the prerequisite patches were applied.
Please make that clear in the commit message.
Okay.
Re: [PATCH v1 05/11] dt-bindings: regulator: Change regulator-coupled-max-spread property
In reply to Dmitry OsipenkoQuoted 22 lines · Dmitry Osipenko
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>; };
Quoted 5 lines
Signed-off-by Dmitry Osipenko <[email protected]>--- Documentation/devicetree/bindings/regulator/regulator.txt | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-)
Rob Herring
maintainerRe: [PATCH v1 05/11] dt-bindings: regulator: Change regulator-coupled-max-spread property
Quoted 24 lines · Rob Herring
Quoted 22 lines · Dmitry Osipenko
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>; };
Quoted 7 lines
Quoted 5 lines
Signed-off-by Dmitry Osipenko <[email protected]>--- Documentation/devicetree/bindings/regulator/regulator.txt | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-)Reviewed-by Rob Herring <[email protected]>
Sent this by mistake. Disregard as I have questions.
Rob
PATCH 6/11 regulator: core: Limit regulators coupling to a single couple
In reply to Dmitry OsipenkoDevice 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.
--- include/linux/regulator/driver.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
include/linux/regulator/driver.h +1 -1
@@ -15,7 +15,7 @@#ifndef __LINUX_REGULATOR_DRIVER_H_#define __LINUX_REGULATOR_DRIVER_H_ -#define MAX_COUPLED 4 +#define MAX_COUPLED 2#include <linux/device.h> #include <linux/notifier.h>
Signature · 1 line
2.19.0
PATCH 7/11 dt-bindings: regulator: Document new regulator-max-step-microvolt property
In reply to Dmitry OsipenkoCertain hardware may require supply voltage to be changed in steps. Define new property that allow to describe such hardware.
--- Documentation/devicetree/bindings/regulator/regulator.txt | 2 ++ 1 file changed, 2 insertions(+)
Documentation/devicetree/bindings/regulator/regulator.txt +2 -0
@@ -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
Signature · 1 line
2.19.0
Re: [PATCH v1 07/11] dt-bindings: regulator: Document new regulator-max-step-microvolt property
Quoted 8 lines · Dmitry Osipenko
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 <[email protected]>--- Documentation/devicetree/bindings/regulator/regulator.txt | 2 ++ 1 file changed, 2 insertions(+)
PATCH 8/11 regulator: core: Add new max_uV_step constraint
In reply to Dmitry OsipenkoOn 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.
--- drivers/regulator/core.c | 41 +++++++++++++++++++++++++++++++ drivers/regulator/of_regulator.c | 4 +++ include/linux/regulator/machine.h | 3 +++ 3 files changed, 48 insertions(+)
drivers/regulator/core.c +41 -0
@@ -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 ) {
drivers/regulator/of_regulator.c +4 -0
@@ -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" );
include/linux/regulator/machine.h +3 -0
@@ -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;
Signature · 1 line
2.19.0
PATCH 9/11 regulator: core: Decouple regulators on regulator_unregister()
In reply to Dmitry OsipenkoRegulators shall be uncoupled if one of the couples disappear.
--- drivers/regulator/core.c | 38 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+)
drivers/regulator/core.c +38 -0
@@ -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);
Signature · 1 line
2.19.0
PATCH 10/11 regulator: core: Use ww_mutex for regulators locking
In reply to Dmitry OsipenkoWait/wound mutex shall be used in order to avoid lockups on locking of coupled regulators.
--- drivers/regulator/core.c | 401 +++++++++++++++++++++++-------- include/linux/regulator/driver.h | 3 +- 2 files changed, 307 insertions(+), 97 deletions(-)
drivers/regulator/core.c +305 -96
@@ -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. +*/ +static void regulator_lock (struct regulator_dev *rdev) { -regulator_lock_nested (rdev,0 ); +regulator_lock_nested (rdev,NULL ); }/**
@@ -191,50 +221,46 @@ static inline void regulator_lock(struct regulator_dev *rdev)*/ static 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 ; }
include/linux/regulator/driver.h +2 -1
@@ -20,6 +20,7 @@#include <linux/device.h> #include <linux/notifier.h> #include <linux/regulator/consumer.h> +#include <linux/ww_mutex.h> 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;
Signature · 1 line
2.19.0
Mark Brown
maintainerRe: [PATCH v1 10/11] regulator: core: Use ww_mutex for regulators locking
Quoted 2 lines · Dmitry Osipenko
Wait/wound mutex shall be used in order to avoid lockups on locking of coupled regulators.
This breaks the build due to a few of the drivers (wm8350 and da9210 at least) taking rdev locks in their interrupt handlers. I'd suggest adding a function for those drivers to use - exposing the ww context seems like the wrong thing to do there.
Otherwise the series looks good, I've applied everything up to here. Thanks for taking the time to pick this up, and especially for figuring out the ww_mutex conversion.
Re: [PATCH v1 10/11] regulator: core: Use ww_mutex for regulators locking
Quoted 9 lines · Mark Brown
Quoted 2 lines · Dmitry Osipenko
Wait/wound mutex shall be used in order to avoid lockups on locking of coupled regulators.
This breaks the build due to a few of the drivers (wm8350 and da9210 at least) taking rdev locks in their interrupt handlers. I'd suggest adding a function for those drivers to use - exposing the ww context seems like the wrong thing to do there.
I totally missed those drivers, will fix them in v2.
Quoted 4 lines
Otherwise the series looks good, I've applied everything up to here. Thanks for taking the time to pick this up, and especially for figuring out the
ww_mutexconversion.
Thank you.
PATCH 11/11 regulator: core: Properly handle case where supply is the couple
In reply to Dmitry OsipenkoCheck whether supply regulator is the couple to avoid infinite recursion during of locking.
--- drivers/regulator/core.c | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-)
drivers/regulator/core.c +17 -2
@@ -233,6 +233,21 @@ static 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,
Signature · 1 line
2.19.0
Quoted 5 lines · Dmitry Osipenko
1. Re-worked the original "Add voltage balancing mechanism" patch from Maciej by:
1) Fixing infinite loop withinregulator_balance_voltage().
2) Handlingsuspend_state_tproperly.
3) Fixing broken compilation of the patch.
Thanks for fixing these, this series no longer hangs for me on beagle-x15.
Regards,
Tony
Quoted 9 lines · Tony Lindgren
Quoted 5 lines · Dmitry Osipenko
1. Re-worked the original "Add voltage balancing mechanism" patch from Maciej by:
1) Fixing infinite loop withinregulator_balance_voltage().
2) Handlingsuspend_state_tproperly.
3) Fixing broken compilation of the patch.Thanks for fixing these, this series no longer hangs for me on beagle-x15.
Thank you very much for testing once again!