| From fc5ad42fd2f3a9974bf52992cda8735e965cf16c Mon Sep 17 00:00:00 2001 |
| From: Dmitry Osipenko <digetx@gmail.com> |
| Date: Mon, 24 Jun 2019 00:08:31 +0300 |
| Subject: [PATCH] regulator: core: Introduce API for regulators coupling |
| customization |
| |
| commit d8ca7d184b33af7913c244900df77c6cad6a5590 upstream. |
| |
| Right now regulator core supports only one type of regulators coupling, |
| the "voltage max-spread" which keeps voltages of coupled regulators in a |
| given range from each other. A more sophisticated coupling may be required |
| in practice, one example is the NVIDIA Tegra SoCs which besides the |
| max-spreading have other restrictions that must be adhered. Introduce API |
| that allow platforms to provide their own customized coupling algorithms. |
| |
| Signed-off-by: Dmitry Osipenko <digetx@gmail.com> |
| Signed-off-by: Mark Brown <broonie@kernel.org> |
| Signed-off-by: Paul Gortmaker <paul.gortmaker@windriver.com> |
| |
| diff --git a/drivers/regulator/core.c b/drivers/regulator/core.c |
| index 794d2538af0b..e7be71938441 100644 |
| --- a/drivers/regulator/core.c |
| +++ b/drivers/regulator/core.c |
| @@ -23,6 +23,7 @@ |
| #include <linux/regmap.h> |
| #include <linux/regulator/of_regulator.h> |
| #include <linux/regulator/consumer.h> |
| +#include <linux/regulator/coupler.h> |
| #include <linux/regulator/driver.h> |
| #include <linux/regulator/machine.h> |
| #include <linux/module.h> |
| @@ -50,6 +51,7 @@ static DEFINE_MUTEX(regulator_list_mutex); |
| static LIST_HEAD(regulator_map_list); |
| static LIST_HEAD(regulator_ena_gpio_list); |
| static LIST_HEAD(regulator_supply_alias_list); |
| +static LIST_HEAD(regulator_coupler_list); |
| static bool has_full_constraints; |
| |
| static struct dentry *debugfs_root; |
| @@ -3436,11 +3438,10 @@ static int regulator_get_optimal_voltage(struct regulator_dev *rdev, |
| struct coupling_desc *c_desc = &rdev->coupling_desc; |
| struct regulator_dev **c_rdevs = c_desc->coupled_rdevs; |
| struct regulation_constraints *constraints = rdev->constraints; |
| - int max_spread = constraints->max_spread; |
| int desired_min_uV = 0, desired_max_uV = INT_MAX; |
| int max_current_uV = 0, min_current_uV = INT_MAX; |
| int highest_min_uV = 0, target_uV, possible_uV; |
| - int i, ret; |
| + int i, ret, max_spread; |
| bool done; |
| |
| *current_uV = -1; |
| @@ -3494,6 +3495,8 @@ static int regulator_get_optimal_voltage(struct regulator_dev *rdev, |
| } |
| } |
| |
| + max_spread = constraints->max_spread[0]; |
| + |
| /* |
| * Let target_uV be equal to the desired one if possible. |
| * If not, set it to minimum voltage, allowed by other coupled |
| @@ -3575,9 +3578,11 @@ static int regulator_balance_voltage(struct regulator_dev *rdev, |
| struct regulator_dev **c_rdevs; |
| struct regulator_dev *best_rdev; |
| struct coupling_desc *c_desc = &rdev->coupling_desc; |
| + struct regulator_coupler *coupler = c_desc->coupler; |
| int i, ret, n_coupled, best_min_uV, best_max_uV, best_c_rdev; |
| - bool best_c_rdev_done, c_rdev_done[MAX_COUPLED]; |
| unsigned int delta, best_delta; |
| + unsigned long c_rdev_done = 0; |
| + bool best_c_rdev_done; |
| |
| c_rdevs = c_desc->coupled_rdevs; |
| n_coupled = c_desc->n_coupled; |
| @@ -3594,8 +3599,9 @@ static int regulator_balance_voltage(struct regulator_dev *rdev, |
| return -EPERM; |
| } |
| |
| - for (i = 0; i < n_coupled; i++) |
| - c_rdev_done[i] = false; |
| + /* Invoke custom balancer for customized couplers */ |
| + if (coupler && coupler->balance_voltage) |
| + return coupler->balance_voltage(coupler, rdev, state); |
| |
| /* |
| * Find the best possible voltage change on each loop. Leave the loop |
| @@ -3622,7 +3628,7 @@ static int regulator_balance_voltage(struct regulator_dev *rdev, |
| */ |
| int optimal_uV = 0, optimal_max_uV = 0, current_uV = 0; |
| |
| - if (c_rdev_done[i]) |
| + if (test_bit(i, &c_rdev_done)) |
| continue; |
| |
| ret = regulator_get_optimal_voltage(c_rdevs[i], |
| @@ -3657,7 +3663,8 @@ static int regulator_balance_voltage(struct regulator_dev *rdev, |
| if (ret < 0) |
| goto out; |
| |
| - c_rdev_done[best_c_rdev] = best_c_rdev_done; |
| + if (best_c_rdev_done) |
| + set_bit(best_c_rdev, &c_rdev_done); |
| |
| } while (n_coupled > 1); |
| |
| @@ -4709,8 +4716,60 @@ static int regulator_register_resolve_supply(struct device *dev, void *data) |
| return 0; |
| } |
| |
| +int regulator_coupler_register(struct regulator_coupler *coupler) |
| +{ |
| + mutex_lock(®ulator_list_mutex); |
| + list_add_tail(&coupler->list, ®ulator_coupler_list); |
| + mutex_unlock(®ulator_list_mutex); |
| + |
| + return 0; |
| +} |
| + |
| +static struct regulator_coupler * |
| +regulator_find_coupler(struct regulator_dev *rdev) |
| +{ |
| + struct regulator_coupler *coupler; |
| + int err; |
| + |
| + /* |
| + * Note that regulators are appended to the list and the generic |
| + * coupler is registered first, hence it will be attached at last |
| + * if nobody cared. |
| + */ |
| + list_for_each_entry_reverse(coupler, ®ulator_coupler_list, list) { |
| + err = coupler->attach_regulator(coupler, rdev); |
| + if (!err) { |
| + if (!coupler->balance_voltage && |
| + rdev->coupling_desc.n_coupled > 2) |
| + goto err_unsupported; |
| + |
| + return coupler; |
| + } |
| + |
| + if (err < 0) |
| + return ERR_PTR(err); |
| + |
| + if (err == 1) |
| + continue; |
| + |
| + break; |
| + } |
| + |
| + return ERR_PTR(-EINVAL); |
| + |
| +err_unsupported: |
| + if (coupler->detach_regulator) |
| + coupler->detach_regulator(coupler, rdev); |
| + |
| + rdev_err(rdev, |
| + "Voltage balancing for multiple regulator couples is unimplemented\n"); |
| + |
| + return ERR_PTR(-EPERM); |
| +} |
| + |
| static void regulator_resolve_coupling(struct regulator_dev *rdev) |
| { |
| + struct regulator_coupler *coupler = rdev->coupling_desc.coupler; |
| struct coupling_desc *c_desc = &rdev->coupling_desc; |
| int n_coupled = c_desc->n_coupled; |
| struct regulator_dev *c_rdev; |
| @@ -4726,6 +4785,12 @@ static void regulator_resolve_coupling(struct regulator_dev *rdev) |
| if (!c_rdev) |
| continue; |
| |
| + if (c_rdev->coupling_desc.coupler != coupler) { |
| + rdev_err(rdev, "coupler mismatch with %s\n", |
| + rdev_get_name(c_rdev)); |
| + return; |
| + } |
| + |
| regulator_lock(c_rdev); |
| |
| c_desc->coupled_rdevs[i] = c_rdev; |
| @@ -4739,10 +4804,12 @@ static void regulator_resolve_coupling(struct regulator_dev *rdev) |
| |
| static void regulator_remove_coupling(struct regulator_dev *rdev) |
| { |
| + struct regulator_coupler *coupler = rdev->coupling_desc.coupler; |
| 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; |
| + int err; |
| |
| n_coupled = c_desc->n_coupled; |
| |
| @@ -4772,21 +4839,33 @@ static void regulator_remove_coupling(struct regulator_dev *rdev) |
| c_desc->coupled_rdevs[i] = NULL; |
| c_desc->n_resolved--; |
| } |
| + |
| + if (coupler && coupler->detach_regulator) { |
| + err = coupler->detach_regulator(coupler, rdev); |
| + if (err) |
| + rdev_err(rdev, "failed to detach from coupler: %d\n", |
| + err); |
| + } |
| + |
| + kfree(rdev->coupling_desc.coupled_rdevs); |
| + rdev->coupling_desc.coupled_rdevs = NULL; |
| } |
| |
| static int regulator_init_coupling(struct regulator_dev *rdev) |
| { |
| - int n_phandles; |
| + int err, n_phandles; |
| + size_t alloc_size; |
| |
| if (!IS_ENABLED(CONFIG_OF)) |
| n_phandles = 0; |
| else |
| n_phandles = of_get_n_coupled(rdev); |
| |
| - if (n_phandles + 1 > MAX_COUPLED) { |
| - rdev_err(rdev, "too many regulators coupled\n"); |
| - return -EPERM; |
| - } |
| + alloc_size = sizeof(*rdev) * (n_phandles + 1); |
| + |
| + rdev->coupling_desc.coupled_rdevs = kzalloc(alloc_size, GFP_KERNEL); |
| + if (!rdev->coupling_desc.coupled_rdevs) |
| + return -ENOMEM; |
| |
| /* |
| * Every regulator should always have coupling descriptor filled with |
| @@ -4800,23 +4879,35 @@ static int regulator_init_coupling(struct regulator_dev *rdev) |
| if (n_phandles == 0) |
| return 0; |
| |
| - /* regulator, which can't change its voltage, can't be coupled */ |
| - if (!regulator_ops_is_valid(rdev, REGULATOR_CHANGE_VOLTAGE)) { |
| - rdev_err(rdev, "voltage operation not allowed\n"); |
| + if (!of_check_coupling_data(rdev)) |
| return -EPERM; |
| - } |
| |
| - if (rdev->constraints->max_spread <= 0) { |
| - rdev_err(rdev, "wrong max_spread value\n"); |
| - return -EPERM; |
| + rdev->coupling_desc.coupler = regulator_find_coupler(rdev); |
| + if (IS_ERR(rdev->coupling_desc.coupler)) { |
| + err = PTR_ERR(rdev->coupling_desc.coupler); |
| + rdev_err(rdev, "failed to get coupler: %d\n", err); |
| + return err; |
| } |
| |
| - if (!of_check_coupling_data(rdev)) |
| + return 0; |
| +} |
| + |
| +static int generic_coupler_attach(struct regulator_coupler *coupler, |
| + struct regulator_dev *rdev) |
| +{ |
| + if (rdev->coupling_desc.n_coupled > 2) { |
| + rdev_err(rdev, |
| + "Voltage balancing for multiple regulator couples is unimplemented\n"); |
| return -EPERM; |
| + } |
| |
| return 0; |
| } |
| |
| +static struct regulator_coupler generic_regulator_coupler = { |
| + .attach_regulator = generic_coupler_attach, |
| +}; |
| + |
| /** |
| * regulator_register - register regulator |
| * @regulator_desc: regulator to register |
| @@ -4979,7 +5070,9 @@ regulator_register(const struct regulator_desc *regulator_desc, |
| if (ret < 0) |
| goto wash; |
| |
| + mutex_lock(®ulator_list_mutex); |
| ret = regulator_init_coupling(rdev); |
| + mutex_unlock(®ulator_list_mutex); |
| if (ret < 0) |
| goto wash; |
| |
| @@ -5028,6 +5121,7 @@ regulator_register(const struct regulator_desc *regulator_desc, |
| unset_supplies: |
| mutex_lock(®ulator_list_mutex); |
| unset_regulator_supplies(rdev); |
| + regulator_remove_coupling(rdev); |
| mutex_unlock(®ulator_list_mutex); |
| wash: |
| kfree(rdev->coupling_desc.coupled_rdevs); |
| @@ -5487,6 +5581,8 @@ static int __init regulator_init(void) |
| #endif |
| regulator_dummy_init(); |
| |
| + regulator_coupler_register(&generic_regulator_coupler); |
| + |
| return ret; |
| } |
| |
| diff --git a/drivers/regulator/of_regulator.c b/drivers/regulator/of_regulator.c |
| index 557ce6c9c024..921afd116f43 100644 |
| --- a/drivers/regulator/of_regulator.c |
| +++ b/drivers/regulator/of_regulator.c |
| @@ -21,7 +21,8 @@ static const char *const regulator_states[PM_SUSPEND_MAX + 1] = { |
| [PM_SUSPEND_MAX] = "regulator-state-disk", |
| }; |
| |
| -static void of_get_regulation_constraints(struct device_node *np, |
| +static int of_get_regulation_constraints(struct device *dev, |
| + struct device_node *np, |
| struct regulator_init_data **init_data, |
| const struct regulator_desc *desc) |
| { |
| @@ -30,8 +31,13 @@ static void of_get_regulation_constraints(struct device_node *np, |
| struct device_node *suspend_np; |
| unsigned int mode; |
| int ret, i, len; |
| + int n_phandles; |
| u32 pval; |
| |
| + n_phandles = of_count_phandle_with_args(np, "regulator-coupled-with", |
| + NULL); |
| + n_phandles = max(n_phandles, 0); |
| + |
| constraints->name = of_get_property(np, "regulator-name", NULL); |
| |
| if (!of_property_read_u32(np, "regulator-min-microvolt", &pval)) |
| @@ -163,9 +169,17 @@ static void of_get_regulation_constraints(struct device_node *np, |
| if (!of_property_read_u32(np, "regulator-system-load", &pval)) |
| constraints->system_load = pval; |
| |
| - if (!of_property_read_u32(np, "regulator-coupled-max-spread", |
| - &pval)) |
| - constraints->max_spread = pval; |
| + if (n_phandles) { |
| + constraints->max_spread = devm_kzalloc(dev, |
| + sizeof(*constraints->max_spread) * n_phandles, |
| + GFP_KERNEL); |
| + |
| + if (!constraints->max_spread) |
| + return -ENOMEM; |
| + |
| + of_property_read_u32_array(np, "regulator-coupled-max-spread", |
| + constraints->max_spread, n_phandles); |
| + } |
| |
| if (!of_property_read_u32(np, "regulator-max-step-microvolt", |
| &pval)) |
| @@ -242,6 +256,8 @@ static void of_get_regulation_constraints(struct device_node *np, |
| suspend_state = NULL; |
| suspend_np = NULL; |
| } |
| + |
| + return 0; |
| } |
| |
| /** |
| @@ -267,7 +283,9 @@ struct regulator_init_data *of_get_regulator_init_data(struct device *dev, |
| if (!init_data) |
| return NULL; /* Out of memory? */ |
| |
| - of_get_regulation_constraints(node, &init_data, desc); |
| + if (of_get_regulation_constraints(dev, node, &init_data, desc)) |
| + return NULL; |
| + |
| return init_data; |
| } |
| EXPORT_SYMBOL_GPL(of_get_regulator_init_data); |
| @@ -473,7 +491,8 @@ int of_get_n_coupled(struct regulator_dev *rdev) |
| |
| /* Looks for "to_find" device_node in src's "regulator-coupled-with" property */ |
| static bool of_coupling_find_node(struct device_node *src, |
| - struct device_node *to_find) |
| + struct device_node *to_find, |
| + int *index) |
| { |
| int n_phandles, i; |
| bool found = false; |
| @@ -495,8 +514,10 @@ static bool of_coupling_find_node(struct device_node *src, |
| |
| of_node_put(tmp); |
| |
| - if (found) |
| + if (found) { |
| + *index = i; |
| break; |
| + } |
| } |
| |
| return found; |
| @@ -517,22 +538,23 @@ static bool of_coupling_find_node(struct device_node *src, |
| */ |
| bool of_check_coupling_data(struct regulator_dev *rdev) |
| { |
| - int max_spread = rdev->constraints->max_spread; |
| struct device_node *node = rdev->dev.of_node; |
| int n_phandles = of_get_n_coupled(rdev); |
| struct device_node *c_node; |
| + int index; |
| int i; |
| bool ret = true; |
| |
| - if (max_spread <= 0) { |
| - dev_err(&rdev->dev, "max_spread value invalid\n"); |
| - return false; |
| - } |
| - |
| /* iterate over rdev's phandles */ |
| for (i = 0; i < n_phandles; i++) { |
| + int max_spread = rdev->constraints->max_spread[i]; |
| int c_max_spread, c_n_phandles; |
| |
| + if (max_spread <= 0) { |
| + dev_err(&rdev->dev, "max_spread value invalid\n"); |
| + return false; |
| + } |
| + |
| c_node = of_parse_phandle(node, |
| "regulator-coupled-with", i); |
| |
| @@ -549,22 +571,23 @@ bool of_check_coupling_data(struct regulator_dev *rdev) |
| goto clean; |
| } |
| |
| - if (of_property_read_u32(c_node, "regulator-coupled-max-spread", |
| - &c_max_spread)) { |
| + if (!of_coupling_find_node(c_node, node, &index)) { |
| + dev_err(&rdev->dev, "missing 2-way linking for coupled regulators\n"); |
| ret = false; |
| goto clean; |
| } |
| |
| - if (c_max_spread != max_spread) { |
| - dev_err(&rdev->dev, |
| - "coupled regulators max_spread mismatch\n"); |
| + if (of_property_read_u32_index(c_node, "regulator-coupled-max-spread", |
| + index, &c_max_spread)) { |
| ret = false; |
| goto clean; |
| } |
| |
| - if (!of_coupling_find_node(c_node, node)) { |
| - dev_err(&rdev->dev, "missing 2-way linking for coupled regulators\n"); |
| + if (c_max_spread != max_spread) { |
| + dev_err(&rdev->dev, |
| + "coupled regulators max_spread mismatch\n"); |
| ret = false; |
| + goto clean; |
| } |
| |
| clean: |
| diff --git a/include/linux/regulator/coupler.h b/include/linux/regulator/coupler.h |
| new file mode 100644 |
| index 000000000000..98dd1f74d605 |
| --- /dev/null |
| +++ b/include/linux/regulator/coupler.h |
| @@ -0,0 +1,62 @@ |
| +/* SPDX-License-Identifier: GPL-2.0 */ |
| +/* |
| + * coupler.h -- SoC Regulator support, coupler API. |
| + * |
| + * Regulator Coupler Interface. |
| + */ |
| + |
| +#ifndef __LINUX_REGULATOR_COUPLER_H_ |
| +#define __LINUX_REGULATOR_COUPLER_H_ |
| + |
| +#include <linux/kernel.h> |
| +#include <linux/suspend.h> |
| + |
| +struct regulator_coupler; |
| +struct regulator_dev; |
| + |
| +/** |
| + * struct regulator_coupler - customized regulator's coupler |
| + * |
| + * Regulator's coupler allows to customize coupling algorithm. |
| + * |
| + * @list: couplers list entry |
| + * @attach_regulator: Callback invoked on creation of a coupled regulator, |
| + * couples are unresolved at this point. The callee should |
| + * check that it could handle the regulator and return 0 on |
| + * success, -errno on failure and 1 if given regulator is |
| + * not suitable for this coupler (case of having multiple |
| + * regulators in a system). Callback shall be implemented. |
| + * @detach_regulator: Callback invoked on destruction of a coupled regulator. |
| + * This callback is optional and could be NULL. |
| + * @balance_voltage: Callback invoked when voltage of a coupled regulator is |
| + * changing. Called with all of the coupled rdev's being held |
| + * under "consumer lock". The callee should perform voltage |
| + * balancing, changing voltage of the coupled regulators as |
| + * needed. It's up to the coupler to verify the voltage |
| + * before changing it in hardware, i.e. coupler should |
| + * check consumer's min/max and etc. This callback is |
| + * optional and could be NULL, in which case a generic |
| + * voltage balancer will be used. |
| + */ |
| +struct regulator_coupler { |
| + struct list_head list; |
| + |
| + int (*attach_regulator)(struct regulator_coupler *coupler, |
| + struct regulator_dev *rdev); |
| + int (*detach_regulator)(struct regulator_coupler *coupler, |
| + struct regulator_dev *rdev); |
| + int (*balance_voltage)(struct regulator_coupler *coupler, |
| + struct regulator_dev *rdev, |
| + suspend_state_t state); |
| +}; |
| + |
| +#ifdef CONFIG_REGULATOR |
| +int regulator_coupler_register(struct regulator_coupler *coupler); |
| +#else |
| +static inline int regulator_coupler_register(struct regulator_coupler *coupler) |
| +{ |
| + return 0; |
| +} |
| +#endif |
| + |
| +#endif |
| diff --git a/include/linux/regulator/driver.h b/include/linux/regulator/driver.h |
| index d45ab52c91c9..64ef7514f62d 100644 |
| --- a/include/linux/regulator/driver.h |
| +++ b/include/linux/regulator/driver.h |
| @@ -12,8 +12,6 @@ |
| #ifndef __LINUX_REGULATOR_DRIVER_H_ |
| #define __LINUX_REGULATOR_DRIVER_H_ |
| |
| -#define MAX_COUPLED 2 |
| - |
| #include <linux/device.h> |
| #include <linux/notifier.h> |
| #include <linux/regulator/consumer.h> |
| @@ -423,7 +421,8 @@ struct regulator_config { |
| * incremented. |
| */ |
| struct coupling_desc { |
| - struct regulator_dev *coupled_rdevs[MAX_COUPLED]; |
| + struct regulator_dev **coupled_rdevs; |
| + struct regulator_coupler *coupler; |
| int n_resolved; |
| int n_coupled; |
| }; |
| @@ -549,4 +548,5 @@ void regulator_unlock(struct regulator_dev *rdev); |
| */ |
| int regulator_desc_list_voltage_linear_range(const struct regulator_desc *desc, |
| unsigned int selector); |
| + |
| #endif |
| diff --git a/include/linux/regulator/machine.h b/include/linux/regulator/machine.h |
| index 5539efa76d26..a84cc8879c3e 100644 |
| --- a/include/linux/regulator/machine.h |
| +++ b/include/linux/regulator/machine.h |
| @@ -153,7 +153,7 @@ struct regulation_constraints { |
| int system_load; |
| |
| /* used for coupled regulators */ |
| - int max_spread; |
| + u32 *max_spread; |
| |
| /* used for changing voltage in steps */ |
| int max_uV_step; |
| -- |
| 2.7.4 |
| |