msm_thermal module made a copy of the CPUFreq policy structure pointer but continues to try and use it after the core has been hotplugged (and the original structure is freed). The fix is to use cpufreq_cpu_get/put instead of copying cpufreq pointers. CRs-fixed: 342351 Change-Id: I312d8bccc09a5df5805f99fecdc9409d9d4ed2be Signed-off-by: Eugene Seah <eseah@codeaurora.org>
168 lines
4.1 KiB
C
168 lines
4.1 KiB
C
/* 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.
|
|
*
|
|
*/
|
|
|
|
#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
|
|
|
|
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 delayed_work check_temp_work;
|
|
|
|
static int update_cpu_max_freq(struct cpufreq_policy *cpu_policy,
|
|
int cpu, int 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 = cpufreq_update_policy(cpu);
|
|
if (!ret)
|
|
pr_info("msm_thermal: Limiting core%d max frequency to %d\n",
|
|
cpu, max_freq);
|
|
|
|
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;
|
|
int cpu = 0;
|
|
int ret = 0;
|
|
|
|
tsens_dev.sensor_num = DEF_TEMP_SENSOR;
|
|
ret = tsens_get_temp(&tsens_dev, &temp);
|
|
if (ret) {
|
|
pr_debug("msm_thermal: Unable to read TSENS sensor %d\n",
|
|
tsens_dev.sensor_num);
|
|
goto reschedule;
|
|
}
|
|
|
|
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);
|
|
}
|
|
|
|
reschedule:
|
|
if (enabled)
|
|
schedule_delayed_work(&check_temp_work,
|
|
msecs_to_jiffies(check_interval_ms));
|
|
}
|
|
|
|
static void disable_msm_thermal(void)
|
|
{
|
|
int cpu = 0;
|
|
struct cpufreq_policy *cpu_policy = NULL;
|
|
|
|
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);
|
|
}
|
|
}
|
|
}
|
|
|
|
static int set_enabled(const char *val, const struct kernel_param *kp)
|
|
{
|
|
int ret = 0;
|
|
|
|
ret = param_set_bool(val, kp);
|
|
if (!enabled)
|
|
disable_msm_thermal();
|
|
else
|
|
pr_info("msm_thermal: no action for enabled = %d\n", enabled);
|
|
|
|
pr_info("msm_thermal: enabled = %d\n", enabled);
|
|
|
|
return ret;
|
|
}
|
|
|
|
static struct kernel_param_ops module_ops = {
|
|
.set = set_enabled,
|
|
.get = param_get_bool,
|
|
};
|
|
|
|
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 ret = 0;
|
|
|
|
enabled = 1;
|
|
INIT_DELAYED_WORK(&check_temp_work, check_temp);
|
|
|
|
schedule_delayed_work(&check_temp_work, 0);
|
|
|
|
return ret;
|
|
}
|
|
fs_initcall(msm_thermal_init);
|
|
|