soc: rockchip: system_monitor: on/off cpu according to temperature

Change-Id: Ie2c55dd4c11700c0446cf0abec7e580f6abda8fe
Signed-off-by: Finley Xiao <finley.xiao@rock-chips.com>
This commit is contained in:
Finley Xiao
2019-03-20 17:01:21 +08:00
committed by Tao Huang
parent 0b38b5b1f5
commit e321bfaca5
2 changed files with 120 additions and 11 deletions

View File

@@ -8,11 +8,25 @@ Required properties:
Optional properties:
- rockchip,video-4k-offline-cpus: A string containing cpus which will be killed
when play 4k video.
- rockchip,thermal-zone: A thermal zone node containing thermal sensor,
it's used to get the current temperature.
- rockchip,polling-delay: The maximum number of milliseconds to wait
between polls.
- rockchip,offline-cpu-temp: An integer indicating the trip temperature level.
- rockchip,temp-hysteresis: A low hysteresis value on rockchip,offline-cpu-temp
property (above).
- rockchip,temp-offline-cpus: A string containing cpus which will be killed
when temperature is high.
Example:
system-monitor {
compatible = "rockchip,system-monitor";
rockchip,thermal-zone = "soc-thermal";
rockchip,polling-delay = <200>; /* milliseconds */
rockchip,offline-cpu-temp = <110000>; /* millicelsius */
rockchip,temp-offline-cpus = "2-3";
rockchip,video-4k-offline-cpus = "2-3";
};

View File

@@ -14,9 +14,11 @@
#include <linux/platform_device.h>
#include <linux/uaccess.h>
#include <linux/slab.h>
#include <linux/thermal.h>
#include <soc/rockchip/rockchip-system-status.h>
#define VIDEO_1080P_SIZE (1920 * 1080)
#define THERMAL_POLLING_DELAY 200 /* milliseconds */
struct video_info {
unsigned int width;
@@ -39,9 +41,17 @@ struct system_monitor {
struct device *dev;
struct cpumask video_4k_offline_cpus;
struct cpumask status_offline_cpus;
struct cpumask temp_offline_cpus;
struct cpumask offline_cpus;
struct notifier_block status_nb;
struct kobject *kobj;
struct thermal_zone_device *tz;
struct delayed_work thermal_work;
int offline_cpus_temp;
int temp_hysteresis;
unsigned int delay;
bool is_temp_offline;
};
static unsigned long system_status;
@@ -49,6 +59,7 @@ static unsigned long ref_count[32] = {0};
static DEFINE_MUTEX(system_status_mutex);
static DEFINE_MUTEX(video_info_mutex);
static DEFINE_MUTEX(cpu_on_off_mutex);
static LIST_HEAD(video_info_list);
static struct system_monitor *system_monitor;
@@ -354,26 +365,58 @@ static struct system_monitor_attr status =
static int rockchip_system_monitor_parse_dt(struct system_monitor *monitor)
{
struct device_node *np = monitor->dev->of_node;
const char *buf = NULL;
const char *tz_name, *buf = NULL;
if (!of_property_read_string(np, "rockchip,video-4k-offline-cpus",
&buf))
if (of_property_read_string(np, "rockchip,video-4k-offline-cpus", &buf))
cpumask_clear(&system_monitor->video_4k_offline_cpus);
else
cpulist_parse(buf, &monitor->video_4k_offline_cpus);
if (of_property_read_string(np, "rockchip,thermal-zone", &tz_name))
goto out;
monitor->tz = thermal_zone_get_zone_by_name(tz_name);
if (IS_ERR(monitor->tz)) {
monitor->tz = NULL;
goto out;
}
if (of_property_read_u32(np, "rockchip,polling-delay",
&monitor->delay))
monitor->delay = THERMAL_POLLING_DELAY;
if (of_property_read_string(np, "rockchip,temp-offline-cpus",
&buf))
cpumask_clear(&system_monitor->temp_offline_cpus);
else
cpulist_parse(buf, &system_monitor->temp_offline_cpus);
if (of_property_read_u32(np, "rockchip,offline-cpu-temp",
&system_monitor->offline_cpus_temp))
system_monitor->offline_cpus_temp = INT_MAX;
of_property_read_u32(np, "rockchip,temp-hysteresis",
&system_monitor->temp_hysteresis);
out:
return 0;
}
static void rockchip_system_monitor_cpu_on_off(void)
{
#ifdef CONFIG_HOTPLUG_CPU
struct cpumask online_cpus, offline_cpus;
unsigned int cpu;
struct cpumask online_cpus;
if (cpumask_equal(&system_monitor->offline_cpus,
&system_monitor->status_offline_cpus))
return;
cpumask_copy(&system_monitor->offline_cpus,
&system_monitor->status_offline_cpus);
mutex_lock(&cpu_on_off_mutex);
cpumask_clear(&offline_cpus);
if (system_monitor->is_temp_offline) {
cpumask_or(&offline_cpus, &system_monitor->status_offline_cpus,
&system_monitor->temp_offline_cpus);
} else {
cpumask_copy(&offline_cpus,
&system_monitor->status_offline_cpus);
}
if (cpumask_equal(&offline_cpus, &system_monitor->offline_cpus))
goto out;
cpumask_copy(&system_monitor->offline_cpus, &offline_cpus);
for_each_cpu(cpu, &system_monitor->offline_cpus) {
if (cpu_online(cpu))
cpu_down(cpu);
@@ -384,12 +427,58 @@ static void rockchip_system_monitor_cpu_on_off(void)
&system_monitor->offline_cpus);
cpumask_xor(&online_cpus, cpu_online_mask, &online_cpus);
if (cpumask_empty(&online_cpus))
return;
goto out;
for_each_cpu(cpu, &online_cpus)
cpu_up(cpu);
out:
mutex_unlock(&cpu_on_off_mutex);
#endif
}
static void rockchip_system_monitor_temp_cpu_on_off(int temp)
{
bool is_temp_offline;
if (cpumask_empty(&system_monitor->temp_offline_cpus))
return;
if (temp > system_monitor->offline_cpus_temp)
is_temp_offline = true;
else if (temp < system_monitor->offline_cpus_temp -
system_monitor->temp_hysteresis)
is_temp_offline = false;
else
return;
if (system_monitor->is_temp_offline == is_temp_offline)
return;
system_monitor->is_temp_offline = is_temp_offline;
rockchip_system_monitor_cpu_on_off();
}
static void rockchip_system_monitor_thermal_update(void)
{
int temp, ret;
ret = thermal_zone_get_temp(system_monitor->tz, &temp);
if (ret || temp == THERMAL_TEMP_INVALID)
goto out;
dev_dbg(system_monitor->dev, "temperature=%d\n", temp);
rockchip_system_monitor_temp_cpu_on_off(temp);
out:
mod_delayed_work(system_freezable_wq, &system_monitor->thermal_work,
msecs_to_jiffies(system_monitor->delay));
}
static void rockchip_system_monitor_thermal_check(struct work_struct *work)
{
rockchip_system_monitor_thermal_update();
}
static void rockchip_system_status_cpu_on_off(unsigned long status)
{
struct cpumask offline_cpus;
@@ -432,11 +521,17 @@ static int rockchip_system_monitor_probe(struct platform_device *pdev)
if (sysfs_create_file(system_monitor->kobj, &status.attr))
dev_err(dev, "failed to create system status sysfs\n");
cpumask_clear(&system_monitor->video_4k_offline_cpus);
cpumask_clear(&system_monitor->status_offline_cpus);
cpumask_clear(&system_monitor->offline_cpus);
rockchip_system_monitor_parse_dt(system_monitor);
if (system_monitor->tz) {
INIT_DELAYED_WORK(&system_monitor->thermal_work,
rockchip_system_monitor_thermal_check);
mod_delayed_work(system_freezable_wq,
&system_monitor->thermal_work,
msecs_to_jiffies(system_monitor->delay));
}
system_monitor->status_nb.notifier_call =
rockchip_system_status_notifier;