diff options
author | Stephen Rothwell <sfr@canb.auug.org.au> | 2018-11-21 12:10:40 +1100 |
---|---|---|
committer | Stephen Rothwell <sfr@canb.auug.org.au> | 2018-11-21 12:10:40 +1100 |
commit | 0f01e1cf50dce5e079905e4d2c836a278366ccbe (patch) | |
tree | 892c9cbe826525efe32aed2e766814b4939d407b /drivers | |
parent | 19b9ef1c54eb4eb3843560b22b9ab071639cbc65 (diff) | |
parent | 094faeb68e062d631d42716d8f854972ba25bf58 (diff) |
Merge remote-tracking branch 'regulator/for-next'
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/mfd/wm8994-core.c | 9 | ||||
-rw-r--r-- | drivers/regulator/as3711-regulator.c | 5 | ||||
-rw-r--r-- | drivers/regulator/bd718x7-regulator.c | 32 | ||||
-rw-r--r-- | drivers/regulator/bd9571mwv-regulator.c | 10 | ||||
-rw-r--r-- | drivers/regulator/core.c | 879 | ||||
-rw-r--r-- | drivers/regulator/da9210-regulator.c | 4 | ||||
-rw-r--r-- | drivers/regulator/lochnagar-regulator.c | 50 | ||||
-rw-r--r-- | drivers/regulator/max77686-regulator.c | 19 | ||||
-rw-r--r-- | drivers/regulator/of_regulator.c | 9 | ||||
-rw-r--r-- | drivers/regulator/pfuze100-regulator.c | 2 | ||||
-rw-r--r-- | drivers/regulator/s2mps11.c | 47 | ||||
-rw-r--r-- | drivers/regulator/stpmic1_regulator.c | 4 | ||||
-rw-r--r-- | drivers/regulator/wm8350-regulator.c | 4 | ||||
-rw-r--r-- | drivers/regulator/wm8994-regulator.c | 20 |
14 files changed, 874 insertions, 220 deletions
diff --git a/drivers/mfd/wm8994-core.c b/drivers/mfd/wm8994-core.c index 22bd6525e09c..04a177efd245 100644 --- a/drivers/mfd/wm8994-core.c +++ b/drivers/mfd/wm8994-core.c @@ -21,7 +21,6 @@ #include <linux/mfd/core.h> #include <linux/of.h> #include <linux/of_device.h> -#include <linux/of_gpio.h> #include <linux/pm_runtime.h> #include <linux/regmap.h> #include <linux/regulator/consumer.h> @@ -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/as3711-regulator.c b/drivers/regulator/as3711-regulator.c index 565a71343a8e..f7fe218bb3e4 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, <g.liakhovetski@gmx.de> - * - * 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 <linux/err.h> diff --git a/drivers/regulator/bd718x7-regulator.c b/drivers/regulator/bd718x7-regulator.c index 7ba14dae5848..b8dcdc21dc22 100644 --- a/drivers/regulator/bd718x7-regulator.c +++ b/drivers/regulator/bd718x7-regulator.c @@ -131,6 +131,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, @@ -1008,7 +1009,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; }; @@ -1018,11 +1019,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), }, }; @@ -1054,13 +1055,36 @@ 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; 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; diff --git a/drivers/regulator/bd9571mwv-regulator.c b/drivers/regulator/bd9571mwv-regulator.c index 274c5ed7cd73..e12dd1f750f3 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 <marek.vasut+renesas@gmail.com> * - * 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 diff --git a/drivers/regulator/core.c b/drivers/regulator/core.c index 2c66b528aede..1acf6567f40a 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); @@ -105,6 +107,11 @@ 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 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); @@ -149,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 @@ -157,25 +164,54 @@ 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); } +EXPORT_SYMBOL_GPL(regulator_lock); /** * regulator_unlock - unlock a single regulator @@ -184,47 +220,191 @@ 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) +{ + mutex_lock(®ulator_nesting_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); +} +EXPORT_SYMBOL_GPL(regulator_unlock); + +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) { - if (rdev->ref_cnt != 0) { - rdev->ref_cnt--; + struct regulator_dev *c_rdev; + int i; + + for (i = n_coupled; i > 0; i--) { + c_rdev = rdev->coupling_desc.coupled_rdevs[i - 1]; + + if (!c_rdev) + continue; + + if (c_rdev->supply && !regulator_supply_is_couple(c_rdev)) + regulator_unlock_recursive( + c_rdev->supply->rdev, + c_rdev->coupling_desc.n_coupled); + + regulator_unlock(c_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, err; - if (!rdev->ref_cnt) { - rdev->mutex_owner = NULL; - mutex_unlock(&rdev->mutex); + for (i = 0; i < rdev->coupling_desc.n_coupled; i++) { + c_rdev = rdev->coupling_desc.coupled_rdevs[i]; + + if (!c_rdev) + continue; + + 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; + } + + /* shouldn't happen */ + WARN_ON_ONCE(err != -EALREADY); + } + } else { + *old_contended_rdev = NULL; + } + + if (c_rdev->supply && !regulator_supply_is_couple(c_rdev)) { + 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_lock_supply - lock a regulator and its supplies - * @rdev: regulator source + * 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_lock_supply(struct regulator_dev *rdev) +static void regulator_unlock_dependent(struct regulator_dev *rdev, + struct ww_acquire_ctx *ww_ctx) { - int i; - - for (i = 0; rdev; rdev = rdev_get_supply(rdev), i++) - regulator_lock_nested(rdev, i); + regulator_unlock_recursive(rdev, rdev->coupling_desc.n_coupled); + ww_acquire_fini(ww_ctx); } /** - * regulator_unlock_supply - unlock a regulator and its supplies - * @rdev: regulator source + * 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 void regulator_unlock_supply(struct regulator_dev *rdev) +static void regulator_lock_dependent(struct regulator_dev *rdev, + struct ww_acquire_ctx *ww_ctx) { - struct regulator *supply; + 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); - while (1) { - regulator_unlock(rdev); - supply = rdev->supply; + 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); +} + +/** + * 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; - if (!rdev->supply) - return; + for_each_child_of_node(parent, child) { + regnode = of_parse_phandle(child, prop_name, 0); - rdev = supply->rdev; + if (!regnode) { + regnode = of_get_child_regulator(child, prop_name); + if (regnode) + return regnode; + } else { + return regnode; + } } + return NULL; } /** @@ -247,6 +427,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; @@ -738,7 +922,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 @@ -1713,6 +1897,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); @@ -2230,7 +2424,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)) @@ -2241,18 +2448,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 */ } @@ -2260,6 +2469,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; } /** @@ -2276,23 +2491,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; - } - - mutex_lock(&rdev->mutex); + regulator_lock_dependent(rdev, &ww_ctx); ret = _regulator_enable(rdev); - mutex_unlock(&rdev->mutex); - - if (ret != 0 && rdev->supply) - regulator_disable(rdev->supply); + regulator_unlock_dependent(rdev, &ww_ctx); return ret; } @@ -2334,7 +2541,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))) @@ -2372,6 +2579,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; } @@ -2390,17 +2603,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; - mutex_lock(&rdev->mutex); + regulator_lock_dependent(rdev, &ww_ctx); ret = _regulator_disable(rdev); - mutex_unlock(&rdev->mutex); - - if (ret == 0 && rdev->supply) - regulator_disable(rdev->supply); + regulator_unlock_dependent(rdev, &ww_ctx); return ret; } @@ -2411,7 +2622,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); @@ -2444,12 +2655,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; - mutex_lock(&rdev->mutex); + regulator_lock_dependent(rdev, &ww_ctx); 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, &ww_ctx); if (rdev->supply) while (rdev->open_count--) @@ -2463,9 +2677,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); @@ -2486,17 +2701,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); - if (rdev->supply) { - for (i = 0; i < count; i++) { - ret = regulator_disable(rdev->supply); - if (ret != 0) { - rdev_err(rdev, - "Supply disable failed: %d\n", ret); - } - } - } + regulator_unlock_dependent(rdev, &ww_ctx); } /** @@ -2597,9 +2805,9 @@ int regulator_is_enabled(struct regulator *regulator) if (regulator->always_on) return 1; - mutex_lock(®ulator->rdev->mutex); + regulator_lock(regulator->rdev); ret = _regulator_is_enabled(regulator->rdev); - mutex_unlock(®ulator->rdev->mutex); + regulator_unlock(regulator->rdev); return ret; } @@ -3013,8 +3221,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 @@ -3054,10 +3260,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) && @@ -3069,13 +3292,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; @@ -3083,7 +3306,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; @@ -3095,7 +3318,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; } } @@ -3105,7 +3328,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, @@ -3119,10 +3342,273 @@ 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; +} + +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, + 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.base); + + 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: + /* 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) { + + 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; + } + + ret = regulator_set_voltage_rdev(best_rdev, best_min_uV, + best_max_uV, state); + + if (ret < 0) + goto out; + + c_rdev_done[best_c_rdev] = best_c_rdev_done; + + } while (n_coupled > 1); + +out: return ret; } @@ -3146,14 +3632,15 @@ out2: */ 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_supply(regulator->rdev); + regulator_lock_dependent(regulator->rdev, &ww_ctx); ret = regulator_set_voltage_unlocked(regulator, min_uV, max_uV, PM_SUSPEND_ON); - regulator_unlock_supply(regulator->rdev); + regulator_unlock_dependent(regulator->rdev, &ww_ctx); return ret; } @@ -3225,18 +3712,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_supply(regulator->rdev); + regulator_lock_dependent(regulator->rdev, &ww_ctx); ret = _regulator_set_suspend_voltage(regulator, min_uV, max_uV, state); - regulator_unlock_supply(regulator->rdev); + regulator_unlock_dependent(regulator->rdev, &ww_ctx); return ret; } @@ -3426,13 +3914,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_supply(regulator->rdev); - + regulator_lock_dependent(regulator->rdev, &ww_ctx); ret = _regulator_get_voltage(regulator->rdev); - - regulator_unlock_supply(regulator->rdev); + regulator_unlock_dependent(regulator->rdev, &ww_ctx); return ret; } @@ -3968,7 +4455,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; @@ -4070,10 +4557,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) @@ -4157,7 +4640,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; @@ -4171,33 +4654,58 @@ 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 (!c_rdev) + continue; - if (rdev->coupling_desc.n_resolved < n_coupled) - return -1; - else - return 0; + regulator_lock(c_rdev); + + c_desc->coupled_rdevs[i] = c_rdev; + c_desc->n_resolved++; + + regulator_unlock(c_rdev); + + regulator_resolve_coupling(c_rdev); + } } -static int regulator_register_fill_coupling_array(struct device *dev, - void *data) +static void regulator_remove_coupling(struct regulator_dev *rdev) { - struct regulator_dev *rdev = dev_to_rdev(dev); + 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; - if (!IS_ENABLED(CONFIG_OF)) - return 0; + n_coupled = c_desc->n_coupled; + + for (i = 1; i < n_coupled; i++) { + c_rdev = c_desc->coupled_rdevs[i]; - if (regulator_fill_coupling_array(rdev)) - rdev_dbg(rdev, "unable to resolve coupling\n"); + if (!c_rdev) + continue; - return 0; + 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_resolve_coupling(struct regulator_dev *rdev) +static int regulator_init_coupling(struct regulator_dev *rdev) { int n_phandles; @@ -4237,13 +4745,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; } @@ -4318,7 +4819,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; @@ -4380,11 +4881,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 */ @@ -4418,6 +4916,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); @@ -4456,15 +4959,19 @@ 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); + regulator_remove_coupling(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); @@ -4672,8 +5179,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, "", @@ -4730,8 +5235,105 @@ 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; + + 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_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); + + mutex_unlock(®ulator_list_mutex); } static int regulator_summary_show_roots(struct device *dev, void *data) @@ -4747,12 +5349,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; } @@ -4873,9 +5481,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); diff --git a/drivers/regulator/da9210-regulator.c b/drivers/regulator/da9210-regulator.c index d0496d6b0934..84dba64ed11e 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/lochnagar-regulator.c b/drivers/regulator/lochnagar-regulator.c index 2b5073b9ff86..5a89e6d4b9a6 100644 --- a/drivers/regulator/lochnagar-regulator.c +++ b/drivers/regulator/lochnagar-regulator.c @@ -13,6 +13,7 @@ #include <linux/module.h> #include <linux/mutex.h> #include <linux/of.h> +#include <linux/of_device.h> #include <linux/platform_device.h> #include <linux/regmap.h> #include <linux/regulator/driver.h> @@ -20,6 +21,8 @@ #include <linux/regulator/of_regulator.h> #include <linux/mfd/lochnagar.h> +#include <linux/mfd/lochnagar1_regs.h> +#include <linux/mfd/lochnagar2_regs.h> static const struct regulator_ops lochnagar_micvdd_ops = { .enable = regulator_enable_regmap, @@ -212,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; @@ -242,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, diff --git a/drivers/regulator/max77686-regulator.c b/drivers/regulator/max77686-regulator.c index bee060937f56..f5cee1775905 100644 --- a/drivers/regulator/max77686-regulator.c +++ b/drivers/regulator/max77686-regulator.c @@ -11,8 +11,7 @@ #include <linux/kernel.h> #include <linux/bug.h> #include <linux/err.h> -#include <linux/gpio.h> -#include <linux/of_gpio.h> +#include <linux/gpio/consumer.h> #include <linux/slab.h> #include <linux/platform_device.h> #include <linux/regulator/driver.h> @@ -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; diff --git a/drivers/regulator/of_regulator.c b/drivers/regulator/of_regulator.c index c4223b3e0dff..c711a0a2bc4b 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", }; @@ -170,6 +171,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"); @@ -181,9 +186,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; } diff --git a/drivers/regulator/pfuze100-regulator.c b/drivers/regulator/pfuze100-regulator.c index dd41a9bb3f5c..df5df1c495ad 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/drivers/regulator/s2mps11.c b/drivers/regulator/s2mps11.c index 5bb6f4ca48db..63e66f485cc0 100644 --- a/drivers/regulator/s2mps11.c +++ b/drivers/regulator/s2mps11.c @@ -5,7 +5,7 @@ #include <linux/bug.h> #include <linux/err.h> -#include <linux/gpio.h> +#include <linux/gpio/consumer.h> #include <linux/slab.h> #include <linux/module.h> #include <linux/of.h> @@ -14,7 +14,6 @@ #include <linux/regulator/driver.h> #include <linux/regulator/machine.h> #include <linux/regulator/of_regulator.h> -#include <linux/of_gpio.h> #include <linux/mfd/samsung/core.h> #include <linux/mfd/samsung/s2mps11.h> #include <linux/mfd/samsung/s2mps13.h> @@ -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,10 @@ 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), - GFP_KERNEL); - if (!s2mps11->ext_control_gpio) + s2mps11->ext_control_gpiod = devm_kcalloc(&pdev->dev, rdev_num, + sizeof(*s2mps11->ext_control_gpiod), GFP_KERNEL); + 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 +1167,6 @@ common_reg: 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 +1177,7 @@ common_reg: 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 +1188,7 @@ common_reg: 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) { diff --git a/drivers/regulator/stpmic1_regulator.c b/drivers/regulator/stpmic1_regulator.c index e15634edb8ce..eac0848a78c7 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 8ad11b074b49..a1c7dfee5c37 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/drivers/regulator/wm8994-regulator.c b/drivers/regulator/wm8994-regulator.c index 7a4ce6df4f22..d7fec533c403 100644 --- a/drivers/regulator/wm8994-regulator.c +++ b/drivers/regulator/wm8994-regulator.c @@ -19,7 +19,7 @@ #include <linux/platform_device.h> #include <linux/regulator/driver.h> #include <linux/regulator/machine.h> -#include <linux/gpio.h> +#include <linux/gpio/consumer.h> #include <linux/slab.h> #include <linux/mfd/wm8994/core.h> @@ -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; |