aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/thermal/samsung/exynos_thermal.c
diff options
context:
space:
mode:
authorAmit Daniel Kachhap <amit.daniel@samsung.com>2013-06-24 06:50:25 -0400
committerEduardo Valentin <eduardo.valentin@ti.com>2013-08-13 09:51:59 -0400
commit1b678641c24f035f020bcecf8a92cde145a49724 (patch)
tree25fb1eea7998bf4daadd525690f7fec7a0eaa55c /drivers/thermal/samsung/exynos_thermal.c
parent44328fcc4964d04a8e70e14fd443a8389e98a1de (diff)
thermal: exynos: Bifurcate exynos thermal common and tmu controller code
This code bifurcates exynos thermal implementation into common and sensor specific parts. The common thermal code interacts with core thermal layer and core cpufreq cooling parts and is independent of SOC specific driver. This change is needed to cleanly add support for new TMU sensors. Acked-by: Kukjin Kim <kgene.kim@samsung.com> Acked-by: Jonghwa Lee <jonghwa3.lee@samsung.com> Acked-by: Eduardo Valentin <eduardo.valentin@ti.com> Signed-off-by: Amit Daniel Kachhap <amit.daniel@samsung.com> Signed-off-by: Eduardo Valentin <eduardo.valentin@ti.com>
Diffstat (limited to 'drivers/thermal/samsung/exynos_thermal.c')
-rw-r--r--drivers/thermal/samsung/exynos_thermal.c419
1 files changed, 6 insertions, 413 deletions
diff --git a/drivers/thermal/samsung/exynos_thermal.c b/drivers/thermal/samsung/exynos_thermal.c
index 9af4b93c9f86..f3500ab6615d 100644
--- a/drivers/thermal/samsung/exynos_thermal.c
+++ b/drivers/thermal/samsung/exynos_thermal.c
@@ -21,23 +21,15 @@
21 * 21 *
22 */ 22 */
23 23
24#include <linux/module.h>
25#include <linux/err.h>
26#include <linux/kernel.h>
27#include <linux/slab.h>
28#include <linux/platform_device.h>
29#include <linux/interrupt.h>
30#include <linux/clk.h> 24#include <linux/clk.h>
31#include <linux/workqueue.h>
32#include <linux/sysfs.h>
33#include <linux/kobject.h>
34#include <linux/io.h> 25#include <linux/io.h>
35#include <linux/mutex.h> 26#include <linux/interrupt.h>
36#include <linux/platform_data/exynos_thermal.h> 27#include <linux/module.h>
37#include <linux/thermal.h>
38#include <linux/cpufreq.h>
39#include <linux/cpu_cooling.h>
40#include <linux/of.h> 28#include <linux/of.h>
29#include <linux/platform_device.h>
30#include <linux/platform_data/exynos_thermal.h>
31
32#include "exynos_thermal_common.h"
41 33
42/* Exynos generic registers */ 34/* Exynos generic registers */
43#define EXYNOS_TMU_REG_TRIMINFO 0x0 35#define EXYNOS_TMU_REG_TRIMINFO 0x0
@@ -88,16 +80,6 @@
88#define EFUSE_MIN_VALUE 40 80#define EFUSE_MIN_VALUE 40
89#define EFUSE_MAX_VALUE 100 81#define EFUSE_MAX_VALUE 100
90 82
91/* In-kernel thermal framework related macros & definations */
92#define SENSOR_NAME_LEN 16
93#define MAX_TRIP_COUNT 8
94#define MAX_COOLING_DEVICE 4
95#define MAX_THRESHOLD_LEVS 4
96
97#define ACTIVE_INTERVAL 500
98#define IDLE_INTERVAL 10000
99#define MCELSIUS 1000
100
101#ifdef CONFIG_THERMAL_EMULATION 83#ifdef CONFIG_THERMAL_EMULATION
102#define EXYNOS_EMUL_TIME 0x57F0 84#define EXYNOS_EMUL_TIME 0x57F0
103#define EXYNOS_EMUL_TIME_SHIFT 16 85#define EXYNOS_EMUL_TIME_SHIFT 16
@@ -106,17 +88,6 @@
106#define EXYNOS_EMUL_ENABLE 0x1 88#define EXYNOS_EMUL_ENABLE 0x1
107#endif /* CONFIG_THERMAL_EMULATION */ 89#endif /* CONFIG_THERMAL_EMULATION */
108 90
109/* CPU Zone information */
110#define PANIC_ZONE 4
111#define WARN_ZONE 3
112#define MONITOR_ZONE 2
113#define SAFE_ZONE 1
114
115#define GET_ZONE(trip) (trip + 2)
116#define GET_TRIP(zone) (zone - 2)
117
118#define EXYNOS_ZONE_COUNT 3
119
120struct exynos_tmu_data { 91struct exynos_tmu_data {
121 struct exynos_tmu_platform_data *pdata; 92 struct exynos_tmu_platform_data *pdata;
122 struct resource *mem; 93 struct resource *mem;
@@ -129,384 +100,6 @@ struct exynos_tmu_data {
129 u8 temp_error1, temp_error2; 100 u8 temp_error1, temp_error2;
130}; 101};
131 102
132struct thermal_trip_point_conf {
133 int trip_val[MAX_TRIP_COUNT];
134 int trip_count;
135 u8 trigger_falling;
136};
137
138struct thermal_cooling_conf {
139 struct freq_clip_table freq_data[MAX_TRIP_COUNT];
140 int freq_clip_count;
141};
142
143struct thermal_sensor_conf {
144 char name[SENSOR_NAME_LEN];
145 int (*read_temperature)(void *data);
146 int (*write_emul_temp)(void *drv_data, unsigned long temp);
147 struct thermal_trip_point_conf trip_data;
148 struct thermal_cooling_conf cooling_data;
149 void *private_data;
150};
151
152struct exynos_thermal_zone {
153 enum thermal_device_mode mode;
154 struct thermal_zone_device *therm_dev;
155 struct thermal_cooling_device *cool_dev[MAX_COOLING_DEVICE];
156 unsigned int cool_dev_size;
157 struct platform_device *exynos4_dev;
158 struct thermal_sensor_conf *sensor_conf;
159 bool bind;
160};
161
162static struct exynos_thermal_zone *th_zone;
163static void exynos_unregister_thermal(void);
164static int exynos_register_thermal(struct thermal_sensor_conf *sensor_conf);
165
166/* Get mode callback functions for thermal zone */
167static int exynos_get_mode(struct thermal_zone_device *thermal,
168 enum thermal_device_mode *mode)
169{
170 if (th_zone)
171 *mode = th_zone->mode;
172 return 0;
173}
174
175/* Set mode callback functions for thermal zone */
176static int exynos_set_mode(struct thermal_zone_device *thermal,
177 enum thermal_device_mode mode)
178{
179 if (!th_zone->therm_dev) {
180 pr_notice("thermal zone not registered\n");
181 return 0;
182 }
183
184 mutex_lock(&th_zone->therm_dev->lock);
185
186 if (mode == THERMAL_DEVICE_ENABLED &&
187 !th_zone->sensor_conf->trip_data.trigger_falling)
188 th_zone->therm_dev->polling_delay = IDLE_INTERVAL;
189 else
190 th_zone->therm_dev->polling_delay = 0;
191
192 mutex_unlock(&th_zone->therm_dev->lock);
193
194 th_zone->mode = mode;
195 thermal_zone_device_update(th_zone->therm_dev);
196 pr_info("thermal polling set for duration=%d msec\n",
197 th_zone->therm_dev->polling_delay);
198 return 0;
199}
200
201
202/* Get trip type callback functions for thermal zone */
203static int exynos_get_trip_type(struct thermal_zone_device *thermal, int trip,
204 enum thermal_trip_type *type)
205{
206 switch (GET_ZONE(trip)) {
207 case MONITOR_ZONE:
208 case WARN_ZONE:
209 *type = THERMAL_TRIP_ACTIVE;
210 break;
211 case PANIC_ZONE:
212 *type = THERMAL_TRIP_CRITICAL;
213 break;
214 default:
215 return -EINVAL;
216 }
217 return 0;
218}
219
220/* Get trip temperature callback functions for thermal zone */
221static int exynos_get_trip_temp(struct thermal_zone_device *thermal, int trip,
222 unsigned long *temp)
223{
224 if (trip < GET_TRIP(MONITOR_ZONE) || trip > GET_TRIP(PANIC_ZONE))
225 return -EINVAL;
226
227 *temp = th_zone->sensor_conf->trip_data.trip_val[trip];
228 /* convert the temperature into millicelsius */
229 *temp = *temp * MCELSIUS;
230
231 return 0;
232}
233
234/* Get critical temperature callback functions for thermal zone */
235static int exynos_get_crit_temp(struct thermal_zone_device *thermal,
236 unsigned long *temp)
237{
238 int ret;
239 /* Panic zone */
240 ret = exynos_get_trip_temp(thermal, GET_TRIP(PANIC_ZONE), temp);
241 return ret;
242}
243
244/* Bind callback functions for thermal zone */
245static int exynos_bind(struct thermal_zone_device *thermal,
246 struct thermal_cooling_device *cdev)
247{
248 int ret = 0, i, tab_size, level;
249 struct freq_clip_table *tab_ptr, *clip_data;
250 struct thermal_sensor_conf *data = th_zone->sensor_conf;
251
252 tab_ptr = (struct freq_clip_table *)data->cooling_data.freq_data;
253 tab_size = data->cooling_data.freq_clip_count;
254
255 if (tab_ptr == NULL || tab_size == 0)
256 return -EINVAL;
257
258 /* find the cooling device registered*/
259 for (i = 0; i < th_zone->cool_dev_size; i++)
260 if (cdev == th_zone->cool_dev[i])
261 break;
262
263 /* No matching cooling device */
264 if (i == th_zone->cool_dev_size)
265 return 0;
266
267 /* Bind the thermal zone to the cpufreq cooling device */
268 for (i = 0; i < tab_size; i++) {
269 clip_data = (struct freq_clip_table *)&(tab_ptr[i]);
270 level = cpufreq_cooling_get_level(0, clip_data->freq_clip_max);
271 if (level == THERMAL_CSTATE_INVALID)
272 return 0;
273 switch (GET_ZONE(i)) {
274 case MONITOR_ZONE:
275 case WARN_ZONE:
276 if (thermal_zone_bind_cooling_device(thermal, i, cdev,
277 level, 0)) {
278 pr_err("error binding cdev inst %d\n", i);
279 ret = -EINVAL;
280 }
281 th_zone->bind = true;
282 break;
283 default:
284 ret = -EINVAL;
285 }
286 }
287
288 return ret;
289}
290
291/* Unbind callback functions for thermal zone */
292static int exynos_unbind(struct thermal_zone_device *thermal,
293 struct thermal_cooling_device *cdev)
294{
295 int ret = 0, i, tab_size;
296 struct thermal_sensor_conf *data = th_zone->sensor_conf;
297
298 if (th_zone->bind == false)
299 return 0;
300
301 tab_size = data->cooling_data.freq_clip_count;
302
303 if (tab_size == 0)
304 return -EINVAL;
305
306 /* find the cooling device registered*/
307 for (i = 0; i < th_zone->cool_dev_size; i++)
308 if (cdev == th_zone->cool_dev[i])
309 break;
310
311 /* No matching cooling device */
312 if (i == th_zone->cool_dev_size)
313 return 0;
314
315 /* Bind the thermal zone to the cpufreq cooling device */
316 for (i = 0; i < tab_size; i++) {
317 switch (GET_ZONE(i)) {
318 case MONITOR_ZONE:
319 case WARN_ZONE:
320 if (thermal_zone_unbind_cooling_device(thermal, i,
321 cdev)) {
322 pr_err("error unbinding cdev inst=%d\n", i);
323 ret = -EINVAL;
324 }
325 th_zone->bind = false;
326 break;
327 default:
328 ret = -EINVAL;
329 }
330 }
331 return ret;
332}
333
334/* Get temperature callback functions for thermal zone */
335static int exynos_get_temp(struct thermal_zone_device *thermal,
336 unsigned long *temp)
337{
338 void *data;
339
340 if (!th_zone->sensor_conf) {
341 pr_info("Temperature sensor not initialised\n");
342 return -EINVAL;
343 }
344 data = th_zone->sensor_conf->private_data;
345 *temp = th_zone->sensor_conf->read_temperature(data);
346 /* convert the temperature into millicelsius */
347 *temp = *temp * MCELSIUS;
348 return 0;
349}
350
351/* Get temperature callback functions for thermal zone */
352static int exynos_set_emul_temp(struct thermal_zone_device *thermal,
353 unsigned long temp)
354{
355 void *data;
356 int ret = -EINVAL;
357
358 if (!th_zone->sensor_conf) {
359 pr_info("Temperature sensor not initialised\n");
360 return -EINVAL;
361 }
362 data = th_zone->sensor_conf->private_data;
363 if (th_zone->sensor_conf->write_emul_temp)
364 ret = th_zone->sensor_conf->write_emul_temp(data, temp);
365 return ret;
366}
367
368/* Get the temperature trend */
369static int exynos_get_trend(struct thermal_zone_device *thermal,
370 int trip, enum thermal_trend *trend)
371{
372 int ret;
373 unsigned long trip_temp;
374
375 ret = exynos_get_trip_temp(thermal, trip, &trip_temp);
376 if (ret < 0)
377 return ret;
378
379 if (thermal->temperature >= trip_temp)
380 *trend = THERMAL_TREND_RAISE_FULL;
381 else
382 *trend = THERMAL_TREND_DROP_FULL;
383
384 return 0;
385}
386/* Operation callback functions for thermal zone */
387static struct thermal_zone_device_ops const exynos_dev_ops = {
388 .bind = exynos_bind,
389 .unbind = exynos_unbind,
390 .get_temp = exynos_get_temp,
391 .set_emul_temp = exynos_set_emul_temp,
392 .get_trend = exynos_get_trend,
393 .get_mode = exynos_get_mode,
394 .set_mode = exynos_set_mode,
395 .get_trip_type = exynos_get_trip_type,
396 .get_trip_temp = exynos_get_trip_temp,
397 .get_crit_temp = exynos_get_crit_temp,
398};
399
400/*
401 * This function may be called from interrupt based temperature sensor
402 * when threshold is changed.
403 */
404static void exynos_report_trigger(void)
405{
406 unsigned int i;
407 char data[10];
408 char *envp[] = { data, NULL };
409
410 if (!th_zone || !th_zone->therm_dev)
411 return;
412 if (th_zone->bind == false) {
413 for (i = 0; i < th_zone->cool_dev_size; i++) {
414 if (!th_zone->cool_dev[i])
415 continue;
416 exynos_bind(th_zone->therm_dev,
417 th_zone->cool_dev[i]);
418 }
419 }
420
421 thermal_zone_device_update(th_zone->therm_dev);
422
423 mutex_lock(&th_zone->therm_dev->lock);
424 /* Find the level for which trip happened */
425 for (i = 0; i < th_zone->sensor_conf->trip_data.trip_count; i++) {
426 if (th_zone->therm_dev->last_temperature <
427 th_zone->sensor_conf->trip_data.trip_val[i] * MCELSIUS)
428 break;
429 }
430
431 if (th_zone->mode == THERMAL_DEVICE_ENABLED &&
432 !th_zone->sensor_conf->trip_data.trigger_falling) {
433 if (i > 0)
434 th_zone->therm_dev->polling_delay = ACTIVE_INTERVAL;
435 else
436 th_zone->therm_dev->polling_delay = IDLE_INTERVAL;
437 }
438
439 snprintf(data, sizeof(data), "%u", i);
440 kobject_uevent_env(&th_zone->therm_dev->device.kobj, KOBJ_CHANGE, envp);
441 mutex_unlock(&th_zone->therm_dev->lock);
442}
443
444/* Register with the in-kernel thermal management */
445static int exynos_register_thermal(struct thermal_sensor_conf *sensor_conf)
446{
447 int ret;
448 struct cpumask mask_val;
449
450 if (!sensor_conf || !sensor_conf->read_temperature) {
451 pr_err("Temperature sensor not initialised\n");
452 return -EINVAL;
453 }
454
455 th_zone = kzalloc(sizeof(struct exynos_thermal_zone), GFP_KERNEL);
456 if (!th_zone)
457 return -ENOMEM;
458
459 th_zone->sensor_conf = sensor_conf;
460 cpumask_set_cpu(0, &mask_val);
461 th_zone->cool_dev[0] = cpufreq_cooling_register(&mask_val);
462 if (IS_ERR(th_zone->cool_dev[0])) {
463 pr_err("Failed to register cpufreq cooling device\n");
464 ret = -EINVAL;
465 goto err_unregister;
466 }
467 th_zone->cool_dev_size++;
468
469 th_zone->therm_dev = thermal_zone_device_register(sensor_conf->name,
470 EXYNOS_ZONE_COUNT, 0, NULL, &exynos_dev_ops, NULL, 0,
471 sensor_conf->trip_data.trigger_falling ?
472 0 : IDLE_INTERVAL);
473
474 if (IS_ERR(th_zone->therm_dev)) {
475 pr_err("Failed to register thermal zone device\n");
476 ret = PTR_ERR(th_zone->therm_dev);
477 goto err_unregister;
478 }
479 th_zone->mode = THERMAL_DEVICE_ENABLED;
480
481 pr_info("Exynos: Kernel Thermal management registered\n");
482
483 return 0;
484
485err_unregister:
486 exynos_unregister_thermal();
487 return ret;
488}
489
490/* Un-Register with the in-kernel thermal management */
491static void exynos_unregister_thermal(void)
492{
493 int i;
494
495 if (!th_zone)
496 return;
497
498 if (th_zone->therm_dev)
499 thermal_zone_device_unregister(th_zone->therm_dev);
500
501 for (i = 0; i < th_zone->cool_dev_size; i++) {
502 if (th_zone->cool_dev[i])
503 cpufreq_cooling_unregister(th_zone->cool_dev[i]);
504 }
505
506 kfree(th_zone);
507 pr_info("Exynos: Kernel Thermal management unregistered\n");
508}
509
510/* 103/*
511 * TMU treats temperature as a mapped temperature code. 104 * TMU treats temperature as a mapped temperature code.
512 * The temperature is converted differently depending on the calibration type. 105 * The temperature is converted differently depending on the calibration type.