arm: tegra: thermal: Lower limit during throttle
[linux-2.6.git] / arch / arm / mach-tegra / tegra3_thermal.c
1 /*
2  * arch/arm/mach-tegra/tegra3_thermal.c
3  *
4  * Copyright (C) 2010-2011 NVIDIA Corporation.
5  *
6  * This software is licensed under the terms of the GNU General Public
7  * License version 2, as published by the Free Software Foundation, and
8  * may be copied, distributed, and modified under those terms.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  *
15  */
16
17 #include <linux/kernel.h>
18 #include <linux/cpufreq.h>
19 #include <linux/delay.h>
20 #include <linux/mutex.h>
21 #include <linux/init.h>
22 #include <linux/err.h>
23 #include <linux/clk.h>
24 #include <linux/debugfs.h>
25 #include <linux/seq_file.h>
26 #include <linux/uaccess.h>
27 #include <linux/thermal.h>
28 #include <mach/thermal.h>
29 #include <mach/edp.h>
30 #include <linux/slab.h>
31
32 #include "clock.h"
33 #include "cpu-tegra.h"
34 #include "dvfs.h"
35
36 #define MAX_ZONES (16)
37
38 struct tegra_thermal {
39         struct tegra_thermal_device *device;
40         long temp_throttle_tj;
41         long temp_shutdown_tj;
42 #ifdef CONFIG_TEGRA_THERMAL_SYSFS
43         struct thermal_zone_device *thz;
44         int tc1;
45         int tc2;
46         long passive_delay;
47 #else
48         long temp_throttle_low_tj;
49 #endif
50 #ifdef CONFIG_TEGRA_EDP_LIMITS
51         int edp_thermal_zone_val;
52         long edp_offset;
53         long hysteresis_edp;
54 #endif
55 };
56
57 static struct tegra_thermal thermal_state = {
58         .device = NULL,
59 #ifdef CONFIG_TEGRA_EDP_LIMITS
60         .edp_thermal_zone_val = -1,
61 #endif
62 };
63
64 #ifndef CONFIG_TEGRA_THERMAL_SYSFS
65 static bool throttle_enb;
66 struct mutex mutex;
67 #endif
68
69 #ifdef CONFIG_TEGRA_EDP_LIMITS
70 static inline long edp2tj(struct tegra_thermal *thermal,
71                                 long edp_temp)
72 {
73         return edp_temp + thermal->edp_offset;
74 }
75
76 static inline long tj2edp(struct tegra_thermal *thermal,
77                                 long temp_tj)
78 {
79         return temp_tj - thermal->edp_offset;
80 }
81 #endif
82
83 static inline long dev2tj(struct tegra_thermal_device *dev,
84                                 long dev_temp)
85 {
86         return dev_temp + dev->offset;
87 }
88
89 static inline long tj2dev(struct tegra_thermal_device *dev,
90                                 long tj_temp)
91 {
92         return tj_temp - dev->offset;
93 }
94
95 #ifdef CONFIG_TEGRA_THERMAL_SYSFS
96
97 static int tegra_thermal_zone_bind(struct thermal_zone_device *thermal,
98                                 struct thermal_cooling_device *cdevice) {
99         /* Support only Thermal Throttling (1 trip) for now */
100         return thermal_zone_bind_cooling_device(thermal, 0, cdevice);
101 }
102
103 static int tegra_thermal_zone_unbind(struct thermal_zone_device *thermal,
104                                 struct thermal_cooling_device *cdevice) {
105         /* Support only Thermal Throttling (1 trip) for now */
106         return thermal_zone_unbind_cooling_device(thermal, 0, cdevice);
107 }
108
109 static int tegra_thermal_zone_get_temp(struct thermal_zone_device *thz,
110                                                 long *temp)
111 {
112         struct tegra_thermal *thermal = thz->devdata;
113         thermal->device->get_temp(thermal->device->data, temp);
114
115         return 0;
116 }
117
118 static int tegra_thermal_zone_get_trip_type(
119                         struct thermal_zone_device *thermal,
120                         int trip,
121                         enum thermal_trip_type *type) {
122
123         /* Support only Thermal Throttling (1 trip) for now */
124         if (trip != 0)
125                 return -EINVAL;
126
127         *type = THERMAL_TRIP_PASSIVE;
128
129         return 0;
130 }
131
132 static int tegra_thermal_zone_get_trip_temp(struct thermal_zone_device *thz,
133                                                 int trip,
134                                                 long *temp) {
135         struct tegra_thermal *thermal = thz->devdata;
136
137         /* Support only Thermal Throttling (1 trip) for now */
138         if (trip != 0)
139                 return -EINVAL;
140
141         *temp = tj2dev(thermal->device, thermal->temp_throttle_tj);
142
143         return 0;
144 }
145
146 static struct thermal_zone_device_ops tegra_thermal_zone_ops = {
147         .bind = tegra_thermal_zone_bind,
148         .unbind = tegra_thermal_zone_unbind,
149         .get_temp = tegra_thermal_zone_get_temp,
150         .get_trip_type = tegra_thermal_zone_get_trip_type,
151         .get_trip_temp = tegra_thermal_zone_get_trip_temp,
152 };
153 #endif
154
155 /* The thermal sysfs handles notifying the throttling
156  * cooling device */
157 #ifndef CONFIG_TEGRA_THERMAL_SYSFS
158 static void tegra_therm_throttle(bool enable)
159 {
160         if (throttle_enb != enable) {
161                 mutex_lock(&mutex);
162                 tegra_throttling_enable(enable);
163                 throttle_enb = enable;
164                 mutex_unlock(&mutex);
165         }
166 }
167 #endif
168
169 /* Make sure this function remains stateless */
170 void tegra_thermal_alert(void *data)
171 {
172         struct tegra_thermal *thermal = data;
173         int err;
174         long temp_dev, temp_tj;
175         long lo_limit_throttle_tj, hi_limit_throttle_tj;
176         long lo_limit_edp_tj = 0, hi_limit_edp_tj = 0;
177         int lo_limit_tj = 0, hi_limit_tj = 0;
178 #ifdef CONFIG_TEGRA_EDP_LIMITS
179         const struct tegra_edp_limits *z;
180         int zones_sz;
181         int i;
182 #endif
183
184         if (thermal != &thermal_state)
185                 BUG();
186
187 #ifdef CONFIG_TEGRA_THERMAL_SYSFS
188         if (thermal->thz) {
189                 if (!thermal->thz->passive)
190                         thermal_zone_device_update(thermal->thz);
191         }
192 #endif
193
194         err = thermal->device->get_temp(thermal->device->data, &temp_dev);
195         if (err) {
196                 pr_err("%s: get temp fail(%d)", __func__, err);
197                 return;
198         }
199
200         /* Convert all temps to tj and then do all work/logic in terms of
201            tj in order to avoid confusion */
202         temp_tj = dev2tj(thermal->device, temp_dev);
203
204         lo_limit_throttle_tj = dev2tj(thermal->device, 0);
205         hi_limit_throttle_tj = thermal->temp_throttle_tj;
206
207 #ifndef CONFIG_TEGRA_THERMAL_SYSFS
208         /* Check to see if we are currently throttling */
209         if ((tegra_is_throttling() &&
210                 (temp_tj > thermal->temp_throttle_low_tj))
211                 || (temp_tj >= thermal->temp_throttle_tj)) {
212                 lo_limit_throttle_tj = thermal->temp_throttle_low_tj;
213                 hi_limit_throttle_tj = thermal->temp_shutdown_tj;
214         }
215 #else
216         if (temp_tj > thermal->temp_throttle_tj) {
217                 lo_limit_throttle_tj = thermal->temp_throttle_tj;
218                 hi_limit_throttle_tj = thermal->temp_shutdown_tj;
219         }
220 #endif
221
222 #ifdef CONFIG_TEGRA_EDP_LIMITS
223         tegra_get_cpu_edp_limits(&z, &zones_sz);
224
225 /* edp table based off of tdiode measurements */
226 #define EDP_TEMP_TJ(_index)     edp2tj(thermal, z[_index].temperature * 1000)
227
228         if (temp_tj < EDP_TEMP_TJ(0)) {
229                 lo_limit_edp_tj = dev2tj(thermal->device, 0);
230                 hi_limit_edp_tj = EDP_TEMP_TJ(0);
231         } else if (temp_tj >= EDP_TEMP_TJ(zones_sz-1)) {
232                 lo_limit_edp_tj = EDP_TEMP_TJ(zones_sz-1) -
233                                         thermal->hysteresis_edp;
234                 hi_limit_edp_tj = thermal->temp_shutdown_tj;
235         } else {
236                 for (i = 0; (i + 1) < zones_sz; i++) {
237                         if ((temp_tj >= EDP_TEMP_TJ(i)) &&
238                                 (temp_tj < EDP_TEMP_TJ(i+1))) {
239                                 lo_limit_edp_tj = EDP_TEMP_TJ(i) -
240                                                         thermal->hysteresis_edp;
241                                 hi_limit_edp_tj = EDP_TEMP_TJ(i+1);
242                                 break;
243                         }
244                 }
245         }
246 #undef EDP_TEMP_TJ
247 #else
248         lo_limit_edp_tj = 0;
249         hi_limit_edp_tj = thermal->temp_shutdown_tj;
250 #endif
251
252         /* Get smallest window size */
253         lo_limit_tj = max(lo_limit_throttle_tj, lo_limit_edp_tj);
254         hi_limit_tj = min(hi_limit_throttle_tj, hi_limit_edp_tj);
255
256         thermal->device->set_limits(thermal->device->data,
257                                         tj2dev(thermal->device, lo_limit_tj),
258                                         tj2dev(thermal->device, hi_limit_tj));
259
260 #ifndef CONFIG_TEGRA_THERMAL_SYSFS
261         if (temp_tj >= thermal->temp_throttle_tj) {
262                 /* start throttling */
263                 if (!tegra_is_throttling())
264                         tegra_therm_throttle(true);
265         } else if (temp_tj <= thermal->temp_throttle_low_tj) {
266                 /* switch off throttling */
267                 if (tegra_is_throttling())
268                         tegra_therm_throttle(false);
269         }
270 #endif
271
272 #ifdef CONFIG_TEGRA_EDP_LIMITS
273         /* inform edp governor */
274         if (thermal->edp_thermal_zone_val != temp_tj)
275                 tegra_edp_update_thermal_zone(tj2edp(thermal, temp_tj)/1000);
276
277         thermal->edp_thermal_zone_val = temp_tj;
278 #endif
279 }
280
281 int tegra_thermal_set_device(struct tegra_thermal_device *device)
282 {
283 #ifdef CONFIG_TEGRA_THERMAL_SYSFS
284         struct thermal_zone_device *thz;
285 #endif
286
287         /* only support one device */
288         if (thermal_state.device)
289                 return -EINVAL;
290
291         thermal_state.device = device;
292
293 #ifdef CONFIG_TEGRA_THERMAL_SYSFS
294         thz = thermal_zone_device_register("thermal",
295                                         1, /* trips */
296                                         &thermal_state,
297                                         &tegra_thermal_zone_ops,
298                                         thermal_state.tc1, /* dT/dt */
299                                         thermal_state.tc2, /* throttle */
300                                         thermal_state.passive_delay,
301                                         0); /* polling delay */
302
303         if (IS_ERR(thz)) {
304                 thz = NULL;
305                 return -ENODEV;
306         }
307
308         thermal_state.thz = thz;
309 #endif
310         thermal_state.device->set_alert(thermal_state.device->data,
311                                         tegra_thermal_alert,
312                                         &thermal_state);
313
314         thermal_state.device->set_shutdown_temp(thermal_state.device->data,
315                                 tj2dev(device, thermal_state.temp_shutdown_tj));
316
317         /* initialize limits */
318         tegra_thermal_alert(&thermal_state);
319
320         return 0;
321 }
322
323 int __init tegra_thermal_init(struct tegra_thermal_data *data)
324 {
325 #ifdef CONFIG_TEGRA_THERMAL_SYSFS
326         thermal_state.tc1 = data->tc1;
327         thermal_state.tc2 = data->tc2;
328         thermal_state.passive_delay = data->passive_delay;
329 #else
330         mutex_init(&mutex);
331         thermal_state.temp_throttle_low_tj = data->temp_throttle +
332                                                 data->temp_offset -
333                                                 data->hysteresis_throttle;
334 #endif
335 #ifdef CONFIG_TEGRA_EDP_LIMITS
336         thermal_state.edp_offset = data->edp_offset;
337         thermal_state.hysteresis_edp = data->hysteresis_edp;
338 #endif
339         thermal_state.temp_throttle_tj = data->temp_throttle +
340                                                 data->temp_offset;
341         thermal_state.temp_shutdown_tj = data->temp_shutdown +
342                                                 data->temp_offset;
343
344         return 0;
345 }
346
347 int tegra_thermal_exit(void)
348 {
349 #ifdef CONFIG_TEGRA_THERMAL_SYSFS
350         if (thermal_state.thz)
351                 thermal_zone_device_unregister(thermal_state.thz);
352 #endif
353
354         return 0;
355 }