Unverified Commit d8ca7d18 authored by Dmitry Osipenko's avatar Dmitry Osipenko Committed by Mark Brown
Browse files

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: default avatarDmitry Osipenko <digetx@gmail.com>
Signed-off-by: default avatarMark Brown <broonie@kernel.org>
parent a188339c
Loading
Loading
Loading
Loading
+116 −20
Original line number Diff line number Diff line
@@ -28,6 +28,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>
@@ -55,6 +56,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;
@@ -3439,11 +3441,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;
@@ -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.
	 * 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 *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;
@@ -3597,8 +3602,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
@@ -3625,7 +3631,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],
@@ -3660,7 +3666,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);

@@ -4712,8 +4719,60 @@ static int regulator_register_resolve_supply(struct device *dev, void *data)
	return 0;
}

int regulator_coupler_register(struct regulator_coupler *coupler)
{
	mutex_lock(&regulator_list_mutex);
	list_add_tail(&coupler->list, &regulator_coupler_list);
	mutex_unlock(&regulator_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, &regulator_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;
@@ -4729,6 +4788,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;
@@ -4742,10 +4807,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;

@@ -4775,21 +4842,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
@@ -4803,23 +4882,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;

	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 (rdev->constraints->max_spread <= 0) {
		rdev_err(rdev, "wrong max_spread value\n");
		return -EPERM;
	return 0;
}

	if (!of_check_coupling_data(rdev))
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
@@ -4981,7 +5072,9 @@ regulator_register(const struct regulator_desc *regulator_desc,
	if (ret < 0)
		goto wash;

	mutex_lock(&regulator_list_mutex);
	ret = regulator_init_coupling(rdev);
	mutex_unlock(&regulator_list_mutex);
	if (ret < 0)
		goto wash;

@@ -5030,6 +5123,7 @@ regulator_register(const struct regulator_desc *regulator_desc,
unset_supplies:
	mutex_lock(&regulator_list_mutex);
	unset_regulator_supplies(rdev);
	regulator_remove_coupling(rdev);
	mutex_unlock(&regulator_list_mutex);
wash:
	kfree(rdev->constraints);
@@ -5485,6 +5579,8 @@ static int __init regulator_init(void)
#endif
	regulator_dummy_init();

	regulator_coupler_register(&generic_regulator_coupler);

	return ret;
}

+43 −20
Original line number Diff line number Diff line
@@ -25,7 +25,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)
{
@@ -34,8 +35,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))
@@ -167,9 +173,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))
@@ -246,6 +260,8 @@ static void of_get_regulation_constraints(struct device_node *np,
		suspend_state = 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)
		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);
@@ -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 */
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;
@@ -499,9 +518,11 @@ static bool of_coupling_find_node(struct device_node *src,

		of_node_put(tmp);

		if (found)
		if (found) {
			*index = i;
			break;
		}
	}

	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)
{
	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;

	/* 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;
		}

	/* iterate over rdev's phandles */
	for (i = 0; i < n_phandles; i++) {
		int c_max_spread, c_n_phandles;

		c_node = of_parse_phandle(node,
					  "regulator-coupled-with", i);

@@ -553,22 +575,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:
+62 −0
Original line number Diff line number Diff line
/* 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
+3 −3
Original line number Diff line number Diff line
@@ -15,8 +15,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>
@@ -426,7 +424,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;
};
@@ -552,4 +551,5 @@ void regulator_unlock(struct regulator_dev *rdev);
 */
int regulator_desc_list_voltage_linear_range(const struct regulator_desc *desc,
					     unsigned int selector);

#endif
+1 −1
Original line number Diff line number Diff line
@@ -156,7 +156,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;