rk29: cpufreq: throttle more when run 1.2G
author黄涛 <huangtao@rock-chips.com>
Wed, 14 Sep 2011 02:52:45 +0000 (10:52 +0800)
committer黄涛 <huangtao@rock-chips.com>
Wed, 14 Sep 2011 02:53:13 +0000 (10:53 +0800)
arch/arm/mach-rk29/cpufreq.c

index 6da7b65b4f46b190da36087f5e5d408b9b79b51e..bbc7bcc601073214fa081afa90acdaa2481ff5a5 100755 (executable)
@@ -1,6 +1,6 @@
-/* arch/arm/mach-rk2818/cpufreq.c
+/* arch/arm/mach-rk29/cpufreq.c
  *
- * Copyright (C) 2010 ROCKCHIP, Inc.
+ * Copyright (C) 2010, 2011 ROCKCHIP, Inc.
  *
  * This software is licensed under the terms of the GNU General Public
  * License version 2, as published by the Free Software Foundation, and
  *
  */
 
-#ifdef CONFIG_CPU_FREQ_DEBUG
-#define DEBUG
-#endif
-#define pr_fmt(fmt) "%s: " fmt, __func__
-
 #include <linux/clk.h>
 #include <linux/cpufreq.h>
 #include <linux/err.h>
@@ -32,7 +27,6 @@
 #include <../../../drivers/video/rk29_fb.h>
 
 #define MHZ    (1000*1000)
-#define KHZ    1000
 
 static int no_cpufreq_access;
 
@@ -64,13 +58,15 @@ static int vcore_uV;
 static struct workqueue_struct *wq;
 
 #ifdef CONFIG_RK29_CPU_FREQ_LIMIT_BY_TEMP
-static int limit = 1;
-module_param(limit, int, 0644);
+static bool limit = true;
+module_param(limit, bool, 0644);
 
-#define LIMIT_SECS     30
-static int limit_secs = LIMIT_SECS;
+static int limit_secs = 30;
 module_param(limit_secs, int, 0644);
 
+static int limit_secs_1200 = 6;
+module_param(limit_secs_1200, int, 0644);
+
 static int limit_temp;
 module_param(limit_temp, int, 0444);
 
@@ -86,9 +82,9 @@ enum {
 };
 static uint debug_mask = DEBUG_CHANGE;
 module_param(debug_mask, uint, 0644);
-#define dprintk(mask, fmt, ...) do { if (mask & debug_mask) printk(KERN_DEBUG pr_fmt(fmt), ##__VA_ARGS__); } while (0)
+#define dprintk(mask, fmt, ...) do { if (mask & debug_mask) printk(KERN_DEBUG "cpufreq: " fmt, ##__VA_ARGS__); } while (0)
 
-#define LIMIT_AVG_FREQ (816 * KHZ) /* kHz */
+#define LIMIT_AVG_FREQ (816 * 1000) /* kHz */
 static unsigned int limit_avg_freq = LIMIT_AVG_FREQ;
 module_param(limit_avg_freq, uint, 0444);
 
@@ -107,16 +103,17 @@ module_param_call(limit_avg_voltage, rk29_cpufreq_set_limit_avg_voltage, param_g
 
 #define CONFIG_RK29_CPU_FREQ_LIMIT_BY_DISP
 #ifdef CONFIG_RK29_CPU_FREQ_LIMIT_BY_DISP
-static bool limit_fb1_is_on;
-static bool limit_hdmi_is_on;
-static inline bool aclk_limit(void) { return limit_hdmi_is_on && limit_fb1_is_on; }
-module_param(limit_fb1_is_on, bool, 0644);
-module_param(limit_hdmi_is_on, bool, 0644);
+static bool limit_fb1_enabled;
+static bool limit_hdmi_enabled;
+static inline bool aclk_limit(void) { return limit_hdmi_enabled && limit_fb1_enabled; }
+module_param(limit_fb1_enabled, bool, 0644);
+module_param(limit_hdmi_enabled, bool, 0644);
 #else
 static inline bool aclk_limit(void) { return false; }
 #endif
 
 #if defined(CONFIG_RK29_CPU_FREQ_LIMIT_BY_DISP) || defined(CONFIG_RK29_CPU_FREQ_LIMIT_BY_TEMP)
+static unsigned int limit_max_freq;
 static int limit_index_816 = -1;
 static unsigned int limit_freq_816;
 static int limit_index_1008 = -1;
@@ -138,6 +135,7 @@ static void board_do_update_cpufreq_table(struct cpufreq_frequency_table *table)
        limit_avg_freq = 0;
        limit_avg_index = -1;
 #if defined(CONFIG_RK29_CPU_FREQ_LIMIT_BY_DISP) || defined(CONFIG_RK29_CPU_FREQ_LIMIT_BY_TEMP)
+       limit_max_freq = 0;
        limit_index_816 = -1;
        limit_freq_816 = 0;
        limit_index_1008 = -1;
@@ -145,17 +143,19 @@ static void board_do_update_cpufreq_table(struct cpufreq_frequency_table *table)
 #endif
 
        for (i = 0; table[i].frequency != CPUFREQ_TABLE_END; i++) {
-               table[i].frequency = clk_round_rate(arm_clk, table[i].frequency * KHZ) / KHZ;
+               table[i].frequency = clk_round_rate(arm_clk, table[i].frequency * 1000) / 1000;
                if (table[i].index <= limit_avg_voltage && limit_avg_freq < table[i].frequency) {
                        limit_avg_freq = table[i].frequency;
                        limit_avg_index = i;
                }
 #if defined(CONFIG_RK29_CPU_FREQ_LIMIT_BY_DISP) || defined(CONFIG_RK29_CPU_FREQ_LIMIT_BY_TEMP)
-               if (table[i].frequency <= 816 * KHZ &&
+               if (limit_max_freq < table[i].frequency)
+                       limit_max_freq = table[i].frequency;
+               if (table[i].frequency <= 816000 &&
                    (limit_index_816 < 0 ||
                    (limit_index_816 >= 0 && table[limit_index_816].frequency < table[i].frequency)))
                        limit_index_816 = i;
-               if (table[i].frequency <= 1008 * KHZ &&
+               if (table[i].frequency <= 1008000 &&
                    (limit_index_1008 < 0 ||
                    (limit_index_1008 >= 0 && table[limit_index_1008].frequency < table[i].frequency))) {
                        limit_index_1008 = i;
@@ -185,13 +185,25 @@ static int rk29_cpufreq_verify(struct cpufreq_policy *policy)
 }
 
 #ifdef CONFIG_RK29_CPU_FREQ_LIMIT_BY_TEMP
+static bool limit_vpu_enabled;
+static bool limit_gpu_enabled;
+static bool limit_gpu_high;
+module_param(limit_vpu_enabled, bool, 0644);
+module_param(limit_gpu_enabled, bool, 0644);
+module_param(limit_gpu_high, bool, 0644);
+static struct clk* aclk_vepu;
+static struct clk* clk_gpu;
+#define GPU_LOW_RATE   (300 * MHZ)
+static unsigned long limit_gpu_low_rate = GPU_LOW_RATE;
+module_param(limit_gpu_low_rate, ulong, 0644);
+
 #define TEMP_COEFF_IDLE -1000
 #define TEMP_COEFF_408  -325
 #define TEMP_COEFF_624  -202
 #define TEMP_COEFF_816  -78
 #define TEMP_COEFF_1008 325
-#define TEMP_COEFF_1200 (TEMP_COEFF_1008 * 5 / 2)
-#define WORK_DELAY      (2*HZ)
+#define TEMP_COEFF_1200 1300
+#define WORK_DELAY      HZ
 static void rk29_cpufreq_limit_by_temp(struct cpufreq_policy *policy, unsigned int relation, int *index)
 {
        int c, ms;
@@ -200,11 +212,13 @@ static void rk29_cpufreq_limit_by_temp(struct cpufreq_policy *policy, unsigned i
        cputime64_t wall;
        u64 idle_time_us;
        static u64 last_idle_time_us;
+       int idle;
        unsigned int cur = policy->cur;
        int overheat_temp_1200, overheat_temp;
        int temp;
        int target_index;
        unsigned int target_freq;
+       bool overheat;
 
        if (!limit || !rk29_cpufreq_is_ondemand_policy(policy) ||
            (limit_index_816 < 0) || (relation & MASK_FURTHER_CPUFREQ)) {
@@ -222,14 +236,14 @@ static void rk29_cpufreq_limit_by_temp(struct cpufreq_policy *policy, unsigned i
        }
 
        temp = limit_temp;
-       if (idle_time_us != last_idle_time_us) {
-               temp -= idle_time_us - last_idle_time_us; // -1000
-               dprintk(DEBUG_TEMP, "idle %lld us\n", idle_time_us - last_idle_time_us);
+       idle = idle_time_us - last_idle_time_us;
+       if (idle) {
+               temp -= idle; // -1000
                last_idle_time_us = idle_time_us;
        }
 
        ms = div_u64(ktime_us_delta(now, last), 1000);
-       dprintk(DEBUG_TEMP, "%d kHz (%d uV) elapsed %d ms\n", cur, vcore_uV, ms);
+       dprintk(DEBUG_TEMP, "%d kHz (%d uV) elapsed %d ms idle %d us\n", cur, vcore_uV, ms, idle);
        last = now;
 
        if (cur <= 408 * 1000)
@@ -249,18 +263,24 @@ static void rk29_cpufreq_limit_by_temp(struct cpufreq_policy *policy, unsigned i
 
        target_index = *index;
        target_freq = freq_table[target_index].frequency;
-       overheat_temp = TEMP_COEFF_1008 * limit_secs * 1000;
-       overheat_temp_1200 = overheat_temp - TEMP_COEFF_1200 * (WORK_DELAY/HZ) * 1000;
+       overheat_temp = TEMP_COEFF_1008 * limit_secs * MSEC_PER_SEC;
+       overheat_temp_1200 = TEMP_COEFF_1200 * limit_secs_1200 * MSEC_PER_SEC;
+       overheat = false;
 
-       if (temp > overheat_temp && target_freq > limit_freq_816)
+       if (temp >= overheat_temp && target_freq > limit_freq_816) {
                target_index = limit_index_816;
-       else if (target_freq > limit_freq_1008 && limit_freq_1008 > limit_freq_816 &&
-                temp > overheat_temp_1200 && temp <= overheat_temp)
+               overheat = true;
+       } else if (target_freq > limit_freq_1008 && limit_freq_1008 > limit_freq_816 &&
+                temp >= overheat_temp_1200 && temp < overheat_temp) {
+               target_index = limit_index_1008;
+               overheat = true;
+       } else if (target_freq > 1008000 && (limit_vpu_enabled || (limit_gpu_enabled && limit_gpu_high))) {
                target_index = limit_index_1008;
+       }
 
+       dprintk(DEBUG_TEMP, "%d kHz c %d temp %d (%s) selected %d kHz\n", target_freq, c, temp, overheat ? "overheat" : "normal", freq_table[target_index].frequency);
        limit_temp = temp;
        *index = target_index;
-       dprintk(DEBUG_TEMP, "c %d temp %d (%s) index %d\n", c, temp, temp > overheat_temp ? "overheat" : "normal", target_index);
 }
 #else
 #define rk29_cpufreq_limit_by_temp(...) do {} while (0)
@@ -276,10 +296,10 @@ static void rk29_cpufreq_limit_by_disp(int *index)
                return;
 
        if (ddr_max_rate < 492 * MHZ) {
-               if (limit_index_816 >= 0 && frequency > 816 * KHZ)
+               if (limit_index_816 >= 0 && frequency > 816000)
                        new_index = limit_index_816;
        } else {
-               if (limit_index_1008 >= 0 && frequency > 1008 * KHZ)
+               if (limit_index_1008 >= 0 && frequency > 1008000)
                        new_index = limit_index_1008;
        }
 
@@ -337,8 +357,8 @@ static int rk29_cpufreq_do_target(struct cpufreq_policy *policy, unsigned int ta
        freqs.new = freq->frequency;
        freqs.cpu = 0;
        new_vcore_uV = freq->index;
-       new_arm_rate = freqs.new * KHZ;
-       dprintk(DEBUG_CHANGE, "%d Hz r %d(%c) selected %d Hz (%d uV)\n",
+       new_arm_rate = freqs.new * 1000;
+       dprintk(DEBUG_CHANGE, "%d kHz r %d(%c) selected %d kHz (%d uV)\n",
                target_freq, relation, relation & CPUFREQ_RELATION_H ? 'H' : 'L',
                freq->frequency, new_vcore_uV);
 
@@ -357,9 +377,9 @@ static int rk29_cpufreq_do_target(struct cpufreq_policy *policy, unsigned int ta
 
        cpufreq_notify_transition(&freqs, CPUFREQ_PRECHANGE);
        dprintk(DEBUG_CHANGE, "pre change\n");
-       clk_set_rate(arm_clk, freqs.new * KHZ + aclk_limit());
+       clk_set_rate(arm_clk, freqs.new * 1000 + aclk_limit());
        dprintk(DEBUG_CHANGE, "post change\n");
-       freqs.new = clk_get_rate(arm_clk) / KHZ;
+       freqs.new = clk_get_rate(arm_clk) / 1000;
        cpufreq_notify_transition(&freqs, CPUFREQ_POSTCHANGE);
 
 #ifdef CONFIG_REGULATOR
@@ -373,7 +393,7 @@ static int rk29_cpufreq_do_target(struct cpufreq_policy *policy, unsigned int ta
                }
        }
 #endif
-       dprintk(DEBUG_CHANGE, "ok, got %d kHz\n", freqs.new);
+       dprintk(DEBUG_CHANGE, "got %d kHz\n", freqs.new);
 
        return err;
 }
@@ -424,12 +444,91 @@ static void rk29_cpufreq_limit_by_temp_work_func(struct work_struct *work)
        struct cpufreq_policy *policy = cpufreq_cpu_get(0);
 
        if (policy) {
-               dprintk(DEBUG_TEMP, "target %d KHz\n", policy->cur);
+               dprintk(DEBUG_TEMP, "check %d kHz\n", policy->cur);
                cpufreq_driver_target(policy, policy->cur, CPUFREQ_RELATION_L);
                cpufreq_cpu_put(policy);
        }
        queue_delayed_work(wq, &rk29_cpufreq_limit_by_temp_work, WORK_DELAY);
 }
+
+static int rk29_cpufreq_aclk_vepu_notifier_event(struct notifier_block *this,
+               unsigned long event, void *ptr)
+{
+       switch (event) {
+       case CLK_PRE_ENABLE:
+               limit_vpu_enabled = true;
+               break;
+       case CLK_ABORT_ENABLE:
+       case CLK_POST_DISABLE:
+               limit_vpu_enabled = false;
+               break;
+       default:
+               return NOTIFY_OK;
+       }
+
+       if (limit_vpu_enabled) {
+               struct cpufreq_policy *policy = cpufreq_cpu_get(0);
+
+               if (policy) {
+                       dprintk(DEBUG_TEMP, "vpu on\n");
+                       cpufreq_driver_target(policy, policy->cur, CPUFREQ_RELATION_L);
+                       cpufreq_cpu_put(policy);
+               }
+       }
+       return NOTIFY_OK;
+}
+
+static struct notifier_block rk29_cpufreq_aclk_vepu_notifier = {
+       .notifier_call = rk29_cpufreq_aclk_vepu_notifier_event,
+};
+
+static int rk29_cpufreq_clk_gpu_notifier_event(struct notifier_block *this,
+               unsigned long event, void *ptr)
+{
+       struct clk_notifier_data *cnd = ptr;
+       bool gpu_high_old = limit_gpu_enabled && limit_gpu_high;
+       bool gpu_high;
+
+       switch (event) {
+       case CLK_PRE_RATE_CHANGE:
+               if (cnd->new_rate > limit_gpu_low_rate)
+                       limit_gpu_high = true;
+               break;
+       case CLK_ABORT_RATE_CHANGE:
+               if (cnd->new_rate > limit_gpu_low_rate && cnd->old_rate <= limit_gpu_low_rate)
+                       limit_gpu_high = false;
+               break;
+       case CLK_POST_RATE_CHANGE:
+               if (cnd->new_rate <= limit_gpu_low_rate)
+                       limit_gpu_high = false;
+               break;
+       case CLK_PRE_ENABLE:
+               limit_gpu_enabled = true;
+               break;
+       case CLK_ABORT_ENABLE:
+       case CLK_POST_DISABLE:
+               limit_gpu_enabled = false;
+               break;
+       default:
+               return NOTIFY_OK;
+       }
+
+       gpu_high = limit_gpu_enabled && limit_gpu_high;
+       if (gpu_high_old != gpu_high && gpu_high) {
+               struct cpufreq_policy *policy = cpufreq_cpu_get(0);
+
+               if (policy) {
+                       dprintk(DEBUG_TEMP, "gpu high\n");
+                       cpufreq_driver_target(policy, policy->cur, CPUFREQ_RELATION_L);
+                       cpufreq_cpu_put(policy);
+               }
+       }
+       return NOTIFY_OK;
+}
+
+static struct notifier_block rk29_cpufreq_clk_gpu_notifier = {
+       .notifier_call = rk29_cpufreq_clk_gpu_notifier_event,
+};
 #endif
 
 #ifdef CONFIG_RK29_CPU_FREQ_LIMIT_BY_DISP
@@ -451,16 +550,16 @@ static int rk29_cpufreq_fb_notifier_event(struct notifier_block *this,
 {
        switch (event) {
        case RK29FB_EVENT_HDMI_ON:
-               limit_hdmi_is_on = true;
+               limit_hdmi_enabled = true;
                break;
        case RK29FB_EVENT_HDMI_OFF:
-               limit_hdmi_is_on = false;
+               limit_hdmi_enabled = false;
                break;
        case RK29FB_EVENT_FB1_ON:
-               limit_fb1_is_on = true;
+               limit_fb1_enabled = true;
                break;
        case RK29FB_EVENT_FB1_OFF:
-               limit_fb1_is_on = false;
+               limit_fb1_enabled = false;
                break;
        }
 
@@ -509,7 +608,7 @@ static int rk29_cpufreq_init(struct cpufreq_policy *policy)
        board_update_cpufreq_table(freq_table); /* force update frequency */
        BUG_ON(cpufreq_frequency_table_cpuinfo(policy, freq_table));
        cpufreq_frequency_table_get_attr(freq_table, policy->cpu);
-       policy->cur = clk_get_rate(arm_clk) / KHZ;
+       policy->cur = clk_get_rate(arm_clk) / 1000;
 
        policy->cpuinfo.transition_latency = 40 * NSEC_PER_USEC; // make default sampling_rate to 40000
 
@@ -525,6 +624,12 @@ static int rk29_cpufreq_init(struct cpufreq_policy *policy)
                queue_delayed_work(wq, &rk29_cpufreq_limit_by_temp_work, WORK_DELAY);
        }
        cpufreq_register_notifier(&notifier_policy_block, CPUFREQ_POLICY_NOTIFIER);
+       if (limit_max_freq > 1008000) {
+               clk_gpu = clk_get(NULL, "gpu");
+               aclk_vepu = clk_get(NULL, "aclk_vepu");
+               clk_notifier_register(clk_gpu, &rk29_cpufreq_clk_gpu_notifier);
+               clk_notifier_register(aclk_vepu, &rk29_cpufreq_aclk_vepu_notifier);
+       }
 #endif
 #ifdef CONFIG_RK29_CPU_FREQ_LIMIT_BY_DISP
        rk29fb_register_notifier(&rk29_cpufreq_fb_notifier);
@@ -538,6 +643,12 @@ static int rk29_cpufreq_exit(struct cpufreq_policy *policy)
        rk29fb_unregister_notifier(&rk29_cpufreq_fb_notifier);
 #endif
 #ifdef CONFIG_RK29_CPU_FREQ_LIMIT_BY_TEMP
+       if (limit_max_freq > 1008000) {
+               clk_notifier_unregister(clk_gpu, &rk29_cpufreq_clk_gpu_notifier);
+               clk_notifier_unregister(aclk_vepu, &rk29_cpufreq_aclk_vepu_notifier);
+               clk_put(clk_gpu);
+               clk_put(aclk_vepu);
+       }
        cpufreq_unregister_notifier(&notifier_policy_block, CPUFREQ_POLICY_NOTIFIER);
        if (wq)
                cancel_delayed_work(&rk29_cpufreq_limit_by_temp_work);