ARM: tegra: power: Added global EDP Capping table
Peter Boonstoppel [Fri, 15 Jul 2011 17:54:05 +0000 (10:54 -0700)]
 - Added table with EDP Capping values for different SKUs/regulator
   currents in new file edp.c
 - New entry point tegra_init_cpu_edp_limits()
 - Added DebugFS entry under debug/edp to list the currently
   selected EDP table
 - Populated EDP table in edp.c with data from Bug 844268
 - edp.c keeps main EDP table; cpu-tegra.c and board-cardhu-power.c
   both read from there

Bug 840255

Original-Change-Id: I55c2ee16278be8cd3005218bedebe76846d137d8
Reviewed-on: http://git-master/r/40938
Reviewed-by: Varun Colbert <vcolbert@nvidia.com>
Tested-by: Varun Colbert <vcolbert@nvidia.com>

Rebase-Id: R9a5f2bcfc1e6e0b5aee37cd700d75f9ef5cea30b

arch/arm/mach-tegra/Makefile
arch/arm/mach-tegra/board-cardhu-power.c
arch/arm/mach-tegra/board-cardhu-sensors.c
arch/arm/mach-tegra/cpu-tegra.c
arch/arm/mach-tegra/edp.c [new file with mode: 0644]
arch/arm/mach-tegra/include/mach/edp.h
arch/arm/mach-tegra/tegra3_clocks.c

index 8ffa0d9..bca9660 100644 (file)
@@ -40,6 +40,7 @@ obj-y                                   += dvfs.o
 obj-$(CONFIG_ARCH_TEGRA_2x_SOC)         += tegra2_dvfs.o
 obj-$(CONFIG_ARCH_TEGRA_3x_SOC)         += tegra3_dvfs.o
 obj-$(CONFIG_ARCH_TEGRA_3x_SOC)         += latency_allowance.o
+obj-$(CONFIG_TEGRA_EDP_LIMITS)          += edp.o
 endif
 ifeq ($(CONFIG_TEGRA_SILICON_PLATFORM),y)
 obj-$(CONFIG_ARCH_TEGRA_2x_SOC)         += tegra2_speedo.o
index 725c84f..b7c9ae9 100644 (file)
@@ -982,27 +982,12 @@ int __init cardhu_power_off_init(void)
 }
 
 #ifdef CONFIG_TEGRA_EDP_LIMITS
-/*
- * placeholder for now. needs to be changed with characterized data.
- * step size cannot be less than 4C
- */
-static struct tegra_edp_limits cardhu_edp_limits[] = {
-/* Temperature  1 CPU    2 CPUs   3 CPUs   4 CPUs */
-       {60,    {1400000, 1300000, 1300000, 1300000} },
-       {70,    {1400000, 1300000, 1300000, 1260000} },
-       {80,    {1400000, 1300000, 1300000, 1200000} },
-       {90,    {1400000, 1300000, 1300000, 1100000} },
-};
-
-void cardhu_thermal_zones_info(struct tegra_edp_limits **z, int *sz)
-{
-       *z = cardhu_edp_limits;
-       *sz = ARRAY_SIZE(cardhu_edp_limits);
-}
 
 int __init cardhu_edp_init(void)
 {
-       tegra_init_cpu_edp_limits(cardhu_edp_limits, ARRAY_SIZE(cardhu_edp_limits));
+       /* Temporary initalization, needs to be set to the actual
+          regulator current */
+       tegra_init_cpu_edp_limits(5000);
        return 0;
 }
 #endif
index 8e29cd4..6ed5a8f 100644 (file)
@@ -548,16 +548,13 @@ static struct i2c_board_info cardhu_i2c4_nct1008_board_info[] = {
        }
 };
 
-#ifdef CONFIG_TEGRA_EDP_LIMITS
-extern void cardhu_thermal_zones_info(struct tegra_edp_limits **, int *);
-#endif
 
 static int cardhu_nct1008_init(void)
 {
        int nct1008_port = -1;
        int ret;
 #ifdef CONFIG_TEGRA_EDP_LIMITS
-       struct tegra_edp_limits *z;
+       const struct tegra_edp_limits *z;
        int zones_sz;
        int i;
        bool throttle_ok = false;
@@ -589,7 +586,7 @@ static int cardhu_nct1008_init(void)
        }
 
 #ifdef CONFIG_TEGRA_EDP_LIMITS
-       cardhu_thermal_zones_info(&z, &zones_sz);
+       tegra_get_cpu_edp_limits(&z, &zones_sz);
        zones_sz = min(zones_sz, MAX_ZONES);
        for (i = 0; i < zones_sz; i++) {
                cardhu_nct1008_pdata.thermal_zones[i] = z[i].temperature;
index 1eb1a29..ad8c359 100644 (file)
@@ -304,6 +304,8 @@ static struct notifier_block tegra_cpu_edp_notifier = {
 
 static void tegra_cpu_edp_init(bool resume)
 {
+       tegra_get_cpu_edp_limits(&cpu_edp_limits, &cpu_edp_limits_size);
+
        if (!cpu_edp_limits) {
                if (!resume)
                        pr_info("cpu-tegra: no EDP table is provided\n");
@@ -332,12 +334,6 @@ static void tegra_cpu_edp_exit(void)
        unregister_hotcpu_notifier(&tegra_cpu_edp_notifier);
 }
 
-void tegra_init_cpu_edp_limits(const struct tegra_edp_limits *limits, int size)
-{
-       cpu_edp_limits = limits;
-       cpu_edp_limits_size = size;
-}
-
 #else  /* CONFIG_TEGRA_EDP_LIMITS */
 
 #define edp_governor_speed(requested_speed) (requested_speed)
diff --git a/arch/arm/mach-tegra/edp.c b/arch/arm/mach-tegra/edp.c
new file mode 100644 (file)
index 0000000..9d93529
--- /dev/null
@@ -0,0 +1,229 @@
+/*
+ * arch/arm/mach-tegra/edp.c
+ *
+ * Copyright (C) 2011 NVIDIA, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+ * 02111-1307, USA
+ */
+
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/seq_file.h>
+#include <linux/slab.h>
+#include <mach/edp.h>
+
+#include "fuse.h"
+
+
+static const struct tegra_edp_limits *edp_limits;
+static int edp_limits_size;
+
+
+/*
+ * Temperature step size cannot be less than 4C because of hysteresis
+ * delta
+ * Code assumes different temperatures for the same speedo_id /
+ * regulator_cur are adjacent in the table, and higest regulator_cur
+ * comes first
+ */
+static char __initdata tegra_edp_map[] = {
+       0x00, 0x3d, 0x2d, 0x8c, 0x82, 0x82, 0x82, 0x00,
+       0x3d, 0x3c, 0x8c, 0x82, 0x82, 0x82, 0x00, 0x3d,
+       0x4b, 0x8c, 0x82, 0x82, 0x82, 0x00, 0x3d, 0x5a,
+       0x8c, 0x82, 0x82, 0x82, 0x00, 0x32, 0x2d, 0x8c,
+       0x82, 0x82, 0x82, 0x00, 0x32, 0x3c, 0x8c, 0x82,
+       0x82, 0x82, 0x00, 0x32, 0x4b, 0x8c, 0x82, 0x82,
+       0x78, 0x00, 0x32, 0x5a, 0x8c, 0x82, 0x82, 0x6e,
+       0x00, 0x28, 0x2d, 0x8c, 0x82, 0x82, 0x78, 0x00,
+       0x28, 0x3c, 0x8c, 0x82, 0x82, 0x73, 0x00, 0x28,
+       0x4b, 0x8c, 0x82, 0x78, 0x6e, 0x00, 0x28, 0x5a,
+       0x8c, 0x82, 0x73, 0x5f, 0x00, 0x23, 0x2d, 0x8c,
+       0x82, 0x82, 0x6e, 0x00, 0x23, 0x3c, 0x8c, 0x82,
+       0x78, 0x69, 0x00, 0x23, 0x4b, 0x8c, 0x82, 0x73,
+       0x5f, 0x00, 0x23, 0x5a, 0x8c, 0x82, 0x69, 0x55,
+       0x01, 0x2f, 0x2d, 0x8c, 0x78, 0x78, 0x78, 0x01,
+       0x2f, 0x3c, 0x8c, 0x78, 0x78, 0x78, 0x01, 0x2f,
+       0x4b, 0x8c, 0x78, 0x78, 0x78, 0x01, 0x2f, 0x5a,
+       0x8c, 0x78, 0x78, 0x78, 0x01, 0x28, 0x2d, 0x8c,
+       0x78, 0x78, 0x78, 0x01, 0x28, 0x3c, 0x8c, 0x78,
+       0x78, 0x78, 0x01, 0x28, 0x4b, 0x8c, 0x78, 0x78,
+       0x73, 0x01, 0x28, 0x5a, 0x8c, 0x78, 0x73, 0x69,
+       0x01, 0x23, 0x2d, 0x8c, 0x78, 0x78, 0x78, 0x01,
+       0x23, 0x3c, 0x8c, 0x78, 0x78, 0x6e, 0x01, 0x23,
+       0x4b, 0x8c, 0x78, 0x78, 0x64, 0x01, 0x23, 0x5a,
+       0x8c, 0x78, 0x6e, 0x5a, 0x01, 0x1e, 0x2d, 0x8c,
+       0x78, 0x78, 0x69, 0x01, 0x1e, 0x3c, 0x8c, 0x78,
+       0x78, 0x64, 0x01, 0x1e, 0x4b, 0x8c, 0x78, 0x6e,
+       0x5a, 0x01, 0x1e, 0x5a, 0x8c, 0x78, 0x64, 0x5a,
+       0x01, 0x19, 0x2d, 0x8c, 0x78, 0x6e, 0x5a, 0x01,
+       0x19, 0x3c, 0x8c, 0x78, 0x69, 0x55, 0x01, 0x19,
+       0x4b, 0x8c, 0x78, 0x5f, 0x4b, 0x01, 0x19, 0x5a,
+       0x8c, 0x73, 0x5a, 0x3c, 0x02, 0x3d, 0x2d, 0x8c,
+       0x82, 0x82, 0x82, 0x02, 0x3d, 0x3c, 0x8c, 0x82,
+       0x82, 0x82, 0x02, 0x3d, 0x4b, 0x8c, 0x82, 0x82,
+       0x82, 0x02, 0x3d, 0x5a, 0x8c, 0x82, 0x82, 0x82,
+       0x02, 0x32, 0x2d, 0x8c, 0x82, 0x82, 0x82, 0x02,
+       0x32, 0x3c, 0x8c, 0x82, 0x82, 0x82, 0x02, 0x32,
+       0x4b, 0x8c, 0x82, 0x82, 0x78, 0x02, 0x32, 0x5a,
+       0x8c, 0x82, 0x82, 0x6e, 0x02, 0x28, 0x2d, 0x8c,
+       0x82, 0x82, 0x78, 0x02, 0x28, 0x3c, 0x8c, 0x82,
+       0x82, 0x73, 0x02, 0x28, 0x4b, 0x8c, 0x82, 0x78,
+       0x6e, 0x02, 0x28, 0x5a, 0x8c, 0x82, 0x73, 0x5f,
+       0x02, 0x23, 0x2d, 0x8c, 0x82, 0x82, 0x6e, 0x02,
+       0x23, 0x3c, 0x8c, 0x82, 0x78, 0x69, 0x02, 0x23,
+       0x4b, 0x8c, 0x82, 0x73, 0x5f, 0x02, 0x23, 0x5a,
+       0x8c, 0x82, 0x69, 0x55, 0x03, 0x3d, 0x2d, 0x8c,
+       0x82, 0x82, 0x82, 0x03, 0x3d, 0x3c, 0x8c, 0x82,
+       0x82, 0x82, 0x03, 0x3d, 0x4b, 0x8c, 0x82, 0x82,
+       0x82, 0x03, 0x3d, 0x5a, 0x8c, 0x82, 0x82, 0x82,
+       0x03, 0x32, 0x2d, 0x8c, 0x82, 0x82, 0x82, 0x03,
+       0x32, 0x3c, 0x8c, 0x82, 0x82, 0x82, 0x03, 0x32,
+       0x4b, 0x8c, 0x82, 0x82, 0x73, 0x03, 0x32, 0x5a,
+       0x8c, 0x82, 0x82, 0x6e, 0x03, 0x28, 0x2d, 0x8c,
+       0x82, 0x82, 0x78, 0x03, 0x28, 0x3c, 0x8c, 0x82,
+       0x82, 0x73, 0x03, 0x28, 0x4b, 0x8c, 0x82, 0x7d,
+       0x6e, 0x03, 0x28, 0x5a, 0x8c, 0x82, 0x73, 0x5f,
+       0x03, 0x23, 0x2d, 0x8c, 0x82, 0x82, 0x6e, 0x03,
+       0x23, 0x3c, 0x8c, 0x82, 0x78, 0x6e, 0x03, 0x23,
+       0x4b, 0x8c, 0x82, 0x78, 0x5f, 0x03, 0x23, 0x5a,
+       0x8c, 0x82, 0x6e, 0x5a,
+};
+
+
+/*
+ * "Safe entry" to be used when no match for speedo_id /
+ * regulator_cur is found; must be the last one
+ */
+static struct tegra_edp_limits edp_default_limits[] = {
+       {90, {1000000, 1000000, 1000000, 1000000} },
+};
+
+
+
+/*
+ * Specify regulator current in mA, e.g. 5000mA
+ * Use 0 for default
+ */
+void __init tegra_init_cpu_edp_limits(unsigned int regulator_mA)
+{
+       int cpu_speedo_id = tegra_cpu_speedo_id();
+       int i, j;
+       struct tegra_edp_limits *e;
+       struct tegra_edp_entry *t = (struct tegra_edp_entry *)tegra_edp_map;
+       int tsize = sizeof(tegra_edp_map)/sizeof(struct tegra_edp_entry);
+
+       if (!regulator_mA) {
+               edp_limits = edp_default_limits;
+               edp_limits_size = ARRAY_SIZE(edp_default_limits);
+               return;
+       }
+
+       for (i = 0; i < tsize; i++) {
+               if (t[i].speedo_id == cpu_speedo_id &&
+                   t[i].regulator_100mA <= regulator_mA / 100)
+                       break;
+       }
+
+       /* No entry found in tegra_edp_map */
+       if (i >= tsize) {
+               edp_limits = edp_default_limits;
+               edp_limits_size = ARRAY_SIZE(edp_default_limits);
+               return;
+       }
+
+       /* Find all rows for this entry */
+       for (j = i + 1; j < tsize; j++) {
+               if (t[i].speedo_id != t[j].speedo_id ||
+                   t[i].regulator_100mA != t[j].regulator_100mA)
+                       break;
+       }
+
+       edp_limits_size = j - i;
+       e = kmalloc(sizeof(struct tegra_edp_limits) * edp_limits_size,
+                   GFP_KERNEL);
+       BUG_ON(!e);
+
+       for (j = 0; j < edp_limits_size; j++) {
+               e[j].temperature = (int)t[i+j].temperature;
+               e[j].freq_limits[0] = (unsigned int)t[i+j].freq_limits[0] * 10000;
+               e[j].freq_limits[1] = (unsigned int)t[i+j].freq_limits[1] * 10000;
+               e[j].freq_limits[2] = (unsigned int)t[i+j].freq_limits[2] * 10000;
+               e[j].freq_limits[3] = (unsigned int)t[i+j].freq_limits[3] * 10000;
+       }
+
+       if (edp_limits && edp_limits != edp_default_limits)
+               kfree(edp_limits);
+
+       edp_limits = e;
+}
+
+void tegra_get_cpu_edp_limits(const struct tegra_edp_limits **limits, int *size)
+{
+       *limits = edp_limits;
+       *size = edp_limits_size;
+}
+
+#ifdef CONFIG_DEBUG_FS
+
+static int edp_debugfs_show(struct seq_file *s, void *data)
+{
+       int i;
+       const struct tegra_edp_limits *limits;
+       int size;
+
+       tegra_get_cpu_edp_limits(&limits, &size);
+
+       seq_printf(s, "-- EDP table --\n");
+       for (i = 0; i < size; i++) {
+               seq_printf(s, "%4dC: %10d %10d %10d %10d\n",
+                          limits[i].temperature,
+                          limits[i].freq_limits[0],
+                          limits[i].freq_limits[1],
+                          limits[i].freq_limits[2],
+                          limits[i].freq_limits[3]);
+       }
+
+       return 0;
+}
+
+
+static int edp_debugfs_open(struct inode *inode, struct file *file)
+{
+       return single_open(file, edp_debugfs_show, inode->i_private);
+}
+
+
+static const struct file_operations edp_debugfs_fops = {
+       .open           = edp_debugfs_open,
+       .read           = seq_read,
+       .llseek         = seq_lseek,
+       .release        = single_release,
+};
+
+
+static int __init tegra_edp_debugfs_init()
+{
+       struct dentry *d;
+
+       d = debugfs_create_file("edp", S_IRUGO, NULL, NULL,
+                               &edp_debugfs_fops);
+       if (!d)
+               return -ENOMEM;
+
+       return 0;
+}
+
+late_initcall(tegra_edp_debugfs_init);
+#endif /* CONFIG_DEBUG_FS */
index 21a56eb..1af8d9e 100644 (file)
 #ifndef __MACH_EDP_H
 #define __MACH_EDP_H
 
+#include <linux/debugfs.h>
+
+struct tegra_edp_entry {
+       char speedo_id;
+       char regulator_100mA;
+       char temperature;
+       char freq_limits[4];
+};
+
 struct tegra_edp_limits {
-       int     temperature;
+       int temperature;
        unsigned int freq_limits[4];
 };
 
+
 #ifdef CONFIG_TEGRA_EDP_LIMITS
+
+
 int tegra_edp_update_thermal_zone(int temperature);
-void tegra_init_cpu_edp_limits(const struct tegra_edp_limits *limits, int size);
+void tegra_init_cpu_edp_limits(unsigned int regulator_mA);
+void tegra_get_cpu_edp_limits(const struct tegra_edp_limits **limits, int *size);
+
 #else
-static inline void tegra_init_cpu_edp_limits(
-       const struct tegra_edp_limits *limits, int size)
-{ }
+static inline void tegra_init_cpu_edp_limits(int regulator_mA)
+{}
 static inline int tegra_edp_update_thermal_zone(int temperature)
 { return -1; }
+static inline void tegra_get_cpu_edp_limits(struct tegra_edp_limits **limits,
+                                           int *size)
+{}
 #endif
 
 #endif /* __MACH_EDP_H */
index ab5b695..77e3542 100644 (file)
@@ -4086,9 +4086,6 @@ struct clk *tegra_ptr_clks[] = {
        &tegra_clk_cbus,
 };
 
-static struct tegra_edp_limits default_cpu_edp_limits[] = {
-       {90, { 1000000, 1000000, 1000000, 1000000 } },
-};
 
 static void tegra3_init_one_clock(struct clk *c)
 {
@@ -4134,9 +4131,10 @@ void __init tegra_soc_init_clocks(void)
        for (i = 0; i < ARRAY_SIZE(tegra_clk_out_list); i++)
                tegra3_init_one_clock(&tegra_clk_out_list[i]);
 
-       tegra_init_cpu_edp_limits(default_cpu_edp_limits,
-                                 ARRAY_SIZE(default_cpu_edp_limits));
        emc_bridge = &tegra_clk_emc_bridge;
+
+       /* Initialize to default */
+       tegra_init_cpu_edp_limits(0);
 }
 
 #ifdef CONFIG_CPU_FREQ