ARM: tegra: power: Limit CPU rate on system EDP alarm
Alex Frid [Thu, 6 Oct 2011 04:20:21 +0000 (21:20 -0700)]
System electrical design point (EDP) alarm is generated when system
power source (battery) over-current is detected.

Part of the system EDP management is CPU frequency capping added by
this commit. Maximum CPU clock frequency is pre-determined depending
on number of CPU cores on-line. It is combined with CPU regulator EDP
limit and applied to final CPU rate; CPU voltage is scaled down by
DVFS, respectively. The system EDP limit of CPU rate is removed after
alarm is canceled.

EDP event can be emulated via debugfs entry /d/cpu-tegra/edp_alarm.

(cherry picked from commit fa673d27766ff9513139e94a498e4c24827d7c57)

arm: tegra: power: Removed erroneous ';'

(cherry picked from commit b4b404381b2d1823b7c127858950f853428fe3b5)

Change-Id: I60ec0e87f9442b698a8824895aac0a1f955565b4
Signed-off-by: Alex Frid <afrid@nvidia.com>
Reviewed-on: http://git-master/r/67823
Reviewed-by: Simone Willett <swillett@nvidia.com>
Tested-by: Simone Willett <swillett@nvidia.com>

Rebase-Id: R6a004bea8dfc99cd965f94035481907007bd1e32

arch/arm/mach-tegra/cpu-tegra.c
arch/arm/mach-tegra/edp.c
arch/arm/mach-tegra/include/mach/edp.h

index 52551b8..96179f0 100644 (file)
@@ -136,43 +136,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;
@@ -182,22 +155,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;
@@ -210,7 +190,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;
@@ -254,6 +234,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;
@@ -264,10 +264,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));
 }
 
@@ -281,8 +281,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);
@@ -332,9 +332,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;
@@ -356,19 +357,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 */
+
 static int tegra_verify_speed(struct cpufreq_policy *policy)
 {
        return cpufreq_frequency_table_verify(policy, freq_table);
index 7b0c02b..b8d6048 100644 (file)
@@ -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;
index 92d2e4f..48321ca 100644 (file)
@@ -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