dvfs: add virtual temperature control
author陈亮 <cl@rock-chips.com>
Wed, 17 Sep 2014 01:34:33 +0000 (18:34 -0700)
committer陈亮 <cl@rock-chips.com>
Wed, 17 Sep 2014 01:34:33 +0000 (18:34 -0700)
Signed-off-by: 陈亮 <cl@rock-chips.com>
arch/arm/boot/dts/rk312x.dtsi
arch/arm/boot/dts/rk3288.dtsi
arch/arm/mach-rockchip/ddr_freq.c
arch/arm/mach-rockchip/dvfs.c
include/linux/rockchip/dvfs.h

index d0229748a9aa542c5aebfd9785a9b3bebc3294bd..05ac265b1e5da0722c1d189762facf892f1ebfcd 100755 (executable)
          };
 
          dvfs {
-               temp-limit-enable = <0>;
-               target-temp = <80>;
-
                vd_arm: vd_arm {
                        regulator_name = "vdd_arm";
                        pd_core {
                                                816000 1100000
                                                1008000 1100000
                                                >;
+                                       temp-limit-enable = <0>;
+                                       target-temp = <80>;
                                        temp-channel = <1>;
                                        normal-temp-limit = <
                                        /*delta-temp    delta-freq*/
index 51b7f061a5712a1dda3715ba31e0ba3b6bd1d967..c26b49c13b495a329ddd483ef2c98a3961550725 100755 (executable)
        };
 
        dvfs {
-               temp-limit-enable = <1>;
-               target-temp = <80>;
 
                vd_arm: vd_arm {
                        regulator_name = "vdd_arm";
                                                816000 1100000
                                                1008000 1100000
                                                >;
+                                       temp-limit-enable = <1>;
+                                       target-temp = <80>;
                                        temp-channel = <1>;
                                        normal-temp-limit = <
                                        /*delta-temp    delta-freq*/
index 5c41cf7b0520ab240616433c88b7ac0fe6c8379f..dd09a5b307356771e17f244b0aa6a7d19aceaf2c 100644 (file)
@@ -291,7 +291,6 @@ static noinline long ddrfreq_work(unsigned long sys_status)
 
        if (ddr.reboot_rate && (s & SYS_STATUS_REBOOT)) {
                ddrfreq_mode(false, ddr.reboot_rate, "shutdown/reboot");
-               rockchip_cpufreq_reboot_limit_freq();
 
                return timeout;
        }
@@ -769,6 +768,7 @@ CLK_NOTIFIER(pd_vop1, LCDC1)
 static int ddrfreq_reboot_notifier_event(struct notifier_block *this, unsigned long event, void *ptr)
 {
        rockchip_set_system_status(SYS_STATUS_REBOOT);
+       rockchip_cpufreq_reboot_limit_freq();
 
        return NOTIFY_OK;
 }
index 9647969a9543542b09051ed2b04b5c5b1d907ec4..ba676f6864644d82b6883db43c7e75f1d28d975b 100644 (file)
@@ -23,6 +23,9 @@
 #include <linux/rockchip/common.h>
 #include <linux/fb.h>
 #include <linux/reboot.h>
+#include <linux/rockchip/cpu.h>
+#include <linux/tick.h>
+#include <dt-bindings/clock/rk_system_status.h>
 #include "../../../drivers/clk/rockchip/clk-pd.h"
 
 extern int rockchip_tsadc_get_temp(int chn);
@@ -33,7 +36,7 @@ static DEFINE_MUTEX(rk_dvfs_mutex);
 static struct workqueue_struct *dvfs_wq;
 static struct dvfs_node *clk_cpu_dvfs_node;
 static unsigned int target_temp = 80;
-static int temp_limit_enable = 1;
+static int temp_limit_enable;
 
 static int pd_gpu_off, early_suspend;
 static DEFINE_MUTEX(switch_vdd_gpu_mutex);
@@ -855,6 +858,67 @@ static void dvfs_temp_limit_work_func(struct work_struct *work)
 }
 #endif
 
+static void dvfs_virt_temp_limit_work_func(void)
+{
+       const struct cpufreq_frequency_table *limits_table = NULL;
+       unsigned int new_temp_limit_rate = -1;
+       unsigned int nr_cpus = num_online_cpus();
+       static bool in_perf;
+       int i;
+
+       if (!soc_is_rk3126() && !soc_is_rk3128())
+               return;
+
+       if (rockchip_get_system_status() & SYS_STATUS_PERFORMANCE) {
+               in_perf = true;
+       } else if (in_perf) {
+               in_perf = false;
+       } else {
+               static u64 last_time_in_idle;
+               static u64 last_time_in_idle_timestamp;
+               u64 time_in_idle = 0, now;
+               u32 delta_idle;
+               u32 delta_time;
+               unsigned cpu, busy_cpus;
+
+               for_each_online_cpu(cpu) {
+                       time_in_idle += get_cpu_idle_time_us(cpu, &now);
+               }
+               delta_time = now - last_time_in_idle_timestamp;
+               delta_idle = time_in_idle - last_time_in_idle;
+               last_time_in_idle = time_in_idle;
+               last_time_in_idle_timestamp = now;
+               delta_idle += delta_time >> 4; /* +6.25% */
+               if (delta_idle > (nr_cpus - 1)
+                   * delta_time && delta_idle < (nr_cpus + 1) * delta_time)
+                       busy_cpus = 1;
+               else if (delta_idle > (nr_cpus - 2) * delta_time)
+                       busy_cpus = 2;
+               else if (delta_idle > (nr_cpus - 3) * delta_time)
+                       busy_cpus = 3;
+               else
+                       busy_cpus = 4;
+
+               limits_table = clk_cpu_dvfs_node->virt_temp_limit_table[busy_cpus-1];
+               DVFS_DBG("delta time %6u us idle %6u us %u cpus select table %d\n",
+                        delta_time, delta_idle, nr_cpus, busy_cpus);
+       }
+
+       if (limits_table) {
+               new_temp_limit_rate = limits_table[0].frequency;
+               for (i = 0; limits_table[i].frequency != CPUFREQ_TABLE_END; i++) {
+                       if (target_temp >= limits_table[i].index)
+                               new_temp_limit_rate = limits_table[i].frequency;
+               }
+       }
+
+       if (clk_cpu_dvfs_node->temp_limit_rate != new_temp_limit_rate) {
+               clk_cpu_dvfs_node->temp_limit_rate = new_temp_limit_rate;
+               dvfs_clk_set_rate(clk_cpu_dvfs_node, clk_cpu_dvfs_node->last_set_rate);
+               DVFS_DBG("temp_limit_rate:%d\n", (int)clk_cpu_dvfs_node->temp_limit_rate);
+       }
+}
+
 static void dvfs_temp_limit_work_func(struct work_struct *work)
 {
        int temp=0, delta_temp=0;
@@ -867,6 +931,9 @@ static void dvfs_temp_limit_work_func(struct work_struct *work)
 
        temp = rockchip_tsadc_get_temp(1);
 
+       if (temp == INVALID_TEMP)
+               return dvfs_virt_temp_limit_work_func();
+
        //debounce
        delta_temp = (old_temp>temp) ? (old_temp-temp) : (temp-old_temp);
        if (delta_temp <= 1)
@@ -1592,16 +1659,6 @@ int of_dvfs_init(void)
                return PTR_ERR(dvfs_dev_node);
        }
 
-       val = of_get_property(dvfs_dev_node, "target-temp", NULL);
-       if (val) {
-               target_temp = be32_to_cpup(val);
-       }
-
-       val = of_get_property(dvfs_dev_node, "temp-limit-enable", NULL);
-       if (val) {
-               temp_limit_enable = be32_to_cpup(val);
-       }
-
        for_each_available_child_of_node(dvfs_dev_node, vd_dev_node) {
                vd = kzalloc(sizeof(struct vd_node), GFP_KERNEL);
                if (!vd)
@@ -1667,13 +1724,27 @@ int of_dvfs_init(void)
                                else
                                        dvfs_node->regu_mode_table = NULL;
 
+                               val = of_get_property(clk_dev_node, "temp-limit-enable", NULL);
+                               if (val)
+                                       temp_limit_enable = be32_to_cpup(val);
                                if (temp_limit_enable) {
+                                       val = of_get_property(clk_dev_node, "target-temp", NULL);
+                                       if (val)
+                                               target_temp = be32_to_cpup(val);
                                        val = of_get_property(clk_dev_node, "temp-channel", NULL);
-                                       if (val) {
+                                       if (val)
                                                dvfs_node->temp_channel = be32_to_cpup(val);
-                                       }
+
                                        dvfs_node->nor_temp_limit_table = of_get_temp_limit_table(clk_dev_node, "normal-temp-limit");
                                        dvfs_node->per_temp_limit_table = of_get_temp_limit_table(clk_dev_node, "performance-temp-limit");
+                                       dvfs_node->virt_temp_limit_table[0] =
+                                               of_get_temp_limit_table(clk_dev_node, "virt-temp-limit-1-cpu-busy");
+                                       dvfs_node->virt_temp_limit_table[1] =
+                                               of_get_temp_limit_table(clk_dev_node, "virt-temp-limit-2-cpu-busy");
+                                       dvfs_node->virt_temp_limit_table[2] =
+                                               of_get_temp_limit_table(clk_dev_node, "virt-temp-limit-3-cpu-busy");
+                                       dvfs_node->virt_temp_limit_table[3] =
+                                               of_get_temp_limit_table(clk_dev_node, "virt-temp-limit-4-cpu-busy");
                                }
                                dvfs_node->temp_limit_rate = -1;
                                dvfs_node->dev.of_node = clk_dev_node;
index 163b9557592864d286924030cd961db154c16b68..209374a211825590c0448694b63e19f98f3b5391 100644 (file)
@@ -109,6 +109,7 @@ struct dvfs_node {
        struct cpufreq_frequency_table  *dvfs_table;
        struct cpufreq_frequency_table  *per_temp_limit_table;
        struct cpufreq_frequency_table  *nor_temp_limit_table;
+       struct cpufreq_frequency_table  *virt_temp_limit_table[4];
        clk_set_rate_callback   clk_dvfs_target;
        struct cpufreq_frequency_table  *regu_mode_table;
        int                     regu_mode_en;