regulator: core: Introduce API for regulators coupling customization
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>
This commit is contained in:
parent
a188339ca5
commit
d8ca7d184b
5 changed files with 228 additions and 47 deletions
|
@ -28,6 +28,7 @@
|
||||||
#include <linux/regmap.h>
|
#include <linux/regmap.h>
|
||||||
#include <linux/regulator/of_regulator.h>
|
#include <linux/regulator/of_regulator.h>
|
||||||
#include <linux/regulator/consumer.h>
|
#include <linux/regulator/consumer.h>
|
||||||
|
#include <linux/regulator/coupler.h>
|
||||||
#include <linux/regulator/driver.h>
|
#include <linux/regulator/driver.h>
|
||||||
#include <linux/regulator/machine.h>
|
#include <linux/regulator/machine.h>
|
||||||
#include <linux/module.h>
|
#include <linux/module.h>
|
||||||
|
@ -55,6 +56,7 @@ static DEFINE_MUTEX(regulator_list_mutex);
|
||||||
static LIST_HEAD(regulator_map_list);
|
static LIST_HEAD(regulator_map_list);
|
||||||
static LIST_HEAD(regulator_ena_gpio_list);
|
static LIST_HEAD(regulator_ena_gpio_list);
|
||||||
static LIST_HEAD(regulator_supply_alias_list);
|
static LIST_HEAD(regulator_supply_alias_list);
|
||||||
|
static LIST_HEAD(regulator_coupler_list);
|
||||||
static bool has_full_constraints;
|
static bool has_full_constraints;
|
||||||
|
|
||||||
static struct dentry *debugfs_root;
|
static struct dentry *debugfs_root;
|
||||||
|
@ -3439,11 +3441,10 @@ static int regulator_get_optimal_voltage(struct regulator_dev *rdev,
|
||||||
struct coupling_desc *c_desc = &rdev->coupling_desc;
|
struct coupling_desc *c_desc = &rdev->coupling_desc;
|
||||||
struct regulator_dev **c_rdevs = c_desc->coupled_rdevs;
|
struct regulator_dev **c_rdevs = c_desc->coupled_rdevs;
|
||||||
struct regulation_constraints *constraints = rdev->constraints;
|
struct regulation_constraints *constraints = rdev->constraints;
|
||||||
int max_spread = constraints->max_spread;
|
|
||||||
int desired_min_uV = 0, desired_max_uV = INT_MAX;
|
int desired_min_uV = 0, desired_max_uV = INT_MAX;
|
||||||
int max_current_uV = 0, min_current_uV = INT_MAX;
|
int max_current_uV = 0, min_current_uV = INT_MAX;
|
||||||
int highest_min_uV = 0, target_uV, possible_uV;
|
int highest_min_uV = 0, target_uV, possible_uV;
|
||||||
int i, ret;
|
int i, ret, max_spread;
|
||||||
bool done;
|
bool done;
|
||||||
|
|
||||||
*current_uV = -1;
|
*current_uV = -1;
|
||||||
|
@ -3497,6 +3498,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.
|
* Let target_uV be equal to the desired one if possible.
|
||||||
* If not, set it to minimum voltage, allowed by other coupled
|
* If not, set it to minimum voltage, allowed by other coupled
|
||||||
|
@ -3578,9 +3581,11 @@ static int regulator_balance_voltage(struct regulator_dev *rdev,
|
||||||
struct regulator_dev **c_rdevs;
|
struct regulator_dev **c_rdevs;
|
||||||
struct regulator_dev *best_rdev;
|
struct regulator_dev *best_rdev;
|
||||||
struct coupling_desc *c_desc = &rdev->coupling_desc;
|
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;
|
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 int delta, best_delta;
|
||||||
|
unsigned long c_rdev_done = 0;
|
||||||
|
bool best_c_rdev_done;
|
||||||
|
|
||||||
c_rdevs = c_desc->coupled_rdevs;
|
c_rdevs = c_desc->coupled_rdevs;
|
||||||
n_coupled = c_desc->n_coupled;
|
n_coupled = c_desc->n_coupled;
|
||||||
|
@ -3597,8 +3602,9 @@ static int regulator_balance_voltage(struct regulator_dev *rdev,
|
||||||
return -EPERM;
|
return -EPERM;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (i = 0; i < n_coupled; i++)
|
/* Invoke custom balancer for customized couplers */
|
||||||
c_rdev_done[i] = false;
|
if (coupler && coupler->balance_voltage)
|
||||||
|
return coupler->balance_voltage(coupler, rdev, state);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Find the best possible voltage change on each loop. Leave the loop
|
* Find the best possible voltage change on each loop. Leave the loop
|
||||||
|
@ -3625,7 +3631,7 @@ static int regulator_balance_voltage(struct regulator_dev *rdev,
|
||||||
*/
|
*/
|
||||||
int optimal_uV = 0, optimal_max_uV = 0, current_uV = 0;
|
int optimal_uV = 0, optimal_max_uV = 0, current_uV = 0;
|
||||||
|
|
||||||
if (c_rdev_done[i])
|
if (test_bit(i, &c_rdev_done))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
ret = regulator_get_optimal_voltage(c_rdevs[i],
|
ret = regulator_get_optimal_voltage(c_rdevs[i],
|
||||||
|
@ -3660,7 +3666,8 @@ static int regulator_balance_voltage(struct regulator_dev *rdev,
|
||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
goto out;
|
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);
|
} while (n_coupled > 1);
|
||||||
|
|
||||||
|
@ -4712,8 +4719,60 @@ static int regulator_register_resolve_supply(struct device *dev, void *data)
|
||||||
return 0;
|
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)
|
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;
|
struct coupling_desc *c_desc = &rdev->coupling_desc;
|
||||||
int n_coupled = c_desc->n_coupled;
|
int n_coupled = c_desc->n_coupled;
|
||||||
struct regulator_dev *c_rdev;
|
struct regulator_dev *c_rdev;
|
||||||
|
@ -4729,6 +4788,12 @@ static void regulator_resolve_coupling(struct regulator_dev *rdev)
|
||||||
if (!c_rdev)
|
if (!c_rdev)
|
||||||
continue;
|
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);
|
regulator_lock(c_rdev);
|
||||||
|
|
||||||
c_desc->coupled_rdevs[i] = c_rdev;
|
c_desc->coupled_rdevs[i] = c_rdev;
|
||||||
|
@ -4742,10 +4807,12 @@ static void regulator_resolve_coupling(struct regulator_dev *rdev)
|
||||||
|
|
||||||
static void regulator_remove_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 coupling_desc *__c_desc, *c_desc = &rdev->coupling_desc;
|
||||||
struct regulator_dev *__c_rdev, *c_rdev;
|
struct regulator_dev *__c_rdev, *c_rdev;
|
||||||
unsigned int __n_coupled, n_coupled;
|
unsigned int __n_coupled, n_coupled;
|
||||||
int i, k;
|
int i, k;
|
||||||
|
int err;
|
||||||
|
|
||||||
n_coupled = c_desc->n_coupled;
|
n_coupled = c_desc->n_coupled;
|
||||||
|
|
||||||
|
@ -4775,21 +4842,33 @@ static void regulator_remove_coupling(struct regulator_dev *rdev)
|
||||||
c_desc->coupled_rdevs[i] = NULL;
|
c_desc->coupled_rdevs[i] = NULL;
|
||||||
c_desc->n_resolved--;
|
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)
|
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))
|
if (!IS_ENABLED(CONFIG_OF))
|
||||||
n_phandles = 0;
|
n_phandles = 0;
|
||||||
else
|
else
|
||||||
n_phandles = of_get_n_coupled(rdev);
|
n_phandles = of_get_n_coupled(rdev);
|
||||||
|
|
||||||
if (n_phandles + 1 > MAX_COUPLED) {
|
alloc_size = sizeof(*rdev) * (n_phandles + 1);
|
||||||
rdev_err(rdev, "too many regulators coupled\n");
|
|
||||||
return -EPERM;
|
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
|
* Every regulator should always have coupling descriptor filled with
|
||||||
|
@ -4803,23 +4882,35 @@ static int regulator_init_coupling(struct regulator_dev *rdev)
|
||||||
if (n_phandles == 0)
|
if (n_phandles == 0)
|
||||||
return 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");
|
|
||||||
return -EPERM;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (rdev->constraints->max_spread <= 0) {
|
|
||||||
rdev_err(rdev, "wrong max_spread value\n");
|
|
||||||
return -EPERM;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!of_check_coupling_data(rdev))
|
if (!of_check_coupling_data(rdev))
|
||||||
return -EPERM;
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
return 0;
|
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_register - register regulator
|
||||||
* @regulator_desc: regulator to register
|
* @regulator_desc: regulator to register
|
||||||
|
@ -4981,7 +5072,9 @@ regulator_register(const struct regulator_desc *regulator_desc,
|
||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
goto wash;
|
goto wash;
|
||||||
|
|
||||||
|
mutex_lock(®ulator_list_mutex);
|
||||||
ret = regulator_init_coupling(rdev);
|
ret = regulator_init_coupling(rdev);
|
||||||
|
mutex_unlock(®ulator_list_mutex);
|
||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
goto wash;
|
goto wash;
|
||||||
|
|
||||||
|
@ -5030,6 +5123,7 @@ regulator_register(const struct regulator_desc *regulator_desc,
|
||||||
unset_supplies:
|
unset_supplies:
|
||||||
mutex_lock(®ulator_list_mutex);
|
mutex_lock(®ulator_list_mutex);
|
||||||
unset_regulator_supplies(rdev);
|
unset_regulator_supplies(rdev);
|
||||||
|
regulator_remove_coupling(rdev);
|
||||||
mutex_unlock(®ulator_list_mutex);
|
mutex_unlock(®ulator_list_mutex);
|
||||||
wash:
|
wash:
|
||||||
kfree(rdev->constraints);
|
kfree(rdev->constraints);
|
||||||
|
@ -5485,6 +5579,8 @@ static int __init regulator_init(void)
|
||||||
#endif
|
#endif
|
||||||
regulator_dummy_init();
|
regulator_dummy_init();
|
||||||
|
|
||||||
|
regulator_coupler_register(&generic_regulator_coupler);
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -25,7 +25,8 @@ static const char *const regulator_states[PM_SUSPEND_MAX + 1] = {
|
||||||
[PM_SUSPEND_MAX] = "regulator-state-disk",
|
[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,
|
struct regulator_init_data **init_data,
|
||||||
const struct regulator_desc *desc)
|
const struct regulator_desc *desc)
|
||||||
{
|
{
|
||||||
|
@ -34,8 +35,13 @@ static void of_get_regulation_constraints(struct device_node *np,
|
||||||
struct device_node *suspend_np;
|
struct device_node *suspend_np;
|
||||||
unsigned int mode;
|
unsigned int mode;
|
||||||
int ret, i, len;
|
int ret, i, len;
|
||||||
|
int n_phandles;
|
||||||
u32 pval;
|
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);
|
constraints->name = of_get_property(np, "regulator-name", NULL);
|
||||||
|
|
||||||
if (!of_property_read_u32(np, "regulator-min-microvolt", &pval))
|
if (!of_property_read_u32(np, "regulator-min-microvolt", &pval))
|
||||||
|
@ -167,9 +173,17 @@ static void of_get_regulation_constraints(struct device_node *np,
|
||||||
if (!of_property_read_u32(np, "regulator-system-load", &pval))
|
if (!of_property_read_u32(np, "regulator-system-load", &pval))
|
||||||
constraints->system_load = pval;
|
constraints->system_load = pval;
|
||||||
|
|
||||||
if (!of_property_read_u32(np, "regulator-coupled-max-spread",
|
if (n_phandles) {
|
||||||
&pval))
|
constraints->max_spread = devm_kzalloc(dev,
|
||||||
constraints->max_spread = pval;
|
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",
|
if (!of_property_read_u32(np, "regulator-max-step-microvolt",
|
||||||
&pval))
|
&pval))
|
||||||
|
@ -246,6 +260,8 @@ static void of_get_regulation_constraints(struct device_node *np,
|
||||||
suspend_state = NULL;
|
suspend_state = NULL;
|
||||||
suspend_np = NULL;
|
suspend_np = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -271,7 +287,9 @@ struct regulator_init_data *of_get_regulator_init_data(struct device *dev,
|
||||||
if (!init_data)
|
if (!init_data)
|
||||||
return NULL; /* Out of memory? */
|
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;
|
return init_data;
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL_GPL(of_get_regulator_init_data);
|
EXPORT_SYMBOL_GPL(of_get_regulator_init_data);
|
||||||
|
@ -477,7 +495,8 @@ int of_get_n_coupled(struct regulator_dev *rdev)
|
||||||
|
|
||||||
/* Looks for "to_find" device_node in src's "regulator-coupled-with" property */
|
/* Looks for "to_find" device_node in src's "regulator-coupled-with" property */
|
||||||
static bool of_coupling_find_node(struct device_node *src,
|
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;
|
int n_phandles, i;
|
||||||
bool found = false;
|
bool found = false;
|
||||||
|
@ -499,8 +518,10 @@ static bool of_coupling_find_node(struct device_node *src,
|
||||||
|
|
||||||
of_node_put(tmp);
|
of_node_put(tmp);
|
||||||
|
|
||||||
if (found)
|
if (found) {
|
||||||
|
*index = i;
|
||||||
break;
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return found;
|
return found;
|
||||||
|
@ -521,22 +542,23 @@ static bool of_coupling_find_node(struct device_node *src,
|
||||||
*/
|
*/
|
||||||
bool of_check_coupling_data(struct regulator_dev *rdev)
|
bool of_check_coupling_data(struct regulator_dev *rdev)
|
||||||
{
|
{
|
||||||
int max_spread = rdev->constraints->max_spread;
|
|
||||||
struct device_node *node = rdev->dev.of_node;
|
struct device_node *node = rdev->dev.of_node;
|
||||||
int n_phandles = of_get_n_coupled(rdev);
|
int n_phandles = of_get_n_coupled(rdev);
|
||||||
struct device_node *c_node;
|
struct device_node *c_node;
|
||||||
|
int index;
|
||||||
int i;
|
int i;
|
||||||
bool ret = true;
|
bool ret = true;
|
||||||
|
|
||||||
if (max_spread <= 0) {
|
|
||||||
dev_err(&rdev->dev, "max_spread value invalid\n");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* iterate over rdev's phandles */
|
/* iterate over rdev's phandles */
|
||||||
for (i = 0; i < n_phandles; i++) {
|
for (i = 0; i < n_phandles; i++) {
|
||||||
|
int max_spread = rdev->constraints->max_spread[i];
|
||||||
int c_max_spread, c_n_phandles;
|
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,
|
c_node = of_parse_phandle(node,
|
||||||
"regulator-coupled-with", i);
|
"regulator-coupled-with", i);
|
||||||
|
|
||||||
|
@ -553,8 +575,14 @@ bool of_check_coupling_data(struct regulator_dev *rdev)
|
||||||
goto clean;
|
goto clean;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (of_property_read_u32(c_node, "regulator-coupled-max-spread",
|
if (!of_coupling_find_node(c_node, node, &index)) {
|
||||||
&c_max_spread)) {
|
dev_err(&rdev->dev, "missing 2-way linking for coupled regulators\n");
|
||||||
|
ret = false;
|
||||||
|
goto clean;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (of_property_read_u32_index(c_node, "regulator-coupled-max-spread",
|
||||||
|
index, &c_max_spread)) {
|
||||||
ret = false;
|
ret = false;
|
||||||
goto clean;
|
goto clean;
|
||||||
}
|
}
|
||||||
|
@ -566,11 +594,6 @@ bool of_check_coupling_data(struct regulator_dev *rdev)
|
||||||
goto clean;
|
goto clean;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!of_coupling_find_node(c_node, node)) {
|
|
||||||
dev_err(&rdev->dev, "missing 2-way linking for coupled regulators\n");
|
|
||||||
ret = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
clean:
|
clean:
|
||||||
of_node_put(c_node);
|
of_node_put(c_node);
|
||||||
if (!ret)
|
if (!ret)
|
||||||
|
|
62
include/linux/regulator/coupler.h
Normal file
62
include/linux/regulator/coupler.h
Normal file
|
@ -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
|
|
@ -15,8 +15,6 @@
|
||||||
#ifndef __LINUX_REGULATOR_DRIVER_H_
|
#ifndef __LINUX_REGULATOR_DRIVER_H_
|
||||||
#define __LINUX_REGULATOR_DRIVER_H_
|
#define __LINUX_REGULATOR_DRIVER_H_
|
||||||
|
|
||||||
#define MAX_COUPLED 2
|
|
||||||
|
|
||||||
#include <linux/device.h>
|
#include <linux/device.h>
|
||||||
#include <linux/notifier.h>
|
#include <linux/notifier.h>
|
||||||
#include <linux/regulator/consumer.h>
|
#include <linux/regulator/consumer.h>
|
||||||
|
@ -426,7 +424,8 @@ struct regulator_config {
|
||||||
* incremented.
|
* incremented.
|
||||||
*/
|
*/
|
||||||
struct coupling_desc {
|
struct coupling_desc {
|
||||||
struct regulator_dev *coupled_rdevs[MAX_COUPLED];
|
struct regulator_dev **coupled_rdevs;
|
||||||
|
struct regulator_coupler *coupler;
|
||||||
int n_resolved;
|
int n_resolved;
|
||||||
int n_coupled;
|
int n_coupled;
|
||||||
};
|
};
|
||||||
|
@ -552,4 +551,5 @@ void regulator_unlock(struct regulator_dev *rdev);
|
||||||
*/
|
*/
|
||||||
int regulator_desc_list_voltage_linear_range(const struct regulator_desc *desc,
|
int regulator_desc_list_voltage_linear_range(const struct regulator_desc *desc,
|
||||||
unsigned int selector);
|
unsigned int selector);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -156,7 +156,7 @@ struct regulation_constraints {
|
||||||
int system_load;
|
int system_load;
|
||||||
|
|
||||||
/* used for coupled regulators */
|
/* used for coupled regulators */
|
||||||
int max_spread;
|
u32 *max_spread;
|
||||||
|
|
||||||
/* used for changing voltage in steps */
|
/* used for changing voltage in steps */
|
||||||
int max_uV_step;
|
int max_uV_step;
|
||||||
|
|
Loading…
Add table
Reference in a new issue