aboutsummaryrefslogtreecommitdiffstats
path: root/arch/arm/mach-tegra/tegra3_thermal.c
diff options
context:
space:
mode:
Diffstat (limited to 'arch/arm/mach-tegra/tegra3_thermal.c')
-rw-r--r--arch/arm/mach-tegra/tegra3_thermal.c544
1 files changed, 544 insertions, 0 deletions
diff --git a/arch/arm/mach-tegra/tegra3_thermal.c b/arch/arm/mach-tegra/tegra3_thermal.c
new file mode 100644
index 00000000000..8ad7bd5b670
--- /dev/null
+++ b/arch/arm/mach-tegra/tegra3_thermal.c
@@ -0,0 +1,544 @@
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
38struct 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 struct mutex mutex;
56};
57
58static struct tegra_thermal thermal_state = {
59 .device = NULL,
60#ifdef CONFIG_TEGRA_EDP_LIMITS
61 .edp_thermal_zone_val = -1,
62#endif
63};
64
65#ifndef CONFIG_TEGRA_THERMAL_SYSFS
66static bool throttle_enb;
67#endif
68
69#ifdef CONFIG_TEGRA_EDP_LIMITS
70static inline long edp2tj(struct tegra_thermal *thermal,
71 long edp_temp)
72{
73 return edp_temp + thermal->edp_offset;
74}
75
76static inline long tj2edp(struct tegra_thermal *thermal,
77 long temp_tj)
78{
79 return temp_tj - thermal->edp_offset;
80}
81#endif
82
83static inline long dev2tj(struct tegra_thermal_device *dev,
84 long dev_temp)
85{
86 return dev_temp + dev->offset;
87}
88
89static 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
97static 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
103static 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
109static 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
118static 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
132static 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
146static 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
158static void tegra_therm_throttle(bool enable)
159{
160 if (throttle_enb != enable) {
161 mutex_lock(&thermal_state.mutex);
162 tegra_throttling_enable(enable);
163 throttle_enb = enable;
164 mutex_unlock(&thermal_state.mutex);
165 }
166}
167#endif
168
169/* Make sure this function remains stateless */
170void 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 long temp_low_dev, temp_low_tj;
178 int lo_limit_tj = 0, hi_limit_tj = 0;
179#ifdef CONFIG_TEGRA_EDP_LIMITS
180 const struct tegra_edp_limits *z;
181 int zones_sz;
182 int i;
183#endif
184
185 if (thermal != &thermal_state)
186 BUG();
187
188 mutex_lock(&thermal_state.mutex);
189
190#ifdef CONFIG_TEGRA_THERMAL_SYSFS
191 if (thermal->thz) {
192 if (!thermal->thz->passive)
193 thermal_zone_device_update(thermal->thz);
194 }
195#endif
196
197 err = thermal->device->get_temp(thermal->device->data, &temp_dev);
198 if (err) {
199 pr_err("%s: get temp fail(%d)", __func__, err);
200 goto done;
201 }
202
203 /* Convert all temps to tj and then do all work/logic in terms of
204 tj in order to avoid confusion */
205 temp_tj = dev2tj(thermal->device, temp_dev);
206 thermal->device->get_temp_low(thermal->device, &temp_low_dev);
207 temp_low_tj = dev2tj(thermal->device, temp_low_dev);
208
209 lo_limit_throttle_tj = temp_low_tj;
210 hi_limit_throttle_tj = thermal->temp_throttle_tj;
211
212#ifndef CONFIG_TEGRA_THERMAL_SYSFS
213 /* Check to see if we are currently throttling */
214 if ((tegra_is_throttling() &&
215 (temp_tj > thermal->temp_throttle_low_tj))
216 || (temp_tj >= thermal->temp_throttle_tj)) {
217 lo_limit_throttle_tj = thermal->temp_throttle_low_tj;
218 hi_limit_throttle_tj = thermal->temp_shutdown_tj;
219 }
220#else
221 if (temp_tj > thermal->temp_throttle_tj) {
222 lo_limit_throttle_tj = thermal->temp_throttle_tj;
223 hi_limit_throttle_tj = thermal->temp_shutdown_tj;
224 }
225#endif
226
227#ifdef CONFIG_TEGRA_EDP_LIMITS
228 tegra_get_cpu_edp_limits(&z, &zones_sz);
229
230/* edp table based off of tdiode measurements */
231#define EDP_TEMP_TJ(_index) edp2tj(thermal, z[_index].temperature * 1000)
232
233 if (temp_tj < EDP_TEMP_TJ(0)) {
234 lo_limit_edp_tj = temp_low_tj;
235 hi_limit_edp_tj = EDP_TEMP_TJ(0);
236 } else if (temp_tj >= EDP_TEMP_TJ(zones_sz-1)) {
237 lo_limit_edp_tj = EDP_TEMP_TJ(zones_sz-1) -
238 thermal->hysteresis_edp;
239 hi_limit_edp_tj = thermal->temp_shutdown_tj;
240 } else {
241 for (i = 0; (i + 1) < zones_sz; i++) {
242 if ((temp_tj >= EDP_TEMP_TJ(i)) &&
243 (temp_tj < EDP_TEMP_TJ(i+1))) {
244 lo_limit_edp_tj = EDP_TEMP_TJ(i) -
245 thermal->hysteresis_edp;
246 hi_limit_edp_tj = EDP_TEMP_TJ(i+1);
247 break;
248 }
249 }
250 }
251#undef EDP_TEMP_TJ
252#else
253 lo_limit_edp_tj = temp_low_tj;
254 hi_limit_edp_tj = thermal->temp_shutdown_tj;
255#endif
256
257 /* Get smallest window size */
258 lo_limit_tj = max(lo_limit_throttle_tj, lo_limit_edp_tj);
259 hi_limit_tj = min(hi_limit_throttle_tj, hi_limit_edp_tj);
260
261 thermal->device->set_limits(thermal->device->data,
262 tj2dev(thermal->device, lo_limit_tj),
263 tj2dev(thermal->device, hi_limit_tj));
264
265#ifndef CONFIG_TEGRA_THERMAL_SYSFS
266 if (temp_tj >= thermal->temp_throttle_tj) {
267 /* start throttling */
268 if (!tegra_is_throttling())
269 tegra_therm_throttle(true);
270 } else if (temp_tj <= thermal->temp_throttle_low_tj) {
271 /* switch off throttling */
272 if (tegra_is_throttling())
273 tegra_therm_throttle(false);
274 }
275#endif
276
277#ifdef CONFIG_TEGRA_EDP_LIMITS
278 /* inform edp governor */
279 if (thermal->edp_thermal_zone_val != temp_tj)
280 tegra_edp_update_thermal_zone(tj2edp(thermal, temp_tj)/1000);
281
282 thermal->edp_thermal_zone_val = temp_tj;
283#endif
284
285done:
286 mutex_unlock(&thermal_state.mutex);
287}
288
289int tegra_thermal_set_device(struct tegra_thermal_device *device)
290{
291#ifdef CONFIG_TEGRA_THERMAL_SYSFS
292 struct thermal_zone_device *thz;
293#endif
294
295 /* only support one device */
296 if (thermal_state.device)
297 return -EINVAL;
298
299 thermal_state.device = device;
300
301#ifdef CONFIG_TEGRA_THERMAL_SYSFS
302 thz = thermal_zone_device_register(thermal_state.device->name,
303 1, /* trips */
304 &thermal_state,
305 &tegra_thermal_zone_ops,
306 thermal_state.tc1, /* dT/dt */
307 thermal_state.tc2, /* throttle */
308 thermal_state.passive_delay,
309 0); /* polling delay */
310
311 if (IS_ERR(thz)) {
312 thz = NULL;
313 return -ENODEV;
314 }
315
316 thermal_state.thz = thz;
317#endif
318 thermal_state.device->set_alert(thermal_state.device->data,
319 tegra_thermal_alert,
320 &thermal_state);
321
322 thermal_state.device->set_shutdown_temp(thermal_state.device->data,
323 tj2dev(device, thermal_state.temp_shutdown_tj));
324
325 /* initialize limits */
326 tegra_thermal_alert(&thermal_state);
327
328 return 0;
329}
330
331int __init tegra_thermal_init(struct tegra_thermal_data *data)
332{
333#ifdef CONFIG_TEGRA_THERMAL_SYSFS
334 thermal_state.tc1 = data->tc1;
335 thermal_state.tc2 = data->tc2;
336 thermal_state.passive_delay = data->passive_delay;
337#else
338 thermal_state.temp_throttle_low_tj = data->temp_throttle +
339 data->temp_offset -
340 data->hysteresis_throttle;
341#endif
342 mutex_init(&thermal_state.mutex);
343#ifdef CONFIG_TEGRA_EDP_LIMITS
344 thermal_state.edp_offset = data->edp_offset;
345 thermal_state.hysteresis_edp = data->hysteresis_edp;
346#endif
347 thermal_state.temp_throttle_tj = data->temp_throttle +
348 data->temp_offset;
349 thermal_state.temp_shutdown_tj = data->temp_shutdown +
350 data->temp_offset;
351
352 return 0;
353}
354
355int tegra_thermal_exit(void)
356{
357#ifdef CONFIG_TEGRA_THERMAL_SYSFS
358 if (thermal_state.thz)
359 thermal_zone_device_unregister(thermal_state.thz);
360#endif
361
362 return 0;
363}
364
365#ifdef CONFIG_DEBUG_FS
366
367static int tegra_thermal_throttle_temp_tj_set(void *data, u64 val)
368{
369#ifndef CONFIG_TEGRA_THERMAL_SYSFS
370 long throttle_hysteresis = thermal_state.temp_throttle_tj -
371 thermal_state.temp_throttle_low_tj;
372#endif
373
374 mutex_lock(&thermal_state.mutex);
375 thermal_state.temp_throttle_tj = val;
376#ifndef CONFIG_TEGRA_THERMAL_SYSFS
377 thermal_state.temp_throttle_low_tj = thermal_state.temp_throttle_tj -
378 throttle_hysteresis;
379#endif
380 mutex_unlock(&thermal_state.mutex);
381
382 tegra_thermal_alert(&thermal_state);
383
384 return 0;
385}
386
387static int tegra_thermal_throttle_temp_tj_get(void *data, u64 *val)
388{
389 *val = (u64)thermal_state.temp_throttle_tj;
390 return 0;
391}
392
393DEFINE_SIMPLE_ATTRIBUTE(throttle_temp_tj_fops,
394 tegra_thermal_throttle_temp_tj_get,
395 tegra_thermal_throttle_temp_tj_set,
396 "%llu\n");
397
398static int tegra_thermal_shutdown_temp_tj_set(void *data, u64 val)
399{
400 thermal_state.temp_shutdown_tj = val;
401
402 if (thermal_state.device)
403 thermal_state.device->set_shutdown_temp(
404 thermal_state.device->data,
405 tj2dev(thermal_state.device,
406 thermal_state.temp_shutdown_tj));
407
408 tegra_thermal_alert(&thermal_state);
409
410 return 0;
411}
412
413static int tegra_thermal_shutdown_temp_tj_get(void *data, u64 *val)
414{
415 *val = (u64)thermal_state.temp_shutdown_tj;
416 return 0;
417}
418
419DEFINE_SIMPLE_ATTRIBUTE(shutdown_temp_tj_fops,
420 tegra_thermal_shutdown_temp_tj_get,
421 tegra_thermal_shutdown_temp_tj_set,
422 "%llu\n");
423
424
425static int tegra_thermal_temp_tj_get(void *data, u64 *val)
426{
427 long temp_tj, temp_dev;
428
429 if (thermal_state.device) {
430 thermal_state.device->get_temp(thermal_state.device->data,
431 &temp_dev);
432
433 /* Convert all temps to tj and then do all work/logic in
434 terms of tj in order to avoid confusion */
435 temp_tj = dev2tj(thermal_state.device, temp_dev);
436 } else {
437 temp_tj = -1;
438 }
439
440 *val = (u64)temp_tj;
441
442 return 0;
443}
444
445DEFINE_SIMPLE_ATTRIBUTE(temp_tj_fops,
446 tegra_thermal_temp_tj_get,
447 NULL,
448 "%llu\n");
449
450#ifdef CONFIG_TEGRA_THERMAL_SYSFS
451static int tegra_thermal_tc1_set(void *data, u64 val)
452{
453 thermal_state.thz->tc1 = val;
454 return 0;
455}
456
457static int tegra_thermal_tc1_get(void *data, u64 *val)
458{
459 *val = (u64)thermal_state.thz->tc1;
460 return 0;
461}
462
463DEFINE_SIMPLE_ATTRIBUTE(tc1_fops,
464 tegra_thermal_tc1_get,
465 tegra_thermal_tc1_set,
466 "%llu\n");
467
468static int tegra_thermal_tc2_set(void *data, u64 val)
469{
470 thermal_state.thz->tc2 = val;
471 return 0;
472}
473
474static int tegra_thermal_tc2_get(void *data, u64 *val)
475{
476 *val = (u64)thermal_state.thz->tc2;
477 return 0;
478}
479
480DEFINE_SIMPLE_ATTRIBUTE(tc2_fops,
481 tegra_thermal_tc2_get,
482 tegra_thermal_tc2_set,
483 "%llu\n");
484
485static int tegra_thermal_passive_delay_set(void *data, u64 val)
486{
487 thermal_state.thz->passive_delay = val;
488 return 0;
489}
490
491static int tegra_thermal_passive_delay_get(void *data, u64 *val)
492{
493 *val = (u64)thermal_state.thz->passive_delay;
494 return 0;
495}
496
497DEFINE_SIMPLE_ATTRIBUTE(passive_delay_fops,
498 tegra_thermal_passive_delay_get,
499 tegra_thermal_passive_delay_set,
500 "%llu\n");
501#endif
502
503
504static struct dentry *thermal_debugfs_root;
505
506static int __init tegra_thermal_debug_init(void)
507{
508 thermal_debugfs_root = debugfs_create_dir("tegra_thermal", 0);
509
510 if (!debugfs_create_file("throttle_temp_tj", 0644, thermal_debugfs_root,
511 NULL, &throttle_temp_tj_fops))
512 goto err_out;
513
514 if (!debugfs_create_file("shutdown_temp_tj", 0644, thermal_debugfs_root,
515 NULL, &shutdown_temp_tj_fops))
516 goto err_out;
517
518 if (!debugfs_create_file("temp_tj", 0644, thermal_debugfs_root,
519 NULL, &temp_tj_fops))
520 goto err_out;
521
522#ifdef CONFIG_TEGRA_THERMAL_SYSFS
523 if (!debugfs_create_file("tc1", 0644, thermal_debugfs_root,
524 NULL, &tc1_fops))
525 goto err_out;
526
527 if (!debugfs_create_file("tc2", 0644, thermal_debugfs_root,
528 NULL, &tc2_fops))
529 goto err_out;
530
531 if (!debugfs_create_file("passive_delay", 0644, thermal_debugfs_root,
532 NULL, &passive_delay_fops))
533 goto err_out;
534#endif
535
536 return 0;
537
538err_out:
539 debugfs_remove_recursive(thermal_debugfs_root);
540 return -ENOMEM;
541}
542
543late_initcall(tegra_thermal_debug_init);
544#endif