msm: thermal: Simplify kernel thermal safeguard mechanism
Using CPUFreq policy objects and setting the max frequency limit by overriding the policy->max node, leads to race/overwrite conditions with an user trying to use the scaling_max_frequency node. The thermal limits are directly communicated to the 'msm' cpufreq driver and use the cpufreq_update_policy() to ensure that the frequency is limited as per the thermal safeguard requirements. CRs-fixed: 370343 Change-Id: Iab5a15e0f0d25da4b9f6a9417dbfc01bf5d6f8f6 Signed-off-by: Praveen Chidambaram <pchidamb@codeaurora.org>
This commit is contained in:
committed by
Stephen Boyd
parent
042d742d21
commit
af014a3fc0
@@ -66,6 +66,7 @@ config THERMAL_PM8XXX
|
||||
config THERMAL_MONITOR
|
||||
bool "Monitor thermal state and limit CPU Frequency"
|
||||
depends on THERMAL_TSENS8960
|
||||
depends on CPU_FREQ_MSM
|
||||
default n
|
||||
help
|
||||
This enables thermal monitoring capability in the kernel in the
|
||||
|
||||
@@ -14,60 +14,51 @@
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/cpufreq.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/msm_tsens.h>
|
||||
#include <linux/workqueue.h>
|
||||
#include <linux/cpu.h>
|
||||
|
||||
#define DEF_TEMP_SENSOR 0
|
||||
#define DEF_THERMAL_CHECK_MS 1000
|
||||
#define DEF_ALLOWED_MAX_HIGH 60
|
||||
#define DEF_ALLOWED_MAX_FREQ 918000
|
||||
#include <linux/cpufreq.h>
|
||||
#include <linux/msm_tsens.h>
|
||||
#include <linux/msm_thermal.h>
|
||||
#include <mach/cpufreq.h>
|
||||
|
||||
static int enabled;
|
||||
static int allowed_max_high = DEF_ALLOWED_MAX_HIGH;
|
||||
static int allowed_max_low = (DEF_ALLOWED_MAX_HIGH - 10);
|
||||
static int allowed_max_freq = DEF_ALLOWED_MAX_FREQ;
|
||||
static int check_interval_ms = DEF_THERMAL_CHECK_MS;
|
||||
|
||||
module_param(allowed_max_high, int, 0);
|
||||
module_param(allowed_max_freq, int, 0);
|
||||
module_param(check_interval_ms, int, 0);
|
||||
|
||||
static struct msm_thermal_data msm_thermal_info;
|
||||
static uint32_t limited_max_freq = MSM_CPUFREQ_NO_LIMIT;
|
||||
static struct delayed_work check_temp_work;
|
||||
|
||||
static int update_cpu_max_freq(struct cpufreq_policy *cpu_policy,
|
||||
int cpu, int max_freq)
|
||||
static int update_cpu_max_freq(int cpu, uint32_t max_freq)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
if (!cpu_policy)
|
||||
return -EINVAL;
|
||||
|
||||
cpufreq_verify_within_limits(cpu_policy,
|
||||
cpu_policy->min, max_freq);
|
||||
cpu_policy->user_policy.max = max_freq;
|
||||
ret = msm_cpufreq_set_freq_limits(cpu, MSM_CPUFREQ_NO_LIMIT, max_freq);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = cpufreq_update_policy(cpu);
|
||||
if (!ret)
|
||||
pr_info("msm_thermal: Limiting core%d max frequency to %d\n",
|
||||
cpu, max_freq);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
limited_max_freq = max_freq;
|
||||
if (max_freq != MSM_CPUFREQ_NO_LIMIT)
|
||||
pr_info("msm_thermal: Limiting cpu%d max frequency to %d\n",
|
||||
cpu, max_freq);
|
||||
else
|
||||
pr_info("msm_thermal: Max frequency reset for cpu%d\n", cpu);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void check_temp(struct work_struct *work)
|
||||
{
|
||||
struct cpufreq_policy *cpu_policy = NULL;
|
||||
struct tsens_device tsens_dev;
|
||||
unsigned long temp = 0;
|
||||
unsigned int max_freq = 0;
|
||||
int update_policy = 0;
|
||||
uint32_t max_freq = limited_max_freq;
|
||||
int cpu = 0;
|
||||
int ret = 0;
|
||||
|
||||
tsens_dev.sensor_num = DEF_TEMP_SENSOR;
|
||||
tsens_dev.sensor_num = msm_thermal_info.sensor_id;
|
||||
ret = tsens_get_temp(&tsens_dev, &temp);
|
||||
if (ret) {
|
||||
pr_debug("msm_thermal: Unable to read TSENS sensor %d\n",
|
||||
@@ -75,61 +66,42 @@ static void check_temp(struct work_struct *work)
|
||||
goto reschedule;
|
||||
}
|
||||
|
||||
if (temp >= msm_thermal_info.limit_temp)
|
||||
max_freq = msm_thermal_info.limit_freq;
|
||||
else if (temp <
|
||||
msm_thermal_info.limit_temp - msm_thermal_info.temp_hysteresis)
|
||||
max_freq = MSM_CPUFREQ_NO_LIMIT;
|
||||
|
||||
if (max_freq == limited_max_freq)
|
||||
goto reschedule;
|
||||
|
||||
/* Update new limits */
|
||||
for_each_possible_cpu(cpu) {
|
||||
update_policy = 0;
|
||||
cpu_policy = cpufreq_cpu_get(cpu);
|
||||
if (!cpu_policy) {
|
||||
pr_debug("msm_thermal: NULL policy on cpu %d\n", cpu);
|
||||
continue;
|
||||
}
|
||||
if (temp >= allowed_max_high) {
|
||||
if (cpu_policy->max > allowed_max_freq) {
|
||||
update_policy = 1;
|
||||
max_freq = allowed_max_freq;
|
||||
} else {
|
||||
pr_debug("msm_thermal: policy max for cpu %d "
|
||||
"already < allowed_max_freq\n", cpu);
|
||||
}
|
||||
} else if (temp < allowed_max_low) {
|
||||
if (cpu_policy->max < cpu_policy->cpuinfo.max_freq) {
|
||||
max_freq = cpu_policy->cpuinfo.max_freq;
|
||||
update_policy = 1;
|
||||
} else {
|
||||
pr_debug("msm_thermal: policy max for cpu %d "
|
||||
"already at max allowed\n", cpu);
|
||||
}
|
||||
}
|
||||
|
||||
if (update_policy)
|
||||
update_cpu_max_freq(cpu_policy, cpu, max_freq);
|
||||
|
||||
cpufreq_cpu_put(cpu_policy);
|
||||
ret = update_cpu_max_freq(cpu, max_freq);
|
||||
if (ret)
|
||||
pr_debug("Unable to limit cpu%d max freq to %d\n",
|
||||
cpu, max_freq);
|
||||
}
|
||||
|
||||
reschedule:
|
||||
if (enabled)
|
||||
schedule_delayed_work(&check_temp_work,
|
||||
msecs_to_jiffies(check_interval_ms));
|
||||
msecs_to_jiffies(msm_thermal_info.poll_ms));
|
||||
}
|
||||
|
||||
static void disable_msm_thermal(void)
|
||||
{
|
||||
int cpu = 0;
|
||||
struct cpufreq_policy *cpu_policy = NULL;
|
||||
|
||||
/* make sure check_temp is no longer running */
|
||||
cancel_delayed_work(&check_temp_work);
|
||||
flush_scheduled_work();
|
||||
|
||||
if (limited_max_freq == MSM_CPUFREQ_NO_LIMIT)
|
||||
return;
|
||||
|
||||
for_each_possible_cpu(cpu) {
|
||||
cpu_policy = cpufreq_cpu_get(cpu);
|
||||
if (cpu_policy) {
|
||||
if (cpu_policy->max < cpu_policy->cpuinfo.max_freq)
|
||||
update_cpu_max_freq(cpu_policy, cpu,
|
||||
cpu_policy->
|
||||
cpuinfo.max_freq);
|
||||
cpufreq_cpu_put(cpu_policy);
|
||||
}
|
||||
update_cpu_max_freq(cpu, MSM_CPUFREQ_NO_LIMIT);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -156,16 +128,17 @@ static struct kernel_param_ops module_ops = {
|
||||
module_param_cb(enabled, &module_ops, &enabled, 0644);
|
||||
MODULE_PARM_DESC(enabled, "enforce thermal limit on cpu");
|
||||
|
||||
static int __init msm_thermal_init(void)
|
||||
int __init msm_thermal_init(struct msm_thermal_data *pdata)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
BUG_ON(!pdata);
|
||||
BUG_ON(pdata->sensor_id >= TSENS_MAX_SENSORS);
|
||||
memcpy(&msm_thermal_info, pdata, sizeof(struct msm_thermal_data));
|
||||
|
||||
enabled = 1;
|
||||
INIT_DELAYED_WORK(&check_temp_work, check_temp);
|
||||
|
||||
schedule_delayed_work(&check_temp_work, 0);
|
||||
|
||||
return ret;
|
||||
}
|
||||
fs_initcall(msm_thermal_init);
|
||||
|
||||
|
||||
34
include/linux/msm_thermal.h
Normal file
34
include/linux/msm_thermal.h
Normal file
@@ -0,0 +1,34 @@
|
||||
/*
|
||||
* Copyright (c) 2012, Code Aurora Forum. All rights reserved.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 and
|
||||
* only version 2 as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*/
|
||||
|
||||
#ifndef __MSM_THERMAL_H
|
||||
#define __MSM_THERMAL_H
|
||||
|
||||
struct msm_thermal_data {
|
||||
uint32_t sensor_id;
|
||||
uint32_t poll_ms;
|
||||
uint32_t limit_temp;
|
||||
uint32_t temp_hysteresis;
|
||||
uint32_t limit_freq;
|
||||
};
|
||||
|
||||
#ifdef CONFIG_THERMAL_MONITOR
|
||||
extern int msm_thermal_init(struct msm_thermal_data *pdata);
|
||||
#else
|
||||
static inline int msm_thermal_init(struct msm_thermal_data *pdata)
|
||||
{
|
||||
return -ENOSYS;
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /*__MSM_THERMAL_H*/
|
||||
Reference in New Issue
Block a user