blob: a8d372073bbb76eab8ed408a052a19d73aec131c [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>
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>
24#include <mach/cpufreq.h>
Praveen Chidambaramf248bb72012-01-20 11:38:44 -070025
26static int enabled;
Praveen Chidambaram91814362012-05-25 17:36:07 -060027static struct msm_thermal_data msm_thermal_info;
28static uint32_t limited_max_freq = MSM_CPUFREQ_NO_LIMIT;
Praveen Chidambaramf248bb72012-01-20 11:38:44 -070029static struct delayed_work check_temp_work;
30
Praveen Chidambaram91814362012-05-25 17:36:07 -060031static int update_cpu_max_freq(int cpu, uint32_t max_freq)
Praveen Chidambaramf248bb72012-01-20 11:38:44 -070032{
Praveen Chidambaramf248bb72012-01-20 11:38:44 -070033 int ret = 0;
34
Praveen Chidambaram91814362012-05-25 17:36:07 -060035 ret = msm_cpufreq_set_freq_limits(cpu, MSM_CPUFREQ_NO_LIMIT, max_freq);
36 if (ret)
37 return ret;
Praveen Chidambaramf248bb72012-01-20 11:38:44 -070038
39 ret = cpufreq_update_policy(cpu);
Praveen Chidambaram91814362012-05-25 17:36:07 -060040 if (ret)
41 return ret;
42
43 limited_max_freq = max_freq;
44 if (max_freq != MSM_CPUFREQ_NO_LIMIT)
45 pr_info("msm_thermal: Limiting cpu%d max frequency to %d\n",
46 cpu, max_freq);
47 else
48 pr_info("msm_thermal: Max frequency reset for cpu%d\n", cpu);
Praveen Chidambaramf248bb72012-01-20 11:38:44 -070049
50 return ret;
51}
52
53static void check_temp(struct work_struct *work)
54{
Praveen Chidambaramf248bb72012-01-20 11:38:44 -070055 struct tsens_device tsens_dev;
56 unsigned long temp = 0;
Praveen Chidambaram91814362012-05-25 17:36:07 -060057 uint32_t max_freq = limited_max_freq;
Praveen Chidambaramf248bb72012-01-20 11:38:44 -070058 int cpu = 0;
59 int ret = 0;
60
Praveen Chidambaram91814362012-05-25 17:36:07 -060061 tsens_dev.sensor_num = msm_thermal_info.sensor_id;
Praveen Chidambaramf248bb72012-01-20 11:38:44 -070062 ret = tsens_get_temp(&tsens_dev, &temp);
63 if (ret) {
Jeff Ohlsteinf744c862012-02-01 17:52:44 -080064 pr_debug("msm_thermal: Unable to read TSENS sensor %d\n",
Praveen Chidambaramf248bb72012-01-20 11:38:44 -070065 tsens_dev.sensor_num);
66 goto reschedule;
67 }
68
Praveen Chidambaram91814362012-05-25 17:36:07 -060069 if (temp >= msm_thermal_info.limit_temp)
70 max_freq = msm_thermal_info.limit_freq;
71 else if (temp <
72 msm_thermal_info.limit_temp - msm_thermal_info.temp_hysteresis)
73 max_freq = MSM_CPUFREQ_NO_LIMIT;
74
75 if (max_freq == limited_max_freq)
76 goto reschedule;
77
78 /* Update new limits */
Praveen Chidambaramf248bb72012-01-20 11:38:44 -070079 for_each_possible_cpu(cpu) {
Praveen Chidambaram91814362012-05-25 17:36:07 -060080 ret = update_cpu_max_freq(cpu, max_freq);
81 if (ret)
82 pr_debug("Unable to limit cpu%d max freq to %d\n",
83 cpu, max_freq);
Praveen Chidambaramf248bb72012-01-20 11:38:44 -070084 }
85
86reschedule:
87 if (enabled)
88 schedule_delayed_work(&check_temp_work,
Praveen Chidambaram91814362012-05-25 17:36:07 -060089 msecs_to_jiffies(msm_thermal_info.poll_ms));
Praveen Chidambaramf248bb72012-01-20 11:38:44 -070090}
91
Praveen Chidambaramf248bb72012-01-20 11:38:44 -070092static void disable_msm_thermal(void)
93{
94 int cpu = 0;
Praveen Chidambaramf248bb72012-01-20 11:38:44 -070095
Eugene Seahcbc07532012-04-11 19:32:27 -060096 /* make sure check_temp is no longer running */
97 cancel_delayed_work(&check_temp_work);
98 flush_scheduled_work();
99
Praveen Chidambaram91814362012-05-25 17:36:07 -0600100 if (limited_max_freq == MSM_CPUFREQ_NO_LIMIT)
101 return;
102
Praveen Chidambaramf248bb72012-01-20 11:38:44 -0700103 for_each_possible_cpu(cpu) {
Praveen Chidambaram91814362012-05-25 17:36:07 -0600104 update_cpu_max_freq(cpu, MSM_CPUFREQ_NO_LIMIT);
Praveen Chidambaramf248bb72012-01-20 11:38:44 -0700105 }
Praveen Chidambaramf248bb72012-01-20 11:38:44 -0700106}
107
108static int set_enabled(const char *val, const struct kernel_param *kp)
109{
110 int ret = 0;
111
112 ret = param_set_bool(val, kp);
113 if (!enabled)
114 disable_msm_thermal();
115 else
116 pr_info("msm_thermal: no action for enabled = %d\n", enabled);
117
118 pr_info("msm_thermal: enabled = %d\n", enabled);
119
120 return ret;
121}
122
123static struct kernel_param_ops module_ops = {
124 .set = set_enabled,
125 .get = param_get_bool,
126};
127
128module_param_cb(enabled, &module_ops, &enabled, 0644);
129MODULE_PARM_DESC(enabled, "enforce thermal limit on cpu");
130
Praveen Chidambaram91814362012-05-25 17:36:07 -0600131int __init msm_thermal_init(struct msm_thermal_data *pdata)
Praveen Chidambaramf248bb72012-01-20 11:38:44 -0700132{
133 int ret = 0;
134
Praveen Chidambaram91814362012-05-25 17:36:07 -0600135 BUG_ON(!pdata);
136 BUG_ON(pdata->sensor_id >= TSENS_MAX_SENSORS);
137 memcpy(&msm_thermal_info, pdata, sizeof(struct msm_thermal_data));
138
Praveen Chidambaramf248bb72012-01-20 11:38:44 -0700139 enabled = 1;
Praveen Chidambaramf248bb72012-01-20 11:38:44 -0700140 INIT_DELAYED_WORK(&check_temp_work, check_temp);
Praveen Chidambaramf248bb72012-01-20 11:38:44 -0700141 schedule_delayed_work(&check_temp_work, 0);
142
143 return ret;
144}