ARM: tegra: Make CPU thermal throttling configurable
authorTodd Poynor <toddpoynor@google.com>
Sat, 18 Dec 2010 03:36:23 +0000 (19:36 -0800)
committerTodd Poynor <toddpoynor@google.com>
Tue, 21 Dec 2010 03:24:27 +0000 (19:24 -0800)
Based on work by Dmitriy Gruzman and Varun Wadekar.

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

index d351811d955130a5673cfacadbd6a77a444b9fda..983297dfa25e9b6bb273bac82f68007aa62653e4 100644 (file)
@@ -96,3 +96,10 @@ config TEGRA_IOVMM
 
 config TEGRA_ARB_SEMAPHORE
        bool
+
+config TEGRA_THERMAL_THROTTLE
+       bool "Enable throttling of CPU speed on overtemp"
+       depends on CPU_FREQ
+       default y
+       help
+         Also requires enabling a temperature sensor such as NCT1008.
index d8c103e8964c51070ae78889e91d190a7ee1a39e..eddd1b7fabf462145ed834bcbff03d0db66e70df 100644 (file)
@@ -50,27 +50,146 @@ static struct cpufreq_frequency_table freq_table[] = {
        { 8, CPUFREQ_TABLE_END },
 };
 
-/* CPU frequency is gradually lowered when throttling is enabled */
-#define THROTTLE_START_INDEX   2
-#define THROTTLE_END_INDEX     6
-#define THROTTLE_DELAY         msecs_to_jiffies(2000)
-#define NO_DELAY               msecs_to_jiffies(0)
-
 #define NUM_CPUS       2
 
 static struct clk *cpu_clk;
 static struct clk *emc_clk;
 
-static struct workqueue_struct *workqueue;
-
 static unsigned long target_cpu_speed[NUM_CPUS];
 static DEFINE_MUTEX(tegra_cpu_lock);
 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
+
+#ifdef CONFIG_TEGRA_THERMAL_THROTTLE
+#define THROTTLE_DELAY         msecs_to_jiffies(2000)
+#define NO_DELAY               msecs_to_jiffies(0)
+
 static DEFINE_MUTEX(throttling_lock);
 static bool is_throttling;
 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;
+
+       mutex_lock(&tegra_cpu_lock);
+
+       if (tegra_throttling_needed(&rate) && tegra_update_cpu_speed(rate) == 0) {
+               queue_delayed_work(workqueue, &throttle_work, THROTTLE_DELAY);
+       }
+
+       mutex_unlock(&tegra_cpu_lock);
+}
+
+/**
+ * tegra_throttling_enable
+ * This function may sleep
+ */
+void tegra_throttling_enable(void)
+{
+       mutex_lock(&throttling_lock);
+
+       if (!is_throttling) {
+               is_throttling = true;
+               queue_delayed_work(workqueue, &throttle_work, NO_DELAY);
+       }
+
+       mutex_unlock(&throttling_lock);
+}
+EXPORT_SYMBOL_GPL(tegra_throttling_enable);
+
+/**
+ * tegra_throttling_disable
+ * This function may sleep
+ */
+void tegra_throttling_disable(void)
+{
+       mutex_lock(&throttling_lock);
+
+       if (is_throttling) {
+               cancel_delayed_work_sync(&throttle_work);
+               is_throttling = false;
+       }
 
+       mutex_unlock(&throttling_lock);
+}
+EXPORT_SYMBOL_GPL(tegra_throttling_disable);
+
+#ifdef CONFIG_DEBUG_FS
+static int throttle_debug_set(void *data, u64 val)
+{
+       if (val) {
+               tegra_throttling_enable();
+       } else {
+               tegra_throttling_disable();
+       }
+
+       return 0;
+}
+static int throttle_debug_get(void *data, u64 *val)
+{
+       *val = (u64) is_throttling;
+       return 0;
+}
+
+DEFINE_SIMPLE_ATTRIBUTE(throttle_fops, throttle_debug_get, throttle_debug_set, "%llu\n");
+
+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 (!debugfs_create_file("throttle", 0644, cpu_tegra_debugfs_root, NULL, &throttle_fops))
+               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 */
+
+#else /* CONFIG_TEGRA_THERMAL_THROTTLE */
+#define tegra_cpu_is_throttling() (0)
+#endif /* CONFIG_TEGRA_THERMAL_THROTTLE */
 
 int tegra_verify_speed(struct cpufreq_policy *policy)
 {
@@ -169,8 +288,7 @@ static int tegra_target(struct cpufreq_policy *policy,
 {
        int idx;
        unsigned int freq;
-       unsigned int highest_speed;
-       unsigned int limit_when_throttling;
+       unsigned int new_speed;
        int ret = 0;
 
        mutex_lock(&tegra_cpu_lock);
@@ -187,136 +305,30 @@ static int tegra_target(struct cpufreq_policy *policy,
 
        target_cpu_speed[policy->cpu] = freq;
 
-       highest_speed = tegra_cpu_highest_speed();
+       new_speed = tegra_cpu_highest_speed();
 
        /* Do not go above this frequency when throttling */
-       limit_when_throttling = freq_table[THROTTLE_START_INDEX].frequency;
-
-       if (is_throttling && highest_speed > limit_when_throttling) {
-               if (tegra_getspeed(0) < limit_when_throttling) {
-                       ret = tegra_update_cpu_speed(limit_when_throttling);
-                       goto out;
-               } else {
-                       ret = -EBUSY;
-                       goto out;
+
+       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;
+                       }
                }
        }
 
-       ret = tegra_update_cpu_speed(highest_speed);
+       ret = tegra_update_cpu_speed(new_speed);
 out:
        mutex_unlock(&tegra_cpu_lock);
        return ret;
 }
 
-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;
-
-       mutex_lock(&tegra_cpu_lock);
-
-       if (tegra_throttling_needed(&rate) && tegra_update_cpu_speed(rate) == 0) {
-               queue_delayed_work(workqueue, &throttle_work, THROTTLE_DELAY);
-       }
-
-       mutex_unlock(&tegra_cpu_lock);
-}
-
-/**
- * tegra_throttling_enable
- * This functions may sleep
- */
-void tegra_throttling_enable(void)
-{
-       mutex_lock(&throttling_lock);
-
-       if (!is_throttling) {
-               is_throttling = true;
-               queue_delayed_work(workqueue, &throttle_work, NO_DELAY);
-       }
-
-       mutex_unlock(&throttling_lock);
-}
-EXPORT_SYMBOL_GPL(tegra_throttling_enable);
-
-/**
- * tegra_throttling_disable
- * This functions may sleep
- */
-void tegra_throttling_disable(void)
-{
-       mutex_lock(&throttling_lock);
-
-       if (is_throttling) {
-               cancel_delayed_work_sync(&throttle_work);
-               is_throttling = false;
-       }
-
-       mutex_unlock(&throttling_lock);
-}
-EXPORT_SYMBOL_GPL(tegra_throttling_disable);
-
-#ifdef CONFIG_DEBUG_FS
-static int throttle_debug_set(void *data, u64 val)
-{
-       if (val) {
-               tegra_throttling_enable();
-       } else {
-               tegra_throttling_disable();
-       }
-
-       return 0;
-}
-static int throttle_debug_get(void *data, u64 *val)
-{
-       *val = (u64) is_throttling;
-       return 0;
-}
-
-DEFINE_SIMPLE_ATTRIBUTE(throttle_fops, throttle_debug_get, throttle_debug_set, "%llu\n");
-
-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 (!debugfs_create_file("throttle", 0644, cpu_tegra_debugfs_root, NULL, &throttle_fops))
-               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
 
 static int tegra_pm_notify(struct notifier_block *nb, unsigned long event,
        void *dummy)
@@ -367,7 +379,6 @@ static int tegra_cpu_init(struct cpufreq_policy *policy)
        cpumask_copy(policy->related_cpus, cpu_possible_mask);
 
        if (policy->cpu == 0) {
-               INIT_DELAYED_WORK(&throttle_work, tegra_throttle_work_func);
                register_pm_notifier(&tegra_cpu_pm_notifier);
        }
 
@@ -400,15 +411,20 @@ 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");
        if (!workqueue)
                return -ENOMEM;
+       INIT_DELAYED_WORK(&throttle_work, tegra_throttle_work_func);
+#endif
        return cpufreq_register_driver(&tegra_cpufreq_driver);
 }
 
 static void __exit tegra_cpufreq_exit(void)
 {
+#ifdef CONFIG_TEGRA_THERMAL_THROTTLE
        destroy_workqueue(workqueue);
+#endif
         cpufreq_unregister_driver(&tegra_cpufreq_driver);
 }