cpufreq: rockchip: limit frequency when reboot
authorFinley Xiao <finley.xiao@rock-chips.com>
Tue, 16 May 2017 09:00:04 +0000 (17:00 +0800)
committerFinley Xiao <finley.xiao@rock-chips.com>
Thu, 18 May 2017 01:22:27 +0000 (09:22 +0800)
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>
drivers/cpufreq/rockchip-cpufreq.c

index 24010498730fbda5dccbb8b2c838e8d1d7212f38..52ac81e2202b67bdbfed99a0a9b572dc1474e596 100644 (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);