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:
Praveen Chidambaram
2012-05-25 17:36:07 -06:00
committed by Stephen Boyd
parent 042d742d21
commit af014a3fc0
3 changed files with 80 additions and 72 deletions

View File

@@ -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

View File

@@ -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);

View 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*/