All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH v2 0/8] Introduce customized regulators coupling
@ 2019-06-03 23:58 Dmitry Osipenko
  2019-06-03 23:58 ` [PATCH v2 1/8] regulator: core: Introduce API for regulators coupling customization Dmitry Osipenko
                   ` (7 more replies)
  0 siblings, 8 replies; 21+ messages in thread
From: Dmitry Osipenko @ 2019-06-03 23:58 UTC (permalink / raw)
  To: Mark Brown, Thierry Reding, Jonathan Hunter, Peter De Schrijver,
	Rob Herring
  Cc: devicetree, linux-tegra, linux-kernel

Hello,

This series introduces a way of specifying a customized regulators coupler
which is necessary for cases like a non-trivial DVFS implementation. For
now I'm primarily targeting the CPUFreq driver of NVIDIA Tegra20 and Tegra30
SoC's to get into a better shape, such that things like CPU voltage scaling
could be supported. Both these SoC's have voltage-coupled regulators, one of
the coupled regulators powers CPU and other(s) power SoC peripherals. CPU and
each of the SoC's peripherals have it's own demand for a minimal voltage
(which basically depends on the clock rate), hence regulators voltage shall
not get lower than the minimum value required by one of peripherals (or CPU).
Right now none of peripheral drivers support voltage scaling in the upstream
kernel and voltages are statically specified in board device-trees via
minimum voltage values of the regulators. In order to implement a full-featured
DVFS, all drivers should gain support for voltage scaling and then there should
be some solution for having disabled drivers and hardware that is left in
enabled state by bootloader. That is not an easy problem to solve, so I'm
trying to start easy by getting some basics to work at first.

NVIDIA Tegra20 SoC's have a quite straight-forward voltage coupling between 3
regulators and the customized coupler is needed to address the missing
support of a full-featured system-wide DVFS, support for coupling of more than
2 regulators and support for a "min-spread" voltage. Probably it should be
possible to switch to a generic coupler later on, but for now it will be
much easier to start with a custom coupler that has all necessary features
in a simplified form.

NVIDIA Tegra30 SoC's have a bit more complicated coupling rules due to
variable dependency between the regulators (min-spread value depends on
a voltage of one of the coupled regulators).

This series has been tested on multiple devices by different people without
any known issues. CPUFreq voltage scaling works perfectly well with it and
voltage of peripherals is maintained at a good level. In a result thermal
sensors show that SoC package is a less warm by few degrees during of CPU
idling.

Changelog:

v2: The coupler's registration is now done in a more generic fashion and
    allow multiple couplers to be registered in a system.

    Added device-tree binding document for NVIDIA Tegra20/30 SoC's that
    describes hardware specifics of these SoC's in regards to regulators
    voltage coupling. In a result coupled regulators that are dedicated to
    SoC could be distinguished from each other, which in turns is also useful
    for the customized couplers implementation.

    The customized couplers got some more polish and now have a bit more
    stricter checkings for coupling rules violation.

The initial v1 of this series could be found here:

	https://lore.kernel.org/patchwork/project/lkml/list/?series=390439

This series, along with CPUFreq and other "in-progress" patches, could be
found here:

	https://github.com/grate-driver/linux/commits/master

Dmitry Osipenko (8):
  regulator: core: Introduce API for regulators coupling customization
  regulator: core: Parse max-spread value per regulator couple
  regulator: core: Expose some of core functions
  regulator: core Bump MAX_COUPLED to 3
  dt-bindings: regulator: Document regulators coupling of NVIDIA
    Tegra20/30 SoC's
  regulator: core: Don't attach generic coupler to Tegra SoC regulators
  soc/tegra: regulators: Add regulators coupler for Tegra20
  soc/tegra: regulators: Add regulators coupler for Tegra30

 .../nvidia,tegra-regulators-coupling.txt      |  65 ++++
 drivers/regulator/core.c                      | 143 +++++--
 drivers/regulator/of_regulator.c              |  49 ++-
 drivers/soc/tegra/Kconfig                     |  12 +
 drivers/soc/tegra/Makefile                    |   2 +
 drivers/soc/tegra/regulators-tegra20.c        | 348 ++++++++++++++++++
 drivers/soc/tegra/regulators-tegra30.c        | 300 +++++++++++++++
 include/linux/regulator/driver.h              |  46 ++-
 include/linux/regulator/machine.h             |   3 +-
 9 files changed, 916 insertions(+), 52 deletions(-)
 create mode 100644 Documentation/devicetree/bindings/regulator/nvidia,tegra-regulators-coupling.txt
 create mode 100644 drivers/soc/tegra/regulators-tegra20.c
 create mode 100644 drivers/soc/tegra/regulators-tegra30.c

-- 
2.21.0

^ permalink raw reply	[flat|nested] 21+ messages in thread

* [PATCH v2 1/8] regulator: core: Introduce API for regulators coupling customization
  2019-06-03 23:58 [PATCH v2 0/8] Introduce customized regulators coupling Dmitry Osipenko
@ 2019-06-03 23:58 ` Dmitry Osipenko
  2019-06-17 15:23   ` Mark Brown
  2019-06-03 23:58 ` [PATCH v2 2/8] regulator: core: Parse max-spread value per regulator couple Dmitry Osipenko
                   ` (6 subsequent siblings)
  7 siblings, 1 reply; 21+ messages in thread
From: Dmitry Osipenko @ 2019-06-03 23:58 UTC (permalink / raw)
  To: Mark Brown, Thierry Reding, Jonathan Hunter, Peter De Schrijver,
	Rob Herring
  Cc: devicetree, linux-tegra, linux-kernel

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 SoC's 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>
---
 drivers/regulator/core.c         | 66 ++++++++++++++++++++++++++++++++
 include/linux/regulator/driver.h | 33 ++++++++++++++++
 2 files changed, 99 insertions(+)

diff --git a/drivers/regulator/core.c b/drivers/regulator/core.c
index 85f61e5dc312..3b2d10a46bb5 100644
--- a/drivers/regulator/core.c
+++ b/drivers/regulator/core.c
@@ -50,6 +50,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;
@@ -3573,6 +3574,7 @@ 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;
@@ -3592,6 +3594,9 @@ static int regulator_balance_voltage(struct regulator_dev *rdev,
 		return -EPERM;
 	}
 
+	if (coupler && coupler->balance_voltage)
+		return coupler->balance_voltage(coupler, rdev, state);
+
 	for (i = 0; i < n_coupled; i++)
 		c_rdev_done[i] = false;
 
@@ -4707,8 +4712,33 @@ 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(&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;
+
+	list_for_each_entry(coupler, &regulator_coupler_list, list) {
+		err = coupler->attach_regulator(coupler, rdev);
+		if (!err)
+			return coupler;
+	}
+
+	return NULL;
+}
+
 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;
@@ -4724,6 +4754,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;
@@ -4737,10 +4773,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;
 
@@ -4770,6 +4808,13 @@ 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);
+	}
 }
 
 static int regulator_init_coupling(struct regulator_dev *rdev)
@@ -4812,9 +4857,25 @@ static int regulator_init_coupling(struct regulator_dev *rdev)
 	if (!of_check_coupling_data(rdev))
 		return -EPERM;
 
+	rdev->coupling_desc.coupler = regulator_find_coupler(rdev);
+	if (!rdev->coupling_desc.coupler) {
+		rdev_err(rdev, "failed to find coupler\n");
+		return -EINVAL;
+	}
+
 	return 0;
 }
 
+static int generic_coupler_attach(struct regulator_coupler *coupler,
+				  struct regulator_dev *rdev)
+{
+	return 0;
+}
+
+static struct regulator_coupler generic_regulator_coupler = {
+	.attach_regulator = generic_coupler_attach,
+};
+
 /**
  * regulator_register - register regulator
  * @regulator_desc: regulator to register
@@ -4976,7 +5037,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;
 
@@ -5025,6 +5088,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);
@@ -5480,6 +5544,8 @@ static int __init regulator_init(void)
 #endif
 	regulator_dummy_init();
 
+	regulator_coupler_register(&generic_regulator_coupler);
+
 	return ret;
 }
 
diff --git a/include/linux/regulator/driver.h b/include/linux/regulator/driver.h
index 377da2357118..18bbbd4135a1 100644
--- a/include/linux/regulator/driver.h
+++ b/include/linux/regulator/driver.h
@@ -20,6 +20,7 @@
 #include <linux/device.h>
 #include <linux/notifier.h>
 #include <linux/regulator/consumer.h>
+#include <linux/suspend.h>
 #include <linux/ww_mutex.h>
 
 struct gpio_desc;
@@ -28,6 +29,7 @@ struct regulator_dev;
 struct regulator_config;
 struct regulator_init_data;
 struct regulator_enable_gpio;
+struct regulator_coupler;
 
 enum regulator_status {
 	REGULATOR_STATUS_OFF,
@@ -427,6 +429,7 @@ struct regulator_config {
  */
 struct coupling_desc {
 	struct regulator_dev *coupled_rdevs[MAX_COUPLED];
+	struct regulator_coupler *coupler;
 	int n_resolved;
 	int n_coupled;
 };
@@ -482,6 +485,33 @@ struct regulator_dev {
 	unsigned long last_off_jiffy;
 };
 
+/**
+ * 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 otherwise.
+ * @detach_regulator: Callback invoked on destruction of a coupled regulator.
+ * @balance_voltage: Callback invoked when voltage of a coupled regulator is
+ *                   changing. The callee should perform voltage balancing
+ *                   and change voltage of the coupled regulators.
+ */
+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);
+};
+
 struct regulator_dev *
 regulator_register(const struct regulator_desc *regulator_desc,
 		   const struct regulator_config *config);
@@ -552,4 +582,7 @@ void regulator_unlock(struct regulator_dev *rdev);
  */
 int regulator_desc_list_voltage_linear_range(const struct regulator_desc *desc,
 					     unsigned int selector);
+
+int regulator_coupler_register(struct regulator_coupler *coupler);
+
 #endif
-- 
2.21.0

^ permalink raw reply related	[flat|nested] 21+ messages in thread

* [PATCH v2 2/8] regulator: core: Parse max-spread value per regulator couple
  2019-06-03 23:58 [PATCH v2 0/8] Introduce customized regulators coupling Dmitry Osipenko
  2019-06-03 23:58 ` [PATCH v2 1/8] regulator: core: Introduce API for regulators coupling customization Dmitry Osipenko
@ 2019-06-03 23:58 ` Dmitry Osipenko
  2019-06-17 15:25   ` Mark Brown
  2019-06-03 23:58 ` [PATCH v2 3/8] regulator: core: Expose some of core functions Dmitry Osipenko
                   ` (5 subsequent siblings)
  7 siblings, 1 reply; 21+ messages in thread
From: Dmitry Osipenko @ 2019-06-03 23:58 UTC (permalink / raw)
  To: Mark Brown, Thierry Reding, Jonathan Hunter, Peter De Schrijver,
	Rob Herring
  Cc: devicetree, linux-tegra, linux-kernel

Parse and validate max-spread value per regulator couple. Now regulator
core can take multiple regulator couples, although balancing is still
limited to a single couple.

Signed-off-by: Dmitry Osipenko <digetx@gmail.com>
---
 drivers/regulator/core.c          | 13 ++++----
 drivers/regulator/of_regulator.c  | 49 +++++++++++++++++++++----------
 include/linux/regulator/machine.h |  3 +-
 3 files changed, 43 insertions(+), 22 deletions(-)

diff --git a/drivers/regulator/core.c b/drivers/regulator/core.c
index 3b2d10a46bb5..c148dd7d306e 100644
--- a/drivers/regulator/core.c
+++ b/drivers/regulator/core.c
@@ -3435,7 +3435,7 @@ 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 max_spread = constraints->max_spread[0];
 	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;
@@ -3597,6 +3597,12 @@ static int regulator_balance_voltage(struct regulator_dev *rdev,
 	if (coupler && coupler->balance_voltage)
 		return coupler->balance_voltage(coupler, rdev, state);
 
+	if (n_coupled > 2) {
+		rdev_err(rdev,
+			 "Voltage balancing for multiple regulator couples is unimplemented\n");
+		return -EPERM;
+	}
+
 	for (i = 0; i < n_coupled; i++)
 		c_rdev_done[i] = false;
 
@@ -4849,11 +4855,6 @@ static int regulator_init_coupling(struct regulator_dev *rdev)
 		return -EPERM;
 	}
 
-	if (rdev->constraints->max_spread <= 0) {
-		rdev_err(rdev, "wrong max_spread value\n");
-		return -EPERM;
-	}
-
 	if (!of_check_coupling_data(rdev))
 		return -EPERM;
 
diff --git a/drivers/regulator/of_regulator.c b/drivers/regulator/of_regulator.c
index 0ead1164e4d6..41cf8819f60a 100644
--- a/drivers/regulator/of_regulator.c
+++ b/drivers/regulator/of_regulator.c
@@ -30,8 +30,14 @@ 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 pvals[MAX_COUPLED];
 	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,12 @@ 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 (!of_property_read_u32_array(np, "regulator-coupled-max-spread",
+					pvals, n_phandles)) {
+
+		for (i = 0; i < n_phandles; i++)
+			constraints->max_spread[i] = pvals[i];
+	}
 
 	if (!of_property_read_u32(np, "regulator-max-step-microvolt",
 				  &pval))
@@ -473,7 +482,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 +505,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 +529,28 @@ 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");
+	if (n_phandles >= MAX_COUPLED) {
+		dev_err(&rdev->dev, "please bump MAX_COUPLED number\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 +567,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/machine.h b/include/linux/regulator/machine.h
index 1d34a70ffda2..53830d0c0b6f 100644
--- a/include/linux/regulator/machine.h
+++ b/include/linux/regulator/machine.h
@@ -15,6 +15,7 @@
 #ifndef __LINUX_REGULATOR_MACHINE_H_
 #define __LINUX_REGULATOR_MACHINE_H_
 
+#include <linux/regulator/driver.h>
 #include <linux/regulator/consumer.h>
 #include <linux/suspend.h>
 
@@ -156,7 +157,7 @@ struct regulation_constraints {
 	int system_load;
 
 	/* used for coupled regulators */
-	int max_spread;
+	int max_spread[MAX_COUPLED];
 
 	/* used for changing voltage in steps */
 	int max_uV_step;
-- 
2.21.0

^ permalink raw reply related	[flat|nested] 21+ messages in thread

* [PATCH v2 3/8] regulator: core: Expose some of core functions
  2019-06-03 23:58 [PATCH v2 0/8] Introduce customized regulators coupling Dmitry Osipenko
  2019-06-03 23:58 ` [PATCH v2 1/8] regulator: core: Introduce API for regulators coupling customization Dmitry Osipenko
  2019-06-03 23:58 ` [PATCH v2 2/8] regulator: core: Parse max-spread value per regulator couple Dmitry Osipenko
@ 2019-06-03 23:58 ` Dmitry Osipenko
  2019-06-17 15:32   ` Mark Brown
  2019-06-03 23:59 ` [PATCH v2 4/8] regulator: core Bump MAX_COUPLED to 3 Dmitry Osipenko
                   ` (4 subsequent siblings)
  7 siblings, 1 reply; 21+ messages in thread
From: Dmitry Osipenko @ 2019-06-03 23:58 UTC (permalink / raw)
  To: Mark Brown, Thierry Reding, Jonathan Hunter, Peter De Schrijver,
	Rob Herring
  Cc: devicetree, linux-tegra, linux-kernel

Expose some of internal functions that are required for implementation of
customized regulator couplers.

Signed-off-by: Dmitry Osipenko <digetx@gmail.com>
---
 drivers/regulator/core.c         | 58 +++++++++++++++-----------------
 include/linux/regulator/driver.h | 11 ++++++
 2 files changed, 38 insertions(+), 31 deletions(-)

diff --git a/drivers/regulator/core.c b/drivers/regulator/core.c
index c148dd7d306e..5a5b86d3edfb 100644
--- a/drivers/regulator/core.c
+++ b/drivers/regulator/core.c
@@ -94,7 +94,6 @@ struct regulator_supply_alias {
 
 static int _regulator_is_enabled(struct regulator_dev *rdev);
 static int _regulator_disable(struct regulator *regulator);
-static int _regulator_get_voltage(struct regulator_dev *rdev);
 static int _regulator_get_current_limit(struct regulator_dev *rdev);
 static unsigned int _regulator_get_mode(struct regulator_dev *rdev);
 static int _notifier_call_chain(struct regulator_dev *rdev,
@@ -103,15 +102,12 @@ 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);
 static void _regulator_put(struct regulator *regulator);
 
-static const char *rdev_get_name(struct regulator_dev *rdev)
+const char *rdev_get_name(struct regulator_dev *rdev)
 {
 	if (rdev->constraints && rdev->constraints->name)
 		return rdev->constraints->name;
@@ -425,8 +421,8 @@ static struct device_node *of_get_regulator(struct device *dev, const char *supp
 }
 
 /* Platform voltage constraint check */
-static int regulator_check_voltage(struct regulator_dev *rdev,
-				   int *min_uV, int *max_uV)
+int regulator_check_voltage(struct regulator_dev *rdev,
+			    int *min_uV, int *max_uV)
 {
 	BUG_ON(*min_uV > *max_uV);
 
@@ -458,9 +454,9 @@ static int regulator_check_states(suspend_state_t state)
 /* Make sure we select a voltage that suits the needs of all
  * regulator consumers
  */
-static int regulator_check_consumers(struct regulator_dev *rdev,
-				     int *min_uV, int *max_uV,
-				     suspend_state_t state)
+int regulator_check_consumers(struct regulator_dev *rdev,
+			      int *min_uV, int *max_uV,
+			      suspend_state_t state)
 {
 	struct regulator *regulator;
 	struct regulator_voltage *voltage;
@@ -571,7 +567,7 @@ static ssize_t regulator_uV_show(struct device *dev,
 	ssize_t ret;
 
 	regulator_lock(rdev);
-	ret = sprintf(buf, "%d\n", _regulator_get_voltage(rdev));
+	ret = sprintf(buf, "%d\n", regulator_get_voltage_rdev(rdev));
 	regulator_unlock(rdev);
 
 	return ret;
@@ -942,7 +938,7 @@ static int drms_uA_update(struct regulator_dev *rdev)
 			rdev_err(rdev, "failed to set load %d\n", current_uA);
 	} else {
 		/* get output voltage */
-		output_uV = _regulator_get_voltage(rdev);
+		output_uV = regulator_get_voltage_rdev(rdev);
 		if (output_uV <= 0) {
 			rdev_err(rdev, "invalid output voltage found\n");
 			return -EINVAL;
@@ -1055,7 +1051,7 @@ static void print_constraints(struct regulator_dev *rdev)
 
 	if (!constraints->min_uV ||
 	    constraints->min_uV != constraints->max_uV) {
-		ret = _regulator_get_voltage(rdev);
+		ret = regulator_get_voltage_rdev(rdev);
 		if (ret > 0)
 			count += scnprintf(buf + count, len - count,
 					   "at %d mV ", ret / 1000);
@@ -1114,7 +1110,7 @@ static int machine_constraints_voltage(struct regulator_dev *rdev,
 	if (rdev->constraints->apply_uV &&
 	    rdev->constraints->min_uV && rdev->constraints->max_uV) {
 		int target_min, target_max;
-		int current_uV = _regulator_get_voltage(rdev);
+		int current_uV = regulator_get_voltage_rdev(rdev);
 
 		if (current_uV == -ENOTRECOVERABLE) {
 			/* This regulator can't be read and must be initialized */
@@ -1124,7 +1120,7 @@ static int machine_constraints_voltage(struct regulator_dev *rdev,
 			_regulator_do_set_voltage(rdev,
 						  rdev->constraints->min_uV,
 						  rdev->constraints->max_uV);
-			current_uV = _regulator_get_voltage(rdev);
+			current_uV = regulator_get_voltage_rdev(rdev);
 		}
 
 		if (current_uV < 0) {
@@ -3066,7 +3062,7 @@ static int _regulator_call_set_voltage(struct regulator_dev *rdev,
 	struct pre_voltage_change_data data;
 	int ret;
 
-	data.old_uV = _regulator_get_voltage(rdev);
+	data.old_uV = regulator_get_voltage_rdev(rdev);
 	data.min_uV = min_uV;
 	data.max_uV = max_uV;
 	ret = _notifier_call_chain(rdev, REGULATOR_EVENT_PRE_VOLTAGE_CHANGE,
@@ -3090,7 +3086,7 @@ static int _regulator_call_set_voltage_sel(struct regulator_dev *rdev,
 	struct pre_voltage_change_data data;
 	int ret;
 
-	data.old_uV = _regulator_get_voltage(rdev);
+	data.old_uV = regulator_get_voltage_rdev(rdev);
 	data.min_uV = uV;
 	data.max_uV = uV;
 	ret = _notifier_call_chain(rdev, REGULATOR_EVENT_PRE_VOLTAGE_CHANGE,
@@ -3143,7 +3139,7 @@ static int _regulator_do_set_voltage(struct regulator_dev *rdev,
 	unsigned int selector;
 	int old_selector = -1;
 	const struct regulator_ops *ops = rdev->desc->ops;
-	int old_uV = _regulator_get_voltage(rdev);
+	int old_uV = regulator_get_voltage_rdev(rdev);
 
 	trace_regulator_set_voltage(rdev_get_name(rdev), min_uV, max_uV);
 
@@ -3170,7 +3166,7 @@ static int _regulator_do_set_voltage(struct regulator_dev *rdev,
 				best_val = ops->list_voltage(rdev,
 							     selector);
 			else
-				best_val = _regulator_get_voltage(rdev);
+				best_val = regulator_get_voltage_rdev(rdev);
 		}
 
 	} else if (ops->set_voltage_sel) {
@@ -3289,7 +3285,7 @@ static int regulator_set_voltage_unlocked(struct regulator *regulator,
 	 * changing the voltage.
 	 */
 	if (!regulator_ops_is_valid(rdev, REGULATOR_CHANGE_VOLTAGE)) {
-		current_uV = _regulator_get_voltage(rdev);
+		current_uV = regulator_get_voltage_rdev(rdev);
 		if (min_uV <= current_uV && current_uV <= max_uV) {
 			voltage->min_uV = min_uV;
 			voltage->max_uV = max_uV;
@@ -3326,8 +3322,8 @@ static int regulator_set_voltage_unlocked(struct regulator *regulator,
 	return ret;
 }
 
-static int regulator_set_voltage_rdev(struct regulator_dev *rdev, int min_uV,
-				      int max_uV, suspend_state_t state)
+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;
@@ -3355,7 +3351,7 @@ static int regulator_set_voltage_rdev(struct regulator_dev *rdev, int min_uV,
 
 		best_supply_uV += rdev->desc->min_dropout_uV;
 
-		current_supply_uV = _regulator_get_voltage(rdev->supply->rdev);
+		current_supply_uV = regulator_get_voltage_rdev(rdev->supply->rdev);
 		if (current_supply_uV < 0) {
 			ret = current_supply_uV;
 			goto out;
@@ -3406,7 +3402,7 @@ static int regulator_limit_voltage_step(struct regulator_dev *rdev,
 		return 1;
 
 	if (*current_uV < 0) {
-		*current_uV = _regulator_get_voltage(rdev);
+		*current_uV = regulator_get_voltage_rdev(rdev);
 
 		if (*current_uV < 0)
 			return *current_uV;
@@ -3510,7 +3506,7 @@ static int regulator_get_optimal_voltage(struct regulator_dev *rdev,
 		if (!_regulator_is_enabled(c_rdevs[i]))
 			continue;
 
-		tmp_act = _regulator_get_voltage(c_rdevs[i]);
+		tmp_act = regulator_get_voltage_rdev(c_rdevs[i]);
 		if (tmp_act < 0)
 			return tmp_act;
 
@@ -3552,7 +3548,7 @@ static int regulator_get_optimal_voltage(struct regulator_dev *rdev,
 	if (n_coupled > 1 && *current_uV == -1) {
 
 		if (_regulator_is_enabled(rdev)) {
-			ret = _regulator_get_voltage(rdev);
+			ret = regulator_get_voltage_rdev(rdev);
 			if (ret < 0)
 				return ret;
 
@@ -3922,7 +3918,7 @@ int regulator_sync_voltage(struct regulator *regulator)
 }
 EXPORT_SYMBOL_GPL(regulator_sync_voltage);
 
-static int _regulator_get_voltage(struct regulator_dev *rdev)
+int regulator_get_voltage_rdev(struct regulator_dev *rdev)
 {
 	int sel, ret;
 	bool bypassed;
@@ -3939,7 +3935,7 @@ static int _regulator_get_voltage(struct regulator_dev *rdev)
 				return -EPROBE_DEFER;
 			}
 
-			return _regulator_get_voltage(rdev->supply->rdev);
+			return regulator_get_voltage_rdev(rdev->supply->rdev);
 		}
 	}
 
@@ -3955,7 +3951,7 @@ static int _regulator_get_voltage(struct regulator_dev *rdev)
 	} else if (rdev->desc->fixed_uV && (rdev->desc->n_voltages == 1)) {
 		ret = rdev->desc->fixed_uV;
 	} else if (rdev->supply) {
-		ret = _regulator_get_voltage(rdev->supply->rdev);
+		ret = regulator_get_voltage_rdev(rdev->supply->rdev);
 	} else {
 		return -EINVAL;
 	}
@@ -3980,7 +3976,7 @@ int regulator_get_voltage(struct regulator *regulator)
 	int ret;
 
 	regulator_lock_dependent(regulator->rdev, &ww_ctx);
-	ret = _regulator_get_voltage(regulator->rdev);
+	ret = regulator_get_voltage_rdev(regulator->rdev);
 	regulator_unlock_dependent(regulator->rdev, &ww_ctx);
 
 	return ret;
@@ -5343,7 +5339,7 @@ static void regulator_summary_show_subtree(struct seq_file *s,
 		   rdev->use_count, rdev->open_count, rdev->bypass_count,
 		   regulator_opmode_to_str(opmode));
 
-	seq_printf(s, "%5dmV ", _regulator_get_voltage(rdev) / 1000);
+	seq_printf(s, "%5dmV ", regulator_get_voltage_rdev(rdev) / 1000);
 	seq_printf(s, "%5dmA ",
 		   _regulator_get_current_limit_unlocked(rdev) / 1000);
 
diff --git a/include/linux/regulator/driver.h b/include/linux/regulator/driver.h
index 18bbbd4135a1..dfd8e518b283 100644
--- a/include/linux/regulator/driver.h
+++ b/include/linux/regulator/driver.h
@@ -585,4 +585,15 @@ int regulator_desc_list_voltage_linear_range(const struct regulator_desc *desc,
 
 int regulator_coupler_register(struct regulator_coupler *coupler);
 
+const char *rdev_get_name(struct regulator_dev *rdev);
+int regulator_check_consumers(struct regulator_dev *rdev,
+			      int *min_uV, int *max_uV,
+			      suspend_state_t state);
+int regulator_check_voltage(struct regulator_dev *rdev,
+			    int *min_uV, int *max_uV);
+int regulator_get_voltage_rdev(struct regulator_dev *rdev);
+int regulator_set_voltage_rdev(struct regulator_dev *rdev,
+			       int min_uV, int max_uV,
+			       suspend_state_t state);
+
 #endif
-- 
2.21.0

^ permalink raw reply related	[flat|nested] 21+ messages in thread

* [PATCH v2 4/8] regulator: core Bump MAX_COUPLED to 3
  2019-06-03 23:58 [PATCH v2 0/8] Introduce customized regulators coupling Dmitry Osipenko
                   ` (2 preceding siblings ...)
  2019-06-03 23:58 ` [PATCH v2 3/8] regulator: core: Expose some of core functions Dmitry Osipenko
@ 2019-06-03 23:59 ` Dmitry Osipenko
  2019-06-03 23:59 ` [PATCH v2 5/8] dt-bindings: regulator: Document regulators coupling of NVIDIA Tegra20/30 SoC's Dmitry Osipenko
                   ` (3 subsequent siblings)
  7 siblings, 0 replies; 21+ messages in thread
From: Dmitry Osipenko @ 2019-06-03 23:59 UTC (permalink / raw)
  To: Mark Brown, Thierry Reding, Jonathan Hunter, Peter De Schrijver,
	Rob Herring
  Cc: devicetree, linux-tegra, linux-kernel

NVIDIA Tegra20 SoC's couple 3 regulators, bump the MAX_COUPLED accordingly
to support that case of coupling.

Signed-off-by: Dmitry Osipenko <digetx@gmail.com>
---
 include/linux/regulator/driver.h | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/include/linux/regulator/driver.h b/include/linux/regulator/driver.h
index dfd8e518b283..5e81020d66ae 100644
--- a/include/linux/regulator/driver.h
+++ b/include/linux/regulator/driver.h
@@ -15,7 +15,7 @@
 #ifndef __LINUX_REGULATOR_DRIVER_H_
 #define __LINUX_REGULATOR_DRIVER_H_
 
-#define MAX_COUPLED		2
+#define MAX_COUPLED		3
 
 #include <linux/device.h>
 #include <linux/notifier.h>
-- 
2.21.0

^ permalink raw reply related	[flat|nested] 21+ messages in thread

* [PATCH v2 5/8] dt-bindings: regulator: Document regulators coupling of NVIDIA Tegra20/30 SoC's
  2019-06-03 23:58 [PATCH v2 0/8] Introduce customized regulators coupling Dmitry Osipenko
                   ` (3 preceding siblings ...)
  2019-06-03 23:59 ` [PATCH v2 4/8] regulator: core Bump MAX_COUPLED to 3 Dmitry Osipenko
@ 2019-06-03 23:59 ` Dmitry Osipenko
  2019-06-03 23:59 ` [PATCH v2 6/8] regulator: core: Don't attach generic coupler to Tegra SoC regulators Dmitry Osipenko
                   ` (2 subsequent siblings)
  7 siblings, 0 replies; 21+ messages in thread
From: Dmitry Osipenko @ 2019-06-03 23:59 UTC (permalink / raw)
  To: Mark Brown, Thierry Reding, Jonathan Hunter, Peter De Schrijver,
	Rob Herring
  Cc: devicetree, linux-tegra, linux-kernel

There is voltage coupling between three regulators on Tegra20 boards and
between two on Tegra30. The voltage coupling is a SoC-level feature and
thus it is mandatory and common for all of the Tegra boards.

Signed-off-by: Dmitry Osipenko <digetx@gmail.com>
---
 .../nvidia,tegra-regulators-coupling.txt      | 65 +++++++++++++++++++
 1 file changed, 65 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/regulator/nvidia,tegra-regulators-coupling.txt

diff --git a/Documentation/devicetree/bindings/regulator/nvidia,tegra-regulators-coupling.txt b/Documentation/devicetree/bindings/regulator/nvidia,tegra-regulators-coupling.txt
new file mode 100644
index 000000000000..4bf2dbf7c6cc
--- /dev/null
+++ b/Documentation/devicetree/bindings/regulator/nvidia,tegra-regulators-coupling.txt
@@ -0,0 +1,65 @@
+NVIDIA Tegra Regulators Coupling
+================================
+
+NVIDIA Tegra SoC's have a mandatory voltage-coupling between regulators.
+Thus on Tegra20 there are 3 coupled regulators and on NVIDIA Tegra30
+there are 2.
+
+Tegra20 voltage coupling
+------------------------
+
+On Tegra20 SoC's there are 3 coupled regulators: CORE, RTC and CPU.
+The CORE and RTC voltages shall be in a range of 170mV from each other
+and they both shall be higher than the CPU voltage by at least 120mV.
+
+Tegra30 voltage coupling
+------------------------
+
+On Tegra30 SoC's there are 2 coupled regulators: CORE and CPU. The CORE
+and CPU voltages shall be in a range of 300mV from each other and CORE
+voltage shall be higher than the CPU by N mV, where N depends on the CPU
+voltage.
+
+Required properties:
+- nvidia,tegra-core-regulator: Boolean property that designates regulator
+  as the "Core domain" voltage regulator.
+- nvidia,tegra-rtc-regulator: Boolean property that designates regulator
+  as the "RTC domain" voltage regulator.
+- nvidia,tegra-cpu-regulator: Boolean property that designates regulator
+  as the "CPU domain" voltage regulator.
+
+Example:
+
+	pmic {
+		regulators {
+			core_vdd_reg: core {
+				regulator-name = "vdd_core";
+				regulator-min-microvolt = <950000>;
+				regulator-max-microvolt = <1300000>;
+				regulator-coupled-with = <&rtc_vdd_reg &cpu_vdd_reg>;
+				regulator-coupled-max-spread = <170000 550000>;
+
+				nvidia,tegra-core-regulator;
+			};
+
+			rtc_vdd_reg: rtc {
+				regulator-name = "vdd_rtc";
+				regulator-min-microvolt = <950000>;
+				regulator-max-microvolt = <1300000>;
+				regulator-coupled-with = <&core_vdd_reg &cpu_vdd_reg>;
+				regulator-coupled-max-spread = <170000 550000>;
+
+				nvidia,tegra-rtc-regulator;
+			};
+
+			cpu_vdd_reg: cpu {
+				regulator-name = "vdd_cpu";
+				regulator-min-microvolt = <750000>;
+				regulator-max-microvolt = <1125000>;
+				regulator-coupled-with = <&core_vdd_reg &rtc_vdd_reg>;
+				regulator-coupled-max-spread = <550000 550000>;
+
+				nvidia,tegra-cpu-regulator;
+			};
+		};
+	};
-- 
2.21.0

^ permalink raw reply related	[flat|nested] 21+ messages in thread

* [PATCH v2 6/8] regulator: core: Don't attach generic coupler to Tegra SoC regulators
  2019-06-03 23:58 [PATCH v2 0/8] Introduce customized regulators coupling Dmitry Osipenko
                   ` (4 preceding siblings ...)
  2019-06-03 23:59 ` [PATCH v2 5/8] dt-bindings: regulator: Document regulators coupling of NVIDIA Tegra20/30 SoC's Dmitry Osipenko
@ 2019-06-03 23:59 ` Dmitry Osipenko
  2019-06-17 15:44   ` Mark Brown
  2019-06-03 23:59 ` [PATCH v2 7/8] soc/tegra: regulators: Add regulators coupler for Tegra20 Dmitry Osipenko
  2019-06-03 23:59 ` [PATCH v2 8/8] soc/tegra: regulators: Add regulators coupler for Tegra30 Dmitry Osipenko
  7 siblings, 1 reply; 21+ messages in thread
From: Dmitry Osipenko @ 2019-06-03 23:59 UTC (permalink / raw)
  To: Mark Brown, Thierry Reding, Jonathan Hunter, Peter De Schrijver,
	Rob Herring
  Cc: devicetree, linux-tegra, linux-kernel

Today generic coupler can't handle regulators coupling that is specific to
NVIDIA Tegra SoC's, hence don't allow generic coupler to attach to those
regulators. Later on it should be possible to switch at least Tegra20 to a
generic coupler, once all prerequisite bits will get resolved in upstream
(voltage management support by all drivers, etc).

Signed-off-by: Dmitry Osipenko <digetx@gmail.com>
---
 drivers/regulator/core.c | 10 ++++++++++
 1 file changed, 10 insertions(+)

diff --git a/drivers/regulator/core.c b/drivers/regulator/core.c
index 5a5b86d3edfb..68cccaf2e8f2 100644
--- a/drivers/regulator/core.c
+++ b/drivers/regulator/core.c
@@ -4866,6 +4866,16 @@ static int regulator_init_coupling(struct regulator_dev *rdev)
 static int generic_coupler_attach(struct regulator_coupler *coupler,
 				  struct regulator_dev *rdev)
 {
+	/*
+	 * Generic coupler isn't suitable for NVIVIA Tegra SoC's, at least
+	 * for now. Hence filter out the unwanted regulators as they shall be
+	 * managed by a platform-specific coupler.
+	 */
+	if (of_property_read_bool(rdev->dev.of_node, "tegra-core-regulator") ||
+	    of_property_read_bool(rdev->dev.of_node, "tegra-rtc-regulator") ||
+	    of_property_read_bool(rdev->dev.of_node, "tegra-cpu-regulator"))
+		return -EPERM;
+
 	return 0;
 }
 
-- 
2.21.0

^ permalink raw reply related	[flat|nested] 21+ messages in thread

* [PATCH v2 7/8] soc/tegra: regulators: Add regulators coupler for Tegra20
  2019-06-03 23:58 [PATCH v2 0/8] Introduce customized regulators coupling Dmitry Osipenko
                   ` (5 preceding siblings ...)
  2019-06-03 23:59 ` [PATCH v2 6/8] regulator: core: Don't attach generic coupler to Tegra SoC regulators Dmitry Osipenko
@ 2019-06-03 23:59 ` Dmitry Osipenko
  2019-06-17 15:46   ` Mark Brown
  2019-06-03 23:59 ` [PATCH v2 8/8] soc/tegra: regulators: Add regulators coupler for Tegra30 Dmitry Osipenko
  7 siblings, 1 reply; 21+ messages in thread
From: Dmitry Osipenko @ 2019-06-03 23:59 UTC (permalink / raw)
  To: Mark Brown, Thierry Reding, Jonathan Hunter, Peter De Schrijver,
	Rob Herring
  Cc: devicetree, linux-tegra, linux-kernel

Add regulators coupler for Tegra20 SoC's that performs voltage balancing
of a coupled regulators and thus provides voltage scaling functionality.

There are 3 coupled regulators on all Tegra20 SoC's: CORE, RTC and CPU.
The CORE and RTC voltages shall be in range of 170mV from each other and
they both shall be higher than the CPU voltage by at least 120mV. This
sounds like it could be handle by a generic voltage balancer, but the CORE
voltage scaling isn't implemented in any of the upstream drivers yet.
It will take quite some time and effort to hook up voltage scaling for
all of the drivers, hence we will use a custom coupler that will manage
the CPU voltage scaling for the starter.

Signed-off-by: Dmitry Osipenko <digetx@gmail.com>
---
 drivers/soc/tegra/Kconfig              |   6 +
 drivers/soc/tegra/Makefile             |   1 +
 drivers/soc/tegra/regulators-tegra20.c | 348 +++++++++++++++++++++++++
 3 files changed, 355 insertions(+)
 create mode 100644 drivers/soc/tegra/regulators-tegra20.c

diff --git a/drivers/soc/tegra/Kconfig b/drivers/soc/tegra/Kconfig
index fbfce48ffb0d..4453c982c0b9 100644
--- a/drivers/soc/tegra/Kconfig
+++ b/drivers/soc/tegra/Kconfig
@@ -16,6 +16,7 @@ config ARCH_TEGRA_2x_SOC
 	select SOC_TEGRA_FLOWCTRL
 	select SOC_TEGRA_PMC
 	select TEGRA_TIMER
+	select SOC_TEGRA20_VOLTAGE_COUPLER if REGULATOR
 	help
 	  Support for NVIDIA Tegra AP20 and T20 processors, based on the
 	  ARM CortexA9MP CPU and the ARM PL310 L2 cache controller
@@ -134,3 +135,8 @@ config SOC_TEGRA_POWERGATE_BPMP
 	def_bool y
 	depends on PM_GENERIC_DOMAINS
 	depends on TEGRA_BPMP
+
+config SOC_TEGRA20_VOLTAGE_COUPLER
+	bool "Voltage scaling support for Tegra20 SoC's"
+	depends on ARCH_TEGRA_2x_SOC
+	depends on REGULATOR
diff --git a/drivers/soc/tegra/Makefile b/drivers/soc/tegra/Makefile
index 902759fe5f4d..9f0bdd53bef8 100644
--- a/drivers/soc/tegra/Makefile
+++ b/drivers/soc/tegra/Makefile
@@ -5,3 +5,4 @@ obj-y += common.o
 obj-$(CONFIG_SOC_TEGRA_FLOWCTRL) += flowctrl.o
 obj-$(CONFIG_SOC_TEGRA_PMC) += pmc.o
 obj-$(CONFIG_SOC_TEGRA_POWERGATE_BPMP) += powergate-bpmp.o
+obj-$(CONFIG_SOC_TEGRA20_VOLTAGE_COUPLER) += regulators-tegra20.o
diff --git a/drivers/soc/tegra/regulators-tegra20.c b/drivers/soc/tegra/regulators-tegra20.c
new file mode 100644
index 000000000000..a64138f2e092
--- /dev/null
+++ b/drivers/soc/tegra/regulators-tegra20.c
@@ -0,0 +1,348 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Voltage regulators coupling resolver for NVIDIA Tegra20
+ *
+ * Copyright (C) 2019 GRATE-DRIVER project
+ */
+
+#define pr_fmt(fmt)	"tegra voltage-coupler: " fmt
+
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/of.h>
+#include <linux/regulator/driver.h>
+#include <linux/regulator/machine.h>
+
+struct tegra_regulator_coupler {
+	struct regulator_coupler coupler;
+	struct regulator_dev *core_rdev;
+	struct regulator_dev *cpu_rdev;
+	struct regulator_dev *rtc_rdev;
+	int core_min_uV;
+};
+
+static inline struct tegra_regulator_coupler *
+to_tegra_coupler(struct regulator_coupler *coupler)
+{
+	return container_of(coupler, struct tegra_regulator_coupler, coupler);
+}
+
+static int tegra20_core_limit(struct tegra_regulator_coupler *tegra,
+			      struct regulator_dev *core_rdev)
+{
+	int core_min_uV = 0;
+	int core_max_uV;
+	int core_cur_uV;
+	int err;
+
+	if (tegra->core_min_uV > 0)
+		return tegra->core_min_uV;
+
+	core_cur_uV = regulator_get_voltage_rdev(core_rdev);
+	if (core_cur_uV < 0)
+		return core_cur_uV;
+
+	core_max_uV = max(core_cur_uV, 1200000);
+
+	err = regulator_check_voltage(core_rdev, &core_min_uV, &core_max_uV);
+	if (err)
+		return err;
+
+	/*
+	 * Limit minimum CORE voltage to a value left from bootloader or,
+	 * if it's unreasonably low value, to the most common 1.2v or to
+	 * whatever maximum value defined via board's device-tree.
+	 */
+	tegra->core_min_uV = core_max_uV;
+
+	pr_info("core minimum voltage limited to %duV\n", tegra->core_min_uV);
+
+	return tegra->core_min_uV;
+}
+
+static int tegra20_core_rtc_max_spread(struct regulator_dev *core_rdev,
+				       struct regulator_dev *rtc_rdev)
+{
+	struct coupling_desc *c_desc = &core_rdev->coupling_desc;
+	struct regulator_dev *rdev;
+	int max_spread;
+	unsigned int i;
+
+	for (i = 1; i < c_desc->n_coupled; i++) {
+		max_spread = core_rdev->constraints->max_spread[i - 1];
+		rdev = c_desc->coupled_rdevs[i];
+
+		if (rdev == rtc_rdev && max_spread)
+			return max_spread;
+	}
+
+	pr_err_once("rtc-core max-spread is undefined in device-tree\n");
+
+	return 150000;
+}
+
+static int tegra20_core_rtc_update(struct tegra_regulator_coupler *tegra,
+				   struct regulator_dev *core_rdev,
+				   struct regulator_dev *rtc_rdev,
+				   int cpu_uV, int cpu_min_uV)
+{
+	int core_min_uV, core_max_uV = INT_MAX;
+	int rtc_min_uV, rtc_max_uV = INT_MAX;
+	int core_target_uV;
+	int rtc_target_uV;
+	int max_spread;
+	int core_uV;
+	int rtc_uV;
+	int err;
+
+	/*
+	 * RTC and CORE voltages should be no more than 170mV from each other,
+	 * CPU should be below RTC and CORE by at least 120mV. This applies
+	 * to all Tegra20 SoC's.
+	 */
+	max_spread = tegra20_core_rtc_max_spread(core_rdev, rtc_rdev);
+
+	/*
+	 * The core voltage scaling is currently not hooked up in drivers,
+	 * hence we will limit the minimum core voltage to a reasonable value.
+	 * This should be good enough for the time being.
+	 */
+	core_min_uV = tegra20_core_limit(tegra, core_rdev);
+	if (core_min_uV < 0)
+		return core_min_uV;
+
+	err = regulator_check_voltage(core_rdev, &core_min_uV, &core_max_uV);
+	if (err)
+		return err;
+
+	err = regulator_check_consumers(core_rdev, &core_min_uV, &core_max_uV,
+					PM_SUSPEND_ON);
+	if (err)
+		return err;
+
+	core_min_uV = max(cpu_min_uV + 125000, core_min_uV);
+	if (core_min_uV > core_max_uV)
+		return -EINVAL;
+
+	core_uV = regulator_get_voltage_rdev(core_rdev);
+	if (core_uV < 0)
+		return core_uV;
+
+	if (cpu_uV + 120000 > core_uV)
+		pr_err("core-cpu voltage constraint violated: %d %d\n",
+		       core_uV, cpu_uV + 120000);
+
+	rtc_uV = regulator_get_voltage_rdev(rtc_rdev);
+	if (rtc_uV < 0)
+		return rtc_uV;
+
+	if (cpu_uV + 120000 > rtc_uV)
+		pr_err("rtc-cpu voltage constraint violated: %d %d\n",
+		       rtc_uV, cpu_uV + 120000);
+
+	if (abs(core_uV - rtc_uV) > 170000)
+		pr_err("core-rtc voltage constraint violated: %d %d\n",
+		       core_uV, rtc_uV);
+
+	rtc_min_uV = max(cpu_min_uV + 125000, core_min_uV - max_spread);
+
+	err = regulator_check_voltage(rtc_rdev, &rtc_min_uV, &rtc_max_uV);
+	if (err)
+		return err;
+
+	while (core_uV != core_min_uV || rtc_uV != rtc_min_uV) {
+		if (core_uV < core_min_uV) {
+			core_target_uV = min(core_uV + max_spread, core_min_uV);
+			core_target_uV = min(rtc_uV + max_spread, core_target_uV);
+		} else {
+			core_target_uV = max(core_uV - max_spread, core_min_uV);
+			core_target_uV = max(rtc_uV - max_spread, core_target_uV);
+		}
+
+		err = regulator_set_voltage_rdev(core_rdev,
+						 core_target_uV,
+						 core_max_uV,
+						 PM_SUSPEND_ON);
+		if (err)
+			return err;
+
+		core_uV = core_target_uV;
+
+		if (rtc_uV < rtc_min_uV) {
+			rtc_target_uV = min(rtc_uV + max_spread, rtc_min_uV);
+			rtc_target_uV = min(core_uV + max_spread, rtc_target_uV);
+		} else {
+			rtc_target_uV = max(rtc_uV - max_spread, rtc_min_uV);
+			rtc_target_uV = max(core_uV - max_spread, rtc_target_uV);
+		}
+
+		err = regulator_set_voltage_rdev(rtc_rdev,
+						 rtc_target_uV,
+						 rtc_max_uV,
+						 PM_SUSPEND_ON);
+		if (err)
+			return err;
+
+		rtc_uV = rtc_target_uV;
+	}
+
+	return 0;
+}
+
+static int tegra20_core_voltage_update(struct tegra_regulator_coupler *tegra,
+				       struct regulator_dev *cpu_rdev,
+				       struct regulator_dev *core_rdev,
+				       struct regulator_dev *rtc_rdev)
+{
+	int cpu_uV;
+
+	cpu_uV = regulator_get_voltage_rdev(cpu_rdev);
+	if (cpu_uV < 0)
+		return cpu_uV;
+
+	return tegra20_core_rtc_update(tegra, core_rdev, rtc_rdev,
+				       cpu_uV, cpu_uV);
+}
+
+static int tegra20_cpu_voltage_update(struct tegra_regulator_coupler *tegra,
+				      struct regulator_dev *cpu_rdev,
+				      struct regulator_dev *core_rdev,
+				      struct regulator_dev *rtc_rdev)
+{
+	int cpu_max_uV = INT_MAX;
+	int cpu_min_uV = 0;
+	int cpu_uV;
+	int err;
+
+	err = regulator_check_voltage(cpu_rdev, &cpu_min_uV, &cpu_max_uV);
+	if (err)
+		return err;
+
+	err = regulator_check_consumers(cpu_rdev, &cpu_min_uV, &cpu_max_uV,
+					PM_SUSPEND_ON);
+	if (err)
+		return err;
+
+	cpu_uV = regulator_get_voltage_rdev(cpu_rdev);
+	if (cpu_uV < 0)
+		return cpu_uV;
+
+	if (cpu_min_uV > cpu_uV) {
+		err = tegra20_core_rtc_update(tegra, core_rdev, rtc_rdev,
+					      cpu_uV, cpu_min_uV);
+		if (err)
+			return err;
+
+		err = regulator_set_voltage_rdev(cpu_rdev, cpu_min_uV,
+						 cpu_max_uV, PM_SUSPEND_ON);
+		if (err)
+			return err;
+	} else if (cpu_min_uV < cpu_uV)  {
+		err = regulator_set_voltage_rdev(cpu_rdev, cpu_min_uV,
+						 cpu_max_uV, PM_SUSPEND_ON);
+		if (err)
+			return err;
+
+		err = tegra20_core_rtc_update(tegra, core_rdev, rtc_rdev,
+					      cpu_uV, cpu_min_uV);
+		if (err)
+			return err;
+	}
+
+	return 0;
+}
+
+static int tegra20_regulator_balance_voltage(struct regulator_coupler *coupler,
+					     struct regulator_dev *rdev,
+					     suspend_state_t state)
+{
+	struct tegra_regulator_coupler *tegra = to_tegra_coupler(coupler);
+	struct regulator_dev *core_rdev = tegra->core_rdev;
+	struct regulator_dev *cpu_rdev = tegra->cpu_rdev;
+	struct regulator_dev *rtc_rdev = tegra->rtc_rdev;
+
+	if ((core_rdev != rdev && cpu_rdev != rdev && rtc_rdev != rdev) ||
+	    state != PM_SUSPEND_ON) {
+		pr_err("regulators are not coupled properly\n");
+		return -EINVAL;
+	}
+
+	if (rdev == cpu_rdev)
+		return tegra20_cpu_voltage_update(tegra, cpu_rdev,
+						  core_rdev, rtc_rdev);
+
+	if (rdev == core_rdev)
+		return tegra20_core_voltage_update(tegra, cpu_rdev,
+						   core_rdev, rtc_rdev);
+
+	pr_err("changing %s voltage not permitted\n", rdev_get_name(rtc_rdev));
+
+	return -EPERM;
+}
+
+static int tegra20_regulator_attach(struct regulator_coupler *coupler,
+				    struct regulator_dev *rdev)
+{
+	struct tegra_regulator_coupler *tegra = to_tegra_coupler(coupler);
+	struct device_node *np = rdev->dev.of_node;
+
+	if (of_property_read_bool(np, "nvidia,tegra-core-regulator") &&
+	    !tegra->core_rdev) {
+		tegra->core_rdev = rdev;
+		return 0;
+	}
+
+	if (of_property_read_bool(np, "nvidia,tegra-rtc-regulator") &&
+	    !tegra->rtc_rdev) {
+		tegra->rtc_rdev = rdev;
+		return 0;
+	}
+
+	if (of_property_read_bool(np, "nvidia,tegra-cpu-regulator") &&
+	    !tegra->cpu_rdev) {
+		tegra->cpu_rdev = rdev;
+		return 0;
+	}
+
+	return -EINVAL;
+}
+
+static int tegra20_regulator_detach(struct regulator_coupler *coupler,
+				    struct regulator_dev *rdev)
+{
+	struct tegra_regulator_coupler *tegra = to_tegra_coupler(coupler);
+
+	if (tegra->core_rdev == rdev) {
+		tegra->core_rdev = NULL;
+		return 0;
+	}
+
+	if (tegra->rtc_rdev == rdev) {
+		tegra->rtc_rdev = NULL;
+		return 0;
+	}
+
+	if (tegra->cpu_rdev == rdev) {
+		tegra->cpu_rdev = NULL;
+		return 0;
+	}
+
+	return -EINVAL;
+}
+
+static struct tegra_regulator_coupler tegra20_coupler = {
+	.coupler = {
+		.attach_regulator = tegra20_regulator_attach,
+		.detach_regulator = tegra20_regulator_detach,
+		.balance_voltage = tegra20_regulator_balance_voltage,
+	},
+};
+
+static int __init tegra_regulator_coupler_init(void)
+{
+	if (!of_machine_is_compatible("nvidia,tegra20"))
+		return 0;
+
+	return regulator_coupler_register(&tegra20_coupler.coupler);
+}
+arch_initcall(tegra_regulator_coupler_init);
-- 
2.21.0

^ permalink raw reply related	[flat|nested] 21+ messages in thread

* [PATCH v2 8/8] soc/tegra: regulators: Add regulators coupler for Tegra30
  2019-06-03 23:58 [PATCH v2 0/8] Introduce customized regulators coupling Dmitry Osipenko
                   ` (6 preceding siblings ...)
  2019-06-03 23:59 ` [PATCH v2 7/8] soc/tegra: regulators: Add regulators coupler for Tegra20 Dmitry Osipenko
@ 2019-06-03 23:59 ` Dmitry Osipenko
  7 siblings, 0 replies; 21+ messages in thread
From: Dmitry Osipenko @ 2019-06-03 23:59 UTC (permalink / raw)
  To: Mark Brown, Thierry Reding, Jonathan Hunter, Peter De Schrijver,
	Rob Herring
  Cc: devicetree, linux-tegra, linux-kernel

Add regulators coupler for Tegra30 SoC's that performs voltage balancing
of a coupled regulators and thus provides voltage scaling functionality.

There are 2 coupled regulators on all Tegra30 SoC's: CORE and CPU. The
coupled regulator voltages shall be in a range of 300mV from each other
and CORE voltage shall be higher than the CPU by N mV, where N depends
on the CPU voltage.

Signed-off-by: Dmitry Osipenko <digetx@gmail.com>
---
 drivers/soc/tegra/Kconfig              |   6 +
 drivers/soc/tegra/Makefile             |   1 +
 drivers/soc/tegra/regulators-tegra30.c | 300 +++++++++++++++++++++++++
 3 files changed, 307 insertions(+)
 create mode 100644 drivers/soc/tegra/regulators-tegra30.c

diff --git a/drivers/soc/tegra/Kconfig b/drivers/soc/tegra/Kconfig
index 4453c982c0b9..6909c9433369 100644
--- a/drivers/soc/tegra/Kconfig
+++ b/drivers/soc/tegra/Kconfig
@@ -30,6 +30,7 @@ config ARCH_TEGRA_3x_SOC
 	select SOC_TEGRA_FLOWCTRL
 	select SOC_TEGRA_PMC
 	select TEGRA_TIMER
+	select SOC_TEGRA30_VOLTAGE_COUPLER if REGULATOR
 	help
 	  Support for NVIDIA Tegra T30 processor family, based on the
 	  ARM CortexA9MP CPU and the ARM PL310 L2 cache controller
@@ -140,3 +141,8 @@ config SOC_TEGRA20_VOLTAGE_COUPLER
 	bool "Voltage scaling support for Tegra20 SoC's"
 	depends on ARCH_TEGRA_2x_SOC
 	depends on REGULATOR
+
+config SOC_TEGRA30_VOLTAGE_COUPLER
+	bool "Voltage scaling support for Tegra30 SoC's"
+	depends on ARCH_TEGRA_3x_SOC
+	depends on REGULATOR
diff --git a/drivers/soc/tegra/Makefile b/drivers/soc/tegra/Makefile
index 9f0bdd53bef8..9c809c1814bd 100644
--- a/drivers/soc/tegra/Makefile
+++ b/drivers/soc/tegra/Makefile
@@ -6,3 +6,4 @@ obj-$(CONFIG_SOC_TEGRA_FLOWCTRL) += flowctrl.o
 obj-$(CONFIG_SOC_TEGRA_PMC) += pmc.o
 obj-$(CONFIG_SOC_TEGRA_POWERGATE_BPMP) += powergate-bpmp.o
 obj-$(CONFIG_SOC_TEGRA20_VOLTAGE_COUPLER) += regulators-tegra20.o
+obj-$(CONFIG_SOC_TEGRA30_VOLTAGE_COUPLER) += regulators-tegra30.o
diff --git a/drivers/soc/tegra/regulators-tegra30.c b/drivers/soc/tegra/regulators-tegra30.c
new file mode 100644
index 000000000000..26330f14126c
--- /dev/null
+++ b/drivers/soc/tegra/regulators-tegra30.c
@@ -0,0 +1,300 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Voltage regulators coupling resolver for NVIDIA Tegra30
+ *
+ * Copyright (C) 2019 GRATE-DRIVER project
+ */
+
+#define pr_fmt(fmt)	"tegra voltage-coupler: " fmt
+
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/of.h>
+#include <linux/regulator/driver.h>
+#include <linux/regulator/machine.h>
+
+#include <soc/tegra/fuse.h>
+
+struct tegra_regulator_coupler {
+	struct regulator_coupler coupler;
+	struct regulator_dev *core_rdev;
+	struct regulator_dev *cpu_rdev;
+	int core_min_uV;
+};
+
+static inline struct tegra_regulator_coupler *
+to_tegra_coupler(struct regulator_coupler *coupler)
+{
+	return container_of(coupler, struct tegra_regulator_coupler, coupler);
+}
+
+static int tegra30_core_limit(struct tegra_regulator_coupler *tegra,
+			      struct regulator_dev *core_rdev)
+{
+	int core_min_uV = 0;
+	int core_max_uV;
+	int core_cur_uV;
+	int err;
+
+	if (tegra->core_min_uV > 0)
+		return tegra->core_min_uV;
+
+	core_cur_uV = regulator_get_voltage_rdev(core_rdev);
+	if (core_cur_uV < 0)
+		return core_cur_uV;
+
+	core_max_uV = max(core_cur_uV, 1200000);
+
+	err = regulator_check_voltage(core_rdev, &core_min_uV, &core_max_uV);
+	if (err)
+		return err;
+
+	/*
+	 * Limit minimum CORE voltage to a value left from bootloader or,
+	 * if it's unreasonably low value, to the most common 1.2v or to
+	 * whatever maximum value defined via board's device-tree.
+	 */
+	tegra->core_min_uV = core_max_uV;
+
+	pr_info("core minimum voltage limited to %duV\n", tegra->core_min_uV);
+
+	return tegra->core_min_uV;
+}
+
+static int tegra30_core_cpu_limit(int cpu_uV)
+{
+	if (cpu_uV < 800000)
+		return 950000;
+
+	if (cpu_uV < 900000)
+		return 1000000;
+
+	if (cpu_uV < 1000000)
+		return 1100000;
+
+	if (cpu_uV < 1100000)
+		return 1200000;
+
+	if (cpu_uV < 1250000) {
+		switch (tegra_sku_info.cpu_speedo_id) {
+		case 0 ... 1:
+		case 4:
+		case 7 ... 8:
+			return 1200000;
+
+		default:
+			return 1300000;
+		}
+	}
+
+	return -EINVAL;
+}
+
+static int tegra30_voltage_update(struct tegra_regulator_coupler *tegra,
+				  struct regulator_dev *cpu_rdev,
+				  struct regulator_dev *core_rdev)
+{
+	int core_min_uV, core_max_uV = INT_MAX;
+	int cpu_min_uV, cpu_max_uV = INT_MAX;
+	int core_min_limited_uV;
+	int core_target_uV;
+	int cpu_target_uV;
+	int core_max_step;
+	int cpu_max_step;
+	int max_spread;
+	int core_uV;
+	int cpu_uV;
+	int err;
+
+	/*
+	 * CPU voltage should not got lower than 300mV from the CORE.
+	 * CPU voltage should stay below the CORE by 100mV+, depending
+	 * by the CORE voltage. This applies to all Tegra30 SoC's.
+	 */
+	max_spread = cpu_rdev->constraints->max_spread[0];
+	cpu_max_step = cpu_rdev->constraints->max_uV_step;
+	core_max_step = core_rdev->constraints->max_uV_step;
+
+	if (!max_spread) {
+		pr_err_once("cpu-core max-spread is undefined in device-tree\n");
+		max_spread = 300000;
+	}
+
+	if (!cpu_max_step) {
+		pr_err_once("cpu max-step is undefined in device-tree\n");
+		cpu_max_step = 150000;
+	}
+
+	if (!core_max_step) {
+		pr_err_once("core max-step is undefined in device-tree\n");
+		core_max_step = 150000;
+	}
+
+	/*
+	 * The CORE voltage scaling is currently not hooked up in drivers,
+	 * hence we will limit the minimum CORE voltage to a reasonable value.
+	 * This should be good enough for the time being.
+	 */
+	core_min_uV = tegra30_core_limit(tegra, core_rdev);
+	if (core_min_uV < 0)
+		return core_min_uV;
+
+	err = regulator_check_consumers(core_rdev, &core_min_uV, &core_max_uV,
+					PM_SUSPEND_ON);
+	if (err)
+		return err;
+
+	cpu_min_uV = core_min_uV - max_spread;
+
+	err = regulator_check_consumers(cpu_rdev, &cpu_min_uV, &cpu_max_uV,
+					PM_SUSPEND_ON);
+	if (err)
+		return err;
+
+	err = regulator_check_voltage(cpu_rdev, &cpu_min_uV, &cpu_max_uV);
+	if (err)
+		return err;
+
+	cpu_uV = regulator_get_voltage_rdev(cpu_rdev);
+	if (cpu_uV < 0)
+		return cpu_uV;
+
+	core_uV = regulator_get_voltage_rdev(core_rdev);
+	if (core_uV < 0)
+		return core_uV;
+
+	/*
+	 * Bootloader shall set up voltages correctly, but if it
+	 * happens that there is a violation, then try to fix it
+	 * at first.
+	 */
+	core_min_limited_uV = tegra30_core_cpu_limit(cpu_uV);
+	if (core_min_limited_uV < 0)
+		return core_min_limited_uV;
+
+	core_min_uV = max(core_min_uV, tegra30_core_cpu_limit(cpu_min_uV));
+
+	err = regulator_check_voltage(core_rdev, &core_min_uV, &core_max_uV);
+	if (err)
+		return err;
+
+	if (core_min_limited_uV > core_uV) {
+		pr_err("core voltage constraint violated: %d %d %d\n",
+		       core_uV, core_min_limited_uV, cpu_uV);
+		goto update_core;
+	}
+
+	while (cpu_uV != cpu_min_uV || core_uV != core_min_uV) {
+		if (cpu_uV < cpu_min_uV) {
+			cpu_target_uV = min(cpu_uV + cpu_max_step, cpu_min_uV);
+		} else {
+			cpu_target_uV = max(cpu_uV - cpu_max_step, cpu_min_uV);
+			cpu_target_uV = max(core_uV - max_spread, cpu_target_uV);
+		}
+
+		err = regulator_set_voltage_rdev(cpu_rdev,
+						 cpu_target_uV,
+						 cpu_max_uV,
+						 PM_SUSPEND_ON);
+		if (err)
+			return err;
+
+		cpu_uV = cpu_target_uV;
+update_core:
+		core_min_limited_uV = tegra30_core_cpu_limit(cpu_uV);
+		if (core_min_limited_uV < 0)
+			return core_min_limited_uV;
+
+		core_target_uV = max(core_min_limited_uV, core_min_uV);
+
+		if (core_uV < core_target_uV) {
+			core_target_uV = min(core_target_uV, core_uV + core_max_step);
+			core_target_uV = min(core_target_uV, cpu_uV + max_spread);
+		} else {
+			core_target_uV = max(core_target_uV, core_uV - core_max_step);
+		}
+
+		err = regulator_set_voltage_rdev(core_rdev,
+						 core_target_uV,
+						 core_max_uV,
+						 PM_SUSPEND_ON);
+		if (err)
+			return err;
+
+		core_uV = core_target_uV;
+	}
+
+	return 0;
+}
+
+static int tegra30_regulator_balance_voltage(struct regulator_coupler *coupler,
+					     struct regulator_dev *rdev,
+					     suspend_state_t state)
+{
+	struct tegra_regulator_coupler *tegra = to_tegra_coupler(coupler);
+	struct regulator_dev *core_rdev = tegra->core_rdev;
+	struct regulator_dev *cpu_rdev = tegra->cpu_rdev;
+
+	if ((core_rdev != rdev && cpu_rdev != rdev) || state != PM_SUSPEND_ON) {
+		pr_err("regulators are not coupled properly\n");
+		return -EINVAL;
+	}
+
+	return tegra30_voltage_update(tegra, cpu_rdev, core_rdev);
+}
+
+static int tegra30_regulator_attach(struct regulator_coupler *coupler,
+				    struct regulator_dev *rdev)
+{
+	struct tegra_regulator_coupler *tegra = to_tegra_coupler(coupler);
+	struct device_node *np = rdev->dev.of_node;
+
+	if (of_property_read_bool(np, "nvidia,tegra-core-regulator") &&
+	    !tegra->core_rdev) {
+		tegra->core_rdev = rdev;
+		return 0;
+	}
+
+	if (of_property_read_bool(np, "nvidia,tegra-cpu-regulator") &&
+	    !tegra->cpu_rdev) {
+		tegra->cpu_rdev = rdev;
+		return 0;
+	}
+
+	return -EINVAL;
+}
+
+static int tegra30_regulator_detach(struct regulator_coupler *coupler,
+				    struct regulator_dev *rdev)
+{
+	struct tegra_regulator_coupler *tegra = to_tegra_coupler(coupler);
+
+	if (tegra->core_rdev == rdev) {
+		tegra->core_rdev = NULL;
+		return 0;
+	}
+
+	if (tegra->cpu_rdev == rdev) {
+		tegra->cpu_rdev = NULL;
+		return 0;
+	}
+
+	return -EINVAL;
+}
+
+static struct tegra_regulator_coupler tegra30_coupler = {
+	.coupler = {
+		.attach_regulator = tegra30_regulator_attach,
+		.detach_regulator = tegra30_regulator_detach,
+		.balance_voltage = tegra30_regulator_balance_voltage,
+	},
+};
+
+static int __init tegra_regulator_coupler_init(void)
+{
+	if (!of_machine_is_compatible("nvidia,tegra30"))
+		return 0;
+
+	return regulator_coupler_register(&tegra30_coupler.coupler);
+}
+arch_initcall(tegra_regulator_coupler_init);
-- 
2.21.0

^ permalink raw reply related	[flat|nested] 21+ messages in thread

* Re: [PATCH v2 1/8] regulator: core: Introduce API for regulators coupling customization
  2019-06-03 23:58 ` [PATCH v2 1/8] regulator: core: Introduce API for regulators coupling customization Dmitry Osipenko
@ 2019-06-17 15:23   ` Mark Brown
  2019-06-18 16:25     ` Dmitry Osipenko
  0 siblings, 1 reply; 21+ messages in thread
From: Mark Brown @ 2019-06-17 15:23 UTC (permalink / raw)
  To: Dmitry Osipenko
  Cc: Thierry Reding, Jonathan Hunter, Peter De Schrijver, Rob Herring,
	devicetree, linux-tegra, linux-kernel

[-- Attachment #1: Type: text/plain, Size: 976 bytes --]

On Tue, Jun 04, 2019 at 02:58:57AM +0300, Dmitry Osipenko wrote:
> 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 SoC's which besides the
> max-spreading have other restrictions that must be adhered. Introduce API
> that allow platforms to provide their own customized coupling algorithms.

So, I don't completely hate this and I'm not sure there's any good
solutions here...

> + * @balance_voltage: Callback invoked when voltage of a coupled regulator is
> + *                   changing. The callee should perform voltage balancing
> + *                   and change voltage of the coupled regulators.

Some documentation on what the context for calling this is (locking and
so on) and how it should do the adjustments it wants to do would be
good.

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 488 bytes --]

^ permalink raw reply	[flat|nested] 21+ messages in thread

* Re: [PATCH v2 2/8] regulator: core: Parse max-spread value per regulator couple
  2019-06-03 23:58 ` [PATCH v2 2/8] regulator: core: Parse max-spread value per regulator couple Dmitry Osipenko
@ 2019-06-17 15:25   ` Mark Brown
  2019-06-18 16:27     ` Dmitry Osipenko
  0 siblings, 1 reply; 21+ messages in thread
From: Mark Brown @ 2019-06-17 15:25 UTC (permalink / raw)
  To: Dmitry Osipenko
  Cc: Thierry Reding, Jonathan Hunter, Peter De Schrijver, Rob Herring,
	devicetree, linux-tegra, linux-kernel

[-- Attachment #1: Type: text/plain, Size: 487 bytes --]

On Tue, Jun 04, 2019 at 02:58:58AM +0300, Dmitry Osipenko wrote:

> +#include <linux/regulator/driver.h>
>  #include <linux/regulator/consumer.h>
>  #include <linux/suspend.h>
>  
> @@ -156,7 +157,7 @@ struct regulation_constraints {
>  	int system_load;
>  
>  	/* used for coupled regulators */
> -	int max_spread;
> +	int max_spread[MAX_COUPLED];

I'd rather move MAX_COUPLED into a separate header than include driver.h
so we've got a bit more compile time sepration.

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 488 bytes --]

^ permalink raw reply	[flat|nested] 21+ messages in thread

* Re: [PATCH v2 3/8] regulator: core: Expose some of core functions
  2019-06-03 23:58 ` [PATCH v2 3/8] regulator: core: Expose some of core functions Dmitry Osipenko
@ 2019-06-17 15:32   ` Mark Brown
  2019-06-18 16:29     ` Dmitry Osipenko
  0 siblings, 1 reply; 21+ messages in thread
From: Mark Brown @ 2019-06-17 15:32 UTC (permalink / raw)
  To: Dmitry Osipenko
  Cc: Thierry Reding, Jonathan Hunter, Peter De Schrijver, Rob Herring,
	devicetree, linux-tegra, linux-kernel

[-- Attachment #1: Type: text/plain, Size: 440 bytes --]

On Tue, Jun 04, 2019 at 02:58:59AM +0300, Dmitry Osipenko wrote:
> Expose some of internal functions that are required for implementation of
> customized regulator couplers.

>  include/linux/regulator/driver.h | 11 ++++++

I'm a bit dubious about exposing any of this but there's no way we
should be exposing it directly to random drivers, this needs a new
header so it's more obvious that normal code shouldn't be using these
interfaces.

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 488 bytes --]

^ permalink raw reply	[flat|nested] 21+ messages in thread

* Re: [PATCH v2 6/8] regulator: core: Don't attach generic coupler to Tegra SoC regulators
  2019-06-03 23:59 ` [PATCH v2 6/8] regulator: core: Don't attach generic coupler to Tegra SoC regulators Dmitry Osipenko
@ 2019-06-17 15:44   ` Mark Brown
  2019-06-18 16:47     ` Dmitry Osipenko
  0 siblings, 1 reply; 21+ messages in thread
From: Mark Brown @ 2019-06-17 15:44 UTC (permalink / raw)
  To: Dmitry Osipenko
  Cc: Thierry Reding, Jonathan Hunter, Peter De Schrijver, Rob Herring,
	devicetree, linux-tegra, linux-kernel

[-- Attachment #1: Type: text/plain, Size: 931 bytes --]

On Tue, Jun 04, 2019 at 02:59:02AM +0300, Dmitry Osipenko wrote:

>  static int generic_coupler_attach(struct regulator_coupler *coupler,
>  				  struct regulator_dev *rdev)
>  {
> +	/*
> +	 * Generic coupler isn't suitable for NVIVIA Tegra SoC's, at least
> +	 * for now. Hence filter out the unwanted regulators as they shall be
> +	 * managed by a platform-specific coupler.
> +	 */
> +	if (of_property_read_bool(rdev->dev.of_node, "tegra-core-regulator") ||
> +	    of_property_read_bool(rdev->dev.of_node, "tegra-rtc-regulator") ||
> +	    of_property_read_bool(rdev->dev.of_node, "tegra-cpu-regulator"))
> +		return -EPERM;
> +

I'm having a hard time loving this as it requires explicit DT changes
for implementation.  I'm thinking that since the couplers are going to
need to be built in it'd be better to make sure that any custom ones get
registered first and then only bind the generic coupler to anything they
reject.

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 488 bytes --]

^ permalink raw reply	[flat|nested] 21+ messages in thread

* Re: [PATCH v2 7/8] soc/tegra: regulators: Add regulators coupler for Tegra20
  2019-06-03 23:59 ` [PATCH v2 7/8] soc/tegra: regulators: Add regulators coupler for Tegra20 Dmitry Osipenko
@ 2019-06-17 15:46   ` Mark Brown
  2019-06-18 16:52     ` Dmitry Osipenko
  0 siblings, 1 reply; 21+ messages in thread
From: Mark Brown @ 2019-06-17 15:46 UTC (permalink / raw)
  To: Dmitry Osipenko
  Cc: Thierry Reding, Jonathan Hunter, Peter De Schrijver, Rob Herring,
	devicetree, linux-tegra, linux-kernel

[-- Attachment #1: Type: text/plain, Size: 545 bytes --]

On Tue, Jun 04, 2019 at 02:59:03AM +0300, Dmitry Osipenko wrote:

> +config SOC_TEGRA20_VOLTAGE_COUPLER
> +	bool "Voltage scaling support for Tegra20 SoC's"

SoCs

> +	depends on ARCH_TEGRA_2x_SOC

Can we have an || COMPILE_TEST here please?

> +++ b/drivers/soc/tegra/regulators-tegra20.c
> @@ -0,0 +1,348 @@
> +// SPDX-License-Identifier: GPL-2.0+
> +/*
> + * Voltage regulators coupling resolver for NVIDIA Tegra20
> + *
> + * Copyright (C) 2019 GRATE-DRIVER project

Please make the entire comment a C++ one so things look more
intentional.

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 488 bytes --]

^ permalink raw reply	[flat|nested] 21+ messages in thread

* Re: [PATCH v2 1/8] regulator: core: Introduce API for regulators coupling customization
  2019-06-17 15:23   ` Mark Brown
@ 2019-06-18 16:25     ` Dmitry Osipenko
  0 siblings, 0 replies; 21+ messages in thread
From: Dmitry Osipenko @ 2019-06-18 16:25 UTC (permalink / raw)
  To: Mark Brown
  Cc: Thierry Reding, Jonathan Hunter, Peter De Schrijver, Rob Herring,
	devicetree, linux-tegra, linux-kernel

17.06.2019 18:23, Mark Brown пишет:
> On Tue, Jun 04, 2019 at 02:58:57AM +0300, Dmitry Osipenko wrote:
>> 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 SoC's which besides the
>> max-spreading have other restrictions that must be adhered. Introduce API
>> that allow platforms to provide their own customized coupling algorithms.
> 
> So, I don't completely hate this and I'm not sure there's any good
> solutions here...

Thanks!

>> + * @balance_voltage: Callback invoked when voltage of a coupled regulator is
>> + *                   changing. The callee should perform voltage balancing
>> + *                   and change voltage of the coupled regulators.
> 
> Some documentation on what the context for calling this is (locking and
> so on) and how it should do the adjustments it wants to do would be
> good.
> 

Okay, I'll extend the doc.

^ permalink raw reply	[flat|nested] 21+ messages in thread

* Re: [PATCH v2 2/8] regulator: core: Parse max-spread value per regulator couple
  2019-06-17 15:25   ` Mark Brown
@ 2019-06-18 16:27     ` Dmitry Osipenko
  0 siblings, 0 replies; 21+ messages in thread
From: Dmitry Osipenko @ 2019-06-18 16:27 UTC (permalink / raw)
  To: Mark Brown
  Cc: Thierry Reding, Jonathan Hunter, Peter De Schrijver, Rob Herring,
	devicetree, linux-tegra, linux-kernel

17.06.2019 18:25, Mark Brown пишет:
> On Tue, Jun 04, 2019 at 02:58:58AM +0300, Dmitry Osipenko wrote:
> 
>> +#include <linux/regulator/driver.h>
>>  #include <linux/regulator/consumer.h>
>>  #include <linux/suspend.h>
>>  
>> @@ -156,7 +157,7 @@ struct regulation_constraints {
>>  	int system_load;
>>  
>>  	/* used for coupled regulators */
>> -	int max_spread;
>> +	int max_spread[MAX_COUPLED];
> 
> I'd rather move MAX_COUPLED into a separate header than include driver.h
> so we've got a bit more compile time sepration.
> 

I'll address this in the next revision.

^ permalink raw reply	[flat|nested] 21+ messages in thread

* Re: [PATCH v2 3/8] regulator: core: Expose some of core functions
  2019-06-17 15:32   ` Mark Brown
@ 2019-06-18 16:29     ` Dmitry Osipenko
  0 siblings, 0 replies; 21+ messages in thread
From: Dmitry Osipenko @ 2019-06-18 16:29 UTC (permalink / raw)
  To: Mark Brown
  Cc: Thierry Reding, Jonathan Hunter, Peter De Schrijver, Rob Herring,
	devicetree, linux-tegra, linux-kernel

17.06.2019 18:32, Mark Brown пишет:
> On Tue, Jun 04, 2019 at 02:58:59AM +0300, Dmitry Osipenko wrote:
>> Expose some of internal functions that are required for implementation of
>> customized regulator couplers.
> 
>>  include/linux/regulator/driver.h | 11 ++++++
> 
> I'm a bit dubious about exposing any of this but there's no way we
> should be exposing it directly to random drivers, this needs a new
> header so it's more obvious that normal code shouldn't be using these
> interfaces.
> 

That's a good call! Will address it in the v3.

^ permalink raw reply	[flat|nested] 21+ messages in thread

* Re: [PATCH v2 6/8] regulator: core: Don't attach generic coupler to Tegra SoC regulators
  2019-06-17 15:44   ` Mark Brown
@ 2019-06-18 16:47     ` Dmitry Osipenko
  0 siblings, 0 replies; 21+ messages in thread
From: Dmitry Osipenko @ 2019-06-18 16:47 UTC (permalink / raw)
  To: Mark Brown
  Cc: Thierry Reding, Jonathan Hunter, Peter De Schrijver, Rob Herring,
	devicetree, linux-tegra, linux-kernel

17.06.2019 18:44, Mark Brown пишет:
> On Tue, Jun 04, 2019 at 02:59:02AM +0300, Dmitry Osipenko wrote:
> 
>>  static int generic_coupler_attach(struct regulator_coupler *coupler,
>>  				  struct regulator_dev *rdev)
>>  {
>> +	/*
>> +	 * Generic coupler isn't suitable for NVIVIA Tegra SoC's, at least
>> +	 * for now. Hence filter out the unwanted regulators as they shall be
>> +	 * managed by a platform-specific coupler.
>> +	 */
>> +	if (of_property_read_bool(rdev->dev.of_node, "tegra-core-regulator") ||
>> +	    of_property_read_bool(rdev->dev.of_node, "tegra-rtc-regulator") ||
>> +	    of_property_read_bool(rdev->dev.of_node, "tegra-cpu-regulator"))
>> +		return -EPERM;
>> +
> 
> I'm having a hard time loving this as it requires explicit DT changes
> for implementation.  I'm thinking that since the couplers are going to
> need to be built in it'd be better to make sure that any custom ones get
> registered first and then only bind the generic coupler to anything they
> reject.
> 

Actually, the current implementation at first tries all the custom couplers and then
falls back to the generic.

What's currently missing is that any error code of the attach_regulator() callback is
treated as "go try next coupler", but we probably need a special error code for the
"skip me" case to differentiate from a error that should abort the coupler's lookup
process.

I'll re-work this in v3.

^ permalink raw reply	[flat|nested] 21+ messages in thread

* Re: [PATCH v2 7/8] soc/tegra: regulators: Add regulators coupler for Tegra20
  2019-06-17 15:46   ` Mark Brown
@ 2019-06-18 16:52     ` Dmitry Osipenko
  2019-06-18 17:14       ` Mark Brown
  0 siblings, 1 reply; 21+ messages in thread
From: Dmitry Osipenko @ 2019-06-18 16:52 UTC (permalink / raw)
  To: Mark Brown
  Cc: Thierry Reding, Jonathan Hunter, Peter De Schrijver, Rob Herring,
	devicetree, linux-tegra, linux-kernel

17.06.2019 18:46, Mark Brown пишет:
> On Tue, Jun 04, 2019 at 02:59:03AM +0300, Dmitry Osipenko wrote:
> 
>> +config SOC_TEGRA20_VOLTAGE_COUPLER
>> +	bool "Voltage scaling support for Tegra20 SoC's"
> 
> SoCs
> 
>> +	depends on ARCH_TEGRA_2x_SOC
> 
> Can we have an || COMPILE_TEST here please?

Good call!

>> +++ b/drivers/soc/tegra/regulators-tegra20.c
>> @@ -0,0 +1,348 @@
>> +// SPDX-License-Identifier: GPL-2.0+
>> +/*
>> + * Voltage regulators coupling resolver for NVIDIA Tegra20
>> + *
>> + * Copyright (C) 2019 GRATE-DRIVER project
> 
> Please make the entire comment a C++ one so things look more
> intentional.
> 

This is actually a correct SPDX comment style for the source files, accordingly to
checkpatch. While the C++ style should be used for header files only. I'm not
following the SPDX effort much, don't know from where these SPDX-style rules are
coming from.

^ permalink raw reply	[flat|nested] 21+ messages in thread

* Re: [PATCH v2 7/8] soc/tegra: regulators: Add regulators coupler for Tegra20
  2019-06-18 16:52     ` Dmitry Osipenko
@ 2019-06-18 17:14       ` Mark Brown
  2019-06-18 17:19         ` Dmitry Osipenko
  0 siblings, 1 reply; 21+ messages in thread
From: Mark Brown @ 2019-06-18 17:14 UTC (permalink / raw)
  To: Dmitry Osipenko
  Cc: Thierry Reding, Jonathan Hunter, Peter De Schrijver, Rob Herring,
	devicetree, linux-tegra, linux-kernel

[-- Attachment #1: Type: text/plain, Size: 744 bytes --]

On Tue, Jun 18, 2019 at 07:52:06PM +0300, Dmitry Osipenko wrote:
> 17.06.2019 18:46, Mark Brown пишет:

> >> +// SPDX-License-Identifier: GPL-2.0+
> >> +/*
> >> + * Voltage regulators coupling resolver for NVIDIA Tegra20
> >> + *
> >> + * Copyright (C) 2019 GRATE-DRIVER project

> > Please make the entire comment a C++ one so things look more
> > intentional.

> This is actually a correct SPDX comment style for the source files, accordingly to
> checkpatch. While the C++ style should be used for header files only. I'm not
> following the SPDX effort much, don't know from where these SPDX-style rules are
> coming from.

The SPDX bit is fine, what I'm saying is make the rest of the comment
also be a C++ comment.

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 488 bytes --]

^ permalink raw reply	[flat|nested] 21+ messages in thread

* Re: [PATCH v2 7/8] soc/tegra: regulators: Add regulators coupler for Tegra20
  2019-06-18 17:14       ` Mark Brown
@ 2019-06-18 17:19         ` Dmitry Osipenko
  0 siblings, 0 replies; 21+ messages in thread
From: Dmitry Osipenko @ 2019-06-18 17:19 UTC (permalink / raw)
  To: Mark Brown
  Cc: Thierry Reding, Jonathan Hunter, Peter De Schrijver, Rob Herring,
	devicetree, linux-tegra, linux-kernel

18.06.2019 20:14, Mark Brown пишет:
> On Tue, Jun 18, 2019 at 07:52:06PM +0300, Dmitry Osipenko wrote:
>> 17.06.2019 18:46, Mark Brown пишет:
> 
>>>> +// SPDX-License-Identifier: GPL-2.0+
>>>> +/*
>>>> + * Voltage regulators coupling resolver for NVIDIA Tegra20
>>>> + *
>>>> + * Copyright (C) 2019 GRATE-DRIVER project
> 
>>> Please make the entire comment a C++ one so things look more
>>> intentional.
> 
>> This is actually a correct SPDX comment style for the source files, accordingly to
>> checkpatch. While the C++ style should be used for header files only. I'm not
>> following the SPDX effort much, don't know from where these SPDX-style rules are
>> coming from.
> 
> The SPDX bit is fine, what I'm saying is make the rest of the comment
> also be a C++ comment.
> 

Ah, I just realized that the "// " is the C++ comment style and not C. I can turn it
all into the C++, no problems.

^ permalink raw reply	[flat|nested] 21+ messages in thread

end of thread, other threads:[~2019-06-18 17:19 UTC | newest]

Thread overview: 21+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2019-06-03 23:58 [PATCH v2 0/8] Introduce customized regulators coupling Dmitry Osipenko
2019-06-03 23:58 ` [PATCH v2 1/8] regulator: core: Introduce API for regulators coupling customization Dmitry Osipenko
2019-06-17 15:23   ` Mark Brown
2019-06-18 16:25     ` Dmitry Osipenko
2019-06-03 23:58 ` [PATCH v2 2/8] regulator: core: Parse max-spread value per regulator couple Dmitry Osipenko
2019-06-17 15:25   ` Mark Brown
2019-06-18 16:27     ` Dmitry Osipenko
2019-06-03 23:58 ` [PATCH v2 3/8] regulator: core: Expose some of core functions Dmitry Osipenko
2019-06-17 15:32   ` Mark Brown
2019-06-18 16:29     ` Dmitry Osipenko
2019-06-03 23:59 ` [PATCH v2 4/8] regulator: core Bump MAX_COUPLED to 3 Dmitry Osipenko
2019-06-03 23:59 ` [PATCH v2 5/8] dt-bindings: regulator: Document regulators coupling of NVIDIA Tegra20/30 SoC's Dmitry Osipenko
2019-06-03 23:59 ` [PATCH v2 6/8] regulator: core: Don't attach generic coupler to Tegra SoC regulators Dmitry Osipenko
2019-06-17 15:44   ` Mark Brown
2019-06-18 16:47     ` Dmitry Osipenko
2019-06-03 23:59 ` [PATCH v2 7/8] soc/tegra: regulators: Add regulators coupler for Tegra20 Dmitry Osipenko
2019-06-17 15:46   ` Mark Brown
2019-06-18 16:52     ` Dmitry Osipenko
2019-06-18 17:14       ` Mark Brown
2019-06-18 17:19         ` Dmitry Osipenko
2019-06-03 23:59 ` [PATCH v2 8/8] soc/tegra: regulators: Add regulators coupler for Tegra30 Dmitry Osipenko

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.