Re: [TEGRA194_CPUFREQ PATCH v5 3/4] cpufreq: Add Tegra194 cpufreq driver

2020-07-15 Thread Sumit Gupta

Thank you for the review,


Add support for CPU frequency scaling on Tegra194. The frequency
of each core can be adjusted by writing a clock divisor value to
a MSR on the core. The range of valid divisors is queried from
the BPMP.

Signed-off-by: Mikko Perttunen 
Signed-off-by: Sumit Gupta 
---
  drivers/cpufreq/Kconfig.arm|   6 +
  drivers/cpufreq/Makefile   |   1 +
  drivers/cpufreq/tegra194-cpufreq.c | 397 +
  3 files changed, 404 insertions(+)
  create mode 100644 drivers/cpufreq/tegra194-cpufreq.c

diff --git a/drivers/cpufreq/Kconfig.arm b/drivers/cpufreq/Kconfig.arm
index 15c1a12..f3d8f09 100644
--- a/drivers/cpufreq/Kconfig.arm
+++ b/drivers/cpufreq/Kconfig.arm
@@ -314,6 +314,12 @@ config ARM_TEGRA186_CPUFREQ
   help
 This adds the CPUFreq driver support for Tegra186 SOCs.

+config ARM_TEGRA194_CPUFREQ
+ tristate "Tegra194 CPUFreq support"
+ depends on ARCH_TEGRA && TEGRA_BPMP


Shouldn't this depend on ARCH_TEGRA_194_SOC instead ? And I asked you
to add a default y here itself instead of patch 4/4.


Ok.


+ help
+   This adds CPU frequency driver support for Tegra194 SOCs.
+
  config ARM_TI_CPUFREQ
   bool "Texas Instruments CPUFreq support"
   depends on ARCH_OMAP2PLUS
diff --git a/drivers/cpufreq/Makefile b/drivers/cpufreq/Makefile
index f6670c4..66b5563 100644
--- a/drivers/cpufreq/Makefile
+++ b/drivers/cpufreq/Makefile
@@ -83,6 +83,7 @@ obj-$(CONFIG_ARM_TANGO_CPUFREQ) += tango-cpufreq.o
  obj-$(CONFIG_ARM_TEGRA20_CPUFREQ)+= tegra20-cpufreq.o
  obj-$(CONFIG_ARM_TEGRA124_CPUFREQ)   += tegra124-cpufreq.o
  obj-$(CONFIG_ARM_TEGRA186_CPUFREQ)   += tegra186-cpufreq.o
+obj-$(CONFIG_ARM_TEGRA194_CPUFREQ)   += tegra194-cpufreq.o
  obj-$(CONFIG_ARM_TI_CPUFREQ) += ti-cpufreq.o
  obj-$(CONFIG_ARM_VEXPRESS_SPC_CPUFREQ)   += vexpress-spc-cpufreq.o

diff --git a/drivers/cpufreq/tegra194-cpufreq.c 
b/drivers/cpufreq/tegra194-cpufreq.c
+static struct cpufreq_frequency_table *
+init_freq_table(struct platform_device *pdev, struct tegra_bpmp *bpmp,
+ unsigned int cluster_id)
+{
+ struct cpufreq_frequency_table *freq_table;
+ struct mrq_cpu_ndiv_limits_response resp;
+ unsigned int num_freqs, ndiv, delta_ndiv;
+ struct mrq_cpu_ndiv_limits_request req;
+ struct tegra_bpmp_message msg;
+ u16 freq_table_step_size;
+ int err, index;
+
+ memset(, 0, sizeof(req));
+ req.cluster_id = cluster_id;
+
+ memset(, 0, sizeof(msg));
+ msg.mrq = MRQ_CPU_NDIV_LIMITS;
+ msg.tx.data = 
+ msg.tx.size = sizeof(req);
+ msg.rx.data = 
+ msg.rx.size = sizeof(resp);
+
+ err = tegra_bpmp_transfer(bpmp, );
+ if (err)
+ return ERR_PTR(err);
+
+ /*
+  * Make sure frequency table step is a multiple of mdiv to match
+  * vhint table granularity.
+  */
+ freq_table_step_size = resp.mdiv *
+ DIV_ROUND_UP(CPUFREQ_TBL_STEP_HZ, resp.ref_clk_hz);
+
+ dev_dbg(>dev, "cluster %d: frequency table step size: %d\n",
+ cluster_id, freq_table_step_size);
+
+ delta_ndiv = resp.ndiv_max - resp.ndiv_min;
+
+ if (unlikely(delta_ndiv == 0))
+ num_freqs = 1;
+ else
+ /* We store both ndiv_min and ndiv_max hence the +1 */
+ num_freqs = delta_ndiv / freq_table_step_size + 1;


You need {} in the if else blocks here because of the comment here.


Ok.


+
+ num_freqs += (delta_ndiv % freq_table_step_size) ? 1 : 0;
+
+ freq_table = devm_kcalloc(>dev, num_freqs + 1,
+   sizeof(*freq_table), GFP_KERNEL);
+ if (!freq_table)
+ return ERR_PTR(-ENOMEM);
+
+ for (index = 0, ndiv = resp.ndiv_min;
+ ndiv < resp.ndiv_max;
+ index++, ndiv += freq_table_step_size) {
+ freq_table[index].driver_data = ndiv;
+ freq_table[index].frequency = map_ndiv_to_freq(, ndiv);
+ }
+
+ freq_table[index].driver_data = resp.ndiv_max;
+ freq_table[index++].frequency = map_ndiv_to_freq(, resp.ndiv_max);
+ freq_table[index].frequency = CPUFREQ_TABLE_END;
+
+ return freq_table;
+}
+
+static int tegra194_cpufreq_probe(struct platform_device *pdev)
+{
+ struct tegra194_cpufreq_data *data;
+ struct tegra_bpmp *bpmp;
+ int err, i;
+
+ data = devm_kzalloc(>dev, sizeof(*data), GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+
+ data->num_clusters = MAX_CLUSTERS;
+ data->tables = devm_kcalloc(>dev, data->num_clusters,
+ sizeof(*data->tables), GFP_KERNEL);
+ if (!data->tables)
+ return -ENOMEM;
+
+ platform_set_drvdata(pdev, data);
+
+ bpmp = tegra_bpmp_get(>dev);
+ if (IS_ERR(bpmp))
+ return PTR_ERR(bpmp);
+
+ read_counters_wq = alloc_workqueue("read_counters_wq", __WQ_LEGACY, 1);
+ if (!read_counters_wq) {
+ dev_err(>dev, "fail to 

Re: [TEGRA194_CPUFREQ PATCH v5 3/4] cpufreq: Add Tegra194 cpufreq driver

2020-07-15 Thread Viresh Kumar
On 13-07-20, 19:36, Sumit Gupta wrote:
> Add support for CPU frequency scaling on Tegra194. The frequency
> of each core can be adjusted by writing a clock divisor value to
> a MSR on the core. The range of valid divisors is queried from
> the BPMP.
> 
> Signed-off-by: Mikko Perttunen 
> Signed-off-by: Sumit Gupta 
> ---
>  drivers/cpufreq/Kconfig.arm|   6 +
>  drivers/cpufreq/Makefile   |   1 +
>  drivers/cpufreq/tegra194-cpufreq.c | 397 
> +
>  3 files changed, 404 insertions(+)
>  create mode 100644 drivers/cpufreq/tegra194-cpufreq.c
> 
> diff --git a/drivers/cpufreq/Kconfig.arm b/drivers/cpufreq/Kconfig.arm
> index 15c1a12..f3d8f09 100644
> --- a/drivers/cpufreq/Kconfig.arm
> +++ b/drivers/cpufreq/Kconfig.arm
> @@ -314,6 +314,12 @@ config ARM_TEGRA186_CPUFREQ
>   help
> This adds the CPUFreq driver support for Tegra186 SOCs.
>  
> +config ARM_TEGRA194_CPUFREQ
> + tristate "Tegra194 CPUFreq support"
> + depends on ARCH_TEGRA && TEGRA_BPMP

Shouldn't this depend on ARCH_TEGRA_194_SOC instead ? And I asked you
to add a default y here itself instead of patch 4/4.

> + help
> +   This adds CPU frequency driver support for Tegra194 SOCs.
> +
>  config ARM_TI_CPUFREQ
>   bool "Texas Instruments CPUFreq support"
>   depends on ARCH_OMAP2PLUS
> diff --git a/drivers/cpufreq/Makefile b/drivers/cpufreq/Makefile
> index f6670c4..66b5563 100644
> --- a/drivers/cpufreq/Makefile
> +++ b/drivers/cpufreq/Makefile
> @@ -83,6 +83,7 @@ obj-$(CONFIG_ARM_TANGO_CPUFREQ) += 
> tango-cpufreq.o
>  obj-$(CONFIG_ARM_TEGRA20_CPUFREQ)+= tegra20-cpufreq.o
>  obj-$(CONFIG_ARM_TEGRA124_CPUFREQ)   += tegra124-cpufreq.o
>  obj-$(CONFIG_ARM_TEGRA186_CPUFREQ)   += tegra186-cpufreq.o
> +obj-$(CONFIG_ARM_TEGRA194_CPUFREQ)   += tegra194-cpufreq.o
>  obj-$(CONFIG_ARM_TI_CPUFREQ) += ti-cpufreq.o
>  obj-$(CONFIG_ARM_VEXPRESS_SPC_CPUFREQ)   += vexpress-spc-cpufreq.o
>  
> diff --git a/drivers/cpufreq/tegra194-cpufreq.c 
> b/drivers/cpufreq/tegra194-cpufreq.c
> +static struct cpufreq_frequency_table *
> +init_freq_table(struct platform_device *pdev, struct tegra_bpmp *bpmp,
> + unsigned int cluster_id)
> +{
> + struct cpufreq_frequency_table *freq_table;
> + struct mrq_cpu_ndiv_limits_response resp;
> + unsigned int num_freqs, ndiv, delta_ndiv;
> + struct mrq_cpu_ndiv_limits_request req;
> + struct tegra_bpmp_message msg;
> + u16 freq_table_step_size;
> + int err, index;
> +
> + memset(, 0, sizeof(req));
> + req.cluster_id = cluster_id;
> +
> + memset(, 0, sizeof(msg));
> + msg.mrq = MRQ_CPU_NDIV_LIMITS;
> + msg.tx.data = 
> + msg.tx.size = sizeof(req);
> + msg.rx.data = 
> + msg.rx.size = sizeof(resp);
> +
> + err = tegra_bpmp_transfer(bpmp, );
> + if (err)
> + return ERR_PTR(err);
> +
> + /*
> +  * Make sure frequency table step is a multiple of mdiv to match
> +  * vhint table granularity.
> +  */
> + freq_table_step_size = resp.mdiv *
> + DIV_ROUND_UP(CPUFREQ_TBL_STEP_HZ, resp.ref_clk_hz);
> +
> + dev_dbg(>dev, "cluster %d: frequency table step size: %d\n",
> + cluster_id, freq_table_step_size);
> +
> + delta_ndiv = resp.ndiv_max - resp.ndiv_min;
> +
> + if (unlikely(delta_ndiv == 0))
> + num_freqs = 1;
> + else
> + /* We store both ndiv_min and ndiv_max hence the +1 */
> + num_freqs = delta_ndiv / freq_table_step_size + 1;

You need {} in the if else blocks here because of the comment here.

> +
> + num_freqs += (delta_ndiv % freq_table_step_size) ? 1 : 0;
> +
> + freq_table = devm_kcalloc(>dev, num_freqs + 1,
> +   sizeof(*freq_table), GFP_KERNEL);
> + if (!freq_table)
> + return ERR_PTR(-ENOMEM);
> +
> + for (index = 0, ndiv = resp.ndiv_min;
> + ndiv < resp.ndiv_max;
> + index++, ndiv += freq_table_step_size) {
> + freq_table[index].driver_data = ndiv;
> + freq_table[index].frequency = map_ndiv_to_freq(, ndiv);
> + }
> +
> + freq_table[index].driver_data = resp.ndiv_max;
> + freq_table[index++].frequency = map_ndiv_to_freq(, resp.ndiv_max);
> + freq_table[index].frequency = CPUFREQ_TABLE_END;
> +
> + return freq_table;
> +}
> +
> +static int tegra194_cpufreq_probe(struct platform_device *pdev)
> +{
> + struct tegra194_cpufreq_data *data;
> + struct tegra_bpmp *bpmp;
> + int err, i;
> +
> + data = devm_kzalloc(>dev, sizeof(*data), GFP_KERNEL);
> + if (!data)
> + return -ENOMEM;
> +
> + data->num_clusters = MAX_CLUSTERS;
> + data->tables = devm_kcalloc(>dev, data->num_clusters,
> + sizeof(*data->tables), GFP_KERNEL);
> + if (!data->tables)
> + return -ENOMEM;
> +
> + platform_set_drvdata(pdev, data);
> +
> + 

[TEGRA194_CPUFREQ PATCH v5 3/4] cpufreq: Add Tegra194 cpufreq driver

2020-07-13 Thread Sumit Gupta
Add support for CPU frequency scaling on Tegra194. The frequency
of each core can be adjusted by writing a clock divisor value to
a MSR on the core. The range of valid divisors is queried from
the BPMP.

Signed-off-by: Mikko Perttunen 
Signed-off-by: Sumit Gupta 
---
 drivers/cpufreq/Kconfig.arm|   6 +
 drivers/cpufreq/Makefile   |   1 +
 drivers/cpufreq/tegra194-cpufreq.c | 397 +
 3 files changed, 404 insertions(+)
 create mode 100644 drivers/cpufreq/tegra194-cpufreq.c

diff --git a/drivers/cpufreq/Kconfig.arm b/drivers/cpufreq/Kconfig.arm
index 15c1a12..f3d8f09 100644
--- a/drivers/cpufreq/Kconfig.arm
+++ b/drivers/cpufreq/Kconfig.arm
@@ -314,6 +314,12 @@ config ARM_TEGRA186_CPUFREQ
help
  This adds the CPUFreq driver support for Tegra186 SOCs.
 
+config ARM_TEGRA194_CPUFREQ
+   tristate "Tegra194 CPUFreq support"
+   depends on ARCH_TEGRA && TEGRA_BPMP
+   help
+ This adds CPU frequency driver support for Tegra194 SOCs.
+
 config ARM_TI_CPUFREQ
bool "Texas Instruments CPUFreq support"
depends on ARCH_OMAP2PLUS
diff --git a/drivers/cpufreq/Makefile b/drivers/cpufreq/Makefile
index f6670c4..66b5563 100644
--- a/drivers/cpufreq/Makefile
+++ b/drivers/cpufreq/Makefile
@@ -83,6 +83,7 @@ obj-$(CONFIG_ARM_TANGO_CPUFREQ)   += 
tango-cpufreq.o
 obj-$(CONFIG_ARM_TEGRA20_CPUFREQ)  += tegra20-cpufreq.o
 obj-$(CONFIG_ARM_TEGRA124_CPUFREQ) += tegra124-cpufreq.o
 obj-$(CONFIG_ARM_TEGRA186_CPUFREQ) += tegra186-cpufreq.o
+obj-$(CONFIG_ARM_TEGRA194_CPUFREQ) += tegra194-cpufreq.o
 obj-$(CONFIG_ARM_TI_CPUFREQ)   += ti-cpufreq.o
 obj-$(CONFIG_ARM_VEXPRESS_SPC_CPUFREQ) += vexpress-spc-cpufreq.o
 
diff --git a/drivers/cpufreq/tegra194-cpufreq.c 
b/drivers/cpufreq/tegra194-cpufreq.c
new file mode 100644
index 000..450477f
--- /dev/null
+++ b/drivers/cpufreq/tegra194-cpufreq.c
@@ -0,0 +1,397 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2020, NVIDIA CORPORATION. All rights reserved
+ */
+
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+
+#include 
+
+#include 
+#include 
+
+#define KHZ 1000
+#define REF_CLK_MHZ 408 /* 408 MHz */
+#define US_DELAY500
+#define US_DELAY_MIN2
+#define CPUFREQ_TBL_STEP_HZ (50 * KHZ * KHZ)
+#define MAX_CNT ~0U
+
+/* cpufreq transisition latency */
+#define TEGRA_CPUFREQ_TRANSITION_LATENCY (300 * 1000) /* unit in nanoseconds */
+
+enum cluster {
+   CLUSTER0,
+   CLUSTER1,
+   CLUSTER2,
+   CLUSTER3,
+   MAX_CLUSTERS,
+};
+
+struct tegra194_cpufreq_data {
+   void __iomem *regs;
+   size_t num_clusters;
+   struct cpufreq_frequency_table **tables;
+};
+
+struct tegra_cpu_ctr {
+   u32 cpu;
+   u32 delay;
+   u32 coreclk_cnt, last_coreclk_cnt;
+   u32 refclk_cnt, last_refclk_cnt;
+};
+
+struct read_counters_work {
+   struct work_struct work;
+   struct tegra_cpu_ctr c;
+};
+
+static struct workqueue_struct *read_counters_wq;
+
+static enum cluster get_cpu_cluster(u8 cpu)
+{
+   return MPIDR_AFFINITY_LEVEL(cpu_logical_map(cpu), 1);
+}
+
+/*
+ * Read per-core Read-only system register NVFREQ_FEEDBACK_EL1.
+ * The register provides frequency feedback information to
+ * determine the average actual frequency a core has run at over
+ * a period of time.
+ * [31:0] PLLP counter: Counts at fixed frequency (408 MHz)
+ * [63:32] Core clock counter: counts on every core clock cycle
+ * where the core is architecturally clocking
+ */
+static u64 read_freq_feedback(void)
+{
+   u64 val = 0;
+
+   asm volatile("mrs %0, s3_0_c15_c0_5" : "=r" (val) : );
+
+   return val;
+}
+
+static inline u32 map_ndiv_to_freq(struct mrq_cpu_ndiv_limits_response
+  *nltbl, u16 ndiv)
+{
+   return nltbl->ref_clk_hz / KHZ * ndiv / (nltbl->pdiv * nltbl->mdiv);
+}
+
+static void tegra_read_counters(struct work_struct *work)
+{
+   struct read_counters_work *read_counters_work;
+   struct tegra_cpu_ctr *c;
+   u64 val;
+
+   /*
+* ref_clk_counter(32 bit counter) runs on constant clk,
+* pll_p(408MHz).
+* It will take = 2 ^ 32 / 408 MHz to overflow ref clk counter
+*  = 10526880 usec = 10.527 sec to overflow
+*
+* Like wise core_clk_counter(32 bit counter) runs on core clock.
+* It's synchronized to crab_clk (cpu_crab_clk) which runs at
+* freq of cluster. Assuming max cluster clock ~2000MHz,
+* It will take = 2 ^ 32 / 2000 MHz to overflow core clk counter
+*  = ~2.147 sec to overflow
+*/
+   read_counters_work = container_of(work, struct read_counters_work,
+ work);
+   c = _counters_work->c;
+
+   val = read_freq_feedback();
+