summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--arch/arm/mach-tegra/cpu-tegra.c164
-rw-r--r--arch/arm/mach-tegra/edp.c102
-rw-r--r--arch/arm/mach-tegra/include/mach/edp.h14
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