All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH v2 0/6] Tegra210 DFLL implementation
@ 2018-01-24 12:45 ` Peter De Schrijver
  0 siblings, 0 replies; 56+ messages in thread
From: Peter De Schrijver @ 2018-01-24 12:45 UTC (permalink / raw)
  To: linux-tegra-u79uwXL29TY76Z2rM5mHXA,
	linux-clk-u79uwXL29TY76Z2rM5mHXA,
	mturquette-rdvid1DuHRBWk0Htik3J/w, sboyd-sgV2jX0FEOL9JmXXK+q4OQ
  Cc: Peter De Schrijver

This series introduces support for the DFLL as a CPU clock source
on Tegra210. As Jetson TX2 uses a PWM controlled regulator IC which
is driven directly by the DFLLs PWM output, we also introduce support
for PWM regulators next to I2C controlled regulators. The DFLL output
frequency is directly controlled by the regulator voltage. The registers
for controlling the PWM are part of the DFLL IP block, so there's no
separate linux regulator object involved because the regulator IC only
supplies the rail powering the DFLL and the CPUs. It doesn't have any
other controls.

Changes since v1:
* improved commit messages
* some style cleanups

Peter De Schrijver (6):
  clk: tegra: dfll registration for multiple SoCs
  clk: tegra: DT align parameter for CVB calculation
  clk: tegra: add CVB tables for Tegra210 CPU DFLL
  clk: tegra: dfll: support PWM regulator control
  clk: tegra: build clk-dfll.c for Tegra124 and Tegra210
  cpufreq: tegra124-cpufreq: extend to support Tegra210

 drivers/clk/tegra/Kconfig                  |   5 +
 drivers/clk/tegra/Makefile                 |   2 +-
 drivers/clk/tegra/clk-dfll.c               | 481 +++++++++++++++++++++++-----
 drivers/clk/tegra/clk-dfll.h               |   7 +
 drivers/clk/tegra/clk-tegra124-dfll-fcpu.c | 492 ++++++++++++++++++++++++++++-
 drivers/clk/tegra/cvb.c                    |  18 +-
 drivers/clk/tegra/cvb.h                    |   6 +-
 drivers/cpufreq/tegra124-cpufreq.c         |  13 +-
 8 files changed, 920 insertions(+), 104 deletions(-)

-- 
1.9.1

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

* [PATCH v2 0/6] Tegra210 DFLL implementation
@ 2018-01-24 12:45 ` Peter De Schrijver
  0 siblings, 0 replies; 56+ messages in thread
From: Peter De Schrijver @ 2018-01-24 12:45 UTC (permalink / raw)
  To: linux-tegra, linux-clk, mturquette, sboyd; +Cc: Peter De Schrijver

This series introduces support for the DFLL as a CPU clock source
on Tegra210. As Jetson TX2 uses a PWM controlled regulator IC which
is driven directly by the DFLLs PWM output, we also introduce support
for PWM regulators next to I2C controlled regulators. The DFLL output
frequency is directly controlled by the regulator voltage. The registers
for controlling the PWM are part of the DFLL IP block, so there's no
separate linux regulator object involved because the regulator IC only
supplies the rail powering the DFLL and the CPUs. It doesn't have any
other controls.

Changes since v1:
* improved commit messages
* some style cleanups

Peter De Schrijver (6):
  clk: tegra: dfll registration for multiple SoCs
  clk: tegra: DT align parameter for CVB calculation
  clk: tegra: add CVB tables for Tegra210 CPU DFLL
  clk: tegra: dfll: support PWM regulator control
  clk: tegra: build clk-dfll.c for Tegra124 and Tegra210
  cpufreq: tegra124-cpufreq: extend to support Tegra210

 drivers/clk/tegra/Kconfig                  |   5 +
 drivers/clk/tegra/Makefile                 |   2 +-
 drivers/clk/tegra/clk-dfll.c               | 481 +++++++++++++++++++++++-----
 drivers/clk/tegra/clk-dfll.h               |   7 +
 drivers/clk/tegra/clk-tegra124-dfll-fcpu.c | 492 ++++++++++++++++++++++++++++-
 drivers/clk/tegra/cvb.c                    |  18 +-
 drivers/clk/tegra/cvb.h                    |   6 +-
 drivers/cpufreq/tegra124-cpufreq.c         |  13 +-
 8 files changed, 920 insertions(+), 104 deletions(-)

-- 
1.9.1

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

* [PATCH v2 0/6] Tegra210 DFLL implementation
  2018-01-24 12:45 ` Peter De Schrijver
@ 2018-01-24 12:45   ` Peter De Schrijver
  -1 siblings, 0 replies; 56+ messages in thread
From: Peter De Schrijver @ 2018-01-24 12:45 UTC (permalink / raw)
  To: linux-tegra, linux-clk, mturquette, sboyd; +Cc: Peter De Schrijver

This series introduces support for the DFLL as a CPU clock source
on Tegra210. As Jetson TX2 uses a PWM controlled regulator IC which
is driven directly by the DFLLs PWM output, we also introduce support
for PWM regulators next to I2C controlled regulators. The DFLL output
frequency is directly controlled by the regulator voltage. The registers
for controlling the PWM are part of the DFLL IP block, so there's no
separate linux regulator object involved because the regulator IC only
supplies the rail powering the DFLL and the CPUs. It doesn't have any
other controls.

Changes since v1:
* improved commit messages

Peter De Schrijver (6):
  clk: tegra: dfll registration for multiple SoCs
  clk: tegra: DT align parameter for CVB calculation
  clk: tegra: add CVB tables for Tegra210 CPU DFLL
  clk: tegra: dfll: support PWM regulator control
  clk: tegra: build clk-dfll.c for Tegra124 and Tegra210
  cpufreq: tegra124-cpufreq: extend to support Tegra210

 drivers/clk/tegra/Kconfig                  |   5 +
 drivers/clk/tegra/Makefile                 |   2 +-
 drivers/clk/tegra/clk-dfll.c               | 481 +++++++++++++++++++++++-----
 drivers/clk/tegra/clk-dfll.h               |   7 +
 drivers/clk/tegra/clk-tegra124-dfll-fcpu.c | 492 ++++++++++++++++++++++++++++-
 drivers/clk/tegra/cvb.c                    |  17 +-
 drivers/clk/tegra/cvb.h                    |   6 +-
 drivers/cpufreq/tegra124-cpufreq.c         |  13 +-
 8 files changed, 919 insertions(+), 104 deletions(-)

-- 
1.9.1


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

* [PATCH v2 0/6] Tegra210 DFLL implementation
@ 2018-01-24 12:45   ` Peter De Schrijver
  0 siblings, 0 replies; 56+ messages in thread
From: Peter De Schrijver @ 2018-01-24 12:45 UTC (permalink / raw)
  To: linux-tegra, linux-clk, mturquette, sboyd; +Cc: Peter De Schrijver

This series introduces support for the DFLL as a CPU clock source
on Tegra210. As Jetson TX2 uses a PWM controlled regulator IC which
is driven directly by the DFLLs PWM output, we also introduce support
for PWM regulators next to I2C controlled regulators. The DFLL output
frequency is directly controlled by the regulator voltage. The registers
for controlling the PWM are part of the DFLL IP block, so there's no
separate linux regulator object involved because the regulator IC only
supplies the rail powering the DFLL and the CPUs. It doesn't have any
other controls.

Changes since v1:
* improved commit messages

Peter De Schrijver (6):
  clk: tegra: dfll registration for multiple SoCs
  clk: tegra: DT align parameter for CVB calculation
  clk: tegra: add CVB tables for Tegra210 CPU DFLL
  clk: tegra: dfll: support PWM regulator control
  clk: tegra: build clk-dfll.c for Tegra124 and Tegra210
  cpufreq: tegra124-cpufreq: extend to support Tegra210

 drivers/clk/tegra/Kconfig                  |   5 +
 drivers/clk/tegra/Makefile                 |   2 +-
 drivers/clk/tegra/clk-dfll.c               | 481 +++++++++++++++++++++++-----
 drivers/clk/tegra/clk-dfll.h               |   7 +
 drivers/clk/tegra/clk-tegra124-dfll-fcpu.c | 492 ++++++++++++++++++++++++++++-
 drivers/clk/tegra/cvb.c                    |  17 +-
 drivers/clk/tegra/cvb.h                    |   6 +-
 drivers/cpufreq/tegra124-cpufreq.c         |  13 +-
 8 files changed, 919 insertions(+), 104 deletions(-)

-- 
1.9.1

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

* [PATCH v2 1/6] clk: tegra: dfll registration for multiple SoCs
  2018-01-24 12:45 ` Peter De Schrijver
@ 2018-01-24 12:45     ` Peter De Schrijver
  -1 siblings, 0 replies; 56+ messages in thread
From: Peter De Schrijver @ 2018-01-24 12:45 UTC (permalink / raw)
  To: linux-tegra-u79uwXL29TY76Z2rM5mHXA,
	linux-clk-u79uwXL29TY76Z2rM5mHXA,
	mturquette-rdvid1DuHRBWk0Htik3J/w, sboyd-sgV2jX0FEOL9JmXXK+q4OQ
  Cc: Peter De Schrijver

In a future patch, support for the DFLL in Tegra210 will be introduced.
This requires support for more than 1 set of CVB and CPU max frequency
tables.

Signed-off-by: Peter De Schrijver <pdeschrijver-DDmLM1+adcrQT0dZR+AlfA@public.gmane.org>
---
 drivers/clk/tegra/clk-tegra124-dfll-fcpu.c | 43 +++++++++++++++++++++++-------
 1 file changed, 33 insertions(+), 10 deletions(-)

diff --git a/drivers/clk/tegra/clk-tegra124-dfll-fcpu.c b/drivers/clk/tegra/clk-tegra124-dfll-fcpu.c
index 269d359..440eb8d 100644
--- a/drivers/clk/tegra/clk-tegra124-dfll-fcpu.c
+++ b/drivers/clk/tegra/clk-tegra124-dfll-fcpu.c
@@ -21,6 +21,7 @@
 #include <linux/err.h>
 #include <linux/kernel.h>
 #include <linux/init.h>
+#include <linux/of_device.h>
 #include <linux/platform_device.h>
 #include <soc/tegra/fuse.h>
 
@@ -28,8 +29,15 @@
 #include "clk-dfll.h"
 #include "cvb.h"
 
+struct dfll_fcpu_data {
+	const unsigned long *cpu_max_freq_table;
+	unsigned int cpu_max_freq_table_size;
+	const struct cvb_table *cpu_cvb_tables;
+	unsigned int cpu_cvb_tables_size;
+};
+
 /* Maximum CPU frequency, indexed by CPU speedo id */
-static const unsigned long cpu_max_freq_table[] = {
+static const unsigned long tegra124_cpu_max_freq_table[] = {
 	[0] = 2014500000UL,
 	[1] = 2320500000UL,
 	[2] = 2116500000UL,
@@ -82,16 +90,36 @@
 	},
 };
 
+static const struct dfll_fcpu_data tegra124_dfll_fcpu_data = {
+	.cpu_max_freq_table = tegra124_cpu_max_freq_table,
+	.cpu_max_freq_table_size = ARRAY_SIZE(tegra124_cpu_max_freq_table),
+	.cpu_cvb_tables = tegra124_cpu_cvb_tables,
+	.cpu_cvb_tables_size = ARRAY_SIZE(tegra124_cpu_cvb_tables)
+};
+
+static const struct of_device_id tegra124_dfll_fcpu_of_match[] = {
+	{
+		.compatible = "nvidia,tegra124-dfll",
+		.data = &tegra124_dfll_fcpu_data,
+	},
+	{ },
+};
+
 static int tegra124_dfll_fcpu_probe(struct platform_device *pdev)
 {
 	int process_id, speedo_id, speedo_value, err;
 	struct tegra_dfll_soc_data *soc;
+	const struct of_device_id *of_id;
+	const struct dfll_fcpu_data *fcpu_data;
+
+	of_id = of_match_device(tegra124_dfll_fcpu_of_match, &pdev->dev);
+	fcpu_data = of_id->data;
 
 	process_id = tegra_sku_info.cpu_process_id;
 	speedo_id = tegra_sku_info.cpu_speedo_id;
 	speedo_value = tegra_sku_info.cpu_speedo_value;
 
-	if (speedo_id >= ARRAY_SIZE(cpu_max_freq_table)) {
+	if (speedo_id >= fcpu_data->cpu_max_freq_table_size) {
 		dev_err(&pdev->dev, "unknown max CPU freq for speedo_id=%d\n",
 			speedo_id);
 		return -ENODEV;
@@ -107,10 +135,10 @@ static int tegra124_dfll_fcpu_probe(struct platform_device *pdev)
 		return -ENODEV;
 	}
 
-	soc->max_freq = cpu_max_freq_table[speedo_id];
+	soc->max_freq = fcpu_data->cpu_max_freq_table[speedo_id];
 
-	soc->cvb = tegra_cvb_add_opp_table(soc->dev, tegra124_cpu_cvb_tables,
-					   ARRAY_SIZE(tegra124_cpu_cvb_tables),
+	soc->cvb = tegra_cvb_add_opp_table(soc->dev, fcpu_data->cpu_cvb_tables,
+					   fcpu_data->cpu_cvb_tables_size,
 					   process_id, speedo_id, speedo_value,
 					   soc->max_freq);
 	if (IS_ERR(soc->cvb)) {
@@ -142,11 +170,6 @@ static int tegra124_dfll_fcpu_remove(struct platform_device *pdev)
 	return 0;
 }
 
-static const struct of_device_id tegra124_dfll_fcpu_of_match[] = {
-	{ .compatible = "nvidia,tegra124-dfll", },
-	{ },
-};
-
 static const struct dev_pm_ops tegra124_dfll_pm_ops = {
 	SET_RUNTIME_PM_OPS(tegra_dfll_runtime_suspend,
 			   tegra_dfll_runtime_resume, NULL)
-- 
1.9.1

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

* [PATCH v2 1/6] clk: tegra: dfll registration for multiple SoCs
@ 2018-01-24 12:45     ` Peter De Schrijver
  0 siblings, 0 replies; 56+ messages in thread
From: Peter De Schrijver @ 2018-01-24 12:45 UTC (permalink / raw)
  To: linux-tegra, linux-clk, mturquette, sboyd; +Cc: Peter De Schrijver

In a future patch, support for the DFLL in Tegra210 will be introduced.
This requires support for more than 1 set of CVB and CPU max frequency
tables.

Signed-off-by: Peter De Schrijver <pdeschrijver@nvidia.com>
---
 drivers/clk/tegra/clk-tegra124-dfll-fcpu.c | 43 +++++++++++++++++++++++-------
 1 file changed, 33 insertions(+), 10 deletions(-)

diff --git a/drivers/clk/tegra/clk-tegra124-dfll-fcpu.c b/drivers/clk/tegra/clk-tegra124-dfll-fcpu.c
index 269d359..440eb8d 100644
--- a/drivers/clk/tegra/clk-tegra124-dfll-fcpu.c
+++ b/drivers/clk/tegra/clk-tegra124-dfll-fcpu.c
@@ -21,6 +21,7 @@
 #include <linux/err.h>
 #include <linux/kernel.h>
 #include <linux/init.h>
+#include <linux/of_device.h>
 #include <linux/platform_device.h>
 #include <soc/tegra/fuse.h>
 
@@ -28,8 +29,15 @@
 #include "clk-dfll.h"
 #include "cvb.h"
 
+struct dfll_fcpu_data {
+	const unsigned long *cpu_max_freq_table;
+	unsigned int cpu_max_freq_table_size;
+	const struct cvb_table *cpu_cvb_tables;
+	unsigned int cpu_cvb_tables_size;
+};
+
 /* Maximum CPU frequency, indexed by CPU speedo id */
-static const unsigned long cpu_max_freq_table[] = {
+static const unsigned long tegra124_cpu_max_freq_table[] = {
 	[0] = 2014500000UL,
 	[1] = 2320500000UL,
 	[2] = 2116500000UL,
@@ -82,16 +90,36 @@
 	},
 };
 
+static const struct dfll_fcpu_data tegra124_dfll_fcpu_data = {
+	.cpu_max_freq_table = tegra124_cpu_max_freq_table,
+	.cpu_max_freq_table_size = ARRAY_SIZE(tegra124_cpu_max_freq_table),
+	.cpu_cvb_tables = tegra124_cpu_cvb_tables,
+	.cpu_cvb_tables_size = ARRAY_SIZE(tegra124_cpu_cvb_tables)
+};
+
+static const struct of_device_id tegra124_dfll_fcpu_of_match[] = {
+	{
+		.compatible = "nvidia,tegra124-dfll",
+		.data = &tegra124_dfll_fcpu_data,
+	},
+	{ },
+};
+
 static int tegra124_dfll_fcpu_probe(struct platform_device *pdev)
 {
 	int process_id, speedo_id, speedo_value, err;
 	struct tegra_dfll_soc_data *soc;
+	const struct of_device_id *of_id;
+	const struct dfll_fcpu_data *fcpu_data;
+
+	of_id = of_match_device(tegra124_dfll_fcpu_of_match, &pdev->dev);
+	fcpu_data = of_id->data;
 
 	process_id = tegra_sku_info.cpu_process_id;
 	speedo_id = tegra_sku_info.cpu_speedo_id;
 	speedo_value = tegra_sku_info.cpu_speedo_value;
 
-	if (speedo_id >= ARRAY_SIZE(cpu_max_freq_table)) {
+	if (speedo_id >= fcpu_data->cpu_max_freq_table_size) {
 		dev_err(&pdev->dev, "unknown max CPU freq for speedo_id=%d\n",
 			speedo_id);
 		return -ENODEV;
@@ -107,10 +135,10 @@ static int tegra124_dfll_fcpu_probe(struct platform_device *pdev)
 		return -ENODEV;
 	}
 
-	soc->max_freq = cpu_max_freq_table[speedo_id];
+	soc->max_freq = fcpu_data->cpu_max_freq_table[speedo_id];
 
-	soc->cvb = tegra_cvb_add_opp_table(soc->dev, tegra124_cpu_cvb_tables,
-					   ARRAY_SIZE(tegra124_cpu_cvb_tables),
+	soc->cvb = tegra_cvb_add_opp_table(soc->dev, fcpu_data->cpu_cvb_tables,
+					   fcpu_data->cpu_cvb_tables_size,
 					   process_id, speedo_id, speedo_value,
 					   soc->max_freq);
 	if (IS_ERR(soc->cvb)) {
@@ -142,11 +170,6 @@ static int tegra124_dfll_fcpu_remove(struct platform_device *pdev)
 	return 0;
 }
 
-static const struct of_device_id tegra124_dfll_fcpu_of_match[] = {
-	{ .compatible = "nvidia,tegra124-dfll", },
-	{ },
-};
-
 static const struct dev_pm_ops tegra124_dfll_pm_ops = {
 	SET_RUNTIME_PM_OPS(tegra_dfll_runtime_suspend,
 			   tegra_dfll_runtime_resume, NULL)
-- 
1.9.1

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

* [PATCH v2 2/6] clk: tegra: DT align parameter for CVB calculation
  2018-01-24 12:45 ` Peter De Schrijver
@ 2018-01-24 12:45   ` Peter De Schrijver
  -1 siblings, 0 replies; 56+ messages in thread
From: Peter De Schrijver @ 2018-01-24 12:45 UTC (permalink / raw)
  To: linux-tegra, linux-clk, mturquette, sboyd; +Cc: Peter De Schrijver

When using a PWM controlled regulator, the voltage step and offset are
determined by the regulator IC in use. This is specified in DT rather
than fixed in the CVB table. Hence pass this information to the CVB table
calculation function. For backwards compatibility the table values are used
if the corresponding parameter is 0.

Signed-off-by: Peter De Schrijver <pdeschrijver@nvidia.com>
---
 drivers/clk/tegra/clk-tegra124-dfll-fcpu.c | 23 +++++++++++++++++++++--
 drivers/clk/tegra/cvb.c                    | 18 ++++++++++++++----
 drivers/clk/tegra/cvb.h                    |  5 +++--
 3 files changed, 38 insertions(+), 8 deletions(-)

diff --git a/drivers/clk/tegra/clk-tegra124-dfll-fcpu.c b/drivers/clk/tegra/clk-tegra124-dfll-fcpu.c
index 440eb8d..6205ce1 100644
--- a/drivers/clk/tegra/clk-tegra124-dfll-fcpu.c
+++ b/drivers/clk/tegra/clk-tegra124-dfll-fcpu.c
@@ -111,6 +111,7 @@ static int tegra124_dfll_fcpu_probe(struct platform_device *pdev)
 	struct tegra_dfll_soc_data *soc;
 	const struct of_device_id *of_id;
 	const struct dfll_fcpu_data *fcpu_data;
+	struct rail_alignment align;
 
 	of_id = of_match_device(tegra124_dfll_fcpu_of_match, &pdev->dev);
 	fcpu_data = of_id->data;
@@ -135,12 +136,30 @@ static int tegra124_dfll_fcpu_probe(struct platform_device *pdev)
 		return -ENODEV;
 	}
 
+	err = of_property_read_u32(pdev->dev.of_node, "nvidia,align-offset-uv",
+					&align.offset_uv);
+	if (err < 0) {
+		dev_err(&pdev->dev,
+			"offset uv not found, default to table value\n");
+		align.offset_uv = 0;
+	}
+
+	err = of_property_read_u32(pdev->dev.of_node, "nvidia,align-step-uv",
+					&align.step_uv);
+	if (err < 0) {
+		dev_err(&pdev->dev,
+			"step uv not found, default to table value\n");
+		align.step_uv = 0;
+	}
+
 	soc->max_freq = fcpu_data->cpu_max_freq_table[speedo_id];
 
 	soc->cvb = tegra_cvb_add_opp_table(soc->dev, fcpu_data->cpu_cvb_tables,
 					   fcpu_data->cpu_cvb_tables_size,
-					   process_id, speedo_id, speedo_value,
-					   soc->max_freq);
+					   &align, process_id, speedo_id,
+					   speedo_value, soc->max_freq);
+	soc->alignment = align;
+
 	if (IS_ERR(soc->cvb)) {
 		dev_err(&pdev->dev, "couldn't add OPP table: %ld\n",
 			PTR_ERR(soc->cvb));
diff --git a/drivers/clk/tegra/cvb.c b/drivers/clk/tegra/cvb.c
index da9e8e7..561012a 100644
--- a/drivers/clk/tegra/cvb.c
+++ b/drivers/clk/tegra/cvb.c
@@ -62,11 +62,19 @@ static int round_voltage(int mv, const struct rail_alignment *align, int up)
 }
 
 static int build_opp_table(struct device *dev, const struct cvb_table *table,
+			   struct rail_alignment *align,
 			   int speedo_value, unsigned long max_freq)
 {
-	const struct rail_alignment *align = &table->alignment;
 	int i, ret, dfll_mv, min_mv, max_mv;
 
+	if (!align->step_uv)
+		align->step_uv = table->alignment.step_uv;
+	if (!align->step_uv)
+		return -EINVAL;
+
+	if (!align->offset_uv)
+		align->offset_uv = table->alignment.offset_uv;
+
 	min_mv = round_voltage(table->min_millivolts, align, UP);
 	max_mv = round_voltage(table->max_millivolts, align, DOWN);
 
@@ -109,8 +117,9 @@ static int build_opp_table(struct device *dev, const struct cvb_table *table,
  */
 const struct cvb_table *
 tegra_cvb_add_opp_table(struct device *dev, const struct cvb_table *tables,
-			size_t count, int process_id, int speedo_id,
-			int speedo_value, unsigned long max_freq)
+			size_t count, struct rail_alignment *align,
+			int process_id, int speedo_id, int speedo_value,
+			unsigned long max_freq)
 {
 	size_t i;
 	int ret;
@@ -124,7 +133,8 @@ static int build_opp_table(struct device *dev, const struct cvb_table *table,
 		if (table->process_id != -1 && table->process_id != process_id)
 			continue;
 
-		ret = build_opp_table(dev, table, speedo_value, max_freq);
+		ret = build_opp_table(dev, table, align, speedo_value,
+				      max_freq);
 		return ret ? ERR_PTR(ret) : table;
 	}
 
diff --git a/drivers/clk/tegra/cvb.h b/drivers/clk/tegra/cvb.h
index c1f0779..cfa110f 100644
--- a/drivers/clk/tegra/cvb.h
+++ b/drivers/clk/tegra/cvb.h
@@ -59,8 +59,9 @@ struct cvb_table {
 
 const struct cvb_table *
 tegra_cvb_add_opp_table(struct device *dev, const struct cvb_table *cvb_tables,
-			size_t count, int process_id, int speedo_id,
-			int speedo_value, unsigned long max_freq);
+			size_t count, struct rail_alignment *align,
+			int process_id, int speedo_id, int speedo_value,
+			unsigned long max_freq);
 void tegra_cvb_remove_opp_table(struct device *dev,
 				const struct cvb_table *table,
 				unsigned long max_freq);
-- 
1.9.1


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

* [PATCH v2 2/6] clk: tegra: DT align parameter for CVB calculation
@ 2018-01-24 12:45   ` Peter De Schrijver
  0 siblings, 0 replies; 56+ messages in thread
From: Peter De Schrijver @ 2018-01-24 12:45 UTC (permalink / raw)
  To: linux-tegra, linux-clk, mturquette, sboyd; +Cc: Peter De Schrijver

When using a PWM controlled regulator, the voltage step and offset are
determined by the regulator IC in use. This is specified in DT rather
than fixed in the CVB table. Hence pass this information to the CVB table
calculation function. For backwards compatibility the table values are used
if the corresponding parameter is 0.

Signed-off-by: Peter De Schrijver <pdeschrijver@nvidia.com>
---
 drivers/clk/tegra/clk-tegra124-dfll-fcpu.c | 23 +++++++++++++++++++++--
 drivers/clk/tegra/cvb.c                    | 18 ++++++++++++++----
 drivers/clk/tegra/cvb.h                    |  5 +++--
 3 files changed, 38 insertions(+), 8 deletions(-)

diff --git a/drivers/clk/tegra/clk-tegra124-dfll-fcpu.c b/drivers/clk/tegra/clk-tegra124-dfll-fcpu.c
index 440eb8d..6205ce1 100644
--- a/drivers/clk/tegra/clk-tegra124-dfll-fcpu.c
+++ b/drivers/clk/tegra/clk-tegra124-dfll-fcpu.c
@@ -111,6 +111,7 @@ static int tegra124_dfll_fcpu_probe(struct platform_device *pdev)
 	struct tegra_dfll_soc_data *soc;
 	const struct of_device_id *of_id;
 	const struct dfll_fcpu_data *fcpu_data;
+	struct rail_alignment align;
 
 	of_id = of_match_device(tegra124_dfll_fcpu_of_match, &pdev->dev);
 	fcpu_data = of_id->data;
@@ -135,12 +136,30 @@ static int tegra124_dfll_fcpu_probe(struct platform_device *pdev)
 		return -ENODEV;
 	}
 
+	err = of_property_read_u32(pdev->dev.of_node, "nvidia,align-offset-uv",
+					&align.offset_uv);
+	if (err < 0) {
+		dev_err(&pdev->dev,
+			"offset uv not found, default to table value\n");
+		align.offset_uv = 0;
+	}
+
+	err = of_property_read_u32(pdev->dev.of_node, "nvidia,align-step-uv",
+					&align.step_uv);
+	if (err < 0) {
+		dev_err(&pdev->dev,
+			"step uv not found, default to table value\n");
+		align.step_uv = 0;
+	}
+
 	soc->max_freq = fcpu_data->cpu_max_freq_table[speedo_id];
 
 	soc->cvb = tegra_cvb_add_opp_table(soc->dev, fcpu_data->cpu_cvb_tables,
 					   fcpu_data->cpu_cvb_tables_size,
-					   process_id, speedo_id, speedo_value,
-					   soc->max_freq);
+					   &align, process_id, speedo_id,
+					   speedo_value, soc->max_freq);
+	soc->alignment = align;
+
 	if (IS_ERR(soc->cvb)) {
 		dev_err(&pdev->dev, "couldn't add OPP table: %ld\n",
 			PTR_ERR(soc->cvb));
diff --git a/drivers/clk/tegra/cvb.c b/drivers/clk/tegra/cvb.c
index da9e8e7..561012a 100644
--- a/drivers/clk/tegra/cvb.c
+++ b/drivers/clk/tegra/cvb.c
@@ -62,11 +62,19 @@ static int round_voltage(int mv, const struct rail_alignment *align, int up)
 }
 
 static int build_opp_table(struct device *dev, const struct cvb_table *table,
+			   struct rail_alignment *align,
 			   int speedo_value, unsigned long max_freq)
 {
-	const struct rail_alignment *align = &table->alignment;
 	int i, ret, dfll_mv, min_mv, max_mv;
 
+	if (!align->step_uv)
+		align->step_uv = table->alignment.step_uv;
+	if (!align->step_uv)
+		return -EINVAL;
+
+	if (!align->offset_uv)
+		align->offset_uv = table->alignment.offset_uv;
+
 	min_mv = round_voltage(table->min_millivolts, align, UP);
 	max_mv = round_voltage(table->max_millivolts, align, DOWN);
 
@@ -109,8 +117,9 @@ static int build_opp_table(struct device *dev, const struct cvb_table *table,
  */
 const struct cvb_table *
 tegra_cvb_add_opp_table(struct device *dev, const struct cvb_table *tables,
-			size_t count, int process_id, int speedo_id,
-			int speedo_value, unsigned long max_freq)
+			size_t count, struct rail_alignment *align,
+			int process_id, int speedo_id, int speedo_value,
+			unsigned long max_freq)
 {
 	size_t i;
 	int ret;
@@ -124,7 +133,8 @@ static int build_opp_table(struct device *dev, const struct cvb_table *table,
 		if (table->process_id != -1 && table->process_id != process_id)
 			continue;
 
-		ret = build_opp_table(dev, table, speedo_value, max_freq);
+		ret = build_opp_table(dev, table, align, speedo_value,
+				      max_freq);
 		return ret ? ERR_PTR(ret) : table;
 	}
 
diff --git a/drivers/clk/tegra/cvb.h b/drivers/clk/tegra/cvb.h
index c1f0779..cfa110f 100644
--- a/drivers/clk/tegra/cvb.h
+++ b/drivers/clk/tegra/cvb.h
@@ -59,8 +59,9 @@ struct cvb_table {
 
 const struct cvb_table *
 tegra_cvb_add_opp_table(struct device *dev, const struct cvb_table *cvb_tables,
-			size_t count, int process_id, int speedo_id,
-			int speedo_value, unsigned long max_freq);
+			size_t count, struct rail_alignment *align,
+			int process_id, int speedo_id, int speedo_value,
+			unsigned long max_freq);
 void tegra_cvb_remove_opp_table(struct device *dev,
 				const struct cvb_table *table,
 				unsigned long max_freq);
-- 
1.9.1

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

* [PATCH v2 3/6] clk: tegra: add CVB tables for Tegra210 CPU DFLL
  2018-01-24 12:45 ` Peter De Schrijver
@ 2018-01-24 12:45   ` Peter De Schrijver
  -1 siblings, 0 replies; 56+ messages in thread
From: Peter De Schrijver @ 2018-01-24 12:45 UTC (permalink / raw)
  To: linux-tegra, linux-clk, mturquette, sboyd; +Cc: Peter De Schrijver

Signed-off-by: Peter De Schrijver <pdeschrijver@nvidia.com>
---
 drivers/clk/tegra/clk-tegra124-dfll-fcpu.c | 426 +++++++++++++++++++++++++++++
 drivers/clk/tegra/cvb.h                    |   1 +
 2 files changed, 427 insertions(+)

diff --git a/drivers/clk/tegra/clk-tegra124-dfll-fcpu.c b/drivers/clk/tegra/clk-tegra124-dfll-fcpu.c
index 6205ce1..9b61f80 100644
--- a/drivers/clk/tegra/clk-tegra124-dfll-fcpu.c
+++ b/drivers/clk/tegra/clk-tegra124-dfll-fcpu.c
@@ -90,6 +90,421 @@ struct dfll_fcpu_data {
 	},
 };
 
+static const unsigned long tegra210_cpu_max_freq_table[] = {
+	[0] = 1912500000UL,
+	[1] = 1912500000UL,
+	[2] = 2218500000UL,
+	[3] = 1785000000UL,
+	[4] = 1632000000UL,
+	[5] = 1912500000UL,
+	[6] = 2014500000UL,
+	[7] = 1734000000UL,
+	[8] = 1683000000UL,
+	[9] = 1555500000UL,
+	[10] = 1504500000UL,
+};
+
+#define CPU_CVB_TABLE \
+	.speedo_scale = 100,	\
+	.voltage_scale = 1000,	\
+	.entries = {		\
+		{204000000UL,	{1007452, -23865, 370} }, \
+		{306000000UL,	{1052709, -24875, 370} }, \
+		{408000000UL,	{1099069, -25895, 370} }, \
+		{510000000UL,	{1146534, -26905, 370} }, \
+		{612000000UL,	{1195102, -27915, 370} }, \
+		{714000000UL,	{1244773, -28925, 370} }, \
+		{816000000UL,	{1295549, -29935, 370} }, \
+		{918000000UL,	{1347428, -30955, 370} }, \
+		{1020000000UL,	{1400411, -31965, 370} }, \
+		{1122000000UL,	{1454497, -32975, 370} }, \
+		{1224000000UL,	{1509687, -33985, 370} }, \
+		{1326000000UL,	{1565981, -35005, 370} }, \
+		{1428000000UL,	{1623379, -36015, 370} }, \
+		{1530000000UL,	{1681880, -37025, 370} }, \
+		{1632000000UL,	{1741485, -38035, 370} }, \
+		{1734000000UL,	{1802194, -39055, 370} }, \
+		{1836000000UL,	{1864006, -40065, 370} }, \
+		{1912500000UL,	{1910780, -40815, 370} }, \
+		{2014500000UL,	{1227000,      0,   0} }, \
+		{2218500000UL,	{1227000,      0,   0} }, \
+		{0,		{      0,      0,   0} }, \
+	}
+
+#define CPU_CVB_TABLE_XA \
+	.speedo_scale = 100,	\
+	.voltage_scale = 1000,	\
+	.entries = {		\
+		{204000000UL,	{1250024, -39785, 565} }, \
+		{306000000UL,	{1297556, -41145, 565} }, \
+		{408000000UL,	{1346718, -42505, 565} }, \
+		{510000000UL,	{1397511, -43855, 565} }, \
+		{612000000UL,	{1449933, -45215, 565} }, \
+		{714000000UL,	{1503986, -46575, 565} }, \
+		{816000000UL,	{1559669, -47935, 565} }, \
+		{918000000UL,	{1616982, -49295, 565} }, \
+		{1020000000UL,	{1675926, -50645, 565} }, \
+		{1122000000UL,	{1736500, -52005, 565} }, \
+		{1224000000UL,	{1798704, -53365, 565} }, \
+		{1326000000UL,	{1862538, -54725, 565} }, \
+		{1428000000UL,	{1928003, -56085, 565} }, \
+		{1530000000UL,	{1995097, -57435, 565} }, \
+		{1606500000UL,	{2046149, -58445, 565} }, \
+		{1632000000UL,	{2063822, -58795, 565} }, \
+		{0,		{      0,      0,   0} }, \
+	}
+
+#define CPU_CVB_TABLE_EUCM1 \
+	.speedo_scale = 100,	\
+	.voltage_scale = 1000,	\
+	.entries = {		\
+		{204000000UL,	{734429, 0, 0} }, \
+		{306000000UL,	{768191, 0, 0} }, \
+		{408000000UL,	{801953, 0, 0} }, \
+		{510000000UL,	{835715, 0, 0} }, \
+		{612000000UL,	{869477, 0, 0} }, \
+		{714000000UL,	{903239, 0, 0} }, \
+		{816000000UL,	{937001, 0, 0} }, \
+		{918000000UL,	{970763, 0, 0} }, \
+		{1020000000UL,	{1004525, 0, 0} }, \
+		{1122000000UL,	{1038287, 0, 0} }, \
+		{1224000000UL,	{1072049, 0, 0} }, \
+		{1326000000UL,	{1105811, 0, 0} }, \
+		{1428000000UL,	{1130000, 0, 0} }, \
+		{1555500000UL,	{1130000, 0, 0} }, \
+		{1632000000UL,	{1170000, 0, 0} }, \
+		{1734000000UL,	{1227500, 0, 0} }, \
+		{0,		{      0, 0, 0} }, \
+	}
+
+#define CPU_CVB_TABLE_EUCM2 \
+	.speedo_scale = 100,	\
+	.voltage_scale = 1000,	\
+	.entries = {		\
+		{204000000UL,	{742283, 0, 0} }, \
+		{306000000UL,	{776249, 0, 0} }, \
+		{408000000UL,	{810215, 0, 0} }, \
+		{510000000UL,	{844181, 0, 0} }, \
+		{612000000UL,	{878147, 0, 0} }, \
+		{714000000UL,	{912113, 0, 0} }, \
+		{816000000UL,	{946079, 0, 0} }, \
+		{918000000UL,	{980045, 0, 0} }, \
+		{1020000000UL,	{1014011, 0, 0} }, \
+		{1122000000UL,	{1047977, 0, 0} }, \
+		{1224000000UL,	{1081943, 0, 0} }, \
+		{1326000000UL,	{1090000, 0, 0} }, \
+		{1479000000UL,	{1090000, 0, 0} }, \
+		{1555500000UL,	{1162000, 0, 0} }, \
+		{1683000000UL,	{1195000, 0, 0} }, \
+		{0,		{      0, 0, 0} }, \
+	}
+
+#define CPU_CVB_TABLE_EUCM2_JOINT_RAIL \
+	.speedo_scale = 100,	\
+	.voltage_scale = 1000,	\
+	.entries = {		\
+		{204000000UL,	{742283, 0, 0} }, \
+		{306000000UL,	{776249, 0, 0} }, \
+		{408000000UL,	{810215, 0, 0} }, \
+		{510000000UL,	{844181, 0, 0} }, \
+		{612000000UL,	{878147, 0, 0} }, \
+		{714000000UL,	{912113, 0, 0} }, \
+		{816000000UL,	{946079, 0, 0} }, \
+		{918000000UL,	{980045, 0, 0} }, \
+		{1020000000UL,	{1014011, 0, 0} }, \
+		{1122000000UL,	{1047977, 0, 0} }, \
+		{1224000000UL,	{1081943, 0, 0} }, \
+		{1326000000UL,	{1090000, 0, 0} }, \
+		{1479000000UL,	{1090000, 0, 0} }, \
+		{1504500000UL,	{1120000, 0, 0} }, \
+		{0,		{      0, 0, 0} }, \
+	}
+
+#define CPU_CVB_TABLE_ODN \
+	.speedo_scale = 100,	\
+	.voltage_scale = 1000,	\
+	.entries = {		\
+		{204000000UL,	{721094, 0, 0} }, \
+		{306000000UL,	{754040, 0, 0} }, \
+		{408000000UL,	{786986, 0, 0} }, \
+		{510000000UL,	{819932, 0, 0} }, \
+		{612000000UL,	{852878, 0, 0} }, \
+		{714000000UL,	{885824, 0, 0} }, \
+		{816000000UL,	{918770, 0, 0} }, \
+		{918000000UL,	{915716, 0, 0} }, \
+		{1020000000UL,	{984662, 0, 0} }, \
+		{1122000000UL,	{1017608, 0, 0} }, \
+		{1224000000UL,	{1050554, 0, 0} }, \
+		{1326000000UL,	{1083500, 0, 0} }, \
+		{1428000000UL,	{1116446, 0, 0} }, \
+		{1581000000UL,	{1130000, 0, 0} }, \
+		{1683000000UL,	{1168000, 0, 0} }, \
+		{1785000000UL,	{1227500, 0, 0} }, \
+		{0,		{      0, 0, 0} }, \
+	}
+
+struct cvb_table tegra210_cpu_cvb_tables[] = {
+	{
+		.speedo_id = 10,
+		.process_id = 0,
+		.min_millivolts = 840,
+		.max_millivolts = 1120,
+		CPU_CVB_TABLE_EUCM2_JOINT_RAIL,
+		.cpu_dfll_data = {
+			.tune0_low = 0xffead0ff,
+			.tune0_high = 0xffead0ff,
+			.tune1 = 0x20091d9,
+			.tune_high_min_millivolts = 864,
+		}
+	},
+	{
+		.speedo_id = 10,
+		.process_id = 1,
+		.min_millivolts = 840,
+		.max_millivolts = 1120,
+		CPU_CVB_TABLE_EUCM2_JOINT_RAIL,
+		.cpu_dfll_data = {
+			.tune0_low = 0xffead0ff,
+			.tune0_high = 0xffead0ff,
+			.tune1 = 0x20091d9,
+			.tune_high_min_millivolts = 864,
+		}
+	},
+	{
+		.speedo_id = 9,
+		.process_id = 0,
+		.min_millivolts = 900,
+		.max_millivolts = 1162,
+		CPU_CVB_TABLE_EUCM2,
+		.cpu_dfll_data = {
+			.tune0_low = 0xffead0ff,
+			.tune0_high = 0xffead0ff,
+			.tune1 = 0x20091d9,
+		}
+	},
+	{
+		.speedo_id = 9,
+		.process_id = 1,
+		.min_millivolts = 900,
+		.max_millivolts = 1162,
+		CPU_CVB_TABLE_EUCM2,
+		.cpu_dfll_data = {
+			.tune0_low = 0xffead0ff,
+			.tune0_high = 0xffead0ff,
+			.tune1 = 0x20091d9,
+		}
+	},
+	{
+		.speedo_id = 8,
+		.process_id = 0,
+		.min_millivolts = 900,
+		.max_millivolts = 1195,
+		CPU_CVB_TABLE_EUCM2,
+		.cpu_dfll_data = {
+			.tune0_low = 0xffead0ff,
+			.tune0_high = 0xffead0ff,
+			.tune1 = 0x20091d9,
+		}
+	},
+	{
+		.speedo_id = 8,
+		.process_id = 1,
+		.min_millivolts = 900,
+		.max_millivolts = 1195,
+		CPU_CVB_TABLE_EUCM2,
+		.cpu_dfll_data = {
+			.tune0_low = 0xffead0ff,
+			.tune0_high = 0xffead0ff,
+			.tune1 = 0x20091d9,
+		}
+	},
+	{
+		.speedo_id = 7,
+		.process_id = 0,
+		.min_millivolts = 841,
+		.max_millivolts = 1227,
+		CPU_CVB_TABLE_EUCM1,
+		.cpu_dfll_data = {
+			.tune0_low = 0xffead0ff,
+			.tune0_high = 0xffead0ff,
+			.tune1 = 0x20091d9,
+			.tune_high_min_millivolts = 864,
+		}
+	},
+	{
+		.speedo_id = 7,
+		.process_id = 1,
+		.min_millivolts = 841,
+		.max_millivolts = 1227,
+		CPU_CVB_TABLE_EUCM1,
+		.cpu_dfll_data = {
+			.tune0_low = 0xffead0ff,
+			.tune0_high = 0xffead0ff,
+			.tune1 = 0x20091d9,
+			.tune_high_min_millivolts = 864,
+		}
+	},
+	{
+		.speedo_id = 6,
+		.process_id = 0,
+		.min_millivolts = 870,
+		.max_millivolts = 1150,
+		CPU_CVB_TABLE,
+		.cpu_dfll_data = {
+			.tune0_low = 0xffead0ff,
+			.tune1 = 0x20091d9,
+		}
+	},
+	{
+		.speedo_id = 6,
+		.process_id = 1,
+		.min_millivolts = 870,
+		.max_millivolts = 1150,
+		CPU_CVB_TABLE,
+		.cpu_dfll_data = {
+			.tune0_low = 0xffead0ff,
+			.tune1 = 0x25501d0,
+		}
+	},
+	{
+		.speedo_id = 5,
+		.process_id = 0,
+		.min_millivolts = 818,
+		.max_millivolts = 1227,
+		CPU_CVB_TABLE,
+		.cpu_dfll_data = {
+			.tune0_low = 0xffead0ff,
+			.tune0_high = 0xffead0ff,
+			.tune1 = 0x20091d9,
+			.tune_high_min_millivolts = 864,
+		}
+	},
+	{
+		.speedo_id = 5,
+		.process_id = 1,
+		.min_millivolts = 818,
+		.max_millivolts = 1227,
+		CPU_CVB_TABLE,
+		.cpu_dfll_data = {
+			.tune0_low = 0xffead0ff,
+			.tune0_high = 0xffead0ff,
+			.tune1 = 0x25501d0,
+			.tune_high_min_millivolts = 864,
+		}
+	},
+	{
+		.speedo_id = 4,
+		.process_id = -1,
+		.min_millivolts = 918,
+		.max_millivolts = 1113,
+		CPU_CVB_TABLE_XA,
+		.cpu_dfll_data = {
+			.tune0_low = 0xffead0ff,
+			.tune1 = 0x17711BD,
+		}
+	},
+	{
+		.speedo_id = 3,
+		.process_id = 0,
+		.min_millivolts = 825,
+		.max_millivolts = 1227,
+		CPU_CVB_TABLE_ODN,
+		.cpu_dfll_data = {
+			.tune0_low = 0xffead0ff,
+			.tune0_high = 0xffead0ff,
+			.tune1 = 0x20091d9,
+			.tune_high_min_millivolts = 864,
+		}
+	},
+	{
+		.speedo_id = 3,
+		.process_id = 1,
+		.min_millivolts = 825,
+		.max_millivolts = 1227,
+		CPU_CVB_TABLE_ODN,
+		.cpu_dfll_data = {
+			.tune0_low = 0xffead0ff,
+			.tune0_high = 0xffead0ff,
+			.tune1 = 0x25501d0,
+			.tune_high_min_millivolts = 864,
+		}
+	},
+	{
+		.speedo_id = 2,
+		.process_id = 0,
+		.min_millivolts = 870,
+		.max_millivolts = 1227,
+		CPU_CVB_TABLE,
+		.cpu_dfll_data = {
+			.tune0_low = 0xffead0ff,
+			.tune1 = 0x20091d9,
+		}
+	},
+	{
+		.speedo_id = 2,
+		.process_id = 1,
+		.min_millivolts = 870,
+		.max_millivolts = 1227,
+		CPU_CVB_TABLE,
+		.cpu_dfll_data = {
+			.tune0_low = 0xffead0ff,
+			.tune1 = 0x25501d0,
+		}
+	},
+	{
+		.speedo_id = 1,
+		.process_id = 0,
+		.min_millivolts = 837,
+		.max_millivolts = 1227,
+		CPU_CVB_TABLE,
+		.cpu_dfll_data = {
+			.tune0_low = 0xffead0ff,
+			.tune0_high = 0xffead0ff,
+			.tune1 = 0x20091d9,
+			.tune_high_min_millivolts = 864,
+		}
+	},
+	{
+		.speedo_id = 1,
+		.process_id = 1,
+		.min_millivolts = 837,
+		.max_millivolts = 1227,
+		CPU_CVB_TABLE,
+		.cpu_dfll_data = {
+			.tune0_low = 0xffead0ff,
+			.tune0_high = 0xffead0ff,
+			.tune1 = 0x25501d0,
+			.tune_high_min_millivolts = 864,
+		}
+	},
+	{
+		.speedo_id = 0,
+		.process_id = 0,
+		.min_millivolts = 850,
+		.max_millivolts = 1170,
+		CPU_CVB_TABLE,
+		.cpu_dfll_data = {
+			.tune0_low = 0xffead0ff,
+			.tune0_high = 0xffead0ff,
+			.tune1 = 0x20091d9,
+			.tune_high_min_millivolts = 864,
+		}
+	},
+	{
+		.speedo_id = 0,
+		.process_id = 1,
+		.min_millivolts = 850,
+		.max_millivolts = 1170,
+		CPU_CVB_TABLE,
+		.cpu_dfll_data = {
+			.tune0_low = 0xffead0ff,
+			.tune0_high = 0xffead0ff,
+			.tune1 = 0x25501d0,
+			.tune_high_min_millivolts = 864,
+		}
+	},
+};
+
 static const struct dfll_fcpu_data tegra124_dfll_fcpu_data = {
 	.cpu_max_freq_table = tegra124_cpu_max_freq_table,
 	.cpu_max_freq_table_size = ARRAY_SIZE(tegra124_cpu_max_freq_table),
@@ -97,11 +512,22 @@ struct dfll_fcpu_data {
 	.cpu_cvb_tables_size = ARRAY_SIZE(tegra124_cpu_cvb_tables)
 };
 
+static const struct dfll_fcpu_data tegra210_dfll_fcpu_data = {
+	.cpu_max_freq_table = tegra210_cpu_max_freq_table,
+	.cpu_max_freq_table_size = ARRAY_SIZE(tegra210_cpu_max_freq_table),
+	.cpu_cvb_tables = tegra210_cpu_cvb_tables,
+	.cpu_cvb_tables_size = ARRAY_SIZE(tegra210_cpu_cvb_tables),
+};
+
 static const struct of_device_id tegra124_dfll_fcpu_of_match[] = {
 	{
 		.compatible = "nvidia,tegra124-dfll",
 		.data = &tegra124_dfll_fcpu_data,
 	},
+	{
+		.compatible = "nvidia,tegra210-dfll",
+		.data = &tegra210_dfll_fcpu_data
+	},
 	{ },
 };
 
diff --git a/drivers/clk/tegra/cvb.h b/drivers/clk/tegra/cvb.h
index cfa110f..72c0a2d 100644
--- a/drivers/clk/tegra/cvb.h
+++ b/drivers/clk/tegra/cvb.h
@@ -41,6 +41,7 @@ struct cvb_cpu_dfll_data {
 	u32 tune0_low;
 	u32 tune0_high;
 	u32 tune1;
+	unsigned int tune_high_min_millivolts;
 };
 
 struct cvb_table {
-- 
1.9.1


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

* [PATCH v2 3/6] clk: tegra: add CVB tables for Tegra210 CPU DFLL
@ 2018-01-24 12:45   ` Peter De Schrijver
  0 siblings, 0 replies; 56+ messages in thread
From: Peter De Schrijver @ 2018-01-24 12:45 UTC (permalink / raw)
  To: linux-tegra, linux-clk, mturquette, sboyd; +Cc: Peter De Schrijver

Signed-off-by: Peter De Schrijver <pdeschrijver@nvidia.com>
---
 drivers/clk/tegra/clk-tegra124-dfll-fcpu.c | 426 +++++++++++++++++++++++++++++
 drivers/clk/tegra/cvb.h                    |   1 +
 2 files changed, 427 insertions(+)

diff --git a/drivers/clk/tegra/clk-tegra124-dfll-fcpu.c b/drivers/clk/tegra/clk-tegra124-dfll-fcpu.c
index 6205ce1..9b61f80 100644
--- a/drivers/clk/tegra/clk-tegra124-dfll-fcpu.c
+++ b/drivers/clk/tegra/clk-tegra124-dfll-fcpu.c
@@ -90,6 +90,421 @@ struct dfll_fcpu_data {
 	},
 };
 
+static const unsigned long tegra210_cpu_max_freq_table[] = {
+	[0] = 1912500000UL,
+	[1] = 1912500000UL,
+	[2] = 2218500000UL,
+	[3] = 1785000000UL,
+	[4] = 1632000000UL,
+	[5] = 1912500000UL,
+	[6] = 2014500000UL,
+	[7] = 1734000000UL,
+	[8] = 1683000000UL,
+	[9] = 1555500000UL,
+	[10] = 1504500000UL,
+};
+
+#define CPU_CVB_TABLE \
+	.speedo_scale = 100,	\
+	.voltage_scale = 1000,	\
+	.entries = {		\
+		{204000000UL,	{1007452, -23865, 370} }, \
+		{306000000UL,	{1052709, -24875, 370} }, \
+		{408000000UL,	{1099069, -25895, 370} }, \
+		{510000000UL,	{1146534, -26905, 370} }, \
+		{612000000UL,	{1195102, -27915, 370} }, \
+		{714000000UL,	{1244773, -28925, 370} }, \
+		{816000000UL,	{1295549, -29935, 370} }, \
+		{918000000UL,	{1347428, -30955, 370} }, \
+		{1020000000UL,	{1400411, -31965, 370} }, \
+		{1122000000UL,	{1454497, -32975, 370} }, \
+		{1224000000UL,	{1509687, -33985, 370} }, \
+		{1326000000UL,	{1565981, -35005, 370} }, \
+		{1428000000UL,	{1623379, -36015, 370} }, \
+		{1530000000UL,	{1681880, -37025, 370} }, \
+		{1632000000UL,	{1741485, -38035, 370} }, \
+		{1734000000UL,	{1802194, -39055, 370} }, \
+		{1836000000UL,	{1864006, -40065, 370} }, \
+		{1912500000UL,	{1910780, -40815, 370} }, \
+		{2014500000UL,	{1227000,      0,   0} }, \
+		{2218500000UL,	{1227000,      0,   0} }, \
+		{0,		{      0,      0,   0} }, \
+	}
+
+#define CPU_CVB_TABLE_XA \
+	.speedo_scale = 100,	\
+	.voltage_scale = 1000,	\
+	.entries = {		\
+		{204000000UL,	{1250024, -39785, 565} }, \
+		{306000000UL,	{1297556, -41145, 565} }, \
+		{408000000UL,	{1346718, -42505, 565} }, \
+		{510000000UL,	{1397511, -43855, 565} }, \
+		{612000000UL,	{1449933, -45215, 565} }, \
+		{714000000UL,	{1503986, -46575, 565} }, \
+		{816000000UL,	{1559669, -47935, 565} }, \
+		{918000000UL,	{1616982, -49295, 565} }, \
+		{1020000000UL,	{1675926, -50645, 565} }, \
+		{1122000000UL,	{1736500, -52005, 565} }, \
+		{1224000000UL,	{1798704, -53365, 565} }, \
+		{1326000000UL,	{1862538, -54725, 565} }, \
+		{1428000000UL,	{1928003, -56085, 565} }, \
+		{1530000000UL,	{1995097, -57435, 565} }, \
+		{1606500000UL,	{2046149, -58445, 565} }, \
+		{1632000000UL,	{2063822, -58795, 565} }, \
+		{0,		{      0,      0,   0} }, \
+	}
+
+#define CPU_CVB_TABLE_EUCM1 \
+	.speedo_scale = 100,	\
+	.voltage_scale = 1000,	\
+	.entries = {		\
+		{204000000UL,	{734429, 0, 0} }, \
+		{306000000UL,	{768191, 0, 0} }, \
+		{408000000UL,	{801953, 0, 0} }, \
+		{510000000UL,	{835715, 0, 0} }, \
+		{612000000UL,	{869477, 0, 0} }, \
+		{714000000UL,	{903239, 0, 0} }, \
+		{816000000UL,	{937001, 0, 0} }, \
+		{918000000UL,	{970763, 0, 0} }, \
+		{1020000000UL,	{1004525, 0, 0} }, \
+		{1122000000UL,	{1038287, 0, 0} }, \
+		{1224000000UL,	{1072049, 0, 0} }, \
+		{1326000000UL,	{1105811, 0, 0} }, \
+		{1428000000UL,	{1130000, 0, 0} }, \
+		{1555500000UL,	{1130000, 0, 0} }, \
+		{1632000000UL,	{1170000, 0, 0} }, \
+		{1734000000UL,	{1227500, 0, 0} }, \
+		{0,		{      0, 0, 0} }, \
+	}
+
+#define CPU_CVB_TABLE_EUCM2 \
+	.speedo_scale = 100,	\
+	.voltage_scale = 1000,	\
+	.entries = {		\
+		{204000000UL,	{742283, 0, 0} }, \
+		{306000000UL,	{776249, 0, 0} }, \
+		{408000000UL,	{810215, 0, 0} }, \
+		{510000000UL,	{844181, 0, 0} }, \
+		{612000000UL,	{878147, 0, 0} }, \
+		{714000000UL,	{912113, 0, 0} }, \
+		{816000000UL,	{946079, 0, 0} }, \
+		{918000000UL,	{980045, 0, 0} }, \
+		{1020000000UL,	{1014011, 0, 0} }, \
+		{1122000000UL,	{1047977, 0, 0} }, \
+		{1224000000UL,	{1081943, 0, 0} }, \
+		{1326000000UL,	{1090000, 0, 0} }, \
+		{1479000000UL,	{1090000, 0, 0} }, \
+		{1555500000UL,	{1162000, 0, 0} }, \
+		{1683000000UL,	{1195000, 0, 0} }, \
+		{0,		{      0, 0, 0} }, \
+	}
+
+#define CPU_CVB_TABLE_EUCM2_JOINT_RAIL \
+	.speedo_scale = 100,	\
+	.voltage_scale = 1000,	\
+	.entries = {		\
+		{204000000UL,	{742283, 0, 0} }, \
+		{306000000UL,	{776249, 0, 0} }, \
+		{408000000UL,	{810215, 0, 0} }, \
+		{510000000UL,	{844181, 0, 0} }, \
+		{612000000UL,	{878147, 0, 0} }, \
+		{714000000UL,	{912113, 0, 0} }, \
+		{816000000UL,	{946079, 0, 0} }, \
+		{918000000UL,	{980045, 0, 0} }, \
+		{1020000000UL,	{1014011, 0, 0} }, \
+		{1122000000UL,	{1047977, 0, 0} }, \
+		{1224000000UL,	{1081943, 0, 0} }, \
+		{1326000000UL,	{1090000, 0, 0} }, \
+		{1479000000UL,	{1090000, 0, 0} }, \
+		{1504500000UL,	{1120000, 0, 0} }, \
+		{0,		{      0, 0, 0} }, \
+	}
+
+#define CPU_CVB_TABLE_ODN \
+	.speedo_scale = 100,	\
+	.voltage_scale = 1000,	\
+	.entries = {		\
+		{204000000UL,	{721094, 0, 0} }, \
+		{306000000UL,	{754040, 0, 0} }, \
+		{408000000UL,	{786986, 0, 0} }, \
+		{510000000UL,	{819932, 0, 0} }, \
+		{612000000UL,	{852878, 0, 0} }, \
+		{714000000UL,	{885824, 0, 0} }, \
+		{816000000UL,	{918770, 0, 0} }, \
+		{918000000UL,	{915716, 0, 0} }, \
+		{1020000000UL,	{984662, 0, 0} }, \
+		{1122000000UL,	{1017608, 0, 0} }, \
+		{1224000000UL,	{1050554, 0, 0} }, \
+		{1326000000UL,	{1083500, 0, 0} }, \
+		{1428000000UL,	{1116446, 0, 0} }, \
+		{1581000000UL,	{1130000, 0, 0} }, \
+		{1683000000UL,	{1168000, 0, 0} }, \
+		{1785000000UL,	{1227500, 0, 0} }, \
+		{0,		{      0, 0, 0} }, \
+	}
+
+struct cvb_table tegra210_cpu_cvb_tables[] = {
+	{
+		.speedo_id = 10,
+		.process_id = 0,
+		.min_millivolts = 840,
+		.max_millivolts = 1120,
+		CPU_CVB_TABLE_EUCM2_JOINT_RAIL,
+		.cpu_dfll_data = {
+			.tune0_low = 0xffead0ff,
+			.tune0_high = 0xffead0ff,
+			.tune1 = 0x20091d9,
+			.tune_high_min_millivolts = 864,
+		}
+	},
+	{
+		.speedo_id = 10,
+		.process_id = 1,
+		.min_millivolts = 840,
+		.max_millivolts = 1120,
+		CPU_CVB_TABLE_EUCM2_JOINT_RAIL,
+		.cpu_dfll_data = {
+			.tune0_low = 0xffead0ff,
+			.tune0_high = 0xffead0ff,
+			.tune1 = 0x20091d9,
+			.tune_high_min_millivolts = 864,
+		}
+	},
+	{
+		.speedo_id = 9,
+		.process_id = 0,
+		.min_millivolts = 900,
+		.max_millivolts = 1162,
+		CPU_CVB_TABLE_EUCM2,
+		.cpu_dfll_data = {
+			.tune0_low = 0xffead0ff,
+			.tune0_high = 0xffead0ff,
+			.tune1 = 0x20091d9,
+		}
+	},
+	{
+		.speedo_id = 9,
+		.process_id = 1,
+		.min_millivolts = 900,
+		.max_millivolts = 1162,
+		CPU_CVB_TABLE_EUCM2,
+		.cpu_dfll_data = {
+			.tune0_low = 0xffead0ff,
+			.tune0_high = 0xffead0ff,
+			.tune1 = 0x20091d9,
+		}
+	},
+	{
+		.speedo_id = 8,
+		.process_id = 0,
+		.min_millivolts = 900,
+		.max_millivolts = 1195,
+		CPU_CVB_TABLE_EUCM2,
+		.cpu_dfll_data = {
+			.tune0_low = 0xffead0ff,
+			.tune0_high = 0xffead0ff,
+			.tune1 = 0x20091d9,
+		}
+	},
+	{
+		.speedo_id = 8,
+		.process_id = 1,
+		.min_millivolts = 900,
+		.max_millivolts = 1195,
+		CPU_CVB_TABLE_EUCM2,
+		.cpu_dfll_data = {
+			.tune0_low = 0xffead0ff,
+			.tune0_high = 0xffead0ff,
+			.tune1 = 0x20091d9,
+		}
+	},
+	{
+		.speedo_id = 7,
+		.process_id = 0,
+		.min_millivolts = 841,
+		.max_millivolts = 1227,
+		CPU_CVB_TABLE_EUCM1,
+		.cpu_dfll_data = {
+			.tune0_low = 0xffead0ff,
+			.tune0_high = 0xffead0ff,
+			.tune1 = 0x20091d9,
+			.tune_high_min_millivolts = 864,
+		}
+	},
+	{
+		.speedo_id = 7,
+		.process_id = 1,
+		.min_millivolts = 841,
+		.max_millivolts = 1227,
+		CPU_CVB_TABLE_EUCM1,
+		.cpu_dfll_data = {
+			.tune0_low = 0xffead0ff,
+			.tune0_high = 0xffead0ff,
+			.tune1 = 0x20091d9,
+			.tune_high_min_millivolts = 864,
+		}
+	},
+	{
+		.speedo_id = 6,
+		.process_id = 0,
+		.min_millivolts = 870,
+		.max_millivolts = 1150,
+		CPU_CVB_TABLE,
+		.cpu_dfll_data = {
+			.tune0_low = 0xffead0ff,
+			.tune1 = 0x20091d9,
+		}
+	},
+	{
+		.speedo_id = 6,
+		.process_id = 1,
+		.min_millivolts = 870,
+		.max_millivolts = 1150,
+		CPU_CVB_TABLE,
+		.cpu_dfll_data = {
+			.tune0_low = 0xffead0ff,
+			.tune1 = 0x25501d0,
+		}
+	},
+	{
+		.speedo_id = 5,
+		.process_id = 0,
+		.min_millivolts = 818,
+		.max_millivolts = 1227,
+		CPU_CVB_TABLE,
+		.cpu_dfll_data = {
+			.tune0_low = 0xffead0ff,
+			.tune0_high = 0xffead0ff,
+			.tune1 = 0x20091d9,
+			.tune_high_min_millivolts = 864,
+		}
+	},
+	{
+		.speedo_id = 5,
+		.process_id = 1,
+		.min_millivolts = 818,
+		.max_millivolts = 1227,
+		CPU_CVB_TABLE,
+		.cpu_dfll_data = {
+			.tune0_low = 0xffead0ff,
+			.tune0_high = 0xffead0ff,
+			.tune1 = 0x25501d0,
+			.tune_high_min_millivolts = 864,
+		}
+	},
+	{
+		.speedo_id = 4,
+		.process_id = -1,
+		.min_millivolts = 918,
+		.max_millivolts = 1113,
+		CPU_CVB_TABLE_XA,
+		.cpu_dfll_data = {
+			.tune0_low = 0xffead0ff,
+			.tune1 = 0x17711BD,
+		}
+	},
+	{
+		.speedo_id = 3,
+		.process_id = 0,
+		.min_millivolts = 825,
+		.max_millivolts = 1227,
+		CPU_CVB_TABLE_ODN,
+		.cpu_dfll_data = {
+			.tune0_low = 0xffead0ff,
+			.tune0_high = 0xffead0ff,
+			.tune1 = 0x20091d9,
+			.tune_high_min_millivolts = 864,
+		}
+	},
+	{
+		.speedo_id = 3,
+		.process_id = 1,
+		.min_millivolts = 825,
+		.max_millivolts = 1227,
+		CPU_CVB_TABLE_ODN,
+		.cpu_dfll_data = {
+			.tune0_low = 0xffead0ff,
+			.tune0_high = 0xffead0ff,
+			.tune1 = 0x25501d0,
+			.tune_high_min_millivolts = 864,
+		}
+	},
+	{
+		.speedo_id = 2,
+		.process_id = 0,
+		.min_millivolts = 870,
+		.max_millivolts = 1227,
+		CPU_CVB_TABLE,
+		.cpu_dfll_data = {
+			.tune0_low = 0xffead0ff,
+			.tune1 = 0x20091d9,
+		}
+	},
+	{
+		.speedo_id = 2,
+		.process_id = 1,
+		.min_millivolts = 870,
+		.max_millivolts = 1227,
+		CPU_CVB_TABLE,
+		.cpu_dfll_data = {
+			.tune0_low = 0xffead0ff,
+			.tune1 = 0x25501d0,
+		}
+	},
+	{
+		.speedo_id = 1,
+		.process_id = 0,
+		.min_millivolts = 837,
+		.max_millivolts = 1227,
+		CPU_CVB_TABLE,
+		.cpu_dfll_data = {
+			.tune0_low = 0xffead0ff,
+			.tune0_high = 0xffead0ff,
+			.tune1 = 0x20091d9,
+			.tune_high_min_millivolts = 864,
+		}
+	},
+	{
+		.speedo_id = 1,
+		.process_id = 1,
+		.min_millivolts = 837,
+		.max_millivolts = 1227,
+		CPU_CVB_TABLE,
+		.cpu_dfll_data = {
+			.tune0_low = 0xffead0ff,
+			.tune0_high = 0xffead0ff,
+			.tune1 = 0x25501d0,
+			.tune_high_min_millivolts = 864,
+		}
+	},
+	{
+		.speedo_id = 0,
+		.process_id = 0,
+		.min_millivolts = 850,
+		.max_millivolts = 1170,
+		CPU_CVB_TABLE,
+		.cpu_dfll_data = {
+			.tune0_low = 0xffead0ff,
+			.tune0_high = 0xffead0ff,
+			.tune1 = 0x20091d9,
+			.tune_high_min_millivolts = 864,
+		}
+	},
+	{
+		.speedo_id = 0,
+		.process_id = 1,
+		.min_millivolts = 850,
+		.max_millivolts = 1170,
+		CPU_CVB_TABLE,
+		.cpu_dfll_data = {
+			.tune0_low = 0xffead0ff,
+			.tune0_high = 0xffead0ff,
+			.tune1 = 0x25501d0,
+			.tune_high_min_millivolts = 864,
+		}
+	},
+};
+
 static const struct dfll_fcpu_data tegra124_dfll_fcpu_data = {
 	.cpu_max_freq_table = tegra124_cpu_max_freq_table,
 	.cpu_max_freq_table_size = ARRAY_SIZE(tegra124_cpu_max_freq_table),
@@ -97,11 +512,22 @@ struct dfll_fcpu_data {
 	.cpu_cvb_tables_size = ARRAY_SIZE(tegra124_cpu_cvb_tables)
 };
 
+static const struct dfll_fcpu_data tegra210_dfll_fcpu_data = {
+	.cpu_max_freq_table = tegra210_cpu_max_freq_table,
+	.cpu_max_freq_table_size = ARRAY_SIZE(tegra210_cpu_max_freq_table),
+	.cpu_cvb_tables = tegra210_cpu_cvb_tables,
+	.cpu_cvb_tables_size = ARRAY_SIZE(tegra210_cpu_cvb_tables),
+};
+
 static const struct of_device_id tegra124_dfll_fcpu_of_match[] = {
 	{
 		.compatible = "nvidia,tegra124-dfll",
 		.data = &tegra124_dfll_fcpu_data,
 	},
+	{
+		.compatible = "nvidia,tegra210-dfll",
+		.data = &tegra210_dfll_fcpu_data
+	},
 	{ },
 };
 
diff --git a/drivers/clk/tegra/cvb.h b/drivers/clk/tegra/cvb.h
index cfa110f..72c0a2d 100644
--- a/drivers/clk/tegra/cvb.h
+++ b/drivers/clk/tegra/cvb.h
@@ -41,6 +41,7 @@ struct cvb_cpu_dfll_data {
 	u32 tune0_low;
 	u32 tune0_high;
 	u32 tune1;
+	unsigned int tune_high_min_millivolts;
 };
 
 struct cvb_table {
-- 
1.9.1

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

* [PATCH v2 4/6] clk: tegra: dfll: support PWM regulator control
  2018-01-24 12:45 ` Peter De Schrijver
@ 2018-01-24 12:45   ` Peter De Schrijver
  -1 siblings, 0 replies; 56+ messages in thread
From: Peter De Schrijver @ 2018-01-24 12:45 UTC (permalink / raw)
  To: linux-tegra, linux-clk, mturquette, sboyd; +Cc: Peter De Schrijver

The DFLL can directly generate a PWM signal to control the regulator IC
rather then sending i2c messages. This patch adds support for this mode.
In this mode the LUT is not used and also there is no regulator object
involved because there is no way to control the regulator voltage without
also changing the DFLL output frequency. Also the register debugfs file
is slightly reworked to only show the i2c registers when i2c mode is in
use.

Signed-off-by: Peter De Schrijver <pdeschrijver@nvidia.com>
---
 drivers/clk/tegra/clk-dfll.c | 481 ++++++++++++++++++++++++++++++++++++-------
 drivers/clk/tegra/clk-dfll.h |   7 +
 2 files changed, 410 insertions(+), 78 deletions(-)

diff --git a/drivers/clk/tegra/clk-dfll.c b/drivers/clk/tegra/clk-dfll.c
index 0a7deee..204717c 100644
--- a/drivers/clk/tegra/clk-dfll.c
+++ b/drivers/clk/tegra/clk-dfll.c
@@ -243,6 +243,12 @@ enum dfll_tune_range {
 	DFLL_TUNE_LOW = 1,
 };
 
+
+enum tegra_dfll_pmu_if {
+	TEGRA_DFLL_PMU_I2C = 0,
+	TEGRA_DFLL_PMU_PWM = 1,
+};
+
 /**
  * struct dfll_rate_req - target DFLL rate request data
  * @rate: target frequency, after the postscaling
@@ -292,18 +298,27 @@ struct tegra_dfll {
 	u32				force_mode;
 	u32				cf;
 	u32				ci;
-	u32				cg;
+	s32				cg;
 	bool				cg_scale;
+	u32				reg_init_uV;
 
 	/* I2C interface parameters */
 	u32				i2c_fs_rate;
 	u32				i2c_reg;
 	u32				i2c_slave_addr;
 
-	/* i2c_lut array entries are regulator framework selectors */
-	unsigned			i2c_lut[MAX_DFLL_VOLTAGES];
-	int				i2c_lut_size;
-	u8				lut_min, lut_max, lut_safe;
+	/* lut array entries are regulator framework selectors or PWM values*/
+	unsigned int			lut[MAX_DFLL_VOLTAGES];
+	unsigned int			lut_uv[MAX_DFLL_VOLTAGES];
+	int				lut_size;
+	u8				lut_bottom, lut_min, lut_max, lut_safe;
+
+	/* PWM interface */
+	enum tegra_dfll_pmu_if		pmu_if;
+	unsigned long			pwm_rate;
+	struct pinctrl			*pwm_pin;
+	struct pinctrl_state		*pwm_enable_state;
+	struct pinctrl_state		*pwm_disable_state;
 };
 
 #define clk_hw_to_dfll(_hw) container_of(_hw, struct tegra_dfll, dfll_clk_hw)
@@ -490,6 +505,36 @@ static void dfll_set_mode(struct tegra_dfll *td,
 }
 
 /*
+ * DVCO rate control
+ */
+
+static unsigned long get_dvco_rate_below(struct tegra_dfll *td, u8 out_min)
+{
+	struct dev_pm_opp *opp;
+	unsigned long rate, prev_rate;
+	int uv, min_uv;
+
+	min_uv = td->lut_uv[out_min];
+	for (rate = 0, prev_rate = 0; ; rate++) {
+		rcu_read_lock();
+		opp = dev_pm_opp_find_freq_ceil(td->soc->dev, &rate);
+		if (IS_ERR(opp)) {
+			rcu_read_unlock();
+			break;
+		}
+		uv = dev_pm_opp_get_voltage(opp);
+		rcu_read_unlock();
+
+		if (uv && uv > min_uv)
+			return prev_rate;
+
+		prev_rate = rate;
+	}
+
+	return prev_rate;
+}
+
+/*
  * DFLL-to-I2C controller interface
  */
 
@@ -518,6 +563,118 @@ static int dfll_i2c_set_output_enabled(struct tegra_dfll *td, bool enable)
 	return 0;
 }
 
+
+/*
+ * DFLL-to-PWM controller interface
+ */
+
+/**
+ * dfll_pwm_set_output_enabled - enable/disable PWM voltage requests
+ * @td: DFLL instance
+ * @enable: whether to enable or disable the PWM voltage requests
+ *
+ * Set the master enable control for PWM control value updates. If disabled,
+ * then the PWM signal is not driven. Also configure the PWM output pad
+ * to the appropriate state.
+ */
+static int dfll_pwm_set_output_enabled(struct tegra_dfll *td, bool enable)
+{
+	int ret;
+	u32 val, div;
+
+	if (enable) {
+		ret = pinctrl_select_state(td->pwm_pin, td->pwm_enable_state);
+		if (ret < 0) {
+			dev_err(td->dev, "setting enable state failed\n");
+			return -EINVAL;
+		}
+		val = dfll_readl(td, DFLL_OUTPUT_CFG);
+		val &= ~DFLL_OUTPUT_CFG_PWM_DIV_MASK;
+		div = DIV_ROUND_UP(td->ref_rate, td->pwm_rate);
+		val |= (div << DFLL_OUTPUT_CFG_PWM_DIV_SHIFT)
+				& DFLL_OUTPUT_CFG_PWM_DIV_MASK;
+		dfll_writel(td, val, DFLL_OUTPUT_CFG);
+		dfll_wmb(td);
+
+		val |= DFLL_OUTPUT_CFG_PWM_ENABLE;
+		dfll_writel(td, val, DFLL_OUTPUT_CFG);
+		dfll_wmb(td);
+	} else {
+		ret = pinctrl_select_state(td->pwm_pin, td->pwm_disable_state);
+		if (ret < 0)
+			dev_warn(td->dev, "setting disable state failed\n");
+
+		val = dfll_readl(td, DFLL_OUTPUT_CFG);
+		val &= ~DFLL_OUTPUT_CFG_PWM_ENABLE;
+		dfll_writel(td, val, DFLL_OUTPUT_CFG);
+		dfll_wmb(td);
+	}
+
+	return 0;
+}
+/**
+ * dfll_set_force_output_value - set fixed value for force output
+ * @td: DFLL instance
+ * @out_val: value to force output
+ *
+ * Set the fixec value for force output, DFLL will output this value when
+ * force output is enabled.
+ */
+static u32 dfll_set_force_output_value(struct tegra_dfll *td, u8 out_val)
+{
+	u32 val = dfll_readl(td, DFLL_OUTPUT_FORCE);
+
+	val = (val & DFLL_OUTPUT_FORCE_ENABLE) | (out_val & OUT_MASK);
+	dfll_writel(td, val, DFLL_OUTPUT_FORCE);
+	dfll_wmb(td);
+
+	return dfll_readl(td, DFLL_OUTPUT_FORCE);
+}
+
+/**
+ * dfll_set_force_output_enabled - enable/disable force output
+ * @td: DFLL instance
+ * @enable: whether to enable or disable the force output
+ *
+ * Set the enable control for fouce output with fixed value.
+ */
+static void dfll_set_force_output_enabled(struct tegra_dfll *td, bool enable)
+{
+	u32 val = dfll_readl(td, DFLL_OUTPUT_FORCE);
+
+	if (enable)
+		val |= DFLL_OUTPUT_FORCE_ENABLE;
+	else
+		val &= ~DFLL_OUTPUT_FORCE_ENABLE;
+
+	dfll_writel(td, val, DFLL_OUTPUT_FORCE);
+	dfll_wmb(td);
+}
+
+/**
+ * dfll_i2c_set_output_enabled - enable/disable I2C PMIC voltage requests
+ * @td: DFLL instance
+ * @enable: whether to enable or disable the I2C voltage requests
+ *
+ * Set the master enable control for I2C control value updates. If disabled,
+ * then I2C control messages are inhibited, regardless of the DFLL mode.
+ */
+static int dfll_force_output(struct tegra_dfll *td, unsigned int out_sel)
+{
+	u32 val;
+
+	if (out_sel > OUT_MASK)
+		return -EINVAL;
+
+	val = dfll_set_force_output_value(td, out_sel);
+	if ((td->mode < DFLL_CLOSED_LOOP) &&
+	    !(val & DFLL_OUTPUT_FORCE_ENABLE)) {
+		dfll_set_force_output_enabled(td, true);
+	}
+
+	return 0;
+}
+
 /**
  * dfll_load_lut - load the voltage lookup table
  * @td: struct tegra_dfll *
@@ -531,15 +688,15 @@ static void dfll_load_i2c_lut(struct tegra_dfll *td)
 	u32 val;
 
 	for (i = 0; i < MAX_DFLL_VOLTAGES; i++) {
-		if (i < td->lut_min)
-			lut_index = td->lut_min;
-		else if (i > td->lut_max)
-			lut_index = td->lut_max;
+		if (i < td->lut_bottom)
+			lut_index = td->lut_bottom;
+		else if (i > td->lut_size - 1)
+			lut_index = td->lut_size - 1;
 		else
 			lut_index = i;
 
 		val = regulator_list_hardware_vsel(td->vdd_reg,
-						     td->i2c_lut[lut_index]);
+						     td->lut[lut_index]);
 		__raw_writel(val, td->lut_base + i * 4);
 	}
 
@@ -595,23 +752,53 @@ static void dfll_init_out_if(struct tegra_dfll *td)
 	u32 val;
 
 	td->lut_min = 0;
-	td->lut_max = td->i2c_lut_size - 1;
-	td->lut_safe = td->lut_min + 1;
-
-	dfll_i2c_writel(td, 0, DFLL_OUTPUT_CFG);
-	val = (td->lut_safe << DFLL_OUTPUT_CFG_SAFE_SHIFT) |
-		(td->lut_max << DFLL_OUTPUT_CFG_MAX_SHIFT) |
-		(td->lut_min << DFLL_OUTPUT_CFG_MIN_SHIFT);
-	dfll_i2c_writel(td, val, DFLL_OUTPUT_CFG);
-	dfll_i2c_wmb(td);
-
-	dfll_writel(td, 0, DFLL_OUTPUT_FORCE);
-	dfll_i2c_writel(td, 0, DFLL_INTR_EN);
-	dfll_i2c_writel(td, DFLL_INTR_MAX_MASK | DFLL_INTR_MIN_MASK,
-			DFLL_INTR_STS);
-
-	dfll_load_i2c_lut(td);
-	dfll_init_i2c_if(td);
+	td->lut_max = td->lut_size - 1;
+	td->lut_safe = td->lut_min + (td->lut_min < td->lut_max ? 1 : 0);
+
+	if (td->pmu_if == TEGRA_DFLL_PMU_PWM) {
+		int vinit = td->reg_init_uV;
+		int vstep = td->soc->alignment.step_uv;
+		int vmin = td->lut_uv[0];
+
+		/* clear DFLL_OUTPUT_CFG before setting new value */
+		dfll_writel(td, 0, DFLL_OUTPUT_CFG);
+		dfll_wmb(td);
+
+		val = dfll_readl(td, DFLL_OUTPUT_CFG);
+		val |= (td->lut_safe << DFLL_OUTPUT_CFG_SAFE_SHIFT) |
+		       (td->lut_max << DFLL_OUTPUT_CFG_MAX_SHIFT) |
+		       (td->lut_min << DFLL_OUTPUT_CFG_MIN_SHIFT);
+		dfll_writel(td, val, DFLL_OUTPUT_CFG);
+		dfll_wmb(td);
+
+		dfll_writel(td, 0, DFLL_OUTPUT_FORCE);
+		dfll_i2c_writel(td, 0, DFLL_INTR_EN);
+		dfll_i2c_writel(td, DFLL_INTR_MAX_MASK | DFLL_INTR_MIN_MASK,
+				DFLL_INTR_STS);
+
+		/* set initial voltage */
+		if ((vinit >= vmin) && vstep) {
+			unsigned int vsel;
+
+			vsel = DIV_ROUND_UP((vinit - vmin), vstep);
+			dfll_force_output(td, vsel);
+		}
+	} else {
+		dfll_i2c_writel(td, 0, DFLL_OUTPUT_CFG);
+		val = (td->lut_safe << DFLL_OUTPUT_CFG_SAFE_SHIFT) |
+			(td->lut_max << DFLL_OUTPUT_CFG_MAX_SHIFT) |
+			(td->lut_min << DFLL_OUTPUT_CFG_MIN_SHIFT);
+		dfll_i2c_writel(td, val, DFLL_OUTPUT_CFG);
+		dfll_i2c_wmb(td);
+
+		dfll_writel(td, 0, DFLL_OUTPUT_FORCE);
+		dfll_i2c_writel(td, 0, DFLL_INTR_EN);
+		dfll_i2c_writel(td, DFLL_INTR_MAX_MASK | DFLL_INTR_MIN_MASK,
+				DFLL_INTR_STS);
+
+		dfll_load_i2c_lut(td);
+		dfll_init_i2c_if(td);
+	}
 }
 
 /*
@@ -637,11 +824,12 @@ static int find_lut_index_for_rate(struct tegra_dfll *td, unsigned long rate)
 	if (IS_ERR(opp))
 		return PTR_ERR(opp);
 
-	uv = dev_pm_opp_get_voltage(opp);
+	uv = dev_pm_opp_get_voltage(opp) / td->soc->alignment.step_uv;
+
 	dev_pm_opp_put(opp);
 
-	for (i = 0; i < td->i2c_lut_size; i++) {
-		if (regulator_list_voltage(td->vdd_reg, td->i2c_lut[i]) == uv)
+	for (i = td->lut_bottom; i < td->lut_size; i++) {
+		if ((td->lut_uv[i] / td->soc->alignment.step_uv) >= uv)
 			return i;
 	}
 
@@ -863,9 +1051,14 @@ static int dfll_lock(struct tegra_dfll *td)
 			return -EINVAL;
 		}
 
-		dfll_i2c_set_output_enabled(td, true);
+		if (td->pmu_if == TEGRA_DFLL_PMU_PWM)
+			dfll_pwm_set_output_enabled(td, true);
+		else
+			dfll_i2c_set_output_enabled(td, true);
+
 		dfll_set_mode(td, DFLL_CLOSED_LOOP);
 		dfll_set_frequency_request(td, req);
+		dfll_set_force_output_enabled(td, false);
 		return 0;
 
 	default:
@@ -889,7 +1082,10 @@ static int dfll_unlock(struct tegra_dfll *td)
 	case DFLL_CLOSED_LOOP:
 		dfll_set_open_loop_config(td);
 		dfll_set_mode(td, DFLL_OPEN_LOOP);
-		dfll_i2c_set_output_enabled(td, false);
+		if (td->pmu_if == TEGRA_DFLL_PMU_PWM)
+			dfll_pwm_set_output_enabled(td, false);
+		else
+			dfll_i2c_set_output_enabled(td, false);
 		return 0;
 
 	case DFLL_OPEN_LOOP:
@@ -1171,15 +1367,17 @@ static int attr_registers_show(struct seq_file *s, void *data)
 		seq_printf(s, "[0x%02x] = 0x%08x\n", offs,
 			   dfll_i2c_readl(td, offs));
 
-	seq_puts(s, "\nINTEGRATED I2C CONTROLLER REGISTERS:\n");
-	offs = DFLL_I2C_CLK_DIVISOR;
-	seq_printf(s, "[0x%02x] = 0x%08x\n", offs,
-		   __raw_readl(td->i2c_controller_base + offs));
-
-	seq_puts(s, "\nLUT:\n");
-	for (offs = 0; offs <  4 * MAX_DFLL_VOLTAGES; offs += 4)
+	if (td->pmu_if == TEGRA_DFLL_PMU_I2C) {
+		seq_puts(s, "\nINTEGRATED I2C CONTROLLER REGISTERS:\n");
+		offs = DFLL_I2C_CLK_DIVISOR;
 		seq_printf(s, "[0x%02x] = 0x%08x\n", offs,
-			   __raw_readl(td->lut_base + offs));
+			   __raw_readl(td->i2c_controller_base + offs));
+
+		seq_puts(s, "\nLUT:\n");
+		for (offs = 0; offs <  4 * MAX_DFLL_VOLTAGES; offs += 4)
+			seq_printf(s, "[0x%02x] = 0x%08x\n", offs,
+				   __raw_readl(td->lut_base + offs));
+	}
 
 	return 0;
 }
@@ -1377,15 +1575,17 @@ static int dfll_init(struct tegra_dfll *td)
  */
 static int find_vdd_map_entry_exact(struct tegra_dfll *td, int uV)
 {
-	int i, n_voltages, reg_uV;
+	int i, n_voltages, reg_mult, align_mult;
 
+	align_mult = uV / td->soc->alignment.step_uv;
 	n_voltages = regulator_count_voltages(td->vdd_reg);
 	for (i = 0; i < n_voltages; i++) {
-		reg_uV = regulator_list_voltage(td->vdd_reg, i);
-		if (reg_uV < 0)
+		reg_mult = regulator_list_voltage(td->vdd_reg, i) /
+					td->soc->alignment.step_uv;
+		if (reg_mult < 0)
 			break;
 
-		if (uV == reg_uV)
+		if (align_mult == reg_mult)
 			return i;
 	}
 
@@ -1399,15 +1599,17 @@ static int find_vdd_map_entry_exact(struct tegra_dfll *td, int uV)
  * */
 static int find_vdd_map_entry_min(struct tegra_dfll *td, int uV)
 {
-	int i, n_voltages, reg_uV;
+	int i, n_voltages, reg_mult, align_mult;
 
+	align_mult = uV / td->soc->alignment.step_uv;
 	n_voltages = regulator_count_voltages(td->vdd_reg);
 	for (i = 0; i < n_voltages; i++) {
-		reg_uV = regulator_list_voltage(td->vdd_reg, i);
-		if (reg_uV < 0)
+		reg_mult = regulator_list_voltage(td->vdd_reg, i) /
+					td->soc->alignment.step_uv;
+		if (reg_mult < 0)
 			break;
 
-		if (uV <= reg_uV)
+		if (align_mult <= reg_mult)
 			return i;
 	}
 
@@ -1415,6 +1617,53 @@ static int find_vdd_map_entry_min(struct tegra_dfll *td, int uV)
 	return -EINVAL;
 }
 
+/*
+ * Look-up table in h/w is ignored when PWM is used as DFLL interface to PMIC.
+ * In this case closed loop output is controlling duty cycle directly. The s/w
+ * look-up that maps PWM duty cycle to voltage is still built by this function.
+ */
+static int dfll_build_lut_pwm(struct tegra_dfll *td, int v_max)
+{
+	int i, reg_volt;
+	unsigned long rate;
+	u8 lut_bottom = MAX_DFLL_VOLTAGES;
+	int v_min = td->soc->cvb->min_millivolts * 1000;
+
+	for (i = 0; i < MAX_DFLL_VOLTAGES; i++) {
+		reg_volt = td->lut_uv[i];
+
+		/* since opp voltage is exact mv */
+		reg_volt = (reg_volt / 1000) * 1000;
+		if (reg_volt > v_max)
+			break;
+
+		td->lut[i] = i;
+		if ((lut_bottom == MAX_DFLL_VOLTAGES) && (reg_volt >= v_min))
+			lut_bottom = i;
+	}
+
+	/* determine voltage boundaries */
+	td->lut_size = i;
+	if ((lut_bottom == MAX_DFLL_VOLTAGES) ||
+	    (lut_bottom + 1 >= td->lut_size)) {
+		dev_err(td->dev, "no voltage above DFLL minimum %d mV\n",
+			td->soc->cvb->min_millivolts);
+		return -EINVAL;
+	}
+	td->lut_bottom = lut_bottom;
+
+	/* determine rate boundaries */
+	rate = get_dvco_rate_below(td, td->lut_bottom);
+	if (!rate) {
+		dev_err(td->dev, "no opp below DFLL minimum voltage %d mV\n",
+			td->soc->cvb->min_millivolts);
+		return -EINVAL;
+	}
+	td->dvco_rate_min = rate;
+
+	return 0;
+}
+
 /**
  * dfll_build_i2c_lut - build the I2C voltage register lookup table
  * @td: DFLL instance
@@ -1427,31 +1676,24 @@ static int find_vdd_map_entry_min(struct tegra_dfll *td, int uV)
  *
  * On success, fills in td->i2c_lut and returns 0, or -err on failure.
  */
-static int dfll_build_i2c_lut(struct tegra_dfll *td)
+static int dfll_build_i2c_lut(struct tegra_dfll *td, int v_max)
 {
+	unsigned long rate;
 	int ret = -EINVAL;
-	int j, v, v_max, v_opp;
+	int j, v, v_opp;
 	int selector;
-	unsigned long rate;
-	struct dev_pm_opp *opp;
 	int lut;
 
-	rate = ULONG_MAX;
-	opp = dev_pm_opp_find_freq_floor(td->soc->dev, &rate);
-	if (IS_ERR(opp)) {
-		dev_err(td->dev, "couldn't get vmax opp, empty opp table?\n");
-		goto out;
-	}
-	v_max = dev_pm_opp_get_voltage(opp);
-	dev_pm_opp_put(opp);
-
 	v = td->soc->cvb->min_millivolts * 1000;
 	lut = find_vdd_map_entry_exact(td, v);
 	if (lut < 0)
 		goto out;
-	td->i2c_lut[0] = lut;
+	td->lut[0] = lut;
+	td->lut_bottom = 0;
 
 	for (j = 1, rate = 0; ; rate++) {
+		struct dev_pm_opp *opp;
+
 		opp = dev_pm_opp_find_freq_ceil(td->soc->dev, &rate);
 		if (IS_ERR(opp))
 			break;
@@ -1470,32 +1712,61 @@ static int dfll_build_i2c_lut(struct tegra_dfll *td)
 			selector = find_vdd_map_entry_min(td, v);
 			if (selector < 0)
 				goto out;
-			if (selector != td->i2c_lut[j - 1])
-				td->i2c_lut[j++] = selector;
+			if (selector != td->lut[j - 1])
+				td->lut[j++] = selector;
 		}
 
 		v = (j == MAX_DFLL_VOLTAGES - 1) ? v_max : v_opp;
 		selector = find_vdd_map_entry_exact(td, v);
 		if (selector < 0)
 			goto out;
-		if (selector != td->i2c_lut[j - 1])
-			td->i2c_lut[j++] = selector;
+		if (selector != td->lut[j - 1])
+			td->lut[j++] = selector;
 
 		if (v >= v_max)
 			break;
 	}
-	td->i2c_lut_size = j;
+	td->lut_size = j;
 
 	if (!td->dvco_rate_min)
 		dev_err(td->dev, "no opp above DFLL minimum voltage %d mV\n",
 			td->soc->cvb->min_millivolts);
-	else
+	else {
 		ret = 0;
+		for (j = 0; j < td->lut_size; j++)
+			td->lut_uv[j] =
+				regulator_list_voltage(td->vdd_reg,
+						       td->lut[j]);
+	}
 
 out:
 	return ret;
 }
 
+static int dfll_build_lut(struct tegra_dfll *td)
+{
+	unsigned long rate;
+	struct dev_pm_opp *opp;
+	int v_max;
+
+	rcu_read_lock();
+
+	rate = ULONG_MAX;
+	opp = dev_pm_opp_find_freq_floor(td->soc->dev, &rate);
+	if (IS_ERR(opp)) {
+		dev_err(td->dev, "couldn't get vmax opp, empty opp table?\n");
+		return -EINVAL;
+	}
+	v_max = dev_pm_opp_get_voltage(opp);
+
+	rcu_read_unlock();
+
+	if (td->pmu_if == TEGRA_DFLL_PMU_PWM)
+		return dfll_build_lut_pwm(td, v_max);
+	else
+		return dfll_build_i2c_lut(td, v_max);
+}
+
 /**
  * read_dt_param - helper function for reading required parameters from the DT
  * @td: DFLL instance
@@ -1554,12 +1825,55 @@ static int dfll_fetch_i2c_params(struct tegra_dfll *td)
 	}
 	td->i2c_reg = vsel_reg;
 
-	ret = dfll_build_i2c_lut(td);
-	if (ret) {
-		dev_err(td->dev, "couldn't build I2C LUT\n");
+	return 0;
+}
+
+static int dfll_fetch_pwm_params(struct tegra_dfll *td)
+{
+	int ret, i;
+	u32 pwm_period;
+
+	if (!td->soc->alignment.step_uv || !td->soc->alignment.offset_uv) {
+		dev_err(td->dev, "Missing step or alignment info for PWM regulator");
+		return -EINVAL;
+	}
+	for (i = 0; i < MAX_DFLL_VOLTAGES; i++)
+		td->lut_uv[i] = td->soc->alignment.offset_uv +
+				i * td->soc->alignment.step_uv;
+
+	ret = read_dt_param(td, "nvidia,init-uv", &td->reg_init_uV);
+	if (!ret) {
+		dev_err(td->dev, "couldn't get initialized voltage\n");
 		return ret;
 	}
 
+	ret = read_dt_param(td, "nvidia,pwm-period", &pwm_period);
+	if (!ret) {
+		dev_err(td->dev, "couldn't get PWM period\n");
+		return ret;
+	}
+	td->pwm_rate = (NSEC_PER_SEC / pwm_period) * (MAX_DFLL_VOLTAGES - 1);
+
+	td->pwm_pin = devm_pinctrl_get(td->dev);
+	if (IS_ERR(td->pwm_pin)) {
+		dev_err(td->dev, "DT: missing pinctrl device\n");
+		return PTR_ERR(td->pwm_pin);
+	}
+
+	td->pwm_enable_state = pinctrl_lookup_state(td->pwm_pin,
+						    "dvfs_pwm_enable");
+	if (IS_ERR(td->pwm_enable_state)) {
+		dev_err(td->dev, "DT: missing pwm enabled state\n");
+		return PTR_ERR(td->pwm_enable_state);
+	}
+
+	td->pwm_disable_state = pinctrl_lookup_state(td->pwm_pin,
+						     "dvfs_pwm_disable");
+	if (IS_ERR(td->pwm_disable_state)) {
+		dev_err(td->dev, "DT: missing pwm disabled state\n");
+		return PTR_ERR(td->pwm_disable_state);
+	}
+
 	return 0;
 }
 
@@ -1625,12 +1939,6 @@ int tegra_dfll_register(struct platform_device *pdev,
 
 	td->soc = soc;
 
-	td->vdd_reg = devm_regulator_get(td->dev, "vdd-cpu");
-	if (IS_ERR(td->vdd_reg)) {
-		dev_err(td->dev, "couldn't get vdd_cpu regulator\n");
-		return PTR_ERR(td->vdd_reg);
-	}
-
 	td->dvco_rst = devm_reset_control_get(td->dev, "dvco");
 	if (IS_ERR(td->dvco_rst)) {
 		dev_err(td->dev, "couldn't get dvco reset\n");
@@ -1643,10 +1951,27 @@ int tegra_dfll_register(struct platform_device *pdev,
 		return ret;
 	}
 
-	ret = dfll_fetch_i2c_params(td);
+	if (of_property_read_bool(td->dev->of_node, "nvidia,pwm-to-pmic")) {
+		td->pmu_if = TEGRA_DFLL_PMU_PWM;
+		ret = dfll_fetch_pwm_params(td);
+	} else  {
+		td->vdd_reg = devm_regulator_get(td->dev, "vdd-cpu");
+		if (IS_ERR(td->vdd_reg)) {
+			dev_err(td->dev, "couldn't get vdd_cpu regulator\n");
+			return PTR_ERR(td->vdd_reg);
+		}
+		td->pmu_if = TEGRA_DFLL_PMU_I2C;
+		ret = dfll_fetch_i2c_params(td);
+	}
 	if (ret)
 		return ret;
 
+	ret = dfll_build_lut(td);
+	if (ret) {
+		dev_err(td->dev, "couldn't build LUT\n");
+		return ret;
+	}
+
 	mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
 	if (!mem) {
 		dev_err(td->dev, "no control register resource\n");
diff --git a/drivers/clk/tegra/clk-dfll.h b/drivers/clk/tegra/clk-dfll.h
index 83352c8..228f54a 100644
--- a/drivers/clk/tegra/clk-dfll.h
+++ b/drivers/clk/tegra/clk-dfll.h
@@ -21,6 +21,7 @@
 #include <linux/platform_device.h>
 #include <linux/reset.h>
 #include <linux/types.h>
+#include "cvb.h"
 
 /**
  * struct tegra_dfll_soc_data - SoC-specific hooks/integration for the DFLL driver
@@ -35,6 +36,12 @@ struct tegra_dfll_soc_data {
 	struct device *dev;
 	unsigned long max_freq;
 	const struct cvb_table *cvb;
+	struct rail_alignment alignment;
+	unsigned int min_millivolts;
+	unsigned int tune_high_min_millivolts;
+	u32 tune0_low;
+	u32 tune0_high;
+	u32 tune1;
 
 	void (*init_clock_trimmers)(void);
 	void (*set_clock_trimmers_high)(void);
-- 
1.9.1


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

* [PATCH v2 4/6] clk: tegra: dfll: support PWM regulator control
@ 2018-01-24 12:45   ` Peter De Schrijver
  0 siblings, 0 replies; 56+ messages in thread
From: Peter De Schrijver @ 2018-01-24 12:45 UTC (permalink / raw)
  To: linux-tegra, linux-clk, mturquette, sboyd; +Cc: Peter De Schrijver

The DFLL can directly generate a PWM signal to control the regulator IC
rather then sending i2c messages. This patch adds support for this mode.
In this mode the LUT is not used and also there is no regulator object
involved because there is no way to control the regulator voltage without
also changing the DFLL output frequency. Also the register debugfs file
is slightly reworked to only show the i2c registers when i2c mode is in
use.

Signed-off-by: Peter De Schrijver <pdeschrijver@nvidia.com>
---
 drivers/clk/tegra/clk-dfll.c | 481 ++++++++++++++++++++++++++++++++++++-------
 drivers/clk/tegra/clk-dfll.h |   7 +
 2 files changed, 410 insertions(+), 78 deletions(-)

diff --git a/drivers/clk/tegra/clk-dfll.c b/drivers/clk/tegra/clk-dfll.c
index 0a7deee..204717c 100644
--- a/drivers/clk/tegra/clk-dfll.c
+++ b/drivers/clk/tegra/clk-dfll.c
@@ -243,6 +243,12 @@ enum dfll_tune_range {
 	DFLL_TUNE_LOW = 1,
 };
 
+
+enum tegra_dfll_pmu_if {
+	TEGRA_DFLL_PMU_I2C = 0,
+	TEGRA_DFLL_PMU_PWM = 1,
+};
+
 /**
  * struct dfll_rate_req - target DFLL rate request data
  * @rate: target frequency, after the postscaling
@@ -292,18 +298,27 @@ struct tegra_dfll {
 	u32				force_mode;
 	u32				cf;
 	u32				ci;
-	u32				cg;
+	s32				cg;
 	bool				cg_scale;
+	u32				reg_init_uV;
 
 	/* I2C interface parameters */
 	u32				i2c_fs_rate;
 	u32				i2c_reg;
 	u32				i2c_slave_addr;
 
-	/* i2c_lut array entries are regulator framework selectors */
-	unsigned			i2c_lut[MAX_DFLL_VOLTAGES];
-	int				i2c_lut_size;
-	u8				lut_min, lut_max, lut_safe;
+	/* lut array entries are regulator framework selectors or PWM values*/
+	unsigned int			lut[MAX_DFLL_VOLTAGES];
+	unsigned int			lut_uv[MAX_DFLL_VOLTAGES];
+	int				lut_size;
+	u8				lut_bottom, lut_min, lut_max, lut_safe;
+
+	/* PWM interface */
+	enum tegra_dfll_pmu_if		pmu_if;
+	unsigned long			pwm_rate;
+	struct pinctrl			*pwm_pin;
+	struct pinctrl_state		*pwm_enable_state;
+	struct pinctrl_state		*pwm_disable_state;
 };
 
 #define clk_hw_to_dfll(_hw) container_of(_hw, struct tegra_dfll, dfll_clk_hw)
@@ -490,6 +505,36 @@ static void dfll_set_mode(struct tegra_dfll *td,
 }
 
 /*
+ * DVCO rate control
+ */
+
+static unsigned long get_dvco_rate_below(struct tegra_dfll *td, u8 out_min)
+{
+	struct dev_pm_opp *opp;
+	unsigned long rate, prev_rate;
+	int uv, min_uv;
+
+	min_uv = td->lut_uv[out_min];
+	for (rate = 0, prev_rate = 0; ; rate++) {
+		rcu_read_lock();
+		opp = dev_pm_opp_find_freq_ceil(td->soc->dev, &rate);
+		if (IS_ERR(opp)) {
+			rcu_read_unlock();
+			break;
+		}
+		uv = dev_pm_opp_get_voltage(opp);
+		rcu_read_unlock();
+
+		if (uv && uv > min_uv)
+			return prev_rate;
+
+		prev_rate = rate;
+	}
+
+	return prev_rate;
+}
+
+/*
  * DFLL-to-I2C controller interface
  */
 
@@ -518,6 +563,118 @@ static int dfll_i2c_set_output_enabled(struct tegra_dfll *td, bool enable)
 	return 0;
 }
 
+
+/*
+ * DFLL-to-PWM controller interface
+ */
+
+/**
+ * dfll_pwm_set_output_enabled - enable/disable PWM voltage requests
+ * @td: DFLL instance
+ * @enable: whether to enable or disable the PWM voltage requests
+ *
+ * Set the master enable control for PWM control value updates. If disabled,
+ * then the PWM signal is not driven. Also configure the PWM output pad
+ * to the appropriate state.
+ */
+static int dfll_pwm_set_output_enabled(struct tegra_dfll *td, bool enable)
+{
+	int ret;
+	u32 val, div;
+
+	if (enable) {
+		ret = pinctrl_select_state(td->pwm_pin, td->pwm_enable_state);
+		if (ret < 0) {
+			dev_err(td->dev, "setting enable state failed\n");
+			return -EINVAL;
+		}
+		val = dfll_readl(td, DFLL_OUTPUT_CFG);
+		val &= ~DFLL_OUTPUT_CFG_PWM_DIV_MASK;
+		div = DIV_ROUND_UP(td->ref_rate, td->pwm_rate);
+		val |= (div << DFLL_OUTPUT_CFG_PWM_DIV_SHIFT)
+				& DFLL_OUTPUT_CFG_PWM_DIV_MASK;
+		dfll_writel(td, val, DFLL_OUTPUT_CFG);
+		dfll_wmb(td);
+
+		val |= DFLL_OUTPUT_CFG_PWM_ENABLE;
+		dfll_writel(td, val, DFLL_OUTPUT_CFG);
+		dfll_wmb(td);
+	} else {
+		ret = pinctrl_select_state(td->pwm_pin, td->pwm_disable_state);
+		if (ret < 0)
+			dev_warn(td->dev, "setting disable state failed\n");
+
+		val = dfll_readl(td, DFLL_OUTPUT_CFG);
+		val &= ~DFLL_OUTPUT_CFG_PWM_ENABLE;
+		dfll_writel(td, val, DFLL_OUTPUT_CFG);
+		dfll_wmb(td);
+	}
+
+	return 0;
+}
+/**
+ * dfll_set_force_output_value - set fixed value for force output
+ * @td: DFLL instance
+ * @out_val: value to force output
+ *
+ * Set the fixec value for force output, DFLL will output this value when
+ * force output is enabled.
+ */
+static u32 dfll_set_force_output_value(struct tegra_dfll *td, u8 out_val)
+{
+	u32 val = dfll_readl(td, DFLL_OUTPUT_FORCE);
+
+	val = (val & DFLL_OUTPUT_FORCE_ENABLE) | (out_val & OUT_MASK);
+	dfll_writel(td, val, DFLL_OUTPUT_FORCE);
+	dfll_wmb(td);
+
+	return dfll_readl(td, DFLL_OUTPUT_FORCE);
+}
+
+/**
+ * dfll_set_force_output_enabled - enable/disable force output
+ * @td: DFLL instance
+ * @enable: whether to enable or disable the force output
+ *
+ * Set the enable control for fouce output with fixed value.
+ */
+static void dfll_set_force_output_enabled(struct tegra_dfll *td, bool enable)
+{
+	u32 val = dfll_readl(td, DFLL_OUTPUT_FORCE);
+
+	if (enable)
+		val |= DFLL_OUTPUT_FORCE_ENABLE;
+	else
+		val &= ~DFLL_OUTPUT_FORCE_ENABLE;
+
+	dfll_writel(td, val, DFLL_OUTPUT_FORCE);
+	dfll_wmb(td);
+}
+
+/**
+ * dfll_i2c_set_output_enabled - enable/disable I2C PMIC voltage requests
+ * @td: DFLL instance
+ * @enable: whether to enable or disable the I2C voltage requests
+ *
+ * Set the master enable control for I2C control value updates. If disabled,
+ * then I2C control messages are inhibited, regardless of the DFLL mode.
+ */
+static int dfll_force_output(struct tegra_dfll *td, unsigned int out_sel)
+{
+	u32 val;
+
+	if (out_sel > OUT_MASK)
+		return -EINVAL;
+
+	val = dfll_set_force_output_value(td, out_sel);
+	if ((td->mode < DFLL_CLOSED_LOOP) &&
+	    !(val & DFLL_OUTPUT_FORCE_ENABLE)) {
+		dfll_set_force_output_enabled(td, true);
+	}
+
+	return 0;
+}
+
 /**
  * dfll_load_lut - load the voltage lookup table
  * @td: struct tegra_dfll *
@@ -531,15 +688,15 @@ static void dfll_load_i2c_lut(struct tegra_dfll *td)
 	u32 val;
 
 	for (i = 0; i < MAX_DFLL_VOLTAGES; i++) {
-		if (i < td->lut_min)
-			lut_index = td->lut_min;
-		else if (i > td->lut_max)
-			lut_index = td->lut_max;
+		if (i < td->lut_bottom)
+			lut_index = td->lut_bottom;
+		else if (i > td->lut_size - 1)
+			lut_index = td->lut_size - 1;
 		else
 			lut_index = i;
 
 		val = regulator_list_hardware_vsel(td->vdd_reg,
-						     td->i2c_lut[lut_index]);
+						     td->lut[lut_index]);
 		__raw_writel(val, td->lut_base + i * 4);
 	}
 
@@ -595,23 +752,53 @@ static void dfll_init_out_if(struct tegra_dfll *td)
 	u32 val;
 
 	td->lut_min = 0;
-	td->lut_max = td->i2c_lut_size - 1;
-	td->lut_safe = td->lut_min + 1;
-
-	dfll_i2c_writel(td, 0, DFLL_OUTPUT_CFG);
-	val = (td->lut_safe << DFLL_OUTPUT_CFG_SAFE_SHIFT) |
-		(td->lut_max << DFLL_OUTPUT_CFG_MAX_SHIFT) |
-		(td->lut_min << DFLL_OUTPUT_CFG_MIN_SHIFT);
-	dfll_i2c_writel(td, val, DFLL_OUTPUT_CFG);
-	dfll_i2c_wmb(td);
-
-	dfll_writel(td, 0, DFLL_OUTPUT_FORCE);
-	dfll_i2c_writel(td, 0, DFLL_INTR_EN);
-	dfll_i2c_writel(td, DFLL_INTR_MAX_MASK | DFLL_INTR_MIN_MASK,
-			DFLL_INTR_STS);
-
-	dfll_load_i2c_lut(td);
-	dfll_init_i2c_if(td);
+	td->lut_max = td->lut_size - 1;
+	td->lut_safe = td->lut_min + (td->lut_min < td->lut_max ? 1 : 0);
+
+	if (td->pmu_if == TEGRA_DFLL_PMU_PWM) {
+		int vinit = td->reg_init_uV;
+		int vstep = td->soc->alignment.step_uv;
+		int vmin = td->lut_uv[0];
+
+		/* clear DFLL_OUTPUT_CFG before setting new value */
+		dfll_writel(td, 0, DFLL_OUTPUT_CFG);
+		dfll_wmb(td);
+
+		val = dfll_readl(td, DFLL_OUTPUT_CFG);
+		val |= (td->lut_safe << DFLL_OUTPUT_CFG_SAFE_SHIFT) |
+		       (td->lut_max << DFLL_OUTPUT_CFG_MAX_SHIFT) |
+		       (td->lut_min << DFLL_OUTPUT_CFG_MIN_SHIFT);
+		dfll_writel(td, val, DFLL_OUTPUT_CFG);
+		dfll_wmb(td);
+
+		dfll_writel(td, 0, DFLL_OUTPUT_FORCE);
+		dfll_i2c_writel(td, 0, DFLL_INTR_EN);
+		dfll_i2c_writel(td, DFLL_INTR_MAX_MASK | DFLL_INTR_MIN_MASK,
+				DFLL_INTR_STS);
+
+		/* set initial voltage */
+		if ((vinit >= vmin) && vstep) {
+			unsigned int vsel;
+
+			vsel = DIV_ROUND_UP((vinit - vmin), vstep);
+			dfll_force_output(td, vsel);
+		}
+	} else {
+		dfll_i2c_writel(td, 0, DFLL_OUTPUT_CFG);
+		val = (td->lut_safe << DFLL_OUTPUT_CFG_SAFE_SHIFT) |
+			(td->lut_max << DFLL_OUTPUT_CFG_MAX_SHIFT) |
+			(td->lut_min << DFLL_OUTPUT_CFG_MIN_SHIFT);
+		dfll_i2c_writel(td, val, DFLL_OUTPUT_CFG);
+		dfll_i2c_wmb(td);
+
+		dfll_writel(td, 0, DFLL_OUTPUT_FORCE);
+		dfll_i2c_writel(td, 0, DFLL_INTR_EN);
+		dfll_i2c_writel(td, DFLL_INTR_MAX_MASK | DFLL_INTR_MIN_MASK,
+				DFLL_INTR_STS);
+
+		dfll_load_i2c_lut(td);
+		dfll_init_i2c_if(td);
+	}
 }
 
 /*
@@ -637,11 +824,12 @@ static int find_lut_index_for_rate(struct tegra_dfll *td, unsigned long rate)
 	if (IS_ERR(opp))
 		return PTR_ERR(opp);
 
-	uv = dev_pm_opp_get_voltage(opp);
+	uv = dev_pm_opp_get_voltage(opp) / td->soc->alignment.step_uv;
+
 	dev_pm_opp_put(opp);
 
-	for (i = 0; i < td->i2c_lut_size; i++) {
-		if (regulator_list_voltage(td->vdd_reg, td->i2c_lut[i]) == uv)
+	for (i = td->lut_bottom; i < td->lut_size; i++) {
+		if ((td->lut_uv[i] / td->soc->alignment.step_uv) >= uv)
 			return i;
 	}
 
@@ -863,9 +1051,14 @@ static int dfll_lock(struct tegra_dfll *td)
 			return -EINVAL;
 		}
 
-		dfll_i2c_set_output_enabled(td, true);
+		if (td->pmu_if == TEGRA_DFLL_PMU_PWM)
+			dfll_pwm_set_output_enabled(td, true);
+		else
+			dfll_i2c_set_output_enabled(td, true);
+
 		dfll_set_mode(td, DFLL_CLOSED_LOOP);
 		dfll_set_frequency_request(td, req);
+		dfll_set_force_output_enabled(td, false);
 		return 0;
 
 	default:
@@ -889,7 +1082,10 @@ static int dfll_unlock(struct tegra_dfll *td)
 	case DFLL_CLOSED_LOOP:
 		dfll_set_open_loop_config(td);
 		dfll_set_mode(td, DFLL_OPEN_LOOP);
-		dfll_i2c_set_output_enabled(td, false);
+		if (td->pmu_if == TEGRA_DFLL_PMU_PWM)
+			dfll_pwm_set_output_enabled(td, false);
+		else
+			dfll_i2c_set_output_enabled(td, false);
 		return 0;
 
 	case DFLL_OPEN_LOOP:
@@ -1171,15 +1367,17 @@ static int attr_registers_show(struct seq_file *s, void *data)
 		seq_printf(s, "[0x%02x] = 0x%08x\n", offs,
 			   dfll_i2c_readl(td, offs));
 
-	seq_puts(s, "\nINTEGRATED I2C CONTROLLER REGISTERS:\n");
-	offs = DFLL_I2C_CLK_DIVISOR;
-	seq_printf(s, "[0x%02x] = 0x%08x\n", offs,
-		   __raw_readl(td->i2c_controller_base + offs));
-
-	seq_puts(s, "\nLUT:\n");
-	for (offs = 0; offs <  4 * MAX_DFLL_VOLTAGES; offs += 4)
+	if (td->pmu_if == TEGRA_DFLL_PMU_I2C) {
+		seq_puts(s, "\nINTEGRATED I2C CONTROLLER REGISTERS:\n");
+		offs = DFLL_I2C_CLK_DIVISOR;
 		seq_printf(s, "[0x%02x] = 0x%08x\n", offs,
-			   __raw_readl(td->lut_base + offs));
+			   __raw_readl(td->i2c_controller_base + offs));
+
+		seq_puts(s, "\nLUT:\n");
+		for (offs = 0; offs <  4 * MAX_DFLL_VOLTAGES; offs += 4)
+			seq_printf(s, "[0x%02x] = 0x%08x\n", offs,
+				   __raw_readl(td->lut_base + offs));
+	}
 
 	return 0;
 }
@@ -1377,15 +1575,17 @@ static int dfll_init(struct tegra_dfll *td)
  */
 static int find_vdd_map_entry_exact(struct tegra_dfll *td, int uV)
 {
-	int i, n_voltages, reg_uV;
+	int i, n_voltages, reg_mult, align_mult;
 
+	align_mult = uV / td->soc->alignment.step_uv;
 	n_voltages = regulator_count_voltages(td->vdd_reg);
 	for (i = 0; i < n_voltages; i++) {
-		reg_uV = regulator_list_voltage(td->vdd_reg, i);
-		if (reg_uV < 0)
+		reg_mult = regulator_list_voltage(td->vdd_reg, i) /
+					td->soc->alignment.step_uv;
+		if (reg_mult < 0)
 			break;
 
-		if (uV == reg_uV)
+		if (align_mult == reg_mult)
 			return i;
 	}
 
@@ -1399,15 +1599,17 @@ static int find_vdd_map_entry_exact(struct tegra_dfll *td, int uV)
  * */
 static int find_vdd_map_entry_min(struct tegra_dfll *td, int uV)
 {
-	int i, n_voltages, reg_uV;
+	int i, n_voltages, reg_mult, align_mult;
 
+	align_mult = uV / td->soc->alignment.step_uv;
 	n_voltages = regulator_count_voltages(td->vdd_reg);
 	for (i = 0; i < n_voltages; i++) {
-		reg_uV = regulator_list_voltage(td->vdd_reg, i);
-		if (reg_uV < 0)
+		reg_mult = regulator_list_voltage(td->vdd_reg, i) /
+					td->soc->alignment.step_uv;
+		if (reg_mult < 0)
 			break;
 
-		if (uV <= reg_uV)
+		if (align_mult <= reg_mult)
 			return i;
 	}
 
@@ -1415,6 +1617,53 @@ static int find_vdd_map_entry_min(struct tegra_dfll *td, int uV)
 	return -EINVAL;
 }
 
+/*
+ * Look-up table in h/w is ignored when PWM is used as DFLL interface to PMIC.
+ * In this case closed loop output is controlling duty cycle directly. The s/w
+ * look-up that maps PWM duty cycle to voltage is still built by this function.
+ */
+static int dfll_build_lut_pwm(struct tegra_dfll *td, int v_max)
+{
+	int i, reg_volt;
+	unsigned long rate;
+	u8 lut_bottom = MAX_DFLL_VOLTAGES;
+	int v_min = td->soc->cvb->min_millivolts * 1000;
+
+	for (i = 0; i < MAX_DFLL_VOLTAGES; i++) {
+		reg_volt = td->lut_uv[i];
+
+		/* since opp voltage is exact mv */
+		reg_volt = (reg_volt / 1000) * 1000;
+		if (reg_volt > v_max)
+			break;
+
+		td->lut[i] = i;
+		if ((lut_bottom == MAX_DFLL_VOLTAGES) && (reg_volt >= v_min))
+			lut_bottom = i;
+	}
+
+	/* determine voltage boundaries */
+	td->lut_size = i;
+	if ((lut_bottom == MAX_DFLL_VOLTAGES) ||
+	    (lut_bottom + 1 >= td->lut_size)) {
+		dev_err(td->dev, "no voltage above DFLL minimum %d mV\n",
+			td->soc->cvb->min_millivolts);
+		return -EINVAL;
+	}
+	td->lut_bottom = lut_bottom;
+
+	/* determine rate boundaries */
+	rate = get_dvco_rate_below(td, td->lut_bottom);
+	if (!rate) {
+		dev_err(td->dev, "no opp below DFLL minimum voltage %d mV\n",
+			td->soc->cvb->min_millivolts);
+		return -EINVAL;
+	}
+	td->dvco_rate_min = rate;
+
+	return 0;
+}
+
 /**
  * dfll_build_i2c_lut - build the I2C voltage register lookup table
  * @td: DFLL instance
@@ -1427,31 +1676,24 @@ static int find_vdd_map_entry_min(struct tegra_dfll *td, int uV)
  *
  * On success, fills in td->i2c_lut and returns 0, or -err on failure.
  */
-static int dfll_build_i2c_lut(struct tegra_dfll *td)
+static int dfll_build_i2c_lut(struct tegra_dfll *td, int v_max)
 {
+	unsigned long rate;
 	int ret = -EINVAL;
-	int j, v, v_max, v_opp;
+	int j, v, v_opp;
 	int selector;
-	unsigned long rate;
-	struct dev_pm_opp *opp;
 	int lut;
 
-	rate = ULONG_MAX;
-	opp = dev_pm_opp_find_freq_floor(td->soc->dev, &rate);
-	if (IS_ERR(opp)) {
-		dev_err(td->dev, "couldn't get vmax opp, empty opp table?\n");
-		goto out;
-	}
-	v_max = dev_pm_opp_get_voltage(opp);
-	dev_pm_opp_put(opp);
-
 	v = td->soc->cvb->min_millivolts * 1000;
 	lut = find_vdd_map_entry_exact(td, v);
 	if (lut < 0)
 		goto out;
-	td->i2c_lut[0] = lut;
+	td->lut[0] = lut;
+	td->lut_bottom = 0;
 
 	for (j = 1, rate = 0; ; rate++) {
+		struct dev_pm_opp *opp;
+
 		opp = dev_pm_opp_find_freq_ceil(td->soc->dev, &rate);
 		if (IS_ERR(opp))
 			break;
@@ -1470,32 +1712,61 @@ static int dfll_build_i2c_lut(struct tegra_dfll *td)
 			selector = find_vdd_map_entry_min(td, v);
 			if (selector < 0)
 				goto out;
-			if (selector != td->i2c_lut[j - 1])
-				td->i2c_lut[j++] = selector;
+			if (selector != td->lut[j - 1])
+				td->lut[j++] = selector;
 		}
 
 		v = (j == MAX_DFLL_VOLTAGES - 1) ? v_max : v_opp;
 		selector = find_vdd_map_entry_exact(td, v);
 		if (selector < 0)
 			goto out;
-		if (selector != td->i2c_lut[j - 1])
-			td->i2c_lut[j++] = selector;
+		if (selector != td->lut[j - 1])
+			td->lut[j++] = selector;
 
 		if (v >= v_max)
 			break;
 	}
-	td->i2c_lut_size = j;
+	td->lut_size = j;
 
 	if (!td->dvco_rate_min)
 		dev_err(td->dev, "no opp above DFLL minimum voltage %d mV\n",
 			td->soc->cvb->min_millivolts);
-	else
+	else {
 		ret = 0;
+		for (j = 0; j < td->lut_size; j++)
+			td->lut_uv[j] =
+				regulator_list_voltage(td->vdd_reg,
+						       td->lut[j]);
+	}
 
 out:
 	return ret;
 }
 
+static int dfll_build_lut(struct tegra_dfll *td)
+{
+	unsigned long rate;
+	struct dev_pm_opp *opp;
+	int v_max;
+
+	rcu_read_lock();
+
+	rate = ULONG_MAX;
+	opp = dev_pm_opp_find_freq_floor(td->soc->dev, &rate);
+	if (IS_ERR(opp)) {
+		dev_err(td->dev, "couldn't get vmax opp, empty opp table?\n");
+		return -EINVAL;
+	}
+	v_max = dev_pm_opp_get_voltage(opp);
+
+	rcu_read_unlock();
+
+	if (td->pmu_if == TEGRA_DFLL_PMU_PWM)
+		return dfll_build_lut_pwm(td, v_max);
+	else
+		return dfll_build_i2c_lut(td, v_max);
+}
+
 /**
  * read_dt_param - helper function for reading required parameters from the DT
  * @td: DFLL instance
@@ -1554,12 +1825,55 @@ static int dfll_fetch_i2c_params(struct tegra_dfll *td)
 	}
 	td->i2c_reg = vsel_reg;
 
-	ret = dfll_build_i2c_lut(td);
-	if (ret) {
-		dev_err(td->dev, "couldn't build I2C LUT\n");
+	return 0;
+}
+
+static int dfll_fetch_pwm_params(struct tegra_dfll *td)
+{
+	int ret, i;
+	u32 pwm_period;
+
+	if (!td->soc->alignment.step_uv || !td->soc->alignment.offset_uv) {
+		dev_err(td->dev, "Missing step or alignment info for PWM regulator");
+		return -EINVAL;
+	}
+	for (i = 0; i < MAX_DFLL_VOLTAGES; i++)
+		td->lut_uv[i] = td->soc->alignment.offset_uv +
+				i * td->soc->alignment.step_uv;
+
+	ret = read_dt_param(td, "nvidia,init-uv", &td->reg_init_uV);
+	if (!ret) {
+		dev_err(td->dev, "couldn't get initialized voltage\n");
 		return ret;
 	}
 
+	ret = read_dt_param(td, "nvidia,pwm-period", &pwm_period);
+	if (!ret) {
+		dev_err(td->dev, "couldn't get PWM period\n");
+		return ret;
+	}
+	td->pwm_rate = (NSEC_PER_SEC / pwm_period) * (MAX_DFLL_VOLTAGES - 1);
+
+	td->pwm_pin = devm_pinctrl_get(td->dev);
+	if (IS_ERR(td->pwm_pin)) {
+		dev_err(td->dev, "DT: missing pinctrl device\n");
+		return PTR_ERR(td->pwm_pin);
+	}
+
+	td->pwm_enable_state = pinctrl_lookup_state(td->pwm_pin,
+						    "dvfs_pwm_enable");
+	if (IS_ERR(td->pwm_enable_state)) {
+		dev_err(td->dev, "DT: missing pwm enabled state\n");
+		return PTR_ERR(td->pwm_enable_state);
+	}
+
+	td->pwm_disable_state = pinctrl_lookup_state(td->pwm_pin,
+						     "dvfs_pwm_disable");
+	if (IS_ERR(td->pwm_disable_state)) {
+		dev_err(td->dev, "DT: missing pwm disabled state\n");
+		return PTR_ERR(td->pwm_disable_state);
+	}
+
 	return 0;
 }
 
@@ -1625,12 +1939,6 @@ int tegra_dfll_register(struct platform_device *pdev,
 
 	td->soc = soc;
 
-	td->vdd_reg = devm_regulator_get(td->dev, "vdd-cpu");
-	if (IS_ERR(td->vdd_reg)) {
-		dev_err(td->dev, "couldn't get vdd_cpu regulator\n");
-		return PTR_ERR(td->vdd_reg);
-	}
-
 	td->dvco_rst = devm_reset_control_get(td->dev, "dvco");
 	if (IS_ERR(td->dvco_rst)) {
 		dev_err(td->dev, "couldn't get dvco reset\n");
@@ -1643,10 +1951,27 @@ int tegra_dfll_register(struct platform_device *pdev,
 		return ret;
 	}
 
-	ret = dfll_fetch_i2c_params(td);
+	if (of_property_read_bool(td->dev->of_node, "nvidia,pwm-to-pmic")) {
+		td->pmu_if = TEGRA_DFLL_PMU_PWM;
+		ret = dfll_fetch_pwm_params(td);
+	} else  {
+		td->vdd_reg = devm_regulator_get(td->dev, "vdd-cpu");
+		if (IS_ERR(td->vdd_reg)) {
+			dev_err(td->dev, "couldn't get vdd_cpu regulator\n");
+			return PTR_ERR(td->vdd_reg);
+		}
+		td->pmu_if = TEGRA_DFLL_PMU_I2C;
+		ret = dfll_fetch_i2c_params(td);
+	}
 	if (ret)
 		return ret;
 
+	ret = dfll_build_lut(td);
+	if (ret) {
+		dev_err(td->dev, "couldn't build LUT\n");
+		return ret;
+	}
+
 	mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
 	if (!mem) {
 		dev_err(td->dev, "no control register resource\n");
diff --git a/drivers/clk/tegra/clk-dfll.h b/drivers/clk/tegra/clk-dfll.h
index 83352c8..228f54a 100644
--- a/drivers/clk/tegra/clk-dfll.h
+++ b/drivers/clk/tegra/clk-dfll.h
@@ -21,6 +21,7 @@
 #include <linux/platform_device.h>
 #include <linux/reset.h>
 #include <linux/types.h>
+#include "cvb.h"
 
 /**
  * struct tegra_dfll_soc_data - SoC-specific hooks/integration for the DFLL driver
@@ -35,6 +36,12 @@ struct tegra_dfll_soc_data {
 	struct device *dev;
 	unsigned long max_freq;
 	const struct cvb_table *cvb;
+	struct rail_alignment alignment;
+	unsigned int min_millivolts;
+	unsigned int tune_high_min_millivolts;
+	u32 tune0_low;
+	u32 tune0_high;
+	u32 tune1;
 
 	void (*init_clock_trimmers)(void);
 	void (*set_clock_trimmers_high)(void);
-- 
1.9.1


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

* [PATCH v2 5/6] clk: tegra: build clk-dfll.c for Tegra124 and Tegra210
  2018-01-24 12:45 ` Peter De Schrijver
@ 2018-01-24 12:45     ` Peter De Schrijver
  -1 siblings, 0 replies; 56+ messages in thread
From: Peter De Schrijver @ 2018-01-24 12:45 UTC (permalink / raw)
  To: linux-tegra-u79uwXL29TY76Z2rM5mHXA,
	linux-clk-u79uwXL29TY76Z2rM5mHXA,
	mturquette-rdvid1DuHRBWk0Htik3J/w, sboyd-sgV2jX0FEOL9JmXXK+q4OQ
  Cc: Peter De Schrijver

Tegra210 has a DFLL as well and can share the majority of the code with
the Tegra124 implementation. So build the same code for both platforms.

Signed-off-by: Peter De Schrijver <pdeschrijver-DDmLM1+adcrQT0dZR+AlfA@public.gmane.org>
---
 drivers/clk/tegra/Kconfig  | 5 +++++
 drivers/clk/tegra/Makefile | 2 +-
 2 files changed, 6 insertions(+), 1 deletion(-)

diff --git a/drivers/clk/tegra/Kconfig b/drivers/clk/tegra/Kconfig
index 7ddacae..57902ab 100644
--- a/drivers/clk/tegra/Kconfig
+++ b/drivers/clk/tegra/Kconfig
@@ -5,3 +5,8 @@ config TEGRA_CLK_EMC
 config CLK_TEGRA_BPMP
 	def_bool y
 	depends on TEGRA_BPMP
+
+config TEGRA_CLK_DFLL
+	depends on (ARCH_TEGRA_124_SOC || ARCH_TEGRA_210_SOC)
+	select PM_OPP
+	def_bool y
diff --git a/drivers/clk/tegra/Makefile b/drivers/clk/tegra/Makefile
index b716923..fc7f4b4 100644
--- a/drivers/clk/tegra/Makefile
+++ b/drivers/clk/tegra/Makefile
@@ -19,7 +19,7 @@ obj-$(CONFIG_ARCH_TEGRA_2x_SOC)         += clk-tegra20.o
 obj-$(CONFIG_ARCH_TEGRA_3x_SOC)         += clk-tegra30.o
 obj-$(CONFIG_ARCH_TEGRA_114_SOC)	+= clk-tegra114.o
 obj-$(CONFIG_ARCH_TEGRA_124_SOC)	+= clk-tegra124.o
-obj-$(CONFIG_ARCH_TEGRA_124_SOC)	+= clk-tegra124-dfll-fcpu.o
+obj-$(CONFIG_TEGRA_CLK_DFLL)		+= clk-tegra124-dfll-fcpu.o
 obj-$(CONFIG_ARCH_TEGRA_132_SOC)	+= clk-tegra124.o
 obj-y					+= cvb.o
 obj-$(CONFIG_ARCH_TEGRA_210_SOC)	+= clk-tegra210.o
-- 
1.9.1

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

* [PATCH v2 5/6] clk: tegra: build clk-dfll.c for Tegra124 and Tegra210
@ 2018-01-24 12:45     ` Peter De Schrijver
  0 siblings, 0 replies; 56+ messages in thread
From: Peter De Schrijver @ 2018-01-24 12:45 UTC (permalink / raw)
  To: linux-tegra, linux-clk, mturquette, sboyd; +Cc: Peter De Schrijver

Tegra210 has a DFLL as well and can share the majority of the code with
the Tegra124 implementation. So build the same code for both platforms.

Signed-off-by: Peter De Schrijver <pdeschrijver@nvidia.com>
---
 drivers/clk/tegra/Kconfig  | 5 +++++
 drivers/clk/tegra/Makefile | 2 +-
 2 files changed, 6 insertions(+), 1 deletion(-)

diff --git a/drivers/clk/tegra/Kconfig b/drivers/clk/tegra/Kconfig
index 7ddacae..57902ab 100644
--- a/drivers/clk/tegra/Kconfig
+++ b/drivers/clk/tegra/Kconfig
@@ -5,3 +5,8 @@ config TEGRA_CLK_EMC
 config CLK_TEGRA_BPMP
 	def_bool y
 	depends on TEGRA_BPMP
+
+config TEGRA_CLK_DFLL
+	depends on (ARCH_TEGRA_124_SOC || ARCH_TEGRA_210_SOC)
+	select PM_OPP
+	def_bool y
diff --git a/drivers/clk/tegra/Makefile b/drivers/clk/tegra/Makefile
index b716923..fc7f4b4 100644
--- a/drivers/clk/tegra/Makefile
+++ b/drivers/clk/tegra/Makefile
@@ -19,7 +19,7 @@ obj-$(CONFIG_ARCH_TEGRA_2x_SOC)         += clk-tegra20.o
 obj-$(CONFIG_ARCH_TEGRA_3x_SOC)         += clk-tegra30.o
 obj-$(CONFIG_ARCH_TEGRA_114_SOC)	+= clk-tegra114.o
 obj-$(CONFIG_ARCH_TEGRA_124_SOC)	+= clk-tegra124.o
-obj-$(CONFIG_ARCH_TEGRA_124_SOC)	+= clk-tegra124-dfll-fcpu.o
+obj-$(CONFIG_TEGRA_CLK_DFLL)		+= clk-tegra124-dfll-fcpu.o
 obj-$(CONFIG_ARCH_TEGRA_132_SOC)	+= clk-tegra124.o
 obj-y					+= cvb.o
 obj-$(CONFIG_ARCH_TEGRA_210_SOC)	+= clk-tegra210.o
-- 
1.9.1

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

* [PATCH v2 6/6] cpufreq: tegra124-cpufreq: extend to support Tegra210
  2018-01-24 12:45 ` Peter De Schrijver
@ 2018-01-24 12:45     ` Peter De Schrijver
  -1 siblings, 0 replies; 56+ messages in thread
From: Peter De Schrijver @ 2018-01-24 12:45 UTC (permalink / raw)
  To: linux-tegra-u79uwXL29TY76Z2rM5mHXA,
	linux-clk-u79uwXL29TY76Z2rM5mHXA,
	mturquette-rdvid1DuHRBWk0Htik3J/w, sboyd-sgV2jX0FEOL9JmXXK+q4OQ
  Cc: Peter De Schrijver

Tegra210 has a very similar CPU clocking scheme than Tegra124. So add
support in this driver. Also allow for the case where the CPU voltage is
controlled directly by the DFLL rather than by a separate regulator object.

Signed-off-by: Peter De Schrijver <pdeschrijver-DDmLM1+adcrQT0dZR+AlfA@public.gmane.org>
---
 drivers/cpufreq/tegra124-cpufreq.c | 13 ++++++-------
 1 file changed, 6 insertions(+), 7 deletions(-)

diff --git a/drivers/cpufreq/tegra124-cpufreq.c b/drivers/cpufreq/tegra124-cpufreq.c
index 4353025..d631dc4 100644
--- a/drivers/cpufreq/tegra124-cpufreq.c
+++ b/drivers/cpufreq/tegra124-cpufreq.c
@@ -64,7 +64,8 @@ static void tegra124_cpu_switch_to_pllx(struct tegra124_cpufreq_priv *priv)
 {
 	clk_set_parent(priv->cpu_clk, priv->pllp_clk);
 	clk_disable_unprepare(priv->dfll_clk);
-	regulator_sync_voltage(priv->vdd_cpu_reg);
+	if (priv->vdd_cpu_reg)
+		regulator_sync_voltage(priv->vdd_cpu_reg);
 	clk_set_parent(priv->cpu_clk, priv->pllx_clk);
 }
 
@@ -89,10 +90,8 @@ static int tegra124_cpufreq_probe(struct platform_device *pdev)
 		return -ENODEV;
 
 	priv->vdd_cpu_reg = regulator_get(cpu_dev, "vdd-cpu");
-	if (IS_ERR(priv->vdd_cpu_reg)) {
-		ret = PTR_ERR(priv->vdd_cpu_reg);
-		goto out_put_np;
-	}
+	if (IS_ERR(priv->vdd_cpu_reg))
+		priv->vdd_cpu_reg = NULL;
 
 	priv->cpu_clk = of_clk_get_by_name(np, "cpu_g");
 	if (IS_ERR(priv->cpu_clk)) {
@@ -148,7 +147,6 @@ static int tegra124_cpufreq_probe(struct platform_device *pdev)
 	clk_put(priv->cpu_clk);
 out_put_vdd_cpu_reg:
 	regulator_put(priv->vdd_cpu_reg);
-out_put_np:
 	of_node_put(np);
 
 	return ret;
@@ -181,7 +179,8 @@ static int __init tegra_cpufreq_init(void)
 	int ret;
 	struct platform_device *pdev;
 
-	if (!of_machine_is_compatible("nvidia,tegra124"))
+	if (!(of_machine_is_compatible("nvidia,tegra124")
+		|| of_machine_is_compatible("nvidia,tegra210")))
 		return -ENODEV;
 
 	/*
-- 
1.9.1

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

* [PATCH v2 6/6] cpufreq: tegra124-cpufreq: extend to support Tegra210
@ 2018-01-24 12:45     ` Peter De Schrijver
  0 siblings, 0 replies; 56+ messages in thread
From: Peter De Schrijver @ 2018-01-24 12:45 UTC (permalink / raw)
  To: linux-tegra, linux-clk, mturquette, sboyd; +Cc: Peter De Schrijver

Tegra210 has a very similar CPU clocking scheme than Tegra124. So add
support in this driver. Also allow for the case where the CPU voltage is
controlled directly by the DFLL rather than by a separate regulator object.

Signed-off-by: Peter De Schrijver <pdeschrijver@nvidia.com>
---
 drivers/cpufreq/tegra124-cpufreq.c | 13 ++++++-------
 1 file changed, 6 insertions(+), 7 deletions(-)

diff --git a/drivers/cpufreq/tegra124-cpufreq.c b/drivers/cpufreq/tegra124-cpufreq.c
index 4353025..d631dc4 100644
--- a/drivers/cpufreq/tegra124-cpufreq.c
+++ b/drivers/cpufreq/tegra124-cpufreq.c
@@ -64,7 +64,8 @@ static void tegra124_cpu_switch_to_pllx(struct tegra124_cpufreq_priv *priv)
 {
 	clk_set_parent(priv->cpu_clk, priv->pllp_clk);
 	clk_disable_unprepare(priv->dfll_clk);
-	regulator_sync_voltage(priv->vdd_cpu_reg);
+	if (priv->vdd_cpu_reg)
+		regulator_sync_voltage(priv->vdd_cpu_reg);
 	clk_set_parent(priv->cpu_clk, priv->pllx_clk);
 }
 
@@ -89,10 +90,8 @@ static int tegra124_cpufreq_probe(struct platform_device *pdev)
 		return -ENODEV;
 
 	priv->vdd_cpu_reg = regulator_get(cpu_dev, "vdd-cpu");
-	if (IS_ERR(priv->vdd_cpu_reg)) {
-		ret = PTR_ERR(priv->vdd_cpu_reg);
-		goto out_put_np;
-	}
+	if (IS_ERR(priv->vdd_cpu_reg))
+		priv->vdd_cpu_reg = NULL;
 
 	priv->cpu_clk = of_clk_get_by_name(np, "cpu_g");
 	if (IS_ERR(priv->cpu_clk)) {
@@ -148,7 +147,6 @@ static int tegra124_cpufreq_probe(struct platform_device *pdev)
 	clk_put(priv->cpu_clk);
 out_put_vdd_cpu_reg:
 	regulator_put(priv->vdd_cpu_reg);
-out_put_np:
 	of_node_put(np);
 
 	return ret;
@@ -181,7 +179,8 @@ static int __init tegra_cpufreq_init(void)
 	int ret;
 	struct platform_device *pdev;
 
-	if (!of_machine_is_compatible("nvidia,tegra124"))
+	if (!(of_machine_is_compatible("nvidia,tegra124")
+		|| of_machine_is_compatible("nvidia,tegra210")))
 		return -ENODEV;
 
 	/*
-- 
1.9.1

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

* Re: [PATCH v2 1/6] clk: tegra: dfll registration for multiple SoCs
  2018-01-24 12:45     ` Peter De Schrijver
@ 2018-01-31 10:13       ` Jon Hunter
  -1 siblings, 0 replies; 56+ messages in thread
From: Jon Hunter @ 2018-01-31 10:13 UTC (permalink / raw)
  To: Peter De Schrijver, linux-tegra, linux-clk, mturquette, sboyd


On 24/01/18 12:45, Peter De Schrijver wrote:
> In a future patch, support for the DFLL in Tegra210 will be introduced.
> This requires support for more than 1 set of CVB and CPU max frequency
> tables.
> 
> Signed-off-by: Peter De Schrijver <pdeschrijver@nvidia.com>
> ---
>  drivers/clk/tegra/clk-tegra124-dfll-fcpu.c | 43 +++++++++++++++++++++++-------
>  1 file changed, 33 insertions(+), 10 deletions(-)
> 
> diff --git a/drivers/clk/tegra/clk-tegra124-dfll-fcpu.c b/drivers/clk/tegra/clk-tegra124-dfll-fcpu.c
> index 269d359..440eb8d 100644
> --- a/drivers/clk/tegra/clk-tegra124-dfll-fcpu.c
> +++ b/drivers/clk/tegra/clk-tegra124-dfll-fcpu.c
> @@ -21,6 +21,7 @@
>  #include <linux/err.h>
>  #include <linux/kernel.h>
>  #include <linux/init.h>
> +#include <linux/of_device.h>
>  #include <linux/platform_device.h>
>  #include <soc/tegra/fuse.h>
>  
> @@ -28,8 +29,15 @@
>  #include "clk-dfll.h"
>  #include "cvb.h"
>  
> +struct dfll_fcpu_data {
> +	const unsigned long *cpu_max_freq_table;
> +	unsigned int cpu_max_freq_table_size;
> +	const struct cvb_table *cpu_cvb_tables;
> +	unsigned int cpu_cvb_tables_size;
> +};
> +
>  /* Maximum CPU frequency, indexed by CPU speedo id */
> -static const unsigned long cpu_max_freq_table[] = {
> +static const unsigned long tegra124_cpu_max_freq_table[] = {
>  	[0] = 2014500000UL,
>  	[1] = 2320500000UL,
>  	[2] = 2116500000UL,
> @@ -82,16 +90,36 @@
>  	},
>  };
>  
> +static const struct dfll_fcpu_data tegra124_dfll_fcpu_data = {
> +	.cpu_max_freq_table = tegra124_cpu_max_freq_table,
> +	.cpu_max_freq_table_size = ARRAY_SIZE(tegra124_cpu_max_freq_table),
> +	.cpu_cvb_tables = tegra124_cpu_cvb_tables,
> +	.cpu_cvb_tables_size = ARRAY_SIZE(tegra124_cpu_cvb_tables)
> +};
> +
> +static const struct of_device_id tegra124_dfll_fcpu_of_match[] = {
> +	{
> +		.compatible = "nvidia,tegra124-dfll",
> +		.data = &tegra124_dfll_fcpu_data,
> +	},
> +	{ },
> +};
> +
>  static int tegra124_dfll_fcpu_probe(struct platform_device *pdev)
>  {
>  	int process_id, speedo_id, speedo_value, err;
>  	struct tegra_dfll_soc_data *soc;
> +	const struct of_device_id *of_id;
> +	const struct dfll_fcpu_data *fcpu_data;
> +
> +	of_id = of_match_device(tegra124_dfll_fcpu_of_match, &pdev->dev);
> +	fcpu_data = of_id->data;
>  
>  	process_id = tegra_sku_info.cpu_process_id;
>  	speedo_id = tegra_sku_info.cpu_speedo_id;
>  	speedo_value = tegra_sku_info.cpu_speedo_value;
>  
> -	if (speedo_id >= ARRAY_SIZE(cpu_max_freq_table)) {
> +	if (speedo_id >= fcpu_data->cpu_max_freq_table_size) {
>  		dev_err(&pdev->dev, "unknown max CPU freq for speedo_id=%d\n",
>  			speedo_id);
>  		return -ENODEV;
> @@ -107,10 +135,10 @@ static int tegra124_dfll_fcpu_probe(struct platform_device *pdev)
>  		return -ENODEV;
>  	}
>  
> -	soc->max_freq = cpu_max_freq_table[speedo_id];
> +	soc->max_freq = fcpu_data->cpu_max_freq_table[speedo_id];
>  
> -	soc->cvb = tegra_cvb_add_opp_table(soc->dev, tegra124_cpu_cvb_tables,
> -					   ARRAY_SIZE(tegra124_cpu_cvb_tables),
> +	soc->cvb = tegra_cvb_add_opp_table(soc->dev, fcpu_data->cpu_cvb_tables,
> +					   fcpu_data->cpu_cvb_tables_size,
>  					   process_id, speedo_id, speedo_value,
>  					   soc->max_freq);
>  	if (IS_ERR(soc->cvb)) {
> @@ -142,11 +170,6 @@ static int tegra124_dfll_fcpu_remove(struct platform_device *pdev)
>  	return 0;
>  }
>  
> -static const struct of_device_id tegra124_dfll_fcpu_of_match[] = {
> -	{ .compatible = "nvidia,tegra124-dfll", },
> -	{ },
> -};
> -
>  static const struct dev_pm_ops tegra124_dfll_pm_ops = {
>  	SET_RUNTIME_PM_OPS(tegra_dfll_runtime_suspend,
>  			   tegra_dfll_runtime_resume, NULL)
> 

Thanks!

Reviewed-by: Jon Hunter <jonathanh@nvidia.com>

Cheers
Jon

-- 
nvpublic

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

* Re: [PATCH v2 1/6] clk: tegra: dfll registration for multiple SoCs
@ 2018-01-31 10:13       ` Jon Hunter
  0 siblings, 0 replies; 56+ messages in thread
From: Jon Hunter @ 2018-01-31 10:13 UTC (permalink / raw)
  To: Peter De Schrijver, linux-tegra, linux-clk, mturquette, sboyd


On 24/01/18 12:45, Peter De Schrijver wrote:
> In a future patch, support for the DFLL in Tegra210 will be introduced.
> This requires support for more than 1 set of CVB and CPU max frequency
> tables.
> 
> Signed-off-by: Peter De Schrijver <pdeschrijver@nvidia.com>
> ---
>  drivers/clk/tegra/clk-tegra124-dfll-fcpu.c | 43 +++++++++++++++++++++++-------
>  1 file changed, 33 insertions(+), 10 deletions(-)
> 
> diff --git a/drivers/clk/tegra/clk-tegra124-dfll-fcpu.c b/drivers/clk/tegra/clk-tegra124-dfll-fcpu.c
> index 269d359..440eb8d 100644
> --- a/drivers/clk/tegra/clk-tegra124-dfll-fcpu.c
> +++ b/drivers/clk/tegra/clk-tegra124-dfll-fcpu.c
> @@ -21,6 +21,7 @@
>  #include <linux/err.h>
>  #include <linux/kernel.h>
>  #include <linux/init.h>
> +#include <linux/of_device.h>
>  #include <linux/platform_device.h>
>  #include <soc/tegra/fuse.h>
>  
> @@ -28,8 +29,15 @@
>  #include "clk-dfll.h"
>  #include "cvb.h"
>  
> +struct dfll_fcpu_data {
> +	const unsigned long *cpu_max_freq_table;
> +	unsigned int cpu_max_freq_table_size;
> +	const struct cvb_table *cpu_cvb_tables;
> +	unsigned int cpu_cvb_tables_size;
> +};
> +
>  /* Maximum CPU frequency, indexed by CPU speedo id */
> -static const unsigned long cpu_max_freq_table[] = {
> +static const unsigned long tegra124_cpu_max_freq_table[] = {
>  	[0] = 2014500000UL,
>  	[1] = 2320500000UL,
>  	[2] = 2116500000UL,
> @@ -82,16 +90,36 @@
>  	},
>  };
>  
> +static const struct dfll_fcpu_data tegra124_dfll_fcpu_data = {
> +	.cpu_max_freq_table = tegra124_cpu_max_freq_table,
> +	.cpu_max_freq_table_size = ARRAY_SIZE(tegra124_cpu_max_freq_table),
> +	.cpu_cvb_tables = tegra124_cpu_cvb_tables,
> +	.cpu_cvb_tables_size = ARRAY_SIZE(tegra124_cpu_cvb_tables)
> +};
> +
> +static const struct of_device_id tegra124_dfll_fcpu_of_match[] = {
> +	{
> +		.compatible = "nvidia,tegra124-dfll",
> +		.data = &tegra124_dfll_fcpu_data,
> +	},
> +	{ },
> +};
> +
>  static int tegra124_dfll_fcpu_probe(struct platform_device *pdev)
>  {
>  	int process_id, speedo_id, speedo_value, err;
>  	struct tegra_dfll_soc_data *soc;
> +	const struct of_device_id *of_id;
> +	const struct dfll_fcpu_data *fcpu_data;
> +
> +	of_id = of_match_device(tegra124_dfll_fcpu_of_match, &pdev->dev);
> +	fcpu_data = of_id->data;
>  
>  	process_id = tegra_sku_info.cpu_process_id;
>  	speedo_id = tegra_sku_info.cpu_speedo_id;
>  	speedo_value = tegra_sku_info.cpu_speedo_value;
>  
> -	if (speedo_id >= ARRAY_SIZE(cpu_max_freq_table)) {
> +	if (speedo_id >= fcpu_data->cpu_max_freq_table_size) {
>  		dev_err(&pdev->dev, "unknown max CPU freq for speedo_id=%d\n",
>  			speedo_id);
>  		return -ENODEV;
> @@ -107,10 +135,10 @@ static int tegra124_dfll_fcpu_probe(struct platform_device *pdev)
>  		return -ENODEV;
>  	}
>  
> -	soc->max_freq = cpu_max_freq_table[speedo_id];
> +	soc->max_freq = fcpu_data->cpu_max_freq_table[speedo_id];
>  
> -	soc->cvb = tegra_cvb_add_opp_table(soc->dev, tegra124_cpu_cvb_tables,
> -					   ARRAY_SIZE(tegra124_cpu_cvb_tables),
> +	soc->cvb = tegra_cvb_add_opp_table(soc->dev, fcpu_data->cpu_cvb_tables,
> +					   fcpu_data->cpu_cvb_tables_size,
>  					   process_id, speedo_id, speedo_value,
>  					   soc->max_freq);
>  	if (IS_ERR(soc->cvb)) {
> @@ -142,11 +170,6 @@ static int tegra124_dfll_fcpu_remove(struct platform_device *pdev)
>  	return 0;
>  }
>  
> -static const struct of_device_id tegra124_dfll_fcpu_of_match[] = {
> -	{ .compatible = "nvidia,tegra124-dfll", },
> -	{ },
> -};
> -
>  static const struct dev_pm_ops tegra124_dfll_pm_ops = {
>  	SET_RUNTIME_PM_OPS(tegra_dfll_runtime_suspend,
>  			   tegra_dfll_runtime_resume, NULL)
> 

Thanks!

Reviewed-by: Jon Hunter <jonathanh@nvidia.com>

Cheers
Jon

-- 
nvpublic

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

* Re: [PATCH v2 2/6] clk: tegra: DT align parameter for CVB calculation
  2018-01-24 12:45   ` Peter De Schrijver
@ 2018-01-31 10:43     ` Jon Hunter
  -1 siblings, 0 replies; 56+ messages in thread
From: Jon Hunter @ 2018-01-31 10:43 UTC (permalink / raw)
  To: Peter De Schrijver, linux-tegra, linux-clk, mturquette, sboyd


On 24/01/18 12:45, Peter De Schrijver wrote:
> When using a PWM controlled regulator, the voltage step and offset are
> determined by the regulator IC in use. This is specified in DT rather
> than fixed in the CVB table. Hence pass this information to the CVB table
> calculation function. For backwards compatibility the table values are used
> if the corresponding parameter is 0.
> 
> Signed-off-by: Peter De Schrijver <pdeschrijver@nvidia.com>
> ---
>  drivers/clk/tegra/clk-tegra124-dfll-fcpu.c | 23 +++++++++++++++++++++--
>  drivers/clk/tegra/cvb.c                    | 18 ++++++++++++++----
>  drivers/clk/tegra/cvb.h                    |  5 +++--
>  3 files changed, 38 insertions(+), 8 deletions(-)
> 
> diff --git a/drivers/clk/tegra/clk-tegra124-dfll-fcpu.c b/drivers/clk/tegra/clk-tegra124-dfll-fcpu.c
> index 440eb8d..6205ce1 100644
> --- a/drivers/clk/tegra/clk-tegra124-dfll-fcpu.c
> +++ b/drivers/clk/tegra/clk-tegra124-dfll-fcpu.c
> @@ -111,6 +111,7 @@ static int tegra124_dfll_fcpu_probe(struct platform_device *pdev)
>  	struct tegra_dfll_soc_data *soc;
>  	const struct of_device_id *of_id;
>  	const struct dfll_fcpu_data *fcpu_data;
> +	struct rail_alignment align;
>  
>  	of_id = of_match_device(tegra124_dfll_fcpu_of_match, &pdev->dev);
>  	fcpu_data = of_id->data;
> @@ -135,12 +136,30 @@ static int tegra124_dfll_fcpu_probe(struct platform_device *pdev)
>  		return -ENODEV;
>  	}
>  
> +	err = of_property_read_u32(pdev->dev.of_node, "nvidia,align-offset-uv",
> +					&align.offset_uv);
> +	if (err < 0) {
> +		dev_err(&pdev->dev,
> +			"offset uv not found, default to table value\n");
> +		align.offset_uv = 0;
> +	}
> +
> +	err = of_property_read_u32(pdev->dev.of_node, "nvidia,align-step-uv",
> +					&align.step_uv);
> +	if (err < 0) {
> +		dev_err(&pdev->dev,
> +			"step uv not found, default to table value\n");
> +		align.step_uv = 0;
> +	}
> +

I am a bit confused by this ...

1. Isn't this going to break Tegra124 DFLL support?
2. These DT properties are not defined anywhere (so the binding doc
   needs updating).
3. I don't see any patches in this series that populate these properties
   in DT.

So I am not sure how this works?!?

>  	soc->max_freq = fcpu_data->cpu_max_freq_table[speedo_id];
>  
>  	soc->cvb = tegra_cvb_add_opp_table(soc->dev, fcpu_data->cpu_cvb_tables,
>  					   fcpu_data->cpu_cvb_tables_size,
> -					   process_id, speedo_id, speedo_value,
> -					   soc->max_freq);
> +					   &align, process_id, speedo_id,
> +					   speedo_value, soc->max_freq);
> +	soc->alignment = align;
> +

This is not defined yet and so breaks compile.

drivers/clk/tegra/clk-tegra124-dfll-fcpu.c: In function
‘tegra124_dfll_fcpu_probe’:
drivers/clk/tegra/clk-tegra124-dfll-fcpu.c:161:5: error: ‘struct
tegra_dfll_soc_data’ has no member named ‘alignment’
  soc->alignment = align;
     ^

What about Tegra124? This is set to NULL? Can we not set this before
calling tegra_cvb_add_opp_table() and then just pass soc->alignment for
both Tegra124 and Tegra210? Then you can avoid the if-statements in
build_opp_table.

>  	if (IS_ERR(soc->cvb)) {
>  		dev_err(&pdev->dev, "couldn't add OPP table: %ld\n",
>  			PTR_ERR(soc->cvb));
> diff --git a/drivers/clk/tegra/cvb.c b/drivers/clk/tegra/cvb.c
> index da9e8e7..561012a 100644
> --- a/drivers/clk/tegra/cvb.c
> +++ b/drivers/clk/tegra/cvb.c
> @@ -62,11 +62,19 @@ static int round_voltage(int mv, const struct rail_alignment *align, int up)
>  }
>  
>  static int build_opp_table(struct device *dev, const struct cvb_table *table,
> +			   struct rail_alignment *align,
>  			   int speedo_value, unsigned long max_freq)
>  {
> -	const struct rail_alignment *align = &table->alignment;
>  	int i, ret, dfll_mv, min_mv, max_mv;
>  
> +	if (!align->step_uv)
> +		align->step_uv = table->alignment.step_uv;
> +	if (!align->step_uv)
> +		return -EINVAL;
> +
> +	if (!align->offset_uv)
> +		align->offset_uv = table->alignment.offset_uv;
> +
>  	min_mv = round_voltage(table->min_millivolts, align, UP);
>  	max_mv = round_voltage(table->max_millivolts, align, DOWN);
>  
> @@ -109,8 +117,9 @@ static int build_opp_table(struct device *dev, const struct cvb_table *table,
>   */
>  const struct cvb_table *
>  tegra_cvb_add_opp_table(struct device *dev, const struct cvb_table *tables,
> -			size_t count, int process_id, int speedo_id,
> -			int speedo_value, unsigned long max_freq)
> +			size_t count, struct rail_alignment *align,
> +			int process_id, int speedo_id, int speedo_value,
> +			unsigned long max_freq)
>  {
>  	size_t i;
>  	int ret;
> @@ -124,7 +133,8 @@ static int build_opp_table(struct device *dev, const struct cvb_table *table,
>  		if (table->process_id != -1 && table->process_id != process_id)
>  			continue;
>  
> -		ret = build_opp_table(dev, table, speedo_value, max_freq);
> +		ret = build_opp_table(dev, table, align, speedo_value,
> +				      max_freq);
>  		return ret ? ERR_PTR(ret) : table;
>  	}
>  
> diff --git a/drivers/clk/tegra/cvb.h b/drivers/clk/tegra/cvb.h
> index c1f0779..cfa110f 100644
> --- a/drivers/clk/tegra/cvb.h
> +++ b/drivers/clk/tegra/cvb.h
> @@ -59,8 +59,9 @@ struct cvb_table {
>  
>  const struct cvb_table *
>  tegra_cvb_add_opp_table(struct device *dev, const struct cvb_table *cvb_tables,
> -			size_t count, int process_id, int speedo_id,
> -			int speedo_value, unsigned long max_freq);
> +			size_t count, struct rail_alignment *align,
> +			int process_id, int speedo_id, int speedo_value,
> +			unsigned long max_freq);
>  void tegra_cvb_remove_opp_table(struct device *dev,
>  				const struct cvb_table *table,
>  				unsigned long max_freq);
> 

Cheers
Jon

-- 
nvpublic

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

* Re: [PATCH v2 2/6] clk: tegra: DT align parameter for CVB calculation
@ 2018-01-31 10:43     ` Jon Hunter
  0 siblings, 0 replies; 56+ messages in thread
From: Jon Hunter @ 2018-01-31 10:43 UTC (permalink / raw)
  To: Peter De Schrijver, linux-tegra, linux-clk, mturquette, sboyd


On 24/01/18 12:45, Peter De Schrijver wrote:
> When using a PWM controlled regulator, the voltage step and offset are
> determined by the regulator IC in use. This is specified in DT rather
> than fixed in the CVB table. Hence pass this information to the CVB table
> calculation function. For backwards compatibility the table values are us=
ed
> if the corresponding parameter is 0.
>=20
> Signed-off-by: Peter De Schrijver <pdeschrijver@nvidia.com>
> ---
>  drivers/clk/tegra/clk-tegra124-dfll-fcpu.c | 23 +++++++++++++++++++++--
>  drivers/clk/tegra/cvb.c                    | 18 ++++++++++++++----
>  drivers/clk/tegra/cvb.h                    |  5 +++--
>  3 files changed, 38 insertions(+), 8 deletions(-)
>=20
> diff --git a/drivers/clk/tegra/clk-tegra124-dfll-fcpu.c b/drivers/clk/teg=
ra/clk-tegra124-dfll-fcpu.c
> index 440eb8d..6205ce1 100644
> --- a/drivers/clk/tegra/clk-tegra124-dfll-fcpu.c
> +++ b/drivers/clk/tegra/clk-tegra124-dfll-fcpu.c
> @@ -111,6 +111,7 @@ static int tegra124_dfll_fcpu_probe(struct platform_d=
evice *pdev)
>  	struct tegra_dfll_soc_data *soc;
>  	const struct of_device_id *of_id;
>  	const struct dfll_fcpu_data *fcpu_data;
> +	struct rail_alignment align;
> =20
>  	of_id =3D of_match_device(tegra124_dfll_fcpu_of_match, &pdev->dev);
>  	fcpu_data =3D of_id->data;
> @@ -135,12 +136,30 @@ static int tegra124_dfll_fcpu_probe(struct platform=
_device *pdev)
>  		return -ENODEV;
>  	}
> =20
> +	err =3D of_property_read_u32(pdev->dev.of_node, "nvidia,align-offset-uv=
",
> +					&align.offset_uv);
> +	if (err < 0) {
> +		dev_err(&pdev->dev,
> +			"offset uv not found, default to table value\n");
> +		align.offset_uv =3D 0;
> +	}
> +
> +	err =3D of_property_read_u32(pdev->dev.of_node, "nvidia,align-step-uv",
> +					&align.step_uv);
> +	if (err < 0) {
> +		dev_err(&pdev->dev,
> +			"step uv not found, default to table value\n");
> +		align.step_uv =3D 0;
> +	}
> +

I am a bit confused by this ...

1. Isn't this going to break Tegra124 DFLL support?
2. These DT properties are not defined anywhere (so the binding doc
   needs updating).
3. I don't see any patches in this series that populate these properties
   in DT.

So I am not sure how this works?!?

>  	soc->max_freq =3D fcpu_data->cpu_max_freq_table[speedo_id];
> =20
>  	soc->cvb =3D tegra_cvb_add_opp_table(soc->dev, fcpu_data->cpu_cvb_table=
s,
>  					   fcpu_data->cpu_cvb_tables_size,
> -					   process_id, speedo_id, speedo_value,
> -					   soc->max_freq);
> +					   &align, process_id, speedo_id,
> +					   speedo_value, soc->max_freq);
> +	soc->alignment =3D align;
> +

This is not defined yet and so breaks compile.

drivers/clk/tegra/clk-tegra124-dfll-fcpu.c: In function
=E2=80=98tegra124_dfll_fcpu_probe=E2=80=99:
drivers/clk/tegra/clk-tegra124-dfll-fcpu.c:161:5: error: =E2=80=98struct
tegra_dfll_soc_data=E2=80=99 has no member named =E2=80=98alignment=E2=80=
=99
  soc->alignment =3D align;
     ^

What about Tegra124? This is set to NULL? Can we not set this before
calling tegra_cvb_add_opp_table() and then just pass soc->alignment for
both Tegra124 and Tegra210? Then you can avoid the if-statements in
build_opp_table.

>  	if (IS_ERR(soc->cvb)) {
>  		dev_err(&pdev->dev, "couldn't add OPP table: %ld\n",
>  			PTR_ERR(soc->cvb));
> diff --git a/drivers/clk/tegra/cvb.c b/drivers/clk/tegra/cvb.c
> index da9e8e7..561012a 100644
> --- a/drivers/clk/tegra/cvb.c
> +++ b/drivers/clk/tegra/cvb.c
> @@ -62,11 +62,19 @@ static int round_voltage(int mv, const struct rail_al=
ignment *align, int up)
>  }
> =20
>  static int build_opp_table(struct device *dev, const struct cvb_table *t=
able,
> +			   struct rail_alignment *align,
>  			   int speedo_value, unsigned long max_freq)
>  {
> -	const struct rail_alignment *align =3D &table->alignment;
>  	int i, ret, dfll_mv, min_mv, max_mv;
> =20
> +	if (!align->step_uv)
> +		align->step_uv =3D table->alignment.step_uv;
> +	if (!align->step_uv)
> +		return -EINVAL;
> +
> +	if (!align->offset_uv)
> +		align->offset_uv =3D table->alignment.offset_uv;
> +
>  	min_mv =3D round_voltage(table->min_millivolts, align, UP);
>  	max_mv =3D round_voltage(table->max_millivolts, align, DOWN);
> =20
> @@ -109,8 +117,9 @@ static int build_opp_table(struct device *dev, const =
struct cvb_table *table,
>   */
>  const struct cvb_table *
>  tegra_cvb_add_opp_table(struct device *dev, const struct cvb_table *tabl=
es,
> -			size_t count, int process_id, int speedo_id,
> -			int speedo_value, unsigned long max_freq)
> +			size_t count, struct rail_alignment *align,
> +			int process_id, int speedo_id, int speedo_value,
> +			unsigned long max_freq)
>  {
>  	size_t i;
>  	int ret;
> @@ -124,7 +133,8 @@ static int build_opp_table(struct device *dev, const =
struct cvb_table *table,
>  		if (table->process_id !=3D -1 && table->process_id !=3D process_id)
>  			continue;
> =20
> -		ret =3D build_opp_table(dev, table, speedo_value, max_freq);
> +		ret =3D build_opp_table(dev, table, align, speedo_value,
> +				      max_freq);
>  		return ret ? ERR_PTR(ret) : table;
>  	}
> =20
> diff --git a/drivers/clk/tegra/cvb.h b/drivers/clk/tegra/cvb.h
> index c1f0779..cfa110f 100644
> --- a/drivers/clk/tegra/cvb.h
> +++ b/drivers/clk/tegra/cvb.h
> @@ -59,8 +59,9 @@ struct cvb_table {
> =20
>  const struct cvb_table *
>  tegra_cvb_add_opp_table(struct device *dev, const struct cvb_table *cvb_=
tables,
> -			size_t count, int process_id, int speedo_id,
> -			int speedo_value, unsigned long max_freq);
> +			size_t count, struct rail_alignment *align,
> +			int process_id, int speedo_id, int speedo_value,
> +			unsigned long max_freq);
>  void tegra_cvb_remove_opp_table(struct device *dev,
>  				const struct cvb_table *table,
>  				unsigned long max_freq);
>=20

Cheers
Jon

--=20
nvpublic

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

* Re: [PATCH v2 3/6] clk: tegra: add CVB tables for Tegra210 CPU DFLL
  2018-01-24 12:45   ` Peter De Schrijver
@ 2018-01-31 10:50     ` Jon Hunter
  -1 siblings, 0 replies; 56+ messages in thread
From: Jon Hunter @ 2018-01-31 10:50 UTC (permalink / raw)
  To: Peter De Schrijver, linux-tegra, linux-clk, mturquette, sboyd


On 24/01/18 12:45, Peter De Schrijver wrote:
> Signed-off-by: Peter De Schrijver <pdeschrijver@nvidia.com>
> ---
>  drivers/clk/tegra/clk-tegra124-dfll-fcpu.c | 426 +++++++++++++++++++++++++++++
>  drivers/clk/tegra/cvb.h                    |   1 +
>  2 files changed, 427 insertions(+)
> 
> diff --git a/drivers/clk/tegra/clk-tegra124-dfll-fcpu.c b/drivers/clk/tegra/clk-tegra124-dfll-fcpu.c
> index 6205ce1..9b61f80 100644
> --- a/drivers/clk/tegra/clk-tegra124-dfll-fcpu.c
> +++ b/drivers/clk/tegra/clk-tegra124-dfll-fcpu.c
> @@ -90,6 +90,421 @@ struct dfll_fcpu_data {
>  	},
>  };
>  
> +static const unsigned long tegra210_cpu_max_freq_table[] = {
> +	[0] = 1912500000UL,
> +	[1] = 1912500000UL,

Any reason why there are two the same here? FWIW I also see this in the
nvidia downstream kernel. Just seems odd.
	
> +	[2] = 2218500000UL,
> +	[3] = 1785000000UL,
> +	[4] = 1632000000UL,
> +	[5] = 1912500000UL,
> +	[6] = 2014500000UL,
> +	[7] = 1734000000UL,
> +	[8] = 1683000000UL,
> +	[9] = 1555500000UL,
> +	[10] = 1504500000UL,
> +};
> +
> +#define CPU_CVB_TABLE \
> +	.speedo_scale = 100,	\
> +	.voltage_scale = 1000,	\
> +	.entries = {		\
> +		{204000000UL,	{1007452, -23865, 370} }, \
> +		{306000000UL,	{1052709, -24875, 370} }, \
> +		{408000000UL,	{1099069, -25895, 370} }, \
> +		{510000000UL,	{1146534, -26905, 370} }, \
> +		{612000000UL,	{1195102, -27915, 370} }, \
> +		{714000000UL,	{1244773, -28925, 370} }, \
> +		{816000000UL,	{1295549, -29935, 370} }, \
> +		{918000000UL,	{1347428, -30955, 370} }, \
> +		{1020000000UL,	{1400411, -31965, 370} }, \
> +		{1122000000UL,	{1454497, -32975, 370} }, \
> +		{1224000000UL,	{1509687, -33985, 370} }, \
> +		{1326000000UL,	{1565981, -35005, 370} }, \
> +		{1428000000UL,	{1623379, -36015, 370} }, \
> +		{1530000000UL,	{1681880, -37025, 370} }, \
> +		{1632000000UL,	{1741485, -38035, 370} }, \
> +		{1734000000UL,	{1802194, -39055, 370} }, \
> +		{1836000000UL,	{1864006, -40065, 370} }, \
> +		{1912500000UL,	{1910780, -40815, 370} }, \
> +		{2014500000UL,	{1227000,      0,   0} }, \
> +		{2218500000UL,	{1227000,      0,   0} }, \
> +		{0,		{      0,      0,   0} }, \
> +	}
> +
> +#define CPU_CVB_TABLE_XA \
> +	.speedo_scale = 100,	\
> +	.voltage_scale = 1000,	\
> +	.entries = {		\
> +		{204000000UL,	{1250024, -39785, 565} }, \
> +		{306000000UL,	{1297556, -41145, 565} }, \
> +		{408000000UL,	{1346718, -42505, 565} }, \
> +		{510000000UL,	{1397511, -43855, 565} }, \
> +		{612000000UL,	{1449933, -45215, 565} }, \
> +		{714000000UL,	{1503986, -46575, 565} }, \
> +		{816000000UL,	{1559669, -47935, 565} }, \
> +		{918000000UL,	{1616982, -49295, 565} }, \
> +		{1020000000UL,	{1675926, -50645, 565} }, \
> +		{1122000000UL,	{1736500, -52005, 565} }, \
> +		{1224000000UL,	{1798704, -53365, 565} }, \
> +		{1326000000UL,	{1862538, -54725, 565} }, \
> +		{1428000000UL,	{1928003, -56085, 565} }, \
> +		{1530000000UL,	{1995097, -57435, 565} }, \
> +		{1606500000UL,	{2046149, -58445, 565} }, \
> +		{1632000000UL,	{2063822, -58795, 565} }, \
> +		{0,		{      0,      0,   0} }, \
> +	}
> +
> +#define CPU_CVB_TABLE_EUCM1 \
> +	.speedo_scale = 100,	\
> +	.voltage_scale = 1000,	\
> +	.entries = {		\
> +		{204000000UL,	{734429, 0, 0} }, \
> +		{306000000UL,	{768191, 0, 0} }, \
> +		{408000000UL,	{801953, 0, 0} }, \
> +		{510000000UL,	{835715, 0, 0} }, \
> +		{612000000UL,	{869477, 0, 0} }, \
> +		{714000000UL,	{903239, 0, 0} }, \
> +		{816000000UL,	{937001, 0, 0} }, \
> +		{918000000UL,	{970763, 0, 0} }, \
> +		{1020000000UL,	{1004525, 0, 0} }, \
> +		{1122000000UL,	{1038287, 0, 0} }, \
> +		{1224000000UL,	{1072049, 0, 0} }, \
> +		{1326000000UL,	{1105811, 0, 0} }, \
> +		{1428000000UL,	{1130000, 0, 0} }, \
> +		{1555500000UL,	{1130000, 0, 0} }, \
> +		{1632000000UL,	{1170000, 0, 0} }, \
> +		{1734000000UL,	{1227500, 0, 0} }, \
> +		{0,		{      0, 0, 0} }, \
> +	}
> +
> +#define CPU_CVB_TABLE_EUCM2 \
> +	.speedo_scale = 100,	\
> +	.voltage_scale = 1000,	\
> +	.entries = {		\
> +		{204000000UL,	{742283, 0, 0} }, \
> +		{306000000UL,	{776249, 0, 0} }, \
> +		{408000000UL,	{810215, 0, 0} }, \
> +		{510000000UL,	{844181, 0, 0} }, \
> +		{612000000UL,	{878147, 0, 0} }, \
> +		{714000000UL,	{912113, 0, 0} }, \
> +		{816000000UL,	{946079, 0, 0} }, \
> +		{918000000UL,	{980045, 0, 0} }, \
> +		{1020000000UL,	{1014011, 0, 0} }, \
> +		{1122000000UL,	{1047977, 0, 0} }, \
> +		{1224000000UL,	{1081943, 0, 0} }, \
> +		{1326000000UL,	{1090000, 0, 0} }, \
> +		{1479000000UL,	{1090000, 0, 0} }, \
> +		{1555500000UL,	{1162000, 0, 0} }, \
> +		{1683000000UL,	{1195000, 0, 0} }, \
> +		{0,		{      0, 0, 0} }, \
> +	}
> +
> +#define CPU_CVB_TABLE_EUCM2_JOINT_RAIL \
> +	.speedo_scale = 100,	\
> +	.voltage_scale = 1000,	\
> +	.entries = {		\
> +		{204000000UL,	{742283, 0, 0} }, \
> +		{306000000UL,	{776249, 0, 0} }, \
> +		{408000000UL,	{810215, 0, 0} }, \
> +		{510000000UL,	{844181, 0, 0} }, \
> +		{612000000UL,	{878147, 0, 0} }, \
> +		{714000000UL,	{912113, 0, 0} }, \
> +		{816000000UL,	{946079, 0, 0} }, \
> +		{918000000UL,	{980045, 0, 0} }, \
> +		{1020000000UL,	{1014011, 0, 0} }, \
> +		{1122000000UL,	{1047977, 0, 0} }, \
> +		{1224000000UL,	{1081943, 0, 0} }, \
> +		{1326000000UL,	{1090000, 0, 0} }, \
> +		{1479000000UL,	{1090000, 0, 0} }, \
> +		{1504500000UL,	{1120000, 0, 0} }, \
> +		{0,		{      0, 0, 0} }, \
> +	}
> +
> +#define CPU_CVB_TABLE_ODN \
> +	.speedo_scale = 100,	\
> +	.voltage_scale = 1000,	\
> +	.entries = {		\
> +		{204000000UL,	{721094, 0, 0} }, \
> +		{306000000UL,	{754040, 0, 0} }, \
> +		{408000000UL,	{786986, 0, 0} }, \
> +		{510000000UL,	{819932, 0, 0} }, \
> +		{612000000UL,	{852878, 0, 0} }, \
> +		{714000000UL,	{885824, 0, 0} }, \
> +		{816000000UL,	{918770, 0, 0} }, \
> +		{918000000UL,	{915716, 0, 0} }, \
> +		{1020000000UL,	{984662, 0, 0} }, \
> +		{1122000000UL,	{1017608, 0, 0} }, \
> +		{1224000000UL,	{1050554, 0, 0} }, \
> +		{1326000000UL,	{1083500, 0, 0} }, \
> +		{1428000000UL,	{1116446, 0, 0} }, \
> +		{1581000000UL,	{1130000, 0, 0} }, \
> +		{1683000000UL,	{1168000, 0, 0} }, \
> +		{1785000000UL,	{1227500, 0, 0} }, \
> +		{0,		{      0, 0, 0} }, \
> +	}
> +
> +struct cvb_table tegra210_cpu_cvb_tables[] = {
> +	{
> +		.speedo_id = 10,
> +		.process_id = 0,
> +		.min_millivolts = 840,
> +		.max_millivolts = 1120,
> +		CPU_CVB_TABLE_EUCM2_JOINT_RAIL,
> +		.cpu_dfll_data = {
> +			.tune0_low = 0xffead0ff,
> +			.tune0_high = 0xffead0ff,
> +			.tune1 = 0x20091d9,
> +			.tune_high_min_millivolts = 864,
> +		}
> +	},
> +	{
> +		.speedo_id = 10,
> +		.process_id = 1,
> +		.min_millivolts = 840,
> +		.max_millivolts = 1120,
> +		CPU_CVB_TABLE_EUCM2_JOINT_RAIL,
> +		.cpu_dfll_data = {
> +			.tune0_low = 0xffead0ff,
> +			.tune0_high = 0xffead0ff,
> +			.tune1 = 0x20091d9,
> +			.tune_high_min_millivolts = 864,
> +		}
> +	},
> +	{
> +		.speedo_id = 9,
> +		.process_id = 0,
> +		.min_millivolts = 900,
> +		.max_millivolts = 1162,
> +		CPU_CVB_TABLE_EUCM2,
> +		.cpu_dfll_data = {
> +			.tune0_low = 0xffead0ff,
> +			.tune0_high = 0xffead0ff,
> +			.tune1 = 0x20091d9,
> +		}
> +	},
> +	{
> +		.speedo_id = 9,
> +		.process_id = 1,
> +		.min_millivolts = 900,
> +		.max_millivolts = 1162,
> +		CPU_CVB_TABLE_EUCM2,
> +		.cpu_dfll_data = {
> +			.tune0_low = 0xffead0ff,
> +			.tune0_high = 0xffead0ff,
> +			.tune1 = 0x20091d9,
> +		}
> +	},
> +	{
> +		.speedo_id = 8,
> +		.process_id = 0,
> +		.min_millivolts = 900,
> +		.max_millivolts = 1195,
> +		CPU_CVB_TABLE_EUCM2,
> +		.cpu_dfll_data = {
> +			.tune0_low = 0xffead0ff,
> +			.tune0_high = 0xffead0ff,
> +			.tune1 = 0x20091d9,
> +		}
> +	},
> +	{
> +		.speedo_id = 8,
> +		.process_id = 1,
> +		.min_millivolts = 900,
> +		.max_millivolts = 1195,
> +		CPU_CVB_TABLE_EUCM2,
> +		.cpu_dfll_data = {
> +			.tune0_low = 0xffead0ff,
> +			.tune0_high = 0xffead0ff,
> +			.tune1 = 0x20091d9,
> +		}
> +	},
> +	{
> +		.speedo_id = 7,
> +		.process_id = 0,
> +		.min_millivolts = 841,
> +		.max_millivolts = 1227,
> +		CPU_CVB_TABLE_EUCM1,
> +		.cpu_dfll_data = {
> +			.tune0_low = 0xffead0ff,
> +			.tune0_high = 0xffead0ff,
> +			.tune1 = 0x20091d9,
> +			.tune_high_min_millivolts = 864,
> +		}
> +	},
> +	{
> +		.speedo_id = 7,
> +		.process_id = 1,
> +		.min_millivolts = 841,
> +		.max_millivolts = 1227,
> +		CPU_CVB_TABLE_EUCM1,
> +		.cpu_dfll_data = {
> +			.tune0_low = 0xffead0ff,
> +			.tune0_high = 0xffead0ff,
> +			.tune1 = 0x20091d9,
> +			.tune_high_min_millivolts = 864,
> +		}
> +	},
> +	{
> +		.speedo_id = 6,
> +		.process_id = 0,
> +		.min_millivolts = 870,
> +		.max_millivolts = 1150,
> +		CPU_CVB_TABLE,
> +		.cpu_dfll_data = {
> +			.tune0_low = 0xffead0ff,
> +			.tune1 = 0x20091d9,
> +		}
> +	},
> +	{
> +		.speedo_id = 6,
> +		.process_id = 1,
> +		.min_millivolts = 870,
> +		.max_millivolts = 1150,
> +		CPU_CVB_TABLE,
> +		.cpu_dfll_data = {
> +			.tune0_low = 0xffead0ff,
> +			.tune1 = 0x25501d0,
> +		}
> +	},
> +	{
> +		.speedo_id = 5,
> +		.process_id = 0,
> +		.min_millivolts = 818,
> +		.max_millivolts = 1227,
> +		CPU_CVB_TABLE,
> +		.cpu_dfll_data = {
> +			.tune0_low = 0xffead0ff,
> +			.tune0_high = 0xffead0ff,
> +			.tune1 = 0x20091d9,
> +			.tune_high_min_millivolts = 864,
> +		}
> +	},
> +	{
> +		.speedo_id = 5,
> +		.process_id = 1,
> +		.min_millivolts = 818,
> +		.max_millivolts = 1227,
> +		CPU_CVB_TABLE,
> +		.cpu_dfll_data = {
> +			.tune0_low = 0xffead0ff,
> +			.tune0_high = 0xffead0ff,
> +			.tune1 = 0x25501d0,
> +			.tune_high_min_millivolts = 864,
> +		}
> +	},
> +	{
> +		.speedo_id = 4,
> +		.process_id = -1,
> +		.min_millivolts = 918,
> +		.max_millivolts = 1113,
> +		CPU_CVB_TABLE_XA,
> +		.cpu_dfll_data = {
> +			.tune0_low = 0xffead0ff,
> +			.tune1 = 0x17711BD,
> +		}
> +	},
> +	{
> +		.speedo_id = 3,
> +		.process_id = 0,
> +		.min_millivolts = 825,
> +		.max_millivolts = 1227,
> +		CPU_CVB_TABLE_ODN,
> +		.cpu_dfll_data = {
> +			.tune0_low = 0xffead0ff,
> +			.tune0_high = 0xffead0ff,
> +			.tune1 = 0x20091d9,
> +			.tune_high_min_millivolts = 864,
> +		}
> +	},
> +	{
> +		.speedo_id = 3,
> +		.process_id = 1,
> +		.min_millivolts = 825,
> +		.max_millivolts = 1227,
> +		CPU_CVB_TABLE_ODN,
> +		.cpu_dfll_data = {
> +			.tune0_low = 0xffead0ff,
> +			.tune0_high = 0xffead0ff,
> +			.tune1 = 0x25501d0,
> +			.tune_high_min_millivolts = 864,
> +		}
> +	},
> +	{
> +		.speedo_id = 2,
> +		.process_id = 0,
> +		.min_millivolts = 870,
> +		.max_millivolts = 1227,
> +		CPU_CVB_TABLE,
> +		.cpu_dfll_data = {
> +			.tune0_low = 0xffead0ff,
> +			.tune1 = 0x20091d9,
> +		}
> +	},
> +	{
> +		.speedo_id = 2,
> +		.process_id = 1,
> +		.min_millivolts = 870,
> +		.max_millivolts = 1227,
> +		CPU_CVB_TABLE,
> +		.cpu_dfll_data = {
> +			.tune0_low = 0xffead0ff,
> +			.tune1 = 0x25501d0,
> +		}
> +	},
> +	{
> +		.speedo_id = 1,
> +		.process_id = 0,
> +		.min_millivolts = 837,
> +		.max_millivolts = 1227,
> +		CPU_CVB_TABLE,
> +		.cpu_dfll_data = {
> +			.tune0_low = 0xffead0ff,
> +			.tune0_high = 0xffead0ff,
> +			.tune1 = 0x20091d9,
> +			.tune_high_min_millivolts = 864,
> +		}
> +	},
> +	{
> +		.speedo_id = 1,
> +		.process_id = 1,
> +		.min_millivolts = 837,
> +		.max_millivolts = 1227,
> +		CPU_CVB_TABLE,
> +		.cpu_dfll_data = {
> +			.tune0_low = 0xffead0ff,
> +			.tune0_high = 0xffead0ff,
> +			.tune1 = 0x25501d0,
> +			.tune_high_min_millivolts = 864,
> +		}
> +	},
> +	{
> +		.speedo_id = 0,
> +		.process_id = 0,
> +		.min_millivolts = 850,
> +		.max_millivolts = 1170,
> +		CPU_CVB_TABLE,
> +		.cpu_dfll_data = {
> +			.tune0_low = 0xffead0ff,
> +			.tune0_high = 0xffead0ff,
> +			.tune1 = 0x20091d9,
> +			.tune_high_min_millivolts = 864,
> +		}
> +	},
> +	{
> +		.speedo_id = 0,
> +		.process_id = 1,
> +		.min_millivolts = 850,
> +		.max_millivolts = 1170,
> +		CPU_CVB_TABLE,
> +		.cpu_dfll_data = {
> +			.tune0_low = 0xffead0ff,
> +			.tune0_high = 0xffead0ff,
> +			.tune1 = 0x25501d0,
> +			.tune_high_min_millivolts = 864,
> +		}
> +	},
> +};
> +
>  static const struct dfll_fcpu_data tegra124_dfll_fcpu_data = {
>  	.cpu_max_freq_table = tegra124_cpu_max_freq_table,
>  	.cpu_max_freq_table_size = ARRAY_SIZE(tegra124_cpu_max_freq_table),
> @@ -97,11 +512,22 @@ struct dfll_fcpu_data {
>  	.cpu_cvb_tables_size = ARRAY_SIZE(tegra124_cpu_cvb_tables)
>  };
>  
> +static const struct dfll_fcpu_data tegra210_dfll_fcpu_data = {
> +	.cpu_max_freq_table = tegra210_cpu_max_freq_table,
> +	.cpu_max_freq_table_size = ARRAY_SIZE(tegra210_cpu_max_freq_table),
> +	.cpu_cvb_tables = tegra210_cpu_cvb_tables,
> +	.cpu_cvb_tables_size = ARRAY_SIZE(tegra210_cpu_cvb_tables),
> +};
> +
>  static const struct of_device_id tegra124_dfll_fcpu_of_match[] = {
>  	{
>  		.compatible = "nvidia,tegra124-dfll",
>  		.data = &tegra124_dfll_fcpu_data,
>  	},
> +	{
> +		.compatible = "nvidia,tegra210-dfll",
> +		.data = &tegra210_dfll_fcpu_data
> +	},
>  	{ },
>  };
>  
> diff --git a/drivers/clk/tegra/cvb.h b/drivers/clk/tegra/cvb.h
> index cfa110f..72c0a2d 100644
> --- a/drivers/clk/tegra/cvb.h
> +++ b/drivers/clk/tegra/cvb.h
> @@ -41,6 +41,7 @@ struct cvb_cpu_dfll_data {
>  	u32 tune0_low;
>  	u32 tune0_high;
>  	u32 tune1;
> +	unsigned int tune_high_min_millivolts;
>  };
>  
>  struct cvb_table {
> 

Otherwise ...

Reviewed-by: Jon Hunter <jonathanh@nvidia.com>

Cheers
Jon

-- 
nvpublic

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

* Re: [PATCH v2 3/6] clk: tegra: add CVB tables for Tegra210 CPU DFLL
@ 2018-01-31 10:50     ` Jon Hunter
  0 siblings, 0 replies; 56+ messages in thread
From: Jon Hunter @ 2018-01-31 10:50 UTC (permalink / raw)
  To: Peter De Schrijver, linux-tegra, linux-clk, mturquette, sboyd


On 24/01/18 12:45, Peter De Schrijver wrote:
> Signed-off-by: Peter De Schrijver <pdeschrijver@nvidia.com>
> ---
>  drivers/clk/tegra/clk-tegra124-dfll-fcpu.c | 426 +++++++++++++++++++++++++++++
>  drivers/clk/tegra/cvb.h                    |   1 +
>  2 files changed, 427 insertions(+)
> 
> diff --git a/drivers/clk/tegra/clk-tegra124-dfll-fcpu.c b/drivers/clk/tegra/clk-tegra124-dfll-fcpu.c
> index 6205ce1..9b61f80 100644
> --- a/drivers/clk/tegra/clk-tegra124-dfll-fcpu.c
> +++ b/drivers/clk/tegra/clk-tegra124-dfll-fcpu.c
> @@ -90,6 +90,421 @@ struct dfll_fcpu_data {
>  	},
>  };
>  
> +static const unsigned long tegra210_cpu_max_freq_table[] = {
> +	[0] = 1912500000UL,
> +	[1] = 1912500000UL,

Any reason why there are two the same here? FWIW I also see this in the
nvidia downstream kernel. Just seems odd.
	
> +	[2] = 2218500000UL,
> +	[3] = 1785000000UL,
> +	[4] = 1632000000UL,
> +	[5] = 1912500000UL,
> +	[6] = 2014500000UL,
> +	[7] = 1734000000UL,
> +	[8] = 1683000000UL,
> +	[9] = 1555500000UL,
> +	[10] = 1504500000UL,
> +};
> +
> +#define CPU_CVB_TABLE \
> +	.speedo_scale = 100,	\
> +	.voltage_scale = 1000,	\
> +	.entries = {		\
> +		{204000000UL,	{1007452, -23865, 370} }, \
> +		{306000000UL,	{1052709, -24875, 370} }, \
> +		{408000000UL,	{1099069, -25895, 370} }, \
> +		{510000000UL,	{1146534, -26905, 370} }, \
> +		{612000000UL,	{1195102, -27915, 370} }, \
> +		{714000000UL,	{1244773, -28925, 370} }, \
> +		{816000000UL,	{1295549, -29935, 370} }, \
> +		{918000000UL,	{1347428, -30955, 370} }, \
> +		{1020000000UL,	{1400411, -31965, 370} }, \
> +		{1122000000UL,	{1454497, -32975, 370} }, \
> +		{1224000000UL,	{1509687, -33985, 370} }, \
> +		{1326000000UL,	{1565981, -35005, 370} }, \
> +		{1428000000UL,	{1623379, -36015, 370} }, \
> +		{1530000000UL,	{1681880, -37025, 370} }, \
> +		{1632000000UL,	{1741485, -38035, 370} }, \
> +		{1734000000UL,	{1802194, -39055, 370} }, \
> +		{1836000000UL,	{1864006, -40065, 370} }, \
> +		{1912500000UL,	{1910780, -40815, 370} }, \
> +		{2014500000UL,	{1227000,      0,   0} }, \
> +		{2218500000UL,	{1227000,      0,   0} }, \
> +		{0,		{      0,      0,   0} }, \
> +	}
> +
> +#define CPU_CVB_TABLE_XA \
> +	.speedo_scale = 100,	\
> +	.voltage_scale = 1000,	\
> +	.entries = {		\
> +		{204000000UL,	{1250024, -39785, 565} }, \
> +		{306000000UL,	{1297556, -41145, 565} }, \
> +		{408000000UL,	{1346718, -42505, 565} }, \
> +		{510000000UL,	{1397511, -43855, 565} }, \
> +		{612000000UL,	{1449933, -45215, 565} }, \
> +		{714000000UL,	{1503986, -46575, 565} }, \
> +		{816000000UL,	{1559669, -47935, 565} }, \
> +		{918000000UL,	{1616982, -49295, 565} }, \
> +		{1020000000UL,	{1675926, -50645, 565} }, \
> +		{1122000000UL,	{1736500, -52005, 565} }, \
> +		{1224000000UL,	{1798704, -53365, 565} }, \
> +		{1326000000UL,	{1862538, -54725, 565} }, \
> +		{1428000000UL,	{1928003, -56085, 565} }, \
> +		{1530000000UL,	{1995097, -57435, 565} }, \
> +		{1606500000UL,	{2046149, -58445, 565} }, \
> +		{1632000000UL,	{2063822, -58795, 565} }, \
> +		{0,		{      0,      0,   0} }, \
> +	}
> +
> +#define CPU_CVB_TABLE_EUCM1 \
> +	.speedo_scale = 100,	\
> +	.voltage_scale = 1000,	\
> +	.entries = {		\
> +		{204000000UL,	{734429, 0, 0} }, \
> +		{306000000UL,	{768191, 0, 0} }, \
> +		{408000000UL,	{801953, 0, 0} }, \
> +		{510000000UL,	{835715, 0, 0} }, \
> +		{612000000UL,	{869477, 0, 0} }, \
> +		{714000000UL,	{903239, 0, 0} }, \
> +		{816000000UL,	{937001, 0, 0} }, \
> +		{918000000UL,	{970763, 0, 0} }, \
> +		{1020000000UL,	{1004525, 0, 0} }, \
> +		{1122000000UL,	{1038287, 0, 0} }, \
> +		{1224000000UL,	{1072049, 0, 0} }, \
> +		{1326000000UL,	{1105811, 0, 0} }, \
> +		{1428000000UL,	{1130000, 0, 0} }, \
> +		{1555500000UL,	{1130000, 0, 0} }, \
> +		{1632000000UL,	{1170000, 0, 0} }, \
> +		{1734000000UL,	{1227500, 0, 0} }, \
> +		{0,		{      0, 0, 0} }, \
> +	}
> +
> +#define CPU_CVB_TABLE_EUCM2 \
> +	.speedo_scale = 100,	\
> +	.voltage_scale = 1000,	\
> +	.entries = {		\
> +		{204000000UL,	{742283, 0, 0} }, \
> +		{306000000UL,	{776249, 0, 0} }, \
> +		{408000000UL,	{810215, 0, 0} }, \
> +		{510000000UL,	{844181, 0, 0} }, \
> +		{612000000UL,	{878147, 0, 0} }, \
> +		{714000000UL,	{912113, 0, 0} }, \
> +		{816000000UL,	{946079, 0, 0} }, \
> +		{918000000UL,	{980045, 0, 0} }, \
> +		{1020000000UL,	{1014011, 0, 0} }, \
> +		{1122000000UL,	{1047977, 0, 0} }, \
> +		{1224000000UL,	{1081943, 0, 0} }, \
> +		{1326000000UL,	{1090000, 0, 0} }, \
> +		{1479000000UL,	{1090000, 0, 0} }, \
> +		{1555500000UL,	{1162000, 0, 0} }, \
> +		{1683000000UL,	{1195000, 0, 0} }, \
> +		{0,		{      0, 0, 0} }, \
> +	}
> +
> +#define CPU_CVB_TABLE_EUCM2_JOINT_RAIL \
> +	.speedo_scale = 100,	\
> +	.voltage_scale = 1000,	\
> +	.entries = {		\
> +		{204000000UL,	{742283, 0, 0} }, \
> +		{306000000UL,	{776249, 0, 0} }, \
> +		{408000000UL,	{810215, 0, 0} }, \
> +		{510000000UL,	{844181, 0, 0} }, \
> +		{612000000UL,	{878147, 0, 0} }, \
> +		{714000000UL,	{912113, 0, 0} }, \
> +		{816000000UL,	{946079, 0, 0} }, \
> +		{918000000UL,	{980045, 0, 0} }, \
> +		{1020000000UL,	{1014011, 0, 0} }, \
> +		{1122000000UL,	{1047977, 0, 0} }, \
> +		{1224000000UL,	{1081943, 0, 0} }, \
> +		{1326000000UL,	{1090000, 0, 0} }, \
> +		{1479000000UL,	{1090000, 0, 0} }, \
> +		{1504500000UL,	{1120000, 0, 0} }, \
> +		{0,		{      0, 0, 0} }, \
> +	}
> +
> +#define CPU_CVB_TABLE_ODN \
> +	.speedo_scale = 100,	\
> +	.voltage_scale = 1000,	\
> +	.entries = {		\
> +		{204000000UL,	{721094, 0, 0} }, \
> +		{306000000UL,	{754040, 0, 0} }, \
> +		{408000000UL,	{786986, 0, 0} }, \
> +		{510000000UL,	{819932, 0, 0} }, \
> +		{612000000UL,	{852878, 0, 0} }, \
> +		{714000000UL,	{885824, 0, 0} }, \
> +		{816000000UL,	{918770, 0, 0} }, \
> +		{918000000UL,	{915716, 0, 0} }, \
> +		{1020000000UL,	{984662, 0, 0} }, \
> +		{1122000000UL,	{1017608, 0, 0} }, \
> +		{1224000000UL,	{1050554, 0, 0} }, \
> +		{1326000000UL,	{1083500, 0, 0} }, \
> +		{1428000000UL,	{1116446, 0, 0} }, \
> +		{1581000000UL,	{1130000, 0, 0} }, \
> +		{1683000000UL,	{1168000, 0, 0} }, \
> +		{1785000000UL,	{1227500, 0, 0} }, \
> +		{0,		{      0, 0, 0} }, \
> +	}
> +
> +struct cvb_table tegra210_cpu_cvb_tables[] = {
> +	{
> +		.speedo_id = 10,
> +		.process_id = 0,
> +		.min_millivolts = 840,
> +		.max_millivolts = 1120,
> +		CPU_CVB_TABLE_EUCM2_JOINT_RAIL,
> +		.cpu_dfll_data = {
> +			.tune0_low = 0xffead0ff,
> +			.tune0_high = 0xffead0ff,
> +			.tune1 = 0x20091d9,
> +			.tune_high_min_millivolts = 864,
> +		}
> +	},
> +	{
> +		.speedo_id = 10,
> +		.process_id = 1,
> +		.min_millivolts = 840,
> +		.max_millivolts = 1120,
> +		CPU_CVB_TABLE_EUCM2_JOINT_RAIL,
> +		.cpu_dfll_data = {
> +			.tune0_low = 0xffead0ff,
> +			.tune0_high = 0xffead0ff,
> +			.tune1 = 0x20091d9,
> +			.tune_high_min_millivolts = 864,
> +		}
> +	},
> +	{
> +		.speedo_id = 9,
> +		.process_id = 0,
> +		.min_millivolts = 900,
> +		.max_millivolts = 1162,
> +		CPU_CVB_TABLE_EUCM2,
> +		.cpu_dfll_data = {
> +			.tune0_low = 0xffead0ff,
> +			.tune0_high = 0xffead0ff,
> +			.tune1 = 0x20091d9,
> +		}
> +	},
> +	{
> +		.speedo_id = 9,
> +		.process_id = 1,
> +		.min_millivolts = 900,
> +		.max_millivolts = 1162,
> +		CPU_CVB_TABLE_EUCM2,
> +		.cpu_dfll_data = {
> +			.tune0_low = 0xffead0ff,
> +			.tune0_high = 0xffead0ff,
> +			.tune1 = 0x20091d9,
> +		}
> +	},
> +	{
> +		.speedo_id = 8,
> +		.process_id = 0,
> +		.min_millivolts = 900,
> +		.max_millivolts = 1195,
> +		CPU_CVB_TABLE_EUCM2,
> +		.cpu_dfll_data = {
> +			.tune0_low = 0xffead0ff,
> +			.tune0_high = 0xffead0ff,
> +			.tune1 = 0x20091d9,
> +		}
> +	},
> +	{
> +		.speedo_id = 8,
> +		.process_id = 1,
> +		.min_millivolts = 900,
> +		.max_millivolts = 1195,
> +		CPU_CVB_TABLE_EUCM2,
> +		.cpu_dfll_data = {
> +			.tune0_low = 0xffead0ff,
> +			.tune0_high = 0xffead0ff,
> +			.tune1 = 0x20091d9,
> +		}
> +	},
> +	{
> +		.speedo_id = 7,
> +		.process_id = 0,
> +		.min_millivolts = 841,
> +		.max_millivolts = 1227,
> +		CPU_CVB_TABLE_EUCM1,
> +		.cpu_dfll_data = {
> +			.tune0_low = 0xffead0ff,
> +			.tune0_high = 0xffead0ff,
> +			.tune1 = 0x20091d9,
> +			.tune_high_min_millivolts = 864,
> +		}
> +	},
> +	{
> +		.speedo_id = 7,
> +		.process_id = 1,
> +		.min_millivolts = 841,
> +		.max_millivolts = 1227,
> +		CPU_CVB_TABLE_EUCM1,
> +		.cpu_dfll_data = {
> +			.tune0_low = 0xffead0ff,
> +			.tune0_high = 0xffead0ff,
> +			.tune1 = 0x20091d9,
> +			.tune_high_min_millivolts = 864,
> +		}
> +	},
> +	{
> +		.speedo_id = 6,
> +		.process_id = 0,
> +		.min_millivolts = 870,
> +		.max_millivolts = 1150,
> +		CPU_CVB_TABLE,
> +		.cpu_dfll_data = {
> +			.tune0_low = 0xffead0ff,
> +			.tune1 = 0x20091d9,
> +		}
> +	},
> +	{
> +		.speedo_id = 6,
> +		.process_id = 1,
> +		.min_millivolts = 870,
> +		.max_millivolts = 1150,
> +		CPU_CVB_TABLE,
> +		.cpu_dfll_data = {
> +			.tune0_low = 0xffead0ff,
> +			.tune1 = 0x25501d0,
> +		}
> +	},
> +	{
> +		.speedo_id = 5,
> +		.process_id = 0,
> +		.min_millivolts = 818,
> +		.max_millivolts = 1227,
> +		CPU_CVB_TABLE,
> +		.cpu_dfll_data = {
> +			.tune0_low = 0xffead0ff,
> +			.tune0_high = 0xffead0ff,
> +			.tune1 = 0x20091d9,
> +			.tune_high_min_millivolts = 864,
> +		}
> +	},
> +	{
> +		.speedo_id = 5,
> +		.process_id = 1,
> +		.min_millivolts = 818,
> +		.max_millivolts = 1227,
> +		CPU_CVB_TABLE,
> +		.cpu_dfll_data = {
> +			.tune0_low = 0xffead0ff,
> +			.tune0_high = 0xffead0ff,
> +			.tune1 = 0x25501d0,
> +			.tune_high_min_millivolts = 864,
> +		}
> +	},
> +	{
> +		.speedo_id = 4,
> +		.process_id = -1,
> +		.min_millivolts = 918,
> +		.max_millivolts = 1113,
> +		CPU_CVB_TABLE_XA,
> +		.cpu_dfll_data = {
> +			.tune0_low = 0xffead0ff,
> +			.tune1 = 0x17711BD,
> +		}
> +	},
> +	{
> +		.speedo_id = 3,
> +		.process_id = 0,
> +		.min_millivolts = 825,
> +		.max_millivolts = 1227,
> +		CPU_CVB_TABLE_ODN,
> +		.cpu_dfll_data = {
> +			.tune0_low = 0xffead0ff,
> +			.tune0_high = 0xffead0ff,
> +			.tune1 = 0x20091d9,
> +			.tune_high_min_millivolts = 864,
> +		}
> +	},
> +	{
> +		.speedo_id = 3,
> +		.process_id = 1,
> +		.min_millivolts = 825,
> +		.max_millivolts = 1227,
> +		CPU_CVB_TABLE_ODN,
> +		.cpu_dfll_data = {
> +			.tune0_low = 0xffead0ff,
> +			.tune0_high = 0xffead0ff,
> +			.tune1 = 0x25501d0,
> +			.tune_high_min_millivolts = 864,
> +		}
> +	},
> +	{
> +		.speedo_id = 2,
> +		.process_id = 0,
> +		.min_millivolts = 870,
> +		.max_millivolts = 1227,
> +		CPU_CVB_TABLE,
> +		.cpu_dfll_data = {
> +			.tune0_low = 0xffead0ff,
> +			.tune1 = 0x20091d9,
> +		}
> +	},
> +	{
> +		.speedo_id = 2,
> +		.process_id = 1,
> +		.min_millivolts = 870,
> +		.max_millivolts = 1227,
> +		CPU_CVB_TABLE,
> +		.cpu_dfll_data = {
> +			.tune0_low = 0xffead0ff,
> +			.tune1 = 0x25501d0,
> +		}
> +	},
> +	{
> +		.speedo_id = 1,
> +		.process_id = 0,
> +		.min_millivolts = 837,
> +		.max_millivolts = 1227,
> +		CPU_CVB_TABLE,
> +		.cpu_dfll_data = {
> +			.tune0_low = 0xffead0ff,
> +			.tune0_high = 0xffead0ff,
> +			.tune1 = 0x20091d9,
> +			.tune_high_min_millivolts = 864,
> +		}
> +	},
> +	{
> +		.speedo_id = 1,
> +		.process_id = 1,
> +		.min_millivolts = 837,
> +		.max_millivolts = 1227,
> +		CPU_CVB_TABLE,
> +		.cpu_dfll_data = {
> +			.tune0_low = 0xffead0ff,
> +			.tune0_high = 0xffead0ff,
> +			.tune1 = 0x25501d0,
> +			.tune_high_min_millivolts = 864,
> +		}
> +	},
> +	{
> +		.speedo_id = 0,
> +		.process_id = 0,
> +		.min_millivolts = 850,
> +		.max_millivolts = 1170,
> +		CPU_CVB_TABLE,
> +		.cpu_dfll_data = {
> +			.tune0_low = 0xffead0ff,
> +			.tune0_high = 0xffead0ff,
> +			.tune1 = 0x20091d9,
> +			.tune_high_min_millivolts = 864,
> +		}
> +	},
> +	{
> +		.speedo_id = 0,
> +		.process_id = 1,
> +		.min_millivolts = 850,
> +		.max_millivolts = 1170,
> +		CPU_CVB_TABLE,
> +		.cpu_dfll_data = {
> +			.tune0_low = 0xffead0ff,
> +			.tune0_high = 0xffead0ff,
> +			.tune1 = 0x25501d0,
> +			.tune_high_min_millivolts = 864,
> +		}
> +	},
> +};
> +
>  static const struct dfll_fcpu_data tegra124_dfll_fcpu_data = {
>  	.cpu_max_freq_table = tegra124_cpu_max_freq_table,
>  	.cpu_max_freq_table_size = ARRAY_SIZE(tegra124_cpu_max_freq_table),
> @@ -97,11 +512,22 @@ struct dfll_fcpu_data {
>  	.cpu_cvb_tables_size = ARRAY_SIZE(tegra124_cpu_cvb_tables)
>  };
>  
> +static const struct dfll_fcpu_data tegra210_dfll_fcpu_data = {
> +	.cpu_max_freq_table = tegra210_cpu_max_freq_table,
> +	.cpu_max_freq_table_size = ARRAY_SIZE(tegra210_cpu_max_freq_table),
> +	.cpu_cvb_tables = tegra210_cpu_cvb_tables,
> +	.cpu_cvb_tables_size = ARRAY_SIZE(tegra210_cpu_cvb_tables),
> +};
> +
>  static const struct of_device_id tegra124_dfll_fcpu_of_match[] = {
>  	{
>  		.compatible = "nvidia,tegra124-dfll",
>  		.data = &tegra124_dfll_fcpu_data,
>  	},
> +	{
> +		.compatible = "nvidia,tegra210-dfll",
> +		.data = &tegra210_dfll_fcpu_data
> +	},
>  	{ },
>  };
>  
> diff --git a/drivers/clk/tegra/cvb.h b/drivers/clk/tegra/cvb.h
> index cfa110f..72c0a2d 100644
> --- a/drivers/clk/tegra/cvb.h
> +++ b/drivers/clk/tegra/cvb.h
> @@ -41,6 +41,7 @@ struct cvb_cpu_dfll_data {
>  	u32 tune0_low;
>  	u32 tune0_high;
>  	u32 tune1;
> +	unsigned int tune_high_min_millivolts;
>  };
>  
>  struct cvb_table {
> 

Otherwise ...

Reviewed-by: Jon Hunter <jonathanh@nvidia.com>

Cheers
Jon

-- 
nvpublic

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

* Re: [PATCH v2 5/6] clk: tegra: build clk-dfll.c for Tegra124 and Tegra210
  2018-01-24 12:45     ` Peter De Schrijver
@ 2018-01-31 11:03       ` Jon Hunter
  -1 siblings, 0 replies; 56+ messages in thread
From: Jon Hunter @ 2018-01-31 11:03 UTC (permalink / raw)
  To: Peter De Schrijver, linux-tegra, linux-clk, mturquette, sboyd


On 24/01/18 12:45, Peter De Schrijver wrote:
> Tegra210 has a DFLL as well and can share the majority of the code with
> the Tegra124 implementation. So build the same code for both platforms.
> 
> Signed-off-by: Peter De Schrijver <pdeschrijver@nvidia.com>
> ---
>  drivers/clk/tegra/Kconfig  | 5 +++++
>  drivers/clk/tegra/Makefile | 2 +-
>  2 files changed, 6 insertions(+), 1 deletion(-)
> 
> diff --git a/drivers/clk/tegra/Kconfig b/drivers/clk/tegra/Kconfig
> index 7ddacae..57902ab 100644
> --- a/drivers/clk/tegra/Kconfig
> +++ b/drivers/clk/tegra/Kconfig
> @@ -5,3 +5,8 @@ config TEGRA_CLK_EMC
>  config CLK_TEGRA_BPMP
>  	def_bool y
>  	depends on TEGRA_BPMP
> +
> +config TEGRA_CLK_DFLL
> +	depends on (ARCH_TEGRA_124_SOC || ARCH_TEGRA_210_SOC)
> +	select PM_OPP
> +	def_bool y

Being picky, the description does not mention adding the dependency on
PM_OPP. However, I do see that the driver does use this and so seems
valid. Maybe update the description to clarify that this is needed.

BTW, I see that for ARM this is already selected by ARCH_TEGRA.

> diff --git a/drivers/clk/tegra/Makefile b/drivers/clk/tegra/Makefile
> index b716923..fc7f4b4 100644
> --- a/drivers/clk/tegra/Makefile
> +++ b/drivers/clk/tegra/Makefile
> @@ -19,7 +19,7 @@ obj-$(CONFIG_ARCH_TEGRA_2x_SOC)         += clk-tegra20.o
>  obj-$(CONFIG_ARCH_TEGRA_3x_SOC)         += clk-tegra30.o
>  obj-$(CONFIG_ARCH_TEGRA_114_SOC)	+= clk-tegra114.o
>  obj-$(CONFIG_ARCH_TEGRA_124_SOC)	+= clk-tegra124.o
> -obj-$(CONFIG_ARCH_TEGRA_124_SOC)	+= clk-tegra124-dfll-fcpu.o
> +obj-$(CONFIG_TEGRA_CLK_DFLL)		+= clk-tegra124-dfll-fcpu.o
>  obj-$(CONFIG_ARCH_TEGRA_132_SOC)	+= clk-tegra124.o
>  obj-y					+= cvb.o
>  obj-$(CONFIG_ARCH_TEGRA_210_SOC)	+= clk-tegra210.o
> 

Cheers
Jon

-- 
nvpublic

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

* Re: [PATCH v2 5/6] clk: tegra: build clk-dfll.c for Tegra124 and Tegra210
@ 2018-01-31 11:03       ` Jon Hunter
  0 siblings, 0 replies; 56+ messages in thread
From: Jon Hunter @ 2018-01-31 11:03 UTC (permalink / raw)
  To: Peter De Schrijver, linux-tegra, linux-clk, mturquette, sboyd


On 24/01/18 12:45, Peter De Schrijver wrote:
> Tegra210 has a DFLL as well and can share the majority of the code with
> the Tegra124 implementation. So build the same code for both platforms.
> 
> Signed-off-by: Peter De Schrijver <pdeschrijver@nvidia.com>
> ---
>  drivers/clk/tegra/Kconfig  | 5 +++++
>  drivers/clk/tegra/Makefile | 2 +-
>  2 files changed, 6 insertions(+), 1 deletion(-)
> 
> diff --git a/drivers/clk/tegra/Kconfig b/drivers/clk/tegra/Kconfig
> index 7ddacae..57902ab 100644
> --- a/drivers/clk/tegra/Kconfig
> +++ b/drivers/clk/tegra/Kconfig
> @@ -5,3 +5,8 @@ config TEGRA_CLK_EMC
>  config CLK_TEGRA_BPMP
>  	def_bool y
>  	depends on TEGRA_BPMP
> +
> +config TEGRA_CLK_DFLL
> +	depends on (ARCH_TEGRA_124_SOC || ARCH_TEGRA_210_SOC)
> +	select PM_OPP
> +	def_bool y

Being picky, the description does not mention adding the dependency on
PM_OPP. However, I do see that the driver does use this and so seems
valid. Maybe update the description to clarify that this is needed.

BTW, I see that for ARM this is already selected by ARCH_TEGRA.

> diff --git a/drivers/clk/tegra/Makefile b/drivers/clk/tegra/Makefile
> index b716923..fc7f4b4 100644
> --- a/drivers/clk/tegra/Makefile
> +++ b/drivers/clk/tegra/Makefile
> @@ -19,7 +19,7 @@ obj-$(CONFIG_ARCH_TEGRA_2x_SOC)         += clk-tegra20.o
>  obj-$(CONFIG_ARCH_TEGRA_3x_SOC)         += clk-tegra30.o
>  obj-$(CONFIG_ARCH_TEGRA_114_SOC)	+= clk-tegra114.o
>  obj-$(CONFIG_ARCH_TEGRA_124_SOC)	+= clk-tegra124.o
> -obj-$(CONFIG_ARCH_TEGRA_124_SOC)	+= clk-tegra124-dfll-fcpu.o
> +obj-$(CONFIG_TEGRA_CLK_DFLL)		+= clk-tegra124-dfll-fcpu.o
>  obj-$(CONFIG_ARCH_TEGRA_132_SOC)	+= clk-tegra124.o
>  obj-y					+= cvb.o
>  obj-$(CONFIG_ARCH_TEGRA_210_SOC)	+= clk-tegra210.o
> 

Cheers
Jon

-- 
nvpublic

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

* Re: [PATCH v2 6/6] cpufreq: tegra124-cpufreq: extend to support Tegra210
  2018-01-24 12:45     ` Peter De Schrijver
@ 2018-01-31 11:06       ` Jon Hunter
  -1 siblings, 0 replies; 56+ messages in thread
From: Jon Hunter @ 2018-01-31 11:06 UTC (permalink / raw)
  To: Peter De Schrijver, linux-tegra, linux-clk, mturquette, sboyd


On 24/01/18 12:45, Peter De Schrijver wrote:
> Tegra210 has a very similar CPU clocking scheme than Tegra124. So add
> support in this driver. Also allow for the case where the CPU voltage is
> controlled directly by the DFLL rather than by a separate regulator object.
> 
> Signed-off-by: Peter De Schrijver <pdeschrijver@nvidia.com>
> ---
>  drivers/cpufreq/tegra124-cpufreq.c | 13 ++++++-------
>  1 file changed, 6 insertions(+), 7 deletions(-)
> 
> diff --git a/drivers/cpufreq/tegra124-cpufreq.c b/drivers/cpufreq/tegra124-cpufreq.c
> index 4353025..d631dc4 100644
> --- a/drivers/cpufreq/tegra124-cpufreq.c
> +++ b/drivers/cpufreq/tegra124-cpufreq.c
> @@ -64,7 +64,8 @@ static void tegra124_cpu_switch_to_pllx(struct tegra124_cpufreq_priv *priv)
>  {
>  	clk_set_parent(priv->cpu_clk, priv->pllp_clk);
>  	clk_disable_unprepare(priv->dfll_clk);
> -	regulator_sync_voltage(priv->vdd_cpu_reg);
> +	if (priv->vdd_cpu_reg)
> +		regulator_sync_voltage(priv->vdd_cpu_reg);
>  	clk_set_parent(priv->cpu_clk, priv->pllx_clk);
>  }
>  
> @@ -89,10 +90,8 @@ static int tegra124_cpufreq_probe(struct platform_device *pdev)
>  		return -ENODEV;
>  
>  	priv->vdd_cpu_reg = regulator_get(cpu_dev, "vdd-cpu");
> -	if (IS_ERR(priv->vdd_cpu_reg)) {
> -		ret = PTR_ERR(priv->vdd_cpu_reg);
> -		goto out_put_np;
> -	}
> +	if (IS_ERR(priv->vdd_cpu_reg))
> +		priv->vdd_cpu_reg = NULL;
>  

For Tegra124, don't we still want to return an error here?

>  	priv->cpu_clk = of_clk_get_by_name(np, "cpu_g");
>  	if (IS_ERR(priv->cpu_clk)) {
> @@ -148,7 +147,6 @@ static int tegra124_cpufreq_probe(struct platform_device *pdev)
>  	clk_put(priv->cpu_clk);
>  out_put_vdd_cpu_reg:
>  	regulator_put(priv->vdd_cpu_reg);
> -out_put_np:
>  	of_node_put(np);
>  
>  	return ret;
> @@ -181,7 +179,8 @@ static int __init tegra_cpufreq_init(void)
>  	int ret;
>  	struct platform_device *pdev;
>  
> -	if (!of_machine_is_compatible("nvidia,tegra124"))
> +	if (!(of_machine_is_compatible("nvidia,tegra124")
> +		|| of_machine_is_compatible("nvidia,tegra210")))
>  		return -ENODEV;
>  
>  	/*
> 

Cheers
Jon

-- 
nvpublic

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

* Re: [PATCH v2 6/6] cpufreq: tegra124-cpufreq: extend to support Tegra210
@ 2018-01-31 11:06       ` Jon Hunter
  0 siblings, 0 replies; 56+ messages in thread
From: Jon Hunter @ 2018-01-31 11:06 UTC (permalink / raw)
  To: Peter De Schrijver, linux-tegra, linux-clk, mturquette, sboyd


On 24/01/18 12:45, Peter De Schrijver wrote:
> Tegra210 has a very similar CPU clocking scheme than Tegra124. So add
> support in this driver. Also allow for the case where the CPU voltage is
> controlled directly by the DFLL rather than by a separate regulator object.
> 
> Signed-off-by: Peter De Schrijver <pdeschrijver@nvidia.com>
> ---
>  drivers/cpufreq/tegra124-cpufreq.c | 13 ++++++-------
>  1 file changed, 6 insertions(+), 7 deletions(-)
> 
> diff --git a/drivers/cpufreq/tegra124-cpufreq.c b/drivers/cpufreq/tegra124-cpufreq.c
> index 4353025..d631dc4 100644
> --- a/drivers/cpufreq/tegra124-cpufreq.c
> +++ b/drivers/cpufreq/tegra124-cpufreq.c
> @@ -64,7 +64,8 @@ static void tegra124_cpu_switch_to_pllx(struct tegra124_cpufreq_priv *priv)
>  {
>  	clk_set_parent(priv->cpu_clk, priv->pllp_clk);
>  	clk_disable_unprepare(priv->dfll_clk);
> -	regulator_sync_voltage(priv->vdd_cpu_reg);
> +	if (priv->vdd_cpu_reg)
> +		regulator_sync_voltage(priv->vdd_cpu_reg);
>  	clk_set_parent(priv->cpu_clk, priv->pllx_clk);
>  }
>  
> @@ -89,10 +90,8 @@ static int tegra124_cpufreq_probe(struct platform_device *pdev)
>  		return -ENODEV;
>  
>  	priv->vdd_cpu_reg = regulator_get(cpu_dev, "vdd-cpu");
> -	if (IS_ERR(priv->vdd_cpu_reg)) {
> -		ret = PTR_ERR(priv->vdd_cpu_reg);
> -		goto out_put_np;
> -	}
> +	if (IS_ERR(priv->vdd_cpu_reg))
> +		priv->vdd_cpu_reg = NULL;
>  

For Tegra124, don't we still want to return an error here?

>  	priv->cpu_clk = of_clk_get_by_name(np, "cpu_g");
>  	if (IS_ERR(priv->cpu_clk)) {
> @@ -148,7 +147,6 @@ static int tegra124_cpufreq_probe(struct platform_device *pdev)
>  	clk_put(priv->cpu_clk);
>  out_put_vdd_cpu_reg:
>  	regulator_put(priv->vdd_cpu_reg);
> -out_put_np:
>  	of_node_put(np);
>  
>  	return ret;
> @@ -181,7 +179,8 @@ static int __init tegra_cpufreq_init(void)
>  	int ret;
>  	struct platform_device *pdev;
>  
> -	if (!of_machine_is_compatible("nvidia,tegra124"))
> +	if (!(of_machine_is_compatible("nvidia,tegra124")
> +		|| of_machine_is_compatible("nvidia,tegra210")))
>  		return -ENODEV;
>  
>  	/*
> 

Cheers
Jon

-- 
nvpublic

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

* Re: [PATCH v2 4/6] clk: tegra: dfll: support PWM regulator control
  2018-01-24 12:45   ` Peter De Schrijver
@ 2018-01-31 11:26     ` Jon Hunter
  -1 siblings, 0 replies; 56+ messages in thread
From: Jon Hunter @ 2018-01-31 11:26 UTC (permalink / raw)
  To: Peter De Schrijver, linux-tegra, linux-clk, mturquette, sboyd


On 24/01/18 12:45, Peter De Schrijver wrote:
> The DFLL can directly generate a PWM signal to control the regulator IC
> rather then sending i2c messages. This patch adds support for this mode.
> In this mode the LUT is not used and also there is no regulator object
> involved because there is no way to control the regulator voltage without
> also changing the DFLL output frequency. Also the register debugfs file
> is slightly reworked to only show the i2c registers when i2c mode is in
> use.
> 
> Signed-off-by: Peter De Schrijver <pdeschrijver@nvidia.com>
> ---
>  drivers/clk/tegra/clk-dfll.c | 481 ++++++++++++++++++++++++++++++++++++-------
>  drivers/clk/tegra/clk-dfll.h |   7 +
>  2 files changed, 410 insertions(+), 78 deletions(-)
> 
> diff --git a/drivers/clk/tegra/clk-dfll.c b/drivers/clk/tegra/clk-dfll.c
> index 0a7deee..204717c 100644
> --- a/drivers/clk/tegra/clk-dfll.c
> +++ b/drivers/clk/tegra/clk-dfll.c
> @@ -243,6 +243,12 @@ enum dfll_tune_range {
>  	DFLL_TUNE_LOW = 1,
>  };
>  
> +
> +enum tegra_dfll_pmu_if {
> +	TEGRA_DFLL_PMU_I2C = 0,
> +	TEGRA_DFLL_PMU_PWM = 1,
> +};
> +
>  /**
>   * struct dfll_rate_req - target DFLL rate request data
>   * @rate: target frequency, after the postscaling
> @@ -292,18 +298,27 @@ struct tegra_dfll {
>  	u32				force_mode;
>  	u32				cf;
>  	u32				ci;
> -	u32				cg;
> +	s32				cg;

Why has the type been changed?

>  	bool				cg_scale;
> +	u32				reg_init_uV;
>  
>  	/* I2C interface parameters */
>  	u32				i2c_fs_rate;
>  	u32				i2c_reg;
>  	u32				i2c_slave_addr;
>  
> -	/* i2c_lut array entries are regulator framework selectors */
> -	unsigned			i2c_lut[MAX_DFLL_VOLTAGES];
> -	int				i2c_lut_size;
> -	u8				lut_min, lut_max, lut_safe;
> +	/* lut array entries are regulator framework selectors or PWM values*/
> +	unsigned int			lut[MAX_DFLL_VOLTAGES];
> +	unsigned int			lut_uv[MAX_DFLL_VOLTAGES];
> +	int				lut_size;
> +	u8				lut_bottom, lut_min, lut_max, lut_safe;

You say the LUT is not used for PWM, but you are changing the name here
and it appears to be used to some degree in PWM and so I am a bit
confused here.

> +
> +	/* PWM interface */
> +	enum tegra_dfll_pmu_if		pmu_if;
> +	unsigned long			pwm_rate;
> +	struct pinctrl			*pwm_pin;
> +	struct pinctrl_state		*pwm_enable_state;
> +	struct pinctrl_state		*pwm_disable_state;
>  };
>  
>  #define clk_hw_to_dfll(_hw) container_of(_hw, struct tegra_dfll, dfll_clk_hw)
> @@ -490,6 +505,36 @@ static void dfll_set_mode(struct tegra_dfll *td,
>  }
>  
>  /*
> + * DVCO rate control
> + */
> +
> +static unsigned long get_dvco_rate_below(struct tegra_dfll *td, u8 out_min)
> +{
> +	struct dev_pm_opp *opp;
> +	unsigned long rate, prev_rate;
> +	int uv, min_uv;
> +
> +	min_uv = td->lut_uv[out_min];
> +	for (rate = 0, prev_rate = 0; ; rate++) {
> +		rcu_read_lock();
> +		opp = dev_pm_opp_find_freq_ceil(td->soc->dev, &rate);
> +		if (IS_ERR(opp)) {
> +			rcu_read_unlock();
> +			break;

'prev_rate' could be uninitialised here and so you may return a bogus value.

> +		}
> +		uv = dev_pm_opp_get_voltage(opp);
> +		rcu_read_unlock();
> +
> +		if (uv && uv > min_uv)
> +			return prev_rate;
> +
> +		prev_rate = rate;
> +	}
> +
> +	return prev_rate;
> +}
> +
> +/*
>   * DFLL-to-I2C controller interface
>   */
>  
> @@ -518,6 +563,118 @@ static int dfll_i2c_set_output_enabled(struct tegra_dfll *td, bool enable)
>  	return 0;
>  }
>  
> +
> +/*
> + * DFLL-to-PWM controller interface
> + */
> +
> +/**
> + * dfll_pwm_set_output_enabled - enable/disable PWM voltage requests
> + * @td: DFLL instance
> + * @enable: whether to enable or disable the PWM voltage requests
> + *
> + * Set the master enable control for PWM control value updates. If disabled,
> + * then the PWM signal is not driven. Also configure the PWM output pad
> + * to the appropriate state.
> + */
> +static int dfll_pwm_set_output_enabled(struct tegra_dfll *td, bool enable)
> +{
> +	int ret;
> +	u32 val, div;
> +
> +	if (enable) {
> +		ret = pinctrl_select_state(td->pwm_pin, td->pwm_enable_state);
> +		if (ret < 0) {
> +			dev_err(td->dev, "setting enable state failed\n");
> +			return -EINVAL;

Why not return ret?

> +		}
> +		val = dfll_readl(td, DFLL_OUTPUT_CFG);
> +		val &= ~DFLL_OUTPUT_CFG_PWM_DIV_MASK;
> +		div = DIV_ROUND_UP(td->ref_rate, td->pwm_rate);
> +		val |= (div << DFLL_OUTPUT_CFG_PWM_DIV_SHIFT)
> +				& DFLL_OUTPUT_CFG_PWM_DIV_MASK;
> +		dfll_writel(td, val, DFLL_OUTPUT_CFG);
> +		dfll_wmb(td);
> +
> +		val |= DFLL_OUTPUT_CFG_PWM_ENABLE;
> +		dfll_writel(td, val, DFLL_OUTPUT_CFG);
> +		dfll_wmb(td);
> +	} else {
> +		ret = pinctrl_select_state(td->pwm_pin, td->pwm_disable_state);
> +		if (ret < 0)
> +			dev_warn(td->dev, "setting disable state failed\n");
> +
> +		val = dfll_readl(td, DFLL_OUTPUT_CFG);
> +		val &= ~DFLL_OUTPUT_CFG_PWM_ENABLE;
> +		dfll_writel(td, val, DFLL_OUTPUT_CFG);
> +		dfll_wmb(td);
> +	}
> +
> +	return 0;
> +}
> +/**
> + * dfll_set_force_output_value - set fixed value for force output
> + * @td: DFLL instance
> + * @out_val: value to force output
> + *
> + * Set the fixec value for force output, DFLL will output this value when

s/fixec/fixed/

> + * force output is enabled.
> + */
> +static u32 dfll_set_force_output_value(struct tegra_dfll *td, u8 out_val)
> +{
> +	u32 val = dfll_readl(td, DFLL_OUTPUT_FORCE);
> +
> +	val = (val & DFLL_OUTPUT_FORCE_ENABLE) | (out_val & OUT_MASK);

Don't you need to clear the OUT_MASK bits first?

> +	dfll_writel(td, val, DFLL_OUTPUT_FORCE);
> +	dfll_wmb(td);
> +
> +	return dfll_readl(td, DFLL_OUTPUT_FORCE);
> +}
> +
> +/**
> + * dfll_set_force_output_enabled - enable/disable force output
> + * @td: DFLL instance
> + * @enable: whether to enable or disable the force output
> + *
> + * Set the enable control for fouce output with fixed value.
> + */
> +static void dfll_set_force_output_enabled(struct tegra_dfll *td, bool enable)
> +{
> +	u32 val = dfll_readl(td, DFLL_OUTPUT_FORCE);
> +
> +	if (enable)
> +		val |= DFLL_OUTPUT_FORCE_ENABLE;
> +	else
> +		val &= ~DFLL_OUTPUT_FORCE_ENABLE;
> +
> +	dfll_writel(td, val, DFLL_OUTPUT_FORCE);
> +	dfll_wmb(td);
> +}
> +
> +/**
> + * dfll_i2c_set_output_enabled - enable/disable I2C PMIC voltage requests
> + * @td: DFLL instance
> + * @enable: whether to enable or disable the I2C voltage requests
> + *
> + * Set the master enable control for I2C control value updates. If disabled,
> + * then I2C control messages are inhibited, regardless of the DFLL mode.
> + */
> +static int dfll_force_output(struct tegra_dfll *td, unsigned int out_sel)
> +{
> +	u32 val;
> +
> +	if (out_sel > OUT_MASK)
> +		return -EINVAL;
> +
> +	val = dfll_set_force_output_value(td, out_sel);
> +	if ((td->mode < DFLL_CLOSED_LOOP) &&
> +	    !(val & DFLL_OUTPUT_FORCE_ENABLE)) {
> +		dfll_set_force_output_enabled(td, true);
> +	}
> +
> +	return 0;
> +}
> +
>  /**
>   * dfll_load_lut - load the voltage lookup table
>   * @td: struct tegra_dfll *
> @@ -531,15 +688,15 @@ static void dfll_load_i2c_lut(struct tegra_dfll *td)
>  	u32 val;
>  
>  	for (i = 0; i < MAX_DFLL_VOLTAGES; i++) {
> -		if (i < td->lut_min)
> -			lut_index = td->lut_min;
> -		else if (i > td->lut_max)
> -			lut_index = td->lut_max;
> +		if (i < td->lut_bottom)
> +			lut_index = td->lut_bottom;
> +		else if (i > td->lut_size - 1)
> +			lut_index = td->lut_size - 1;
>  		else
>  			lut_index = i;
>  
>  		val = regulator_list_hardware_vsel(td->vdd_reg,
> -						     td->i2c_lut[lut_index]);
> +						     td->lut[lut_index]);
>  		__raw_writel(val, td->lut_base + i * 4);
>  	}
>  
> @@ -595,23 +752,53 @@ static void dfll_init_out_if(struct tegra_dfll *td)
>  	u32 val;
>  
>  	td->lut_min = 0;
> -	td->lut_max = td->i2c_lut_size - 1;
> -	td->lut_safe = td->lut_min + 1;
> -
> -	dfll_i2c_writel(td, 0, DFLL_OUTPUT_CFG);
> -	val = (td->lut_safe << DFLL_OUTPUT_CFG_SAFE_SHIFT) |
> -		(td->lut_max << DFLL_OUTPUT_CFG_MAX_SHIFT) |
> -		(td->lut_min << DFLL_OUTPUT_CFG_MIN_SHIFT);
> -	dfll_i2c_writel(td, val, DFLL_OUTPUT_CFG);
> -	dfll_i2c_wmb(td);
> -
> -	dfll_writel(td, 0, DFLL_OUTPUT_FORCE);
> -	dfll_i2c_writel(td, 0, DFLL_INTR_EN);
> -	dfll_i2c_writel(td, DFLL_INTR_MAX_MASK | DFLL_INTR_MIN_MASK,
> -			DFLL_INTR_STS);
> -
> -	dfll_load_i2c_lut(td);
> -	dfll_init_i2c_if(td);
> +	td->lut_max = td->lut_size - 1;
> +	td->lut_safe = td->lut_min + (td->lut_min < td->lut_max ? 1 : 0);
> +
> +	if (td->pmu_if == TEGRA_DFLL_PMU_PWM) {
> +		int vinit = td->reg_init_uV;
> +		int vstep = td->soc->alignment.step_uv;
> +		int vmin = td->lut_uv[0];
> +
> +		/* clear DFLL_OUTPUT_CFG before setting new value */
> +		dfll_writel(td, 0, DFLL_OUTPUT_CFG);
> +		dfll_wmb(td);
> +
> +		val = dfll_readl(td, DFLL_OUTPUT_CFG);
> +		val |= (td->lut_safe << DFLL_OUTPUT_CFG_SAFE_SHIFT) |
> +		       (td->lut_max << DFLL_OUTPUT_CFG_MAX_SHIFT) |
> +		       (td->lut_min << DFLL_OUTPUT_CFG_MIN_SHIFT);
> +		dfll_writel(td, val, DFLL_OUTPUT_CFG);
> +		dfll_wmb(td);
> +
> +		dfll_writel(td, 0, DFLL_OUTPUT_FORCE);
> +		dfll_i2c_writel(td, 0, DFLL_INTR_EN);
> +		dfll_i2c_writel(td, DFLL_INTR_MAX_MASK | DFLL_INTR_MIN_MASK,
> +				DFLL_INTR_STS);
> +
> +		/* set initial voltage */
> +		if ((vinit >= vmin) && vstep) {
> +			unsigned int vsel;
> +
> +			vsel = DIV_ROUND_UP((vinit - vmin), vstep);
> +			dfll_force_output(td, vsel);
> +		}
> +	} else {
> +		dfll_i2c_writel(td, 0, DFLL_OUTPUT_CFG);
> +		val = (td->lut_safe << DFLL_OUTPUT_CFG_SAFE_SHIFT) |
> +			(td->lut_max << DFLL_OUTPUT_CFG_MAX_SHIFT) |
> +			(td->lut_min << DFLL_OUTPUT_CFG_MIN_SHIFT);
> +		dfll_i2c_writel(td, val, DFLL_OUTPUT_CFG);
> +		dfll_i2c_wmb(td);
> +
> +		dfll_writel(td, 0, DFLL_OUTPUT_FORCE);
> +		dfll_i2c_writel(td, 0, DFLL_INTR_EN);
> +		dfll_i2c_writel(td, DFLL_INTR_MAX_MASK | DFLL_INTR_MIN_MASK,
> +				DFLL_INTR_STS);
> +
> +		dfll_load_i2c_lut(td);
> +		dfll_init_i2c_if(td);
> +	}
>  }
>  
>  /*
> @@ -637,11 +824,12 @@ static int find_lut_index_for_rate(struct tegra_dfll *td, unsigned long rate)
>  	if (IS_ERR(opp))
>  		return PTR_ERR(opp);
>  
> -	uv = dev_pm_opp_get_voltage(opp);
> +	uv = dev_pm_opp_get_voltage(opp) / td->soc->alignment.step_uv;
> +
>  	dev_pm_opp_put(opp);
>  
> -	for (i = 0; i < td->i2c_lut_size; i++) {
> -		if (regulator_list_voltage(td->vdd_reg, td->i2c_lut[i]) == uv)
> +	for (i = td->lut_bottom; i < td->lut_size; i++) {
> +		if ((td->lut_uv[i] / td->soc->alignment.step_uv) >= uv)
>  			return i;
>  	}
>  
> @@ -863,9 +1051,14 @@ static int dfll_lock(struct tegra_dfll *td)
>  			return -EINVAL;
>  		}
>  
> -		dfll_i2c_set_output_enabled(td, true);
> +		if (td->pmu_if == TEGRA_DFLL_PMU_PWM)
> +			dfll_pwm_set_output_enabled(td, true);
> +		else
> +			dfll_i2c_set_output_enabled(td, true);
> +
>  		dfll_set_mode(td, DFLL_CLOSED_LOOP);
>  		dfll_set_frequency_request(td, req);
> +		dfll_set_force_output_enabled(td, false);
>  		return 0;
>  
>  	default:
> @@ -889,7 +1082,10 @@ static int dfll_unlock(struct tegra_dfll *td)
>  	case DFLL_CLOSED_LOOP:
>  		dfll_set_open_loop_config(td);
>  		dfll_set_mode(td, DFLL_OPEN_LOOP);
> -		dfll_i2c_set_output_enabled(td, false);
> +		if (td->pmu_if == TEGRA_DFLL_PMU_PWM)
> +			dfll_pwm_set_output_enabled(td, false);
> +		else
> +			dfll_i2c_set_output_enabled(td, false);
>  		return 0;
>  
>  	case DFLL_OPEN_LOOP:
> @@ -1171,15 +1367,17 @@ static int attr_registers_show(struct seq_file *s, void *data)
>  		seq_printf(s, "[0x%02x] = 0x%08x\n", offs,
>  			   dfll_i2c_readl(td, offs));
>  
> -	seq_puts(s, "\nINTEGRATED I2C CONTROLLER REGISTERS:\n");
> -	offs = DFLL_I2C_CLK_DIVISOR;
> -	seq_printf(s, "[0x%02x] = 0x%08x\n", offs,
> -		   __raw_readl(td->i2c_controller_base + offs));
> -
> -	seq_puts(s, "\nLUT:\n");
> -	for (offs = 0; offs <  4 * MAX_DFLL_VOLTAGES; offs += 4)
> +	if (td->pmu_if == TEGRA_DFLL_PMU_I2C) {
> +		seq_puts(s, "\nINTEGRATED I2C CONTROLLER REGISTERS:\n");
> +		offs = DFLL_I2C_CLK_DIVISOR;
>  		seq_printf(s, "[0x%02x] = 0x%08x\n", offs,
> -			   __raw_readl(td->lut_base + offs));
> +			   __raw_readl(td->i2c_controller_base + offs));
> +
> +		seq_puts(s, "\nLUT:\n");
> +		for (offs = 0; offs <  4 * MAX_DFLL_VOLTAGES; offs += 4)
> +			seq_printf(s, "[0x%02x] = 0x%08x\n", offs,
> +				   __raw_readl(td->lut_base + offs));
> +	}
>  
>  	return 0;
>  }
> @@ -1377,15 +1575,17 @@ static int dfll_init(struct tegra_dfll *td)
>   */
>  static int find_vdd_map_entry_exact(struct tegra_dfll *td, int uV)
>  {
> -	int i, n_voltages, reg_uV;
> +	int i, n_voltages, reg_mult, align_mult;
>  
> +	align_mult = uV / td->soc->alignment.step_uv;
>  	n_voltages = regulator_count_voltages(td->vdd_reg);
>  	for (i = 0; i < n_voltages; i++) {
> -		reg_uV = regulator_list_voltage(td->vdd_reg, i);
> -		if (reg_uV < 0)
> +		reg_mult = regulator_list_voltage(td->vdd_reg, i) /
> +					td->soc->alignment.step_uv;
> +		if (reg_mult < 0)
>  			break;
>  
> -		if (uV == reg_uV)
> +		if (align_mult == reg_mult)
>  			return i;
>  	}
>  
> @@ -1399,15 +1599,17 @@ static int find_vdd_map_entry_exact(struct tegra_dfll *td, int uV)
>   * */
>  static int find_vdd_map_entry_min(struct tegra_dfll *td, int uV)
>  {
> -	int i, n_voltages, reg_uV;
> +	int i, n_voltages, reg_mult, align_mult;
>  
> +	align_mult = uV / td->soc->alignment.step_uv;
>  	n_voltages = regulator_count_voltages(td->vdd_reg);
>  	for (i = 0; i < n_voltages; i++) {
> -		reg_uV = regulator_list_voltage(td->vdd_reg, i);
> -		if (reg_uV < 0)
> +		reg_mult = regulator_list_voltage(td->vdd_reg, i) /
> +					td->soc->alignment.step_uv;
> +		if (reg_mult < 0)
>  			break;
>  
> -		if (uV <= reg_uV)
> +		if (align_mult <= reg_mult)
>  			return i;
>  	}
>  
> @@ -1415,6 +1617,53 @@ static int find_vdd_map_entry_min(struct tegra_dfll *td, int uV)
>  	return -EINVAL;
>  }
>  
> +/*
> + * Look-up table in h/w is ignored when PWM is used as DFLL interface to PMIC.
> + * In this case closed loop output is controlling duty cycle directly. The s/w
> + * look-up that maps PWM duty cycle to voltage is still built by this function.
> + */
> +static int dfll_build_lut_pwm(struct tegra_dfll *td, int v_max)
> +{
> +	int i, reg_volt;
> +	unsigned long rate;
> +	u8 lut_bottom = MAX_DFLL_VOLTAGES;
> +	int v_min = td->soc->cvb->min_millivolts * 1000;
> +
> +	for (i = 0; i < MAX_DFLL_VOLTAGES; i++) {
> +		reg_volt = td->lut_uv[i];
> +
> +		/* since opp voltage is exact mv */
> +		reg_volt = (reg_volt / 1000) * 1000;
> +		if (reg_volt > v_max)
> +			break;
> +
> +		td->lut[i] = i;
> +		if ((lut_bottom == MAX_DFLL_VOLTAGES) && (reg_volt >= v_min))
> +			lut_bottom = i;
> +	}
> +
> +	/* determine voltage boundaries */
> +	td->lut_size = i;
> +	if ((lut_bottom == MAX_DFLL_VOLTAGES) ||
> +	    (lut_bottom + 1 >= td->lut_size)) {
> +		dev_err(td->dev, "no voltage above DFLL minimum %d mV\n",
> +			td->soc->cvb->min_millivolts);
> +		return -EINVAL;
> +	}
> +	td->lut_bottom = lut_bottom;
> +
> +	/* determine rate boundaries */
> +	rate = get_dvco_rate_below(td, td->lut_bottom);
> +	if (!rate) {
> +		dev_err(td->dev, "no opp below DFLL minimum voltage %d mV\n",
> +			td->soc->cvb->min_millivolts);
> +		return -EINVAL;
> +	}
> +	td->dvco_rate_min = rate;
> +
> +	return 0;
> +}
> +
>  /**
>   * dfll_build_i2c_lut - build the I2C voltage register lookup table
>   * @td: DFLL instance
> @@ -1427,31 +1676,24 @@ static int find_vdd_map_entry_min(struct tegra_dfll *td, int uV)
>   *
>   * On success, fills in td->i2c_lut and returns 0, or -err on failure.
>   */
> -static int dfll_build_i2c_lut(struct tegra_dfll *td)
> +static int dfll_build_i2c_lut(struct tegra_dfll *td, int v_max)
>  {
> +	unsigned long rate;
>  	int ret = -EINVAL;
> -	int j, v, v_max, v_opp;
> +	int j, v, v_opp;
>  	int selector;
> -	unsigned long rate;
> -	struct dev_pm_opp *opp;
>  	int lut;
>  
> -	rate = ULONG_MAX;
> -	opp = dev_pm_opp_find_freq_floor(td->soc->dev, &rate);
> -	if (IS_ERR(opp)) {
> -		dev_err(td->dev, "couldn't get vmax opp, empty opp table?\n");
> -		goto out;
> -	}
> -	v_max = dev_pm_opp_get_voltage(opp);
> -	dev_pm_opp_put(opp);
> -
>  	v = td->soc->cvb->min_millivolts * 1000;
>  	lut = find_vdd_map_entry_exact(td, v);
>  	if (lut < 0)
>  		goto out;
> -	td->i2c_lut[0] = lut;
> +	td->lut[0] = lut;
> +	td->lut_bottom = 0;
>  
>  	for (j = 1, rate = 0; ; rate++) {
> +		struct dev_pm_opp *opp;
> +
>  		opp = dev_pm_opp_find_freq_ceil(td->soc->dev, &rate);
>  		if (IS_ERR(opp))
>  			break;
> @@ -1470,32 +1712,61 @@ static int dfll_build_i2c_lut(struct tegra_dfll *td)
>  			selector = find_vdd_map_entry_min(td, v);
>  			if (selector < 0)
>  				goto out;
> -			if (selector != td->i2c_lut[j - 1])
> -				td->i2c_lut[j++] = selector;
> +			if (selector != td->lut[j - 1])
> +				td->lut[j++] = selector;
>  		}
>  
>  		v = (j == MAX_DFLL_VOLTAGES - 1) ? v_max : v_opp;
>  		selector = find_vdd_map_entry_exact(td, v);
>  		if (selector < 0)
>  			goto out;
> -		if (selector != td->i2c_lut[j - 1])
> -			td->i2c_lut[j++] = selector;
> +		if (selector != td->lut[j - 1])
> +			td->lut[j++] = selector;
>  
>  		if (v >= v_max)
>  			break;
>  	}
> -	td->i2c_lut_size = j;
> +	td->lut_size = j;
>  
>  	if (!td->dvco_rate_min)
>  		dev_err(td->dev, "no opp above DFLL minimum voltage %d mV\n",
>  			td->soc->cvb->min_millivolts);
> -	else
> +	else {
>  		ret = 0;
> +		for (j = 0; j < td->lut_size; j++)
> +			td->lut_uv[j] =
> +				regulator_list_voltage(td->vdd_reg,
> +						       td->lut[j]);
> +	}
>  
>  out:
>  	return ret;
>  }
>  
> +static int dfll_build_lut(struct tegra_dfll *td)
> +{
> +	unsigned long rate;
> +	struct dev_pm_opp *opp;
> +	int v_max;
> +
> +	rcu_read_lock();
> +
> +	rate = ULONG_MAX;
> +	opp = dev_pm_opp_find_freq_floor(td->soc->dev, &rate);
> +	if (IS_ERR(opp)) {
> +		dev_err(td->dev, "couldn't get vmax opp, empty opp table?\n");
> +		return -EINVAL;
> +	}
> +	v_max = dev_pm_opp_get_voltage(opp);
> +
> +	rcu_read_unlock();
> +
> +	if (td->pmu_if == TEGRA_DFLL_PMU_PWM)
> +		return dfll_build_lut_pwm(td, v_max);
> +	else
> +		return dfll_build_i2c_lut(td, v_max);
> +}
> +
>  /**
>   * read_dt_param - helper function for reading required parameters from the DT
>   * @td: DFLL instance
> @@ -1554,12 +1825,55 @@ static int dfll_fetch_i2c_params(struct tegra_dfll *td)
>  	}
>  	td->i2c_reg = vsel_reg;
>  
> -	ret = dfll_build_i2c_lut(td);
> -	if (ret) {
> -		dev_err(td->dev, "couldn't build I2C LUT\n");
> +	return 0;
> +}
> +
> +static int dfll_fetch_pwm_params(struct tegra_dfll *td)
> +{
> +	int ret, i;
> +	u32 pwm_period;
> +
> +	if (!td->soc->alignment.step_uv || !td->soc->alignment.offset_uv) {
> +		dev_err(td->dev, "Missing step or alignment info for PWM regulator");
> +		return -EINVAL;
> +	}
> +	for (i = 0; i < MAX_DFLL_VOLTAGES; i++)
> +		td->lut_uv[i] = td->soc->alignment.offset_uv +
> +				i * td->soc->alignment.step_uv;
> +
> +	ret = read_dt_param(td, "nvidia,init-uv", &td->reg_init_uV);
> +	if (!ret) {
> +		dev_err(td->dev, "couldn't get initialized voltage\n");
>  		return ret;
>  	}
>  
> +	ret = read_dt_param(td, "nvidia,pwm-period", &pwm_period);
> +	if (!ret) {
> +		dev_err(td->dev, "couldn't get PWM period\n");
> +		return ret;
> +	}
> +	td->pwm_rate = (NSEC_PER_SEC / pwm_period) * (MAX_DFLL_VOLTAGES - 1);
> +
> +	td->pwm_pin = devm_pinctrl_get(td->dev);
> +	if (IS_ERR(td->pwm_pin)) {
> +		dev_err(td->dev, "DT: missing pinctrl device\n");
> +		return PTR_ERR(td->pwm_pin);
> +	}
> +
> +	td->pwm_enable_state = pinctrl_lookup_state(td->pwm_pin,
> +						    "dvfs_pwm_enable");
> +	if (IS_ERR(td->pwm_enable_state)) {
> +		dev_err(td->dev, "DT: missing pwm enabled state\n");
> +		return PTR_ERR(td->pwm_enable_state);
> +	}
> +
> +	td->pwm_disable_state = pinctrl_lookup_state(td->pwm_pin,
> +						     "dvfs_pwm_disable");
> +	if (IS_ERR(td->pwm_disable_state)) {
> +		dev_err(td->dev, "DT: missing pwm disabled state\n");
> +		return PTR_ERR(td->pwm_disable_state);
> +	}
> +
>  	return 0;
>  }
>  
> @@ -1625,12 +1939,6 @@ int tegra_dfll_register(struct platform_device *pdev,
>  
>  	td->soc = soc;
>  
> -	td->vdd_reg = devm_regulator_get(td->dev, "vdd-cpu");
> -	if (IS_ERR(td->vdd_reg)) {
> -		dev_err(td->dev, "couldn't get vdd_cpu regulator\n");
> -		return PTR_ERR(td->vdd_reg);
> -	}
> -
>  	td->dvco_rst = devm_reset_control_get(td->dev, "dvco");
>  	if (IS_ERR(td->dvco_rst)) {
>  		dev_err(td->dev, "couldn't get dvco reset\n");
> @@ -1643,10 +1951,27 @@ int tegra_dfll_register(struct platform_device *pdev,
>  		return ret;
>  	}
>  
> -	ret = dfll_fetch_i2c_params(td);
> +	if (of_property_read_bool(td->dev->of_node, "nvidia,pwm-to-pmic")) {
> +		td->pmu_if = TEGRA_DFLL_PMU_PWM;
> +		ret = dfll_fetch_pwm_params(td);
> +	} else  {
> +		td->vdd_reg = devm_regulator_get(td->dev, "vdd-cpu");
> +		if (IS_ERR(td->vdd_reg)) {
> +			dev_err(td->dev, "couldn't get vdd_cpu regulator\n");
> +			return PTR_ERR(td->vdd_reg);
> +		}
> +		td->pmu_if = TEGRA_DFLL_PMU_I2C;
> +		ret = dfll_fetch_i2c_params(td);
> +	}
>  	if (ret)
>  		return ret;
>  
> +	ret = dfll_build_lut(td);
> +	if (ret) {
> +		dev_err(td->dev, "couldn't build LUT\n");
> +		return ret;
> +	}
> +
>  	mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
>  	if (!mem) {
>  		dev_err(td->dev, "no control register resource\n");
> diff --git a/drivers/clk/tegra/clk-dfll.h b/drivers/clk/tegra/clk-dfll.h
> index 83352c8..228f54a 100644
> --- a/drivers/clk/tegra/clk-dfll.h
> +++ b/drivers/clk/tegra/clk-dfll.h
> @@ -21,6 +21,7 @@
>  #include <linux/platform_device.h>
>  #include <linux/reset.h>
>  #include <linux/types.h>
> +#include "cvb.h"
>  
>  /**
>   * struct tegra_dfll_soc_data - SoC-specific hooks/integration for the DFLL driver
> @@ -35,6 +36,12 @@ struct tegra_dfll_soc_data {
>  	struct device *dev;
>  	unsigned long max_freq;
>  	const struct cvb_table *cvb;
> +	struct rail_alignment alignment;
> +	unsigned int min_millivolts;
> +	unsigned int tune_high_min_millivolts;
> +	u32 tune0_low;
> +	u32 tune0_high;
> +	u32 tune1;
>  
>  	void (*init_clock_trimmers)(void);
>  	void (*set_clock_trimmers_high)(void);
> 

Overall this is quite a big change. I wonder if it is worth splitting
into a preparatory patch (with all the renaming, etc) for the existing
i2c mode and then add the pwm support. It is hard to see how the
existing i2c code has been changed.

Cheers
Jon

-- 
nvpublic

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

* Re: [PATCH v2 4/6] clk: tegra: dfll: support PWM regulator control
@ 2018-01-31 11:26     ` Jon Hunter
  0 siblings, 0 replies; 56+ messages in thread
From: Jon Hunter @ 2018-01-31 11:26 UTC (permalink / raw)
  To: Peter De Schrijver, linux-tegra, linux-clk, mturquette, sboyd


On 24/01/18 12:45, Peter De Schrijver wrote:
> The DFLL can directly generate a PWM signal to control the regulator IC
> rather then sending i2c messages. This patch adds support for this mode.
> In this mode the LUT is not used and also there is no regulator object
> involved because there is no way to control the regulator voltage without
> also changing the DFLL output frequency. Also the register debugfs file
> is slightly reworked to only show the i2c registers when i2c mode is in
> use.
> 
> Signed-off-by: Peter De Schrijver <pdeschrijver@nvidia.com>
> ---
>  drivers/clk/tegra/clk-dfll.c | 481 ++++++++++++++++++++++++++++++++++++-------
>  drivers/clk/tegra/clk-dfll.h |   7 +
>  2 files changed, 410 insertions(+), 78 deletions(-)
> 
> diff --git a/drivers/clk/tegra/clk-dfll.c b/drivers/clk/tegra/clk-dfll.c
> index 0a7deee..204717c 100644
> --- a/drivers/clk/tegra/clk-dfll.c
> +++ b/drivers/clk/tegra/clk-dfll.c
> @@ -243,6 +243,12 @@ enum dfll_tune_range {
>  	DFLL_TUNE_LOW = 1,
>  };
>  
> +
> +enum tegra_dfll_pmu_if {
> +	TEGRA_DFLL_PMU_I2C = 0,
> +	TEGRA_DFLL_PMU_PWM = 1,
> +};
> +
>  /**
>   * struct dfll_rate_req - target DFLL rate request data
>   * @rate: target frequency, after the postscaling
> @@ -292,18 +298,27 @@ struct tegra_dfll {
>  	u32				force_mode;
>  	u32				cf;
>  	u32				ci;
> -	u32				cg;
> +	s32				cg;

Why has the type been changed?

>  	bool				cg_scale;
> +	u32				reg_init_uV;
>  
>  	/* I2C interface parameters */
>  	u32				i2c_fs_rate;
>  	u32				i2c_reg;
>  	u32				i2c_slave_addr;
>  
> -	/* i2c_lut array entries are regulator framework selectors */
> -	unsigned			i2c_lut[MAX_DFLL_VOLTAGES];
> -	int				i2c_lut_size;
> -	u8				lut_min, lut_max, lut_safe;
> +	/* lut array entries are regulator framework selectors or PWM values*/
> +	unsigned int			lut[MAX_DFLL_VOLTAGES];
> +	unsigned int			lut_uv[MAX_DFLL_VOLTAGES];
> +	int				lut_size;
> +	u8				lut_bottom, lut_min, lut_max, lut_safe;

You say the LUT is not used for PWM, but you are changing the name here
and it appears to be used to some degree in PWM and so I am a bit
confused here.

> +
> +	/* PWM interface */
> +	enum tegra_dfll_pmu_if		pmu_if;
> +	unsigned long			pwm_rate;
> +	struct pinctrl			*pwm_pin;
> +	struct pinctrl_state		*pwm_enable_state;
> +	struct pinctrl_state		*pwm_disable_state;
>  };
>  
>  #define clk_hw_to_dfll(_hw) container_of(_hw, struct tegra_dfll, dfll_clk_hw)
> @@ -490,6 +505,36 @@ static void dfll_set_mode(struct tegra_dfll *td,
>  }
>  
>  /*
> + * DVCO rate control
> + */
> +
> +static unsigned long get_dvco_rate_below(struct tegra_dfll *td, u8 out_min)
> +{
> +	struct dev_pm_opp *opp;
> +	unsigned long rate, prev_rate;
> +	int uv, min_uv;
> +
> +	min_uv = td->lut_uv[out_min];
> +	for (rate = 0, prev_rate = 0; ; rate++) {
> +		rcu_read_lock();
> +		opp = dev_pm_opp_find_freq_ceil(td->soc->dev, &rate);
> +		if (IS_ERR(opp)) {
> +			rcu_read_unlock();
> +			break;

'prev_rate' could be uninitialised here and so you may return a bogus value.

> +		}
> +		uv = dev_pm_opp_get_voltage(opp);
> +		rcu_read_unlock();
> +
> +		if (uv && uv > min_uv)
> +			return prev_rate;
> +
> +		prev_rate = rate;
> +	}
> +
> +	return prev_rate;
> +}
> +
> +/*
>   * DFLL-to-I2C controller interface
>   */
>  
> @@ -518,6 +563,118 @@ static int dfll_i2c_set_output_enabled(struct tegra_dfll *td, bool enable)
>  	return 0;
>  }
>  
> +
> +/*
> + * DFLL-to-PWM controller interface
> + */
> +
> +/**
> + * dfll_pwm_set_output_enabled - enable/disable PWM voltage requests
> + * @td: DFLL instance
> + * @enable: whether to enable or disable the PWM voltage requests
> + *
> + * Set the master enable control for PWM control value updates. If disabled,
> + * then the PWM signal is not driven. Also configure the PWM output pad
> + * to the appropriate state.
> + */
> +static int dfll_pwm_set_output_enabled(struct tegra_dfll *td, bool enable)
> +{
> +	int ret;
> +	u32 val, div;
> +
> +	if (enable) {
> +		ret = pinctrl_select_state(td->pwm_pin, td->pwm_enable_state);
> +		if (ret < 0) {
> +			dev_err(td->dev, "setting enable state failed\n");
> +			return -EINVAL;

Why not return ret?

> +		}
> +		val = dfll_readl(td, DFLL_OUTPUT_CFG);
> +		val &= ~DFLL_OUTPUT_CFG_PWM_DIV_MASK;
> +		div = DIV_ROUND_UP(td->ref_rate, td->pwm_rate);
> +		val |= (div << DFLL_OUTPUT_CFG_PWM_DIV_SHIFT)
> +				& DFLL_OUTPUT_CFG_PWM_DIV_MASK;
> +		dfll_writel(td, val, DFLL_OUTPUT_CFG);
> +		dfll_wmb(td);
> +
> +		val |= DFLL_OUTPUT_CFG_PWM_ENABLE;
> +		dfll_writel(td, val, DFLL_OUTPUT_CFG);
> +		dfll_wmb(td);
> +	} else {
> +		ret = pinctrl_select_state(td->pwm_pin, td->pwm_disable_state);
> +		if (ret < 0)
> +			dev_warn(td->dev, "setting disable state failed\n");
> +
> +		val = dfll_readl(td, DFLL_OUTPUT_CFG);
> +		val &= ~DFLL_OUTPUT_CFG_PWM_ENABLE;
> +		dfll_writel(td, val, DFLL_OUTPUT_CFG);
> +		dfll_wmb(td);
> +	}
> +
> +	return 0;
> +}
> +/**
> + * dfll_set_force_output_value - set fixed value for force output
> + * @td: DFLL instance
> + * @out_val: value to force output
> + *
> + * Set the fixec value for force output, DFLL will output this value when

s/fixec/fixed/

> + * force output is enabled.
> + */
> +static u32 dfll_set_force_output_value(struct tegra_dfll *td, u8 out_val)
> +{
> +	u32 val = dfll_readl(td, DFLL_OUTPUT_FORCE);
> +
> +	val = (val & DFLL_OUTPUT_FORCE_ENABLE) | (out_val & OUT_MASK);

Don't you need to clear the OUT_MASK bits first?

> +	dfll_writel(td, val, DFLL_OUTPUT_FORCE);
> +	dfll_wmb(td);
> +
> +	return dfll_readl(td, DFLL_OUTPUT_FORCE);
> +}
> +
> +/**
> + * dfll_set_force_output_enabled - enable/disable force output
> + * @td: DFLL instance
> + * @enable: whether to enable or disable the force output
> + *
> + * Set the enable control for fouce output with fixed value.
> + */
> +static void dfll_set_force_output_enabled(struct tegra_dfll *td, bool enable)
> +{
> +	u32 val = dfll_readl(td, DFLL_OUTPUT_FORCE);
> +
> +	if (enable)
> +		val |= DFLL_OUTPUT_FORCE_ENABLE;
> +	else
> +		val &= ~DFLL_OUTPUT_FORCE_ENABLE;
> +
> +	dfll_writel(td, val, DFLL_OUTPUT_FORCE);
> +	dfll_wmb(td);
> +}
> +
> +/**
> + * dfll_i2c_set_output_enabled - enable/disable I2C PMIC voltage requests
> + * @td: DFLL instance
> + * @enable: whether to enable or disable the I2C voltage requests
> + *
> + * Set the master enable control for I2C control value updates. If disabled,
> + * then I2C control messages are inhibited, regardless of the DFLL mode.
> + */
> +static int dfll_force_output(struct tegra_dfll *td, unsigned int out_sel)
> +{
> +	u32 val;
> +
> +	if (out_sel > OUT_MASK)
> +		return -EINVAL;
> +
> +	val = dfll_set_force_output_value(td, out_sel);
> +	if ((td->mode < DFLL_CLOSED_LOOP) &&
> +	    !(val & DFLL_OUTPUT_FORCE_ENABLE)) {
> +		dfll_set_force_output_enabled(td, true);
> +	}
> +
> +	return 0;
> +}
> +
>  /**
>   * dfll_load_lut - load the voltage lookup table
>   * @td: struct tegra_dfll *
> @@ -531,15 +688,15 @@ static void dfll_load_i2c_lut(struct tegra_dfll *td)
>  	u32 val;
>  
>  	for (i = 0; i < MAX_DFLL_VOLTAGES; i++) {
> -		if (i < td->lut_min)
> -			lut_index = td->lut_min;
> -		else if (i > td->lut_max)
> -			lut_index = td->lut_max;
> +		if (i < td->lut_bottom)
> +			lut_index = td->lut_bottom;
> +		else if (i > td->lut_size - 1)
> +			lut_index = td->lut_size - 1;
>  		else
>  			lut_index = i;
>  
>  		val = regulator_list_hardware_vsel(td->vdd_reg,
> -						     td->i2c_lut[lut_index]);
> +						     td->lut[lut_index]);
>  		__raw_writel(val, td->lut_base + i * 4);
>  	}
>  
> @@ -595,23 +752,53 @@ static void dfll_init_out_if(struct tegra_dfll *td)
>  	u32 val;
>  
>  	td->lut_min = 0;
> -	td->lut_max = td->i2c_lut_size - 1;
> -	td->lut_safe = td->lut_min + 1;
> -
> -	dfll_i2c_writel(td, 0, DFLL_OUTPUT_CFG);
> -	val = (td->lut_safe << DFLL_OUTPUT_CFG_SAFE_SHIFT) |
> -		(td->lut_max << DFLL_OUTPUT_CFG_MAX_SHIFT) |
> -		(td->lut_min << DFLL_OUTPUT_CFG_MIN_SHIFT);
> -	dfll_i2c_writel(td, val, DFLL_OUTPUT_CFG);
> -	dfll_i2c_wmb(td);
> -
> -	dfll_writel(td, 0, DFLL_OUTPUT_FORCE);
> -	dfll_i2c_writel(td, 0, DFLL_INTR_EN);
> -	dfll_i2c_writel(td, DFLL_INTR_MAX_MASK | DFLL_INTR_MIN_MASK,
> -			DFLL_INTR_STS);
> -
> -	dfll_load_i2c_lut(td);
> -	dfll_init_i2c_if(td);
> +	td->lut_max = td->lut_size - 1;
> +	td->lut_safe = td->lut_min + (td->lut_min < td->lut_max ? 1 : 0);
> +
> +	if (td->pmu_if == TEGRA_DFLL_PMU_PWM) {
> +		int vinit = td->reg_init_uV;
> +		int vstep = td->soc->alignment.step_uv;
> +		int vmin = td->lut_uv[0];
> +
> +		/* clear DFLL_OUTPUT_CFG before setting new value */
> +		dfll_writel(td, 0, DFLL_OUTPUT_CFG);
> +		dfll_wmb(td);
> +
> +		val = dfll_readl(td, DFLL_OUTPUT_CFG);
> +		val |= (td->lut_safe << DFLL_OUTPUT_CFG_SAFE_SHIFT) |
> +		       (td->lut_max << DFLL_OUTPUT_CFG_MAX_SHIFT) |
> +		       (td->lut_min << DFLL_OUTPUT_CFG_MIN_SHIFT);
> +		dfll_writel(td, val, DFLL_OUTPUT_CFG);
> +		dfll_wmb(td);
> +
> +		dfll_writel(td, 0, DFLL_OUTPUT_FORCE);
> +		dfll_i2c_writel(td, 0, DFLL_INTR_EN);
> +		dfll_i2c_writel(td, DFLL_INTR_MAX_MASK | DFLL_INTR_MIN_MASK,
> +				DFLL_INTR_STS);
> +
> +		/* set initial voltage */
> +		if ((vinit >= vmin) && vstep) {
> +			unsigned int vsel;
> +
> +			vsel = DIV_ROUND_UP((vinit - vmin), vstep);
> +			dfll_force_output(td, vsel);
> +		}
> +	} else {
> +		dfll_i2c_writel(td, 0, DFLL_OUTPUT_CFG);
> +		val = (td->lut_safe << DFLL_OUTPUT_CFG_SAFE_SHIFT) |
> +			(td->lut_max << DFLL_OUTPUT_CFG_MAX_SHIFT) |
> +			(td->lut_min << DFLL_OUTPUT_CFG_MIN_SHIFT);
> +		dfll_i2c_writel(td, val, DFLL_OUTPUT_CFG);
> +		dfll_i2c_wmb(td);
> +
> +		dfll_writel(td, 0, DFLL_OUTPUT_FORCE);
> +		dfll_i2c_writel(td, 0, DFLL_INTR_EN);
> +		dfll_i2c_writel(td, DFLL_INTR_MAX_MASK | DFLL_INTR_MIN_MASK,
> +				DFLL_INTR_STS);
> +
> +		dfll_load_i2c_lut(td);
> +		dfll_init_i2c_if(td);
> +	}
>  }
>  
>  /*
> @@ -637,11 +824,12 @@ static int find_lut_index_for_rate(struct tegra_dfll *td, unsigned long rate)
>  	if (IS_ERR(opp))
>  		return PTR_ERR(opp);
>  
> -	uv = dev_pm_opp_get_voltage(opp);
> +	uv = dev_pm_opp_get_voltage(opp) / td->soc->alignment.step_uv;
> +
>  	dev_pm_opp_put(opp);
>  
> -	for (i = 0; i < td->i2c_lut_size; i++) {
> -		if (regulator_list_voltage(td->vdd_reg, td->i2c_lut[i]) == uv)
> +	for (i = td->lut_bottom; i < td->lut_size; i++) {
> +		if ((td->lut_uv[i] / td->soc->alignment.step_uv) >= uv)
>  			return i;
>  	}
>  
> @@ -863,9 +1051,14 @@ static int dfll_lock(struct tegra_dfll *td)
>  			return -EINVAL;
>  		}
>  
> -		dfll_i2c_set_output_enabled(td, true);
> +		if (td->pmu_if == TEGRA_DFLL_PMU_PWM)
> +			dfll_pwm_set_output_enabled(td, true);
> +		else
> +			dfll_i2c_set_output_enabled(td, true);
> +
>  		dfll_set_mode(td, DFLL_CLOSED_LOOP);
>  		dfll_set_frequency_request(td, req);
> +		dfll_set_force_output_enabled(td, false);
>  		return 0;
>  
>  	default:
> @@ -889,7 +1082,10 @@ static int dfll_unlock(struct tegra_dfll *td)
>  	case DFLL_CLOSED_LOOP:
>  		dfll_set_open_loop_config(td);
>  		dfll_set_mode(td, DFLL_OPEN_LOOP);
> -		dfll_i2c_set_output_enabled(td, false);
> +		if (td->pmu_if == TEGRA_DFLL_PMU_PWM)
> +			dfll_pwm_set_output_enabled(td, false);
> +		else
> +			dfll_i2c_set_output_enabled(td, false);
>  		return 0;
>  
>  	case DFLL_OPEN_LOOP:
> @@ -1171,15 +1367,17 @@ static int attr_registers_show(struct seq_file *s, void *data)
>  		seq_printf(s, "[0x%02x] = 0x%08x\n", offs,
>  			   dfll_i2c_readl(td, offs));
>  
> -	seq_puts(s, "\nINTEGRATED I2C CONTROLLER REGISTERS:\n");
> -	offs = DFLL_I2C_CLK_DIVISOR;
> -	seq_printf(s, "[0x%02x] = 0x%08x\n", offs,
> -		   __raw_readl(td->i2c_controller_base + offs));
> -
> -	seq_puts(s, "\nLUT:\n");
> -	for (offs = 0; offs <  4 * MAX_DFLL_VOLTAGES; offs += 4)
> +	if (td->pmu_if == TEGRA_DFLL_PMU_I2C) {
> +		seq_puts(s, "\nINTEGRATED I2C CONTROLLER REGISTERS:\n");
> +		offs = DFLL_I2C_CLK_DIVISOR;
>  		seq_printf(s, "[0x%02x] = 0x%08x\n", offs,
> -			   __raw_readl(td->lut_base + offs));
> +			   __raw_readl(td->i2c_controller_base + offs));
> +
> +		seq_puts(s, "\nLUT:\n");
> +		for (offs = 0; offs <  4 * MAX_DFLL_VOLTAGES; offs += 4)
> +			seq_printf(s, "[0x%02x] = 0x%08x\n", offs,
> +				   __raw_readl(td->lut_base + offs));
> +	}
>  
>  	return 0;
>  }
> @@ -1377,15 +1575,17 @@ static int dfll_init(struct tegra_dfll *td)
>   */
>  static int find_vdd_map_entry_exact(struct tegra_dfll *td, int uV)
>  {
> -	int i, n_voltages, reg_uV;
> +	int i, n_voltages, reg_mult, align_mult;
>  
> +	align_mult = uV / td->soc->alignment.step_uv;
>  	n_voltages = regulator_count_voltages(td->vdd_reg);
>  	for (i = 0; i < n_voltages; i++) {
> -		reg_uV = regulator_list_voltage(td->vdd_reg, i);
> -		if (reg_uV < 0)
> +		reg_mult = regulator_list_voltage(td->vdd_reg, i) /
> +					td->soc->alignment.step_uv;
> +		if (reg_mult < 0)
>  			break;
>  
> -		if (uV == reg_uV)
> +		if (align_mult == reg_mult)
>  			return i;
>  	}
>  
> @@ -1399,15 +1599,17 @@ static int find_vdd_map_entry_exact(struct tegra_dfll *td, int uV)
>   * */
>  static int find_vdd_map_entry_min(struct tegra_dfll *td, int uV)
>  {
> -	int i, n_voltages, reg_uV;
> +	int i, n_voltages, reg_mult, align_mult;
>  
> +	align_mult = uV / td->soc->alignment.step_uv;
>  	n_voltages = regulator_count_voltages(td->vdd_reg);
>  	for (i = 0; i < n_voltages; i++) {
> -		reg_uV = regulator_list_voltage(td->vdd_reg, i);
> -		if (reg_uV < 0)
> +		reg_mult = regulator_list_voltage(td->vdd_reg, i) /
> +					td->soc->alignment.step_uv;
> +		if (reg_mult < 0)
>  			break;
>  
> -		if (uV <= reg_uV)
> +		if (align_mult <= reg_mult)
>  			return i;
>  	}
>  
> @@ -1415,6 +1617,53 @@ static int find_vdd_map_entry_min(struct tegra_dfll *td, int uV)
>  	return -EINVAL;
>  }
>  
> +/*
> + * Look-up table in h/w is ignored when PWM is used as DFLL interface to PMIC.
> + * In this case closed loop output is controlling duty cycle directly. The s/w
> + * look-up that maps PWM duty cycle to voltage is still built by this function.
> + */
> +static int dfll_build_lut_pwm(struct tegra_dfll *td, int v_max)
> +{
> +	int i, reg_volt;
> +	unsigned long rate;
> +	u8 lut_bottom = MAX_DFLL_VOLTAGES;
> +	int v_min = td->soc->cvb->min_millivolts * 1000;
> +
> +	for (i = 0; i < MAX_DFLL_VOLTAGES; i++) {
> +		reg_volt = td->lut_uv[i];
> +
> +		/* since opp voltage is exact mv */
> +		reg_volt = (reg_volt / 1000) * 1000;
> +		if (reg_volt > v_max)
> +			break;
> +
> +		td->lut[i] = i;
> +		if ((lut_bottom == MAX_DFLL_VOLTAGES) && (reg_volt >= v_min))
> +			lut_bottom = i;
> +	}
> +
> +	/* determine voltage boundaries */
> +	td->lut_size = i;
> +	if ((lut_bottom == MAX_DFLL_VOLTAGES) ||
> +	    (lut_bottom + 1 >= td->lut_size)) {
> +		dev_err(td->dev, "no voltage above DFLL minimum %d mV\n",
> +			td->soc->cvb->min_millivolts);
> +		return -EINVAL;
> +	}
> +	td->lut_bottom = lut_bottom;
> +
> +	/* determine rate boundaries */
> +	rate = get_dvco_rate_below(td, td->lut_bottom);
> +	if (!rate) {
> +		dev_err(td->dev, "no opp below DFLL minimum voltage %d mV\n",
> +			td->soc->cvb->min_millivolts);
> +		return -EINVAL;
> +	}
> +	td->dvco_rate_min = rate;
> +
> +	return 0;
> +}
> +
>  /**
>   * dfll_build_i2c_lut - build the I2C voltage register lookup table
>   * @td: DFLL instance
> @@ -1427,31 +1676,24 @@ static int find_vdd_map_entry_min(struct tegra_dfll *td, int uV)
>   *
>   * On success, fills in td->i2c_lut and returns 0, or -err on failure.
>   */
> -static int dfll_build_i2c_lut(struct tegra_dfll *td)
> +static int dfll_build_i2c_lut(struct tegra_dfll *td, int v_max)
>  {
> +	unsigned long rate;
>  	int ret = -EINVAL;
> -	int j, v, v_max, v_opp;
> +	int j, v, v_opp;
>  	int selector;
> -	unsigned long rate;
> -	struct dev_pm_opp *opp;
>  	int lut;
>  
> -	rate = ULONG_MAX;
> -	opp = dev_pm_opp_find_freq_floor(td->soc->dev, &rate);
> -	if (IS_ERR(opp)) {
> -		dev_err(td->dev, "couldn't get vmax opp, empty opp table?\n");
> -		goto out;
> -	}
> -	v_max = dev_pm_opp_get_voltage(opp);
> -	dev_pm_opp_put(opp);
> -
>  	v = td->soc->cvb->min_millivolts * 1000;
>  	lut = find_vdd_map_entry_exact(td, v);
>  	if (lut < 0)
>  		goto out;
> -	td->i2c_lut[0] = lut;
> +	td->lut[0] = lut;
> +	td->lut_bottom = 0;
>  
>  	for (j = 1, rate = 0; ; rate++) {
> +		struct dev_pm_opp *opp;
> +
>  		opp = dev_pm_opp_find_freq_ceil(td->soc->dev, &rate);
>  		if (IS_ERR(opp))
>  			break;
> @@ -1470,32 +1712,61 @@ static int dfll_build_i2c_lut(struct tegra_dfll *td)
>  			selector = find_vdd_map_entry_min(td, v);
>  			if (selector < 0)
>  				goto out;
> -			if (selector != td->i2c_lut[j - 1])
> -				td->i2c_lut[j++] = selector;
> +			if (selector != td->lut[j - 1])
> +				td->lut[j++] = selector;
>  		}
>  
>  		v = (j == MAX_DFLL_VOLTAGES - 1) ? v_max : v_opp;
>  		selector = find_vdd_map_entry_exact(td, v);
>  		if (selector < 0)
>  			goto out;
> -		if (selector != td->i2c_lut[j - 1])
> -			td->i2c_lut[j++] = selector;
> +		if (selector != td->lut[j - 1])
> +			td->lut[j++] = selector;
>  
>  		if (v >= v_max)
>  			break;
>  	}
> -	td->i2c_lut_size = j;
> +	td->lut_size = j;
>  
>  	if (!td->dvco_rate_min)
>  		dev_err(td->dev, "no opp above DFLL minimum voltage %d mV\n",
>  			td->soc->cvb->min_millivolts);
> -	else
> +	else {
>  		ret = 0;
> +		for (j = 0; j < td->lut_size; j++)
> +			td->lut_uv[j] =
> +				regulator_list_voltage(td->vdd_reg,
> +						       td->lut[j]);
> +	}
>  
>  out:
>  	return ret;
>  }
>  
> +static int dfll_build_lut(struct tegra_dfll *td)
> +{
> +	unsigned long rate;
> +	struct dev_pm_opp *opp;
> +	int v_max;
> +
> +	rcu_read_lock();
> +
> +	rate = ULONG_MAX;
> +	opp = dev_pm_opp_find_freq_floor(td->soc->dev, &rate);
> +	if (IS_ERR(opp)) {
> +		dev_err(td->dev, "couldn't get vmax opp, empty opp table?\n");
> +		return -EINVAL;
> +	}
> +	v_max = dev_pm_opp_get_voltage(opp);
> +
> +	rcu_read_unlock();
> +
> +	if (td->pmu_if == TEGRA_DFLL_PMU_PWM)
> +		return dfll_build_lut_pwm(td, v_max);
> +	else
> +		return dfll_build_i2c_lut(td, v_max);
> +}
> +
>  /**
>   * read_dt_param - helper function for reading required parameters from the DT
>   * @td: DFLL instance
> @@ -1554,12 +1825,55 @@ static int dfll_fetch_i2c_params(struct tegra_dfll *td)
>  	}
>  	td->i2c_reg = vsel_reg;
>  
> -	ret = dfll_build_i2c_lut(td);
> -	if (ret) {
> -		dev_err(td->dev, "couldn't build I2C LUT\n");
> +	return 0;
> +}
> +
> +static int dfll_fetch_pwm_params(struct tegra_dfll *td)
> +{
> +	int ret, i;
> +	u32 pwm_period;
> +
> +	if (!td->soc->alignment.step_uv || !td->soc->alignment.offset_uv) {
> +		dev_err(td->dev, "Missing step or alignment info for PWM regulator");
> +		return -EINVAL;
> +	}
> +	for (i = 0; i < MAX_DFLL_VOLTAGES; i++)
> +		td->lut_uv[i] = td->soc->alignment.offset_uv +
> +				i * td->soc->alignment.step_uv;
> +
> +	ret = read_dt_param(td, "nvidia,init-uv", &td->reg_init_uV);
> +	if (!ret) {
> +		dev_err(td->dev, "couldn't get initialized voltage\n");
>  		return ret;
>  	}
>  
> +	ret = read_dt_param(td, "nvidia,pwm-period", &pwm_period);
> +	if (!ret) {
> +		dev_err(td->dev, "couldn't get PWM period\n");
> +		return ret;
> +	}
> +	td->pwm_rate = (NSEC_PER_SEC / pwm_period) * (MAX_DFLL_VOLTAGES - 1);
> +
> +	td->pwm_pin = devm_pinctrl_get(td->dev);
> +	if (IS_ERR(td->pwm_pin)) {
> +		dev_err(td->dev, "DT: missing pinctrl device\n");
> +		return PTR_ERR(td->pwm_pin);
> +	}
> +
> +	td->pwm_enable_state = pinctrl_lookup_state(td->pwm_pin,
> +						    "dvfs_pwm_enable");
> +	if (IS_ERR(td->pwm_enable_state)) {
> +		dev_err(td->dev, "DT: missing pwm enabled state\n");
> +		return PTR_ERR(td->pwm_enable_state);
> +	}
> +
> +	td->pwm_disable_state = pinctrl_lookup_state(td->pwm_pin,
> +						     "dvfs_pwm_disable");
> +	if (IS_ERR(td->pwm_disable_state)) {
> +		dev_err(td->dev, "DT: missing pwm disabled state\n");
> +		return PTR_ERR(td->pwm_disable_state);
> +	}
> +
>  	return 0;
>  }
>  
> @@ -1625,12 +1939,6 @@ int tegra_dfll_register(struct platform_device *pdev,
>  
>  	td->soc = soc;
>  
> -	td->vdd_reg = devm_regulator_get(td->dev, "vdd-cpu");
> -	if (IS_ERR(td->vdd_reg)) {
> -		dev_err(td->dev, "couldn't get vdd_cpu regulator\n");
> -		return PTR_ERR(td->vdd_reg);
> -	}
> -
>  	td->dvco_rst = devm_reset_control_get(td->dev, "dvco");
>  	if (IS_ERR(td->dvco_rst)) {
>  		dev_err(td->dev, "couldn't get dvco reset\n");
> @@ -1643,10 +1951,27 @@ int tegra_dfll_register(struct platform_device *pdev,
>  		return ret;
>  	}
>  
> -	ret = dfll_fetch_i2c_params(td);
> +	if (of_property_read_bool(td->dev->of_node, "nvidia,pwm-to-pmic")) {
> +		td->pmu_if = TEGRA_DFLL_PMU_PWM;
> +		ret = dfll_fetch_pwm_params(td);
> +	} else  {
> +		td->vdd_reg = devm_regulator_get(td->dev, "vdd-cpu");
> +		if (IS_ERR(td->vdd_reg)) {
> +			dev_err(td->dev, "couldn't get vdd_cpu regulator\n");
> +			return PTR_ERR(td->vdd_reg);
> +		}
> +		td->pmu_if = TEGRA_DFLL_PMU_I2C;
> +		ret = dfll_fetch_i2c_params(td);
> +	}
>  	if (ret)
>  		return ret;
>  
> +	ret = dfll_build_lut(td);
> +	if (ret) {
> +		dev_err(td->dev, "couldn't build LUT\n");
> +		return ret;
> +	}
> +
>  	mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
>  	if (!mem) {
>  		dev_err(td->dev, "no control register resource\n");
> diff --git a/drivers/clk/tegra/clk-dfll.h b/drivers/clk/tegra/clk-dfll.h
> index 83352c8..228f54a 100644
> --- a/drivers/clk/tegra/clk-dfll.h
> +++ b/drivers/clk/tegra/clk-dfll.h
> @@ -21,6 +21,7 @@
>  #include <linux/platform_device.h>
>  #include <linux/reset.h>
>  #include <linux/types.h>
> +#include "cvb.h"
>  
>  /**
>   * struct tegra_dfll_soc_data - SoC-specific hooks/integration for the DFLL driver
> @@ -35,6 +36,12 @@ struct tegra_dfll_soc_data {
>  	struct device *dev;
>  	unsigned long max_freq;
>  	const struct cvb_table *cvb;
> +	struct rail_alignment alignment;
> +	unsigned int min_millivolts;
> +	unsigned int tune_high_min_millivolts;
> +	u32 tune0_low;
> +	u32 tune0_high;
> +	u32 tune1;
>  
>  	void (*init_clock_trimmers)(void);
>  	void (*set_clock_trimmers_high)(void);
> 

Overall this is quite a big change. I wonder if it is worth splitting
into a preparatory patch (with all the renaming, etc) for the existing
i2c mode and then add the pwm support. It is hard to see how the
existing i2c code has been changed.

Cheers
Jon

-- 
nvpublic

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

* Re: [PATCH v2 0/6] Tegra210 DFLL implementation
  2018-01-24 12:45 ` Peter De Schrijver
@ 2018-01-31 11:28     ` Jon Hunter
  -1 siblings, 0 replies; 56+ messages in thread
From: Jon Hunter @ 2018-01-31 11:28 UTC (permalink / raw)
  To: Peter De Schrijver, linux-tegra-u79uwXL29TY76Z2rM5mHXA,
	linux-clk-u79uwXL29TY76Z2rM5mHXA,
	mturquette-rdvid1DuHRBWk0Htik3J/w, sboyd-sgV2jX0FEOL9JmXXK+q4OQ


On 24/01/18 12:45, Peter De Schrijver wrote:
> This series introduces support for the DFLL as a CPU clock source
> on Tegra210. As Jetson TX2 uses a PWM controlled regulator IC which
> is driven directly by the DFLLs PWM output, we also introduce support
> for PWM regulators next to I2C controlled regulators. The DFLL output
> frequency is directly controlled by the regulator voltage. The registers
> for controlling the PWM are part of the DFLL IP block, so there's no
> separate linux regulator object involved because the regulator IC only
> supplies the rail powering the DFLL and the CPUs. It doesn't have any
> other controls.
> 
> Changes since v1:
> * improved commit messages
> * some style cleanups
> 
> Peter De Schrijver (6):
>   clk: tegra: dfll registration for multiple SoCs
>   clk: tegra: DT align parameter for CVB calculation
>   clk: tegra: add CVB tables for Tegra210 CPU DFLL
>   clk: tegra: dfll: support PWM regulator control
>   clk: tegra: build clk-dfll.c for Tegra124 and Tegra210
>   cpufreq: tegra124-cpufreq: extend to support Tegra210
> 
>  drivers/clk/tegra/Kconfig                  |   5 +
>  drivers/clk/tegra/Makefile                 |   2 +-
>  drivers/clk/tegra/clk-dfll.c               | 481 +++++++++++++++++++++++-----
>  drivers/clk/tegra/clk-dfll.h               |   7 +
>  drivers/clk/tegra/clk-tegra124-dfll-fcpu.c | 492 ++++++++++++++++++++++++++++-
>  drivers/clk/tegra/cvb.c                    |  18 +-
>  drivers/clk/tegra/cvb.h                    |   6 +-
>  drivers/cpufreq/tegra124-cpufreq.c         |  13 +-
>  8 files changed, 920 insertions(+), 104 deletions(-)

This series appears to be missing DT documentation and DT source changes?

Cheers
Jon

-- 
nvpublic

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

* Re: [PATCH v2 0/6] Tegra210 DFLL implementation
@ 2018-01-31 11:28     ` Jon Hunter
  0 siblings, 0 replies; 56+ messages in thread
From: Jon Hunter @ 2018-01-31 11:28 UTC (permalink / raw)
  To: Peter De Schrijver, linux-tegra, linux-clk, mturquette, sboyd


On 24/01/18 12:45, Peter De Schrijver wrote:
> This series introduces support for the DFLL as a CPU clock source
> on Tegra210. As Jetson TX2 uses a PWM controlled regulator IC which
> is driven directly by the DFLLs PWM output, we also introduce support
> for PWM regulators next to I2C controlled regulators. The DFLL output
> frequency is directly controlled by the regulator voltage. The registers
> for controlling the PWM are part of the DFLL IP block, so there's no
> separate linux regulator object involved because the regulator IC only
> supplies the rail powering the DFLL and the CPUs. It doesn't have any
> other controls.
> 
> Changes since v1:
> * improved commit messages
> * some style cleanups
> 
> Peter De Schrijver (6):
>   clk: tegra: dfll registration for multiple SoCs
>   clk: tegra: DT align parameter for CVB calculation
>   clk: tegra: add CVB tables for Tegra210 CPU DFLL
>   clk: tegra: dfll: support PWM regulator control
>   clk: tegra: build clk-dfll.c for Tegra124 and Tegra210
>   cpufreq: tegra124-cpufreq: extend to support Tegra210
> 
>  drivers/clk/tegra/Kconfig                  |   5 +
>  drivers/clk/tegra/Makefile                 |   2 +-
>  drivers/clk/tegra/clk-dfll.c               | 481 +++++++++++++++++++++++-----
>  drivers/clk/tegra/clk-dfll.h               |   7 +
>  drivers/clk/tegra/clk-tegra124-dfll-fcpu.c | 492 ++++++++++++++++++++++++++++-
>  drivers/clk/tegra/cvb.c                    |  18 +-
>  drivers/clk/tegra/cvb.h                    |   6 +-
>  drivers/cpufreq/tegra124-cpufreq.c         |  13 +-
>  8 files changed, 920 insertions(+), 104 deletions(-)

This series appears to be missing DT documentation and DT source changes?

Cheers
Jon

-- 
nvpublic

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

* Re: [PATCH v2 6/6] cpufreq: tegra124-cpufreq: extend to support Tegra210
  2018-01-31 11:06       ` Jon Hunter
@ 2018-02-01  9:25           ` Peter De Schrijver
  -1 siblings, 0 replies; 56+ messages in thread
From: Peter De Schrijver @ 2018-02-01  9:25 UTC (permalink / raw)
  To: Jon Hunter
  Cc: linux-tegra-u79uwXL29TY76Z2rM5mHXA,
	linux-clk-u79uwXL29TY76Z2rM5mHXA,
	mturquette-rdvid1DuHRBWk0Htik3J/w, sboyd-sgV2jX0FEOL9JmXXK+q4OQ

On Wed, Jan 31, 2018 at 11:06:36AM +0000, Jon Hunter wrote:
> 
> On 24/01/18 12:45, Peter De Schrijver wrote:
> > Tegra210 has a very similar CPU clocking scheme than Tegra124. So add
> > support in this driver. Also allow for the case where the CPU voltage is
> > controlled directly by the DFLL rather than by a separate regulator object.
> > 
> > Signed-off-by: Peter De Schrijver <pdeschrijver-DDmLM1+adcrQT0dZR+AlfA@public.gmane.org>
> > ---
> >  drivers/cpufreq/tegra124-cpufreq.c | 13 ++++++-------
> >  1 file changed, 6 insertions(+), 7 deletions(-)
> > 
> > diff --git a/drivers/cpufreq/tegra124-cpufreq.c b/drivers/cpufreq/tegra124-cpufreq.c
> > index 4353025..d631dc4 100644
> > --- a/drivers/cpufreq/tegra124-cpufreq.c
> > +++ b/drivers/cpufreq/tegra124-cpufreq.c
> > @@ -64,7 +64,8 @@ static void tegra124_cpu_switch_to_pllx(struct tegra124_cpufreq_priv *priv)
> >  {
> >  	clk_set_parent(priv->cpu_clk, priv->pllp_clk);
> >  	clk_disable_unprepare(priv->dfll_clk);
> > -	regulator_sync_voltage(priv->vdd_cpu_reg);
> > +	if (priv->vdd_cpu_reg)
> > +		regulator_sync_voltage(priv->vdd_cpu_reg);
> >  	clk_set_parent(priv->cpu_clk, priv->pllx_clk);
> >  }
> >  
> > @@ -89,10 +90,8 @@ static int tegra124_cpufreq_probe(struct platform_device *pdev)
> >  		return -ENODEV;
> >  
> >  	priv->vdd_cpu_reg = regulator_get(cpu_dev, "vdd-cpu");
> > -	if (IS_ERR(priv->vdd_cpu_reg)) {
> > -		ret = PTR_ERR(priv->vdd_cpu_reg);
> > -		goto out_put_np;
> > -	}
> > +	if (IS_ERR(priv->vdd_cpu_reg))
> > +		priv->vdd_cpu_reg = NULL;
> >  
> 
> For Tegra124, don't we still want to return an error here?

Not necessarily. Also on Tegra124 the DFLL can use PWM and I think jetson TK1
can be reworked to use this.

Peter.

> 
> >  	priv->cpu_clk = of_clk_get_by_name(np, "cpu_g");
> >  	if (IS_ERR(priv->cpu_clk)) {
> > @@ -148,7 +147,6 @@ static int tegra124_cpufreq_probe(struct platform_device *pdev)
> >  	clk_put(priv->cpu_clk);
> >  out_put_vdd_cpu_reg:
> >  	regulator_put(priv->vdd_cpu_reg);
> > -out_put_np:
> >  	of_node_put(np);
> >  
> >  	return ret;
> > @@ -181,7 +179,8 @@ static int __init tegra_cpufreq_init(void)
> >  	int ret;
> >  	struct platform_device *pdev;
> >  
> > -	if (!of_machine_is_compatible("nvidia,tegra124"))
> > +	if (!(of_machine_is_compatible("nvidia,tegra124")
> > +		|| of_machine_is_compatible("nvidia,tegra210")))
> >  		return -ENODEV;
> >  
> >  	/*
> > 
> 
> Cheers
> Jon
> 
> -- 
> nvpublic

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

* Re: [PATCH v2 6/6] cpufreq: tegra124-cpufreq: extend to support Tegra210
@ 2018-02-01  9:25           ` Peter De Schrijver
  0 siblings, 0 replies; 56+ messages in thread
From: Peter De Schrijver @ 2018-02-01  9:25 UTC (permalink / raw)
  To: Jon Hunter; +Cc: linux-tegra, linux-clk, mturquette, sboyd

On Wed, Jan 31, 2018 at 11:06:36AM +0000, Jon Hunter wrote:
> 
> On 24/01/18 12:45, Peter De Schrijver wrote:
> > Tegra210 has a very similar CPU clocking scheme than Tegra124. So add
> > support in this driver. Also allow for the case where the CPU voltage is
> > controlled directly by the DFLL rather than by a separate regulator object.
> > 
> > Signed-off-by: Peter De Schrijver <pdeschrijver@nvidia.com>
> > ---
> >  drivers/cpufreq/tegra124-cpufreq.c | 13 ++++++-------
> >  1 file changed, 6 insertions(+), 7 deletions(-)
> > 
> > diff --git a/drivers/cpufreq/tegra124-cpufreq.c b/drivers/cpufreq/tegra124-cpufreq.c
> > index 4353025..d631dc4 100644
> > --- a/drivers/cpufreq/tegra124-cpufreq.c
> > +++ b/drivers/cpufreq/tegra124-cpufreq.c
> > @@ -64,7 +64,8 @@ static void tegra124_cpu_switch_to_pllx(struct tegra124_cpufreq_priv *priv)
> >  {
> >  	clk_set_parent(priv->cpu_clk, priv->pllp_clk);
> >  	clk_disable_unprepare(priv->dfll_clk);
> > -	regulator_sync_voltage(priv->vdd_cpu_reg);
> > +	if (priv->vdd_cpu_reg)
> > +		regulator_sync_voltage(priv->vdd_cpu_reg);
> >  	clk_set_parent(priv->cpu_clk, priv->pllx_clk);
> >  }
> >  
> > @@ -89,10 +90,8 @@ static int tegra124_cpufreq_probe(struct platform_device *pdev)
> >  		return -ENODEV;
> >  
> >  	priv->vdd_cpu_reg = regulator_get(cpu_dev, "vdd-cpu");
> > -	if (IS_ERR(priv->vdd_cpu_reg)) {
> > -		ret = PTR_ERR(priv->vdd_cpu_reg);
> > -		goto out_put_np;
> > -	}
> > +	if (IS_ERR(priv->vdd_cpu_reg))
> > +		priv->vdd_cpu_reg = NULL;
> >  
> 
> For Tegra124, don't we still want to return an error here?

Not necessarily. Also on Tegra124 the DFLL can use PWM and I think jetson TK1
can be reworked to use this.

Peter.

> 
> >  	priv->cpu_clk = of_clk_get_by_name(np, "cpu_g");
> >  	if (IS_ERR(priv->cpu_clk)) {
> > @@ -148,7 +147,6 @@ static int tegra124_cpufreq_probe(struct platform_device *pdev)
> >  	clk_put(priv->cpu_clk);
> >  out_put_vdd_cpu_reg:
> >  	regulator_put(priv->vdd_cpu_reg);
> > -out_put_np:
> >  	of_node_put(np);
> >  
> >  	return ret;
> > @@ -181,7 +179,8 @@ static int __init tegra_cpufreq_init(void)
> >  	int ret;
> >  	struct platform_device *pdev;
> >  
> > -	if (!of_machine_is_compatible("nvidia,tegra124"))
> > +	if (!(of_machine_is_compatible("nvidia,tegra124")
> > +		|| of_machine_is_compatible("nvidia,tegra210")))
> >  		return -ENODEV;
> >  
> >  	/*
> > 
> 
> Cheers
> Jon
> 
> -- 
> nvpublic

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

* Re: [PATCH v2 0/6] Tegra210 DFLL implementation
  2018-01-31 11:28     ` Jon Hunter
@ 2018-02-01  9:30       ` Peter De Schrijver
  -1 siblings, 0 replies; 56+ messages in thread
From: Peter De Schrijver @ 2018-02-01  9:30 UTC (permalink / raw)
  To: Jon Hunter; +Cc: linux-tegra, linux-clk, mturquette, sboyd

On Wed, Jan 31, 2018 at 11:28:31AM +0000, Jon Hunter wrote:
> 
> On 24/01/18 12:45, Peter De Schrijver wrote:
> > This series introduces support for the DFLL as a CPU clock source
> > on Tegra210. As Jetson TX2 uses a PWM controlled regulator IC which
> > is driven directly by the DFLLs PWM output, we also introduce support
> > for PWM regulators next to I2C controlled regulators. The DFLL output
> > frequency is directly controlled by the regulator voltage. The registers
> > for controlling the PWM are part of the DFLL IP block, so there's no
> > separate linux regulator object involved because the regulator IC only
> > supplies the rail powering the DFLL and the CPUs. It doesn't have any
> > other controls.
> > 
> > Changes since v1:
> > * improved commit messages
> > * some style cleanups
> > 
> > Peter De Schrijver (6):
> >   clk: tegra: dfll registration for multiple SoCs
> >   clk: tegra: DT align parameter for CVB calculation
> >   clk: tegra: add CVB tables for Tegra210 CPU DFLL
> >   clk: tegra: dfll: support PWM regulator control
> >   clk: tegra: build clk-dfll.c for Tegra124 and Tegra210
> >   cpufreq: tegra124-cpufreq: extend to support Tegra210
> > 
> >  drivers/clk/tegra/Kconfig                  |   5 +
> >  drivers/clk/tegra/Makefile                 |   2 +-
> >  drivers/clk/tegra/clk-dfll.c               | 481 +++++++++++++++++++++++-----
> >  drivers/clk/tegra/clk-dfll.h               |   7 +
> >  drivers/clk/tegra/clk-tegra124-dfll-fcpu.c | 492 ++++++++++++++++++++++++++++-
> >  drivers/clk/tegra/cvb.c                    |  18 +-
> >  drivers/clk/tegra/cvb.h                    |   6 +-
> >  drivers/cpufreq/tegra124-cpufreq.c         |  13 +-
> >  8 files changed, 920 insertions(+), 104 deletions(-)
> 
> This series appears to be missing DT documentation and DT source changes?
> 

Yes.. It seems I forgot those..

Peter.

> Cheers
> Jon
> 
> -- 
> nvpublic

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

* Re: [PATCH v2 0/6] Tegra210 DFLL implementation
@ 2018-02-01  9:30       ` Peter De Schrijver
  0 siblings, 0 replies; 56+ messages in thread
From: Peter De Schrijver @ 2018-02-01  9:30 UTC (permalink / raw)
  To: Jon Hunter; +Cc: linux-tegra, linux-clk, mturquette, sboyd

On Wed, Jan 31, 2018 at 11:28:31AM +0000, Jon Hunter wrote:
> 
> On 24/01/18 12:45, Peter De Schrijver wrote:
> > This series introduces support for the DFLL as a CPU clock source
> > on Tegra210. As Jetson TX2 uses a PWM controlled regulator IC which
> > is driven directly by the DFLLs PWM output, we also introduce support
> > for PWM regulators next to I2C controlled regulators. The DFLL output
> > frequency is directly controlled by the regulator voltage. The registers
> > for controlling the PWM are part of the DFLL IP block, so there's no
> > separate linux regulator object involved because the regulator IC only
> > supplies the rail powering the DFLL and the CPUs. It doesn't have any
> > other controls.
> > 
> > Changes since v1:
> > * improved commit messages
> > * some style cleanups
> > 
> > Peter De Schrijver (6):
> >   clk: tegra: dfll registration for multiple SoCs
> >   clk: tegra: DT align parameter for CVB calculation
> >   clk: tegra: add CVB tables for Tegra210 CPU DFLL
> >   clk: tegra: dfll: support PWM regulator control
> >   clk: tegra: build clk-dfll.c for Tegra124 and Tegra210
> >   cpufreq: tegra124-cpufreq: extend to support Tegra210
> > 
> >  drivers/clk/tegra/Kconfig                  |   5 +
> >  drivers/clk/tegra/Makefile                 |   2 +-
> >  drivers/clk/tegra/clk-dfll.c               | 481 +++++++++++++++++++++++-----
> >  drivers/clk/tegra/clk-dfll.h               |   7 +
> >  drivers/clk/tegra/clk-tegra124-dfll-fcpu.c | 492 ++++++++++++++++++++++++++++-
> >  drivers/clk/tegra/cvb.c                    |  18 +-
> >  drivers/clk/tegra/cvb.h                    |   6 +-
> >  drivers/cpufreq/tegra124-cpufreq.c         |  13 +-
> >  8 files changed, 920 insertions(+), 104 deletions(-)
> 
> This series appears to be missing DT documentation and DT source changes?
> 

Yes.. It seems I forgot those..

Peter.

> Cheers
> Jon
> 
> -- 
> nvpublic

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

* Re: [PATCH v2 6/6] cpufreq: tegra124-cpufreq: extend to support Tegra210
  2018-02-01  9:25           ` Peter De Schrijver
@ 2018-02-01  9:53             ` Jon Hunter
  -1 siblings, 0 replies; 56+ messages in thread
From: Jon Hunter @ 2018-02-01  9:53 UTC (permalink / raw)
  To: Peter De Schrijver; +Cc: linux-tegra, linux-clk, mturquette, sboyd


On 01/02/18 09:25, Peter De Schrijver wrote:
> On Wed, Jan 31, 2018 at 11:06:36AM +0000, Jon Hunter wrote:
>>
>> On 24/01/18 12:45, Peter De Schrijver wrote:
>>> Tegra210 has a very similar CPU clocking scheme than Tegra124. So add
>>> support in this driver. Also allow for the case where the CPU voltage is
>>> controlled directly by the DFLL rather than by a separate regulator object.
>>>
>>> Signed-off-by: Peter De Schrijver <pdeschrijver@nvidia.com>
>>> ---
>>>  drivers/cpufreq/tegra124-cpufreq.c | 13 ++++++-------
>>>  1 file changed, 6 insertions(+), 7 deletions(-)
>>>
>>> diff --git a/drivers/cpufreq/tegra124-cpufreq.c b/drivers/cpufreq/tegra124-cpufreq.c
>>> index 4353025..d631dc4 100644
>>> --- a/drivers/cpufreq/tegra124-cpufreq.c
>>> +++ b/drivers/cpufreq/tegra124-cpufreq.c
>>> @@ -64,7 +64,8 @@ static void tegra124_cpu_switch_to_pllx(struct tegra124_cpufreq_priv *priv)
>>>  {
>>>  	clk_set_parent(priv->cpu_clk, priv->pllp_clk);
>>>  	clk_disable_unprepare(priv->dfll_clk);
>>> -	regulator_sync_voltage(priv->vdd_cpu_reg);
>>> +	if (priv->vdd_cpu_reg)
>>> +		regulator_sync_voltage(priv->vdd_cpu_reg);
>>>  	clk_set_parent(priv->cpu_clk, priv->pllx_clk);
>>>  }
>>>  
>>> @@ -89,10 +90,8 @@ static int tegra124_cpufreq_probe(struct platform_device *pdev)
>>>  		return -ENODEV;
>>>  
>>>  	priv->vdd_cpu_reg = regulator_get(cpu_dev, "vdd-cpu");
>>> -	if (IS_ERR(priv->vdd_cpu_reg)) {
>>> -		ret = PTR_ERR(priv->vdd_cpu_reg);
>>> -		goto out_put_np;
>>> -	}
>>> +	if (IS_ERR(priv->vdd_cpu_reg))
>>> +		priv->vdd_cpu_reg = NULL;
>>>  
>>
>> For Tegra124, don't we still want to return an error here?
> 
> Not necessarily. Also on Tegra124 the DFLL can use PWM and I think jetson TK1
> can be reworked to use this.

Seems to me that we should use DT or soc-data to specify which is being
used and then do the appropriate thing. I don't think that we should
assume that because the regulator is not present we are using PWM.

Cheers
Jon

-- 
nvpublic

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

* Re: [PATCH v2 6/6] cpufreq: tegra124-cpufreq: extend to support Tegra210
@ 2018-02-01  9:53             ` Jon Hunter
  0 siblings, 0 replies; 56+ messages in thread
From: Jon Hunter @ 2018-02-01  9:53 UTC (permalink / raw)
  To: Peter De Schrijver; +Cc: linux-tegra, linux-clk, mturquette, sboyd


On 01/02/18 09:25, Peter De Schrijver wrote:
> On Wed, Jan 31, 2018 at 11:06:36AM +0000, Jon Hunter wrote:
>>
>> On 24/01/18 12:45, Peter De Schrijver wrote:
>>> Tegra210 has a very similar CPU clocking scheme than Tegra124. So add
>>> support in this driver. Also allow for the case where the CPU voltage is
>>> controlled directly by the DFLL rather than by a separate regulator object.
>>>
>>> Signed-off-by: Peter De Schrijver <pdeschrijver@nvidia.com>
>>> ---
>>>  drivers/cpufreq/tegra124-cpufreq.c | 13 ++++++-------
>>>  1 file changed, 6 insertions(+), 7 deletions(-)
>>>
>>> diff --git a/drivers/cpufreq/tegra124-cpufreq.c b/drivers/cpufreq/tegra124-cpufreq.c
>>> index 4353025..d631dc4 100644
>>> --- a/drivers/cpufreq/tegra124-cpufreq.c
>>> +++ b/drivers/cpufreq/tegra124-cpufreq.c
>>> @@ -64,7 +64,8 @@ static void tegra124_cpu_switch_to_pllx(struct tegra124_cpufreq_priv *priv)
>>>  {
>>>  	clk_set_parent(priv->cpu_clk, priv->pllp_clk);
>>>  	clk_disable_unprepare(priv->dfll_clk);
>>> -	regulator_sync_voltage(priv->vdd_cpu_reg);
>>> +	if (priv->vdd_cpu_reg)
>>> +		regulator_sync_voltage(priv->vdd_cpu_reg);
>>>  	clk_set_parent(priv->cpu_clk, priv->pllx_clk);
>>>  }
>>>  
>>> @@ -89,10 +90,8 @@ static int tegra124_cpufreq_probe(struct platform_device *pdev)
>>>  		return -ENODEV;
>>>  
>>>  	priv->vdd_cpu_reg = regulator_get(cpu_dev, "vdd-cpu");
>>> -	if (IS_ERR(priv->vdd_cpu_reg)) {
>>> -		ret = PTR_ERR(priv->vdd_cpu_reg);
>>> -		goto out_put_np;
>>> -	}
>>> +	if (IS_ERR(priv->vdd_cpu_reg))
>>> +		priv->vdd_cpu_reg = NULL;
>>>  
>>
>> For Tegra124, don't we still want to return an error here?
> 
> Not necessarily. Also on Tegra124 the DFLL can use PWM and I think jetson TK1
> can be reworked to use this.

Seems to me that we should use DT or soc-data to specify which is being
used and then do the appropriate thing. I don't think that we should
assume that because the regulator is not present we are using PWM.

Cheers
Jon

-- 
nvpublic

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

* Re: [PATCH v2 6/6] cpufreq: tegra124-cpufreq: extend to support Tegra210
  2018-02-01  9:53             ` Jon Hunter
@ 2018-02-01 10:15                 ` Peter De Schrijver
  -1 siblings, 0 replies; 56+ messages in thread
From: Peter De Schrijver @ 2018-02-01 10:15 UTC (permalink / raw)
  To: Jon Hunter
  Cc: linux-tegra-u79uwXL29TY76Z2rM5mHXA,
	linux-clk-u79uwXL29TY76Z2rM5mHXA,
	mturquette-rdvid1DuHRBWk0Htik3J/w, sboyd-sgV2jX0FEOL9JmXXK+q4OQ

On Thu, Feb 01, 2018 at 09:53:43AM +0000, Jon Hunter wrote:
> 
> On 01/02/18 09:25, Peter De Schrijver wrote:
> > On Wed, Jan 31, 2018 at 11:06:36AM +0000, Jon Hunter wrote:
> >>
> >> On 24/01/18 12:45, Peter De Schrijver wrote:
> >>> Tegra210 has a very similar CPU clocking scheme than Tegra124. So add
> >>> support in this driver. Also allow for the case where the CPU voltage is
> >>> controlled directly by the DFLL rather than by a separate regulator object.
> >>>
> >>> Signed-off-by: Peter De Schrijver <pdeschrijver-DDmLM1+adcrQT0dZR+AlfA@public.gmane.org>
> >>> ---
> >>>  drivers/cpufreq/tegra124-cpufreq.c | 13 ++++++-------
> >>>  1 file changed, 6 insertions(+), 7 deletions(-)
> >>>
> >>> diff --git a/drivers/cpufreq/tegra124-cpufreq.c b/drivers/cpufreq/tegra124-cpufreq.c
> >>> index 4353025..d631dc4 100644
> >>> --- a/drivers/cpufreq/tegra124-cpufreq.c
> >>> +++ b/drivers/cpufreq/tegra124-cpufreq.c
> >>> @@ -64,7 +64,8 @@ static void tegra124_cpu_switch_to_pllx(struct tegra124_cpufreq_priv *priv)
> >>>  {
> >>>  	clk_set_parent(priv->cpu_clk, priv->pllp_clk);
> >>>  	clk_disable_unprepare(priv->dfll_clk);
> >>> -	regulator_sync_voltage(priv->vdd_cpu_reg);
> >>> +	if (priv->vdd_cpu_reg)
> >>> +		regulator_sync_voltage(priv->vdd_cpu_reg);
> >>>  	clk_set_parent(priv->cpu_clk, priv->pllx_clk);
> >>>  }
> >>>  
> >>> @@ -89,10 +90,8 @@ static int tegra124_cpufreq_probe(struct platform_device *pdev)
> >>>  		return -ENODEV;
> >>>  
> >>>  	priv->vdd_cpu_reg = regulator_get(cpu_dev, "vdd-cpu");
> >>> -	if (IS_ERR(priv->vdd_cpu_reg)) {
> >>> -		ret = PTR_ERR(priv->vdd_cpu_reg);
> >>> -		goto out_put_np;
> >>> -	}
> >>> +	if (IS_ERR(priv->vdd_cpu_reg))
> >>> +		priv->vdd_cpu_reg = NULL;
> >>>  
> >>
> >> For Tegra124, don't we still want to return an error here?
> > 
> > Not necessarily. Also on Tegra124 the DFLL can use PWM and I think jetson TK1
> > can be reworked to use this.
> 
> Seems to me that we should use DT or soc-data to specify which is being
> used and then do the appropriate thing. I don't think that we should
> assume that because the regulator is not present we are using PWM.

I think we should rather move the sync logic into the DFLL driver in some
future series. The DFLL driver does know about PWM vs I2C regulator.

Peter.

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

* Re: [PATCH v2 6/6] cpufreq: tegra124-cpufreq: extend to support Tegra210
@ 2018-02-01 10:15                 ` Peter De Schrijver
  0 siblings, 0 replies; 56+ messages in thread
From: Peter De Schrijver @ 2018-02-01 10:15 UTC (permalink / raw)
  To: Jon Hunter; +Cc: linux-tegra, linux-clk, mturquette, sboyd

On Thu, Feb 01, 2018 at 09:53:43AM +0000, Jon Hunter wrote:
> 
> On 01/02/18 09:25, Peter De Schrijver wrote:
> > On Wed, Jan 31, 2018 at 11:06:36AM +0000, Jon Hunter wrote:
> >>
> >> On 24/01/18 12:45, Peter De Schrijver wrote:
> >>> Tegra210 has a very similar CPU clocking scheme than Tegra124. So add
> >>> support in this driver. Also allow for the case where the CPU voltage is
> >>> controlled directly by the DFLL rather than by a separate regulator object.
> >>>
> >>> Signed-off-by: Peter De Schrijver <pdeschrijver@nvidia.com>
> >>> ---
> >>>  drivers/cpufreq/tegra124-cpufreq.c | 13 ++++++-------
> >>>  1 file changed, 6 insertions(+), 7 deletions(-)
> >>>
> >>> diff --git a/drivers/cpufreq/tegra124-cpufreq.c b/drivers/cpufreq/tegra124-cpufreq.c
> >>> index 4353025..d631dc4 100644
> >>> --- a/drivers/cpufreq/tegra124-cpufreq.c
> >>> +++ b/drivers/cpufreq/tegra124-cpufreq.c
> >>> @@ -64,7 +64,8 @@ static void tegra124_cpu_switch_to_pllx(struct tegra124_cpufreq_priv *priv)
> >>>  {
> >>>  	clk_set_parent(priv->cpu_clk, priv->pllp_clk);
> >>>  	clk_disable_unprepare(priv->dfll_clk);
> >>> -	regulator_sync_voltage(priv->vdd_cpu_reg);
> >>> +	if (priv->vdd_cpu_reg)
> >>> +		regulator_sync_voltage(priv->vdd_cpu_reg);
> >>>  	clk_set_parent(priv->cpu_clk, priv->pllx_clk);
> >>>  }
> >>>  
> >>> @@ -89,10 +90,8 @@ static int tegra124_cpufreq_probe(struct platform_device *pdev)
> >>>  		return -ENODEV;
> >>>  
> >>>  	priv->vdd_cpu_reg = regulator_get(cpu_dev, "vdd-cpu");
> >>> -	if (IS_ERR(priv->vdd_cpu_reg)) {
> >>> -		ret = PTR_ERR(priv->vdd_cpu_reg);
> >>> -		goto out_put_np;
> >>> -	}
> >>> +	if (IS_ERR(priv->vdd_cpu_reg))
> >>> +		priv->vdd_cpu_reg = NULL;
> >>>  
> >>
> >> For Tegra124, don't we still want to return an error here?
> > 
> > Not necessarily. Also on Tegra124 the DFLL can use PWM and I think jetson TK1
> > can be reworked to use this.
> 
> Seems to me that we should use DT or soc-data to specify which is being
> used and then do the appropriate thing. I don't think that we should
> assume that because the regulator is not present we are using PWM.

I think we should rather move the sync logic into the DFLL driver in some
future series. The DFLL driver does know about PWM vs I2C regulator.

Peter.

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

* Re: [PATCH v2 6/6] cpufreq: tegra124-cpufreq: extend to support Tegra210
  2018-02-01 10:15                 ` Peter De Schrijver
@ 2018-02-01 10:22                   ` Jon Hunter
  -1 siblings, 0 replies; 56+ messages in thread
From: Jon Hunter @ 2018-02-01 10:22 UTC (permalink / raw)
  To: Peter De Schrijver; +Cc: linux-tegra, linux-clk, mturquette, sboyd


On 01/02/18 10:15, Peter De Schrijver wrote:
> On Thu, Feb 01, 2018 at 09:53:43AM +0000, Jon Hunter wrote:
>>
>> On 01/02/18 09:25, Peter De Schrijver wrote:
>>> On Wed, Jan 31, 2018 at 11:06:36AM +0000, Jon Hunter wrote:
>>>>
>>>> On 24/01/18 12:45, Peter De Schrijver wrote:
>>>>> Tegra210 has a very similar CPU clocking scheme than Tegra124. So add
>>>>> support in this driver. Also allow for the case where the CPU voltage is
>>>>> controlled directly by the DFLL rather than by a separate regulator object.
>>>>>
>>>>> Signed-off-by: Peter De Schrijver <pdeschrijver@nvidia.com>
>>>>> ---
>>>>>  drivers/cpufreq/tegra124-cpufreq.c | 13 ++++++-------
>>>>>  1 file changed, 6 insertions(+), 7 deletions(-)
>>>>>
>>>>> diff --git a/drivers/cpufreq/tegra124-cpufreq.c b/drivers/cpufreq/tegra124-cpufreq.c
>>>>> index 4353025..d631dc4 100644
>>>>> --- a/drivers/cpufreq/tegra124-cpufreq.c
>>>>> +++ b/drivers/cpufreq/tegra124-cpufreq.c
>>>>> @@ -64,7 +64,8 @@ static void tegra124_cpu_switch_to_pllx(struct tegra124_cpufreq_priv *priv)
>>>>>  {
>>>>>  	clk_set_parent(priv->cpu_clk, priv->pllp_clk);
>>>>>  	clk_disable_unprepare(priv->dfll_clk);
>>>>> -	regulator_sync_voltage(priv->vdd_cpu_reg);
>>>>> +	if (priv->vdd_cpu_reg)
>>>>> +		regulator_sync_voltage(priv->vdd_cpu_reg);
>>>>>  	clk_set_parent(priv->cpu_clk, priv->pllx_clk);
>>>>>  }
>>>>>  
>>>>> @@ -89,10 +90,8 @@ static int tegra124_cpufreq_probe(struct platform_device *pdev)
>>>>>  		return -ENODEV;
>>>>>  
>>>>>  	priv->vdd_cpu_reg = regulator_get(cpu_dev, "vdd-cpu");
>>>>> -	if (IS_ERR(priv->vdd_cpu_reg)) {
>>>>> -		ret = PTR_ERR(priv->vdd_cpu_reg);
>>>>> -		goto out_put_np;
>>>>> -	}
>>>>> +	if (IS_ERR(priv->vdd_cpu_reg))
>>>>> +		priv->vdd_cpu_reg = NULL;
>>>>>  
>>>>
>>>> For Tegra124, don't we still want to return an error here?
>>>
>>> Not necessarily. Also on Tegra124 the DFLL can use PWM and I think jetson TK1
>>> can be reworked to use this.
>>
>> Seems to me that we should use DT or soc-data to specify which is being
>> used and then do the appropriate thing. I don't think that we should
>> assume that because the regulator is not present we are using PWM.
> 
> I think we should rather move the sync logic into the DFLL driver in some
> future series. The DFLL driver does know about PWM vs I2C regulator.

Not sure I following what you mean by 'sync logic'. Also did you mean
the DFLL driver does NOT know about PWM vs I2C? However, we still know
at probe time what is used right?

Cheers
Jon

-- 
nvpublic

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

* Re: [PATCH v2 6/6] cpufreq: tegra124-cpufreq: extend to support Tegra210
@ 2018-02-01 10:22                   ` Jon Hunter
  0 siblings, 0 replies; 56+ messages in thread
From: Jon Hunter @ 2018-02-01 10:22 UTC (permalink / raw)
  To: Peter De Schrijver; +Cc: linux-tegra, linux-clk, mturquette, sboyd


On 01/02/18 10:15, Peter De Schrijver wrote:
> On Thu, Feb 01, 2018 at 09:53:43AM +0000, Jon Hunter wrote:
>>
>> On 01/02/18 09:25, Peter De Schrijver wrote:
>>> On Wed, Jan 31, 2018 at 11:06:36AM +0000, Jon Hunter wrote:
>>>>
>>>> On 24/01/18 12:45, Peter De Schrijver wrote:
>>>>> Tegra210 has a very similar CPU clocking scheme than Tegra124. So add
>>>>> support in this driver. Also allow for the case where the CPU voltage is
>>>>> controlled directly by the DFLL rather than by a separate regulator object.
>>>>>
>>>>> Signed-off-by: Peter De Schrijver <pdeschrijver@nvidia.com>
>>>>> ---
>>>>>  drivers/cpufreq/tegra124-cpufreq.c | 13 ++++++-------
>>>>>  1 file changed, 6 insertions(+), 7 deletions(-)
>>>>>
>>>>> diff --git a/drivers/cpufreq/tegra124-cpufreq.c b/drivers/cpufreq/tegra124-cpufreq.c
>>>>> index 4353025..d631dc4 100644
>>>>> --- a/drivers/cpufreq/tegra124-cpufreq.c
>>>>> +++ b/drivers/cpufreq/tegra124-cpufreq.c
>>>>> @@ -64,7 +64,8 @@ static void tegra124_cpu_switch_to_pllx(struct tegra124_cpufreq_priv *priv)
>>>>>  {
>>>>>  	clk_set_parent(priv->cpu_clk, priv->pllp_clk);
>>>>>  	clk_disable_unprepare(priv->dfll_clk);
>>>>> -	regulator_sync_voltage(priv->vdd_cpu_reg);
>>>>> +	if (priv->vdd_cpu_reg)
>>>>> +		regulator_sync_voltage(priv->vdd_cpu_reg);
>>>>>  	clk_set_parent(priv->cpu_clk, priv->pllx_clk);
>>>>>  }
>>>>>  
>>>>> @@ -89,10 +90,8 @@ static int tegra124_cpufreq_probe(struct platform_device *pdev)
>>>>>  		return -ENODEV;
>>>>>  
>>>>>  	priv->vdd_cpu_reg = regulator_get(cpu_dev, "vdd-cpu");
>>>>> -	if (IS_ERR(priv->vdd_cpu_reg)) {
>>>>> -		ret = PTR_ERR(priv->vdd_cpu_reg);
>>>>> -		goto out_put_np;
>>>>> -	}
>>>>> +	if (IS_ERR(priv->vdd_cpu_reg))
>>>>> +		priv->vdd_cpu_reg = NULL;
>>>>>  
>>>>
>>>> For Tegra124, don't we still want to return an error here?
>>>
>>> Not necessarily. Also on Tegra124 the DFLL can use PWM and I think jetson TK1
>>> can be reworked to use this.
>>
>> Seems to me that we should use DT or soc-data to specify which is being
>> used and then do the appropriate thing. I don't think that we should
>> assume that because the regulator is not present we are using PWM.
> 
> I think we should rather move the sync logic into the DFLL driver in some
> future series. The DFLL driver does know about PWM vs I2C regulator.

Not sure I following what you mean by 'sync logic'. Also did you mean
the DFLL driver does NOT know about PWM vs I2C? However, we still know
at probe time what is used right?

Cheers
Jon

-- 
nvpublic

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

* Re: [PATCH v2 2/6] clk: tegra: DT align parameter for CVB calculation
  2018-01-31 10:43     ` Jon Hunter
@ 2018-02-01 10:30         ` Peter De Schrijver
  -1 siblings, 0 replies; 56+ messages in thread
From: Peter De Schrijver @ 2018-02-01 10:30 UTC (permalink / raw)
  To: Jon Hunter
  Cc: linux-tegra-u79uwXL29TY76Z2rM5mHXA,
	linux-clk-u79uwXL29TY76Z2rM5mHXA,
	mturquette-rdvid1DuHRBWk0Htik3J/w, sboyd-sgV2jX0FEOL9JmXXK+q4OQ

On Wed, Jan 31, 2018 at 10:43:04AM +0000, Jon Hunter wrote:
> 
> On 24/01/18 12:45, Peter De Schrijver wrote:
> > When using a PWM controlled regulator, the voltage step and offset are
> > determined by the regulator IC in use. This is specified in DT rather
> > than fixed in the CVB table. Hence pass this information to the CVB table
> > calculation function. For backwards compatibility the table values are used
> > if the corresponding parameter is 0.
> > 
> > Signed-off-by: Peter De Schrijver <pdeschrijver-DDmLM1+adcrQT0dZR+AlfA@public.gmane.org>
> > ---
> >  drivers/clk/tegra/clk-tegra124-dfll-fcpu.c | 23 +++++++++++++++++++++--
> >  drivers/clk/tegra/cvb.c                    | 18 ++++++++++++++----
> >  drivers/clk/tegra/cvb.h                    |  5 +++--
> >  3 files changed, 38 insertions(+), 8 deletions(-)
> > 
> > diff --git a/drivers/clk/tegra/clk-tegra124-dfll-fcpu.c b/drivers/clk/tegra/clk-tegra124-dfll-fcpu.c
> > index 440eb8d..6205ce1 100644
> > --- a/drivers/clk/tegra/clk-tegra124-dfll-fcpu.c
> > +++ b/drivers/clk/tegra/clk-tegra124-dfll-fcpu.c
> > @@ -111,6 +111,7 @@ static int tegra124_dfll_fcpu_probe(struct platform_device *pdev)
> >  	struct tegra_dfll_soc_data *soc;
> >  	const struct of_device_id *of_id;
> >  	const struct dfll_fcpu_data *fcpu_data;
> > +	struct rail_alignment align;
> >  
> >  	of_id = of_match_device(tegra124_dfll_fcpu_of_match, &pdev->dev);
> >  	fcpu_data = of_id->data;
> > @@ -135,12 +136,30 @@ static int tegra124_dfll_fcpu_probe(struct platform_device *pdev)
> >  		return -ENODEV;
> >  	}
> >  
> > +	err = of_property_read_u32(pdev->dev.of_node, "nvidia,align-offset-uv",
> > +					&align.offset_uv);
> > +	if (err < 0) {
> > +		dev_err(&pdev->dev,
> > +			"offset uv not found, default to table value\n");
> > +		align.offset_uv = 0;
> > +	}
> > +
> > +	err = of_property_read_u32(pdev->dev.of_node, "nvidia,align-step-uv",
> > +					&align.step_uv);
> > +	if (err < 0) {
> > +		dev_err(&pdev->dev,
> > +			"step uv not found, default to table value\n");
> > +		align.step_uv = 0;
> > +	}
> > +
> 
> I am a bit confused by this ...
> 
> 1. Isn't this going to break Tegra124 DFLL support?

We fall back to the original behaviour in case the properties are missing, so
it should work.

> 2. These DT properties are not defined anywhere (so the binding doc
>    needs updating).
> 3. I don't see any patches in this series that populate these properties
>    in DT.
> 
> So I am not sure how this works?!?
> 

The DT updates are indeed missing.

> >  	soc->max_freq = fcpu_data->cpu_max_freq_table[speedo_id];
> >  
> >  	soc->cvb = tegra_cvb_add_opp_table(soc->dev, fcpu_data->cpu_cvb_tables,
> >  					   fcpu_data->cpu_cvb_tables_size,
> > -					   process_id, speedo_id, speedo_value,
> > -					   soc->max_freq);
> > +					   &align, process_id, speedo_id,
> > +					   speedo_value, soc->max_freq);
> > +	soc->alignment = align;
> > +
> 
> This is not defined yet and so breaks compile.
> 
> drivers/clk/tegra/clk-tegra124-dfll-fcpu.c: In function
> ‘tegra124_dfll_fcpu_probe’:
> drivers/clk/tegra/clk-tegra124-dfll-fcpu.c:161:5: error: ‘struct
> tegra_dfll_soc_data’ has no member named ‘alignment’
>   soc->alignment = align;
>      ^
> 
> What about Tegra124? This is set to NULL? Can we not set this before

It's a struct copy, not a pointer. And both values in the struct will indeed
be set to 0 then.

> calling tegra_cvb_add_opp_table() and then just pass soc->alignment for
> both Tegra124 and Tegra210? Then you can avoid the if-statements in
> build_opp_table.
> 

The table to be used is determined by tegra_cvb_add_opp_table() based on speedo
and process ID. So this logic would have to be duplicated in
tegra124_dfll_fcpu_probe() then. In retrospect it was probably a mistake to
put these regulator parameters in the CVB table, but I think we're stuck
with this unless we want to force people to also update their DT.

> >  	if (IS_ERR(soc->cvb)) {
> >  		dev_err(&pdev->dev, "couldn't add OPP table: %ld\n",
> >  			PTR_ERR(soc->cvb));
> > diff --git a/drivers/clk/tegra/cvb.c b/drivers/clk/tegra/cvb.c
> > index da9e8e7..561012a 100644
> > --- a/drivers/clk/tegra/cvb.c
> > +++ b/drivers/clk/tegra/cvb.c
> > @@ -62,11 +62,19 @@ static int round_voltage(int mv, const struct rail_alignment *align, int up)
> >  }
> >  
> >  static int build_opp_table(struct device *dev, const struct cvb_table *table,
> > +			   struct rail_alignment *align,
> >  			   int speedo_value, unsigned long max_freq)
> >  {
> > -	const struct rail_alignment *align = &table->alignment;
> >  	int i, ret, dfll_mv, min_mv, max_mv;
> >  
> > +	if (!align->step_uv)
> > +		align->step_uv = table->alignment.step_uv;
> > +	if (!align->step_uv)
> > +		return -EINVAL;
> > +
> > +	if (!align->offset_uv)
> > +		align->offset_uv = table->alignment.offset_uv;
> > +
> >  	min_mv = round_voltage(table->min_millivolts, align, UP);
> >  	max_mv = round_voltage(table->max_millivolts, align, DOWN);
> >  
> > @@ -109,8 +117,9 @@ static int build_opp_table(struct device *dev, const struct cvb_table *table,
> >   */
> >  const struct cvb_table *
> >  tegra_cvb_add_opp_table(struct device *dev, const struct cvb_table *tables,
> > -			size_t count, int process_id, int speedo_id,
> > -			int speedo_value, unsigned long max_freq)
> > +			size_t count, struct rail_alignment *align,
> > +			int process_id, int speedo_id, int speedo_value,
> > +			unsigned long max_freq)
> >  {
> >  	size_t i;
> >  	int ret;
> > @@ -124,7 +133,8 @@ static int build_opp_table(struct device *dev, const struct cvb_table *table,
> >  		if (table->process_id != -1 && table->process_id != process_id)
> >  			continue;
> >  
> > -		ret = build_opp_table(dev, table, speedo_value, max_freq);
> > +		ret = build_opp_table(dev, table, align, speedo_value,
> > +				      max_freq);
> >  		return ret ? ERR_PTR(ret) : table;
> >  	}
> >  
> > diff --git a/drivers/clk/tegra/cvb.h b/drivers/clk/tegra/cvb.h
> > index c1f0779..cfa110f 100644
> > --- a/drivers/clk/tegra/cvb.h
> > +++ b/drivers/clk/tegra/cvb.h
> > @@ -59,8 +59,9 @@ struct cvb_table {
> >  
> >  const struct cvb_table *
> >  tegra_cvb_add_opp_table(struct device *dev, const struct cvb_table *cvb_tables,
> > -			size_t count, int process_id, int speedo_id,
> > -			int speedo_value, unsigned long max_freq);
> > +			size_t count, struct rail_alignment *align,
> > +			int process_id, int speedo_id, int speedo_value,
> > +			unsigned long max_freq);
> >  void tegra_cvb_remove_opp_table(struct device *dev,
> >  				const struct cvb_table *table,
> >  				unsigned long max_freq);
> > 
> 
> Cheers
> Jon
> 
> -- 
> nvpublic

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

* Re: [PATCH v2 2/6] clk: tegra: DT align parameter for CVB calculation
@ 2018-02-01 10:30         ` Peter De Schrijver
  0 siblings, 0 replies; 56+ messages in thread
From: Peter De Schrijver @ 2018-02-01 10:30 UTC (permalink / raw)
  To: Jon Hunter; +Cc: linux-tegra, linux-clk, mturquette, sboyd

On Wed, Jan 31, 2018 at 10:43:04AM +0000, Jon Hunter wrote:
>=20
> On 24/01/18 12:45, Peter De Schrijver wrote:
> > When using a PWM controlled regulator, the voltage step and offset are
> > determined by the regulator IC in use. This is specified in DT rather
> > than fixed in the CVB table. Hence pass this information to the CVB tab=
le
> > calculation function. For backwards compatibility the table values are =
used
> > if the corresponding parameter is 0.
> >=20
> > Signed-off-by: Peter De Schrijver <pdeschrijver@nvidia.com>
> > ---
> >  drivers/clk/tegra/clk-tegra124-dfll-fcpu.c | 23 +++++++++++++++++++++-=
-
> >  drivers/clk/tegra/cvb.c                    | 18 ++++++++++++++----
> >  drivers/clk/tegra/cvb.h                    |  5 +++--
> >  3 files changed, 38 insertions(+), 8 deletions(-)
> >=20
> > diff --git a/drivers/clk/tegra/clk-tegra124-dfll-fcpu.c b/drivers/clk/t=
egra/clk-tegra124-dfll-fcpu.c
> > index 440eb8d..6205ce1 100644
> > --- a/drivers/clk/tegra/clk-tegra124-dfll-fcpu.c
> > +++ b/drivers/clk/tegra/clk-tegra124-dfll-fcpu.c
> > @@ -111,6 +111,7 @@ static int tegra124_dfll_fcpu_probe(struct platform=
_device *pdev)
> >  	struct tegra_dfll_soc_data *soc;
> >  	const struct of_device_id *of_id;
> >  	const struct dfll_fcpu_data *fcpu_data;
> > +	struct rail_alignment align;
> > =20
> >  	of_id =3D of_match_device(tegra124_dfll_fcpu_of_match, &pdev->dev);
> >  	fcpu_data =3D of_id->data;
> > @@ -135,12 +136,30 @@ static int tegra124_dfll_fcpu_probe(struct platfo=
rm_device *pdev)
> >  		return -ENODEV;
> >  	}
> > =20
> > +	err =3D of_property_read_u32(pdev->dev.of_node, "nvidia,align-offset-=
uv",
> > +					&align.offset_uv);
> > +	if (err < 0) {
> > +		dev_err(&pdev->dev,
> > +			"offset uv not found, default to table value\n");
> > +		align.offset_uv =3D 0;
> > +	}
> > +
> > +	err =3D of_property_read_u32(pdev->dev.of_node, "nvidia,align-step-uv=
",
> > +					&align.step_uv);
> > +	if (err < 0) {
> > +		dev_err(&pdev->dev,
> > +			"step uv not found, default to table value\n");
> > +		align.step_uv =3D 0;
> > +	}
> > +
>=20
> I am a bit confused by this ...
>=20
> 1. Isn't this going to break Tegra124 DFLL support?

We fall back to the original behaviour in case the properties are missing, =
so
it should work.

> 2. These DT properties are not defined anywhere (so the binding doc
>    needs updating).
> 3. I don't see any patches in this series that populate these properties
>    in DT.
>=20
> So I am not sure how this works?!?
>=20

The DT updates are indeed missing.

> >  	soc->max_freq =3D fcpu_data->cpu_max_freq_table[speedo_id];
> > =20
> >  	soc->cvb =3D tegra_cvb_add_opp_table(soc->dev, fcpu_data->cpu_cvb_tab=
les,
> >  					   fcpu_data->cpu_cvb_tables_size,
> > -					   process_id, speedo_id, speedo_value,
> > -					   soc->max_freq);
> > +					   &align, process_id, speedo_id,
> > +					   speedo_value, soc->max_freq);
> > +	soc->alignment =3D align;
> > +
>=20
> This is not defined yet and so breaks compile.
>=20
> drivers/clk/tegra/clk-tegra124-dfll-fcpu.c: In function
> =E2=80=98tegra124_dfll_fcpu_probe=E2=80=99:
> drivers/clk/tegra/clk-tegra124-dfll-fcpu.c:161:5: error: =E2=80=98struct
> tegra_dfll_soc_data=E2=80=99 has no member named =E2=80=98alignment=E2=80=
=99
>   soc->alignment =3D align;
>      ^
>=20
> What about Tegra124? This is set to NULL? Can we not set this before

It's a struct copy, not a pointer. And both values in the struct will indee=
d
be set to 0 then.

> calling tegra_cvb_add_opp_table() and then just pass soc->alignment for
> both Tegra124 and Tegra210? Then you can avoid the if-statements in
> build_opp_table.
>=20

The table to be used is determined by tegra_cvb_add_opp_table() based on sp=
eedo
and process ID. So this logic would have to be duplicated in
tegra124_dfll_fcpu_probe() then. In retrospect it was probably a mistake to
put these regulator parameters in the CVB table, but I think we're stuck
with this unless we want to force people to also update their DT.

> >  	if (IS_ERR(soc->cvb)) {
> >  		dev_err(&pdev->dev, "couldn't add OPP table: %ld\n",
> >  			PTR_ERR(soc->cvb));
> > diff --git a/drivers/clk/tegra/cvb.c b/drivers/clk/tegra/cvb.c
> > index da9e8e7..561012a 100644
> > --- a/drivers/clk/tegra/cvb.c
> > +++ b/drivers/clk/tegra/cvb.c
> > @@ -62,11 +62,19 @@ static int round_voltage(int mv, const struct rail_=
alignment *align, int up)
> >  }
> > =20
> >  static int build_opp_table(struct device *dev, const struct cvb_table =
*table,
> > +			   struct rail_alignment *align,
> >  			   int speedo_value, unsigned long max_freq)
> >  {
> > -	const struct rail_alignment *align =3D &table->alignment;
> >  	int i, ret, dfll_mv, min_mv, max_mv;
> > =20
> > +	if (!align->step_uv)
> > +		align->step_uv =3D table->alignment.step_uv;
> > +	if (!align->step_uv)
> > +		return -EINVAL;
> > +
> > +	if (!align->offset_uv)
> > +		align->offset_uv =3D table->alignment.offset_uv;
> > +
> >  	min_mv =3D round_voltage(table->min_millivolts, align, UP);
> >  	max_mv =3D round_voltage(table->max_millivolts, align, DOWN);
> > =20
> > @@ -109,8 +117,9 @@ static int build_opp_table(struct device *dev, cons=
t struct cvb_table *table,
> >   */
> >  const struct cvb_table *
> >  tegra_cvb_add_opp_table(struct device *dev, const struct cvb_table *ta=
bles,
> > -			size_t count, int process_id, int speedo_id,
> > -			int speedo_value, unsigned long max_freq)
> > +			size_t count, struct rail_alignment *align,
> > +			int process_id, int speedo_id, int speedo_value,
> > +			unsigned long max_freq)
> >  {
> >  	size_t i;
> >  	int ret;
> > @@ -124,7 +133,8 @@ static int build_opp_table(struct device *dev, cons=
t struct cvb_table *table,
> >  		if (table->process_id !=3D -1 && table->process_id !=3D process_id)
> >  			continue;
> > =20
> > -		ret =3D build_opp_table(dev, table, speedo_value, max_freq);
> > +		ret =3D build_opp_table(dev, table, align, speedo_value,
> > +				      max_freq);
> >  		return ret ? ERR_PTR(ret) : table;
> >  	}
> > =20
> > diff --git a/drivers/clk/tegra/cvb.h b/drivers/clk/tegra/cvb.h
> > index c1f0779..cfa110f 100644
> > --- a/drivers/clk/tegra/cvb.h
> > +++ b/drivers/clk/tegra/cvb.h
> > @@ -59,8 +59,9 @@ struct cvb_table {
> > =20
> >  const struct cvb_table *
> >  tegra_cvb_add_opp_table(struct device *dev, const struct cvb_table *cv=
b_tables,
> > -			size_t count, int process_id, int speedo_id,
> > -			int speedo_value, unsigned long max_freq);
> > +			size_t count, struct rail_alignment *align,
> > +			int process_id, int speedo_id, int speedo_value,
> > +			unsigned long max_freq);
> >  void tegra_cvb_remove_opp_table(struct device *dev,
> >  				const struct cvb_table *table,
> >  				unsigned long max_freq);
> >=20
>=20
> Cheers
> Jon
>=20
> --=20
> nvpublic

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

* Re: [PATCH v2 6/6] cpufreq: tegra124-cpufreq: extend to support Tegra210
  2018-02-01 10:22                   ` Jon Hunter
@ 2018-02-01 10:31                     ` Peter De Schrijver
  -1 siblings, 0 replies; 56+ messages in thread
From: Peter De Schrijver @ 2018-02-01 10:31 UTC (permalink / raw)
  To: Jon Hunter; +Cc: linux-tegra, linux-clk, mturquette, sboyd

On Thu, Feb 01, 2018 at 10:22:11AM +0000, Jon Hunter wrote:
> 
> On 01/02/18 10:15, Peter De Schrijver wrote:
> > On Thu, Feb 01, 2018 at 09:53:43AM +0000, Jon Hunter wrote:
> >>
> >> On 01/02/18 09:25, Peter De Schrijver wrote:
> >>> On Wed, Jan 31, 2018 at 11:06:36AM +0000, Jon Hunter wrote:
> >>>>
> >>>> On 24/01/18 12:45, Peter De Schrijver wrote:
> >>>>> Tegra210 has a very similar CPU clocking scheme than Tegra124. So add
> >>>>> support in this driver. Also allow for the case where the CPU voltage is
> >>>>> controlled directly by the DFLL rather than by a separate regulator object.
> >>>>>
> >>>>> Signed-off-by: Peter De Schrijver <pdeschrijver@nvidia.com>
> >>>>> ---
> >>>>>  drivers/cpufreq/tegra124-cpufreq.c | 13 ++++++-------
> >>>>>  1 file changed, 6 insertions(+), 7 deletions(-)
> >>>>>
> >>>>> diff --git a/drivers/cpufreq/tegra124-cpufreq.c b/drivers/cpufreq/tegra124-cpufreq.c
> >>>>> index 4353025..d631dc4 100644
> >>>>> --- a/drivers/cpufreq/tegra124-cpufreq.c
> >>>>> +++ b/drivers/cpufreq/tegra124-cpufreq.c
> >>>>> @@ -64,7 +64,8 @@ static void tegra124_cpu_switch_to_pllx(struct tegra124_cpufreq_priv *priv)
> >>>>>  {
> >>>>>  	clk_set_parent(priv->cpu_clk, priv->pllp_clk);
> >>>>>  	clk_disable_unprepare(priv->dfll_clk);
> >>>>> -	regulator_sync_voltage(priv->vdd_cpu_reg);
> >>>>> +	if (priv->vdd_cpu_reg)
> >>>>> +		regulator_sync_voltage(priv->vdd_cpu_reg);
> >>>>>  	clk_set_parent(priv->cpu_clk, priv->pllx_clk);
> >>>>>  }
> >>>>>  
> >>>>> @@ -89,10 +90,8 @@ static int tegra124_cpufreq_probe(struct platform_device *pdev)
> >>>>>  		return -ENODEV;
> >>>>>  
> >>>>>  	priv->vdd_cpu_reg = regulator_get(cpu_dev, "vdd-cpu");
> >>>>> -	if (IS_ERR(priv->vdd_cpu_reg)) {
> >>>>> -		ret = PTR_ERR(priv->vdd_cpu_reg);
> >>>>> -		goto out_put_np;
> >>>>> -	}
> >>>>> +	if (IS_ERR(priv->vdd_cpu_reg))
> >>>>> +		priv->vdd_cpu_reg = NULL;
> >>>>>  
> >>>>
> >>>> For Tegra124, don't we still want to return an error here?
> >>>
> >>> Not necessarily. Also on Tegra124 the DFLL can use PWM and I think jetson TK1
> >>> can be reworked to use this.
> >>
> >> Seems to me that we should use DT or soc-data to specify which is being
> >> used and then do the appropriate thing. I don't think that we should
> >> assume that because the regulator is not present we are using PWM.
> > 
> > I think we should rather move the sync logic into the DFLL driver in some
> > future series. The DFLL driver does know about PWM vs I2C regulator.
> 
> Not sure I following what you mean by 'sync logic'. Also did you mean

I meant the code in tegra124_cpu_switch_to_pllx() which ensures vdd_cpu
voltage is set correctly when we switch back to pll_x.

> the DFLL driver does NOT know about PWM vs I2C? However, we still know
> at probe time what is used right?

No, I meant the DFLL driver does know about PWM vs I2C, but the cpufreq
driver does not.

Peter.

> 
> Cheers
> Jon
> 
> -- 
> nvpublic

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

* Re: [PATCH v2 6/6] cpufreq: tegra124-cpufreq: extend to support Tegra210
@ 2018-02-01 10:31                     ` Peter De Schrijver
  0 siblings, 0 replies; 56+ messages in thread
From: Peter De Schrijver @ 2018-02-01 10:31 UTC (permalink / raw)
  To: Jon Hunter; +Cc: linux-tegra, linux-clk, mturquette, sboyd

On Thu, Feb 01, 2018 at 10:22:11AM +0000, Jon Hunter wrote:
> 
> On 01/02/18 10:15, Peter De Schrijver wrote:
> > On Thu, Feb 01, 2018 at 09:53:43AM +0000, Jon Hunter wrote:
> >>
> >> On 01/02/18 09:25, Peter De Schrijver wrote:
> >>> On Wed, Jan 31, 2018 at 11:06:36AM +0000, Jon Hunter wrote:
> >>>>
> >>>> On 24/01/18 12:45, Peter De Schrijver wrote:
> >>>>> Tegra210 has a very similar CPU clocking scheme than Tegra124. So add
> >>>>> support in this driver. Also allow for the case where the CPU voltage is
> >>>>> controlled directly by the DFLL rather than by a separate regulator object.
> >>>>>
> >>>>> Signed-off-by: Peter De Schrijver <pdeschrijver@nvidia.com>
> >>>>> ---
> >>>>>  drivers/cpufreq/tegra124-cpufreq.c | 13 ++++++-------
> >>>>>  1 file changed, 6 insertions(+), 7 deletions(-)
> >>>>>
> >>>>> diff --git a/drivers/cpufreq/tegra124-cpufreq.c b/drivers/cpufreq/tegra124-cpufreq.c
> >>>>> index 4353025..d631dc4 100644
> >>>>> --- a/drivers/cpufreq/tegra124-cpufreq.c
> >>>>> +++ b/drivers/cpufreq/tegra124-cpufreq.c
> >>>>> @@ -64,7 +64,8 @@ static void tegra124_cpu_switch_to_pllx(struct tegra124_cpufreq_priv *priv)
> >>>>>  {
> >>>>>  	clk_set_parent(priv->cpu_clk, priv->pllp_clk);
> >>>>>  	clk_disable_unprepare(priv->dfll_clk);
> >>>>> -	regulator_sync_voltage(priv->vdd_cpu_reg);
> >>>>> +	if (priv->vdd_cpu_reg)
> >>>>> +		regulator_sync_voltage(priv->vdd_cpu_reg);
> >>>>>  	clk_set_parent(priv->cpu_clk, priv->pllx_clk);
> >>>>>  }
> >>>>>  
> >>>>> @@ -89,10 +90,8 @@ static int tegra124_cpufreq_probe(struct platform_device *pdev)
> >>>>>  		return -ENODEV;
> >>>>>  
> >>>>>  	priv->vdd_cpu_reg = regulator_get(cpu_dev, "vdd-cpu");
> >>>>> -	if (IS_ERR(priv->vdd_cpu_reg)) {
> >>>>> -		ret = PTR_ERR(priv->vdd_cpu_reg);
> >>>>> -		goto out_put_np;
> >>>>> -	}
> >>>>> +	if (IS_ERR(priv->vdd_cpu_reg))
> >>>>> +		priv->vdd_cpu_reg = NULL;
> >>>>>  
> >>>>
> >>>> For Tegra124, don't we still want to return an error here?
> >>>
> >>> Not necessarily. Also on Tegra124 the DFLL can use PWM and I think jetson TK1
> >>> can be reworked to use this.
> >>
> >> Seems to me that we should use DT or soc-data to specify which is being
> >> used and then do the appropriate thing. I don't think that we should
> >> assume that because the regulator is not present we are using PWM.
> > 
> > I think we should rather move the sync logic into the DFLL driver in some
> > future series. The DFLL driver does know about PWM vs I2C regulator.
> 
> Not sure I following what you mean by 'sync logic'. Also did you mean

I meant the code in tegra124_cpu_switch_to_pllx() which ensures vdd_cpu
voltage is set correctly when we switch back to pll_x.

> the DFLL driver does NOT know about PWM vs I2C? However, we still know
> at probe time what is used right?

No, I meant the DFLL driver does know about PWM vs I2C, but the cpufreq
driver does not.

Peter.

> 
> Cheers
> Jon
> 
> -- 
> nvpublic

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

* Re: [PATCH v2 3/6] clk: tegra: add CVB tables for Tegra210 CPU DFLL
  2018-01-31 10:50     ` Jon Hunter
@ 2018-02-01 10:39         ` Peter De Schrijver
  -1 siblings, 0 replies; 56+ messages in thread
From: Peter De Schrijver @ 2018-02-01 10:39 UTC (permalink / raw)
  To: Jon Hunter
  Cc: linux-tegra-u79uwXL29TY76Z2rM5mHXA,
	linux-clk-u79uwXL29TY76Z2rM5mHXA,
	mturquette-rdvid1DuHRBWk0Htik3J/w, sboyd-sgV2jX0FEOL9JmXXK+q4OQ

On Wed, Jan 31, 2018 at 10:50:31AM +0000, Jon Hunter wrote:
> 
> On 24/01/18 12:45, Peter De Schrijver wrote:
> > Signed-off-by: Peter De Schrijver <pdeschrijver-DDmLM1+adcrQT0dZR+AlfA@public.gmane.org>
> > ---
> >  drivers/clk/tegra/clk-tegra124-dfll-fcpu.c | 426 +++++++++++++++++++++++++++++
> >  drivers/clk/tegra/cvb.h                    |   1 +
> >  2 files changed, 427 insertions(+)
> > 
> > diff --git a/drivers/clk/tegra/clk-tegra124-dfll-fcpu.c b/drivers/clk/tegra/clk-tegra124-dfll-fcpu.c
> > index 6205ce1..9b61f80 100644
> > --- a/drivers/clk/tegra/clk-tegra124-dfll-fcpu.c
> > +++ b/drivers/clk/tegra/clk-tegra124-dfll-fcpu.c
> > @@ -90,6 +90,421 @@ struct dfll_fcpu_data {
> >  	},
> >  };
> >  
> > +static const unsigned long tegra210_cpu_max_freq_table[] = {
> > +	[0] = 1912500000UL,
> > +	[1] = 1912500000UL,
> 
> Any reason why there are two the same here? FWIW I also see this in the
> nvidia downstream kernel. Just seems odd.
> 	

Not sure why speedo 0 and 1 have the same max frequency. Maybe those were
never characterized beyond the initially planned max frequency.

> > +	[2] = 2218500000UL,
> > +	[3] = 1785000000UL,
> > +	[4] = 1632000000UL,
> > +	[5] = 1912500000UL,
> > +	[6] = 2014500000UL,
> > +	[7] = 1734000000UL,
> > +	[8] = 1683000000UL,
> > +	[9] = 1555500000UL,
> > +	[10] = 1504500000UL,
> > +};
> > +
> > +#define CPU_CVB_TABLE \
> > +	.speedo_scale = 100,	\
> > +	.voltage_scale = 1000,	\
> > +	.entries = {		\
> > +		{204000000UL,	{1007452, -23865, 370} }, \
> > +		{306000000UL,	{1052709, -24875, 370} }, \
> > +		{408000000UL,	{1099069, -25895, 370} }, \
> > +		{510000000UL,	{1146534, -26905, 370} }, \
> > +		{612000000UL,	{1195102, -27915, 370} }, \
> > +		{714000000UL,	{1244773, -28925, 370} }, \
> > +		{816000000UL,	{1295549, -29935, 370} }, \
> > +		{918000000UL,	{1347428, -30955, 370} }, \
> > +		{1020000000UL,	{1400411, -31965, 370} }, \
> > +		{1122000000UL,	{1454497, -32975, 370} }, \
> > +		{1224000000UL,	{1509687, -33985, 370} }, \
> > +		{1326000000UL,	{1565981, -35005, 370} }, \
> > +		{1428000000UL,	{1623379, -36015, 370} }, \
> > +		{1530000000UL,	{1681880, -37025, 370} }, \
> > +		{1632000000UL,	{1741485, -38035, 370} }, \
> > +		{1734000000UL,	{1802194, -39055, 370} }, \
> > +		{1836000000UL,	{1864006, -40065, 370} }, \
> > +		{1912500000UL,	{1910780, -40815, 370} }, \
> > +		{2014500000UL,	{1227000,      0,   0} }, \
> > +		{2218500000UL,	{1227000,      0,   0} }, \
> > +		{0,		{      0,      0,   0} }, \
> > +	}
> > +
> > +#define CPU_CVB_TABLE_XA \
> > +	.speedo_scale = 100,	\
> > +	.voltage_scale = 1000,	\
> > +	.entries = {		\
> > +		{204000000UL,	{1250024, -39785, 565} }, \
> > +		{306000000UL,	{1297556, -41145, 565} }, \
> > +		{408000000UL,	{1346718, -42505, 565} }, \
> > +		{510000000UL,	{1397511, -43855, 565} }, \
> > +		{612000000UL,	{1449933, -45215, 565} }, \
> > +		{714000000UL,	{1503986, -46575, 565} }, \
> > +		{816000000UL,	{1559669, -47935, 565} }, \
> > +		{918000000UL,	{1616982, -49295, 565} }, \
> > +		{1020000000UL,	{1675926, -50645, 565} }, \
> > +		{1122000000UL,	{1736500, -52005, 565} }, \
> > +		{1224000000UL,	{1798704, -53365, 565} }, \
> > +		{1326000000UL,	{1862538, -54725, 565} }, \
> > +		{1428000000UL,	{1928003, -56085, 565} }, \
> > +		{1530000000UL,	{1995097, -57435, 565} }, \
> > +		{1606500000UL,	{2046149, -58445, 565} }, \
> > +		{1632000000UL,	{2063822, -58795, 565} }, \
> > +		{0,		{      0,      0,   0} }, \
> > +	}
> > +
> > +#define CPU_CVB_TABLE_EUCM1 \
> > +	.speedo_scale = 100,	\
> > +	.voltage_scale = 1000,	\
> > +	.entries = {		\
> > +		{204000000UL,	{734429, 0, 0} }, \
> > +		{306000000UL,	{768191, 0, 0} }, \
> > +		{408000000UL,	{801953, 0, 0} }, \
> > +		{510000000UL,	{835715, 0, 0} }, \
> > +		{612000000UL,	{869477, 0, 0} }, \
> > +		{714000000UL,	{903239, 0, 0} }, \
> > +		{816000000UL,	{937001, 0, 0} }, \
> > +		{918000000UL,	{970763, 0, 0} }, \
> > +		{1020000000UL,	{1004525, 0, 0} }, \
> > +		{1122000000UL,	{1038287, 0, 0} }, \
> > +		{1224000000UL,	{1072049, 0, 0} }, \
> > +		{1326000000UL,	{1105811, 0, 0} }, \
> > +		{1428000000UL,	{1130000, 0, 0} }, \
> > +		{1555500000UL,	{1130000, 0, 0} }, \
> > +		{1632000000UL,	{1170000, 0, 0} }, \
> > +		{1734000000UL,	{1227500, 0, 0} }, \
> > +		{0,		{      0, 0, 0} }, \
> > +	}
> > +
> > +#define CPU_CVB_TABLE_EUCM2 \
> > +	.speedo_scale = 100,	\
> > +	.voltage_scale = 1000,	\
> > +	.entries = {		\
> > +		{204000000UL,	{742283, 0, 0} }, \
> > +		{306000000UL,	{776249, 0, 0} }, \
> > +		{408000000UL,	{810215, 0, 0} }, \
> > +		{510000000UL,	{844181, 0, 0} }, \
> > +		{612000000UL,	{878147, 0, 0} }, \
> > +		{714000000UL,	{912113, 0, 0} }, \
> > +		{816000000UL,	{946079, 0, 0} }, \
> > +		{918000000UL,	{980045, 0, 0} }, \
> > +		{1020000000UL,	{1014011, 0, 0} }, \
> > +		{1122000000UL,	{1047977, 0, 0} }, \
> > +		{1224000000UL,	{1081943, 0, 0} }, \
> > +		{1326000000UL,	{1090000, 0, 0} }, \
> > +		{1479000000UL,	{1090000, 0, 0} }, \
> > +		{1555500000UL,	{1162000, 0, 0} }, \
> > +		{1683000000UL,	{1195000, 0, 0} }, \
> > +		{0,		{      0, 0, 0} }, \
> > +	}
> > +
> > +#define CPU_CVB_TABLE_EUCM2_JOINT_RAIL \
> > +	.speedo_scale = 100,	\
> > +	.voltage_scale = 1000,	\
> > +	.entries = {		\
> > +		{204000000UL,	{742283, 0, 0} }, \
> > +		{306000000UL,	{776249, 0, 0} }, \
> > +		{408000000UL,	{810215, 0, 0} }, \
> > +		{510000000UL,	{844181, 0, 0} }, \
> > +		{612000000UL,	{878147, 0, 0} }, \
> > +		{714000000UL,	{912113, 0, 0} }, \
> > +		{816000000UL,	{946079, 0, 0} }, \
> > +		{918000000UL,	{980045, 0, 0} }, \
> > +		{1020000000UL,	{1014011, 0, 0} }, \
> > +		{1122000000UL,	{1047977, 0, 0} }, \
> > +		{1224000000UL,	{1081943, 0, 0} }, \
> > +		{1326000000UL,	{1090000, 0, 0} }, \
> > +		{1479000000UL,	{1090000, 0, 0} }, \
> > +		{1504500000UL,	{1120000, 0, 0} }, \
> > +		{0,		{      0, 0, 0} }, \
> > +	}
> > +
> > +#define CPU_CVB_TABLE_ODN \
> > +	.speedo_scale = 100,	\
> > +	.voltage_scale = 1000,	\
> > +	.entries = {		\
> > +		{204000000UL,	{721094, 0, 0} }, \
> > +		{306000000UL,	{754040, 0, 0} }, \
> > +		{408000000UL,	{786986, 0, 0} }, \
> > +		{510000000UL,	{819932, 0, 0} }, \
> > +		{612000000UL,	{852878, 0, 0} }, \
> > +		{714000000UL,	{885824, 0, 0} }, \
> > +		{816000000UL,	{918770, 0, 0} }, \
> > +		{918000000UL,	{915716, 0, 0} }, \
> > +		{1020000000UL,	{984662, 0, 0} }, \
> > +		{1122000000UL,	{1017608, 0, 0} }, \
> > +		{1224000000UL,	{1050554, 0, 0} }, \
> > +		{1326000000UL,	{1083500, 0, 0} }, \
> > +		{1428000000UL,	{1116446, 0, 0} }, \
> > +		{1581000000UL,	{1130000, 0, 0} }, \
> > +		{1683000000UL,	{1168000, 0, 0} }, \
> > +		{1785000000UL,	{1227500, 0, 0} }, \
> > +		{0,		{      0, 0, 0} }, \
> > +	}
> > +
> > +struct cvb_table tegra210_cpu_cvb_tables[] = {
> > +	{
> > +		.speedo_id = 10,
> > +		.process_id = 0,
> > +		.min_millivolts = 840,
> > +		.max_millivolts = 1120,
> > +		CPU_CVB_TABLE_EUCM2_JOINT_RAIL,
> > +		.cpu_dfll_data = {
> > +			.tune0_low = 0xffead0ff,
> > +			.tune0_high = 0xffead0ff,
> > +			.tune1 = 0x20091d9,
> > +			.tune_high_min_millivolts = 864,
> > +		}
> > +	},
> > +	{
> > +		.speedo_id = 10,
> > +		.process_id = 1,
> > +		.min_millivolts = 840,
> > +		.max_millivolts = 1120,
> > +		CPU_CVB_TABLE_EUCM2_JOINT_RAIL,
> > +		.cpu_dfll_data = {
> > +			.tune0_low = 0xffead0ff,
> > +			.tune0_high = 0xffead0ff,
> > +			.tune1 = 0x20091d9,
> > +			.tune_high_min_millivolts = 864,
> > +		}
> > +	},
> > +	{
> > +		.speedo_id = 9,
> > +		.process_id = 0,
> > +		.min_millivolts = 900,
> > +		.max_millivolts = 1162,
> > +		CPU_CVB_TABLE_EUCM2,
> > +		.cpu_dfll_data = {
> > +			.tune0_low = 0xffead0ff,
> > +			.tune0_high = 0xffead0ff,
> > +			.tune1 = 0x20091d9,
> > +		}
> > +	},
> > +	{
> > +		.speedo_id = 9,
> > +		.process_id = 1,
> > +		.min_millivolts = 900,
> > +		.max_millivolts = 1162,
> > +		CPU_CVB_TABLE_EUCM2,
> > +		.cpu_dfll_data = {
> > +			.tune0_low = 0xffead0ff,
> > +			.tune0_high = 0xffead0ff,
> > +			.tune1 = 0x20091d9,
> > +		}
> > +	},
> > +	{
> > +		.speedo_id = 8,
> > +		.process_id = 0,
> > +		.min_millivolts = 900,
> > +		.max_millivolts = 1195,
> > +		CPU_CVB_TABLE_EUCM2,
> > +		.cpu_dfll_data = {
> > +			.tune0_low = 0xffead0ff,
> > +			.tune0_high = 0xffead0ff,
> > +			.tune1 = 0x20091d9,
> > +		}
> > +	},
> > +	{
> > +		.speedo_id = 8,
> > +		.process_id = 1,
> > +		.min_millivolts = 900,
> > +		.max_millivolts = 1195,
> > +		CPU_CVB_TABLE_EUCM2,
> > +		.cpu_dfll_data = {
> > +			.tune0_low = 0xffead0ff,
> > +			.tune0_high = 0xffead0ff,
> > +			.tune1 = 0x20091d9,
> > +		}
> > +	},
> > +	{
> > +		.speedo_id = 7,
> > +		.process_id = 0,
> > +		.min_millivolts = 841,
> > +		.max_millivolts = 1227,
> > +		CPU_CVB_TABLE_EUCM1,
> > +		.cpu_dfll_data = {
> > +			.tune0_low = 0xffead0ff,
> > +			.tune0_high = 0xffead0ff,
> > +			.tune1 = 0x20091d9,
> > +			.tune_high_min_millivolts = 864,
> > +		}
> > +	},
> > +	{
> > +		.speedo_id = 7,
> > +		.process_id = 1,
> > +		.min_millivolts = 841,
> > +		.max_millivolts = 1227,
> > +		CPU_CVB_TABLE_EUCM1,
> > +		.cpu_dfll_data = {
> > +			.tune0_low = 0xffead0ff,
> > +			.tune0_high = 0xffead0ff,
> > +			.tune1 = 0x20091d9,
> > +			.tune_high_min_millivolts = 864,
> > +		}
> > +	},
> > +	{
> > +		.speedo_id = 6,
> > +		.process_id = 0,
> > +		.min_millivolts = 870,
> > +		.max_millivolts = 1150,
> > +		CPU_CVB_TABLE,
> > +		.cpu_dfll_data = {
> > +			.tune0_low = 0xffead0ff,
> > +			.tune1 = 0x20091d9,
> > +		}
> > +	},
> > +	{
> > +		.speedo_id = 6,
> > +		.process_id = 1,
> > +		.min_millivolts = 870,
> > +		.max_millivolts = 1150,
> > +		CPU_CVB_TABLE,
> > +		.cpu_dfll_data = {
> > +			.tune0_low = 0xffead0ff,
> > +			.tune1 = 0x25501d0,
> > +		}
> > +	},
> > +	{
> > +		.speedo_id = 5,
> > +		.process_id = 0,
> > +		.min_millivolts = 818,
> > +		.max_millivolts = 1227,
> > +		CPU_CVB_TABLE,
> > +		.cpu_dfll_data = {
> > +			.tune0_low = 0xffead0ff,
> > +			.tune0_high = 0xffead0ff,
> > +			.tune1 = 0x20091d9,
> > +			.tune_high_min_millivolts = 864,
> > +		}
> > +	},
> > +	{
> > +		.speedo_id = 5,
> > +		.process_id = 1,
> > +		.min_millivolts = 818,
> > +		.max_millivolts = 1227,
> > +		CPU_CVB_TABLE,
> > +		.cpu_dfll_data = {
> > +			.tune0_low = 0xffead0ff,
> > +			.tune0_high = 0xffead0ff,
> > +			.tune1 = 0x25501d0,
> > +			.tune_high_min_millivolts = 864,
> > +		}
> > +	},
> > +	{
> > +		.speedo_id = 4,
> > +		.process_id = -1,
> > +		.min_millivolts = 918,
> > +		.max_millivolts = 1113,
> > +		CPU_CVB_TABLE_XA,
> > +		.cpu_dfll_data = {
> > +			.tune0_low = 0xffead0ff,
> > +			.tune1 = 0x17711BD,
> > +		}
> > +	},
> > +	{
> > +		.speedo_id = 3,
> > +		.process_id = 0,
> > +		.min_millivolts = 825,
> > +		.max_millivolts = 1227,
> > +		CPU_CVB_TABLE_ODN,
> > +		.cpu_dfll_data = {
> > +			.tune0_low = 0xffead0ff,
> > +			.tune0_high = 0xffead0ff,
> > +			.tune1 = 0x20091d9,
> > +			.tune_high_min_millivolts = 864,
> > +		}
> > +	},
> > +	{
> > +		.speedo_id = 3,
> > +		.process_id = 1,
> > +		.min_millivolts = 825,
> > +		.max_millivolts = 1227,
> > +		CPU_CVB_TABLE_ODN,
> > +		.cpu_dfll_data = {
> > +			.tune0_low = 0xffead0ff,
> > +			.tune0_high = 0xffead0ff,
> > +			.tune1 = 0x25501d0,
> > +			.tune_high_min_millivolts = 864,
> > +		}
> > +	},
> > +	{
> > +		.speedo_id = 2,
> > +		.process_id = 0,
> > +		.min_millivolts = 870,
> > +		.max_millivolts = 1227,
> > +		CPU_CVB_TABLE,
> > +		.cpu_dfll_data = {
> > +			.tune0_low = 0xffead0ff,
> > +			.tune1 = 0x20091d9,
> > +		}
> > +	},
> > +	{
> > +		.speedo_id = 2,
> > +		.process_id = 1,
> > +		.min_millivolts = 870,
> > +		.max_millivolts = 1227,
> > +		CPU_CVB_TABLE,
> > +		.cpu_dfll_data = {
> > +			.tune0_low = 0xffead0ff,
> > +			.tune1 = 0x25501d0,
> > +		}
> > +	},
> > +	{
> > +		.speedo_id = 1,
> > +		.process_id = 0,
> > +		.min_millivolts = 837,
> > +		.max_millivolts = 1227,
> > +		CPU_CVB_TABLE,
> > +		.cpu_dfll_data = {
> > +			.tune0_low = 0xffead0ff,
> > +			.tune0_high = 0xffead0ff,
> > +			.tune1 = 0x20091d9,
> > +			.tune_high_min_millivolts = 864,
> > +		}
> > +	},
> > +	{
> > +		.speedo_id = 1,
> > +		.process_id = 1,
> > +		.min_millivolts = 837,
> > +		.max_millivolts = 1227,
> > +		CPU_CVB_TABLE,
> > +		.cpu_dfll_data = {
> > +			.tune0_low = 0xffead0ff,
> > +			.tune0_high = 0xffead0ff,
> > +			.tune1 = 0x25501d0,
> > +			.tune_high_min_millivolts = 864,
> > +		}
> > +	},
> > +	{
> > +		.speedo_id = 0,
> > +		.process_id = 0,
> > +		.min_millivolts = 850,
> > +		.max_millivolts = 1170,
> > +		CPU_CVB_TABLE,
> > +		.cpu_dfll_data = {
> > +			.tune0_low = 0xffead0ff,
> > +			.tune0_high = 0xffead0ff,
> > +			.tune1 = 0x20091d9,
> > +			.tune_high_min_millivolts = 864,
> > +		}
> > +	},
> > +	{
> > +		.speedo_id = 0,
> > +		.process_id = 1,
> > +		.min_millivolts = 850,
> > +		.max_millivolts = 1170,
> > +		CPU_CVB_TABLE,
> > +		.cpu_dfll_data = {
> > +			.tune0_low = 0xffead0ff,
> > +			.tune0_high = 0xffead0ff,
> > +			.tune1 = 0x25501d0,
> > +			.tune_high_min_millivolts = 864,
> > +		}
> > +	},
> > +};
> > +
> >  static const struct dfll_fcpu_data tegra124_dfll_fcpu_data = {
> >  	.cpu_max_freq_table = tegra124_cpu_max_freq_table,
> >  	.cpu_max_freq_table_size = ARRAY_SIZE(tegra124_cpu_max_freq_table),
> > @@ -97,11 +512,22 @@ struct dfll_fcpu_data {
> >  	.cpu_cvb_tables_size = ARRAY_SIZE(tegra124_cpu_cvb_tables)
> >  };
> >  
> > +static const struct dfll_fcpu_data tegra210_dfll_fcpu_data = {
> > +	.cpu_max_freq_table = tegra210_cpu_max_freq_table,
> > +	.cpu_max_freq_table_size = ARRAY_SIZE(tegra210_cpu_max_freq_table),
> > +	.cpu_cvb_tables = tegra210_cpu_cvb_tables,
> > +	.cpu_cvb_tables_size = ARRAY_SIZE(tegra210_cpu_cvb_tables),
> > +};
> > +
> >  static const struct of_device_id tegra124_dfll_fcpu_of_match[] = {
> >  	{
> >  		.compatible = "nvidia,tegra124-dfll",
> >  		.data = &tegra124_dfll_fcpu_data,
> >  	},
> > +	{
> > +		.compatible = "nvidia,tegra210-dfll",
> > +		.data = &tegra210_dfll_fcpu_data
> > +	},
> >  	{ },
> >  };
> >  
> > diff --git a/drivers/clk/tegra/cvb.h b/drivers/clk/tegra/cvb.h
> > index cfa110f..72c0a2d 100644
> > --- a/drivers/clk/tegra/cvb.h
> > +++ b/drivers/clk/tegra/cvb.h
> > @@ -41,6 +41,7 @@ struct cvb_cpu_dfll_data {
> >  	u32 tune0_low;
> >  	u32 tune0_high;
> >  	u32 tune1;
> > +	unsigned int tune_high_min_millivolts;
> >  };
> >  
> >  struct cvb_table {
> > 
> 
> Otherwise ...
> 
> Reviewed-by: Jon Hunter <jonathanh-DDmLM1+adcrQT0dZR+AlfA@public.gmane.org>
> 
> Cheers
> Jon
> 
> -- 
> nvpublic

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

* Re: [PATCH v2 3/6] clk: tegra: add CVB tables for Tegra210 CPU DFLL
@ 2018-02-01 10:39         ` Peter De Schrijver
  0 siblings, 0 replies; 56+ messages in thread
From: Peter De Schrijver @ 2018-02-01 10:39 UTC (permalink / raw)
  To: Jon Hunter; +Cc: linux-tegra, linux-clk, mturquette, sboyd

On Wed, Jan 31, 2018 at 10:50:31AM +0000, Jon Hunter wrote:
> 
> On 24/01/18 12:45, Peter De Schrijver wrote:
> > Signed-off-by: Peter De Schrijver <pdeschrijver@nvidia.com>
> > ---
> >  drivers/clk/tegra/clk-tegra124-dfll-fcpu.c | 426 +++++++++++++++++++++++++++++
> >  drivers/clk/tegra/cvb.h                    |   1 +
> >  2 files changed, 427 insertions(+)
> > 
> > diff --git a/drivers/clk/tegra/clk-tegra124-dfll-fcpu.c b/drivers/clk/tegra/clk-tegra124-dfll-fcpu.c
> > index 6205ce1..9b61f80 100644
> > --- a/drivers/clk/tegra/clk-tegra124-dfll-fcpu.c
> > +++ b/drivers/clk/tegra/clk-tegra124-dfll-fcpu.c
> > @@ -90,6 +90,421 @@ struct dfll_fcpu_data {
> >  	},
> >  };
> >  
> > +static const unsigned long tegra210_cpu_max_freq_table[] = {
> > +	[0] = 1912500000UL,
> > +	[1] = 1912500000UL,
> 
> Any reason why there are two the same here? FWIW I also see this in the
> nvidia downstream kernel. Just seems odd.
> 	

Not sure why speedo 0 and 1 have the same max frequency. Maybe those were
never characterized beyond the initially planned max frequency.

> > +	[2] = 2218500000UL,
> > +	[3] = 1785000000UL,
> > +	[4] = 1632000000UL,
> > +	[5] = 1912500000UL,
> > +	[6] = 2014500000UL,
> > +	[7] = 1734000000UL,
> > +	[8] = 1683000000UL,
> > +	[9] = 1555500000UL,
> > +	[10] = 1504500000UL,
> > +};
> > +
> > +#define CPU_CVB_TABLE \
> > +	.speedo_scale = 100,	\
> > +	.voltage_scale = 1000,	\
> > +	.entries = {		\
> > +		{204000000UL,	{1007452, -23865, 370} }, \
> > +		{306000000UL,	{1052709, -24875, 370} }, \
> > +		{408000000UL,	{1099069, -25895, 370} }, \
> > +		{510000000UL,	{1146534, -26905, 370} }, \
> > +		{612000000UL,	{1195102, -27915, 370} }, \
> > +		{714000000UL,	{1244773, -28925, 370} }, \
> > +		{816000000UL,	{1295549, -29935, 370} }, \
> > +		{918000000UL,	{1347428, -30955, 370} }, \
> > +		{1020000000UL,	{1400411, -31965, 370} }, \
> > +		{1122000000UL,	{1454497, -32975, 370} }, \
> > +		{1224000000UL,	{1509687, -33985, 370} }, \
> > +		{1326000000UL,	{1565981, -35005, 370} }, \
> > +		{1428000000UL,	{1623379, -36015, 370} }, \
> > +		{1530000000UL,	{1681880, -37025, 370} }, \
> > +		{1632000000UL,	{1741485, -38035, 370} }, \
> > +		{1734000000UL,	{1802194, -39055, 370} }, \
> > +		{1836000000UL,	{1864006, -40065, 370} }, \
> > +		{1912500000UL,	{1910780, -40815, 370} }, \
> > +		{2014500000UL,	{1227000,      0,   0} }, \
> > +		{2218500000UL,	{1227000,      0,   0} }, \
> > +		{0,		{      0,      0,   0} }, \
> > +	}
> > +
> > +#define CPU_CVB_TABLE_XA \
> > +	.speedo_scale = 100,	\
> > +	.voltage_scale = 1000,	\
> > +	.entries = {		\
> > +		{204000000UL,	{1250024, -39785, 565} }, \
> > +		{306000000UL,	{1297556, -41145, 565} }, \
> > +		{408000000UL,	{1346718, -42505, 565} }, \
> > +		{510000000UL,	{1397511, -43855, 565} }, \
> > +		{612000000UL,	{1449933, -45215, 565} }, \
> > +		{714000000UL,	{1503986, -46575, 565} }, \
> > +		{816000000UL,	{1559669, -47935, 565} }, \
> > +		{918000000UL,	{1616982, -49295, 565} }, \
> > +		{1020000000UL,	{1675926, -50645, 565} }, \
> > +		{1122000000UL,	{1736500, -52005, 565} }, \
> > +		{1224000000UL,	{1798704, -53365, 565} }, \
> > +		{1326000000UL,	{1862538, -54725, 565} }, \
> > +		{1428000000UL,	{1928003, -56085, 565} }, \
> > +		{1530000000UL,	{1995097, -57435, 565} }, \
> > +		{1606500000UL,	{2046149, -58445, 565} }, \
> > +		{1632000000UL,	{2063822, -58795, 565} }, \
> > +		{0,		{      0,      0,   0} }, \
> > +	}
> > +
> > +#define CPU_CVB_TABLE_EUCM1 \
> > +	.speedo_scale = 100,	\
> > +	.voltage_scale = 1000,	\
> > +	.entries = {		\
> > +		{204000000UL,	{734429, 0, 0} }, \
> > +		{306000000UL,	{768191, 0, 0} }, \
> > +		{408000000UL,	{801953, 0, 0} }, \
> > +		{510000000UL,	{835715, 0, 0} }, \
> > +		{612000000UL,	{869477, 0, 0} }, \
> > +		{714000000UL,	{903239, 0, 0} }, \
> > +		{816000000UL,	{937001, 0, 0} }, \
> > +		{918000000UL,	{970763, 0, 0} }, \
> > +		{1020000000UL,	{1004525, 0, 0} }, \
> > +		{1122000000UL,	{1038287, 0, 0} }, \
> > +		{1224000000UL,	{1072049, 0, 0} }, \
> > +		{1326000000UL,	{1105811, 0, 0} }, \
> > +		{1428000000UL,	{1130000, 0, 0} }, \
> > +		{1555500000UL,	{1130000, 0, 0} }, \
> > +		{1632000000UL,	{1170000, 0, 0} }, \
> > +		{1734000000UL,	{1227500, 0, 0} }, \
> > +		{0,		{      0, 0, 0} }, \
> > +	}
> > +
> > +#define CPU_CVB_TABLE_EUCM2 \
> > +	.speedo_scale = 100,	\
> > +	.voltage_scale = 1000,	\
> > +	.entries = {		\
> > +		{204000000UL,	{742283, 0, 0} }, \
> > +		{306000000UL,	{776249, 0, 0} }, \
> > +		{408000000UL,	{810215, 0, 0} }, \
> > +		{510000000UL,	{844181, 0, 0} }, \
> > +		{612000000UL,	{878147, 0, 0} }, \
> > +		{714000000UL,	{912113, 0, 0} }, \
> > +		{816000000UL,	{946079, 0, 0} }, \
> > +		{918000000UL,	{980045, 0, 0} }, \
> > +		{1020000000UL,	{1014011, 0, 0} }, \
> > +		{1122000000UL,	{1047977, 0, 0} }, \
> > +		{1224000000UL,	{1081943, 0, 0} }, \
> > +		{1326000000UL,	{1090000, 0, 0} }, \
> > +		{1479000000UL,	{1090000, 0, 0} }, \
> > +		{1555500000UL,	{1162000, 0, 0} }, \
> > +		{1683000000UL,	{1195000, 0, 0} }, \
> > +		{0,		{      0, 0, 0} }, \
> > +	}
> > +
> > +#define CPU_CVB_TABLE_EUCM2_JOINT_RAIL \
> > +	.speedo_scale = 100,	\
> > +	.voltage_scale = 1000,	\
> > +	.entries = {		\
> > +		{204000000UL,	{742283, 0, 0} }, \
> > +		{306000000UL,	{776249, 0, 0} }, \
> > +		{408000000UL,	{810215, 0, 0} }, \
> > +		{510000000UL,	{844181, 0, 0} }, \
> > +		{612000000UL,	{878147, 0, 0} }, \
> > +		{714000000UL,	{912113, 0, 0} }, \
> > +		{816000000UL,	{946079, 0, 0} }, \
> > +		{918000000UL,	{980045, 0, 0} }, \
> > +		{1020000000UL,	{1014011, 0, 0} }, \
> > +		{1122000000UL,	{1047977, 0, 0} }, \
> > +		{1224000000UL,	{1081943, 0, 0} }, \
> > +		{1326000000UL,	{1090000, 0, 0} }, \
> > +		{1479000000UL,	{1090000, 0, 0} }, \
> > +		{1504500000UL,	{1120000, 0, 0} }, \
> > +		{0,		{      0, 0, 0} }, \
> > +	}
> > +
> > +#define CPU_CVB_TABLE_ODN \
> > +	.speedo_scale = 100,	\
> > +	.voltage_scale = 1000,	\
> > +	.entries = {		\
> > +		{204000000UL,	{721094, 0, 0} }, \
> > +		{306000000UL,	{754040, 0, 0} }, \
> > +		{408000000UL,	{786986, 0, 0} }, \
> > +		{510000000UL,	{819932, 0, 0} }, \
> > +		{612000000UL,	{852878, 0, 0} }, \
> > +		{714000000UL,	{885824, 0, 0} }, \
> > +		{816000000UL,	{918770, 0, 0} }, \
> > +		{918000000UL,	{915716, 0, 0} }, \
> > +		{1020000000UL,	{984662, 0, 0} }, \
> > +		{1122000000UL,	{1017608, 0, 0} }, \
> > +		{1224000000UL,	{1050554, 0, 0} }, \
> > +		{1326000000UL,	{1083500, 0, 0} }, \
> > +		{1428000000UL,	{1116446, 0, 0} }, \
> > +		{1581000000UL,	{1130000, 0, 0} }, \
> > +		{1683000000UL,	{1168000, 0, 0} }, \
> > +		{1785000000UL,	{1227500, 0, 0} }, \
> > +		{0,		{      0, 0, 0} }, \
> > +	}
> > +
> > +struct cvb_table tegra210_cpu_cvb_tables[] = {
> > +	{
> > +		.speedo_id = 10,
> > +		.process_id = 0,
> > +		.min_millivolts = 840,
> > +		.max_millivolts = 1120,
> > +		CPU_CVB_TABLE_EUCM2_JOINT_RAIL,
> > +		.cpu_dfll_data = {
> > +			.tune0_low = 0xffead0ff,
> > +			.tune0_high = 0xffead0ff,
> > +			.tune1 = 0x20091d9,
> > +			.tune_high_min_millivolts = 864,
> > +		}
> > +	},
> > +	{
> > +		.speedo_id = 10,
> > +		.process_id = 1,
> > +		.min_millivolts = 840,
> > +		.max_millivolts = 1120,
> > +		CPU_CVB_TABLE_EUCM2_JOINT_RAIL,
> > +		.cpu_dfll_data = {
> > +			.tune0_low = 0xffead0ff,
> > +			.tune0_high = 0xffead0ff,
> > +			.tune1 = 0x20091d9,
> > +			.tune_high_min_millivolts = 864,
> > +		}
> > +	},
> > +	{
> > +		.speedo_id = 9,
> > +		.process_id = 0,
> > +		.min_millivolts = 900,
> > +		.max_millivolts = 1162,
> > +		CPU_CVB_TABLE_EUCM2,
> > +		.cpu_dfll_data = {
> > +			.tune0_low = 0xffead0ff,
> > +			.tune0_high = 0xffead0ff,
> > +			.tune1 = 0x20091d9,
> > +		}
> > +	},
> > +	{
> > +		.speedo_id = 9,
> > +		.process_id = 1,
> > +		.min_millivolts = 900,
> > +		.max_millivolts = 1162,
> > +		CPU_CVB_TABLE_EUCM2,
> > +		.cpu_dfll_data = {
> > +			.tune0_low = 0xffead0ff,
> > +			.tune0_high = 0xffead0ff,
> > +			.tune1 = 0x20091d9,
> > +		}
> > +	},
> > +	{
> > +		.speedo_id = 8,
> > +		.process_id = 0,
> > +		.min_millivolts = 900,
> > +		.max_millivolts = 1195,
> > +		CPU_CVB_TABLE_EUCM2,
> > +		.cpu_dfll_data = {
> > +			.tune0_low = 0xffead0ff,
> > +			.tune0_high = 0xffead0ff,
> > +			.tune1 = 0x20091d9,
> > +		}
> > +	},
> > +	{
> > +		.speedo_id = 8,
> > +		.process_id = 1,
> > +		.min_millivolts = 900,
> > +		.max_millivolts = 1195,
> > +		CPU_CVB_TABLE_EUCM2,
> > +		.cpu_dfll_data = {
> > +			.tune0_low = 0xffead0ff,
> > +			.tune0_high = 0xffead0ff,
> > +			.tune1 = 0x20091d9,
> > +		}
> > +	},
> > +	{
> > +		.speedo_id = 7,
> > +		.process_id = 0,
> > +		.min_millivolts = 841,
> > +		.max_millivolts = 1227,
> > +		CPU_CVB_TABLE_EUCM1,
> > +		.cpu_dfll_data = {
> > +			.tune0_low = 0xffead0ff,
> > +			.tune0_high = 0xffead0ff,
> > +			.tune1 = 0x20091d9,
> > +			.tune_high_min_millivolts = 864,
> > +		}
> > +	},
> > +	{
> > +		.speedo_id = 7,
> > +		.process_id = 1,
> > +		.min_millivolts = 841,
> > +		.max_millivolts = 1227,
> > +		CPU_CVB_TABLE_EUCM1,
> > +		.cpu_dfll_data = {
> > +			.tune0_low = 0xffead0ff,
> > +			.tune0_high = 0xffead0ff,
> > +			.tune1 = 0x20091d9,
> > +			.tune_high_min_millivolts = 864,
> > +		}
> > +	},
> > +	{
> > +		.speedo_id = 6,
> > +		.process_id = 0,
> > +		.min_millivolts = 870,
> > +		.max_millivolts = 1150,
> > +		CPU_CVB_TABLE,
> > +		.cpu_dfll_data = {
> > +			.tune0_low = 0xffead0ff,
> > +			.tune1 = 0x20091d9,
> > +		}
> > +	},
> > +	{
> > +		.speedo_id = 6,
> > +		.process_id = 1,
> > +		.min_millivolts = 870,
> > +		.max_millivolts = 1150,
> > +		CPU_CVB_TABLE,
> > +		.cpu_dfll_data = {
> > +			.tune0_low = 0xffead0ff,
> > +			.tune1 = 0x25501d0,
> > +		}
> > +	},
> > +	{
> > +		.speedo_id = 5,
> > +		.process_id = 0,
> > +		.min_millivolts = 818,
> > +		.max_millivolts = 1227,
> > +		CPU_CVB_TABLE,
> > +		.cpu_dfll_data = {
> > +			.tune0_low = 0xffead0ff,
> > +			.tune0_high = 0xffead0ff,
> > +			.tune1 = 0x20091d9,
> > +			.tune_high_min_millivolts = 864,
> > +		}
> > +	},
> > +	{
> > +		.speedo_id = 5,
> > +		.process_id = 1,
> > +		.min_millivolts = 818,
> > +		.max_millivolts = 1227,
> > +		CPU_CVB_TABLE,
> > +		.cpu_dfll_data = {
> > +			.tune0_low = 0xffead0ff,
> > +			.tune0_high = 0xffead0ff,
> > +			.tune1 = 0x25501d0,
> > +			.tune_high_min_millivolts = 864,
> > +		}
> > +	},
> > +	{
> > +		.speedo_id = 4,
> > +		.process_id = -1,
> > +		.min_millivolts = 918,
> > +		.max_millivolts = 1113,
> > +		CPU_CVB_TABLE_XA,
> > +		.cpu_dfll_data = {
> > +			.tune0_low = 0xffead0ff,
> > +			.tune1 = 0x17711BD,
> > +		}
> > +	},
> > +	{
> > +		.speedo_id = 3,
> > +		.process_id = 0,
> > +		.min_millivolts = 825,
> > +		.max_millivolts = 1227,
> > +		CPU_CVB_TABLE_ODN,
> > +		.cpu_dfll_data = {
> > +			.tune0_low = 0xffead0ff,
> > +			.tune0_high = 0xffead0ff,
> > +			.tune1 = 0x20091d9,
> > +			.tune_high_min_millivolts = 864,
> > +		}
> > +	},
> > +	{
> > +		.speedo_id = 3,
> > +		.process_id = 1,
> > +		.min_millivolts = 825,
> > +		.max_millivolts = 1227,
> > +		CPU_CVB_TABLE_ODN,
> > +		.cpu_dfll_data = {
> > +			.tune0_low = 0xffead0ff,
> > +			.tune0_high = 0xffead0ff,
> > +			.tune1 = 0x25501d0,
> > +			.tune_high_min_millivolts = 864,
> > +		}
> > +	},
> > +	{
> > +		.speedo_id = 2,
> > +		.process_id = 0,
> > +		.min_millivolts = 870,
> > +		.max_millivolts = 1227,
> > +		CPU_CVB_TABLE,
> > +		.cpu_dfll_data = {
> > +			.tune0_low = 0xffead0ff,
> > +			.tune1 = 0x20091d9,
> > +		}
> > +	},
> > +	{
> > +		.speedo_id = 2,
> > +		.process_id = 1,
> > +		.min_millivolts = 870,
> > +		.max_millivolts = 1227,
> > +		CPU_CVB_TABLE,
> > +		.cpu_dfll_data = {
> > +			.tune0_low = 0xffead0ff,
> > +			.tune1 = 0x25501d0,
> > +		}
> > +	},
> > +	{
> > +		.speedo_id = 1,
> > +		.process_id = 0,
> > +		.min_millivolts = 837,
> > +		.max_millivolts = 1227,
> > +		CPU_CVB_TABLE,
> > +		.cpu_dfll_data = {
> > +			.tune0_low = 0xffead0ff,
> > +			.tune0_high = 0xffead0ff,
> > +			.tune1 = 0x20091d9,
> > +			.tune_high_min_millivolts = 864,
> > +		}
> > +	},
> > +	{
> > +		.speedo_id = 1,
> > +		.process_id = 1,
> > +		.min_millivolts = 837,
> > +		.max_millivolts = 1227,
> > +		CPU_CVB_TABLE,
> > +		.cpu_dfll_data = {
> > +			.tune0_low = 0xffead0ff,
> > +			.tune0_high = 0xffead0ff,
> > +			.tune1 = 0x25501d0,
> > +			.tune_high_min_millivolts = 864,
> > +		}
> > +	},
> > +	{
> > +		.speedo_id = 0,
> > +		.process_id = 0,
> > +		.min_millivolts = 850,
> > +		.max_millivolts = 1170,
> > +		CPU_CVB_TABLE,
> > +		.cpu_dfll_data = {
> > +			.tune0_low = 0xffead0ff,
> > +			.tune0_high = 0xffead0ff,
> > +			.tune1 = 0x20091d9,
> > +			.tune_high_min_millivolts = 864,
> > +		}
> > +	},
> > +	{
> > +		.speedo_id = 0,
> > +		.process_id = 1,
> > +		.min_millivolts = 850,
> > +		.max_millivolts = 1170,
> > +		CPU_CVB_TABLE,
> > +		.cpu_dfll_data = {
> > +			.tune0_low = 0xffead0ff,
> > +			.tune0_high = 0xffead0ff,
> > +			.tune1 = 0x25501d0,
> > +			.tune_high_min_millivolts = 864,
> > +		}
> > +	},
> > +};
> > +
> >  static const struct dfll_fcpu_data tegra124_dfll_fcpu_data = {
> >  	.cpu_max_freq_table = tegra124_cpu_max_freq_table,
> >  	.cpu_max_freq_table_size = ARRAY_SIZE(tegra124_cpu_max_freq_table),
> > @@ -97,11 +512,22 @@ struct dfll_fcpu_data {
> >  	.cpu_cvb_tables_size = ARRAY_SIZE(tegra124_cpu_cvb_tables)
> >  };
> >  
> > +static const struct dfll_fcpu_data tegra210_dfll_fcpu_data = {
> > +	.cpu_max_freq_table = tegra210_cpu_max_freq_table,
> > +	.cpu_max_freq_table_size = ARRAY_SIZE(tegra210_cpu_max_freq_table),
> > +	.cpu_cvb_tables = tegra210_cpu_cvb_tables,
> > +	.cpu_cvb_tables_size = ARRAY_SIZE(tegra210_cpu_cvb_tables),
> > +};
> > +
> >  static const struct of_device_id tegra124_dfll_fcpu_of_match[] = {
> >  	{
> >  		.compatible = "nvidia,tegra124-dfll",
> >  		.data = &tegra124_dfll_fcpu_data,
> >  	},
> > +	{
> > +		.compatible = "nvidia,tegra210-dfll",
> > +		.data = &tegra210_dfll_fcpu_data
> > +	},
> >  	{ },
> >  };
> >  
> > diff --git a/drivers/clk/tegra/cvb.h b/drivers/clk/tegra/cvb.h
> > index cfa110f..72c0a2d 100644
> > --- a/drivers/clk/tegra/cvb.h
> > +++ b/drivers/clk/tegra/cvb.h
> > @@ -41,6 +41,7 @@ struct cvb_cpu_dfll_data {
> >  	u32 tune0_low;
> >  	u32 tune0_high;
> >  	u32 tune1;
> > +	unsigned int tune_high_min_millivolts;
> >  };
> >  
> >  struct cvb_table {
> > 
> 
> Otherwise ...
> 
> Reviewed-by: Jon Hunter <jonathanh@nvidia.com>
> 
> Cheers
> Jon
> 
> -- 
> nvpublic

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

* Re: [PATCH v2 2/6] clk: tegra: DT align parameter for CVB calculation
  2018-02-01 10:30         ` Peter De Schrijver
@ 2018-02-01 10:54             ` Jon Hunter
  -1 siblings, 0 replies; 56+ messages in thread
From: Jon Hunter @ 2018-02-01 10:54 UTC (permalink / raw)
  To: Peter De Schrijver
  Cc: linux-tegra-u79uwXL29TY76Z2rM5mHXA,
	linux-clk-u79uwXL29TY76Z2rM5mHXA,
	mturquette-rdvid1DuHRBWk0Htik3J/w, sboyd-sgV2jX0FEOL9JmXXK+q4OQ


On 01/02/18 10:30, Peter De Schrijver wrote:
> On Wed, Jan 31, 2018 at 10:43:04AM +0000, Jon Hunter wrote:
>>
>> On 24/01/18 12:45, Peter De Schrijver wrote:
>>> When using a PWM controlled regulator, the voltage step and offset are
>>> determined by the regulator IC in use. This is specified in DT rather
>>> than fixed in the CVB table. Hence pass this information to the CVB table
>>> calculation function. For backwards compatibility the table values are used
>>> if the corresponding parameter is 0.
>>>
>>> Signed-off-by: Peter De Schrijver <pdeschrijver-DDmLM1+adcrQT0dZR+AlfA@public.gmane.org>
>>> ---
>>>  drivers/clk/tegra/clk-tegra124-dfll-fcpu.c | 23 +++++++++++++++++++++--
>>>  drivers/clk/tegra/cvb.c                    | 18 ++++++++++++++----
>>>  drivers/clk/tegra/cvb.h                    |  5 +++--
>>>  3 files changed, 38 insertions(+), 8 deletions(-)
>>>
>>> diff --git a/drivers/clk/tegra/clk-tegra124-dfll-fcpu.c b/drivers/clk/tegra/clk-tegra124-dfll-fcpu.c
>>> index 440eb8d..6205ce1 100644
>>> --- a/drivers/clk/tegra/clk-tegra124-dfll-fcpu.c
>>> +++ b/drivers/clk/tegra/clk-tegra124-dfll-fcpu.c
>>> @@ -111,6 +111,7 @@ static int tegra124_dfll_fcpu_probe(struct platform_device *pdev)
>>>  	struct tegra_dfll_soc_data *soc;
>>>  	const struct of_device_id *of_id;
>>>  	const struct dfll_fcpu_data *fcpu_data;
>>> +	struct rail_alignment align;
>>>  
>>>  	of_id = of_match_device(tegra124_dfll_fcpu_of_match, &pdev->dev);
>>>  	fcpu_data = of_id->data;
>>> @@ -135,12 +136,30 @@ static int tegra124_dfll_fcpu_probe(struct platform_device *pdev)
>>>  		return -ENODEV;
>>>  	}
>>>  
>>> +	err = of_property_read_u32(pdev->dev.of_node, "nvidia,align-offset-uv",
>>> +					&align.offset_uv);
>>> +	if (err < 0) {
>>> +		dev_err(&pdev->dev,
>>> +			"offset uv not found, default to table value\n");
>>> +		align.offset_uv = 0;
>>> +	}
>>> +
>>> +	err = of_property_read_u32(pdev->dev.of_node, "nvidia,align-step-uv",
>>> +					&align.step_uv);
>>> +	if (err < 0) {
>>> +		dev_err(&pdev->dev,
>>> +			"step uv not found, default to table value\n");
>>> +		align.step_uv = 0;
>>> +	}
>>> +
>>
>> I am a bit confused by this ...
>>
>> 1. Isn't this going to break Tegra124 DFLL support?
> 
> We fall back to the original behaviour in case the properties are missing, so
> it should work.

Ah yes. However, on Tegra124 now I see all this prints. Can't we only
read these properties if using PWM?

Cheers
Jon

-- 
nvpublic

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

* Re: [PATCH v2 2/6] clk: tegra: DT align parameter for CVB calculation
@ 2018-02-01 10:54             ` Jon Hunter
  0 siblings, 0 replies; 56+ messages in thread
From: Jon Hunter @ 2018-02-01 10:54 UTC (permalink / raw)
  To: Peter De Schrijver; +Cc: linux-tegra, linux-clk, mturquette, sboyd


On 01/02/18 10:30, Peter De Schrijver wrote:
> On Wed, Jan 31, 2018 at 10:43:04AM +0000, Jon Hunter wrote:
>>
>> On 24/01/18 12:45, Peter De Schrijver wrote:
>>> When using a PWM controlled regulator, the voltage step and offset are
>>> determined by the regulator IC in use. This is specified in DT rather
>>> than fixed in the CVB table. Hence pass this information to the CVB table
>>> calculation function. For backwards compatibility the table values are used
>>> if the corresponding parameter is 0.
>>>
>>> Signed-off-by: Peter De Schrijver <pdeschrijver@nvidia.com>
>>> ---
>>>  drivers/clk/tegra/clk-tegra124-dfll-fcpu.c | 23 +++++++++++++++++++++--
>>>  drivers/clk/tegra/cvb.c                    | 18 ++++++++++++++----
>>>  drivers/clk/tegra/cvb.h                    |  5 +++--
>>>  3 files changed, 38 insertions(+), 8 deletions(-)
>>>
>>> diff --git a/drivers/clk/tegra/clk-tegra124-dfll-fcpu.c b/drivers/clk/tegra/clk-tegra124-dfll-fcpu.c
>>> index 440eb8d..6205ce1 100644
>>> --- a/drivers/clk/tegra/clk-tegra124-dfll-fcpu.c
>>> +++ b/drivers/clk/tegra/clk-tegra124-dfll-fcpu.c
>>> @@ -111,6 +111,7 @@ static int tegra124_dfll_fcpu_probe(struct platform_device *pdev)
>>>  	struct tegra_dfll_soc_data *soc;
>>>  	const struct of_device_id *of_id;
>>>  	const struct dfll_fcpu_data *fcpu_data;
>>> +	struct rail_alignment align;
>>>  
>>>  	of_id = of_match_device(tegra124_dfll_fcpu_of_match, &pdev->dev);
>>>  	fcpu_data = of_id->data;
>>> @@ -135,12 +136,30 @@ static int tegra124_dfll_fcpu_probe(struct platform_device *pdev)
>>>  		return -ENODEV;
>>>  	}
>>>  
>>> +	err = of_property_read_u32(pdev->dev.of_node, "nvidia,align-offset-uv",
>>> +					&align.offset_uv);
>>> +	if (err < 0) {
>>> +		dev_err(&pdev->dev,
>>> +			"offset uv not found, default to table value\n");
>>> +		align.offset_uv = 0;
>>> +	}
>>> +
>>> +	err = of_property_read_u32(pdev->dev.of_node, "nvidia,align-step-uv",
>>> +					&align.step_uv);
>>> +	if (err < 0) {
>>> +		dev_err(&pdev->dev,
>>> +			"step uv not found, default to table value\n");
>>> +		align.step_uv = 0;
>>> +	}
>>> +
>>
>> I am a bit confused by this ...
>>
>> 1. Isn't this going to break Tegra124 DFLL support?
> 
> We fall back to the original behaviour in case the properties are missing, so
> it should work.

Ah yes. However, on Tegra124 now I see all this prints. Can't we only
read these properties if using PWM?

Cheers
Jon

-- 
nvpublic

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

* Re: [PATCH v2 4/6] clk: tegra: dfll: support PWM regulator control
  2018-01-31 11:26     ` Jon Hunter
@ 2018-02-01 10:57       ` Peter De Schrijver
  -1 siblings, 0 replies; 56+ messages in thread
From: Peter De Schrijver @ 2018-02-01 10:57 UTC (permalink / raw)
  To: Jon Hunter; +Cc: linux-tegra, linux-clk, mturquette, sboyd

On Wed, Jan 31, 2018 at 11:26:16AM +0000, Jon Hunter wrote:
> 
> On 24/01/18 12:45, Peter De Schrijver wrote:
> > The DFLL can directly generate a PWM signal to control the regulator IC
> > rather then sending i2c messages. This patch adds support for this mode.
> > In this mode the LUT is not used and also there is no regulator object
> > involved because there is no way to control the regulator voltage without
> > also changing the DFLL output frequency. Also the register debugfs file
> > is slightly reworked to only show the i2c registers when i2c mode is in
> > use.
> > 
> > Signed-off-by: Peter De Schrijver <pdeschrijver@nvidia.com>
> > ---
> >  drivers/clk/tegra/clk-dfll.c | 481 ++++++++++++++++++++++++++++++++++++-------
> >  drivers/clk/tegra/clk-dfll.h |   7 +
> >  2 files changed, 410 insertions(+), 78 deletions(-)
> > 
> > diff --git a/drivers/clk/tegra/clk-dfll.c b/drivers/clk/tegra/clk-dfll.c
> > index 0a7deee..204717c 100644
> > --- a/drivers/clk/tegra/clk-dfll.c
> > +++ b/drivers/clk/tegra/clk-dfll.c
> > @@ -243,6 +243,12 @@ enum dfll_tune_range {
> >  	DFLL_TUNE_LOW = 1,
> >  };
> >  
> > +
> > +enum tegra_dfll_pmu_if {
> > +	TEGRA_DFLL_PMU_I2C = 0,
> > +	TEGRA_DFLL_PMU_PWM = 1,
> > +};
> > +
> >  /**
> >   * struct dfll_rate_req - target DFLL rate request data
> >   * @rate: target frequency, after the postscaling
> > @@ -292,18 +298,27 @@ struct tegra_dfll {
> >  	u32				force_mode;
> >  	u32				cf;
> >  	u32				ci;
> > -	u32				cg;
> > +	s32				cg;
> 
> Why has the type been changed?
> 
> >  	bool				cg_scale;
> > +	u32				reg_init_uV;
> >  
> >  	/* I2C interface parameters */
> >  	u32				i2c_fs_rate;
> >  	u32				i2c_reg;
> >  	u32				i2c_slave_addr;
> >  
> > -	/* i2c_lut array entries are regulator framework selectors */
> > -	unsigned			i2c_lut[MAX_DFLL_VOLTAGES];
> > -	int				i2c_lut_size;
> > -	u8				lut_min, lut_max, lut_safe;
> > +	/* lut array entries are regulator framework selectors or PWM values*/
> > +	unsigned int			lut[MAX_DFLL_VOLTAGES];
> > +	unsigned int			lut_uv[MAX_DFLL_VOLTAGES];
> > +	int				lut_size;
> > +	u8				lut_bottom, lut_min, lut_max, lut_safe;
> 
> You say the LUT is not used for PWM, but you are changing the name here
> and it appears to be used to some degree in PWM and so I am a bit
> confused here.
> 

lut itself is not used. lut_uv, mapping voltages to PWM duty cycle, still is.
For i2c the 'PWM duty cycle' then gets mapped to an actual voltage ID sent
over i2c to the regulator by lut. So maybe i2c_lut should keep its name, and
the others should be renamed to uv_map or something?

> > +
> > +	/* PWM interface */
> > +	enum tegra_dfll_pmu_if		pmu_if;
> > +	unsigned long			pwm_rate;
> > +	struct pinctrl			*pwm_pin;
> > +	struct pinctrl_state		*pwm_enable_state;
> > +	struct pinctrl_state		*pwm_disable_state;
> >  };
> >  
> >  #define clk_hw_to_dfll(_hw) container_of(_hw, struct tegra_dfll, dfll_clk_hw)
> > @@ -490,6 +505,36 @@ static void dfll_set_mode(struct tegra_dfll *td,
> >  }
> >  
> >  /*
> > + * DVCO rate control
> > + */
> > +
> > +static unsigned long get_dvco_rate_below(struct tegra_dfll *td, u8 out_min)
> > +{
> > +	struct dev_pm_opp *opp;
> > +	unsigned long rate, prev_rate;
> > +	int uv, min_uv;
> > +
> > +	min_uv = td->lut_uv[out_min];
> > +	for (rate = 0, prev_rate = 0; ; rate++) {
> > +		rcu_read_lock();
> > +		opp = dev_pm_opp_find_freq_ceil(td->soc->dev, &rate);
> > +		if (IS_ERR(opp)) {
> > +			rcu_read_unlock();
> > +			break;
> 
> 'prev_rate' could be uninitialised here and so you may return a bogus value.
> 

prev_rate is set to 0 in the loop initialization?

> > +		}
> > +		uv = dev_pm_opp_get_voltage(opp);
> > +		rcu_read_unlock();
> > +
> > +		if (uv && uv > min_uv)
> > +			return prev_rate;
> > +
> > +		prev_rate = rate;
> > +	}
> > +
> > +	return prev_rate;
> > +}
> > +
> > +/*
> >   * DFLL-to-I2C controller interface
> >   */
> >  
> > @@ -518,6 +563,118 @@ static int dfll_i2c_set_output_enabled(struct tegra_dfll *td, bool enable)
> >  	return 0;
> >  }
> >  
> > +
> > +/*
> > + * DFLL-to-PWM controller interface
> > + */
> > +
> > +/**
> > + * dfll_pwm_set_output_enabled - enable/disable PWM voltage requests
> > + * @td: DFLL instance
> > + * @enable: whether to enable or disable the PWM voltage requests
> > + *
> > + * Set the master enable control for PWM control value updates. If disabled,
> > + * then the PWM signal is not driven. Also configure the PWM output pad
> > + * to the appropriate state.
> > + */
> > +static int dfll_pwm_set_output_enabled(struct tegra_dfll *td, bool enable)
> > +{
> > +	int ret;
> > +	u32 val, div;
> > +
> > +	if (enable) {
> > +		ret = pinctrl_select_state(td->pwm_pin, td->pwm_enable_state);
> > +		if (ret < 0) {
> > +			dev_err(td->dev, "setting enable state failed\n");
> > +			return -EINVAL;
> 
> Why not return ret?
> 

Will do that in the next version.

> > +		}
> > +		val = dfll_readl(td, DFLL_OUTPUT_CFG);
> > +		val &= ~DFLL_OUTPUT_CFG_PWM_DIV_MASK;
> > +		div = DIV_ROUND_UP(td->ref_rate, td->pwm_rate);
> > +		val |= (div << DFLL_OUTPUT_CFG_PWM_DIV_SHIFT)
> > +				& DFLL_OUTPUT_CFG_PWM_DIV_MASK;
> > +		dfll_writel(td, val, DFLL_OUTPUT_CFG);
> > +		dfll_wmb(td);
> > +
> > +		val |= DFLL_OUTPUT_CFG_PWM_ENABLE;
> > +		dfll_writel(td, val, DFLL_OUTPUT_CFG);
> > +		dfll_wmb(td);
> > +	} else {
> > +		ret = pinctrl_select_state(td->pwm_pin, td->pwm_disable_state);
> > +		if (ret < 0)
> > +			dev_warn(td->dev, "setting disable state failed\n");
> > +
> > +		val = dfll_readl(td, DFLL_OUTPUT_CFG);
> > +		val &= ~DFLL_OUTPUT_CFG_PWM_ENABLE;
> > +		dfll_writel(td, val, DFLL_OUTPUT_CFG);
> > +		dfll_wmb(td);
> > +	}
> > +
> > +	return 0;
> > +}
> > +/**
> > + * dfll_set_force_output_value - set fixed value for force output
> > + * @td: DFLL instance
> > + * @out_val: value to force output
> > + *
> > + * Set the fixec value for force output, DFLL will output this value when
> 
> s/fixec/fixed/
> 

Ok.

> > + * force output is enabled.
> > + */
> > +static u32 dfll_set_force_output_value(struct tegra_dfll *td, u8 out_val)
> > +{
> > +	u32 val = dfll_readl(td, DFLL_OUTPUT_FORCE);
> > +
> > +	val = (val & DFLL_OUTPUT_FORCE_ENABLE) | (out_val & OUT_MASK);
> 
> Don't you need to clear the OUT_MASK bits first?
> 

Probably :)

> > +	dfll_writel(td, val, DFLL_OUTPUT_FORCE);
> > +	dfll_wmb(td);
> > +
> > +	return dfll_readl(td, DFLL_OUTPUT_FORCE);
> > +}
> > +
> > +/**
> > + * dfll_set_force_output_enabled - enable/disable force output
> > + * @td: DFLL instance
> > + * @enable: whether to enable or disable the force output
> > + *
> > + * Set the enable control for fouce output with fixed value.
> > + */
> > +static void dfll_set_force_output_enabled(struct tegra_dfll *td, bool enable)
> > +{
> > +	u32 val = dfll_readl(td, DFLL_OUTPUT_FORCE);
> > +
> > +	if (enable)
> > +		val |= DFLL_OUTPUT_FORCE_ENABLE;
> > +	else
> > +		val &= ~DFLL_OUTPUT_FORCE_ENABLE;
> > +
> > +	dfll_writel(td, val, DFLL_OUTPUT_FORCE);
> > +	dfll_wmb(td);
> > +}
> > +
> > +/**
> > + * dfll_i2c_set_output_enabled - enable/disable I2C PMIC voltage requests
> > + * @td: DFLL instance
> > + * @enable: whether to enable or disable the I2C voltage requests
> > + *
> > + * Set the master enable control for I2C control value updates. If disabled,
> > + * then I2C control messages are inhibited, regardless of the DFLL mode.
> > + */
> > +static int dfll_force_output(struct tegra_dfll *td, unsigned int out_sel)
> > +{
> > +	u32 val;
> > +
> > +	if (out_sel > OUT_MASK)
> > +		return -EINVAL;
> > +
> > +	val = dfll_set_force_output_value(td, out_sel);
> > +	if ((td->mode < DFLL_CLOSED_LOOP) &&
> > +	    !(val & DFLL_OUTPUT_FORCE_ENABLE)) {
> > +		dfll_set_force_output_enabled(td, true);
> > +	}
> > +
> > +	return 0;
> > +}
> > +
> >  /**
> >   * dfll_load_lut - load the voltage lookup table
> >   * @td: struct tegra_dfll *
> > @@ -531,15 +688,15 @@ static void dfll_load_i2c_lut(struct tegra_dfll *td)
> >  	u32 val;
> >  
> >  	for (i = 0; i < MAX_DFLL_VOLTAGES; i++) {
> > -		if (i < td->lut_min)
> > -			lut_index = td->lut_min;
> > -		else if (i > td->lut_max)
> > -			lut_index = td->lut_max;
> > +		if (i < td->lut_bottom)
> > +			lut_index = td->lut_bottom;
> > +		else if (i > td->lut_size - 1)
> > +			lut_index = td->lut_size - 1;
> >  		else
> >  			lut_index = i;
> >  
> >  		val = regulator_list_hardware_vsel(td->vdd_reg,
> > -						     td->i2c_lut[lut_index]);
> > +						     td->lut[lut_index]);
> >  		__raw_writel(val, td->lut_base + i * 4);
> >  	}
> >  
> > @@ -595,23 +752,53 @@ static void dfll_init_out_if(struct tegra_dfll *td)
> >  	u32 val;
> >  
> >  	td->lut_min = 0;
> > -	td->lut_max = td->i2c_lut_size - 1;
> > -	td->lut_safe = td->lut_min + 1;
> > -
> > -	dfll_i2c_writel(td, 0, DFLL_OUTPUT_CFG);
> > -	val = (td->lut_safe << DFLL_OUTPUT_CFG_SAFE_SHIFT) |
> > -		(td->lut_max << DFLL_OUTPUT_CFG_MAX_SHIFT) |
> > -		(td->lut_min << DFLL_OUTPUT_CFG_MIN_SHIFT);
> > -	dfll_i2c_writel(td, val, DFLL_OUTPUT_CFG);
> > -	dfll_i2c_wmb(td);
> > -
> > -	dfll_writel(td, 0, DFLL_OUTPUT_FORCE);
> > -	dfll_i2c_writel(td, 0, DFLL_INTR_EN);
> > -	dfll_i2c_writel(td, DFLL_INTR_MAX_MASK | DFLL_INTR_MIN_MASK,
> > -			DFLL_INTR_STS);
> > -
> > -	dfll_load_i2c_lut(td);
> > -	dfll_init_i2c_if(td);
> > +	td->lut_max = td->lut_size - 1;
> > +	td->lut_safe = td->lut_min + (td->lut_min < td->lut_max ? 1 : 0);
> > +
> > +	if (td->pmu_if == TEGRA_DFLL_PMU_PWM) {
> > +		int vinit = td->reg_init_uV;
> > +		int vstep = td->soc->alignment.step_uv;
> > +		int vmin = td->lut_uv[0];
> > +
> > +		/* clear DFLL_OUTPUT_CFG before setting new value */
> > +		dfll_writel(td, 0, DFLL_OUTPUT_CFG);
> > +		dfll_wmb(td);
> > +
> > +		val = dfll_readl(td, DFLL_OUTPUT_CFG);
> > +		val |= (td->lut_safe << DFLL_OUTPUT_CFG_SAFE_SHIFT) |
> > +		       (td->lut_max << DFLL_OUTPUT_CFG_MAX_SHIFT) |
> > +		       (td->lut_min << DFLL_OUTPUT_CFG_MIN_SHIFT);
> > +		dfll_writel(td, val, DFLL_OUTPUT_CFG);
> > +		dfll_wmb(td);
> > +
> > +		dfll_writel(td, 0, DFLL_OUTPUT_FORCE);
> > +		dfll_i2c_writel(td, 0, DFLL_INTR_EN);
> > +		dfll_i2c_writel(td, DFLL_INTR_MAX_MASK | DFLL_INTR_MIN_MASK,
> > +				DFLL_INTR_STS);
> > +
> > +		/* set initial voltage */
> > +		if ((vinit >= vmin) && vstep) {
> > +			unsigned int vsel;
> > +
> > +			vsel = DIV_ROUND_UP((vinit - vmin), vstep);
> > +			dfll_force_output(td, vsel);
> > +		}
> > +	} else {
> > +		dfll_i2c_writel(td, 0, DFLL_OUTPUT_CFG);
> > +		val = (td->lut_safe << DFLL_OUTPUT_CFG_SAFE_SHIFT) |
> > +			(td->lut_max << DFLL_OUTPUT_CFG_MAX_SHIFT) |
> > +			(td->lut_min << DFLL_OUTPUT_CFG_MIN_SHIFT);
> > +		dfll_i2c_writel(td, val, DFLL_OUTPUT_CFG);
> > +		dfll_i2c_wmb(td);
> > +
> > +		dfll_writel(td, 0, DFLL_OUTPUT_FORCE);
> > +		dfll_i2c_writel(td, 0, DFLL_INTR_EN);
> > +		dfll_i2c_writel(td, DFLL_INTR_MAX_MASK | DFLL_INTR_MIN_MASK,
> > +				DFLL_INTR_STS);
> > +
> > +		dfll_load_i2c_lut(td);
> > +		dfll_init_i2c_if(td);
> > +	}
> >  }
> >  
> >  /*
> > @@ -637,11 +824,12 @@ static int find_lut_index_for_rate(struct tegra_dfll *td, unsigned long rate)
> >  	if (IS_ERR(opp))
> >  		return PTR_ERR(opp);
> >  
> > -	uv = dev_pm_opp_get_voltage(opp);
> > +	uv = dev_pm_opp_get_voltage(opp) / td->soc->alignment.step_uv;
> > +
> >  	dev_pm_opp_put(opp);
> >  
> > -	for (i = 0; i < td->i2c_lut_size; i++) {
> > -		if (regulator_list_voltage(td->vdd_reg, td->i2c_lut[i]) == uv)
> > +	for (i = td->lut_bottom; i < td->lut_size; i++) {
> > +		if ((td->lut_uv[i] / td->soc->alignment.step_uv) >= uv)
> >  			return i;
> >  	}
> >  
> > @@ -863,9 +1051,14 @@ static int dfll_lock(struct tegra_dfll *td)
> >  			return -EINVAL;
> >  		}
> >  
> > -		dfll_i2c_set_output_enabled(td, true);
> > +		if (td->pmu_if == TEGRA_DFLL_PMU_PWM)
> > +			dfll_pwm_set_output_enabled(td, true);
> > +		else
> > +			dfll_i2c_set_output_enabled(td, true);
> > +
> >  		dfll_set_mode(td, DFLL_CLOSED_LOOP);
> >  		dfll_set_frequency_request(td, req);
> > +		dfll_set_force_output_enabled(td, false);
> >  		return 0;
> >  
> >  	default:
> > @@ -889,7 +1082,10 @@ static int dfll_unlock(struct tegra_dfll *td)
> >  	case DFLL_CLOSED_LOOP:
> >  		dfll_set_open_loop_config(td);
> >  		dfll_set_mode(td, DFLL_OPEN_LOOP);
> > -		dfll_i2c_set_output_enabled(td, false);
> > +		if (td->pmu_if == TEGRA_DFLL_PMU_PWM)
> > +			dfll_pwm_set_output_enabled(td, false);
> > +		else
> > +			dfll_i2c_set_output_enabled(td, false);
> >  		return 0;
> >  
> >  	case DFLL_OPEN_LOOP:
> > @@ -1171,15 +1367,17 @@ static int attr_registers_show(struct seq_file *s, void *data)
> >  		seq_printf(s, "[0x%02x] = 0x%08x\n", offs,
> >  			   dfll_i2c_readl(td, offs));
> >  
> > -	seq_puts(s, "\nINTEGRATED I2C CONTROLLER REGISTERS:\n");
> > -	offs = DFLL_I2C_CLK_DIVISOR;
> > -	seq_printf(s, "[0x%02x] = 0x%08x\n", offs,
> > -		   __raw_readl(td->i2c_controller_base + offs));
> > -
> > -	seq_puts(s, "\nLUT:\n");
> > -	for (offs = 0; offs <  4 * MAX_DFLL_VOLTAGES; offs += 4)
> > +	if (td->pmu_if == TEGRA_DFLL_PMU_I2C) {
> > +		seq_puts(s, "\nINTEGRATED I2C CONTROLLER REGISTERS:\n");
> > +		offs = DFLL_I2C_CLK_DIVISOR;
> >  		seq_printf(s, "[0x%02x] = 0x%08x\n", offs,
> > -			   __raw_readl(td->lut_base + offs));
> > +			   __raw_readl(td->i2c_controller_base + offs));
> > +
> > +		seq_puts(s, "\nLUT:\n");
> > +		for (offs = 0; offs <  4 * MAX_DFLL_VOLTAGES; offs += 4)
> > +			seq_printf(s, "[0x%02x] = 0x%08x\n", offs,
> > +				   __raw_readl(td->lut_base + offs));
> > +	}
> >  
> >  	return 0;
> >  }
> > @@ -1377,15 +1575,17 @@ static int dfll_init(struct tegra_dfll *td)
> >   */
> >  static int find_vdd_map_entry_exact(struct tegra_dfll *td, int uV)
> >  {
> > -	int i, n_voltages, reg_uV;
> > +	int i, n_voltages, reg_mult, align_mult;
> >  
> > +	align_mult = uV / td->soc->alignment.step_uv;
> >  	n_voltages = regulator_count_voltages(td->vdd_reg);
> >  	for (i = 0; i < n_voltages; i++) {
> > -		reg_uV = regulator_list_voltage(td->vdd_reg, i);
> > -		if (reg_uV < 0)
> > +		reg_mult = regulator_list_voltage(td->vdd_reg, i) /
> > +					td->soc->alignment.step_uv;
> > +		if (reg_mult < 0)
> >  			break;
> >  
> > -		if (uV == reg_uV)
> > +		if (align_mult == reg_mult)
> >  			return i;
> >  	}
> >  
> > @@ -1399,15 +1599,17 @@ static int find_vdd_map_entry_exact(struct tegra_dfll *td, int uV)
> >   * */
> >  static int find_vdd_map_entry_min(struct tegra_dfll *td, int uV)
> >  {
> > -	int i, n_voltages, reg_uV;
> > +	int i, n_voltages, reg_mult, align_mult;
> >  
> > +	align_mult = uV / td->soc->alignment.step_uv;
> >  	n_voltages = regulator_count_voltages(td->vdd_reg);
> >  	for (i = 0; i < n_voltages; i++) {
> > -		reg_uV = regulator_list_voltage(td->vdd_reg, i);
> > -		if (reg_uV < 0)
> > +		reg_mult = regulator_list_voltage(td->vdd_reg, i) /
> > +					td->soc->alignment.step_uv;
> > +		if (reg_mult < 0)
> >  			break;
> >  
> > -		if (uV <= reg_uV)
> > +		if (align_mult <= reg_mult)
> >  			return i;
> >  	}
> >  
> > @@ -1415,6 +1617,53 @@ static int find_vdd_map_entry_min(struct tegra_dfll *td, int uV)
> >  	return -EINVAL;
> >  }
> >  
> > +/*
> > + * Look-up table in h/w is ignored when PWM is used as DFLL interface to PMIC.
> > + * In this case closed loop output is controlling duty cycle directly. The s/w
> > + * look-up that maps PWM duty cycle to voltage is still built by this function.
> > + */
> > +static int dfll_build_lut_pwm(struct tegra_dfll *td, int v_max)
> > +{
> > +	int i, reg_volt;
> > +	unsigned long rate;
> > +	u8 lut_bottom = MAX_DFLL_VOLTAGES;
> > +	int v_min = td->soc->cvb->min_millivolts * 1000;
> > +
> > +	for (i = 0; i < MAX_DFLL_VOLTAGES; i++) {
> > +		reg_volt = td->lut_uv[i];
> > +
> > +		/* since opp voltage is exact mv */
> > +		reg_volt = (reg_volt / 1000) * 1000;
> > +		if (reg_volt > v_max)
> > +			break;
> > +
> > +		td->lut[i] = i;
> > +		if ((lut_bottom == MAX_DFLL_VOLTAGES) && (reg_volt >= v_min))
> > +			lut_bottom = i;
> > +	}
> > +
> > +	/* determine voltage boundaries */
> > +	td->lut_size = i;
> > +	if ((lut_bottom == MAX_DFLL_VOLTAGES) ||
> > +	    (lut_bottom + 1 >= td->lut_size)) {
> > +		dev_err(td->dev, "no voltage above DFLL minimum %d mV\n",
> > +			td->soc->cvb->min_millivolts);
> > +		return -EINVAL;
> > +	}
> > +	td->lut_bottom = lut_bottom;
> > +
> > +	/* determine rate boundaries */
> > +	rate = get_dvco_rate_below(td, td->lut_bottom);
> > +	if (!rate) {
> > +		dev_err(td->dev, "no opp below DFLL minimum voltage %d mV\n",
> > +			td->soc->cvb->min_millivolts);
> > +		return -EINVAL;
> > +	}
> > +	td->dvco_rate_min = rate;
> > +
> > +	return 0;
> > +}
> > +
> >  /**
> >   * dfll_build_i2c_lut - build the I2C voltage register lookup table
> >   * @td: DFLL instance
> > @@ -1427,31 +1676,24 @@ static int find_vdd_map_entry_min(struct tegra_dfll *td, int uV)
> >   *
> >   * On success, fills in td->i2c_lut and returns 0, or -err on failure.
> >   */
> > -static int dfll_build_i2c_lut(struct tegra_dfll *td)
> > +static int dfll_build_i2c_lut(struct tegra_dfll *td, int v_max)
> >  {
> > +	unsigned long rate;
> >  	int ret = -EINVAL;
> > -	int j, v, v_max, v_opp;
> > +	int j, v, v_opp;
> >  	int selector;
> > -	unsigned long rate;
> > -	struct dev_pm_opp *opp;
> >  	int lut;
> >  
> > -	rate = ULONG_MAX;
> > -	opp = dev_pm_opp_find_freq_floor(td->soc->dev, &rate);
> > -	if (IS_ERR(opp)) {
> > -		dev_err(td->dev, "couldn't get vmax opp, empty opp table?\n");
> > -		goto out;
> > -	}
> > -	v_max = dev_pm_opp_get_voltage(opp);
> > -	dev_pm_opp_put(opp);
> > -
> >  	v = td->soc->cvb->min_millivolts * 1000;
> >  	lut = find_vdd_map_entry_exact(td, v);
> >  	if (lut < 0)
> >  		goto out;
> > -	td->i2c_lut[0] = lut;
> > +	td->lut[0] = lut;
> > +	td->lut_bottom = 0;
> >  
> >  	for (j = 1, rate = 0; ; rate++) {
> > +		struct dev_pm_opp *opp;
> > +
> >  		opp = dev_pm_opp_find_freq_ceil(td->soc->dev, &rate);
> >  		if (IS_ERR(opp))
> >  			break;
> > @@ -1470,32 +1712,61 @@ static int dfll_build_i2c_lut(struct tegra_dfll *td)
> >  			selector = find_vdd_map_entry_min(td, v);
> >  			if (selector < 0)
> >  				goto out;
> > -			if (selector != td->i2c_lut[j - 1])
> > -				td->i2c_lut[j++] = selector;
> > +			if (selector != td->lut[j - 1])
> > +				td->lut[j++] = selector;
> >  		}
> >  
> >  		v = (j == MAX_DFLL_VOLTAGES - 1) ? v_max : v_opp;
> >  		selector = find_vdd_map_entry_exact(td, v);
> >  		if (selector < 0)
> >  			goto out;
> > -		if (selector != td->i2c_lut[j - 1])
> > -			td->i2c_lut[j++] = selector;
> > +		if (selector != td->lut[j - 1])
> > +			td->lut[j++] = selector;
> >  
> >  		if (v >= v_max)
> >  			break;
> >  	}
> > -	td->i2c_lut_size = j;
> > +	td->lut_size = j;
> >  
> >  	if (!td->dvco_rate_min)
> >  		dev_err(td->dev, "no opp above DFLL minimum voltage %d mV\n",
> >  			td->soc->cvb->min_millivolts);
> > -	else
> > +	else {
> >  		ret = 0;
> > +		for (j = 0; j < td->lut_size; j++)
> > +			td->lut_uv[j] =
> > +				regulator_list_voltage(td->vdd_reg,
> > +						       td->lut[j]);
> > +	}
> >  
> >  out:
> >  	return ret;
> >  }
> >  
> > +static int dfll_build_lut(struct tegra_dfll *td)
> > +{
> > +	unsigned long rate;
> > +	struct dev_pm_opp *opp;
> > +	int v_max;
> > +
> > +	rcu_read_lock();
> > +
> > +	rate = ULONG_MAX;
> > +	opp = dev_pm_opp_find_freq_floor(td->soc->dev, &rate);
> > +	if (IS_ERR(opp)) {
> > +		dev_err(td->dev, "couldn't get vmax opp, empty opp table?\n");
> > +		return -EINVAL;
> > +	}
> > +	v_max = dev_pm_opp_get_voltage(opp);
> > +
> > +	rcu_read_unlock();
> > +
> > +	if (td->pmu_if == TEGRA_DFLL_PMU_PWM)
> > +		return dfll_build_lut_pwm(td, v_max);
> > +	else
> > +		return dfll_build_i2c_lut(td, v_max);
> > +}
> > +
> >  /**
> >   * read_dt_param - helper function for reading required parameters from the DT
> >   * @td: DFLL instance
> > @@ -1554,12 +1825,55 @@ static int dfll_fetch_i2c_params(struct tegra_dfll *td)
> >  	}
> >  	td->i2c_reg = vsel_reg;
> >  
> > -	ret = dfll_build_i2c_lut(td);
> > -	if (ret) {
> > -		dev_err(td->dev, "couldn't build I2C LUT\n");
> > +	return 0;
> > +}
> > +
> > +static int dfll_fetch_pwm_params(struct tegra_dfll *td)
> > +{
> > +	int ret, i;
> > +	u32 pwm_period;
> > +
> > +	if (!td->soc->alignment.step_uv || !td->soc->alignment.offset_uv) {
> > +		dev_err(td->dev, "Missing step or alignment info for PWM regulator");
> > +		return -EINVAL;
> > +	}
> > +	for (i = 0; i < MAX_DFLL_VOLTAGES; i++)
> > +		td->lut_uv[i] = td->soc->alignment.offset_uv +
> > +				i * td->soc->alignment.step_uv;
> > +
> > +	ret = read_dt_param(td, "nvidia,init-uv", &td->reg_init_uV);
> > +	if (!ret) {
> > +		dev_err(td->dev, "couldn't get initialized voltage\n");
> >  		return ret;
> >  	}
> >  
> > +	ret = read_dt_param(td, "nvidia,pwm-period", &pwm_period);
> > +	if (!ret) {
> > +		dev_err(td->dev, "couldn't get PWM period\n");
> > +		return ret;
> > +	}
> > +	td->pwm_rate = (NSEC_PER_SEC / pwm_period) * (MAX_DFLL_VOLTAGES - 1);
> > +
> > +	td->pwm_pin = devm_pinctrl_get(td->dev);
> > +	if (IS_ERR(td->pwm_pin)) {
> > +		dev_err(td->dev, "DT: missing pinctrl device\n");
> > +		return PTR_ERR(td->pwm_pin);
> > +	}
> > +
> > +	td->pwm_enable_state = pinctrl_lookup_state(td->pwm_pin,
> > +						    "dvfs_pwm_enable");
> > +	if (IS_ERR(td->pwm_enable_state)) {
> > +		dev_err(td->dev, "DT: missing pwm enabled state\n");
> > +		return PTR_ERR(td->pwm_enable_state);
> > +	}
> > +
> > +	td->pwm_disable_state = pinctrl_lookup_state(td->pwm_pin,
> > +						     "dvfs_pwm_disable");
> > +	if (IS_ERR(td->pwm_disable_state)) {
> > +		dev_err(td->dev, "DT: missing pwm disabled state\n");
> > +		return PTR_ERR(td->pwm_disable_state);
> > +	}
> > +
> >  	return 0;
> >  }
> >  
> > @@ -1625,12 +1939,6 @@ int tegra_dfll_register(struct platform_device *pdev,
> >  
> >  	td->soc = soc;
> >  
> > -	td->vdd_reg = devm_regulator_get(td->dev, "vdd-cpu");
> > -	if (IS_ERR(td->vdd_reg)) {
> > -		dev_err(td->dev, "couldn't get vdd_cpu regulator\n");
> > -		return PTR_ERR(td->vdd_reg);
> > -	}
> > -
> >  	td->dvco_rst = devm_reset_control_get(td->dev, "dvco");
> >  	if (IS_ERR(td->dvco_rst)) {
> >  		dev_err(td->dev, "couldn't get dvco reset\n");
> > @@ -1643,10 +1951,27 @@ int tegra_dfll_register(struct platform_device *pdev,
> >  		return ret;
> >  	}
> >  
> > -	ret = dfll_fetch_i2c_params(td);
> > +	if (of_property_read_bool(td->dev->of_node, "nvidia,pwm-to-pmic")) {
> > +		td->pmu_if = TEGRA_DFLL_PMU_PWM;
> > +		ret = dfll_fetch_pwm_params(td);
> > +	} else  {
> > +		td->vdd_reg = devm_regulator_get(td->dev, "vdd-cpu");
> > +		if (IS_ERR(td->vdd_reg)) {
> > +			dev_err(td->dev, "couldn't get vdd_cpu regulator\n");
> > +			return PTR_ERR(td->vdd_reg);
> > +		}
> > +		td->pmu_if = TEGRA_DFLL_PMU_I2C;
> > +		ret = dfll_fetch_i2c_params(td);
> > +	}
> >  	if (ret)
> >  		return ret;
> >  
> > +	ret = dfll_build_lut(td);
> > +	if (ret) {
> > +		dev_err(td->dev, "couldn't build LUT\n");
> > +		return ret;
> > +	}
> > +
> >  	mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> >  	if (!mem) {
> >  		dev_err(td->dev, "no control register resource\n");
> > diff --git a/drivers/clk/tegra/clk-dfll.h b/drivers/clk/tegra/clk-dfll.h
> > index 83352c8..228f54a 100644
> > --- a/drivers/clk/tegra/clk-dfll.h
> > +++ b/drivers/clk/tegra/clk-dfll.h
> > @@ -21,6 +21,7 @@
> >  #include <linux/platform_device.h>
> >  #include <linux/reset.h>
> >  #include <linux/types.h>
> > +#include "cvb.h"
> >  
> >  /**
> >   * struct tegra_dfll_soc_data - SoC-specific hooks/integration for the DFLL driver
> > @@ -35,6 +36,12 @@ struct tegra_dfll_soc_data {
> >  	struct device *dev;
> >  	unsigned long max_freq;
> >  	const struct cvb_table *cvb;
> > +	struct rail_alignment alignment;
> > +	unsigned int min_millivolts;
> > +	unsigned int tune_high_min_millivolts;
> > +	u32 tune0_low;
> > +	u32 tune0_high;
> > +	u32 tune1;
> >  
> >  	void (*init_clock_trimmers)(void);
> >  	void (*set_clock_trimmers_high)(void);
> > 
> 
> Overall this is quite a big change. I wonder if it is worth splitting
> into a preparatory patch (with all the renaming, etc) for the existing
> i2c mode and then add the pwm support. It is hard to see how the
> existing i2c code has been changed.
> 

Yes, this is largely because the original code didn't make a good split between
the 'PWM duty cycle' and the i2c regulator VID mapping. If you look at the hw,
it's obviously designed to use PWM regulators. Then i2c regulator support was
added by adding the LUT which maps a 'PWM duty cycle' to a regulator VID.

Peter.

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

* Re: [PATCH v2 4/6] clk: tegra: dfll: support PWM regulator control
@ 2018-02-01 10:57       ` Peter De Schrijver
  0 siblings, 0 replies; 56+ messages in thread
From: Peter De Schrijver @ 2018-02-01 10:57 UTC (permalink / raw)
  To: Jon Hunter; +Cc: linux-tegra, linux-clk, mturquette, sboyd

On Wed, Jan 31, 2018 at 11:26:16AM +0000, Jon Hunter wrote:
> 
> On 24/01/18 12:45, Peter De Schrijver wrote:
> > The DFLL can directly generate a PWM signal to control the regulator IC
> > rather then sending i2c messages. This patch adds support for this mode.
> > In this mode the LUT is not used and also there is no regulator object
> > involved because there is no way to control the regulator voltage without
> > also changing the DFLL output frequency. Also the register debugfs file
> > is slightly reworked to only show the i2c registers when i2c mode is in
> > use.
> > 
> > Signed-off-by: Peter De Schrijver <pdeschrijver@nvidia.com>
> > ---
> >  drivers/clk/tegra/clk-dfll.c | 481 ++++++++++++++++++++++++++++++++++++-------
> >  drivers/clk/tegra/clk-dfll.h |   7 +
> >  2 files changed, 410 insertions(+), 78 deletions(-)
> > 
> > diff --git a/drivers/clk/tegra/clk-dfll.c b/drivers/clk/tegra/clk-dfll.c
> > index 0a7deee..204717c 100644
> > --- a/drivers/clk/tegra/clk-dfll.c
> > +++ b/drivers/clk/tegra/clk-dfll.c
> > @@ -243,6 +243,12 @@ enum dfll_tune_range {
> >  	DFLL_TUNE_LOW = 1,
> >  };
> >  
> > +
> > +enum tegra_dfll_pmu_if {
> > +	TEGRA_DFLL_PMU_I2C = 0,
> > +	TEGRA_DFLL_PMU_PWM = 1,
> > +};
> > +
> >  /**
> >   * struct dfll_rate_req - target DFLL rate request data
> >   * @rate: target frequency, after the postscaling
> > @@ -292,18 +298,27 @@ struct tegra_dfll {
> >  	u32				force_mode;
> >  	u32				cf;
> >  	u32				ci;
> > -	u32				cg;
> > +	s32				cg;
> 
> Why has the type been changed?
> 
> >  	bool				cg_scale;
> > +	u32				reg_init_uV;
> >  
> >  	/* I2C interface parameters */
> >  	u32				i2c_fs_rate;
> >  	u32				i2c_reg;
> >  	u32				i2c_slave_addr;
> >  
> > -	/* i2c_lut array entries are regulator framework selectors */
> > -	unsigned			i2c_lut[MAX_DFLL_VOLTAGES];
> > -	int				i2c_lut_size;
> > -	u8				lut_min, lut_max, lut_safe;
> > +	/* lut array entries are regulator framework selectors or PWM values*/
> > +	unsigned int			lut[MAX_DFLL_VOLTAGES];
> > +	unsigned int			lut_uv[MAX_DFLL_VOLTAGES];
> > +	int				lut_size;
> > +	u8				lut_bottom, lut_min, lut_max, lut_safe;
> 
> You say the LUT is not used for PWM, but you are changing the name here
> and it appears to be used to some degree in PWM and so I am a bit
> confused here.
> 

lut itself is not used. lut_uv, mapping voltages to PWM duty cycle, still is.
For i2c the 'PWM duty cycle' then gets mapped to an actual voltage ID sent
over i2c to the regulator by lut. So maybe i2c_lut should keep its name, and
the others should be renamed to uv_map or something?

> > +
> > +	/* PWM interface */
> > +	enum tegra_dfll_pmu_if		pmu_if;
> > +	unsigned long			pwm_rate;
> > +	struct pinctrl			*pwm_pin;
> > +	struct pinctrl_state		*pwm_enable_state;
> > +	struct pinctrl_state		*pwm_disable_state;
> >  };
> >  
> >  #define clk_hw_to_dfll(_hw) container_of(_hw, struct tegra_dfll, dfll_clk_hw)
> > @@ -490,6 +505,36 @@ static void dfll_set_mode(struct tegra_dfll *td,
> >  }
> >  
> >  /*
> > + * DVCO rate control
> > + */
> > +
> > +static unsigned long get_dvco_rate_below(struct tegra_dfll *td, u8 out_min)
> > +{
> > +	struct dev_pm_opp *opp;
> > +	unsigned long rate, prev_rate;
> > +	int uv, min_uv;
> > +
> > +	min_uv = td->lut_uv[out_min];
> > +	for (rate = 0, prev_rate = 0; ; rate++) {
> > +		rcu_read_lock();
> > +		opp = dev_pm_opp_find_freq_ceil(td->soc->dev, &rate);
> > +		if (IS_ERR(opp)) {
> > +			rcu_read_unlock();
> > +			break;
> 
> 'prev_rate' could be uninitialised here and so you may return a bogus value.
> 

prev_rate is set to 0 in the loop initialization?

> > +		}
> > +		uv = dev_pm_opp_get_voltage(opp);
> > +		rcu_read_unlock();
> > +
> > +		if (uv && uv > min_uv)
> > +			return prev_rate;
> > +
> > +		prev_rate = rate;
> > +	}
> > +
> > +	return prev_rate;
> > +}
> > +
> > +/*
> >   * DFLL-to-I2C controller interface
> >   */
> >  
> > @@ -518,6 +563,118 @@ static int dfll_i2c_set_output_enabled(struct tegra_dfll *td, bool enable)
> >  	return 0;
> >  }
> >  
> > +
> > +/*
> > + * DFLL-to-PWM controller interface
> > + */
> > +
> > +/**
> > + * dfll_pwm_set_output_enabled - enable/disable PWM voltage requests
> > + * @td: DFLL instance
> > + * @enable: whether to enable or disable the PWM voltage requests
> > + *
> > + * Set the master enable control for PWM control value updates. If disabled,
> > + * then the PWM signal is not driven. Also configure the PWM output pad
> > + * to the appropriate state.
> > + */
> > +static int dfll_pwm_set_output_enabled(struct tegra_dfll *td, bool enable)
> > +{
> > +	int ret;
> > +	u32 val, div;
> > +
> > +	if (enable) {
> > +		ret = pinctrl_select_state(td->pwm_pin, td->pwm_enable_state);
> > +		if (ret < 0) {
> > +			dev_err(td->dev, "setting enable state failed\n");
> > +			return -EINVAL;
> 
> Why not return ret?
> 

Will do that in the next version.

> > +		}
> > +		val = dfll_readl(td, DFLL_OUTPUT_CFG);
> > +		val &= ~DFLL_OUTPUT_CFG_PWM_DIV_MASK;
> > +		div = DIV_ROUND_UP(td->ref_rate, td->pwm_rate);
> > +		val |= (div << DFLL_OUTPUT_CFG_PWM_DIV_SHIFT)
> > +				& DFLL_OUTPUT_CFG_PWM_DIV_MASK;
> > +		dfll_writel(td, val, DFLL_OUTPUT_CFG);
> > +		dfll_wmb(td);
> > +
> > +		val |= DFLL_OUTPUT_CFG_PWM_ENABLE;
> > +		dfll_writel(td, val, DFLL_OUTPUT_CFG);
> > +		dfll_wmb(td);
> > +	} else {
> > +		ret = pinctrl_select_state(td->pwm_pin, td->pwm_disable_state);
> > +		if (ret < 0)
> > +			dev_warn(td->dev, "setting disable state failed\n");
> > +
> > +		val = dfll_readl(td, DFLL_OUTPUT_CFG);
> > +		val &= ~DFLL_OUTPUT_CFG_PWM_ENABLE;
> > +		dfll_writel(td, val, DFLL_OUTPUT_CFG);
> > +		dfll_wmb(td);
> > +	}
> > +
> > +	return 0;
> > +}
> > +/**
> > + * dfll_set_force_output_value - set fixed value for force output
> > + * @td: DFLL instance
> > + * @out_val: value to force output
> > + *
> > + * Set the fixec value for force output, DFLL will output this value when
> 
> s/fixec/fixed/
> 

Ok.

> > + * force output is enabled.
> > + */
> > +static u32 dfll_set_force_output_value(struct tegra_dfll *td, u8 out_val)
> > +{
> > +	u32 val = dfll_readl(td, DFLL_OUTPUT_FORCE);
> > +
> > +	val = (val & DFLL_OUTPUT_FORCE_ENABLE) | (out_val & OUT_MASK);
> 
> Don't you need to clear the OUT_MASK bits first?
> 

Probably :)

> > +	dfll_writel(td, val, DFLL_OUTPUT_FORCE);
> > +	dfll_wmb(td);
> > +
> > +	return dfll_readl(td, DFLL_OUTPUT_FORCE);
> > +}
> > +
> > +/**
> > + * dfll_set_force_output_enabled - enable/disable force output
> > + * @td: DFLL instance
> > + * @enable: whether to enable or disable the force output
> > + *
> > + * Set the enable control for fouce output with fixed value.
> > + */
> > +static void dfll_set_force_output_enabled(struct tegra_dfll *td, bool enable)
> > +{
> > +	u32 val = dfll_readl(td, DFLL_OUTPUT_FORCE);
> > +
> > +	if (enable)
> > +		val |= DFLL_OUTPUT_FORCE_ENABLE;
> > +	else
> > +		val &= ~DFLL_OUTPUT_FORCE_ENABLE;
> > +
> > +	dfll_writel(td, val, DFLL_OUTPUT_FORCE);
> > +	dfll_wmb(td);
> > +}
> > +
> > +/**
> > + * dfll_i2c_set_output_enabled - enable/disable I2C PMIC voltage requests
> > + * @td: DFLL instance
> > + * @enable: whether to enable or disable the I2C voltage requests
> > + *
> > + * Set the master enable control for I2C control value updates. If disabled,
> > + * then I2C control messages are inhibited, regardless of the DFLL mode.
> > + */
> > +static int dfll_force_output(struct tegra_dfll *td, unsigned int out_sel)
> > +{
> > +	u32 val;
> > +
> > +	if (out_sel > OUT_MASK)
> > +		return -EINVAL;
> > +
> > +	val = dfll_set_force_output_value(td, out_sel);
> > +	if ((td->mode < DFLL_CLOSED_LOOP) &&
> > +	    !(val & DFLL_OUTPUT_FORCE_ENABLE)) {
> > +		dfll_set_force_output_enabled(td, true);
> > +	}
> > +
> > +	return 0;
> > +}
> > +
> >  /**
> >   * dfll_load_lut - load the voltage lookup table
> >   * @td: struct tegra_dfll *
> > @@ -531,15 +688,15 @@ static void dfll_load_i2c_lut(struct tegra_dfll *td)
> >  	u32 val;
> >  
> >  	for (i = 0; i < MAX_DFLL_VOLTAGES; i++) {
> > -		if (i < td->lut_min)
> > -			lut_index = td->lut_min;
> > -		else if (i > td->lut_max)
> > -			lut_index = td->lut_max;
> > +		if (i < td->lut_bottom)
> > +			lut_index = td->lut_bottom;
> > +		else if (i > td->lut_size - 1)
> > +			lut_index = td->lut_size - 1;
> >  		else
> >  			lut_index = i;
> >  
> >  		val = regulator_list_hardware_vsel(td->vdd_reg,
> > -						     td->i2c_lut[lut_index]);
> > +						     td->lut[lut_index]);
> >  		__raw_writel(val, td->lut_base + i * 4);
> >  	}
> >  
> > @@ -595,23 +752,53 @@ static void dfll_init_out_if(struct tegra_dfll *td)
> >  	u32 val;
> >  
> >  	td->lut_min = 0;
> > -	td->lut_max = td->i2c_lut_size - 1;
> > -	td->lut_safe = td->lut_min + 1;
> > -
> > -	dfll_i2c_writel(td, 0, DFLL_OUTPUT_CFG);
> > -	val = (td->lut_safe << DFLL_OUTPUT_CFG_SAFE_SHIFT) |
> > -		(td->lut_max << DFLL_OUTPUT_CFG_MAX_SHIFT) |
> > -		(td->lut_min << DFLL_OUTPUT_CFG_MIN_SHIFT);
> > -	dfll_i2c_writel(td, val, DFLL_OUTPUT_CFG);
> > -	dfll_i2c_wmb(td);
> > -
> > -	dfll_writel(td, 0, DFLL_OUTPUT_FORCE);
> > -	dfll_i2c_writel(td, 0, DFLL_INTR_EN);
> > -	dfll_i2c_writel(td, DFLL_INTR_MAX_MASK | DFLL_INTR_MIN_MASK,
> > -			DFLL_INTR_STS);
> > -
> > -	dfll_load_i2c_lut(td);
> > -	dfll_init_i2c_if(td);
> > +	td->lut_max = td->lut_size - 1;
> > +	td->lut_safe = td->lut_min + (td->lut_min < td->lut_max ? 1 : 0);
> > +
> > +	if (td->pmu_if == TEGRA_DFLL_PMU_PWM) {
> > +		int vinit = td->reg_init_uV;
> > +		int vstep = td->soc->alignment.step_uv;
> > +		int vmin = td->lut_uv[0];
> > +
> > +		/* clear DFLL_OUTPUT_CFG before setting new value */
> > +		dfll_writel(td, 0, DFLL_OUTPUT_CFG);
> > +		dfll_wmb(td);
> > +
> > +		val = dfll_readl(td, DFLL_OUTPUT_CFG);
> > +		val |= (td->lut_safe << DFLL_OUTPUT_CFG_SAFE_SHIFT) |
> > +		       (td->lut_max << DFLL_OUTPUT_CFG_MAX_SHIFT) |
> > +		       (td->lut_min << DFLL_OUTPUT_CFG_MIN_SHIFT);
> > +		dfll_writel(td, val, DFLL_OUTPUT_CFG);
> > +		dfll_wmb(td);
> > +
> > +		dfll_writel(td, 0, DFLL_OUTPUT_FORCE);
> > +		dfll_i2c_writel(td, 0, DFLL_INTR_EN);
> > +		dfll_i2c_writel(td, DFLL_INTR_MAX_MASK | DFLL_INTR_MIN_MASK,
> > +				DFLL_INTR_STS);
> > +
> > +		/* set initial voltage */
> > +		if ((vinit >= vmin) && vstep) {
> > +			unsigned int vsel;
> > +
> > +			vsel = DIV_ROUND_UP((vinit - vmin), vstep);
> > +			dfll_force_output(td, vsel);
> > +		}
> > +	} else {
> > +		dfll_i2c_writel(td, 0, DFLL_OUTPUT_CFG);
> > +		val = (td->lut_safe << DFLL_OUTPUT_CFG_SAFE_SHIFT) |
> > +			(td->lut_max << DFLL_OUTPUT_CFG_MAX_SHIFT) |
> > +			(td->lut_min << DFLL_OUTPUT_CFG_MIN_SHIFT);
> > +		dfll_i2c_writel(td, val, DFLL_OUTPUT_CFG);
> > +		dfll_i2c_wmb(td);
> > +
> > +		dfll_writel(td, 0, DFLL_OUTPUT_FORCE);
> > +		dfll_i2c_writel(td, 0, DFLL_INTR_EN);
> > +		dfll_i2c_writel(td, DFLL_INTR_MAX_MASK | DFLL_INTR_MIN_MASK,
> > +				DFLL_INTR_STS);
> > +
> > +		dfll_load_i2c_lut(td);
> > +		dfll_init_i2c_if(td);
> > +	}
> >  }
> >  
> >  /*
> > @@ -637,11 +824,12 @@ static int find_lut_index_for_rate(struct tegra_dfll *td, unsigned long rate)
> >  	if (IS_ERR(opp))
> >  		return PTR_ERR(opp);
> >  
> > -	uv = dev_pm_opp_get_voltage(opp);
> > +	uv = dev_pm_opp_get_voltage(opp) / td->soc->alignment.step_uv;
> > +
> >  	dev_pm_opp_put(opp);
> >  
> > -	for (i = 0; i < td->i2c_lut_size; i++) {
> > -		if (regulator_list_voltage(td->vdd_reg, td->i2c_lut[i]) == uv)
> > +	for (i = td->lut_bottom; i < td->lut_size; i++) {
> > +		if ((td->lut_uv[i] / td->soc->alignment.step_uv) >= uv)
> >  			return i;
> >  	}
> >  
> > @@ -863,9 +1051,14 @@ static int dfll_lock(struct tegra_dfll *td)
> >  			return -EINVAL;
> >  		}
> >  
> > -		dfll_i2c_set_output_enabled(td, true);
> > +		if (td->pmu_if == TEGRA_DFLL_PMU_PWM)
> > +			dfll_pwm_set_output_enabled(td, true);
> > +		else
> > +			dfll_i2c_set_output_enabled(td, true);
> > +
> >  		dfll_set_mode(td, DFLL_CLOSED_LOOP);
> >  		dfll_set_frequency_request(td, req);
> > +		dfll_set_force_output_enabled(td, false);
> >  		return 0;
> >  
> >  	default:
> > @@ -889,7 +1082,10 @@ static int dfll_unlock(struct tegra_dfll *td)
> >  	case DFLL_CLOSED_LOOP:
> >  		dfll_set_open_loop_config(td);
> >  		dfll_set_mode(td, DFLL_OPEN_LOOP);
> > -		dfll_i2c_set_output_enabled(td, false);
> > +		if (td->pmu_if == TEGRA_DFLL_PMU_PWM)
> > +			dfll_pwm_set_output_enabled(td, false);
> > +		else
> > +			dfll_i2c_set_output_enabled(td, false);
> >  		return 0;
> >  
> >  	case DFLL_OPEN_LOOP:
> > @@ -1171,15 +1367,17 @@ static int attr_registers_show(struct seq_file *s, void *data)
> >  		seq_printf(s, "[0x%02x] = 0x%08x\n", offs,
> >  			   dfll_i2c_readl(td, offs));
> >  
> > -	seq_puts(s, "\nINTEGRATED I2C CONTROLLER REGISTERS:\n");
> > -	offs = DFLL_I2C_CLK_DIVISOR;
> > -	seq_printf(s, "[0x%02x] = 0x%08x\n", offs,
> > -		   __raw_readl(td->i2c_controller_base + offs));
> > -
> > -	seq_puts(s, "\nLUT:\n");
> > -	for (offs = 0; offs <  4 * MAX_DFLL_VOLTAGES; offs += 4)
> > +	if (td->pmu_if == TEGRA_DFLL_PMU_I2C) {
> > +		seq_puts(s, "\nINTEGRATED I2C CONTROLLER REGISTERS:\n");
> > +		offs = DFLL_I2C_CLK_DIVISOR;
> >  		seq_printf(s, "[0x%02x] = 0x%08x\n", offs,
> > -			   __raw_readl(td->lut_base + offs));
> > +			   __raw_readl(td->i2c_controller_base + offs));
> > +
> > +		seq_puts(s, "\nLUT:\n");
> > +		for (offs = 0; offs <  4 * MAX_DFLL_VOLTAGES; offs += 4)
> > +			seq_printf(s, "[0x%02x] = 0x%08x\n", offs,
> > +				   __raw_readl(td->lut_base + offs));
> > +	}
> >  
> >  	return 0;
> >  }
> > @@ -1377,15 +1575,17 @@ static int dfll_init(struct tegra_dfll *td)
> >   */
> >  static int find_vdd_map_entry_exact(struct tegra_dfll *td, int uV)
> >  {
> > -	int i, n_voltages, reg_uV;
> > +	int i, n_voltages, reg_mult, align_mult;
> >  
> > +	align_mult = uV / td->soc->alignment.step_uv;
> >  	n_voltages = regulator_count_voltages(td->vdd_reg);
> >  	for (i = 0; i < n_voltages; i++) {
> > -		reg_uV = regulator_list_voltage(td->vdd_reg, i);
> > -		if (reg_uV < 0)
> > +		reg_mult = regulator_list_voltage(td->vdd_reg, i) /
> > +					td->soc->alignment.step_uv;
> > +		if (reg_mult < 0)
> >  			break;
> >  
> > -		if (uV == reg_uV)
> > +		if (align_mult == reg_mult)
> >  			return i;
> >  	}
> >  
> > @@ -1399,15 +1599,17 @@ static int find_vdd_map_entry_exact(struct tegra_dfll *td, int uV)
> >   * */
> >  static int find_vdd_map_entry_min(struct tegra_dfll *td, int uV)
> >  {
> > -	int i, n_voltages, reg_uV;
> > +	int i, n_voltages, reg_mult, align_mult;
> >  
> > +	align_mult = uV / td->soc->alignment.step_uv;
> >  	n_voltages = regulator_count_voltages(td->vdd_reg);
> >  	for (i = 0; i < n_voltages; i++) {
> > -		reg_uV = regulator_list_voltage(td->vdd_reg, i);
> > -		if (reg_uV < 0)
> > +		reg_mult = regulator_list_voltage(td->vdd_reg, i) /
> > +					td->soc->alignment.step_uv;
> > +		if (reg_mult < 0)
> >  			break;
> >  
> > -		if (uV <= reg_uV)
> > +		if (align_mult <= reg_mult)
> >  			return i;
> >  	}
> >  
> > @@ -1415,6 +1617,53 @@ static int find_vdd_map_entry_min(struct tegra_dfll *td, int uV)
> >  	return -EINVAL;
> >  }
> >  
> > +/*
> > + * Look-up table in h/w is ignored when PWM is used as DFLL interface to PMIC.
> > + * In this case closed loop output is controlling duty cycle directly. The s/w
> > + * look-up that maps PWM duty cycle to voltage is still built by this function.
> > + */
> > +static int dfll_build_lut_pwm(struct tegra_dfll *td, int v_max)
> > +{
> > +	int i, reg_volt;
> > +	unsigned long rate;
> > +	u8 lut_bottom = MAX_DFLL_VOLTAGES;
> > +	int v_min = td->soc->cvb->min_millivolts * 1000;
> > +
> > +	for (i = 0; i < MAX_DFLL_VOLTAGES; i++) {
> > +		reg_volt = td->lut_uv[i];
> > +
> > +		/* since opp voltage is exact mv */
> > +		reg_volt = (reg_volt / 1000) * 1000;
> > +		if (reg_volt > v_max)
> > +			break;
> > +
> > +		td->lut[i] = i;
> > +		if ((lut_bottom == MAX_DFLL_VOLTAGES) && (reg_volt >= v_min))
> > +			lut_bottom = i;
> > +	}
> > +
> > +	/* determine voltage boundaries */
> > +	td->lut_size = i;
> > +	if ((lut_bottom == MAX_DFLL_VOLTAGES) ||
> > +	    (lut_bottom + 1 >= td->lut_size)) {
> > +		dev_err(td->dev, "no voltage above DFLL minimum %d mV\n",
> > +			td->soc->cvb->min_millivolts);
> > +		return -EINVAL;
> > +	}
> > +	td->lut_bottom = lut_bottom;
> > +
> > +	/* determine rate boundaries */
> > +	rate = get_dvco_rate_below(td, td->lut_bottom);
> > +	if (!rate) {
> > +		dev_err(td->dev, "no opp below DFLL minimum voltage %d mV\n",
> > +			td->soc->cvb->min_millivolts);
> > +		return -EINVAL;
> > +	}
> > +	td->dvco_rate_min = rate;
> > +
> > +	return 0;
> > +}
> > +
> >  /**
> >   * dfll_build_i2c_lut - build the I2C voltage register lookup table
> >   * @td: DFLL instance
> > @@ -1427,31 +1676,24 @@ static int find_vdd_map_entry_min(struct tegra_dfll *td, int uV)
> >   *
> >   * On success, fills in td->i2c_lut and returns 0, or -err on failure.
> >   */
> > -static int dfll_build_i2c_lut(struct tegra_dfll *td)
> > +static int dfll_build_i2c_lut(struct tegra_dfll *td, int v_max)
> >  {
> > +	unsigned long rate;
> >  	int ret = -EINVAL;
> > -	int j, v, v_max, v_opp;
> > +	int j, v, v_opp;
> >  	int selector;
> > -	unsigned long rate;
> > -	struct dev_pm_opp *opp;
> >  	int lut;
> >  
> > -	rate = ULONG_MAX;
> > -	opp = dev_pm_opp_find_freq_floor(td->soc->dev, &rate);
> > -	if (IS_ERR(opp)) {
> > -		dev_err(td->dev, "couldn't get vmax opp, empty opp table?\n");
> > -		goto out;
> > -	}
> > -	v_max = dev_pm_opp_get_voltage(opp);
> > -	dev_pm_opp_put(opp);
> > -
> >  	v = td->soc->cvb->min_millivolts * 1000;
> >  	lut = find_vdd_map_entry_exact(td, v);
> >  	if (lut < 0)
> >  		goto out;
> > -	td->i2c_lut[0] = lut;
> > +	td->lut[0] = lut;
> > +	td->lut_bottom = 0;
> >  
> >  	for (j = 1, rate = 0; ; rate++) {
> > +		struct dev_pm_opp *opp;
> > +
> >  		opp = dev_pm_opp_find_freq_ceil(td->soc->dev, &rate);
> >  		if (IS_ERR(opp))
> >  			break;
> > @@ -1470,32 +1712,61 @@ static int dfll_build_i2c_lut(struct tegra_dfll *td)
> >  			selector = find_vdd_map_entry_min(td, v);
> >  			if (selector < 0)
> >  				goto out;
> > -			if (selector != td->i2c_lut[j - 1])
> > -				td->i2c_lut[j++] = selector;
> > +			if (selector != td->lut[j - 1])
> > +				td->lut[j++] = selector;
> >  		}
> >  
> >  		v = (j == MAX_DFLL_VOLTAGES - 1) ? v_max : v_opp;
> >  		selector = find_vdd_map_entry_exact(td, v);
> >  		if (selector < 0)
> >  			goto out;
> > -		if (selector != td->i2c_lut[j - 1])
> > -			td->i2c_lut[j++] = selector;
> > +		if (selector != td->lut[j - 1])
> > +			td->lut[j++] = selector;
> >  
> >  		if (v >= v_max)
> >  			break;
> >  	}
> > -	td->i2c_lut_size = j;
> > +	td->lut_size = j;
> >  
> >  	if (!td->dvco_rate_min)
> >  		dev_err(td->dev, "no opp above DFLL minimum voltage %d mV\n",
> >  			td->soc->cvb->min_millivolts);
> > -	else
> > +	else {
> >  		ret = 0;
> > +		for (j = 0; j < td->lut_size; j++)
> > +			td->lut_uv[j] =
> > +				regulator_list_voltage(td->vdd_reg,
> > +						       td->lut[j]);
> > +	}
> >  
> >  out:
> >  	return ret;
> >  }
> >  
> > +static int dfll_build_lut(struct tegra_dfll *td)
> > +{
> > +	unsigned long rate;
> > +	struct dev_pm_opp *opp;
> > +	int v_max;
> > +
> > +	rcu_read_lock();
> > +
> > +	rate = ULONG_MAX;
> > +	opp = dev_pm_opp_find_freq_floor(td->soc->dev, &rate);
> > +	if (IS_ERR(opp)) {
> > +		dev_err(td->dev, "couldn't get vmax opp, empty opp table?\n");
> > +		return -EINVAL;
> > +	}
> > +	v_max = dev_pm_opp_get_voltage(opp);
> > +
> > +	rcu_read_unlock();
> > +
> > +	if (td->pmu_if == TEGRA_DFLL_PMU_PWM)
> > +		return dfll_build_lut_pwm(td, v_max);
> > +	else
> > +		return dfll_build_i2c_lut(td, v_max);
> > +}
> > +
> >  /**
> >   * read_dt_param - helper function for reading required parameters from the DT
> >   * @td: DFLL instance
> > @@ -1554,12 +1825,55 @@ static int dfll_fetch_i2c_params(struct tegra_dfll *td)
> >  	}
> >  	td->i2c_reg = vsel_reg;
> >  
> > -	ret = dfll_build_i2c_lut(td);
> > -	if (ret) {
> > -		dev_err(td->dev, "couldn't build I2C LUT\n");
> > +	return 0;
> > +}
> > +
> > +static int dfll_fetch_pwm_params(struct tegra_dfll *td)
> > +{
> > +	int ret, i;
> > +	u32 pwm_period;
> > +
> > +	if (!td->soc->alignment.step_uv || !td->soc->alignment.offset_uv) {
> > +		dev_err(td->dev, "Missing step or alignment info for PWM regulator");
> > +		return -EINVAL;
> > +	}
> > +	for (i = 0; i < MAX_DFLL_VOLTAGES; i++)
> > +		td->lut_uv[i] = td->soc->alignment.offset_uv +
> > +				i * td->soc->alignment.step_uv;
> > +
> > +	ret = read_dt_param(td, "nvidia,init-uv", &td->reg_init_uV);
> > +	if (!ret) {
> > +		dev_err(td->dev, "couldn't get initialized voltage\n");
> >  		return ret;
> >  	}
> >  
> > +	ret = read_dt_param(td, "nvidia,pwm-period", &pwm_period);
> > +	if (!ret) {
> > +		dev_err(td->dev, "couldn't get PWM period\n");
> > +		return ret;
> > +	}
> > +	td->pwm_rate = (NSEC_PER_SEC / pwm_period) * (MAX_DFLL_VOLTAGES - 1);
> > +
> > +	td->pwm_pin = devm_pinctrl_get(td->dev);
> > +	if (IS_ERR(td->pwm_pin)) {
> > +		dev_err(td->dev, "DT: missing pinctrl device\n");
> > +		return PTR_ERR(td->pwm_pin);
> > +	}
> > +
> > +	td->pwm_enable_state = pinctrl_lookup_state(td->pwm_pin,
> > +						    "dvfs_pwm_enable");
> > +	if (IS_ERR(td->pwm_enable_state)) {
> > +		dev_err(td->dev, "DT: missing pwm enabled state\n");
> > +		return PTR_ERR(td->pwm_enable_state);
> > +	}
> > +
> > +	td->pwm_disable_state = pinctrl_lookup_state(td->pwm_pin,
> > +						     "dvfs_pwm_disable");
> > +	if (IS_ERR(td->pwm_disable_state)) {
> > +		dev_err(td->dev, "DT: missing pwm disabled state\n");
> > +		return PTR_ERR(td->pwm_disable_state);
> > +	}
> > +
> >  	return 0;
> >  }
> >  
> > @@ -1625,12 +1939,6 @@ int tegra_dfll_register(struct platform_device *pdev,
> >  
> >  	td->soc = soc;
> >  
> > -	td->vdd_reg = devm_regulator_get(td->dev, "vdd-cpu");
> > -	if (IS_ERR(td->vdd_reg)) {
> > -		dev_err(td->dev, "couldn't get vdd_cpu regulator\n");
> > -		return PTR_ERR(td->vdd_reg);
> > -	}
> > -
> >  	td->dvco_rst = devm_reset_control_get(td->dev, "dvco");
> >  	if (IS_ERR(td->dvco_rst)) {
> >  		dev_err(td->dev, "couldn't get dvco reset\n");
> > @@ -1643,10 +1951,27 @@ int tegra_dfll_register(struct platform_device *pdev,
> >  		return ret;
> >  	}
> >  
> > -	ret = dfll_fetch_i2c_params(td);
> > +	if (of_property_read_bool(td->dev->of_node, "nvidia,pwm-to-pmic")) {
> > +		td->pmu_if = TEGRA_DFLL_PMU_PWM;
> > +		ret = dfll_fetch_pwm_params(td);
> > +	} else  {
> > +		td->vdd_reg = devm_regulator_get(td->dev, "vdd-cpu");
> > +		if (IS_ERR(td->vdd_reg)) {
> > +			dev_err(td->dev, "couldn't get vdd_cpu regulator\n");
> > +			return PTR_ERR(td->vdd_reg);
> > +		}
> > +		td->pmu_if = TEGRA_DFLL_PMU_I2C;
> > +		ret = dfll_fetch_i2c_params(td);
> > +	}
> >  	if (ret)
> >  		return ret;
> >  
> > +	ret = dfll_build_lut(td);
> > +	if (ret) {
> > +		dev_err(td->dev, "couldn't build LUT\n");
> > +		return ret;
> > +	}
> > +
> >  	mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> >  	if (!mem) {
> >  		dev_err(td->dev, "no control register resource\n");
> > diff --git a/drivers/clk/tegra/clk-dfll.h b/drivers/clk/tegra/clk-dfll.h
> > index 83352c8..228f54a 100644
> > --- a/drivers/clk/tegra/clk-dfll.h
> > +++ b/drivers/clk/tegra/clk-dfll.h
> > @@ -21,6 +21,7 @@
> >  #include <linux/platform_device.h>
> >  #include <linux/reset.h>
> >  #include <linux/types.h>
> > +#include "cvb.h"
> >  
> >  /**
> >   * struct tegra_dfll_soc_data - SoC-specific hooks/integration for the DFLL driver
> > @@ -35,6 +36,12 @@ struct tegra_dfll_soc_data {
> >  	struct device *dev;
> >  	unsigned long max_freq;
> >  	const struct cvb_table *cvb;
> > +	struct rail_alignment alignment;
> > +	unsigned int min_millivolts;
> > +	unsigned int tune_high_min_millivolts;
> > +	u32 tune0_low;
> > +	u32 tune0_high;
> > +	u32 tune1;
> >  
> >  	void (*init_clock_trimmers)(void);
> >  	void (*set_clock_trimmers_high)(void);
> > 
> 
> Overall this is quite a big change. I wonder if it is worth splitting
> into a preparatory patch (with all the renaming, etc) for the existing
> i2c mode and then add the pwm support. It is hard to see how the
> existing i2c code has been changed.
> 

Yes, this is largely because the original code didn't make a good split between
the 'PWM duty cycle' and the i2c regulator VID mapping. If you look at the hw,
it's obviously designed to use PWM regulators. Then i2c regulator support was
added by adding the LUT which maps a 'PWM duty cycle' to a regulator VID.

Peter.

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

* Re: [PATCH v2 2/6] clk: tegra: DT align parameter for CVB calculation
  2018-02-01 10:54             ` Jon Hunter
@ 2018-02-01 11:02                 ` Peter De Schrijver
  -1 siblings, 0 replies; 56+ messages in thread
From: Peter De Schrijver @ 2018-02-01 11:02 UTC (permalink / raw)
  To: Jon Hunter
  Cc: linux-tegra-u79uwXL29TY76Z2rM5mHXA,
	linux-clk-u79uwXL29TY76Z2rM5mHXA,
	mturquette-rdvid1DuHRBWk0Htik3J/w, sboyd-sgV2jX0FEOL9JmXXK+q4OQ

On Thu, Feb 01, 2018 at 10:54:15AM +0000, Jon Hunter wrote:
> 
> On 01/02/18 10:30, Peter De Schrijver wrote:
> > On Wed, Jan 31, 2018 at 10:43:04AM +0000, Jon Hunter wrote:
> >>
> >> On 24/01/18 12:45, Peter De Schrijver wrote:
> >>> When using a PWM controlled regulator, the voltage step and offset are
> >>> determined by the regulator IC in use. This is specified in DT rather
> >>> than fixed in the CVB table. Hence pass this information to the CVB table
> >>> calculation function. For backwards compatibility the table values are used
> >>> if the corresponding parameter is 0.
> >>>
> >>> Signed-off-by: Peter De Schrijver <pdeschrijver-DDmLM1+adcrQT0dZR+AlfA@public.gmane.org>
> >>> ---
> >>>  drivers/clk/tegra/clk-tegra124-dfll-fcpu.c | 23 +++++++++++++++++++++--
> >>>  drivers/clk/tegra/cvb.c                    | 18 ++++++++++++++----
> >>>  drivers/clk/tegra/cvb.h                    |  5 +++--
> >>>  3 files changed, 38 insertions(+), 8 deletions(-)
> >>>
> >>> diff --git a/drivers/clk/tegra/clk-tegra124-dfll-fcpu.c b/drivers/clk/tegra/clk-tegra124-dfll-fcpu.c
> >>> index 440eb8d..6205ce1 100644
> >>> --- a/drivers/clk/tegra/clk-tegra124-dfll-fcpu.c
> >>> +++ b/drivers/clk/tegra/clk-tegra124-dfll-fcpu.c
> >>> @@ -111,6 +111,7 @@ static int tegra124_dfll_fcpu_probe(struct platform_device *pdev)
> >>>  	struct tegra_dfll_soc_data *soc;
> >>>  	const struct of_device_id *of_id;
> >>>  	const struct dfll_fcpu_data *fcpu_data;
> >>> +	struct rail_alignment align;
> >>>  
> >>>  	of_id = of_match_device(tegra124_dfll_fcpu_of_match, &pdev->dev);
> >>>  	fcpu_data = of_id->data;
> >>> @@ -135,12 +136,30 @@ static int tegra124_dfll_fcpu_probe(struct platform_device *pdev)
> >>>  		return -ENODEV;
> >>>  	}
> >>>  
> >>> +	err = of_property_read_u32(pdev->dev.of_node, "nvidia,align-offset-uv",
> >>> +					&align.offset_uv);
> >>> +	if (err < 0) {
> >>> +		dev_err(&pdev->dev,
> >>> +			"offset uv not found, default to table value\n");
> >>> +		align.offset_uv = 0;
> >>> +	}
> >>> +
> >>> +	err = of_property_read_u32(pdev->dev.of_node, "nvidia,align-step-uv",
> >>> +					&align.step_uv);
> >>> +	if (err < 0) {
> >>> +		dev_err(&pdev->dev,
> >>> +			"step uv not found, default to table value\n");
> >>> +		align.step_uv = 0;
> >>> +	}
> >>> +
> >>
> >> I am a bit confused by this ...
> >>
> >> 1. Isn't this going to break Tegra124 DFLL support?
> > 
> > We fall back to the original behaviour in case the properties are missing, so
> > it should work.
> 
> Ah yes. However, on Tegra124 now I see all this prints. Can't we only
> read these properties if using PWM?
> 

I think it would make more sense then to get these parameters from the
regulator object in case of i2c. If that works on Tegra124, we can eliminate
using the CVB table align values entirely.

Peter.

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

* Re: [PATCH v2 2/6] clk: tegra: DT align parameter for CVB calculation
@ 2018-02-01 11:02                 ` Peter De Schrijver
  0 siblings, 0 replies; 56+ messages in thread
From: Peter De Schrijver @ 2018-02-01 11:02 UTC (permalink / raw)
  To: Jon Hunter; +Cc: linux-tegra, linux-clk, mturquette, sboyd

On Thu, Feb 01, 2018 at 10:54:15AM +0000, Jon Hunter wrote:
> 
> On 01/02/18 10:30, Peter De Schrijver wrote:
> > On Wed, Jan 31, 2018 at 10:43:04AM +0000, Jon Hunter wrote:
> >>
> >> On 24/01/18 12:45, Peter De Schrijver wrote:
> >>> When using a PWM controlled regulator, the voltage step and offset are
> >>> determined by the regulator IC in use. This is specified in DT rather
> >>> than fixed in the CVB table. Hence pass this information to the CVB table
> >>> calculation function. For backwards compatibility the table values are used
> >>> if the corresponding parameter is 0.
> >>>
> >>> Signed-off-by: Peter De Schrijver <pdeschrijver@nvidia.com>
> >>> ---
> >>>  drivers/clk/tegra/clk-tegra124-dfll-fcpu.c | 23 +++++++++++++++++++++--
> >>>  drivers/clk/tegra/cvb.c                    | 18 ++++++++++++++----
> >>>  drivers/clk/tegra/cvb.h                    |  5 +++--
> >>>  3 files changed, 38 insertions(+), 8 deletions(-)
> >>>
> >>> diff --git a/drivers/clk/tegra/clk-tegra124-dfll-fcpu.c b/drivers/clk/tegra/clk-tegra124-dfll-fcpu.c
> >>> index 440eb8d..6205ce1 100644
> >>> --- a/drivers/clk/tegra/clk-tegra124-dfll-fcpu.c
> >>> +++ b/drivers/clk/tegra/clk-tegra124-dfll-fcpu.c
> >>> @@ -111,6 +111,7 @@ static int tegra124_dfll_fcpu_probe(struct platform_device *pdev)
> >>>  	struct tegra_dfll_soc_data *soc;
> >>>  	const struct of_device_id *of_id;
> >>>  	const struct dfll_fcpu_data *fcpu_data;
> >>> +	struct rail_alignment align;
> >>>  
> >>>  	of_id = of_match_device(tegra124_dfll_fcpu_of_match, &pdev->dev);
> >>>  	fcpu_data = of_id->data;
> >>> @@ -135,12 +136,30 @@ static int tegra124_dfll_fcpu_probe(struct platform_device *pdev)
> >>>  		return -ENODEV;
> >>>  	}
> >>>  
> >>> +	err = of_property_read_u32(pdev->dev.of_node, "nvidia,align-offset-uv",
> >>> +					&align.offset_uv);
> >>> +	if (err < 0) {
> >>> +		dev_err(&pdev->dev,
> >>> +			"offset uv not found, default to table value\n");
> >>> +		align.offset_uv = 0;
> >>> +	}
> >>> +
> >>> +	err = of_property_read_u32(pdev->dev.of_node, "nvidia,align-step-uv",
> >>> +					&align.step_uv);
> >>> +	if (err < 0) {
> >>> +		dev_err(&pdev->dev,
> >>> +			"step uv not found, default to table value\n");
> >>> +		align.step_uv = 0;
> >>> +	}
> >>> +
> >>
> >> I am a bit confused by this ...
> >>
> >> 1. Isn't this going to break Tegra124 DFLL support?
> > 
> > We fall back to the original behaviour in case the properties are missing, so
> > it should work.
> 
> Ah yes. However, on Tegra124 now I see all this prints. Can't we only
> read these properties if using PWM?
> 

I think it would make more sense then to get these parameters from the
regulator object in case of i2c. If that works on Tegra124, we can eliminate
using the CVB table align values entirely.

Peter.

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

* Re: [PATCH v2 6/6] cpufreq: tegra124-cpufreq: extend to support Tegra210
  2018-02-01  9:25           ` Peter De Schrijver
@ 2018-02-06 12:27             ` Tuomas Tynkkynen
  -1 siblings, 0 replies; 56+ messages in thread
From: Tuomas Tynkkynen @ 2018-02-06 12:27 UTC (permalink / raw)
  To: Peter De Schrijver
  Cc: linux-tegra, linux-clk, Michael Turquette, Stephen Boyd, Jonathan Hunter

(Attempting to post through Gmane - let's see if this works at all).

On Thu, 1 Feb 2018 11:25:20 +0200
Peter De Schrijver <pdeschrijver-DDmLM1+adcrQT0dZR+AlfA@public.gmane.org> wrote:

> On Wed, Jan 31, 2018 at 11:06:36AM +0000, Jon Hunter wrote:
> > 
> > On 24/01/18 12:45, Peter De Schrijver wrote:  
> > > Tegra210 has a very similar CPU clocking scheme than Tegra124. So add
> > > support in this driver. Also allow for the case where the CPU voltage is
> > > controlled directly by the DFLL rather than by a separate regulator object.
> > > 
> > > Signed-off-by: Peter De Schrijver <pdeschrijver-DDmLM1+adcrQT0dZR+AlfA@public.gmane.org>
> > > ---
> > >  drivers/cpufreq/tegra124-cpufreq.c | 13 ++++++-------
> > >  1 file changed, 6 insertions(+), 7 deletions(-)
> > > 
> > > diff --git a/drivers/cpufreq/tegra124-cpufreq.c b/drivers/cpufreq/tegra124-cpufreq.c
> > > index 4353025..d631dc4 100644
> > > --- a/drivers/cpufreq/tegra124-cpufreq.c
> > > +++ b/drivers/cpufreq/tegra124-cpufreq.c
> > > @@ -64,7 +64,8 @@ static void tegra124_cpu_switch_to_pllx(struct tegra124_cpufreq_priv *priv)
> > >  {
> > >  	clk_set_parent(priv->cpu_clk, priv->pllp_clk);
> > >  	clk_disable_unprepare(priv->dfll_clk);
> > > -	regulator_sync_voltage(priv->vdd_cpu_reg);
> > > +	if (priv->vdd_cpu_reg)
> > > +		regulator_sync_voltage(priv->vdd_cpu_reg);
> > >  	clk_set_parent(priv->cpu_clk, priv->pllx_clk);
> > >  }
> > >  
> > > @@ -89,10 +90,8 @@ static int tegra124_cpufreq_probe(struct platform_device *pdev)
> > >  		return -ENODEV;
> > >  
> > >  	priv->vdd_cpu_reg = regulator_get(cpu_dev, "vdd-cpu");
> > > -	if (IS_ERR(priv->vdd_cpu_reg)) {
> > > -		ret = PTR_ERR(priv->vdd_cpu_reg);
> > > -		goto out_put_np;
> > > -	}
> > > +	if (IS_ERR(priv->vdd_cpu_reg))
> > > +		priv->vdd_cpu_reg = NULL;
> > >    
> > 
> > For Tegra124, don't we still want to return an error here?  
> 
> Not necessarily. Also on Tegra124 the DFLL can use PWM and I think jetson TK1
> can be reworked to use this.
> 
> Peter.
> 

Regardless of that, you need to bail out on -EPROBE_DEFER I think.

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

* Re: [PATCH v2 6/6] cpufreq: tegra124-cpufreq: extend to support Tegra210
@ 2018-02-06 12:27             ` Tuomas Tynkkynen
  0 siblings, 0 replies; 56+ messages in thread
From: Tuomas Tynkkynen @ 2018-02-06 12:27 UTC (permalink / raw)
  To: Peter De Schrijver
  Cc: linux-tegra, linux-clk, Michael Turquette, Stephen Boyd, Jonathan Hunter

(Attempting to post through Gmane - let's see if this works at all).

On Thu, 1 Feb 2018 11:25:20 +0200
Peter De Schrijver <pdeschrijver-DDmLM1+adcrQT0dZR+AlfA@public.gmane.org> wrote:

> On Wed, Jan 31, 2018 at 11:06:36AM +0000, Jon Hunter wrote:
> > 
> > On 24/01/18 12:45, Peter De Schrijver wrote:  
> > > Tegra210 has a very similar CPU clocking scheme than Tegra124. So add
> > > support in this driver. Also allow for the case where the CPU voltage is
> > > controlled directly by the DFLL rather than by a separate regulator object.
> > > 
> > > Signed-off-by: Peter De Schrijver <pdeschrijver-DDmLM1+adcrQT0dZR+AlfA@public.gmane.org>
> > > ---
> > >  drivers/cpufreq/tegra124-cpufreq.c | 13 ++++++-------
> > >  1 file changed, 6 insertions(+), 7 deletions(-)
> > > 
> > > diff --git a/drivers/cpufreq/tegra124-cpufreq.c b/drivers/cpufreq/tegra124-cpufreq.c
> > > index 4353025..d631dc4 100644
> > > --- a/drivers/cpufreq/tegra124-cpufreq.c
> > > +++ b/drivers/cpufreq/tegra124-cpufreq.c
> > > @@ -64,7 +64,8 @@ static void tegra124_cpu_switch_to_pllx(struct tegra124_cpufreq_priv *priv)
> > >  {
> > >  	clk_set_parent(priv->cpu_clk, priv->pllp_clk);
> > >  	clk_disable_unprepare(priv->dfll_clk);
> > > -	regulator_sync_voltage(priv->vdd_cpu_reg);
> > > +	if (priv->vdd_cpu_reg)
> > > +		regulator_sync_voltage(priv->vdd_cpu_reg);
> > >  	clk_set_parent(priv->cpu_clk, priv->pllx_clk);
> > >  }
> > >  
> > > @@ -89,10 +90,8 @@ static int tegra124_cpufreq_probe(struct platform_device *pdev)
> > >  		return -ENODEV;
> > >  
> > >  	priv->vdd_cpu_reg = regulator_get(cpu_dev, "vdd-cpu");
> > > -	if (IS_ERR(priv->vdd_cpu_reg)) {
> > > -		ret = PTR_ERR(priv->vdd_cpu_reg);
> > > -		goto out_put_np;
> > > -	}
> > > +	if (IS_ERR(priv->vdd_cpu_reg))
> > > +		priv->vdd_cpu_reg = NULL;
> > >    
> > 
> > For Tegra124, don't we still want to return an error here?  
> 
> Not necessarily. Also on Tegra124 the DFLL can use PWM and I think jetson TK1
> can be reworked to use this.
> 
> Peter.
> 

Regardless of that, you need to bail out on -EPROBE_DEFER I think.

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

* Re: [PATCH v2 6/6] cpufreq: tegra124-cpufreq: extend to support Tegra210
  2018-02-06 12:27             ` Tuomas Tynkkynen
@ 2018-02-06 16:28               ` Peter De Schrijver
  -1 siblings, 0 replies; 56+ messages in thread
From: Peter De Schrijver @ 2018-02-06 16:28 UTC (permalink / raw)
  To: Tuomas Tynkkynen
  Cc: linux-tegra-u79uwXL29TY76Z2rM5mHXA,
	linux-clk-u79uwXL29TY76Z2rM5mHXA, Michael Turquette,
	Stephen Boyd, Jonathan Hunter

On Tue, Feb 06, 2018 at 02:27:20PM +0200, Tuomas Tynkkynen wrote:
> (Attempting to post through Gmane - let's see if this works at all).
> 
> On Thu, 1 Feb 2018 11:25:20 +0200
> Peter De Schrijver <pdeschrijver-DDmLM1+adcrQT0dZR+AlfA-XMD5yJDbdMReXY1tMh2IBg@public.gmane.org> wrote:
> 
> > On Wed, Jan 31, 2018 at 11:06:36AM +0000, Jon Hunter wrote:
> > > 
> > > On 24/01/18 12:45, Peter De Schrijver wrote:  
> > > > Tegra210 has a very similar CPU clocking scheme than Tegra124. So add
> > > > support in this driver. Also allow for the case where the CPU voltage is
> > > > controlled directly by the DFLL rather than by a separate regulator object.
> > > > 
> > > > Signed-off-by: Peter De Schrijver <pdeschrijver-DDmLM1+adcrQT0dZR+AlfA-XMD5yJDbdMReXY1tMh2IBg@public.gmane.org>
> > > > ---
> > > >  drivers/cpufreq/tegra124-cpufreq.c | 13 ++++++-------
> > > >  1 file changed, 6 insertions(+), 7 deletions(-)
> > > > 
> > > > diff --git a/drivers/cpufreq/tegra124-cpufreq.c b/drivers/cpufreq/tegra124-cpufreq.c
> > > > index 4353025..d631dc4 100644
> > > > --- a/drivers/cpufreq/tegra124-cpufreq.c
> > > > +++ b/drivers/cpufreq/tegra124-cpufreq.c
> > > > @@ -64,7 +64,8 @@ static void tegra124_cpu_switch_to_pllx(struct tegra124_cpufreq_priv *priv)
> > > >  {
> > > >  	clk_set_parent(priv->cpu_clk, priv->pllp_clk);
> > > >  	clk_disable_unprepare(priv->dfll_clk);
> > > > -	regulator_sync_voltage(priv->vdd_cpu_reg);
> > > > +	if (priv->vdd_cpu_reg)
> > > > +		regulator_sync_voltage(priv->vdd_cpu_reg);
> > > >  	clk_set_parent(priv->cpu_clk, priv->pllx_clk);
> > > >  }
> > > >  
> > > > @@ -89,10 +90,8 @@ static int tegra124_cpufreq_probe(struct platform_device *pdev)
> > > >  		return -ENODEV;
> > > >  
> > > >  	priv->vdd_cpu_reg = regulator_get(cpu_dev, "vdd-cpu");
> > > > -	if (IS_ERR(priv->vdd_cpu_reg)) {
> > > > -		ret = PTR_ERR(priv->vdd_cpu_reg);
> > > > -		goto out_put_np;
> > > > -	}
> > > > +	if (IS_ERR(priv->vdd_cpu_reg))
> > > > +		priv->vdd_cpu_reg = NULL;
> > > >    
> > > 
> > > For Tegra124, don't we still want to return an error here?  
> > 
> > Not necessarily. Also on Tegra124 the DFLL can use PWM and I think jetson TK1
> > can be reworked to use this.
> > 
> > Peter.
> > 
> 
> Regardless of that, you need to bail out on -EPROBE_DEFER I think.

Ah yes, likely. That said the code isn't correct because the voltage margin
when using PLLX is higher than when using CL-DVFS..

Peter.

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

* Re: [PATCH v2 6/6] cpufreq: tegra124-cpufreq: extend to support Tegra210
@ 2018-02-06 16:28               ` Peter De Schrijver
  0 siblings, 0 replies; 56+ messages in thread
From: Peter De Schrijver @ 2018-02-06 16:28 UTC (permalink / raw)
  To: Tuomas Tynkkynen
  Cc: linux-tegra, linux-clk, Michael Turquette, Stephen Boyd, Jonathan Hunter

On Tue, Feb 06, 2018 at 02:27:20PM +0200, Tuomas Tynkkynen wrote:
> (Attempting to post through Gmane - let's see if this works at all).
> 
> On Thu, 1 Feb 2018 11:25:20 +0200
> Peter De Schrijver <pdeschrijver-DDmLM1+adcrQT0dZR+AlfA@public.gmane.org> wrote:
> 
> > On Wed, Jan 31, 2018 at 11:06:36AM +0000, Jon Hunter wrote:
> > > 
> > > On 24/01/18 12:45, Peter De Schrijver wrote:  
> > > > Tegra210 has a very similar CPU clocking scheme than Tegra124. So add
> > > > support in this driver. Also allow for the case where the CPU voltage is
> > > > controlled directly by the DFLL rather than by a separate regulator object.
> > > > 
> > > > Signed-off-by: Peter De Schrijver <pdeschrijver-DDmLM1+adcrQT0dZR+AlfA@public.gmane.org>
> > > > ---
> > > >  drivers/cpufreq/tegra124-cpufreq.c | 13 ++++++-------
> > > >  1 file changed, 6 insertions(+), 7 deletions(-)
> > > > 
> > > > diff --git a/drivers/cpufreq/tegra124-cpufreq.c b/drivers/cpufreq/tegra124-cpufreq.c
> > > > index 4353025..d631dc4 100644
> > > > --- a/drivers/cpufreq/tegra124-cpufreq.c
> > > > +++ b/drivers/cpufreq/tegra124-cpufreq.c
> > > > @@ -64,7 +64,8 @@ static void tegra124_cpu_switch_to_pllx(struct tegra124_cpufreq_priv *priv)
> > > >  {
> > > >  	clk_set_parent(priv->cpu_clk, priv->pllp_clk);
> > > >  	clk_disable_unprepare(priv->dfll_clk);
> > > > -	regulator_sync_voltage(priv->vdd_cpu_reg);
> > > > +	if (priv->vdd_cpu_reg)
> > > > +		regulator_sync_voltage(priv->vdd_cpu_reg);
> > > >  	clk_set_parent(priv->cpu_clk, priv->pllx_clk);
> > > >  }
> > > >  
> > > > @@ -89,10 +90,8 @@ static int tegra124_cpufreq_probe(struct platform_device *pdev)
> > > >  		return -ENODEV;
> > > >  
> > > >  	priv->vdd_cpu_reg = regulator_get(cpu_dev, "vdd-cpu");
> > > > -	if (IS_ERR(priv->vdd_cpu_reg)) {
> > > > -		ret = PTR_ERR(priv->vdd_cpu_reg);
> > > > -		goto out_put_np;
> > > > -	}
> > > > +	if (IS_ERR(priv->vdd_cpu_reg))
> > > > +		priv->vdd_cpu_reg = NULL;
> > > >    
> > > 
> > > For Tegra124, don't we still want to return an error here?  
> > 
> > Not necessarily. Also on Tegra124 the DFLL can use PWM and I think jetson TK1
> > can be reworked to use this.
> > 
> > Peter.
> > 
> 
> Regardless of that, you need to bail out on -EPROBE_DEFER I think.

Ah yes, likely. That said the code isn't correct because the voltage margin
when using PLLX is higher than when using CL-DVFS..

Peter.

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

end of thread, other threads:[~2018-02-06 16:28 UTC | newest]

Thread overview: 56+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2018-01-24 12:45 [PATCH v2 0/6] Tegra210 DFLL implementation Peter De Schrijver
2018-01-24 12:45 ` Peter De Schrijver
2018-01-24 12:45 ` Peter De Schrijver
2018-01-24 12:45   ` Peter De Schrijver
     [not found] ` <1516797938-32044-1-git-send-email-pdeschrijver-DDmLM1+adcrQT0dZR+AlfA@public.gmane.org>
2018-01-24 12:45   ` [PATCH v2 1/6] clk: tegra: dfll registration for multiple SoCs Peter De Schrijver
2018-01-24 12:45     ` Peter De Schrijver
2018-01-31 10:13     ` Jon Hunter
2018-01-31 10:13       ` Jon Hunter
2018-01-24 12:45   ` [PATCH v2 5/6] clk: tegra: build clk-dfll.c for Tegra124 and Tegra210 Peter De Schrijver
2018-01-24 12:45     ` Peter De Schrijver
2018-01-31 11:03     ` Jon Hunter
2018-01-31 11:03       ` Jon Hunter
2018-01-24 12:45   ` [PATCH v2 6/6] cpufreq: tegra124-cpufreq: extend to support Tegra210 Peter De Schrijver
2018-01-24 12:45     ` Peter De Schrijver
2018-01-31 11:06     ` Jon Hunter
2018-01-31 11:06       ` Jon Hunter
     [not found]       ` <248eee6f-7f37-a5d1-8eff-116c3ef57751-DDmLM1+adcrQT0dZR+AlfA@public.gmane.org>
2018-02-01  9:25         ` Peter De Schrijver
2018-02-01  9:25           ` Peter De Schrijver
2018-02-01  9:53           ` Jon Hunter
2018-02-01  9:53             ` Jon Hunter
     [not found]             ` <f91af7fd-6152-2f44-6e85-f1900356ab1f-DDmLM1+adcrQT0dZR+AlfA@public.gmane.org>
2018-02-01 10:15               ` Peter De Schrijver
2018-02-01 10:15                 ` Peter De Schrijver
2018-02-01 10:22                 ` Jon Hunter
2018-02-01 10:22                   ` Jon Hunter
2018-02-01 10:31                   ` Peter De Schrijver
2018-02-01 10:31                     ` Peter De Schrijver
2018-02-06 12:27           ` Tuomas Tynkkynen
2018-02-06 12:27             ` Tuomas Tynkkynen
2018-02-06 16:28             ` Peter De Schrijver
2018-02-06 16:28               ` Peter De Schrijver
2018-01-31 11:28   ` [PATCH v2 0/6] Tegra210 DFLL implementation Jon Hunter
2018-01-31 11:28     ` Jon Hunter
2018-02-01  9:30     ` Peter De Schrijver
2018-02-01  9:30       ` Peter De Schrijver
2018-01-24 12:45 ` [PATCH v2 2/6] clk: tegra: DT align parameter for CVB calculation Peter De Schrijver
2018-01-24 12:45   ` Peter De Schrijver
2018-01-31 10:43   ` Jon Hunter
2018-01-31 10:43     ` Jon Hunter
     [not found]     ` <7066d1ff-6da4-203d-57ea-cf179fea17be-DDmLM1+adcrQT0dZR+AlfA@public.gmane.org>
2018-02-01 10:30       ` Peter De Schrijver
2018-02-01 10:30         ` Peter De Schrijver
     [not found]         ` <20180201103002.GY7031-Rysk9IDjsxmJz7etNGeUX8VPkgjIgRvpAL8bYrjMMd8@public.gmane.org>
2018-02-01 10:54           ` Jon Hunter
2018-02-01 10:54             ` Jon Hunter
     [not found]             ` <98da869b-e3b5-d703-c2bd-c34a081dfbc8-DDmLM1+adcrQT0dZR+AlfA@public.gmane.org>
2018-02-01 11:02               ` Peter De Schrijver
2018-02-01 11:02                 ` Peter De Schrijver
2018-01-24 12:45 ` [PATCH v2 3/6] clk: tegra: add CVB tables for Tegra210 CPU DFLL Peter De Schrijver
2018-01-24 12:45   ` Peter De Schrijver
2018-01-31 10:50   ` Jon Hunter
2018-01-31 10:50     ` Jon Hunter
     [not found]     ` <2050cb08-46ed-eca4-cfee-e8fb47fa312a-DDmLM1+adcrQT0dZR+AlfA@public.gmane.org>
2018-02-01 10:39       ` Peter De Schrijver
2018-02-01 10:39         ` Peter De Schrijver
2018-01-24 12:45 ` [PATCH v2 4/6] clk: tegra: dfll: support PWM regulator control Peter De Schrijver
2018-01-24 12:45   ` Peter De Schrijver
2018-01-31 11:26   ` Jon Hunter
2018-01-31 11:26     ` Jon Hunter
2018-02-01 10:57     ` Peter De Schrijver
2018-02-01 10:57       ` Peter De Schrijver

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.