blob: e0d8d145ec6b5be6a968c4fd918f4b52674ba6e8 [file] [log] [blame]
Praveen Chidambaramf248bb72012-01-20 11:38:44 -07001/* Copyright (c) 2012, Code Aurora Forum. All rights reserved.
2 *
3 * This program is free software; you can redistribute it and/or modify
4 * it under the terms of the GNU General Public License version 2 and
5 * only version 2 as published by the Free Software Foundation.
6 *
7 * This program is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 * GNU General Public License for more details.
11 *
12 */
13
14#include <linux/kernel.h>
15#include <linux/init.h>
16#include <linux/module.h>
17#include <linux/cpufreq.h>
18#include <linux/mutex.h>
19#include <linux/msm_tsens.h>
20#include <linux/workqueue.h>
Eugene Seah7d6d2732012-03-09 17:48:42 -070021#include <linux/cpu.h>
Praveen Chidambaramf248bb72012-01-20 11:38:44 -070022
23#define DEF_TEMP_SENSOR 0
24#define DEF_THERMAL_CHECK_MS 1000
25#define DEF_ALLOWED_MAX_HIGH 60
26#define DEF_ALLOWED_MAX_FREQ 918000
27
28static int enabled;
29static int allowed_max_high = DEF_ALLOWED_MAX_HIGH;
30static int allowed_max_low = (DEF_ALLOWED_MAX_HIGH - 10);
31static int allowed_max_freq = DEF_ALLOWED_MAX_FREQ;
32static int check_interval_ms = DEF_THERMAL_CHECK_MS;
33
34module_param(allowed_max_high, int, 0);
35module_param(allowed_max_freq, int, 0);
36module_param(check_interval_ms, int, 0);
37
Praveen Chidambaramf248bb72012-01-20 11:38:44 -070038static struct delayed_work check_temp_work;
39
Eugene Seah7d6d2732012-03-09 17:48:42 -070040static int update_cpu_max_freq(struct cpufreq_policy *cpu_policy,
41 int cpu, int max_freq)
Praveen Chidambaramf248bb72012-01-20 11:38:44 -070042{
Praveen Chidambaramf248bb72012-01-20 11:38:44 -070043 int ret = 0;
44
45 if (!cpu_policy)
46 return -EINVAL;
47
48 cpufreq_verify_within_limits(cpu_policy,
49 cpu_policy->min, max_freq);
50 cpu_policy->user_policy.max = max_freq;
51
52 ret = cpufreq_update_policy(cpu);
Praveen Chidambaramc7fc6812012-02-01 13:09:50 -070053 if (!ret)
Praveen Chidambaramf248bb72012-01-20 11:38:44 -070054 pr_info("msm_thermal: Limiting core%d max frequency to %d\n",
55 cpu, max_freq);
56
57 return ret;
58}
59
60static void check_temp(struct work_struct *work)
61{
62 struct cpufreq_policy *cpu_policy = NULL;
63 struct tsens_device tsens_dev;
64 unsigned long temp = 0;
65 unsigned int max_freq = 0;
66 int update_policy = 0;
67 int cpu = 0;
68 int ret = 0;
69
Praveen Chidambaramf248bb72012-01-20 11:38:44 -070070 tsens_dev.sensor_num = DEF_TEMP_SENSOR;
71 ret = tsens_get_temp(&tsens_dev, &temp);
72 if (ret) {
Jeff Ohlsteinf744c862012-02-01 17:52:44 -080073 pr_debug("msm_thermal: Unable to read TSENS sensor %d\n",
Praveen Chidambaramf248bb72012-01-20 11:38:44 -070074 tsens_dev.sensor_num);
75 goto reschedule;
76 }
77
78 for_each_possible_cpu(cpu) {
Eugene Seah7d6d2732012-03-09 17:48:42 -070079 update_policy = 0;
80 cpu_policy = cpufreq_cpu_get(cpu);
Praveen Chidambaramf248bb72012-01-20 11:38:44 -070081 if (!cpu_policy) {
Eugene Seah7d6d2732012-03-09 17:48:42 -070082 pr_debug("msm_thermal: NULL policy on cpu %d\n", cpu);
Praveen Chidambaramf248bb72012-01-20 11:38:44 -070083 continue;
84 }
85 if (temp >= allowed_max_high) {
86 if (cpu_policy->max > allowed_max_freq) {
87 update_policy = 1;
88 max_freq = allowed_max_freq;
Eugene Seah7d6d2732012-03-09 17:48:42 -070089 } else {
90 pr_debug("msm_thermal: policy max for cpu %d "
91 "already < allowed_max_freq\n", cpu);
Praveen Chidambaramf248bb72012-01-20 11:38:44 -070092 }
93 } else if (temp < allowed_max_low) {
94 if (cpu_policy->max < cpu_policy->cpuinfo.max_freq) {
95 max_freq = cpu_policy->cpuinfo.max_freq;
96 update_policy = 1;
Eugene Seah7d6d2732012-03-09 17:48:42 -070097 } else {
98 pr_debug("msm_thermal: policy max for cpu %d "
99 "already at max allowed\n", cpu);
Praveen Chidambaramf248bb72012-01-20 11:38:44 -0700100 }
101 }
102
103 if (update_policy)
Eugene Seah7d6d2732012-03-09 17:48:42 -0700104 update_cpu_max_freq(cpu_policy, cpu, max_freq);
105
106 cpufreq_cpu_put(cpu_policy);
Praveen Chidambaramf248bb72012-01-20 11:38:44 -0700107 }
108
109reschedule:
110 if (enabled)
111 schedule_delayed_work(&check_temp_work,
112 msecs_to_jiffies(check_interval_ms));
Praveen Chidambaramf248bb72012-01-20 11:38:44 -0700113}
114
Praveen Chidambaramf248bb72012-01-20 11:38:44 -0700115static void disable_msm_thermal(void)
116{
117 int cpu = 0;
118 struct cpufreq_policy *cpu_policy = NULL;
119
Eugene Seahcbc07532012-04-11 19:32:27 -0600120 /* make sure check_temp is no longer running */
121 cancel_delayed_work(&check_temp_work);
122 flush_scheduled_work();
123
Praveen Chidambaramf248bb72012-01-20 11:38:44 -0700124 for_each_possible_cpu(cpu) {
Eugene Seah7d6d2732012-03-09 17:48:42 -0700125 cpu_policy = cpufreq_cpu_get(cpu);
126 if (cpu_policy) {
127 if (cpu_policy->max < cpu_policy->cpuinfo.max_freq)
128 update_cpu_max_freq(cpu_policy, cpu,
129 cpu_policy->
130 cpuinfo.max_freq);
131 cpufreq_cpu_put(cpu_policy);
132 }
Praveen Chidambaramf248bb72012-01-20 11:38:44 -0700133 }
Praveen Chidambaramf248bb72012-01-20 11:38:44 -0700134}
135
136static int set_enabled(const char *val, const struct kernel_param *kp)
137{
138 int ret = 0;
139
140 ret = param_set_bool(val, kp);
141 if (!enabled)
142 disable_msm_thermal();
143 else
144 pr_info("msm_thermal: no action for enabled = %d\n", enabled);
145
146 pr_info("msm_thermal: enabled = %d\n", enabled);
147
148 return ret;
149}
150
151static struct kernel_param_ops module_ops = {
152 .set = set_enabled,
153 .get = param_get_bool,
154};
155
156module_param_cb(enabled, &module_ops, &enabled, 0644);
157MODULE_PARM_DESC(enabled, "enforce thermal limit on cpu");
158
159static int __init msm_thermal_init(void)
160{
161 int ret = 0;
162
163 enabled = 1;
Praveen Chidambaramf248bb72012-01-20 11:38:44 -0700164 INIT_DELAYED_WORK(&check_temp_work, check_temp);
165
Praveen Chidambaramf248bb72012-01-20 11:38:44 -0700166 schedule_delayed_work(&check_temp_work, 0);
167
168 return ret;
169}
170fs_initcall(msm_thermal_init);
171