-/* 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>
#include <../../../drivers/video/rk29_fb.h>
#define MHZ (1000*1000)
-#define KHZ 1000
static int no_cpufreq_access;
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);
};
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);
#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;
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;
#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;
}
#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;
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)) {
}
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)
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)
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;
}
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);
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
}
}
#endif
- dprintk(DEBUG_CHANGE, "ok, got %d kHz\n", freqs.new);
+ dprintk(DEBUG_CHANGE, "got %d kHz\n", freqs.new);
return err;
}
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
{
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;
}
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
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);
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);