blob: 0e3daeb70ae49bde95eb4176835133f047cf6534 [file] [log] [blame]
Duy Truong790f06d2013-02-13 16:38:12 -08001/* Copyright (c) 2012, The Linux Foundation. All rights reserved.
Praveen Chidambaramf248bb72012-01-20 11:38:44 -07002 *
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>
Praveen Chidambaramf248bb72012-01-20 11:38:44 -070017#include <linux/mutex.h>
18#include <linux/msm_tsens.h>
19#include <linux/workqueue.h>
Eugene Seah7d6d2732012-03-09 17:48:42 -070020#include <linux/cpu.h>
Praveen Chidambaram91814362012-05-25 17:36:07 -060021#include <linux/cpufreq.h>
22#include <linux/msm_tsens.h>
23#include <linux/msm_thermal.h>
Eugene Seahb77b0c42012-07-02 19:28:50 -060024#include <linux/platform_device.h>
25#include <linux/of.h>
Praveen Chidambaram91814362012-05-25 17:36:07 -060026#include <mach/cpufreq.h>
Praveen Chidambaramf248bb72012-01-20 11:38:44 -070027
28static int enabled;
Praveen Chidambaram91814362012-05-25 17:36:07 -060029static struct msm_thermal_data msm_thermal_info;
30static uint32_t limited_max_freq = MSM_CPUFREQ_NO_LIMIT;
Praveen Chidambaramf248bb72012-01-20 11:38:44 -070031static struct delayed_work check_temp_work;
32
Eugene Seah2ee4a5d2012-06-25 18:16:41 -060033static int limit_idx;
34static int limit_idx_low;
35static int limit_idx_high;
36static struct cpufreq_frequency_table *table;
37
38static int msm_thermal_get_freq_table(void)
39{
40 int ret = 0;
41 int i = 0;
42
43 table = cpufreq_frequency_get_table(0);
44 if (table == NULL) {
45 pr_debug("%s: error reading cpufreq table\n", __func__);
46 ret = -EINVAL;
47 goto fail;
48 }
49
50 while (table[i].frequency != CPUFREQ_TABLE_END)
51 i++;
52
53 limit_idx_low = 0;
54 limit_idx_high = limit_idx = i - 1;
55 BUG_ON(limit_idx_high <= 0 || limit_idx_high <= limit_idx_low);
56fail:
57 return ret;
58}
59
Praveen Chidambaram91814362012-05-25 17:36:07 -060060static int update_cpu_max_freq(int cpu, uint32_t max_freq)
Praveen Chidambaramf248bb72012-01-20 11:38:44 -070061{
Praveen Chidambaramf248bb72012-01-20 11:38:44 -070062 int ret = 0;
63
Praveen Chidambaram91814362012-05-25 17:36:07 -060064 ret = msm_cpufreq_set_freq_limits(cpu, MSM_CPUFREQ_NO_LIMIT, max_freq);
65 if (ret)
66 return ret;
Praveen Chidambaramf248bb72012-01-20 11:38:44 -070067
Praveen Chidambaram91814362012-05-25 17:36:07 -060068 limited_max_freq = max_freq;
69 if (max_freq != MSM_CPUFREQ_NO_LIMIT)
70 pr_info("msm_thermal: Limiting cpu%d max frequency to %d\n",
71 cpu, max_freq);
72 else
73 pr_info("msm_thermal: Max frequency reset for cpu%d\n", cpu);
Praveen Chidambaramf248bb72012-01-20 11:38:44 -070074
Eugene Seah2ee4a5d2012-06-25 18:16:41 -060075 ret = cpufreq_update_policy(cpu);
76
Praveen Chidambaramf248bb72012-01-20 11:38:44 -070077 return ret;
78}
79
80static void check_temp(struct work_struct *work)
81{
Eugene Seah2ee4a5d2012-06-25 18:16:41 -060082 static int limit_init;
Praveen Chidambaramf248bb72012-01-20 11:38:44 -070083 struct tsens_device tsens_dev;
84 unsigned long temp = 0;
Praveen Chidambaram91814362012-05-25 17:36:07 -060085 uint32_t max_freq = limited_max_freq;
Praveen Chidambaramf248bb72012-01-20 11:38:44 -070086 int cpu = 0;
87 int ret = 0;
88
Praveen Chidambaram91814362012-05-25 17:36:07 -060089 tsens_dev.sensor_num = msm_thermal_info.sensor_id;
Praveen Chidambaramf248bb72012-01-20 11:38:44 -070090 ret = tsens_get_temp(&tsens_dev, &temp);
91 if (ret) {
Jeff Ohlsteinf744c862012-02-01 17:52:44 -080092 pr_debug("msm_thermal: Unable to read TSENS sensor %d\n",
Praveen Chidambaramf248bb72012-01-20 11:38:44 -070093 tsens_dev.sensor_num);
94 goto reschedule;
95 }
96
Eugene Seah2ee4a5d2012-06-25 18:16:41 -060097 if (!limit_init) {
98 ret = msm_thermal_get_freq_table();
99 if (ret)
100 goto reschedule;
101 else
102 limit_init = 1;
103 }
Praveen Chidambaram91814362012-05-25 17:36:07 -0600104
Eugene Seah2ee4a5d2012-06-25 18:16:41 -0600105 if (temp >= msm_thermal_info.limit_temp_degC) {
106 if (limit_idx == limit_idx_low)
107 goto reschedule;
108
109 limit_idx -= msm_thermal_info.freq_step;
110 if (limit_idx < limit_idx_low)
111 limit_idx = limit_idx_low;
112 max_freq = table[limit_idx].frequency;
113 } else if (temp < msm_thermal_info.limit_temp_degC -
114 msm_thermal_info.temp_hysteresis_degC) {
115 if (limit_idx == limit_idx_high)
116 goto reschedule;
117
118 limit_idx += msm_thermal_info.freq_step;
119 if (limit_idx >= limit_idx_high) {
120 limit_idx = limit_idx_high;
121 max_freq = MSM_CPUFREQ_NO_LIMIT;
122 } else
123 max_freq = table[limit_idx].frequency;
124 }
Praveen Chidambaram91814362012-05-25 17:36:07 -0600125 if (max_freq == limited_max_freq)
126 goto reschedule;
127
128 /* Update new limits */
Praveen Chidambaramf248bb72012-01-20 11:38:44 -0700129 for_each_possible_cpu(cpu) {
Praveen Chidambaram91814362012-05-25 17:36:07 -0600130 ret = update_cpu_max_freq(cpu, max_freq);
131 if (ret)
132 pr_debug("Unable to limit cpu%d max freq to %d\n",
133 cpu, max_freq);
Praveen Chidambaramf248bb72012-01-20 11:38:44 -0700134 }
135
136reschedule:
137 if (enabled)
138 schedule_delayed_work(&check_temp_work,
Praveen Chidambaram91814362012-05-25 17:36:07 -0600139 msecs_to_jiffies(msm_thermal_info.poll_ms));
Praveen Chidambaramf248bb72012-01-20 11:38:44 -0700140}
141
Praveen Chidambaramf248bb72012-01-20 11:38:44 -0700142static void disable_msm_thermal(void)
143{
144 int cpu = 0;
Praveen Chidambaramf248bb72012-01-20 11:38:44 -0700145
Eugene Seahcbc07532012-04-11 19:32:27 -0600146 /* make sure check_temp is no longer running */
147 cancel_delayed_work(&check_temp_work);
148 flush_scheduled_work();
149
Praveen Chidambaram91814362012-05-25 17:36:07 -0600150 if (limited_max_freq == MSM_CPUFREQ_NO_LIMIT)
151 return;
152
Praveen Chidambaramf248bb72012-01-20 11:38:44 -0700153 for_each_possible_cpu(cpu) {
Praveen Chidambaram91814362012-05-25 17:36:07 -0600154 update_cpu_max_freq(cpu, MSM_CPUFREQ_NO_LIMIT);
Praveen Chidambaramf248bb72012-01-20 11:38:44 -0700155 }
Praveen Chidambaramf248bb72012-01-20 11:38:44 -0700156}
157
158static int set_enabled(const char *val, const struct kernel_param *kp)
159{
160 int ret = 0;
161
162 ret = param_set_bool(val, kp);
163 if (!enabled)
164 disable_msm_thermal();
165 else
166 pr_info("msm_thermal: no action for enabled = %d\n", enabled);
167
168 pr_info("msm_thermal: enabled = %d\n", enabled);
169
170 return ret;
171}
172
173static struct kernel_param_ops module_ops = {
174 .set = set_enabled,
175 .get = param_get_bool,
176};
177
178module_param_cb(enabled, &module_ops, &enabled, 0644);
179MODULE_PARM_DESC(enabled, "enforce thermal limit on cpu");
180
Eugene Seahb77b0c42012-07-02 19:28:50 -0600181int __devinit msm_thermal_init(struct msm_thermal_data *pdata)
Praveen Chidambaramf248bb72012-01-20 11:38:44 -0700182{
183 int ret = 0;
184
Praveen Chidambaram91814362012-05-25 17:36:07 -0600185 BUG_ON(!pdata);
186 BUG_ON(pdata->sensor_id >= TSENS_MAX_SENSORS);
187 memcpy(&msm_thermal_info, pdata, sizeof(struct msm_thermal_data));
188
Praveen Chidambaramf248bb72012-01-20 11:38:44 -0700189 enabled = 1;
Praveen Chidambaramf248bb72012-01-20 11:38:44 -0700190 INIT_DELAYED_WORK(&check_temp_work, check_temp);
Praveen Chidambaramf248bb72012-01-20 11:38:44 -0700191 schedule_delayed_work(&check_temp_work, 0);
192
193 return ret;
194}
Eugene Seahb77b0c42012-07-02 19:28:50 -0600195
196static int __devinit msm_thermal_dev_probe(struct platform_device *pdev)
197{
198 int ret = 0;
199 char *key = NULL;
200 struct device_node *node = pdev->dev.of_node;
201 struct msm_thermal_data data;
202
203 memset(&data, 0, sizeof(struct msm_thermal_data));
204 key = "qcom,sensor-id";
205 ret = of_property_read_u32(node, key, &data.sensor_id);
206 if (ret)
207 goto fail;
208 WARN_ON(data.sensor_id >= TSENS_MAX_SENSORS);
209
210 key = "qcom,poll-ms";
211 ret = of_property_read_u32(node, key, &data.poll_ms);
212 if (ret)
213 goto fail;
214
215 key = "qcom,limit-temp";
216 ret = of_property_read_u32(node, key, &data.limit_temp_degC);
217 if (ret)
218 goto fail;
219
220 key = "qcom,temp-hysteresis";
221 ret = of_property_read_u32(node, key, &data.temp_hysteresis_degC);
222 if (ret)
223 goto fail;
224
225 key = "qcom,freq-step";
226 ret = of_property_read_u32(node, key, &data.freq_step);
227
228fail:
229 if (ret)
230 pr_err("%s: Failed reading node=%s, key=%s\n",
231 __func__, node->full_name, key);
232 else
233 ret = msm_thermal_init(&data);
234
235 return ret;
236}
237
238static struct of_device_id msm_thermal_match_table[] = {
239 {.compatible = "qcom,msm-thermal"},
240 {},
241};
242
243static struct platform_driver msm_thermal_device_driver = {
244 .probe = msm_thermal_dev_probe,
245 .driver = {
246 .name = "msm-thermal",
247 .owner = THIS_MODULE,
248 .of_match_table = msm_thermal_match_table,
249 },
250};
251
252int __init msm_thermal_device_init(void)
253{
254 return platform_driver_register(&msm_thermal_device_driver);
255}