blob: ed80b1b32b4ddf2d0bef47f19e97ba11aab45f38 [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>
21
22#define DEF_TEMP_SENSOR 0
23#define DEF_THERMAL_CHECK_MS 1000
24#define DEF_ALLOWED_MAX_HIGH 60
25#define DEF_ALLOWED_MAX_FREQ 918000
26
27static int enabled;
28static int allowed_max_high = DEF_ALLOWED_MAX_HIGH;
29static int allowed_max_low = (DEF_ALLOWED_MAX_HIGH - 10);
30static int allowed_max_freq = DEF_ALLOWED_MAX_FREQ;
31static int check_interval_ms = DEF_THERMAL_CHECK_MS;
32
33module_param(allowed_max_high, int, 0);
34module_param(allowed_max_freq, int, 0);
35module_param(check_interval_ms, int, 0);
36
37static DEFINE_PER_CPU(struct cpufreq_policy*, policy);
38static struct mutex policy_mutex;
39static struct delayed_work check_temp_work;
40
41static int update_cpu_max_freq(int cpu, int max_freq)
42{
43 struct cpufreq_policy *cpu_policy = per_cpu(policy, cpu);
44 int ret = 0;
45
46 if (!cpu_policy)
47 return -EINVAL;
48
49 cpufreq_verify_within_limits(cpu_policy,
50 cpu_policy->min, max_freq);
51 cpu_policy->user_policy.max = max_freq;
52
53 ret = cpufreq_update_policy(cpu);
54 if (ret)
55 pr_err("msm_thermal: cpufreq update to core%d %d err:%d\n",
56 cpu, max_freq, ret);
57 else
58 pr_info("msm_thermal: Limiting core%d max frequency to %d\n",
59 cpu, max_freq);
60
61 return ret;
62}
63
64static void check_temp(struct work_struct *work)
65{
66 struct cpufreq_policy *cpu_policy = NULL;
67 struct tsens_device tsens_dev;
68 unsigned long temp = 0;
69 unsigned int max_freq = 0;
70 int update_policy = 0;
71 int cpu = 0;
72 int ret = 0;
73
74 mutex_lock(&policy_mutex);
75 tsens_dev.sensor_num = DEF_TEMP_SENSOR;
76 ret = tsens_get_temp(&tsens_dev, &temp);
77 if (ret) {
Jeff Ohlsteinf744c862012-02-01 17:52:44 -080078 pr_debug("msm_thermal: Unable to read TSENS sensor %d\n",
Praveen Chidambaramf248bb72012-01-20 11:38:44 -070079 tsens_dev.sensor_num);
80 goto reschedule;
81 }
82
83 for_each_possible_cpu(cpu) {
84 cpu_policy = per_cpu(policy, cpu);
85 if (!cpu_policy) {
86 pr_debug("msm_thermal: No CPUFreq policy found for "
87 "cpu %d\n", cpu);
88 continue;
89 }
90 if (temp >= allowed_max_high) {
91 if (cpu_policy->max > allowed_max_freq) {
92 update_policy = 1;
93 max_freq = allowed_max_freq;
94 }
95 } else if (temp < allowed_max_low) {
96 if (cpu_policy->max < cpu_policy->cpuinfo.max_freq) {
97 max_freq = cpu_policy->cpuinfo.max_freq;
98 update_policy = 1;
99 }
100 }
101
102 if (update_policy)
103 update_cpu_max_freq(cpu, max_freq);
104 }
105
106reschedule:
107 if (enabled)
108 schedule_delayed_work(&check_temp_work,
109 msecs_to_jiffies(check_interval_ms));
110
111 mutex_unlock(&policy_mutex);
112}
113
114static int msm_thermal_notifier(struct notifier_block *nb,
115 unsigned long event, void *data)
116{
117 if (event == CPUFREQ_START) {
118 struct cpufreq_policy *cpu_policy = data;
119 mutex_lock(&policy_mutex);
120 per_cpu(policy, cpu_policy->cpu) = cpu_policy;
121 mutex_unlock(&policy_mutex);
122 }
123
124 return 0;
125}
126
127static struct notifier_block msm_thermal_notifier_block = {
128 .notifier_call = msm_thermal_notifier,
129};
130
131static void disable_msm_thermal(void)
132{
133 int cpu = 0;
134 struct cpufreq_policy *cpu_policy = NULL;
135
136 cpufreq_unregister_notifier(&msm_thermal_notifier_block,
137 CPUFREQ_POLICY_NOTIFIER);
138 cancel_delayed_work(&check_temp_work);
139
140 mutex_lock(&policy_mutex);
141 for_each_possible_cpu(cpu) {
142 cpu_policy = per_cpu(policy, cpu);
143 if (cpu_policy &&
144 cpu_policy->max < cpu_policy->cpuinfo.max_freq)
145 update_cpu_max_freq(cpu, cpu_policy->cpuinfo.max_freq);
146 }
147 mutex_unlock(&policy_mutex);
148}
149
150static int set_enabled(const char *val, const struct kernel_param *kp)
151{
152 int ret = 0;
153
154 ret = param_set_bool(val, kp);
155 if (!enabled)
156 disable_msm_thermal();
157 else
158 pr_info("msm_thermal: no action for enabled = %d\n", enabled);
159
160 pr_info("msm_thermal: enabled = %d\n", enabled);
161
162 return ret;
163}
164
165static struct kernel_param_ops module_ops = {
166 .set = set_enabled,
167 .get = param_get_bool,
168};
169
170module_param_cb(enabled, &module_ops, &enabled, 0644);
171MODULE_PARM_DESC(enabled, "enforce thermal limit on cpu");
172
173static int __init msm_thermal_init(void)
174{
175 int ret = 0;
176
177 enabled = 1;
178 mutex_init(&policy_mutex);
179 INIT_DELAYED_WORK(&check_temp_work, check_temp);
180
181 ret = cpufreq_register_notifier(&msm_thermal_notifier_block,
182 CPUFREQ_POLICY_NOTIFIER);
183
184 schedule_delayed_work(&check_temp_work, 0);
185
186 return ret;
187}
188fs_initcall(msm_thermal_init);
189