X-Git-Url: http://plrg.eecs.uci.edu/git/?a=blobdiff_plain;f=drivers%2Fcpufreq%2Frockchip-cpufreq.c;h=ba43a69bc1d8e7a32f46c5918099b4f92626d650;hb=9607f47dfec23c5773d74e45ed561859eabce2b7;hp=ecf7f9cc783747783f33ff8bc2f65b9e1e365046;hpb=a26084749b4fa2ea42705e5c3ed398b445e9c3ce;p=firefly-linux-kernel-4.4.55.git diff --git a/drivers/cpufreq/rockchip-cpufreq.c b/drivers/cpufreq/rockchip-cpufreq.c index ecf7f9cc7837..ba43a69bc1d8 100644 --- a/drivers/cpufreq/rockchip-cpufreq.c +++ b/drivers/cpufreq/rockchip-cpufreq.c @@ -1,475 +1,36 @@ /* - * Copyright (C) 2013 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 - * may be copied, distributed, and modified under those terms. + * Copyright (C) 2015 Fuzhou Rockchip Electronics Co., Ltd * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. + * Xiao Feng + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. * + * This program is distributed "as is" WITHOUT ANY WARRANTY of any + * kind, whether express or implied; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. */ -#define pr_fmt(fmt) "cpufreq: " fmt -#include -#include + #include -#include #include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "../../../drivers/clk/rockchip/clk-pd.h" - -extern void dvfs_disable_temp_limit(void); - -#define VERSION "1.0" - -#ifdef DEBUG -#define FREQ_DBG(fmt, args...) pr_debug(fmt, ## args) -#define FREQ_LOG(fmt, args...) pr_debug(fmt, ## args) -#else -#define FREQ_DBG(fmt, args...) do {} while(0) -#define FREQ_LOG(fmt, args...) do {} while(0) -#endif -#define FREQ_ERR(fmt, args...) pr_err(fmt, ## args) - -/* Frequency table index must be sequential starting at 0 */ -static struct cpufreq_frequency_table default_freq_table[] = { - {.frequency = 312 * 1000, .index = 875 * 1000}, - {.frequency = 504 * 1000, .index = 925 * 1000}, - {.frequency = 816 * 1000, .index = 975 * 1000}, - {.frequency = 1008 * 1000, .index = 1075 * 1000}, - {.frequency = 1200 * 1000, .index = 1150 * 1000}, - {.frequency = 1416 * 1000, .index = 1250 * 1000}, - {.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 -#define MASK_FURTHER_CPUFREQ 0x30 -/* With 0x00(NOCHANGE), it depends on the previous "further" status */ -#define CPUFREQ_PRIVATE 0x100 -static unsigned int no_cpufreq_access = 0; -static unsigned int suspend_freq = 816 * 1000; -static unsigned int suspend_volt = 1100000; -static unsigned int low_battery_freq = 600 * 1000; -static unsigned int low_battery_capacity = 5; // 5% -static bool is_booting = true; -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 *aclk_vio1_dvfs_node = NULL; -struct dvfs_node *clk_ddr_dvfs_node = NULL; -/*******************************************************/ -static unsigned int cpufreq_get_rate(unsigned int cpu) -{ - if (clk_cpu_dvfs_node) - return clk_get_rate(clk_cpu_dvfs_node->clk) / 1000; - - return 0; -} - -static bool cpufreq_is_ondemand(struct cpufreq_policy *policy) -{ - char c = 0; - if (policy && policy->governor) - c = policy->governor->name[0]; - return (c == 'o' || c == 'i' || c == 'c' || c == 'h'); -} - -static unsigned int get_freq_from_table(unsigned int max_freq) -{ - unsigned int i; - unsigned int target_freq = 0; - for (i = 0; freq_table[i].frequency != CPUFREQ_TABLE_END; i++) { - unsigned int freq = freq_table[i].frequency; - if (freq <= max_freq && target_freq < freq) { - target_freq = freq; - } - } - if (!target_freq) - target_freq = max_freq; - return target_freq; -} - -static int cpufreq_notifier_policy(struct notifier_block *nb, unsigned long val, void *data) -{ - static unsigned int min_rate=0, max_rate=-1; - struct cpufreq_policy *policy = data; - - if (val != CPUFREQ_ADJUST) - return 0; - - if (cpufreq_is_ondemand(policy)) { - FREQ_DBG("queue work\n"); - dvfs_clk_enable_limit(clk_cpu_dvfs_node, min_rate, max_rate); - } else { - FREQ_DBG("cancel work\n"); - dvfs_clk_get_limit(clk_cpu_dvfs_node, &min_rate, &max_rate); - } - - return 0; -} - -static struct notifier_block notifier_policy_block = { - .notifier_call = cpufreq_notifier_policy -}; - -static int cpufreq_verify(struct cpufreq_policy *policy) -{ - if (!freq_table) - return -EINVAL; - return cpufreq_frequency_table_verify(policy, freq_table); -} - -static int cpufreq_scale_rate_for_dvfs(struct clk *clk, unsigned long rate) -{ - int ret; - struct cpufreq_freqs freqs; - struct cpufreq_policy *policy; - - freqs.new = rate / 1000; - freqs.old = clk_get_rate(clk) / 1000; - - for_each_online_cpu(freqs.cpu) { - policy = cpufreq_cpu_get(freqs.cpu); - cpufreq_notify_transition(policy, &freqs, CPUFREQ_PRECHANGE); - cpufreq_cpu_put(policy); - } - - FREQ_DBG("cpufreq_scale_rate_for_dvfs(%lu)\n", rate); - - ret = clk_set_rate(clk, rate); - - freqs.new = clk_get_rate(clk) / 1000; - /* notifiers */ - for_each_online_cpu(freqs.cpu) { - policy = cpufreq_cpu_get(freqs.cpu); - cpufreq_notify_transition(policy, &freqs, CPUFREQ_POSTCHANGE); - cpufreq_cpu_put(policy); - } - - return ret; - -} - -static int cpufreq_init_cpu0(struct cpufreq_policy *policy) -{ - unsigned int i; - int ret; - struct regulator *vdd_gpu_regulator; - - 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); - vdd_gpu_regulator = dvfs_get_regulator("vdd_gpu"); - if (!IS_ERR_OR_NULL(vdd_gpu_regulator)) { - if (!regulator_is_enabled(vdd_gpu_regulator)) { - ret = regulator_enable(vdd_gpu_regulator); - arm_pm_restart('h', NULL); - } - /* make sure vdd_gpu_regulator is in use, - so it will not be disable by regulator_init_complete*/ - ret = regulator_enable(vdd_gpu_regulator); - if (ret != 0) - arm_pm_restart('h', NULL); - } - if (gpu_is_mali400) - dvfs_clk_enable_limit(clk_gpu_dvfs_node, 133000000, 600000000); - } - - clk_ddr_dvfs_node = clk_get_dvfs_node("clk_ddr"); - if (clk_ddr_dvfs_node){ - clk_enable_dvfs(clk_ddr_dvfs_node); - } - - 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); - - cpufreq_register_notifier(¬ifier_policy_block, CPUFREQ_POLICY_NOTIFIER); - - 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 - - cpumask_setall(policy->cpus); - - return 0; +#include +#include +#include +#include -} - -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_unregister_notifier(¬ifier_policy_block, CPUFREQ_POLICY_NOTIFIER); - - return 0; -} - -static struct freq_attr *cpufreq_attr[] = { - &cpufreq_freq_attr_scaling_available_freqs, - NULL, -}; - -#ifdef CONFIG_CHARGER_DISPLAY -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; - } - } - - 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; - - if (!freq_table) { - FREQ_ERR("no freq table!\n"); - return -EINVAL; - } - - mutex_lock(&cpufreq_mutex); - - is_private = relation & CPUFREQ_PRIVATE; - relation &= ~CPUFREQ_PRIVATE; - - if ((relation & ENABLE_FURTHER_CPUFREQ) && no_cpufreq_access) - no_cpufreq_access--; - if (no_cpufreq_access) { - FREQ_LOG("denied access to %s as it is disabled temporarily\n", __func__); - ret = -EINVAL; - goto out; - } - if (relation & DISABLE_FURTHER_CPUFREQ) - no_cpufreq_access++; - relation &= ~MASK_FURTHER_CPUFREQ; - - ret = cpufreq_frequency_table_target(policy, freq_table, target_freq, relation, &i); - if (ret) { - FREQ_ERR("no freq match for %d(ret=%d)\n", target_freq, ret); - goto out; - } - new_freq = freq_table[i].frequency; - if (!no_cpufreq_access) - new_freq = cpufreq_scale_limit(new_freq, policy, is_private); - - new_rate = new_freq * 1000; - 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; - ret = dvfs_clk_set_rate(clk_cpu_dvfs_node, new_rate); - -out: - FREQ_DBG("set freq (%7u) end, ret %d\n", new_freq, ret); - mutex_unlock(&cpufreq_mutex); - return ret; - -} - -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); - - if (!policy) - return ret; - - if (!cpufreq_is_ondemand(policy)) - goto out; - - switch (event) { - case PM_SUSPEND_PREPARE: - policy->cur++; - ret = cpufreq_driver_target(policy, suspend_freq, DISABLE_FURTHER_CPUFREQ | CPUFREQ_RELATION_H); - if (ret < 0) { - ret = NOTIFY_BAD; - goto out; - } - ret = NOTIFY_OK; - break; - case PM_POST_RESTORE: - case PM_POST_SUSPEND: - //if (target_freq == policy->cur) then cpufreq_driver_target - //will return, and our target will not be called, it casue - //ENABLE_FURTHER_CPUFREQ flag invalid, avoid that. - policy->cur++; - cpufreq_driver_target(policy, suspend_freq, ENABLE_FURTHER_CPUFREQ | CPUFREQ_RELATION_H); - ret = NOTIFY_OK; - break; - } -out: - cpufreq_cpu_put(policy); - return ret; -} - -static struct notifier_block cpufreq_pm_notifier = { - .notifier_call = cpufreq_pm_notifier_event, -}; - -int rockchip_cpufreq_reboot_limit_freq(void) -{ - struct regulator *regulator; - int volt = 0; - u32 rate; - - dvfs_disable_temp_limit(); - dvfs_clk_enable_limit(clk_cpu_dvfs_node, 1000*suspend_freq, 1000*suspend_freq); - - rate = dvfs_clk_get_rate(clk_cpu_dvfs_node); - regulator = dvfs_get_regulator("vdd_arm"); - if (regulator) - volt = regulator_get_voltage(regulator); - else - pr_info("cpufreq: get arm regulator failed\n"); - pr_info("cpufreq: reboot set core rate=%lu, volt=%d\n", - dvfs_clk_get_rate(clk_cpu_dvfs_node), volt); - - return 0; -} - -static int cpufreq_reboot_notifier_event(struct notifier_block *this, - unsigned long event, void *ptr) +static int __init rockchip_cpufreq_driver_init(void) { - rockchip_set_system_status(SYS_STATUS_REBOOT); - rockchip_cpufreq_reboot_limit_freq(); - - return NOTIFY_OK; -} - -static struct notifier_block cpufreq_reboot_notifier = { - .notifier_call = cpufreq_reboot_notifier_event, -}; + struct platform_device *pdev; -static int clk_pd_vio_notifier_call(struct notifier_block *nb, unsigned long event, void *ptr) -{ - switch (event) { - case RK_CLK_PD_PREPARE: - if (aclk_vio1_dvfs_node) - clk_enable_dvfs(aclk_vio1_dvfs_node); - break; - case RK_CLK_PD_UNPREPARE: - if (aclk_vio1_dvfs_node) - clk_disable_dvfs(aclk_vio1_dvfs_node); - break; - } - return NOTIFY_OK; + pdev = platform_device_register_simple("cpufreq-dt", -1, NULL, 0); + return PTR_ERR_OR_ZERO(pdev); } +module_init(rockchip_cpufreq_driver_init); -static struct notifier_block clk_pd_vio_notifier = { - .notifier_call = clk_pd_vio_notifier_call, -}; - -static struct cpufreq_driver cpufreq_driver = { - .flags = CPUFREQ_CONST_LOOPS, - .verify = cpufreq_verify, - .target = cpufreq_target, - .get = cpufreq_get_rate, - .init = cpufreq_init, - .exit = cpufreq_exit, - .name = "rockchip", - .attr = cpufreq_attr, -}; - -static int __init cpufreq_driver_init(void) -{ - struct clk *clk; - - clk = clk_get(NULL, "pd_vio"); - if (clk) { - rk_clk_pd_notifier_register(clk, &clk_pd_vio_notifier); - aclk_vio1_dvfs_node = clk_get_dvfs_node("aclk_vio1"); - if (aclk_vio1_dvfs_node && __clk_is_enabled(clk)){ - clk_enable_dvfs(aclk_vio1_dvfs_node); - } - } - register_reboot_notifier(&cpufreq_reboot_notifier); - register_pm_notifier(&cpufreq_pm_notifier); - return cpufreq_register_driver(&cpufreq_driver); -} +MODULE_AUTHOR("Xiao Feng "); +MODULE_DESCRIPTION("Rockchip cpufreq driver"); +MODULE_LICENSE("GPL v2"); -device_initcall(cpufreq_driver_init);