summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorFinley Xiao <finley.xiao@rock-chips.com>2017-05-16 17:00:04 +0800
committerFinley Xiao <finley.xiao@rock-chips.com>2017-05-18 09:22:27 +0800
commit43310d2ddec79da73c313937e304b1e784c184a2 (patch)
tree48c6a636b9ce9f1d520ef2a30dbb605f3920f21e
parentcdcf4ffeabb8a372ed40e9303d9f6b0229706f0f (diff)
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>
-rw-r--r--drivers/cpufreq/rockchip-cpufreq.c59
1 files changed, 59 insertions, 0 deletions
diff --git a/drivers/cpufreq/rockchip-cpufreq.c b/drivers/cpufreq/rockchip-cpufreq.c
index 24010498730f..52ac81e2202b 100644
--- a/drivers/cpufreq/rockchip-cpufreq.c
+++ b/drivers/cpufreq/rockchip-cpufreq.c
@@ -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);