|
|
|
|
@@ -33,7 +33,7 @@
|
|
|
|
|
#include <asm/unistd.h>
|
|
|
|
|
#include <asm/uaccess.h>
|
|
|
|
|
|
|
|
|
|
#define VERSION "2.2"
|
|
|
|
|
#define VERSION "1.0"
|
|
|
|
|
|
|
|
|
|
#ifdef DEBUG
|
|
|
|
|
#define FREQ_DBG(fmt, args...) pr_debug(fmt, ## args)
|
|
|
|
|
@@ -55,11 +55,8 @@ static struct cpufreq_frequency_table default_freq_table[] = {
|
|
|
|
|
{.frequency = 1608 * 1000, .index = 1350 * 1000},
|
|
|
|
|
{.frequency = CPUFREQ_TABLE_END},
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
static struct cpufreq_frequency_table *freq_table = default_freq_table;
|
|
|
|
|
|
|
|
|
|
/*********************************************************/
|
|
|
|
|
|
|
|
|
|
/* additional symantics for "relation" in cpufreq with pm */
|
|
|
|
|
#define DISABLE_FURTHER_CPUFREQ 0x10
|
|
|
|
|
#define ENABLE_FURTHER_CPUFREQ 0x20
|
|
|
|
|
@@ -68,25 +65,17 @@ static struct cpufreq_frequency_table *freq_table = default_freq_table;
|
|
|
|
|
#define CPUFREQ_PRIVATE 0x100
|
|
|
|
|
static int no_cpufreq_access;
|
|
|
|
|
static unsigned int suspend_freq = 816 * 1000;
|
|
|
|
|
#if defined(CONFIG_ARCH_RK3026)
|
|
|
|
|
static unsigned int suspend_volt = 1100000; // 1.1V
|
|
|
|
|
#else
|
|
|
|
|
static unsigned int suspend_volt = 1000000; // 1V
|
|
|
|
|
#endif
|
|
|
|
|
static unsigned int low_battery_freq = 600 * 1000;
|
|
|
|
|
//static unsigned int low_battery_capacity = 5; // 5%
|
|
|
|
|
static unsigned int low_battery_capacity = 5; // 5%
|
|
|
|
|
static bool is_booting = true;
|
|
|
|
|
static struct workqueue_struct *freq_wq;
|
|
|
|
|
static DEFINE_MUTEX(cpufreq_mutex);
|
|
|
|
|
static bool gpu_is_mali400;
|
|
|
|
|
struct dvfs_node *clk_cpu_dvfs_node = NULL;
|
|
|
|
|
struct dvfs_node *clk_gpu_dvfs_node = NULL;
|
|
|
|
|
struct dvfs_node *clk_ddr_dvfs_node = NULL;
|
|
|
|
|
|
|
|
|
|
static int cpufreq_scale_rate_for_dvfs(struct clk *clk, unsigned long rate);
|
|
|
|
|
|
|
|
|
|
/*******************************************************/
|
|
|
|
|
static unsigned int rk3188_cpufreq_get(unsigned int cpu)
|
|
|
|
|
static unsigned int cpufreq_get_rate(unsigned int cpu)
|
|
|
|
|
{
|
|
|
|
|
if (clk_cpu_dvfs_node)
|
|
|
|
|
return clk_get_rate(clk_cpu_dvfs_node->clk) / 1000;
|
|
|
|
|
@@ -156,7 +145,7 @@ static struct cpufreq_frequency_table temp_limits_gpu_perf[] = {
|
|
|
|
|
{.frequency = 1008 * 1000, .index = 0},
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
static int rk3188_get_temp(void)
|
|
|
|
|
static int get_temp(void)
|
|
|
|
|
{
|
|
|
|
|
return 60;
|
|
|
|
|
}
|
|
|
|
|
@@ -187,7 +176,7 @@ static struct miscdevice sys_state_dev = {
|
|
|
|
|
.minor = MISC_DYNAMIC_MINOR,
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
static void rk3188_cpufreq_temp_limit_work_func(struct work_struct *work)
|
|
|
|
|
static void cpufreq_temp_limit_work_func(struct work_struct *work)
|
|
|
|
|
{
|
|
|
|
|
static bool in_perf = false;
|
|
|
|
|
struct cpufreq_policy *policy;
|
|
|
|
|
@@ -198,7 +187,7 @@ static void rk3188_cpufreq_temp_limit_work_func(struct work_struct *work)
|
|
|
|
|
const struct cpufreq_frequency_table *limits_table = temp_limits[nr_cpus - 1];
|
|
|
|
|
size_t limits_size = ARRAY_SIZE(temp_limits[nr_cpus - 1]);
|
|
|
|
|
|
|
|
|
|
temp = rk3188_get_temp();
|
|
|
|
|
temp = get_temp();
|
|
|
|
|
|
|
|
|
|
if (sys_state == '1') {
|
|
|
|
|
in_perf = true;
|
|
|
|
|
@@ -256,7 +245,7 @@ static void rk3188_cpufreq_temp_limit_work_func(struct work_struct *work)
|
|
|
|
|
if (temp_limit_freq != new_freq) {
|
|
|
|
|
unsigned int cur_freq;
|
|
|
|
|
temp_limit_freq = new_freq;
|
|
|
|
|
cur_freq = rk3188_cpufreq_get(0);
|
|
|
|
|
cur_freq = cpufreq_get_rate(0);
|
|
|
|
|
FREQ_DBG("temp limit %7d KHz cur %7d KHz\n", temp_limit_freq, cur_freq);
|
|
|
|
|
if (cur_freq > temp_limit_freq) {
|
|
|
|
|
policy = cpufreq_cpu_get(0);
|
|
|
|
|
@@ -268,9 +257,9 @@ static void rk3188_cpufreq_temp_limit_work_func(struct work_struct *work)
|
|
|
|
|
queue_delayed_work_on(0, freq_wq, to_delayed_work(work), delay);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static DECLARE_DELAYED_WORK(rk3188_cpufreq_temp_limit_work, rk3188_cpufreq_temp_limit_work_func);
|
|
|
|
|
static DECLARE_DELAYED_WORK(cpufreq_temp_limit_work, cpufreq_temp_limit_work_func);
|
|
|
|
|
|
|
|
|
|
static int rk3188_cpufreq_notifier_policy(struct notifier_block *nb, unsigned long val, void *data)
|
|
|
|
|
static int cpufreq_notifier_policy(struct notifier_block *nb, unsigned long val, void *data)
|
|
|
|
|
{
|
|
|
|
|
struct cpufreq_policy *policy = data;
|
|
|
|
|
|
|
|
|
|
@@ -279,20 +268,20 @@ static int rk3188_cpufreq_notifier_policy(struct notifier_block *nb, unsigned lo
|
|
|
|
|
|
|
|
|
|
if (cpufreq_is_ondemand(policy)) {
|
|
|
|
|
FREQ_DBG("queue work\n");
|
|
|
|
|
queue_delayed_work_on(0, freq_wq, &rk3188_cpufreq_temp_limit_work, 0);
|
|
|
|
|
queue_delayed_work_on(0, freq_wq, &cpufreq_temp_limit_work, 0);
|
|
|
|
|
} else {
|
|
|
|
|
FREQ_DBG("cancel work\n");
|
|
|
|
|
cancel_delayed_work_sync(&rk3188_cpufreq_temp_limit_work);
|
|
|
|
|
cancel_delayed_work_sync(&cpufreq_temp_limit_work);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static struct notifier_block notifier_policy_block = {
|
|
|
|
|
.notifier_call = rk3188_cpufreq_notifier_policy
|
|
|
|
|
.notifier_call = cpufreq_notifier_policy
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
static void rk3188_cpufreq_temp_limit_init(struct cpufreq_policy *policy)
|
|
|
|
|
static void cpufreq_temp_limit_init(struct cpufreq_policy *policy)
|
|
|
|
|
{
|
|
|
|
|
unsigned int i;
|
|
|
|
|
struct cpufreq_frequency_table *table;
|
|
|
|
|
@@ -311,250 +300,31 @@ static void rk3188_cpufreq_temp_limit_init(struct cpufreq_policy *policy)
|
|
|
|
|
}
|
|
|
|
|
misc_register(&sys_state_dev);
|
|
|
|
|
if (cpufreq_is_ondemand(policy)) {
|
|
|
|
|
queue_delayed_work_on(0, freq_wq, &rk3188_cpufreq_temp_limit_work, 0*HZ);
|
|
|
|
|
queue_delayed_work_on(0, freq_wq, &cpufreq_temp_limit_work, 0*HZ);
|
|
|
|
|
}
|
|
|
|
|
cpufreq_register_notifier(¬ifier_policy_block, CPUFREQ_POLICY_NOTIFIER);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void rk3188_cpufreq_temp_limit_exit(void)
|
|
|
|
|
static void cpufreq_temp_limit_exit(void)
|
|
|
|
|
{
|
|
|
|
|
cpufreq_unregister_notifier(¬ifier_policy_block, CPUFREQ_POLICY_NOTIFIER);
|
|
|
|
|
if (freq_wq)
|
|
|
|
|
cancel_delayed_work(&rk3188_cpufreq_temp_limit_work);
|
|
|
|
|
cancel_delayed_work(&cpufreq_temp_limit_work);
|
|
|
|
|
}
|
|
|
|
|
#else
|
|
|
|
|
static inline void rk3188_cpufreq_temp_limit_init(struct cpufreq_policy *policy) {}
|
|
|
|
|
static inline void rk3188_cpufreq_temp_limit_exit(void) {}
|
|
|
|
|
static inline void cpufreq_temp_limit_init(struct cpufreq_policy *policy) {}
|
|
|
|
|
static inline void cpufreq_temp_limit_exit(void) {}
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
/************************************dvfs tst************************************/
|
|
|
|
|
//#define CPU_FREQ_DVFS_TST
|
|
|
|
|
#ifdef CPU_FREQ_DVFS_TST
|
|
|
|
|
static unsigned int freq_dvfs_tst_rate;
|
|
|
|
|
static int test_count;
|
|
|
|
|
#define TEST_FRE_NUM 11
|
|
|
|
|
static int test_tlb_rate[TEST_FRE_NUM] = { 504, 1008, 504, 1200, 252, 816, 1416, 252, 1512, 252, 816 };
|
|
|
|
|
//static int test_tlb_rate[TEST_FRE_NUM]={504,1008,504,1200,252,816,1416,126,1512,126,816};
|
|
|
|
|
|
|
|
|
|
#define TEST_GPU_NUM 3
|
|
|
|
|
|
|
|
|
|
static int test_tlb_gpu[TEST_GPU_NUM] = { 360, 400, 180 };
|
|
|
|
|
static int test_tlb_ddr[TEST_GPU_NUM] = { 401, 200, 500 };
|
|
|
|
|
|
|
|
|
|
static int gpu_ddr = 0;
|
|
|
|
|
|
|
|
|
|
static void rk3188_cpufreq_dvsf_tst_work_func(struct work_struct *work)
|
|
|
|
|
{
|
|
|
|
|
struct cpufreq_policy *policy = cpufreq_cpu_get(0);
|
|
|
|
|
|
|
|
|
|
gpu_ddr++;
|
|
|
|
|
#if 0
|
|
|
|
|
FREQ_LOG("cpufreq_dvsf_tst,ddr%u,gpu%u\n",
|
|
|
|
|
test_tlb_ddr[gpu_ddr % TEST_GPU_NUM],
|
|
|
|
|
test_tlb_gpu[gpu_ddr % TEST_GPU_NUM]);
|
|
|
|
|
clk_set_rate(ddr_clk, test_tlb_ddr[gpu_ddr % TEST_GPU_NUM] * 1000 * 1000);
|
|
|
|
|
clk_set_rate(gpu_clk, test_tlb_gpu[gpu_ddr % TEST_GPU_NUM] * 1000 * 1000);
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
test_count++;
|
|
|
|
|
freq_dvfs_tst_rate = test_tlb_rate[test_count % TEST_FRE_NUM] * 1000;
|
|
|
|
|
printk("cpufreq_dvsf_tst,cpu set rate %d\n", freq_dvfs_tst_rate);
|
|
|
|
|
cpufreq_driver_target(policy, policy->cur, CPUFREQ_RELATION_L);
|
|
|
|
|
cpufreq_cpu_put(policy);
|
|
|
|
|
|
|
|
|
|
queue_delayed_work_on(0, freq_wq, to_delayed_work(work), msecs_to_jiffies(1000));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static DECLARE_DELAYED_WORK(rk3188_cpufreq_dvsf_tst_work, rk3188_cpufreq_dvsf_tst_work_func);
|
|
|
|
|
#endif /* CPU_FREQ_DVFS_TST */
|
|
|
|
|
|
|
|
|
|
/***********************************************************************/
|
|
|
|
|
static int rk3188_cpufreq_verify(struct cpufreq_policy *policy)
|
|
|
|
|
static int cpufreq_verify(struct cpufreq_policy *policy)
|
|
|
|
|
{
|
|
|
|
|
if (!freq_table)
|
|
|
|
|
return -EINVAL;
|
|
|
|
|
return cpufreq_frequency_table_verify(policy, freq_table);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int rk3188_cpufreq_init_cpu0(struct cpufreq_policy *policy)
|
|
|
|
|
{
|
|
|
|
|
unsigned int i;
|
|
|
|
|
//struct cpufreq_frequency_table *table_adjust;
|
|
|
|
|
|
|
|
|
|
gpu_is_mali400 = cpu_is_rk3188();
|
|
|
|
|
|
|
|
|
|
clk_gpu_dvfs_node = clk_get_dvfs_node("clk_gpu");
|
|
|
|
|
if (!clk_gpu_dvfs_node){
|
|
|
|
|
return -EINVAL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
clk_ddr_dvfs_node = clk_get_dvfs_node("clk_ddr");
|
|
|
|
|
if (!clk_ddr_dvfs_node){
|
|
|
|
|
return -EINVAL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
clk_cpu_dvfs_node = clk_get_dvfs_node("clk_core");
|
|
|
|
|
if (!clk_cpu_dvfs_node){
|
|
|
|
|
return -EINVAL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//table_adjust = dvfs_get_freq_volt_table(cpu_clk);
|
|
|
|
|
//dvfs_adjust_table_lmtvolt(cpu_clk, table_adjust);
|
|
|
|
|
//table_adjust = dvfs_get_freq_volt_table(gpu_clk);
|
|
|
|
|
//dvfs_adjust_table_lmtvolt(gpu_clk, table_adjust);
|
|
|
|
|
|
|
|
|
|
clk_enable_dvfs(clk_gpu_dvfs_node);
|
|
|
|
|
if (gpu_is_mali400)
|
|
|
|
|
dvfs_clk_enable_limit(clk_gpu_dvfs_node, 133000000, 600000000);
|
|
|
|
|
|
|
|
|
|
clk_enable_dvfs(clk_ddr_dvfs_node);
|
|
|
|
|
|
|
|
|
|
dvfs_clk_register_set_rate_callback(clk_cpu_dvfs_node, cpufreq_scale_rate_for_dvfs);
|
|
|
|
|
freq_table = dvfs_get_freq_volt_table(clk_cpu_dvfs_node);
|
|
|
|
|
if (freq_table == NULL) {
|
|
|
|
|
freq_table = default_freq_table;
|
|
|
|
|
} else {
|
|
|
|
|
int v = INT_MAX;
|
|
|
|
|
for (i = 0; freq_table[i].frequency != CPUFREQ_TABLE_END; i++) {
|
|
|
|
|
if (freq_table[i].index >= suspend_volt && v > freq_table[i].index) {
|
|
|
|
|
suspend_freq = freq_table[i].frequency;
|
|
|
|
|
v = freq_table[i].index;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
low_battery_freq = get_freq_from_table(low_battery_freq);
|
|
|
|
|
clk_enable_dvfs(clk_cpu_dvfs_node);
|
|
|
|
|
/*if(rk_tflag()){
|
|
|
|
|
#define RK3188_T_LIMIT_FREQ (1416 * 1000)
|
|
|
|
|
dvfs_clk_enable_limit(cpu_clk, 0, RK3188_T_LIMIT_FREQ * 1000);
|
|
|
|
|
for (i = 0; freq_table[i].frequency != CPUFREQ_TABLE_END; i++) {
|
|
|
|
|
if (freq_table[i].frequency > RK3188_T_LIMIT_FREQ) {
|
|
|
|
|
printk("cpufreq: delete arm freq(%u)\n", freq_table[i].frequency);
|
|
|
|
|
freq_table[i].frequency = CPUFREQ_TABLE_END;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}*/
|
|
|
|
|
freq_wq = alloc_workqueue("rk3188_cpufreqd", WQ_NON_REENTRANT | WQ_MEM_RECLAIM | WQ_HIGHPRI | WQ_FREEZABLE, 1);
|
|
|
|
|
rk3188_cpufreq_temp_limit_init(policy);
|
|
|
|
|
#ifdef CPU_FREQ_DVFS_TST
|
|
|
|
|
queue_delayed_work(freq_wq, &rk3188_cpufreq_dvsf_tst_work, msecs_to_jiffies(20 * 1000));
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
printk("rk3188 cpufreq version " VERSION ", suspend freq %d MHz\n", suspend_freq / 1000);
|
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int rk3188_cpufreq_init(struct cpufreq_policy *policy)
|
|
|
|
|
{
|
|
|
|
|
static int cpu0_err;
|
|
|
|
|
|
|
|
|
|
if (policy->cpu == 0) {
|
|
|
|
|
cpu0_err = rk3188_cpufreq_init_cpu0(policy);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (cpu0_err)
|
|
|
|
|
return cpu0_err;
|
|
|
|
|
|
|
|
|
|
//set freq min max
|
|
|
|
|
cpufreq_frequency_table_cpuinfo(policy, freq_table);
|
|
|
|
|
//sys nod
|
|
|
|
|
cpufreq_frequency_table_get_attr(freq_table, policy->cpu);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
policy->cur = rk3188_cpufreq_get(0);
|
|
|
|
|
|
|
|
|
|
policy->cpuinfo.transition_latency = 40 * NSEC_PER_USEC; // make ondemand default sampling_rate to 40000
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* On SMP configuartion, both processors share the voltage
|
|
|
|
|
* and clock. So both CPUs needs to be scaled together and hence
|
|
|
|
|
* needs software co-ordination. Use cpufreq affected_cpus
|
|
|
|
|
* interface to handle this scenario. Additional is_smp() check
|
|
|
|
|
* is to keep SMP_ON_UP build working.
|
|
|
|
|
*/
|
|
|
|
|
if (is_smp())
|
|
|
|
|
cpumask_setall(policy->cpus);
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int rk3188_cpufreq_exit(struct cpufreq_policy *policy)
|
|
|
|
|
{
|
|
|
|
|
if (policy->cpu != 0)
|
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
|
|
cpufreq_frequency_table_cpuinfo(policy, freq_table);
|
|
|
|
|
// clk_put(clk_cpu_dvfs_node->clk);
|
|
|
|
|
rk3188_cpufreq_temp_limit_exit();
|
|
|
|
|
if (freq_wq) {
|
|
|
|
|
flush_workqueue(freq_wq);
|
|
|
|
|
destroy_workqueue(freq_wq);
|
|
|
|
|
freq_wq = NULL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static struct freq_attr *rk3188_cpufreq_attr[] = {
|
|
|
|
|
&cpufreq_freq_attr_scaling_available_freqs,
|
|
|
|
|
NULL,
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
#ifdef CONFIG_POWER_SUPPLY
|
|
|
|
|
extern int rk_get_system_battery_capacity(void);
|
|
|
|
|
#else
|
|
|
|
|
static int rk_get_system_battery_capacity(void) { return 100; }
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
static unsigned int cpufreq_scale_limit(unsigned int target_freq, struct cpufreq_policy *policy, bool is_private)
|
|
|
|
|
{
|
|
|
|
|
bool is_ondemand = cpufreq_is_ondemand(policy);
|
|
|
|
|
|
|
|
|
|
#ifdef CPU_FREQ_DVFS_TST
|
|
|
|
|
if (freq_dvfs_tst_rate) {
|
|
|
|
|
target_freq = freq_dvfs_tst_rate;
|
|
|
|
|
freq_dvfs_tst_rate = 0;
|
|
|
|
|
return target_freq;
|
|
|
|
|
}
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
if (!is_ondemand)
|
|
|
|
|
return target_freq;
|
|
|
|
|
|
|
|
|
|
if (is_booting) {
|
|
|
|
|
s64 boottime_ms = ktime_to_ms(ktime_get_boottime());
|
|
|
|
|
if (boottime_ms > 60 * MSEC_PER_SEC) {
|
|
|
|
|
is_booting = false;
|
|
|
|
|
}/* else if (target_freq > low_battery_freq &&
|
|
|
|
|
rk_get_system_battery_capacity() <= low_battery_capacity) {
|
|
|
|
|
target_freq = low_battery_freq;
|
|
|
|
|
}*/
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#ifdef CONFIG_RK30_CPU_FREQ_LIMIT_BY_TEMP
|
|
|
|
|
{
|
|
|
|
|
static unsigned int ondemand_target = 816 * 1000;
|
|
|
|
|
if (is_private)
|
|
|
|
|
target_freq = ondemand_target;
|
|
|
|
|
else
|
|
|
|
|
ondemand_target = target_freq;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* If the new frequency is more than the thermal max allowed
|
|
|
|
|
* frequency, go ahead and scale the mpu device to proper frequency.
|
|
|
|
|
*/
|
|
|
|
|
target_freq = min(target_freq, temp_limit_freq);
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
return target_freq;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int cpufreq_scale_rate_for_dvfs(struct clk *clk, unsigned long rate)
|
|
|
|
|
{
|
|
|
|
|
|
|
|
|
|
unsigned int i;
|
|
|
|
|
int ret;
|
|
|
|
|
struct cpufreq_freqs freqs;
|
|
|
|
|
@@ -596,9 +366,148 @@ static int cpufreq_scale_rate_for_dvfs(struct clk *clk, unsigned long rate)
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int rk3188_cpufreq_target(struct cpufreq_policy *policy, unsigned int target_freq, unsigned int relation)
|
|
|
|
|
{
|
|
|
|
|
|
|
|
|
|
static int cpufreq_init_cpu0(struct cpufreq_policy *policy)
|
|
|
|
|
{
|
|
|
|
|
unsigned int i;
|
|
|
|
|
gpu_is_mali400 = cpu_is_rk3188();
|
|
|
|
|
|
|
|
|
|
clk_gpu_dvfs_node = clk_get_dvfs_node("clk_gpu");
|
|
|
|
|
if (clk_gpu_dvfs_node){
|
|
|
|
|
clk_enable_dvfs(clk_gpu_dvfs_node);
|
|
|
|
|
if (gpu_is_mali400)
|
|
|
|
|
dvfs_clk_enable_limit(clk_gpu_dvfs_node, 133000000, 600000000);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
clk_cpu_dvfs_node = clk_get_dvfs_node("clk_core");
|
|
|
|
|
if (!clk_cpu_dvfs_node){
|
|
|
|
|
return -EINVAL;
|
|
|
|
|
}
|
|
|
|
|
dvfs_clk_register_set_rate_callback(clk_cpu_dvfs_node, cpufreq_scale_rate_for_dvfs);
|
|
|
|
|
freq_table = dvfs_get_freq_volt_table(clk_cpu_dvfs_node);
|
|
|
|
|
if (freq_table == NULL) {
|
|
|
|
|
freq_table = default_freq_table;
|
|
|
|
|
} else {
|
|
|
|
|
int v = INT_MAX;
|
|
|
|
|
for (i = 0; freq_table[i].frequency != CPUFREQ_TABLE_END; i++) {
|
|
|
|
|
if (freq_table[i].index >= suspend_volt && v > freq_table[i].index) {
|
|
|
|
|
suspend_freq = freq_table[i].frequency;
|
|
|
|
|
v = freq_table[i].index;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
low_battery_freq = get_freq_from_table(low_battery_freq);
|
|
|
|
|
clk_enable_dvfs(clk_cpu_dvfs_node);
|
|
|
|
|
|
|
|
|
|
freq_wq = alloc_workqueue("cpufreq", WQ_NON_REENTRANT | WQ_MEM_RECLAIM | WQ_HIGHPRI | WQ_FREEZABLE, 1);
|
|
|
|
|
cpufreq_temp_limit_init(policy);
|
|
|
|
|
|
|
|
|
|
printk("cpufreq version " VERSION ", suspend freq %d MHz\n", suspend_freq / 1000);
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int cpufreq_init(struct cpufreq_policy *policy)
|
|
|
|
|
{
|
|
|
|
|
static int cpu0_err;
|
|
|
|
|
|
|
|
|
|
if (policy->cpu == 0) {
|
|
|
|
|
cpu0_err = cpufreq_init_cpu0(policy);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (cpu0_err)
|
|
|
|
|
return cpu0_err;
|
|
|
|
|
|
|
|
|
|
//set freq min max
|
|
|
|
|
cpufreq_frequency_table_cpuinfo(policy, freq_table);
|
|
|
|
|
//sys nod
|
|
|
|
|
cpufreq_frequency_table_get_attr(freq_table, policy->cpu);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
policy->cur = clk_get_rate(clk_cpu_dvfs_node->clk) / 1000;
|
|
|
|
|
|
|
|
|
|
policy->cpuinfo.transition_latency = 40 * NSEC_PER_USEC; // make ondemand default sampling_rate to 40000
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* On SMP configuartion, both processors share the voltage
|
|
|
|
|
* and clock. So both CPUs needs to be scaled together and hence
|
|
|
|
|
* needs software co-ordination. Use cpufreq affected_cpus
|
|
|
|
|
* interface to handle this scenario. Additional is_smp() check
|
|
|
|
|
* is to keep SMP_ON_UP build working.
|
|
|
|
|
*/
|
|
|
|
|
if (is_smp())
|
|
|
|
|
cpumask_setall(policy->cpus);
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int cpufreq_exit(struct cpufreq_policy *policy)
|
|
|
|
|
{
|
|
|
|
|
if (policy->cpu != 0)
|
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
|
|
cpufreq_frequency_table_cpuinfo(policy, freq_table);
|
|
|
|
|
clk_put_dvfs_node(clk_cpu_dvfs_node);
|
|
|
|
|
cpufreq_temp_limit_exit();
|
|
|
|
|
if (freq_wq) {
|
|
|
|
|
flush_workqueue(freq_wq);
|
|
|
|
|
destroy_workqueue(freq_wq);
|
|
|
|
|
freq_wq = NULL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static struct freq_attr *cpufreq_attr[] = {
|
|
|
|
|
&cpufreq_freq_attr_scaling_available_freqs,
|
|
|
|
|
NULL,
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
//#ifdef CONFIG_POWER_SUPPLY
|
|
|
|
|
#if 0
|
|
|
|
|
extern int rk_get_system_battery_capacity(void);
|
|
|
|
|
#else
|
|
|
|
|
static int rk_get_system_battery_capacity(void) { return 100; }
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
static unsigned int cpufreq_scale_limit(unsigned int target_freq, struct cpufreq_policy *policy, bool is_private)
|
|
|
|
|
{
|
|
|
|
|
bool is_ondemand = cpufreq_is_ondemand(policy);
|
|
|
|
|
|
|
|
|
|
if (!is_ondemand)
|
|
|
|
|
return target_freq;
|
|
|
|
|
|
|
|
|
|
if (is_booting) {
|
|
|
|
|
s64 boottime_ms = ktime_to_ms(ktime_get_boottime());
|
|
|
|
|
if (boottime_ms > 60 * MSEC_PER_SEC) {
|
|
|
|
|
is_booting = false;
|
|
|
|
|
} else if (target_freq > low_battery_freq &&
|
|
|
|
|
rk_get_system_battery_capacity() <= low_battery_capacity) {
|
|
|
|
|
target_freq = low_battery_freq;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#ifdef CONFIG_RK30_CPU_FREQ_LIMIT_BY_TEMP
|
|
|
|
|
{
|
|
|
|
|
static unsigned int ondemand_target = 816 * 1000;
|
|
|
|
|
if (is_private)
|
|
|
|
|
target_freq = ondemand_target;
|
|
|
|
|
else
|
|
|
|
|
ondemand_target = target_freq;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* If the new frequency is more than the thermal max allowed
|
|
|
|
|
* frequency, go ahead and scale the mpu device to proper frequency.
|
|
|
|
|
*/
|
|
|
|
|
target_freq = min(target_freq, temp_limit_freq);
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
return target_freq;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int cpufreq_target(struct cpufreq_policy *policy, unsigned int target_freq, unsigned int relation)
|
|
|
|
|
{
|
|
|
|
|
unsigned int i, new_freq = target_freq, new_rate, cur_rate;
|
|
|
|
|
int ret = 0;
|
|
|
|
|
bool is_private;
|
|
|
|
|
@@ -634,7 +543,7 @@ static int rk3188_cpufreq_target(struct cpufreq_policy *policy, unsigned int tar
|
|
|
|
|
new_freq = cpufreq_scale_limit(new_freq, policy, is_private);
|
|
|
|
|
|
|
|
|
|
new_rate = new_freq * 1000;
|
|
|
|
|
cur_rate = clk_get_rate(clk_cpu_dvfs_node->clk);
|
|
|
|
|
cur_rate = dvfs_clk_get_rate(clk_cpu_dvfs_node);
|
|
|
|
|
FREQ_LOG("req = %7u new = %7u (was = %7u)\n", target_freq, new_freq, cur_rate / 1000);
|
|
|
|
|
if (new_rate == cur_rate)
|
|
|
|
|
goto out;
|
|
|
|
|
@@ -647,7 +556,7 @@ out:
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int rk3188_cpufreq_pm_notifier_event(struct notifier_block *this, unsigned long event, void *ptr)
|
|
|
|
|
static int cpufreq_pm_notifier_event(struct notifier_block *this, unsigned long event, void *ptr)
|
|
|
|
|
{
|
|
|
|
|
int ret = NOTIFY_DONE;
|
|
|
|
|
struct cpufreq_policy *policy = cpufreq_cpu_get(0);
|
|
|
|
|
@@ -678,11 +587,11 @@ out:
|
|
|
|
|
return ret;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static struct notifier_block rk3188_cpufreq_pm_notifier = {
|
|
|
|
|
.notifier_call = rk3188_cpufreq_pm_notifier_event,
|
|
|
|
|
static struct notifier_block cpufreq_pm_notifier = {
|
|
|
|
|
.notifier_call = cpufreq_pm_notifier_event,
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
static int rk3188_cpufreq_reboot_notifier_event(struct notifier_block *this, unsigned long event, void *ptr)
|
|
|
|
|
static int cpufreq_reboot_notifier_event(struct notifier_block *this, unsigned long event, void *ptr)
|
|
|
|
|
{
|
|
|
|
|
struct cpufreq_policy *policy = cpufreq_cpu_get(0);
|
|
|
|
|
|
|
|
|
|
@@ -695,28 +604,26 @@ static int rk3188_cpufreq_reboot_notifier_event(struct notifier_block *this, uns
|
|
|
|
|
return NOTIFY_OK;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static struct notifier_block rk3188_cpufreq_reboot_notifier = {
|
|
|
|
|
.notifier_call = rk3188_cpufreq_reboot_notifier_event,
|
|
|
|
|
static struct notifier_block cpufreq_reboot_notifier = {
|
|
|
|
|
.notifier_call = cpufreq_reboot_notifier_event,
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
static struct cpufreq_driver rk3188_cpufreq_driver = {
|
|
|
|
|
static struct cpufreq_driver cpufreq_driver = {
|
|
|
|
|
.flags = CPUFREQ_CONST_LOOPS,
|
|
|
|
|
.verify = rk3188_cpufreq_verify,
|
|
|
|
|
.target = rk3188_cpufreq_target,
|
|
|
|
|
.get = rk3188_cpufreq_get,
|
|
|
|
|
.init = rk3188_cpufreq_init,
|
|
|
|
|
.exit = rk3188_cpufreq_exit,
|
|
|
|
|
.name = "rk3188",
|
|
|
|
|
.attr = rk3188_cpufreq_attr,
|
|
|
|
|
.verify = cpufreq_verify,
|
|
|
|
|
.target = cpufreq_target,
|
|
|
|
|
.get = cpufreq_get_rate,
|
|
|
|
|
.init = cpufreq_init,
|
|
|
|
|
.exit = cpufreq_exit,
|
|
|
|
|
.name = "rockchip",
|
|
|
|
|
.attr = cpufreq_attr,
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
static int __init rk3188_cpufreq_driver_init(void)
|
|
|
|
|
static int __init cpufreq_driver_init(void)
|
|
|
|
|
{
|
|
|
|
|
if (!cpu_is_rk3188())
|
|
|
|
|
return 0;
|
|
|
|
|
register_pm_notifier(&rk3188_cpufreq_pm_notifier);
|
|
|
|
|
register_reboot_notifier(&rk3188_cpufreq_reboot_notifier);
|
|
|
|
|
return cpufreq_register_driver(&rk3188_cpufreq_driver);
|
|
|
|
|
register_pm_notifier(&cpufreq_pm_notifier);
|
|
|
|
|
register_reboot_notifier(&cpufreq_reboot_notifier);
|
|
|
|
|
return cpufreq_register_driver(&cpufreq_driver);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
device_initcall(rk3188_cpufreq_driver_init);
|
|
|
|
|
device_initcall(cpufreq_driver_init);
|