mirror of
https://github.com/hardkernel/linux.git
synced 2026-06-09 20:32:04 +09:00
rk29: cpufreq: throttle more when run 1.2G
This commit is contained in:
@@ -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(¬ifier_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(¬ifier_policy_block, CPUFREQ_POLICY_NOTIFIER);
|
||||
if (wq)
|
||||
cancel_delayed_work(&rk29_cpufreq_limit_by_temp_work);
|
||||
|
||||
Reference in New Issue
Block a user