aboutsummaryrefslogtreecommitdiffstats
path: root/drivers
diff options
context:
space:
mode:
Diffstat (limited to 'drivers')
-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 */