aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/thermal/samsung
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
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')
-rw-r--r--drivers/thermal/samsung/Kconfig19
-rw-r--r--drivers/thermal/samsung/Makefile4
-rw-r--r--drivers/thermal/samsung/exynos_thermal.c419
-rw-r--r--drivers/thermal/samsung/exynos_thermal_common.c385
-rw-r--r--drivers/thermal/samsung/exynos_thermal_common.h83
5 files changed, 491 insertions, 419 deletions
diff --git a/drivers/thermal/samsung/Kconfig b/drivers/thermal/samsung/Kconfig
index 2cf31ad90fa9..f8100b1b953a 100644
--- a/drivers/thermal/samsung/Kconfig
+++ b/drivers/thermal/samsung/Kconfig
@@ -1,8 +1,17 @@
1config EXYNOS_THERMAL 1config EXYNOS_THERMAL
2 tristate "Temperature sensor on Samsung EXYNOS" 2 tristate "Exynos thermal management unit driver"
3 depends on ARCH_HAS_BANDGAP 3 depends on ARCH_HAS_BANDGAP
4 help 4 help
5 If you say yes here you get support for TMU (Thermal Management 5 If you say yes here you get support for the TMU (Thermal Management
6 Unit) on SAMSUNG EXYNOS series of SoC. This helps in registering 6 Unit) driver for SAMSUNG EXYNOS series of soc. This driver initialises
7 the exynos thermal driver with the core thermal layer and cpu 7 the TMU, reports temperature and handles cooling action if defined.
8 cooling API's. 8 This driver uses the exynos core thermal API's.
9
10config EXYNOS_THERMAL_CORE
11 bool "Core thermal framework support for EXYNOS SOC's"
12 depends on EXYNOS_THERMAL
13 help
14 If you say yes here you get support for EXYNOS TMU
15 (Thermal Management Unit) common registration/unregistration
16 functions to the core thermal layer and also to use the generic
17 cpu cooling API's.
diff --git a/drivers/thermal/samsung/Makefile b/drivers/thermal/samsung/Makefile
index 1fe6d939e8f1..6227d4fbec08 100644
--- a/drivers/thermal/samsung/Makefile
+++ b/drivers/thermal/samsung/Makefile
@@ -1,4 +1,6 @@
1# 1#
2# Samsung thermal specific Makefile 2# Samsung thermal specific Makefile
3# 3#
4obj-$(CONFIG_EXYNOS_THERMAL) += exynos_thermal.o 4obj-$(CONFIG_EXYNOS_THERMAL) += exynos_soc_thermal.o
5exynos_soc_thermal-y := exynos_thermal.o
6exynos_soc_thermal-$(CONFIG_EXYNOS_THERMAL_CORE) += exynos_thermal_common.o
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.
diff --git a/drivers/thermal/samsung/exynos_thermal_common.c b/drivers/thermal/samsung/exynos_thermal_common.c
new file mode 100644
index 000000000000..f20f458be0a0
--- /dev/null
+++ b/drivers/thermal/samsung/exynos_thermal_common.c
@@ -0,0 +1,385 @@
1/*
2 * exynos_thermal_common.c - Samsung EXYNOS common thermal file
3 *
4 * Copyright (C) 2013 Samsung Electronics
5 * Amit Daniel Kachhap <amit.daniel@samsung.com>
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20 *
21 */
22
23#include <linux/cpu_cooling.h>
24#include <linux/err.h>
25#include <linux/platform_data/exynos_thermal.h>
26#include <linux/slab.h>
27#include <linux/thermal.h>
28
29#include "exynos_thermal_common.h"
30
31struct exynos_thermal_zone {
32 enum thermal_device_mode mode;
33 struct thermal_zone_device *therm_dev;
34 struct thermal_cooling_device *cool_dev[MAX_COOLING_DEVICE];
35 unsigned int cool_dev_size;
36 struct platform_device *exynos4_dev;
37 struct thermal_sensor_conf *sensor_conf;
38 bool bind;
39};
40
41static struct exynos_thermal_zone *th_zone;
42
43/* Get mode callback functions for thermal zone */
44static int exynos_get_mode(struct thermal_zone_device *thermal,
45 enum thermal_device_mode *mode)
46{
47 if (th_zone)
48 *mode = th_zone->mode;
49 return 0;
50}
51
52/* Set mode callback functions for thermal zone */
53static int exynos_set_mode(struct thermal_zone_device *thermal,
54 enum thermal_device_mode mode)
55{
56 if (!th_zone->therm_dev) {
57 pr_notice("thermal zone not registered\n");
58 return 0;
59 }
60
61 mutex_lock(&th_zone->therm_dev->lock);
62
63 if (mode == THERMAL_DEVICE_ENABLED &&
64 !th_zone->sensor_conf->trip_data.trigger_falling)
65 th_zone->therm_dev->polling_delay = IDLE_INTERVAL;
66 else
67 th_zone->therm_dev->polling_delay = 0;
68
69 mutex_unlock(&th_zone->therm_dev->lock);
70
71 th_zone->mode = mode;
72 thermal_zone_device_update(th_zone->therm_dev);
73 pr_info("thermal polling set for duration=%d msec\n",
74 th_zone->therm_dev->polling_delay);
75 return 0;
76}
77
78
79/* Get trip type callback functions for thermal zone */
80static int exynos_get_trip_type(struct thermal_zone_device *thermal, int trip,
81 enum thermal_trip_type *type)
82{
83 switch (GET_ZONE(trip)) {
84 case MONITOR_ZONE:
85 case WARN_ZONE:
86 *type = THERMAL_TRIP_ACTIVE;
87 break;
88 case PANIC_ZONE:
89 *type = THERMAL_TRIP_CRITICAL;
90 break;
91 default:
92 return -EINVAL;
93 }
94 return 0;
95}
96
97/* Get trip temperature callback functions for thermal zone */
98static int exynos_get_trip_temp(struct thermal_zone_device *thermal, int trip,
99 unsigned long *temp)
100{
101 if (trip < GET_TRIP(MONITOR_ZONE) || trip > GET_TRIP(PANIC_ZONE))
102 return -EINVAL;
103
104 *temp = th_zone->sensor_conf->trip_data.trip_val[trip];
105 /* convert the temperature into millicelsius */
106 *temp = *temp * MCELSIUS;
107
108 return 0;
109}
110
111/* Get critical temperature callback functions for thermal zone */
112static int exynos_get_crit_temp(struct thermal_zone_device *thermal,
113 unsigned long *temp)
114{
115 int ret;
116 /* Panic zone */
117 ret = exynos_get_trip_temp(thermal, GET_TRIP(PANIC_ZONE), temp);
118 return ret;
119}
120
121/* Bind callback functions for thermal zone */
122static int exynos_bind(struct thermal_zone_device *thermal,
123 struct thermal_cooling_device *cdev)
124{
125 int ret = 0, i, tab_size, level;
126 struct freq_clip_table *tab_ptr, *clip_data;
127 struct thermal_sensor_conf *data = th_zone->sensor_conf;
128
129 tab_ptr = (struct freq_clip_table *)data->cooling_data.freq_data;
130 tab_size = data->cooling_data.freq_clip_count;
131
132 if (tab_ptr == NULL || tab_size == 0)
133 return -EINVAL;
134
135 /* find the cooling device registered*/
136 for (i = 0; i < th_zone->cool_dev_size; i++)
137 if (cdev == th_zone->cool_dev[i])
138 break;
139
140 /* No matching cooling device */
141 if (i == th_zone->cool_dev_size)
142 return 0;
143
144 /* Bind the thermal zone to the cpufreq cooling device */
145 for (i = 0; i < tab_size; i++) {
146 clip_data = (struct freq_clip_table *)&(tab_ptr[i]);
147 level = cpufreq_cooling_get_level(0, clip_data->freq_clip_max);
148 if (level == THERMAL_CSTATE_INVALID)
149 return 0;
150 switch (GET_ZONE(i)) {
151 case MONITOR_ZONE:
152 case WARN_ZONE:
153 if (thermal_zone_bind_cooling_device(thermal, i, cdev,
154 level, 0)) {
155 pr_err("error binding cdev inst %d\n", i);
156 ret = -EINVAL;
157 }
158 th_zone->bind = true;
159 break;
160 default:
161 ret = -EINVAL;
162 }
163 }
164
165 return ret;
166}
167
168/* Unbind callback functions for thermal zone */
169static int exynos_unbind(struct thermal_zone_device *thermal,
170 struct thermal_cooling_device *cdev)
171{
172 int ret = 0, i, tab_size;
173 struct thermal_sensor_conf *data = th_zone->sensor_conf;
174
175 if (th_zone->bind == false)
176 return 0;
177
178 tab_size = data->cooling_data.freq_clip_count;
179
180 if (tab_size == 0)
181 return -EINVAL;
182
183 /* find the cooling device registered*/
184 for (i = 0; i < th_zone->cool_dev_size; i++)
185 if (cdev == th_zone->cool_dev[i])
186 break;
187
188 /* No matching cooling device */
189 if (i == th_zone->cool_dev_size)
190 return 0;
191
192 /* Bind the thermal zone to the cpufreq cooling device */
193 for (i = 0; i < tab_size; i++) {
194 switch (GET_ZONE(i)) {
195 case MONITOR_ZONE:
196 case WARN_ZONE:
197 if (thermal_zone_unbind_cooling_device(thermal, i,
198 cdev)) {
199 pr_err("error unbinding cdev inst=%d\n", i);
200 ret = -EINVAL;
201 }
202 th_zone->bind = false;
203 break;
204 default:
205 ret = -EINVAL;
206 }
207 }
208 return ret;
209}
210
211/* Get temperature callback functions for thermal zone */
212static int exynos_get_temp(struct thermal_zone_device *thermal,
213 unsigned long *temp)
214{
215 void *data;
216
217 if (!th_zone->sensor_conf) {
218 pr_info("Temperature sensor not initialised\n");
219 return -EINVAL;
220 }
221 data = th_zone->sensor_conf->private_data;
222 *temp = th_zone->sensor_conf->read_temperature(data);
223 /* convert the temperature into millicelsius */
224 *temp = *temp * MCELSIUS;
225 return 0;
226}
227
228/* Get temperature callback functions for thermal zone */
229static int exynos_set_emul_temp(struct thermal_zone_device *thermal,
230 unsigned long temp)
231{
232 void *data;
233 int ret = -EINVAL;
234
235 if (!th_zone->sensor_conf) {
236 pr_info("Temperature sensor not initialised\n");
237 return -EINVAL;
238 }
239 data = th_zone->sensor_conf->private_data;
240 if (th_zone->sensor_conf->write_emul_temp)
241 ret = th_zone->sensor_conf->write_emul_temp(data, temp);
242 return ret;
243}
244
245/* Get the temperature trend */
246static int exynos_get_trend(struct thermal_zone_device *thermal,
247 int trip, enum thermal_trend *trend)
248{
249 int ret;
250 unsigned long trip_temp;
251
252 ret = exynos_get_trip_temp(thermal, trip, &trip_temp);
253 if (ret < 0)
254 return ret;
255
256 if (thermal->temperature >= trip_temp)
257 *trend = THERMAL_TREND_RAISE_FULL;
258 else
259 *trend = THERMAL_TREND_DROP_FULL;
260
261 return 0;
262}
263/* Operation callback functions for thermal zone */
264static struct thermal_zone_device_ops const exynos_dev_ops = {
265 .bind = exynos_bind,
266 .unbind = exynos_unbind,
267 .get_temp = exynos_get_temp,
268 .set_emul_temp = exynos_set_emul_temp,
269 .get_trend = exynos_get_trend,
270 .get_mode = exynos_get_mode,
271 .set_mode = exynos_set_mode,
272 .get_trip_type = exynos_get_trip_type,
273 .get_trip_temp = exynos_get_trip_temp,
274 .get_crit_temp = exynos_get_crit_temp,
275};
276
277/*
278 * This function may be called from interrupt based temperature sensor
279 * when threshold is changed.
280 */
281void exynos_report_trigger(void)
282{
283 unsigned int i;
284 char data[10];
285 char *envp[] = { data, NULL };
286
287 if (!th_zone || !th_zone->therm_dev)
288 return;
289 if (th_zone->bind == false) {
290 for (i = 0; i < th_zone->cool_dev_size; i++) {
291 if (!th_zone->cool_dev[i])
292 continue;
293 exynos_bind(th_zone->therm_dev,
294 th_zone->cool_dev[i]);
295 }
296 }
297
298 thermal_zone_device_update(th_zone->therm_dev);
299
300 mutex_lock(&th_zone->therm_dev->lock);
301 /* Find the level for which trip happened */
302 for (i = 0; i < th_zone->sensor_conf->trip_data.trip_count; i++) {
303 if (th_zone->therm_dev->last_temperature <
304 th_zone->sensor_conf->trip_data.trip_val[i] * MCELSIUS)
305 break;
306 }
307
308 if (th_zone->mode == THERMAL_DEVICE_ENABLED &&
309 !th_zone->sensor_conf->trip_data.trigger_falling) {
310 if (i > 0)
311 th_zone->therm_dev->polling_delay = ACTIVE_INTERVAL;
312 else
313 th_zone->therm_dev->polling_delay = IDLE_INTERVAL;
314 }
315
316 snprintf(data, sizeof(data), "%u", i);
317 kobject_uevent_env(&th_zone->therm_dev->device.kobj, KOBJ_CHANGE, envp);
318 mutex_unlock(&th_zone->therm_dev->lock);
319}
320
321/* Register with the in-kernel thermal management */
322int exynos_register_thermal(struct thermal_sensor_conf *sensor_conf)
323{
324 int ret;
325 struct cpumask mask_val;
326
327 if (!sensor_conf || !sensor_conf->read_temperature) {
328 pr_err("Temperature sensor not initialised\n");
329 return -EINVAL;
330 }
331
332 th_zone = kzalloc(sizeof(struct exynos_thermal_zone), GFP_KERNEL);
333 if (!th_zone)
334 return -ENOMEM;
335
336 th_zone->sensor_conf = sensor_conf;
337 cpumask_set_cpu(0, &mask_val);
338 th_zone->cool_dev[0] = cpufreq_cooling_register(&mask_val);
339 if (IS_ERR(th_zone->cool_dev[0])) {
340 pr_err("Failed to register cpufreq cooling device\n");
341 ret = -EINVAL;
342 goto err_unregister;
343 }
344 th_zone->cool_dev_size++;
345
346 th_zone->therm_dev = thermal_zone_device_register(sensor_conf->name,
347 EXYNOS_ZONE_COUNT, 0, NULL, &exynos_dev_ops, NULL, 0,
348 sensor_conf->trip_data.trigger_falling ?
349 0 : IDLE_INTERVAL);
350
351 if (IS_ERR(th_zone->therm_dev)) {
352 pr_err("Failed to register thermal zone device\n");
353 ret = PTR_ERR(th_zone->therm_dev);
354 goto err_unregister;
355 }
356 th_zone->mode = THERMAL_DEVICE_ENABLED;
357
358 pr_info("Exynos: Kernel Thermal management registered\n");
359
360 return 0;
361
362err_unregister:
363 exynos_unregister_thermal();
364 return ret;
365}
366
367/* Un-Register with the in-kernel thermal management */
368void exynos_unregister_thermal(void)
369{
370 int i;
371
372 if (!th_zone)
373 return;
374
375 if (th_zone->therm_dev)
376 thermal_zone_device_unregister(th_zone->therm_dev);
377
378 for (i = 0; i < th_zone->cool_dev_size; i++) {
379 if (th_zone->cool_dev[i])
380 cpufreq_cooling_unregister(th_zone->cool_dev[i]);
381 }
382
383 kfree(th_zone);
384 pr_info("Exynos: Kernel Thermal management unregistered\n");
385}
diff --git a/drivers/thermal/samsung/exynos_thermal_common.h b/drivers/thermal/samsung/exynos_thermal_common.h
new file mode 100644
index 000000000000..8df18486078c
--- /dev/null
+++ b/drivers/thermal/samsung/exynos_thermal_common.h
@@ -0,0 +1,83 @@
1/*
2 * exynos_thermal_common.h - Samsung EXYNOS common header file
3 *
4 * Copyright (C) 2013 Samsung Electronics
5 * Amit Daniel Kachhap <amit.daniel@samsung.com>
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20 *
21 */
22
23#ifndef _EXYNOS_THERMAL_COMMON_H
24#define _EXYNOS_THERMAL_COMMON_H
25
26/* In-kernel thermal framework related macros & definations */
27#define SENSOR_NAME_LEN 16
28#define MAX_TRIP_COUNT 8
29#define MAX_COOLING_DEVICE 4
30#define MAX_THRESHOLD_LEVS 4
31
32#define ACTIVE_INTERVAL 500
33#define IDLE_INTERVAL 10000
34#define MCELSIUS 1000
35
36/* CPU Zone information */
37#define PANIC_ZONE 4
38#define WARN_ZONE 3
39#define MONITOR_ZONE 2
40#define SAFE_ZONE 1
41
42#define GET_ZONE(trip) (trip + 2)
43#define GET_TRIP(zone) (zone - 2)
44
45#define EXYNOS_ZONE_COUNT 3
46
47struct thermal_trip_point_conf {
48 int trip_val[MAX_TRIP_COUNT];
49 int trip_count;
50 unsigned char trigger_falling;
51};
52
53struct thermal_cooling_conf {
54 struct freq_clip_table freq_data[MAX_TRIP_COUNT];
55 int freq_clip_count;
56};
57
58struct thermal_sensor_conf {
59 char name[SENSOR_NAME_LEN];
60 int (*read_temperature)(void *data);
61 int (*write_emul_temp)(void *drv_data, unsigned long temp);
62 struct thermal_trip_point_conf trip_data;
63 struct thermal_cooling_conf cooling_data;
64 void *private_data;
65};
66
67/*Functions used exynos based thermal sensor driver*/
68#ifdef CONFIG_EXYNOS_THERMAL_CORE
69void exynos_unregister_thermal(void);
70int exynos_register_thermal(struct thermal_sensor_conf *sensor_conf);
71void exynos_report_trigger(void);
72#else
73static inline void
74exynos_unregister_thermal(void) { return; }
75
76static inline int
77exynos_register_thermal(struct thermal_sensor_conf *sensor_conf) { return 0; }
78
79static inline void
80exynos_report_trigger(void) { return; }
81
82#endif /* CONFIG_EXYNOS_THERMAL_CORE */
83#endif /* _EXYNOS_THERMAL_COMMON_H */