clocks: tegra12: Use static CPU-EMC co-relation
Somdutta Roy [Thu, 15 Sep 2016 19:13:43 +0000 (12:13 -0700)]
Using the actmon for handling the CPU - EMC frequency
scaling gave rise to higher AP+DRAM power and issues like
glitches in causal use cases and audio/video playback
Also the existing EMC frequency ranges were not evenly
distributed across corresponding CPU frequencies, thus
giving rise to sharp jumps in EMC frequencies at certain
CPU frequency levels. This patch addresses the above by

- Disabling mon_cpu.emc for T124 actmon.
- Enabling the cpu.emc clock.
- Using DT for static CPU-EMC freq
  corelation.
- Tweaking the lower range of the  CPU - EMC frequency
  corelation after ensuring no casual use cases get hit

Bug 1799917

Change-Id: I94238eeecee603cb6abe7f98917666119eb2fbfc

Signed-off-by: Somdutta Roy <somduttar@nvidia.com>
Change-Id: Ib10880777c68d0332a62c1b118415f0aff987ec5
Reviewed-on: http://git-master/r/1222372
Reviewed-by: Aleksandr Frid <afrid@nvidia.com>
GVS: Gerrit_Virtual_Submit
Reviewed-by: Sai Gurrappadi <sgurrappadi@nvidia.com>
Reviewed-by: Wen Yi <wyi@nvidia.com>
Reviewed-by: Steve Rogers <srogers@nvidia.com>

arch/arm/boot/dts/tegra124-platforms/tegra124-tn8-cpufreq.dtsi [new file with mode: 0644]
arch/arm/boot/dts/tegra124-tn8.dtsi
drivers/platform/tegra/cpu-tegra12.c
drivers/platform/tegra/tegra12_clocks.c
include/linux/platform/tegra/cpu-tegra.h

diff --git a/arch/arm/boot/dts/tegra124-platforms/tegra124-tn8-cpufreq.dtsi b/arch/arm/boot/dts/tegra124-platforms/tegra124-tn8-cpufreq.dtsi
new file mode 100644 (file)
index 0000000..5c37957
--- /dev/null
@@ -0,0 +1,34 @@
+/*
+ * Copyright (c) 2015 NVIDIA CORPORATION. All rights reserved.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+/ {
+       cpufreq {
+               compatible = "nvidia,tegra124-cpufreq";
+               emc-scaling-data {
+                       emc-cpu-limit-table = <
+                               /* CPU    EMC */
+                               51000   12750
+                               312000  50000
+                               564000  102000
+                               696000  204000
+                               828000  300000
+                               960000  396000
+                               1044000 528000
+                               1224000 600000
+                               1326000 792000
+                               1734000 924000
+                               >;
+               };
+       };
+};
index 973c097..8b9b022 100644 (file)
@@ -20,6 +20,7 @@
 
 #include "tegra124-soc-shield.dtsi"
 #include "tegra124-platforms/tegra124-modem-common.dtsi"
+#include "tegra124-platforms/tegra124-tn8-cpufreq.dtsi"
 
 / {
        serial@70006000 {
index 600ca69..cbb4df8 100644 (file)
@@ -39,6 +39,8 @@
 static struct cpufreq_frequency_table freq_table[CPU_FREQ_TABLE_MAX_SIZE];
 static struct tegra_cpufreq_table_data freq_table_data;
 
+static DEFINE_MUTEX(scaling_data_lock);
+
 #ifndef CONFIG_ARCH_TEGRA_13x_SOC
 struct tegra_cpufreq_table_data *tegra_cpufreq_table_get(void)
 {
@@ -250,6 +252,120 @@ unsigned long tegra_emc_to_cpu_ratio(unsigned long cpu_rate)
                return 0;               /* emc min */
 }
 
+static struct device_node *of_get_scaling_node(const char *name)
+{
+       struct device_node *scaling_np = NULL;
+       struct device_node *np =
+               of_find_compatible_node(NULL, NULL, "nvidia,tegra124-cpufreq");
+
+       if (!np || !of_device_is_available(np)) {
+               pr_debug("%s: Tegra124 cpufreq node is not found\n", __func__);
+               of_node_put(np);
+               return NULL;
+       }
+
+       scaling_np = of_get_child_by_name(np, name);
+       of_node_put(np);
+       if (!scaling_np || !of_device_is_available(scaling_np)) {
+               pr_debug("%s: %s for cpufreq is not found\n", __func__, name);
+               of_node_put(scaling_np);
+               return NULL;
+       }
+       return scaling_np;
+}
+
+/*
+ * Vote on memory bus frequency based on cpu frequency.
+ * input cpu rate is in kHz
+ * output emc rate is in Hz
+ */
+static unsigned long emc_max_rate;
+static u32 *emc_cpu_table;
+static int cpu_emc_table_src = CPU_EMC_TABLE_SRC_DT;
+static int emc_cpu_table_size;
+
+static u32 *cpufreq_emc_table_get(int *table_size)
+{
+       int freqs_num;
+       u32 *freqs = NULL;
+       struct device_node *np = NULL;
+       const char *propname = "emc-cpu-limit-table";
+
+       /* Find cpufreq node */
+       np = of_get_scaling_node("emc-scaling-data");
+       if (!np){
+               return ERR_PTR(-ENODATA);
+       }
+
+       /* Read frequency table */
+       if (!of_find_property(np, propname, &freqs_num)) {
+               pr_err("%s: %s is not found\n", __func__, propname);
+               goto out;
+       }
+
+       /* must have even entries */
+       if (!freqs_num || (freqs_num % (sizeof(*freqs) * 2))) {
+               pr_err("%s: invalid %s size %d\n", __func__, propname,
+                               freqs_num);
+               goto out;
+       }
+
+       freqs = kzalloc(freqs_num, GFP_KERNEL);
+       if (!freqs) {
+               pr_err("%s: failed to allocate limit table\n", __func__);
+               goto out;
+       }
+
+       freqs_num /= sizeof(*freqs);
+       if (of_property_read_u32_array(np, propname, freqs, freqs_num)) {
+               pr_err("%s: failed to read %s\n", __func__, propname);
+               goto out;
+       }
+
+       of_node_put(np);
+       *table_size = freqs_num;
+       return freqs;
+
+out:
+       kfree(freqs);
+       of_node_put(np);
+       return ERR_PTR(-ENODATA);
+}
+
+static unsigned long dt_emc_cpu_limit(unsigned long cpu_rate,
+               unsigned long emc_max_rate)
+{
+       int i;
+
+       for (i = 0; i < emc_cpu_table_size; i += 2) {
+               if (cpu_rate < emc_cpu_table[i])
+                       break;
+       }
+
+       if (i)
+               return min(emc_max_rate, emc_cpu_table[i-1] * 1000UL);
+       return 0;
+}
+
+static unsigned long default_emc_cpu_limit(unsigned long cpu_rate,
+               unsigned long emc_max_rate)
+{
+       /* Vote on memory bus frequency based on cpu frequency;
+          cpu rate is in kHz, emc rate is in Hz */
+       if (cpu_rate >= 1300000)
+               return emc_max_rate;    /* cpu >= 1.3GHz, emc max */
+       else if (cpu_rate >= 975000)
+               return 550000000;   /* cpu >= 975 MHz, emc 550 MHz */
+       else if (cpu_rate >= 725000)
+               return  350000000;  /* cpu >= 725 MHz, emc 350 MHz */
+       else if (cpu_rate >= 500000)
+               return  150000000;  /* cpu >= 500 MHz, emc 150 MHz */
+       else if (cpu_rate >= 275000)
+               return  50000000;   /* cpu >= 275 MHz, emc 50 MHz */
+       else
+               return 0;       /* emc min */
+}
+
 #ifdef CONFIG_ARCH_TEGRA_13x_SOC
 /* EMC/CPU frequency operational requirement limit */
 unsigned long tegra_emc_cpu_limit(unsigned long cpu_rate)
@@ -276,6 +392,34 @@ unsigned long tegra_emc_cpu_limit(unsigned long cpu_rate)
        last_emc_rate = emc_rate;
        return emc_rate;
 }
+#else
+unsigned long tegra_emc_cpu_limit(unsigned long cpu_rate)
+{
+       static unsigned long emc_rate;
+
+       if (emc_max_rate == 0) {
+               struct clk *emc = tegra_get_clock_by_name("emc");
+               if (!emc)
+                       return -ENODEV;
+               emc_max_rate = clk_round_rate(emc, ULONG_MAX);
+       }
+
+       mutex_lock(&scaling_data_lock);
+       if (!emc_cpu_table)
+               emc_cpu_table =
+                       cpufreq_emc_table_get(&emc_cpu_table_size);
+
+       if ((cpu_emc_table_src == CPU_EMC_TABLE_SRC_DEFAULT) ||
+                       IS_ERR(emc_cpu_table))
+               emc_rate =
+                       default_emc_cpu_limit(cpu_rate, emc_max_rate);
+       else
+               emc_rate = dt_emc_cpu_limit(cpu_rate, emc_max_rate);
+
+       mutex_unlock(&scaling_data_lock);
+
+       return emc_rate;
+}
 #endif
 
 int tegra_update_mselect_rate(unsigned long cpu_rate)
@@ -297,3 +441,18 @@ int tegra_update_mselect_rate(unsigned long cpu_rate)
        mselect_rate = min(mselect_rate, 102000000UL);
        return clk_set_rate(mselect, mselect_rate);
 }
+
+int set_cpu_emc_limit_table_source(int table_src)
+{
+       if (table_src != CPU_EMC_TABLE_SRC_DT &&
+                       table_src != CPU_EMC_TABLE_SRC_DEFAULT)
+               return -1;
+       cpu_emc_table_src = table_src;
+
+       return 0;
+}
+
+int get_cpu_emc_limit_table_source(void)
+{
+       return cpu_emc_table_src;
+}
index 03d4efd..332e5a0 100644 (file)
@@ -8408,10 +8408,10 @@ struct clk tegra_list_clks[] = {
        SHARED_SCLK("sbc6.sclk", "7000de00.spi",        "sclk", &tegra_clk_apb,        NULL, 0, 0),
 
        SHARED_EMC_CLK("avp.emc",       "nvavp",        "emc",  &tegra_clk_emc, NULL, 0, 0, 0),
+#ifndef CONFIG_ARCH_TEGRA_12x_SOC
        SHARED_EMC_CLK("mon_cpu.emc",   "tegra_mon", "cpu_emc", &tegra_clk_emc, NULL, 0, 0, 0),
-#ifdef CONFIG_ARCH_TEGRA_13x_SOC
-       SHARED_EMC_CLK("cpu.emc",       "tegra-cpu", "cpu_emc", &tegra_clk_emc, NULL, 0, 0, 0),
 #endif
+       SHARED_EMC_CLK("cpu.emc",       "tegra-cpu", "cpu_emc", &tegra_clk_emc, NULL, 0, 0, 0),
        SHARED_EMC_CLK("disp1.emc",     "tegradc.0",    "emc",  &tegra_clk_emc, NULL, 0, SHARED_ISO_BW, BIT(EMC_USER_DC1)),
        SHARED_EMC_CLK("disp2.emc",     "tegradc.1",    "emc",  &tegra_clk_emc, NULL, 0, SHARED_ISO_BW, BIT(EMC_USER_DC2)),
        SHARED_EMC_CLK("disp1.la.emc",  "tegradc.0",    "emc.la",       &tegra_clk_emc, NULL, 0, 0, 0),
index 40c3cf0..99f902e 100644 (file)
@@ -73,7 +73,7 @@ static inline int tegra_update_mselect_rate(unsigned long cpu_rate)
 #else
 int tegra_update_mselect_rate(unsigned long cpu_rate);
 #endif
-#if defined(CONFIG_ARCH_TEGRA_13x_SOC) || defined(CONFIG_ARCH_TEGRA_21x_SOC)
+#if defined(CONFIG_ARCH_TEGRA_13x_SOC) || defined(CONFIG_ARCH_TEGRA_21x_SOC) || defined(CONFIG_ARCH_TEGRA_12x_SOC)
 unsigned long tegra_emc_cpu_limit(unsigned long cpu_rate);
 int set_cpu_emc_limit_table_source(int table_source);
 int get_cpu_emc_limit_table_source(void);