dvfs: add virtual temperature control

Signed-off-by: 陈亮 <cl@rock-chips.com>
This commit is contained in:
陈亮 2014-09-16 18:34:33 -07:00
commit c1a63c40f0
5 changed files with 90 additions and 19 deletions

View file

@ -829,9 +829,6 @@
};
dvfs {
temp-limit-enable = <0>;
target-temp = <80>;
vd_arm: vd_arm {
regulator_name = "vdd_arm";
pd_core {
@ -843,6 +840,8 @@
816000 1100000
1008000 1100000
>;
temp-limit-enable = <0>;
target-temp = <80>;
temp-channel = <1>;
normal-temp-limit = <
/*delta-temp delta-freq*/

View file

@ -898,8 +898,6 @@
};
dvfs {
temp-limit-enable = <1>;
target-temp = <80>;
vd_arm: vd_arm {
regulator_name = "vdd_arm";
@ -913,6 +911,8 @@
816000 1100000
1008000 1100000
>;
temp-limit-enable = <1>;
target-temp = <80>;
temp-channel = <1>;
normal-temp-limit = <
/*delta-temp delta-freq*/

View 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;
}

View 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;

View 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;