ARM: tegra: cpufreq thermal throttling cleanups
authorTodd Poynor <toddpoynor@google.com>
Tue, 21 Dec 2010 19:12:31 +0000 (11:12 -0800)
committerTodd Poynor <toddpoynor@google.com>
Tue, 4 Jan 2011 19:39:17 +0000 (11:39 -0800)
Various review feedback, including:

Keep a global throttling index that specifies a ceiling CPU speed, lowered by one
at each delay interval (while the temperature alarm continues to be signalled).
Avoid lowering the throttle too far based on a transitory lowering of speed
requested by the governor.

Restore governor-requested speed when throttling turned off.

Add cpufreq sysfs attribute for checking throttling state.

Make throttling workqueue high-priority.

Cosmetic changes.

Change-Id: I068bf32115927fa61282f17f4a8798f2aee0b530
Signed-off-by: Todd Poynor <toddpoynor@google.com>
arch/arm/mach-tegra/cpu-tegra.c

index a878545d2740a6d29d6561d6d8e624457076e3f8..2d629461a9b8aa37911de7d5395f23921453395d 100644 (file)
 #include <mach/hardware.h>
 #include <mach/clk.h>
 
-/* Frequency table index must be sequential starting at 0 and frequencies must be ascending*/
+/*
+ * Frequency table index must be sequential starting at 0 and frequencies
+ * must be ascending.
+ */
 static struct cpufreq_frequency_table freq_table[] = {
        { 0, 216000 },
        { 1, 312000 },
@@ -61,44 +64,35 @@ static bool is_suspended;
 
 unsigned int tegra_getspeed(unsigned int cpu);
 static int tegra_update_cpu_speed(unsigned long rate);
-
-/* CPU frequency is gradually lowered when throttling is enabled */
-#define THROTTLE_START_INDEX   2
-#define THROTTLE_END_INDEX     6
+static unsigned long tegra_cpu_highest_speed(void);
 
 #ifdef CONFIG_TEGRA_THERMAL_THROTTLE
+/* CPU frequency is gradually lowered when throttling is enabled */
+#define THROTTLE_LOWEST_INDEX  2 /* 456000 */
+#define THROTTLE_HIGHEST_INDEX 6 /* 912000 */
 #define THROTTLE_DELAY         msecs_to_jiffies(2000)
-#define NO_DELAY               msecs_to_jiffies(0)
 
-static DEFINE_MUTEX(throttling_lock);
 static bool is_throttling;
+static int throttle_index;
+static int throttle_next_index;
 static struct delayed_work throttle_work;
 static struct workqueue_struct *workqueue;
 
 #define tegra_cpu_is_throttling() (is_throttling)
 
-static bool tegra_throttling_needed(unsigned long *rate)
-{
-       unsigned int current_freq = tegra_getspeed(0);
-       int i;
-
-       for (i = THROTTLE_END_INDEX; i >= THROTTLE_START_INDEX; i--) {
-               if (freq_table[i].frequency < current_freq) {
-                       *rate = freq_table[i].frequency;
-                       return true;
-               }
-       }
-
-       return false;
-}
-
 static void tegra_throttle_work_func(struct work_struct *work)
 {
-       unsigned long rate;
+       unsigned int current_freq;
 
        mutex_lock(&tegra_cpu_lock);
+       current_freq = tegra_getspeed(0);
+       throttle_index = throttle_next_index;
+
+       if (freq_table[throttle_index].frequency < current_freq)
+               tegra_update_cpu_speed(freq_table[throttle_index].frequency);
 
-       if (tegra_throttling_needed(&rate) && tegra_update_cpu_speed(rate) == 0) {
+       if (throttle_index > THROTTLE_LOWEST_INDEX) {
+               throttle_next_index = throttle_index - 1;
                queue_delayed_work(workqueue, &throttle_work, THROTTLE_DELAY);
        }
 
@@ -111,20 +105,48 @@ static void tegra_throttle_work_func(struct work_struct *work)
  */
 void tegra_throttling_enable(bool enable)
 {
-       mutex_lock(&throttling_lock);
+       mutex_lock(&tegra_cpu_lock);
 
        if (enable && !is_throttling) {
+               unsigned int current_freq = tegra_getspeed(0);
+
                is_throttling = true;
-               queue_delayed_work(workqueue, &throttle_work, NO_DELAY);
+
+               for (throttle_index = THROTTLE_HIGHEST_INDEX;
+                    throttle_index >= THROTTLE_LOWEST_INDEX;
+                    throttle_index--)
+                       if (freq_table[throttle_index].frequency
+                           < current_freq)
+                               break;
+
+               throttle_index = max(throttle_index, THROTTLE_LOWEST_INDEX);
+               throttle_next_index = throttle_index;
+               queue_delayed_work(workqueue, &throttle_work, 0);
        } else if (!enable && is_throttling) {
                cancel_delayed_work_sync(&throttle_work);
                is_throttling = false;
+               /* restore speed requested by governor */
+               tegra_update_cpu_speed(tegra_cpu_highest_speed());
        }
 
-       mutex_unlock(&throttling_lock);
+       mutex_unlock(&tegra_cpu_lock);
 }
 EXPORT_SYMBOL_GPL(tegra_throttling_enable);
 
+static unsigned int throttle_governor_speed(unsigned int requested_speed)
+{
+       return tegra_cpu_is_throttling() ?
+               min(requested_speed, freq_table[throttle_index].frequency) :
+               requested_speed;
+}
+
+static ssize_t show_throttle(struct cpufreq_policy *policy, char *buf)
+{
+       return sprintf(buf, "%u\n", is_throttling);
+}
+
+cpufreq_freq_attr_ro(throttle);
+
 #ifdef CONFIG_DEBUG_FS
 static int throttle_debug_set(void *data, u64 val)
 {
@@ -170,6 +192,7 @@ module_exit(tegra_cpu_debug_exit);
 
 #else /* CONFIG_TEGRA_THERMAL_THROTTLE */
 #define tegra_cpu_is_throttling() (0)
+#define throttle_governor_speed(requested_speed) (requested_speed)
 
 void tegra_throttling_enable(bool enable)
 {
@@ -289,25 +312,7 @@ static int tegra_target(struct cpufreq_policy *policy,
        freq = freq_table[idx].frequency;
 
        target_cpu_speed[policy->cpu] = freq;
-
-       new_speed = tegra_cpu_highest_speed();
-
-       /* Do not go above this frequency when throttling */
-
-       if (tegra_cpu_is_throttling()) {
-               unsigned int throttle_limit =
-                       freq_table[THROTTLE_START_INDEX].frequency;
-
-               if (new_speed > throttle_limit) {
-                       if (tegra_getspeed(0) < throttle_limit) {
-                               new_speed = throttle_limit;
-                       } else {
-                               ret = -EBUSY;
-                               goto out;
-                       }
-               }
-       }
-
+       new_speed = throttle_governor_speed(tegra_cpu_highest_speed());
        ret = tegra_update_cpu_speed(new_speed);
 out:
        mutex_unlock(&tegra_cpu_lock);
@@ -381,6 +386,9 @@ static int tegra_cpu_exit(struct cpufreq_policy *policy)
 
 static struct freq_attr *tegra_cpufreq_attr[] = {
        &cpufreq_freq_attr_scaling_available_freqs,
+#ifdef CONFIG_TEGRA_THERMAL_THROTTLE
+       &throttle,
+#endif
        NULL,
 };
 
@@ -397,7 +405,13 @@ static struct cpufreq_driver tegra_cpufreq_driver = {
 static int __init tegra_cpufreq_init(void)
 {
 #ifdef CONFIG_TEGRA_THERMAL_THROTTLE
-       workqueue = create_singlethread_workqueue("cpu-tegra");
+       /*
+        * High-priority, others flags default: not bound to a specific
+        * CPU, has rescue worker task (in case of allocation deadlock,
+        * etc.).  Single-threaded.
+        */
+       workqueue = alloc_workqueue("cpu-tegra",
+                                   WQ_HIGHPRI | WQ_UNBOUND | WQ_RESCUER, 1);
        if (!workqueue)
                return -ENOMEM;
        INIT_DELAYED_WORK(&throttle_work, tegra_throttle_work_func);