rk29: cpufreq: throttle more when run 1.2G

This commit is contained in:
黄涛
2011-09-14 10:52:45 +08:00
parent 350abdd331
commit 9cba9b55eb

View File

@@ -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
@@ -13,11 +13,6 @@
*
*/
#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);