diff options
-rw-r--r-- | arch/arm/mach-tegra/Makefile | 1 | ||||
-rw-r--r-- | arch/arm/mach-tegra/board-cardhu-power.c | 21 | ||||
-rw-r--r-- | arch/arm/mach-tegra/board-cardhu-sensors.c | 7 | ||||
-rw-r--r-- | arch/arm/mach-tegra/cpu-tegra.c | 8 | ||||
-rw-r--r-- | arch/arm/mach-tegra/edp.c | 229 | ||||
-rw-r--r-- | arch/arm/mach-tegra/include/mach/edp.h | 26 | ||||
-rw-r--r-- | arch/arm/mach-tegra/tegra3_clocks.c | 8 |
7 files changed, 261 insertions, 39 deletions
diff --git a/arch/arm/mach-tegra/Makefile b/arch/arm/mach-tegra/Makefile index 8ffa0d924c6b..bca96608c759 100644 --- a/arch/arm/mach-tegra/Makefile +++ b/arch/arm/mach-tegra/Makefile @@ -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 diff --git a/arch/arm/mach-tegra/board-cardhu-power.c b/arch/arm/mach-tegra/board-cardhu-power.c index 725c84ff143e..b7c9ae9a128b 100644 --- a/arch/arm/mach-tegra/board-cardhu-power.c +++ b/arch/arm/mach-tegra/board-cardhu-power.c @@ -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 diff --git a/arch/arm/mach-tegra/board-cardhu-sensors.c b/arch/arm/mach-tegra/board-cardhu-sensors.c index 8e29cd48027d..6ed5a8f0cdd7 100644 --- a/arch/arm/mach-tegra/board-cardhu-sensors.c +++ b/arch/arm/mach-tegra/board-cardhu-sensors.c @@ -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; diff --git a/arch/arm/mach-tegra/cpu-tegra.c b/arch/arm/mach-tegra/cpu-tegra.c index 1eb1a29b2f58..ad8c359e2249 100644 --- a/arch/arm/mach-tegra/cpu-tegra.c +++ b/arch/arm/mach-tegra/cpu-tegra.c @@ -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 index 000000000000..9d935292841d --- /dev/null +++ b/arch/arm/mach-tegra/edp.c @@ -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 */ diff --git a/arch/arm/mach-tegra/include/mach/edp.h b/arch/arm/mach-tegra/include/mach/edp.h index 21a56ebc5a8d..1af8d9e55a95 100644 --- a/arch/arm/mach-tegra/include/mach/edp.h +++ b/arch/arm/mach-tegra/include/mach/edp.h @@ -21,20 +21,36 @@ #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 */ diff --git a/arch/arm/mach-tegra/tegra3_clocks.c b/arch/arm/mach-tegra/tegra3_clocks.c index ab5b695e9aab..77e3542fea7d 100644 --- a/arch/arm/mach-tegra/tegra3_clocks.c +++ b/arch/arm/mach-tegra/tegra3_clocks.c @@ -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 |