diff mbox series

[RFC,v1,6/6] soc/tegra: regulators: Add regulators coupler for Tegra30

Message ID 20190414175939.12368-7-digetx@gmail.com
State Deferred
Headers show
Series Introduce machine-specific regulators coupling API | expand

Commit Message

Dmitry Osipenko April 14, 2019, 5:59 p.m. UTC
Add regulators coupler for Tegra30 SoC's that performs voltage balancing
of a coupled regulators and thus provides voltage scaling functionality.

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

Comments

Mark Brown May 8, 2019, 7:58 a.m. UTC | #1
On Sun, Apr 14, 2019 at 08:59:39PM +0300, Dmitry Osipenko wrote:
> Add regulators coupler for Tegra30 SoC's that performs voltage balancing
> of a coupled regulators and thus provides voltage scaling functionality.

Same here, what are the requirements this is implementing?
Dmitry Osipenko May 8, 2019, 1:27 p.m. UTC | #2
08.05.2019 10:58, Mark Brown пишет:
> On Sun, Apr 14, 2019 at 08:59:39PM +0300, Dmitry Osipenko wrote:
>> Add regulators coupler for Tegra30 SoC's that performs voltage balancing
>> of a coupled regulators and thus provides voltage scaling functionality.
> 
> Same here, what are the requirements this is implementing?
> 

There are two coupled regulators: CPU and CORE.

Constraints:

1) The max-spread voltage is 300mV.

2) CORE voltage must be higher than the CPU by at least N mV, where N
varies depending on the CPU voltage.

3) There is a constraint on the maximum CORE voltage depending on
hardware model/revision (cpu_speedo_id) where a higher voltages
apparently may cause physical damage, so it's better to hardcode the
limitation in the code rather than to rely on a board's device-tree
description. This constraint is quite vaguely defined in the downstream
kernel, I'm not really sure if it's solely about the hardware safety.
Mark Brown May 12, 2019, 9:04 a.m. UTC | #3
On Wed, May 08, 2019 at 04:27:42PM +0300, Dmitry Osipenko wrote:

> Constraints:

> 1) The max-spread voltage is 300mV.

> 2) CORE voltage must be higher than the CPU by at least N mV, where N
> varies depending on the CPU voltage.

Those seem like they should be doable in generic code, though the fact
that the constraint is variable makes it annoying to specify - otherwise
it'd just be a minimum and maximum spread.  I'm not really coming up
with any great ideas right now, it's getting into OPP type territory but
it sounds like there's more flexibility for ramping the core voltage so
you'd end up with silly numbers of OPPs.

> 3) There is a constraint on the maximum CORE voltage depending on
> hardware model/revision (cpu_speedo_id) where a higher voltages
> apparently may cause physical damage, so it's better to hardcode the
> limitation in the code rather than to rely on a board's device-tree
> description. This constraint is quite vaguely defined in the downstream
> kernel, I'm not really sure if it's solely about the hardware safety.

I'd expect this to be enforced by the cpufreq driver just not selecting
higher voltages on affected parts.
Dmitry Osipenko May 12, 2019, 6:29 p.m. UTC | #4
12.05.2019 12:04, Mark Brown пишет:
> On Wed, May 08, 2019 at 04:27:42PM +0300, Dmitry Osipenko wrote:
> 
>> Constraints:
> 
>> 1) The max-spread voltage is 300mV.
> 
>> 2) CORE voltage must be higher than the CPU by at least N mV, where N
>> varies depending on the CPU voltage.
> 
> Those seem like they should be doable in generic code, though the fact
> that the constraint is variable makes it annoying to specify - otherwise
> it'd just be a minimum and maximum spread.  I'm not really coming up
> with any great ideas right now, it's getting into OPP type territory but
> it sounds like there's more flexibility for ramping the core voltage so
> you'd end up with silly numbers of OPPs.

The OPP shouldn't have to do anything in regards to the regulators
coupling. The whole idea of the regulators coupling is to make device
drivers to not churn with the coupling. The coupling in this case is
specific to SoC and not to a particular board.

I think the current approach with the customized regulators coupler is
the best solution for the time being. We may consider something more
generic if there will be other users with a similar coupling
requirements, otherwise it's quite difficult to judge what is "generic".
Do you agree?

>> 3) There is a constraint on the maximum CORE voltage depending on
>> hardware model/revision (cpu_speedo_id) where a higher voltages
>> apparently may cause physical damage, so it's better to hardcode the
>> limitation in the code rather than to rely on a board's device-tree
>> description. This constraint is quite vaguely defined in the downstream
>> kernel, I'm not really sure if it's solely about the hardware safety.
> 
> I'd expect this to be enforced by the cpufreq driver just not selecting
> higher voltages on affected parts.
> 

CPUFreq driver will only handle the CPU regulator and it won't know
anything about the CORE.

Anyway, please scratch the third constraint, I messed up it with the
other *minimum* CORE voltage constraint detail which makes the minimum
voltage to depend on the hardware version in addition to the CPU voltage.
Mark Brown May 13, 2019, 5:40 p.m. UTC | #5
On Sun, May 12, 2019 at 09:29:54PM +0300, Dmitry Osipenko wrote:
> 12.05.2019 12:04, Mark Brown пишет:
> > On Wed, May 08, 2019 at 04:27:42PM +0300, Dmitry Osipenko wrote:

> > Those seem like they should be doable in generic code, though the fact
> > that the constraint is variable makes it annoying to specify - otherwise
> > it'd just be a minimum and maximum spread.  I'm not really coming up
> > with any great ideas right now, it's getting into OPP type territory but
> > it sounds like there's more flexibility for ramping the core voltage so
> > you'd end up with silly numbers of OPPs.

> The OPP shouldn't have to do anything in regards to the regulators
> coupling. The whole idea of the regulators coupling is to make device
> drivers to not churn with the coupling. The coupling in this case is
> specific to SoC and not to a particular board.

The thing with OPPs is that they specify a whole table of values that
work together including regulator settings, the result being that you
have many fewer options but don't need to think about constraints.

> I think the current approach with the customized regulators coupler is
> the best solution for the time being. We may consider something more
> generic if there will be other users with a similar coupling
> requirements, otherwise it's quite difficult to judge what is "generic".
> Do you agree?

Some of the constraints (like having drivers loaded) are kind of fun...
Dmitry Osipenko May 14, 2019, 6:30 p.m. UTC | #6
13.05.2019 20:40, Mark Brown пишет:
> On Sun, May 12, 2019 at 09:29:54PM +0300, Dmitry Osipenko wrote:
>> 12.05.2019 12:04, Mark Brown пишет:
>>> On Wed, May 08, 2019 at 04:27:42PM +0300, Dmitry Osipenko wrote:
> 
>>> Those seem like they should be doable in generic code, though the fact
>>> that the constraint is variable makes it annoying to specify - otherwise
>>> it'd just be a minimum and maximum spread.  I'm not really coming up
>>> with any great ideas right now, it's getting into OPP type territory but
>>> it sounds like there's more flexibility for ramping the core voltage so
>>> you'd end up with silly numbers of OPPs.
> 
>> The OPP shouldn't have to do anything in regards to the regulators
>> coupling. The whole idea of the regulators coupling is to make device
>> drivers to not churn with the coupling. The coupling in this case is
>> specific to SoC and not to a particular board.
> 
> The thing with OPPs is that they specify a whole table of values that
> work together including regulator settings, the result being that you
> have many fewer options but don't need to think about constraints.

I'm afraid this is just a way of abusing the OPP's. I actually already
had variant of the CPUFreq driver where it was managing all of the
coupled regulators and gave up on it because it's just not very
practical and adds a lot of unnecessary churning into the code. Note
that it's just the CPUFreq driver, there are quite a lot of other (CORE)
drivers as well and there are a lot of voltage combinations because OPP
entries are also specific to a range of hardware versions.

>> I think the current approach with the customized regulators coupler is
>> the best solution for the time being. We may consider something more
>> generic if there will be other users with a similar coupling
>> requirements, otherwise it's quite difficult to judge what is "generic".
>> Do you agree?
> 
> Some of the constraints (like having drivers loaded) are kind of fun...
> 

AFAIK, there is no good solution in upstream kernel for that problem
yet. Maybe it will be possible to reset hardware into a some more
predictable state early during kernel's boot for the start, will see.
Mark Brown May 15, 2019, 9:05 a.m. UTC | #7
On Tue, May 14, 2019 at 09:30:05PM +0300, Dmitry Osipenko wrote:
> 13.05.2019 20:40, Mark Brown пишет:

> > The thing with OPPs is that they specify a whole table of values that
> > work together including regulator settings, the result being that you
> > have many fewer options but don't need to think about constraints.

> I'm afraid this is just a way of abusing the OPP's. I actually already

There's nothing wrong with handling regulators in an OPP, that's a
totally normal thing.
Dmitry Osipenko May 15, 2019, 11:44 a.m. UTC | #8
15.05.2019 12:05, Mark Brown пишет:
> On Tue, May 14, 2019 at 09:30:05PM +0300, Dmitry Osipenko wrote:
>> 13.05.2019 20:40, Mark Brown пишет:
> 
>>> The thing with OPPs is that they specify a whole table of values that
>>> work together including regulator settings, the result being that you
>>> have many fewer options but don't need to think about constraints.
> 
>> I'm afraid this is just a way of abusing the OPP's. I actually already
> 
> There's nothing wrong with handling regulators in an OPP, that's a
> totally normal thing.
> 

Only if those regulators are directly related to the hardware unit,
which is not the case here. Regulators coupling is the right abstraction
that glues things together, there is absolutely no need in trying to
make workarounds using OPP.
Mark Brown May 15, 2019, 2:56 p.m. UTC | #9
On Wed, May 15, 2019 at 02:44:33PM +0300, Dmitry Osipenko wrote:
> 15.05.2019 12:05, Mark Brown пишет:
> > On Tue, May 14, 2019 at 09:30:05PM +0300, Dmitry Osipenko wrote:

> >> I'm afraid this is just a way of abusing the OPP's. I actually already

> > There's nothing wrong with handling regulators in an OPP, that's a
> > totally normal thing.

> Only if those regulators are directly related to the hardware unit,
> which is not the case here. Regulators coupling is the right abstraction
> that glues things together, there is absolutely no need in trying to
> make workarounds using OPP.

The thing with OPPs is that they are often system level rather than
related to one specific parrt of the device - one of the reasons people
use them is that they eliminate the needs to think about dynamic
combinations of things and instead pick a suitable configuration off a
relatively short menu.  This makes both validation and runtime easier.
diff mbox series

Patch

diff --git a/drivers/soc/tegra/Kconfig b/drivers/soc/tegra/Kconfig
index 545c0da2e069..a5235af27644 100644
--- a/drivers/soc/tegra/Kconfig
+++ b/drivers/soc/tegra/Kconfig
@@ -139,3 +139,9 @@  config SOC_TEGRA20_VOLTAGE_COUPLER
 	def_bool y
 	depends on ARCH_TEGRA_2x_SOC
 	depends on REGULATOR
+
+config SOC_TEGRA30_VOLTAGE_COUPLER
+	bool "Voltage scaling support for Tegra30 SoC's"
+	def_bool y
+	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..ecd6a984e045
--- /dev/null
+++ b/drivers/soc/tegra/regulators-tegra30.c
@@ -0,0 +1,256 @@ 
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Voltage regulators coupling resolver for NVIDIA Tegra30
+ *
+ * Author: Dmitry Osipenko <digetx@gmail.com>
+ */
+
+#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_regulators_coupler {
+	struct regulators_coupler coupler;
+	int core_min_uV;
+};
+
+static const char * const cpu_names[] = {
+	"vdd_cpu,vdd_sys",
+	"+V1.0_VDD_CPU",
+	"vdd_cpu",
+};
+
+static const char * const core_names[] = {
+	"tps62361-vout",
+	"tps62362-vout",
+	"vdd_core",
+};
+
+static inline struct tegra_regulators_coupler *
+to_tegra_coupler(struct regulators_coupler *coupler)
+{
+	return container_of(coupler, struct tegra_regulators_coupler, coupler);
+}
+
+static int tegra30_core_limit(struct tegra_regulators_coupler *tegra,
+			      struct regulator_dev *core_rdev)
+{
+	if (tegra->core_min_uV > 0)
+		return tegra->core_min_uV;
+
+	tegra->core_min_uV = regulator_get_voltage_rdev(core_rdev);
+	if (tegra->core_min_uV > 0)
+		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:
+		case 8:
+			return 1200000;
+
+		default:
+			return 1300000;
+		}
+	}
+
+	return -EINVAL;
+}
+
+static int tegra30_voltage_update(struct tegra_regulators_coupler *tegra,
+				  struct regulator_dev *cpu_rdev,
+				  struct regulator_dev *core_rdev)
+{
+	int cpu_min_uV, cpu_max_uV = INT_MAX;
+	int core_min_uV, core_max_uV = INT_MAX;
+	int core_min_limited_uV;
+	int core_target_uV;
+	int cpu_target_uV;
+	int core_uV;
+	int cpu_uV;
+	int err;
+
+	/*
+	 * The CORE voltage scaling is currently not hooked up in drivers,
+	 * hence we will limit the minimum CORE voltage to the initial 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 - 300000;
+
+	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 + 100000, cpu_min_uV);
+		} else {
+			cpu_target_uV = max(cpu_uV - 100000, cpu_min_uV);
+			cpu_target_uV = max(core_uV - 300000, 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 + 100000);
+			core_target_uV = min(core_target_uV, cpu_uV + 300000);
+		} else {
+			core_target_uV = max(core_target_uV, core_uV - 100000);
+		}
+
+		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 struct regulator_dev *lookup_rdev(struct regulator_dev *rdev,
+					 const char * const *names,
+					 unsigned int num_names)
+{
+	struct coupling_desc *c_desc = &rdev->coupling_desc;
+	unsigned int i, k;
+
+	for (i = 0; i < num_names; i++) {
+		if (!strcmp(names[i], rdev_get_name(rdev)))
+			return rdev;
+	}
+
+	for (k = 0; k < c_desc->n_coupled; k++) {
+		rdev = c_desc->coupled_rdevs[k];
+
+		for (i = 0; i < num_names; i++) {
+			if (!strcmp(names[i], rdev_get_name(rdev)))
+				return rdev;
+		}
+	}
+
+	pr_err_once("%s: failed for %s\n", __func__, rdev_get_name(rdev));
+
+	for (i = 0; i < num_names; i++)
+		pr_err_once("%s: entry%u: %s\n", __func__, i, names[i]);
+
+	return NULL;
+}
+
+static int tegra30_regulator_balance_voltage(struct regulators_coupler *coupler,
+					     struct regulator_dev *rdev,
+					     suspend_state_t state)
+{
+	struct tegra_regulators_coupler *tegra = to_tegra_coupler(coupler);
+	struct regulator_dev *core_rdev;
+	struct regulator_dev *cpu_rdev;
+
+	core_rdev = lookup_rdev(rdev, core_names, ARRAY_SIZE(core_names));
+	cpu_rdev  = lookup_rdev(rdev, cpu_names, ARRAY_SIZE(cpu_names));
+
+	if (!core_rdev || !cpu_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 struct tegra_regulators_coupler tegra30_coupler = {
+	.coupler = {
+		.balance_voltage = tegra30_regulator_balance_voltage,
+	},
+};
+
+static int __init tegra_regulators_coupler_init(void)
+{
+	if (!of_machine_is_compatible("nvidia,tegra30"))
+		return 0;
+
+	return regulators_coupler_register(&tegra30_coupler.coupler);
+}
+arch_initcall(tegra_regulators_coupler_init);