Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Unverified Commit a085a31a authored by Maciej Purski's avatar Maciej Purski Committed by Mark Brown
Browse files

regulator: core: Parse coupled regulators properties



On Odroid XU3/4 and other Exynos5422 based boards there is a case, that
different devices on the board are supplied by different regulators
with non-fixed voltages. If one of these devices temporarily requires
higher voltage, there might occur a situation that the spread between
devices' voltages is so high, that there is a risk of changing
'high' and 'low' states on the interconnection between devices powered
by those regulators.

Add new structure "coupling_desc" to regulator_dev, which contains
pointers to all coupled regulators including the owner of the structure,
number of coupled regulators and counter of currently resolved
regulators.

Add of_functions to parse all data needed in regulator coupling.
Provide method to check DTS data consistency. Check if each coupled
regulator's max_spread is equal and if their lists of regulators match.

Signed-off-by: default avatarMaciej Purski <m.purski@samsung.com>
Signed-off-by: default avatarMark Brown <broonie@kernel.org>
parent f98618b3
Loading
Loading
Loading
Loading
+26 −2
Original line number Diff line number Diff line
@@ -62,6 +62,14 @@ struct regulator_init_data *regulator_of_get_init_data(struct device *dev,
			         const struct regulator_desc *desc,
				 struct regulator_config *config,
				 struct device_node **node);

struct regulator_dev *of_parse_coupled_regulator(struct regulator_dev *rdev,
						 int index);

int of_get_n_coupled(struct regulator_dev *rdev);

bool of_check_coupling_data(struct regulator_dev *rdev);

#else
static inline struct regulator_dev *
of_find_regulator_by_node(struct device_node *np)
@@ -77,8 +85,25 @@ regulator_of_get_init_data(struct device *dev,
{
	return NULL;
}
#endif

static inline struct regulator_dev *
of_parse_coupled_regulator(struct regulator_dev *rdev,
			   int index)
{
	return NULL;
}

static inline int of_get_n_coupled(struct regulator_dev *rdev)
{
	return 0;
}

static inline bool of_check_coupling_data(struct regulator_dev *rdev)
{
	return false;
}

#endif
enum regulator_get_type {
	NORMAL_GET,
	EXCLUSIVE_GET,
@@ -88,5 +113,4 @@ enum regulator_get_type {

struct regulator *_regulator_get(struct device *dev, const char *id,
				 enum regulator_get_type get_type);

#endif
+151 −0
Original line number Diff line number Diff line
@@ -166,6 +166,10 @@ 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;

	constraints->over_current_protection = of_property_read_bool(np,
					"regulator-over-current-protection");

@@ -435,3 +439,150 @@ struct regulator_dev *of_find_regulator_by_node(struct device_node *np)

	return dev ? dev_to_rdev(dev) : NULL;
}

/*
 * Returns number of regulators coupled with rdev.
 */
int of_get_n_coupled(struct regulator_dev *rdev)
{
	struct device_node *node = rdev->dev.of_node;
	int n_phandles;

	n_phandles = of_count_phandle_with_args(node,
						"regulator-coupled-with",
						NULL);

	return (n_phandles > 0) ? n_phandles : 0;
}

/* 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)
{
	int n_phandles, i;
	bool found = false;

	n_phandles = of_count_phandle_with_args(src,
						"regulator-coupled-with",
						NULL);

	for (i = 0; i < n_phandles; i++) {
		struct device_node *tmp = of_parse_phandle(src,
					   "regulator-coupled-with", i);

		if (!tmp)
			break;

		/* found */
		if (tmp == to_find)
			found = true;

		of_node_put(tmp);

		if (found)
			break;
	}

	return found;
}

/**
 * of_check_coupling_data - Parse rdev's coupling properties and check data
 *			    consistency
 * @rdev - pointer to regulator_dev whose data is checked
 *
 * Function checks if all the following conditions are met:
 * - rdev's max_spread is greater than 0
 * - all coupled regulators have the same max_spread
 * - all coupled regulators have the same number of regulator_dev phandles
 * - all regulators are linked to each other
 *
 * Returns true if all conditions are met.
 */
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 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 c_max_spread, c_n_phandles;

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

		if (!c_node)
			ret = false;

		c_n_phandles = of_count_phandle_with_args(c_node,
							  "regulator-coupled-with",
							  NULL);

		if (c_n_phandles != n_phandles) {
			dev_err(&rdev->dev, "number of couped reg phandles mismatch\n");
			ret = false;
			goto clean;
		}

		if (of_property_read_u32(c_node, "regulator-coupled-max-spread",
					 &c_max_spread)) {
			ret = false;
			goto clean;
		}

		if (c_max_spread != max_spread) {
			dev_err(&rdev->dev,
				"coupled regulators max_spread mismatch\n");
			ret = false;
			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:
		of_node_put(c_node);
		if (!ret)
			break;
	}

	return ret;
}

/**
 * of_parse_coupled regulator - Get regulator_dev pointer from rdev's property
 * @rdev: Pointer to regulator_dev, whose DTS is used as a source to parse
 *	  "regulator-coupled-with" property
 * @index: Index in phandles array
 *
 * Returns the regulator_dev pointer parsed from DTS. If it has not been yet
 * registered, returns NULL
 */
struct regulator_dev *of_parse_coupled_regulator(struct regulator_dev *rdev,
						 int index)
{
	struct device_node *node = rdev->dev.of_node;
	struct device_node *c_node;
	struct regulator_dev *c_rdev;

	c_node = of_parse_phandle(node, "regulator-coupled-with", index);
	if (!c_node)
		return NULL;

	c_rdev = of_find_regulator_by_node(c_node);

	of_node_put(c_node);

	return c_rdev;
}
+18 −0
Original line number Diff line number Diff line
@@ -15,6 +15,8 @@
#ifndef __LINUX_REGULATOR_DRIVER_H_
#define __LINUX_REGULATOR_DRIVER_H_

#define MAX_COUPLED		4

#include <linux/device.h>
#include <linux/notifier.h>
#include <linux/regulator/consumer.h>
@@ -409,6 +411,20 @@ struct regulator_config {
	unsigned int ena_gpio_flags;
};

/*
 * struct coupling_desc
 *
 * Describes coupling of regulators. Each regulator should have
 * at least a pointer to itself in coupled_rdevs array.
 * When a new coupled regulator is resolved, n_resolved is
 * incremented.
 */
struct coupling_desc {
	struct regulator_dev *coupled_rdevs[MAX_COUPLED];
	int n_resolved;
	int n_coupled;
};

/*
 * struct regulator_dev
 *
@@ -432,6 +448,8 @@ struct regulator_dev {
	/* lists we own */
	struct list_head consumer_list; /* consumers we supply */

	struct coupling_desc coupling_desc;

	struct blocking_notifier_head notifier;
	struct mutex mutex; /* consumer lock */
	struct task_struct *mutex_owner;
+4 −0
Original line number Diff line number Diff line
@@ -103,6 +103,7 @@ struct regulator_state {
 * @ilim_uA: Maximum input current.
 * @system_load: Load that isn't captured by any consumer requests.
 *
 * @max_spread: Max possible spread between coupled regulators
 * @valid_modes_mask: Mask of modes which may be configured by consumers.
 * @valid_ops_mask: Operations which may be performed by consumers.
 *
@@ -154,6 +155,9 @@ struct regulation_constraints {

	int system_load;

	/* used for coupled regulators */
	int max_spread;

	/* valid regulator operating modes for this machine */
	unsigned int valid_modes_mask;