diff options
-rw-r--r-- | arch/arm/mach-tegra/cpu-tegra.c | 164 | ||||
-rw-r--r-- | arch/arm/mach-tegra/edp.c | 102 | ||||
-rw-r--r-- | arch/arm/mach-tegra/include/mach/edp.h | 14 |
3 files changed, 215 insertions, 65 deletions
diff --git a/arch/arm/mach-tegra/cpu-tegra.c b/arch/arm/mach-tegra/cpu-tegra.c index d6793f87cc5f..5599c298ac5b 100644 --- a/arch/arm/mach-tegra/cpu-tegra.c +++ b/arch/arm/mach-tegra/cpu-tegra.c @@ -139,43 +139,16 @@ static ssize_t show_throttle(struct cpufreq_policy *policy, char *buf) } cpufreq_freq_attr_ro(throttle); - -#ifdef CONFIG_DEBUG_FS - -static struct dentry *cpu_tegra_debugfs_root; - -static int __init tegra_cpu_debug_init(void) -{ - cpu_tegra_debugfs_root = debugfs_create_dir("cpu-tegra", 0); - - if (!cpu_tegra_debugfs_root) - return -ENOMEM; - - if (tegra_throttle_debug_init(cpu_tegra_debugfs_root)) - goto err_out; - - return 0; - -err_out: - debugfs_remove_recursive(cpu_tegra_debugfs_root); - return -ENOMEM; - -} - -static void __exit tegra_cpu_debug_exit(void) -{ - debugfs_remove_recursive(cpu_tegra_debugfs_root); -} - -late_initcall(tegra_cpu_debug_init); -module_exit(tegra_cpu_debug_exit); -#endif /* CONFIG_DEBUG_FS */ #endif /* CONFIG_TEGRA_THERMAL_THROTTLE */ #ifdef CONFIG_TEGRA_EDP_LIMITS static const struct tegra_edp_limits *cpu_edp_limits; static int cpu_edp_limits_size; + +static const unsigned int *system_edp_limits; +static bool system_edp_alarm; + static int edp_thermal_index; static cpumask_t edp_cpumask; static unsigned int edp_limit; @@ -185,22 +158,29 @@ unsigned int tegra_get_edp_limit(void) return edp_limit; } -static void edp_update_limit(void) +static unsigned int edp_predict_limit(unsigned int cpus) { - unsigned int limit = cpumask_weight(&edp_cpumask); -#ifndef CONFIG_TEGRA_EDP_EXACT_FREQ - int i; -#endif + unsigned int limit = 0; - if (!cpu_edp_limits) - return; + BUG_ON(cpus == 0); + if (cpu_edp_limits) { + BUG_ON(edp_thermal_index >= cpu_edp_limits_size); + limit = cpu_edp_limits[edp_thermal_index].freq_limits[cpus - 1]; + } + if (system_edp_limits && system_edp_alarm) + limit = min(limit, system_edp_limits[cpus - 1]); + + return limit; +} + +static void edp_update_limit(void) +{ + unsigned int limit = edp_predict_limit(cpumask_weight(&edp_cpumask)); - BUG_ON((edp_thermal_index >= cpu_edp_limits_size) || (limit == 0)); #ifdef CONFIG_TEGRA_EDP_EXACT_FREQ - edp_limit = cpu_edp_limits[edp_thermal_index].freq_limits[limit - 1]; + edp_limit = limit; #else - limit = cpu_edp_limits[edp_thermal_index].freq_limits[limit - 1]; - + unsigned int i; for (i = 0; freq_table[i].frequency != CPUFREQ_TABLE_END; i++) { if (freq_table[i].frequency > limit) { break; @@ -213,7 +193,7 @@ static void edp_update_limit(void) static unsigned int edp_governor_speed(unsigned int requested_speed) { - if ((!cpu_edp_limits) || (requested_speed <= edp_limit)) + if ((!edp_limit) || (requested_speed <= edp_limit)) return requested_speed; else return edp_limit; @@ -257,6 +237,26 @@ int tegra_edp_update_thermal_zone(int temperature) } EXPORT_SYMBOL_GPL(tegra_edp_update_thermal_zone); +int tegra_system_edp_alarm(bool alarm) +{ + int ret = -ENODEV; + + mutex_lock(&tegra_cpu_lock); + system_edp_alarm = alarm; + + /* Update cpu rate if cpufreq (at least on cpu0) is already started + and cancel emergency throttling after edp limit is applied */ + if (target_cpu_speed[0]) { + edp_update_limit(); + ret = tegra_cpu_set_speed_cap(NULL); + if (!ret && alarm) + tegra_edp_throttle_cpu_now(0); + } + mutex_unlock(&tegra_cpu_lock); + + return ret; +} + bool tegra_cpu_edp_favor_up(unsigned int n, int mp_overhead) { unsigned int current_limit, next_limit; @@ -267,10 +267,10 @@ bool tegra_cpu_edp_favor_up(unsigned int n, int mp_overhead) if (n >= ARRAY_SIZE(cpu_edp_limits->freq_limits)) return false; - current_limit = cpu_edp_limits[edp_thermal_index].freq_limits[n-1]; - next_limit = cpu_edp_limits[edp_thermal_index].freq_limits[n]; + current_limit = edp_predict_limit(n); + next_limit = edp_predict_limit(n + 1); - return ((next_limit * (n + 1)) > + return ((next_limit * (n + 1)) >= (current_limit * n * (100 + mp_overhead) / 100)); } @@ -284,8 +284,8 @@ bool tegra_cpu_edp_favor_down(unsigned int n, int mp_overhead) if (n > ARRAY_SIZE(cpu_edp_limits->freq_limits)) return true; - current_limit = cpu_edp_limits[edp_thermal_index].freq_limits[n-1]; - next_limit = cpu_edp_limits[edp_thermal_index].freq_limits[n-2]; + current_limit = edp_predict_limit(n); + next_limit = edp_predict_limit(n - 1); return ((next_limit * (n - 1) * (100 + mp_overhead) / 100)) > (current_limit * n); @@ -335,9 +335,10 @@ static struct notifier_block tegra_cpu_edp_notifier = { static void tegra_cpu_edp_init(bool resume) { + tegra_get_system_edp_limits(&system_edp_limits); tegra_get_cpu_edp_limits(&cpu_edp_limits, &cpu_edp_limits_size); - if (!cpu_edp_limits) { + if (!(cpu_edp_limits || system_edp_limits)) { if (!resume) pr_info("cpu-tegra: no EDP table is provided\n"); return; @@ -359,19 +360,80 @@ static void tegra_cpu_edp_init(bool resume) static void tegra_cpu_edp_exit(void) { - if (!cpu_edp_limits) + if (!(cpu_edp_limits || system_edp_limits)) return; unregister_hotcpu_notifier(&tegra_cpu_edp_notifier); } -#else /* CONFIG_TEGRA_EDP_LIMITS */ +#ifdef CONFIG_DEBUG_FS + +static int system_edp_alarm_get(void *data, u64 *val) +{ + *val = (u64)system_edp_alarm; + return 0; +} +static int system_edp_alarm_set(void *data, u64 val) +{ + if (val > 1) { /* emulate emergency throttling */ + tegra_edp_throttle_cpu_now(val); + return 0; + } + return tegra_system_edp_alarm((bool)val); +} +DEFINE_SIMPLE_ATTRIBUTE(system_edp_alarm_fops, + system_edp_alarm_get, system_edp_alarm_set, "%llu\n"); +static int __init tegra_edp_debug_init(struct dentry *cpu_tegra_debugfs_root) +{ + if (!debugfs_create_file("edp_alarm", 0644, cpu_tegra_debugfs_root, + NULL, &system_edp_alarm_fops)) + return -ENOMEM; + + return 0; +} +#endif + +#else /* CONFIG_TEGRA_EDP_LIMITS */ #define edp_governor_speed(requested_speed) (requested_speed) #define tegra_cpu_edp_init(resume) #define tegra_cpu_edp_exit() +#define tegra_edp_debug_init(cpu_tegra_debugfs_root) (0) #endif /* CONFIG_TEGRA_EDP_LIMITS */ +#ifdef CONFIG_DEBUG_FS + +static struct dentry *cpu_tegra_debugfs_root; + +static int __init tegra_cpu_debug_init(void) +{ + cpu_tegra_debugfs_root = debugfs_create_dir("cpu-tegra", 0); + + if (!cpu_tegra_debugfs_root) + return -ENOMEM; + + if (tegra_throttle_debug_init(cpu_tegra_debugfs_root)) + goto err_out; + + if (tegra_edp_debug_init(cpu_tegra_debugfs_root)) + goto err_out; + + return 0; + +err_out: + debugfs_remove_recursive(cpu_tegra_debugfs_root); + return -ENOMEM; +} + +static void __exit tegra_cpu_debug_exit(void) +{ + debugfs_remove_recursive(cpu_tegra_debugfs_root); +} + +late_initcall(tegra_cpu_debug_init); +module_exit(tegra_cpu_debug_exit); +#endif /* CONFIG_DEBUG_FS */ + int tegra_verify_speed(struct cpufreq_policy *policy) { return cpufreq_frequency_table_verify(policy, freq_table); diff --git a/arch/arm/mach-tegra/edp.c b/arch/arm/mach-tegra/edp.c index ea64cabcc71f..3808978565bd 100644 --- a/arch/arm/mach-tegra/edp.c +++ b/arch/arm/mach-tegra/edp.c @@ -29,6 +29,7 @@ static const struct tegra_edp_limits *edp_limits; static int edp_limits_size; +static const unsigned int *system_edp_limits; /* * Temperature step size cannot be less than 4C because of hysteresis @@ -166,6 +167,20 @@ static char __initdata tegra_edp_map[] = { }; +static struct system_edp_entry __initdata tegra_system_edp_map[] = { + +/* {SKU, power-limit (in 100mW), {freq limits (in 10Mhz)} } */ + + { 1, 49, {130, 120, 120, 120} }, + { 1, 44, {130, 120, 120, 110} }, + { 1, 37, {130, 120, 110, 100} }, + { 1, 35, {130, 120, 110, 90} }, + { 1, 29, {130, 120, 100, 80} }, + { 1, 27, {130, 120, 90, 80} }, + { 1, 25, {130, 110, 80, 60} }, + { 1, 21, {130, 100, 80, 40} }, +}; + /* * "Safe entry" to be used when no match for speedo_id / * regulator_cur is found; must be the last one @@ -227,18 +242,72 @@ void __init tegra_init_cpu_edp_limits(unsigned int regulator_mA) e[j].freq_limits[3] = (unsigned int)t[i+j].freq_limits[3] * 10000; } - if (edp_limits && edp_limits != edp_default_limits) + if (edp_limits != edp_default_limits) kfree(edp_limits); edp_limits = e; } + +void __init tegra_init_system_edp_limits(unsigned int power_limit_mW) +{ + int cpu_speedo_id = tegra_cpu_speedo_id(); + int i; + unsigned int *e; + struct system_edp_entry *t = + (struct system_edp_entry *)tegra_system_edp_map; + int tsize = sizeof(tegra_system_edp_map) / + sizeof(struct system_edp_entry); + + if (!power_limit_mW) { + e = NULL; + goto out; + } + + for (i = 0; i < tsize; i++) + if (t[i].speedo_id == cpu_speedo_id) + break; + + if (i >= tsize) { + e = NULL; + goto out; + } + + do { + if (t[i].power_limit_100mW <= power_limit_mW / 100) + break; + i++; + } while (i < tsize && t[i].speedo_id == cpu_speedo_id); + + if (i >= tsize || t[i].speedo_id != cpu_speedo_id) + i--; /* No low enough entry in the table, use best possible */ + + e = kmalloc(sizeof(unsigned int) * 4, GFP_KERNEL); + BUG_ON(!e); + + e[0] = (unsigned int)t[i].freq_limits[0] * 10000; + e[1] = (unsigned int)t[i].freq_limits[1] * 10000; + e[2] = (unsigned int)t[i].freq_limits[2] * 10000; + e[3] = (unsigned int)t[i].freq_limits[3] * 10000; + +out: + kfree(system_edp_limits); + + system_edp_limits = e; +} + + void tegra_get_cpu_edp_limits(const struct tegra_edp_limits **limits, int *size) { *limits = edp_limits; *size = edp_limits_size; } +void tegra_get_system_edp_limits(const unsigned int **limits) +{ + *limits = system_edp_limits; +} + #ifdef CONFIG_DEBUG_FS static int edp_limit_debugfs_show(struct seq_file *s, void *data) @@ -250,19 +319,24 @@ static int edp_limit_debugfs_show(struct seq_file *s, void *data) 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]); + + seq_printf(s, "-- CPU EDP table --\n"); + for (i = 0; i < edp_limits_size; i++) { + seq_printf(s, "%4dC: %10u %10u %10u %10u\n", + edp_limits[i].temperature, + edp_limits[i].freq_limits[0], + edp_limits[i].freq_limits[1], + edp_limits[i].freq_limits[2], + edp_limits[i].freq_limits[3]); + } + + if (system_edp_limits) { + seq_printf(s, "\n-- System EDP table --\n"); + seq_printf(s, "%10u %10u %10u %10u\n", + system_edp_limits[0], + system_edp_limits[1], + system_edp_limits[2], + system_edp_limits[3]); } return 0; diff --git a/arch/arm/mach-tegra/include/mach/edp.h b/arch/arm/mach-tegra/include/mach/edp.h index 92d2e4f3f196..48321cae4959 100644 --- a/arch/arm/mach-tegra/include/mach/edp.h +++ b/arch/arm/mach-tegra/include/mach/edp.h @@ -35,18 +35,28 @@ struct tegra_edp_limits { unsigned int freq_limits[4]; }; +struct system_edp_entry { + char speedo_id; + char power_limit_100mW; + char freq_limits[4]; +}; #ifdef CONFIG_TEGRA_EDP_LIMITS int tegra_edp_update_thermal_zone(int temperature); void tegra_init_cpu_edp_limits(unsigned int regulator_mA); +void tegra_init_system_edp_limits(unsigned int power_limit_mW); void tegra_get_cpu_edp_limits(const struct tegra_edp_limits **limits, int *size); unsigned int tegra_get_edp_limit(void); +void tegra_get_system_edp_limits(const unsigned int **limits); +int tegra_system_edp_alarm(bool alarm); #else static inline void tegra_init_cpu_edp_limits(int regulator_mA) {} +static inline void tegra_init_system_edp_limits(int power_limit_mW) +{} 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, @@ -54,6 +64,10 @@ static inline void tegra_get_cpu_edp_limits(struct tegra_edp_limits **limits, {} static inline unsigned int tegra_get_edp_limit(void) { return -1; } +static inline void tegra_get_system_edp_limits(unsigned int **limits) +{} +static inline int tegra_system_edp_alarm(bool alarm) +{ return -1; } #endif #ifdef CONFIG_ARCH_TEGRA_2x_SOC |