arm: tegra: thermal: Thermal cleanup
[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 #endif
216
217 #ifdef CONFIG_TEGRA_EDP_LIMITS
218         tegra_get_cpu_edp_limits(&z, &zones_sz);
219
220 /* edp table based off of tdiode measurements */
221 #define EDP_TEMP_TJ(_index)     edp2tj(thermal, z[_index].temperature * 1000)
222
223         if (temp_tj < EDP_TEMP_TJ(0)) {
224                 lo_limit_edp_tj = dev2tj(thermal->device, 0);
225                 hi_limit_edp_tj = EDP_TEMP_TJ(0);
226         } else if (temp_tj >= EDP_TEMP_TJ(zones_sz-1)) {
227                 lo_limit_edp_tj = EDP_TEMP_TJ(zones_sz-1) -
228                                         thermal->hysteresis_edp;
229                 hi_limit_edp_tj = thermal->temp_shutdown_tj;
230         } else {
231                 for (i = 0; (i + 1) < zones_sz; i++) {
232                         if ((temp_tj >= EDP_TEMP_TJ(i)) &&
233                                 (temp_tj < EDP_TEMP_TJ(i+1))) {
234                                 lo_limit_edp_tj = EDP_TEMP_TJ(i) -
235                                                         thermal->hysteresis_edp;
236                                 hi_limit_edp_tj = EDP_TEMP_TJ(i+1);
237                                 break;
238                         }
239                 }
240         }
241 #undef EDP_TEMP_TJ
242 #else
243         lo_limit_edp_tj = 0;
244         hi_limit_edp_tj = thermal->temp_shutdown_tj;
245 #endif
246
247         /* Get smallest window size */
248         lo_limit_tj = max(lo_limit_throttle_tj, lo_limit_edp_tj);
249         hi_limit_tj = min(hi_limit_throttle_tj, hi_limit_edp_tj);
250
251         thermal->device->set_limits(thermal->device->data,
252                                         tj2dev(thermal->device, lo_limit_tj),
253                                         tj2dev(thermal->device, hi_limit_tj));
254
255 #ifndef CONFIG_TEGRA_THERMAL_SYSFS
256         if (temp_tj >= thermal->temp_throttle_tj) {
257                 /* start throttling */
258                 if (!tegra_is_throttling())
259                         tegra_therm_throttle(true);
260         } else if (temp_tj <= thermal->temp_throttle_low_tj) {
261                 /* switch off throttling */
262                 if (tegra_is_throttling())
263                         tegra_therm_throttle(false);
264         }
265 #endif
266
267 #ifdef CONFIG_TEGRA_EDP_LIMITS
268         /* inform edp governor */
269         if (thermal->edp_thermal_zone_val != temp_tj)
270                 tegra_edp_update_thermal_zone(tj2edp(thermal, temp_tj)/1000);
271
272         thermal->edp_thermal_zone_val = temp_tj;
273 #endif
274 }
275
276 int tegra_thermal_set_device(struct tegra_thermal_device *device)
277 {
278 #ifdef CONFIG_TEGRA_THERMAL_SYSFS
279         struct thermal_zone_device *thz;
280 #endif
281
282         /* only support one device */
283         if (thermal_state.device)
284                 return -EINVAL;
285
286         thermal_state.device = device;
287
288 #ifdef CONFIG_TEGRA_THERMAL_SYSFS
289         thz = thermal_zone_device_register("thermal",
290                                         1, /* trips */
291                                         &thermal_state,
292                                         &tegra_thermal_zone_ops,
293                                         thermal_state.tc1, /* dT/dt */
294                                         thermal_state.tc2, /* throttle */
295                                         thermal_state.passive_delay,
296                                         0); /* polling delay */
297
298         if (IS_ERR(thz)) {
299                 thz = NULL;
300                 return -ENODEV;
301         }
302
303         thermal_state.thz = thz;
304 #endif
305         thermal_state.device->set_alert(thermal_state.device->data,
306                                         tegra_thermal_alert,
307                                         &thermal_state);
308
309         thermal_state.device->set_shutdown_temp(thermal_state.device->data,
310                                 tj2dev(device, thermal_state.temp_shutdown_tj));
311
312         /* initialize limits */
313         tegra_thermal_alert(&thermal_state);
314
315         return 0;
316 }
317
318 int __init tegra_thermal_init(struct tegra_thermal_data *data)
319 {
320 #ifdef CONFIG_TEGRA_THERMAL_SYSFS
321         thermal_state.tc1 = data->tc1;
322         thermal_state.tc2 = data->tc2;
323         thermal_state.passive_delay = data->passive_delay;
324 #else
325         mutex_init(&mutex);
326         thermal_state.temp_throttle_low_tj = data->temp_throttle +
327                                                 data->temp_offset -
328                                                 data->hysteresis_throttle;
329 #endif
330 #ifdef CONFIG_TEGRA_EDP_LIMITS
331         thermal_state.edp_offset = data->edp_offset;
332         thermal_state.hysteresis_edp = data->hysteresis_edp;
333 #endif
334         thermal_state.temp_throttle_tj = data->temp_throttle +
335                                                 data->temp_offset;
336         thermal_state.temp_shutdown_tj = data->temp_shutdown +
337                                                 data->temp_offset;
338
339         return 0;
340 }
341
342 int tegra_thermal_exit(void)
343 {
344 #ifdef CONFIG_TEGRA_THERMAL_SYSFS
345         if (thermal_state.thz)
346                 thermal_zone_device_unregister(thermal_state.thz);
347 #endif
348
349         return 0;
350 }