mirror of
https://github.com/hardkernel/linux.git
synced 2026-06-09 20:32:04 +09:00
rk3368: add big little cpufreq support
Signed-off-by: Xiao Feng <xf@rock-chips.com>
This commit is contained in:
@@ -878,6 +878,11 @@
|
||||
rockchip,grf = <&grf>;
|
||||
};
|
||||
|
||||
cpufreq {
|
||||
compatible = "rockchip,rk3368-cpufreq";
|
||||
rockchip,grf = <&grf>;
|
||||
};
|
||||
|
||||
dvfs {
|
||||
|
||||
vd_arm: vd_arm {
|
||||
|
||||
@@ -64,6 +64,8 @@ CONFIG_CPU_FREQ_GOV_ONDEMAND=y
|
||||
CONFIG_CPU_FREQ_GOV_CONSERVATIVE=y
|
||||
CONFIG_ARM_BIG_LITTLE_CPUFREQ=y
|
||||
CONFIG_ARM_DT_BL_CPUFREQ=y
|
||||
# CONFIG_ARM_ROCKCHIP_CPUFREQ is not set
|
||||
CONFIG_ARM_ROCKCHIP_BL_CPUFREQ=y
|
||||
CONFIG_CPU_IDLE=y
|
||||
CONFIG_ARM64_CPUIDLE=y
|
||||
CONFIG_NET=y
|
||||
|
||||
@@ -113,6 +113,13 @@ config ARM_ROCKCHIP_CPUFREQ
|
||||
This enables the CPUfreq driver for Rockchips CPUs.
|
||||
If in doubt, say Y.
|
||||
|
||||
config ARM_ROCKCHIP_BL_CPUFREQ
|
||||
bool "CPUfreq driver for Rockchip big LITTLE CPUs"
|
||||
depends on ARCH_ROCKCHIP
|
||||
help
|
||||
This enables the CPUfreq driver for Rockchips big LITTLE CPUs.
|
||||
If in doubt, say Y.
|
||||
|
||||
config ARM_S3C2416_CPUFREQ
|
||||
bool "S3C2416 CPU Frequency scaling support"
|
||||
depends on CPU_S3C2416
|
||||
|
||||
@@ -68,6 +68,7 @@ obj-$(CONFIG_PXA25x) += pxa2xx-cpufreq.o
|
||||
obj-$(CONFIG_PXA27x) += pxa2xx-cpufreq.o
|
||||
obj-$(CONFIG_PXA3xx) += pxa3xx-cpufreq.o
|
||||
obj-$(CONFIG_ARM_ROCKCHIP_CPUFREQ) += rockchip-cpufreq.o
|
||||
obj-$(CONFIG_ARM_ROCKCHIP_BL_CPUFREQ) += rockchip_big_little.o
|
||||
obj-$(CONFIG_ARM_S3C2416_CPUFREQ) += s3c2416-cpufreq.o
|
||||
obj-$(CONFIG_ARM_S3C64XX_CPUFREQ) += s3c64xx-cpufreq.o
|
||||
obj-$(CONFIG_ARM_S5PV210_CPUFREQ) += s5pv210-cpufreq.o
|
||||
|
||||
584
drivers/cpufreq/rockchip_big_little.c
Normal file
584
drivers/cpufreq/rockchip_big_little.c
Normal file
@@ -0,0 +1,584 @@
|
||||
/*
|
||||
* Copyright (C) 2015 Fuzhou Rockchip Electronics Co., Ltd
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
*/
|
||||
#define pr_fmt(fmt) "cpufreq: " fmt
|
||||
#include <linux/clk.h>
|
||||
#include <linux/cpufreq.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/kernel_stat.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/reboot.h>
|
||||
#include <linux/suspend.h>
|
||||
#include <linux/tick.h>
|
||||
#include <linux/workqueue.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/regulator/consumer.h>
|
||||
#include <linux/fs.h>
|
||||
#include <linux/miscdevice.h>
|
||||
#include <linux/string.h>
|
||||
#include <linux/rockchip/cpu.h>
|
||||
#include <linux/rockchip/dvfs.h>
|
||||
#include <asm/smp_plat.h>
|
||||
#include <asm/unistd.h>
|
||||
#include <linux/uaccess.h>
|
||||
#include <asm/system_misc.h>
|
||||
#include <linux/cpu.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/mfd/syscon.h>
|
||||
#include <linux/regmap.h>
|
||||
#include <linux/rockchip/common.h>
|
||||
#include <dt-bindings/clock/rk_system_status.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/module.h>
|
||||
#include "../../../drivers/clk/rockchip/clk-pd.h"
|
||||
|
||||
#define RK3368_GRF_CPU_CON(n) (0x500 + 4*n)
|
||||
|
||||
|
||||
#define VERSION "1.0"
|
||||
#define RK_MAX_CLUSTERS 2
|
||||
|
||||
#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[RK_MAX_CLUSTERS + 1];
|
||||
/*********************************************************/
|
||||
/* 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[RK_MAX_CLUSTERS] = {0};
|
||||
static unsigned int suspend_freq[RK_MAX_CLUSTERS] = {816 * 1000, 816 * 1000};
|
||||
static unsigned int suspend_volt = 1100000;
|
||||
static unsigned int low_battery_freq[RK_MAX_CLUSTERS] = {600 * 1000,
|
||||
600 * 1000};
|
||||
static unsigned int low_battery_capacity = 5;
|
||||
static bool is_booting = true;
|
||||
static DEFINE_MUTEX(cpufreq_mutex);
|
||||
static struct dvfs_node *clk_cpu_dvfs_node[RK_MAX_CLUSTERS];
|
||||
static struct dvfs_node *clk_gpu_dvfs_node;
|
||||
static struct dvfs_node *clk_ddr_dvfs_node;
|
||||
static u32 cluster_policy_cpu[RK_MAX_CLUSTERS];
|
||||
static unsigned int big_little = 1;
|
||||
|
||||
/*******************************************************/
|
||||
static int cpu_to_cluster(int cpu)
|
||||
{
|
||||
return topology_physical_package_id(cpu);
|
||||
}
|
||||
|
||||
static unsigned int cpufreq_bl_get_rate(unsigned int cpu)
|
||||
{
|
||||
u32 cur_cluster = cpu_to_cluster(cpu);
|
||||
|
||||
if (clk_cpu_dvfs_node[cur_cluster])
|
||||
return clk_get_rate(clk_cpu_dvfs_node[cur_cluster]->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 cluster)
|
||||
{
|
||||
unsigned int i;
|
||||
unsigned int target_freq = 0;
|
||||
|
||||
for (i = 0; freq_table[cluster][i].frequency !=
|
||||
CPUFREQ_TABLE_END; i++) {
|
||||
unsigned int freq = freq_table[cluster][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;
|
||||
u32 cur_cluster = cpu_to_cluster(policy->cpu);
|
||||
|
||||
if (val != CPUFREQ_ADJUST)
|
||||
return 0;
|
||||
|
||||
if (cpufreq_is_ondemand(policy)) {
|
||||
FREQ_DBG("queue work\n");
|
||||
dvfs_clk_enable_limit(clk_cpu_dvfs_node[cur_cluster],
|
||||
min_rate, max_rate);
|
||||
} else {
|
||||
FREQ_DBG("cancel work\n");
|
||||
dvfs_clk_get_limit(clk_cpu_dvfs_node[cur_cluster],
|
||||
&min_rate, &max_rate);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct notifier_block notifier_policy_block = {
|
||||
.notifier_call = cpufreq_notifier_policy
|
||||
};
|
||||
|
||||
static int cpufreq_bl_verify(struct cpufreq_policy *policy)
|
||||
{
|
||||
u32 cur_cluster = cpu_to_cluster(policy->cpu);
|
||||
|
||||
if (!freq_table[cur_cluster])
|
||||
return -EINVAL;
|
||||
return cpufreq_frequency_table_verify(policy, freq_table[cur_cluster]);
|
||||
}
|
||||
|
||||
static int clk_node_get_cluster_id(struct clk *clk)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < RK_MAX_CLUSTERS; i++) {
|
||||
if (clk_cpu_dvfs_node[i]->clk == clk)
|
||||
return i;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int cpufreq_bl_scale_rate_for_dvfs(struct clk *clk, unsigned long rate)
|
||||
{
|
||||
int ret;
|
||||
struct cpufreq_freqs freqs;
|
||||
struct cpufreq_policy *policy;
|
||||
u32 cur_cluster;
|
||||
|
||||
cur_cluster = clk_node_get_cluster_id(clk);
|
||||
policy = cpufreq_cpu_get(cluster_policy_cpu[cur_cluster]);
|
||||
|
||||
freqs.new = rate / 1000;
|
||||
freqs.old = clk_get_rate(clk) / 1000;
|
||||
|
||||
cpufreq_notify_transition(policy, &freqs, CPUFREQ_PRECHANGE);
|
||||
|
||||
FREQ_DBG("cpufreq_scale_rate_for_dvfs(%lu)\n", rate);
|
||||
|
||||
ret = clk_set_rate(clk, rate);
|
||||
|
||||
freqs.new = clk_get_rate(clk) / 1000;
|
||||
/* notifiers */
|
||||
cpufreq_notify_transition(policy, &freqs, CPUFREQ_POSTCHANGE);
|
||||
|
||||
cpufreq_cpu_put(policy);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int cluster_cpus_freq_dvfs_init(u32 cluster_id, char *dvfs_name)
|
||||
{
|
||||
clk_cpu_dvfs_node[cluster_id] = clk_get_dvfs_node(dvfs_name);
|
||||
|
||||
if (!clk_cpu_dvfs_node[cluster_id]) {
|
||||
FREQ_ERR("%s:cluster_id=%d,get dvfs err\n",
|
||||
__func__, cluster_id);
|
||||
return -EINVAL;
|
||||
}
|
||||
dvfs_clk_register_set_rate_callback(clk_cpu_dvfs_node[cluster_id],
|
||||
cpufreq_bl_scale_rate_for_dvfs);
|
||||
freq_table[cluster_id] =
|
||||
dvfs_get_freq_volt_table(clk_cpu_dvfs_node[cluster_id]);
|
||||
if (freq_table[cluster_id] == NULL) {
|
||||
freq_table[cluster_id] = default_freq_table;
|
||||
} else {
|
||||
int v = INT_MAX;
|
||||
int i;
|
||||
|
||||
for (i = 0; freq_table[cluster_id][i].frequency !=
|
||||
CPUFREQ_TABLE_END; i++) {
|
||||
if (freq_table[cluster_id][i].index >= suspend_volt &&
|
||||
v > freq_table[cluster_id][i].index) {
|
||||
suspend_freq[cluster_id] =
|
||||
freq_table[cluster_id][i].frequency;
|
||||
v = freq_table[cluster_id][i].index;
|
||||
}
|
||||
}
|
||||
}
|
||||
low_battery_freq[cluster_id] =
|
||||
get_freq_from_table(low_battery_freq[cluster_id], cluster_id);
|
||||
clk_enable_dvfs(clk_cpu_dvfs_node[cluster_id]);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int cpufreq_bl_init_cpu0(struct cpufreq_policy *policy)
|
||||
{
|
||||
clk_gpu_dvfs_node = clk_get_dvfs_node("clk_gpu");
|
||||
if (clk_gpu_dvfs_node)
|
||||
clk_enable_dvfs(clk_gpu_dvfs_node);
|
||||
|
||||
clk_ddr_dvfs_node = clk_get_dvfs_node("clk_ddr");
|
||||
if (clk_ddr_dvfs_node)
|
||||
clk_enable_dvfs(clk_ddr_dvfs_node);
|
||||
|
||||
if (big_little == 1) {
|
||||
cluster_cpus_freq_dvfs_init(0, "clk_core_b");
|
||||
cluster_cpus_freq_dvfs_init(1, "clk_core_l");
|
||||
} else {
|
||||
cluster_cpus_freq_dvfs_init(0, "clk_core_l");
|
||||
cluster_cpus_freq_dvfs_init(1, "clk_core_b");
|
||||
}
|
||||
|
||||
cpufreq_register_notifier(¬ifier_policy_block,
|
||||
CPUFREQ_POLICY_NOTIFIER);
|
||||
|
||||
pr_info("cpufreq version " VERSION ", suspend freq %d %d MHz\n",
|
||||
suspend_freq[0] / 1000, suspend_freq[1] / 1000);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int cpufreq_bl_init(struct cpufreq_policy *policy)
|
||||
{
|
||||
static int cpu0_err;
|
||||
u32 cur_cluster = cpu_to_cluster(policy->cpu);
|
||||
|
||||
if (policy->cpu == 0)
|
||||
cpu0_err = cpufreq_bl_init_cpu0(policy);
|
||||
if (cpu0_err)
|
||||
return cpu0_err;
|
||||
|
||||
cluster_policy_cpu[cur_cluster] = policy->cpu;
|
||||
|
||||
/* set freq min max */
|
||||
cpufreq_frequency_table_cpuinfo(policy, freq_table[cur_cluster]);
|
||||
/* sys nod */
|
||||
cpufreq_frequency_table_get_attr(freq_table[cur_cluster], policy->cpu);
|
||||
|
||||
if (cur_cluster < RK_MAX_CLUSTERS) {
|
||||
/* int cpu; */
|
||||
cpumask_copy(policy->cpus, topology_core_cpumask(policy->cpu));
|
||||
/* for_each_cpu(cpu, policy->cpus) {
|
||||
pr_info("%s:policy->cpu=%d,cpu=%d,%02x\n",
|
||||
__func__, policy->cpu, cpu, policy->cpus[0]);
|
||||
} */
|
||||
}
|
||||
|
||||
policy->cur = clk_get_rate(clk_cpu_dvfs_node[cur_cluster]->clk) / 1000;
|
||||
|
||||
/* make ondemand default sampling_rate to 40000 */
|
||||
policy->cpuinfo.transition_latency = 40 * NSEC_PER_USEC;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int cpufreq_bl_exit(struct cpufreq_policy *policy)
|
||||
{
|
||||
u32 cur_cluster = cpu_to_cluster(policy->cpu);
|
||||
|
||||
if (policy->cpu == 0) {
|
||||
cpufreq_unregister_notifier(¬ifier_policy_block,
|
||||
CPUFREQ_POLICY_NOTIFIER);
|
||||
}
|
||||
cpufreq_frequency_table_cpuinfo(policy, freq_table[cur_cluster]);
|
||||
clk_put_dvfs_node(clk_cpu_dvfs_node[cur_cluster]);
|
||||
|
||||
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_bl_scale_limit(unsigned int target_freq,
|
||||
struct cpufreq_policy *policy,
|
||||
bool is_private)
|
||||
{
|
||||
bool is_ondemand = cpufreq_is_ondemand(policy);
|
||||
u32 cur_cluster = cpu_to_cluster(policy->cpu);
|
||||
|
||||
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[cur_cluster] &&
|
||||
rk_get_system_battery_capacity() <=
|
||||
low_battery_capacity) {
|
||||
target_freq = low_battery_freq[cur_cluster];
|
||||
}
|
||||
}
|
||||
|
||||
return target_freq;
|
||||
}
|
||||
|
||||
static int cpufreq_bl_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;
|
||||
u32 cur_cluster = cpu_to_cluster(policy->cpu);
|
||||
|
||||
if (!freq_table[cur_cluster]) {
|
||||
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[cur_cluster])
|
||||
no_cpufreq_access[cur_cluster]--;
|
||||
if (no_cpufreq_access[cur_cluster]) {
|
||||
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[cur_cluster]++;
|
||||
relation &= ~MASK_FURTHER_CPUFREQ;
|
||||
|
||||
ret = cpufreq_frequency_table_target(policy, freq_table[cur_cluster],
|
||||
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[cur_cluster][i].frequency;
|
||||
if (!no_cpufreq_access[cur_cluster])
|
||||
new_freq = cpufreq_bl_scale_limit(new_freq, policy, is_private);
|
||||
|
||||
new_rate = new_freq * 1000;
|
||||
cur_rate = dvfs_clk_get_rate(clk_cpu_dvfs_node[cur_cluster]);
|
||||
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[cur_cluster], 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;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < RK_MAX_CLUSTERS; i++) {
|
||||
struct cpufreq_policy *policy =
|
||||
cpufreq_cpu_get(cluster_policy_cpu[i]);
|
||||
|
||||
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[i],
|
||||
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[i],
|
||||
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,
|
||||
};
|
||||
|
||||
static int rockchip_bl_cpufreq_reboot_limit_freq(void)
|
||||
{
|
||||
struct regulator *regulator;
|
||||
int volt = 0;
|
||||
u32 rate;
|
||||
int i;
|
||||
|
||||
dvfs_disable_temp_limit();
|
||||
|
||||
for (i = 0; i < RK_MAX_CLUSTERS; i++) {
|
||||
dvfs_clk_enable_limit(clk_cpu_dvfs_node[i],
|
||||
1000*suspend_freq[i],
|
||||
1000*suspend_freq[i]);
|
||||
rate = dvfs_clk_get_rate(clk_cpu_dvfs_node[i]);
|
||||
}
|
||||
|
||||
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 cluster0 rate=%lu, cluster1 rate=%lu, volt=%d\n",
|
||||
dvfs_clk_get_rate(clk_cpu_dvfs_node[0]),
|
||||
dvfs_clk_get_rate(clk_cpu_dvfs_node[1]), volt);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int cpufreq_reboot_notifier_event(struct notifier_block *this,
|
||||
unsigned long event, void *ptr)
|
||||
{
|
||||
rockchip_set_system_status(SYS_STATUS_REBOOT);
|
||||
rockchip_bl_cpufreq_reboot_limit_freq();
|
||||
|
||||
return NOTIFY_OK;
|
||||
};
|
||||
|
||||
static struct notifier_block cpufreq_reboot_notifier = {
|
||||
.notifier_call = cpufreq_reboot_notifier_event,
|
||||
};
|
||||
|
||||
static struct cpufreq_driver cpufreq_driver = {
|
||||
.flags = CPUFREQ_CONST_LOOPS,
|
||||
.verify = cpufreq_bl_verify,
|
||||
.target = cpufreq_bl_target,
|
||||
.get = cpufreq_bl_get_rate,
|
||||
.init = cpufreq_bl_init,
|
||||
.exit = cpufreq_bl_exit,
|
||||
.name = "rockchip-big-little",
|
||||
.have_governor_per_policy = true,
|
||||
.attr = cpufreq_attr,
|
||||
};
|
||||
|
||||
static const struct of_device_id cpufreq_match[] = {
|
||||
{
|
||||
.compatible = "rockchip,rk3368-cpufreq",
|
||||
},
|
||||
{},
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, cpufreq_match);
|
||||
|
||||
static int cpufreq_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device_node *np;
|
||||
struct regmap *grf_regmap;
|
||||
unsigned int big_bits, litt_bits;
|
||||
int ret;
|
||||
|
||||
np = pdev->dev.of_node;
|
||||
if (!np)
|
||||
return -ENODEV;
|
||||
|
||||
grf_regmap = syscon_regmap_lookup_by_phandle(np, "rockchip,grf");
|
||||
if (IS_ERR(grf_regmap)) {
|
||||
FREQ_ERR("Cpufreq couldn't find grf regmap\n");
|
||||
return PTR_ERR(grf_regmap);
|
||||
}
|
||||
ret = regmap_read(grf_regmap, RK3368_GRF_CPU_CON(3), &big_bits);
|
||||
if (ret != 0) {
|
||||
FREQ_ERR("Cpufreq couldn't read to GRF\n");
|
||||
return -1;
|
||||
}
|
||||
ret = regmap_read(grf_regmap, RK3368_GRF_CPU_CON(1),
|
||||
&litt_bits);
|
||||
if (ret != 0) {
|
||||
FREQ_ERR("Cpufreq couldn't read to GRF\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
big_bits = (big_bits >> 8) & 0x03;
|
||||
litt_bits = (litt_bits >> 8) & 0x03;
|
||||
|
||||
if (big_bits == 0x01 && litt_bits == 0x00)
|
||||
big_little = 1;
|
||||
else if (big_bits == 0x0 && litt_bits == 0x01)
|
||||
big_little = 0;
|
||||
pr_info("cpufreq: boot from %d\n", big_little);
|
||||
|
||||
register_reboot_notifier(&cpufreq_reboot_notifier);
|
||||
register_pm_notifier(&cpufreq_pm_notifier);
|
||||
|
||||
return cpufreq_register_driver(&cpufreq_driver);
|
||||
}
|
||||
|
||||
static int cpufreq_remove(struct platform_device *pdev)
|
||||
{
|
||||
cpufreq_unregister_driver(&cpufreq_driver);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct platform_driver cpufreq_platdrv = {
|
||||
.driver = {
|
||||
.name = "rockchip-bl-cpufreq",
|
||||
.owner = THIS_MODULE,
|
||||
.of_match_table = cpufreq_match,
|
||||
},
|
||||
.probe = cpufreq_probe,
|
||||
.remove = cpufreq_remove,
|
||||
};
|
||||
module_platform_driver(cpufreq_platdrv);
|
||||
|
||||
MODULE_AUTHOR("Xiao Feng <xf@rock-chips.com>");
|
||||
MODULE_LICENSE("GPL");
|
||||
Reference in New Issue
Block a user