cpufreq: rockchip: limit frequency when reboot

If i2c driver adds a shutdown callback function, the callback will be
executed before cpu's shutdown callback when reboot system, it will
fail to scale voltage like the following.
rk3x-i2c ff650000.i2c: Access denied - device already shutdown
rk3x-i2c ff650000.i2c: Access denied - device already shutdown
rk3x-i2c ff650000.i2c: Access denied - device already shutdown
rk3x-i2c ff650000.i2c: Access denied - device already shutdown
cpu cpu4: _set_opp_voltage: failed to set voltage
(950000 950000 1350000 mV):-5

So add a reboot notifier to limit frequency before i2c's shutdown callback,
and the cpu's shutdown callback will do nothing.

Change-Id: Ic5bb21b511c6f799dc62fd9db237d90522b7d4ee
Signed-off-by: Finley Xiao <finley.xiao@rock-chips.com>
This commit is contained in:
Finley Xiao 2017-05-16 17:00:04 +08:00
commit 43310d2dde

View file

@ -15,6 +15,7 @@
#include <linux/clk.h>
#include <linux/cpu.h>
#include <linux/cpufreq.h>
#include <linux/err.h>
#include <linux/init.h>
#include <linux/kernel.h>
@ -23,6 +24,7 @@
#include <linux/of.h>
#include <linux/platform_device.h>
#include <linux/pm_opp.h>
#include <linux/reboot.h>
#include <linux/slab.h>
#include "../clk/rockchip/clk.h"
@ -31,6 +33,8 @@
#define LEAKAGE_TABLE_END ~1
#define INVALID_VALUE 0xff
#define REBOOT_FREQ 816000 /* kHz */
struct leakage_table {
int min;
int max;
@ -44,6 +48,8 @@ struct cluster_info {
int lkg_volt_sel;
int soc_version;
bool set_opp;
unsigned int reboot_freq;
bool rebooting;
};
static LIST_HEAD(cluster_info_list);
@ -223,6 +229,9 @@ static int rockchip_cpufreq_of_parse_dt(int cpu, struct cluster_info *cluster)
}
}
if (of_property_read_u32(np, "reboot-freq", &cluster->reboot_freq))
cluster->reboot_freq = REBOOT_FREQ;
ret = rockchip_efuse_get_one_byte(np, "cpu_leakage", &cluster->leakage);
if (ret)
dev_err(dev, "Failed to get cpu_leakage\n");
@ -336,6 +345,53 @@ static struct notifier_block rockchip_hotcpu_nb = {
.notifier_call = rockchip_hotcpu_notifier,
};
static int rockchip_reboot_notifier(struct notifier_block *nb,
unsigned long action, void *ptr)
{
int cpu;
struct cluster_info *cluster;
list_for_each_entry(cluster, &cluster_info_list, list_head) {
cpu = cpumask_first_and(&cluster->cpus, cpu_online_mask);
cluster->rebooting = true;
cpufreq_update_policy(cpu);
}
return NOTIFY_OK;
}
static struct notifier_block rockchip_reboot_nb = {
.notifier_call = rockchip_reboot_notifier,
};
static int rockchip_cpufreq_notifier(struct notifier_block *nb,
unsigned long event, void *data)
{
struct cpufreq_policy *policy = data;
struct cluster_info *cluster;
if (event != CPUFREQ_ADJUST)
return NOTIFY_OK;
list_for_each_entry(cluster, &cluster_info_list, list_head) {
if (cluster->rebooting &&
cpumask_test_cpu(policy->cpu, &cluster->cpus)) {
if (cluster->reboot_freq < policy->max)
policy->max = cluster->reboot_freq;
policy->min = policy->max;
pr_info("cpu%d limit freq=%d min=%d max=%d\n",
policy->cpu, cluster->reboot_freq,
policy->min, policy->max);
}
}
return NOTIFY_OK;
}
static struct notifier_block rockchip_cpufreq_nb = {
.notifier_call = rockchip_cpufreq_notifier,
};
static int __init rockchip_cpufreq_driver_init(void)
{
struct platform_device *pdev;
@ -388,6 +444,9 @@ static int __init rockchip_cpufreq_driver_init(void)
}
register_hotcpu_notifier(&rockchip_hotcpu_nb);
register_reboot_notifier(&rockchip_reboot_nb);
cpufreq_register_notifier(&rockchip_cpufreq_nb,
CPUFREQ_POLICY_NOTIFIER);
next:
pdev = platform_device_register_simple("cpufreq-dt", -1, NULL, 0);