aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/thermal
diff options
context:
space:
mode:
authorAmit Daniel Kachhap <amit.kachhap@linaro.org>2012-08-16 07:41:43 -0400
committerZhang Rui <rui.zhang@intel.com>2012-09-24 02:44:38 -0400
commit7e0b55e60659972a563e68fbfdccabf78e25afc7 (patch)
tree220dc4d7ded42a2d3270b4268cbafc9e1177caaa /drivers/thermal
parentf22d9c03ccc9339d02579914d85b2db81a985a8e (diff)
thermal: exynos: register the tmu sensor with the kernel thermal layer
This code added creates a link between temperature sensors, linux thermal framework and cooling devices for samsung exynos platform. This layer monitors the temperature from the sensor and informs the generic thermal layer to take the necessary cooling action. [akpm@linux-foundation.org: fix comment layout] Signed-off-by: Amit Daniel Kachhap <amit.kachhap@linaro.org> Acked-by: Guenter Roeck <guenter.roeck@ericsson.com> Cc: SangWook Ju <sw.ju@samsung.com> Cc: Durgadoss <durgadoss.r@intel.com> Cc: Len Brown <lenb@kernel.org> Cc: Jean Delvare <khali@linux-fr.org> Cc: Kyungmin Park <kmpark@infradead.org> Cc: Kukjin Kim <kgene.kim@samsung.com> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Signed-off-by: Amit Daniel Kachhap <amit.daniel@samsung.com> Signed-off-by: Zhang Rui <rui.zhang@intel.com>
Diffstat (limited to 'drivers/thermal')
-rw-r--r--drivers/thermal/exynos_thermal.c406
1 files changed, 404 insertions, 2 deletions
diff --git a/drivers/thermal/exynos_thermal.c b/drivers/thermal/exynos_thermal.c
index c9a33dd5e4d8..e79cdc9935b7 100644
--- a/drivers/thermal/exynos_thermal.c
+++ b/drivers/thermal/exynos_thermal.c
@@ -34,6 +34,9 @@
34#include <linux/io.h> 34#include <linux/io.h>
35#include <linux/mutex.h> 35#include <linux/mutex.h>
36#include <linux/platform_data/exynos_thermal.h> 36#include <linux/platform_data/exynos_thermal.h>
37#include <linux/thermal.h>
38#include <linux/cpufreq.h>
39#include <linux/cpu_cooling.h>
37#include <linux/of.h> 40#include <linux/of.h>
38 41
39#include <plat/cpu.h> 42#include <plat/cpu.h>
@@ -94,6 +97,7 @@
94 97
95#define ACTIVE_INTERVAL 500 98#define ACTIVE_INTERVAL 500
96#define IDLE_INTERVAL 10000 99#define IDLE_INTERVAL 10000
100#define MCELSIUS 1000
97 101
98/* CPU Zone information */ 102/* CPU Zone information */
99#define PANIC_ZONE 4 103#define PANIC_ZONE 4
@@ -104,6 +108,8 @@
104#define GET_ZONE(trip) (trip + 2) 108#define GET_ZONE(trip) (trip + 2)
105#define GET_TRIP(zone) (zone - 2) 109#define GET_TRIP(zone) (zone - 2)
106 110
111#define EXYNOS_ZONE_COUNT 3
112
107struct exynos_tmu_data { 113struct exynos_tmu_data {
108 struct exynos_tmu_platform_data *pdata; 114 struct exynos_tmu_platform_data *pdata;
109 struct resource *mem; 115 struct resource *mem;
@@ -116,6 +122,371 @@ struct exynos_tmu_data {
116 u8 temp_error1, temp_error2; 122 u8 temp_error1, temp_error2;
117}; 123};
118 124
125struct thermal_trip_point_conf {
126 int trip_val[MAX_TRIP_COUNT];
127 int trip_count;
128};
129
130struct thermal_cooling_conf {
131 struct freq_clip_table freq_data[MAX_TRIP_COUNT];
132 int freq_clip_count;
133};
134
135struct thermal_sensor_conf {
136 char name[SENSOR_NAME_LEN];
137 int (*read_temperature)(void *data);
138 struct thermal_trip_point_conf trip_data;
139 struct thermal_cooling_conf cooling_data;
140 void *private_data;
141};
142
143struct exynos_thermal_zone {
144 enum thermal_device_mode mode;
145 struct thermal_zone_device *therm_dev;
146 struct thermal_cooling_device *cool_dev[MAX_COOLING_DEVICE];
147 unsigned int cool_dev_size;
148 struct platform_device *exynos4_dev;
149 struct thermal_sensor_conf *sensor_conf;
150 bool bind;
151};
152
153static struct exynos_thermal_zone *th_zone;
154static void exynos_unregister_thermal(void);
155static int exynos_register_thermal(struct thermal_sensor_conf *sensor_conf);
156
157/* Get mode callback functions for thermal zone */
158static int exynos_get_mode(struct thermal_zone_device *thermal,
159 enum thermal_device_mode *mode)
160{
161 if (th_zone)
162 *mode = th_zone->mode;
163 return 0;
164}
165
166/* Set mode callback functions for thermal zone */
167static int exynos_set_mode(struct thermal_zone_device *thermal,
168 enum thermal_device_mode mode)
169{
170 if (!th_zone->therm_dev) {
171 pr_notice("thermal zone not registered\n");
172 return 0;
173 }
174
175 mutex_lock(&th_zone->therm_dev->lock);
176
177 if (mode == THERMAL_DEVICE_ENABLED)
178 th_zone->therm_dev->polling_delay = IDLE_INTERVAL;
179 else
180 th_zone->therm_dev->polling_delay = 0;
181
182 mutex_unlock(&th_zone->therm_dev->lock);
183
184 th_zone->mode = mode;
185 thermal_zone_device_update(th_zone->therm_dev);
186 pr_info("thermal polling set for duration=%d msec\n",
187 th_zone->therm_dev->polling_delay);
188 return 0;
189}
190
191
192/* Get trip type callback functions for thermal zone */
193static int exynos_get_trip_type(struct thermal_zone_device *thermal, int trip,
194 enum thermal_trip_type *type)
195{
196 switch (GET_ZONE(trip)) {
197 case MONITOR_ZONE:
198 case WARN_ZONE:
199 *type = THERMAL_TRIP_ACTIVE;
200 break;
201 case PANIC_ZONE:
202 *type = THERMAL_TRIP_CRITICAL;
203 break;
204 default:
205 return -EINVAL;
206 }
207 return 0;
208}
209
210/* Get trip temperature callback functions for thermal zone */
211static int exynos_get_trip_temp(struct thermal_zone_device *thermal, int trip,
212 unsigned long *temp)
213{
214 if (trip < GET_TRIP(MONITOR_ZONE) || trip > GET_TRIP(PANIC_ZONE))
215 return -EINVAL;
216
217 *temp = th_zone->sensor_conf->trip_data.trip_val[trip];
218 /* convert the temperature into millicelsius */
219 *temp = *temp * MCELSIUS;
220
221 return 0;
222}
223
224/* Get critical temperature callback functions for thermal zone */
225static int exynos_get_crit_temp(struct thermal_zone_device *thermal,
226 unsigned long *temp)
227{
228 int ret;
229 /* Panic zone */
230 ret = exynos_get_trip_temp(thermal, GET_TRIP(PANIC_ZONE), temp);
231 return ret;
232}
233
234static int exynos_get_frequency_level(unsigned int cpu, unsigned int freq)
235{
236 int i = 0, ret = -EINVAL;
237 struct cpufreq_frequency_table *table = NULL;
238#ifdef CONFIG_CPU_FREQ
239 table = cpufreq_frequency_get_table(cpu);
240#endif
241 if (!table)
242 return ret;
243
244 while (table[i].frequency != CPUFREQ_TABLE_END) {
245 if (table[i].frequency == CPUFREQ_ENTRY_INVALID)
246 continue;
247 if (table[i].frequency == freq)
248 return i;
249 i++;
250 }
251 return ret;
252}
253
254/* Bind callback functions for thermal zone */
255static int exynos_bind(struct thermal_zone_device *thermal,
256 struct thermal_cooling_device *cdev)
257{
258 int ret = 0, i, tab_size, level;
259 struct freq_clip_table *tab_ptr, *clip_data;
260 struct thermal_sensor_conf *data = th_zone->sensor_conf;
261
262 tab_ptr = (struct freq_clip_table *)data->cooling_data.freq_data;
263 tab_size = data->cooling_data.freq_clip_count;
264
265 if (tab_ptr == NULL || tab_size == 0)
266 return -EINVAL;
267
268 /* find the cooling device registered*/
269 for (i = 0; i < th_zone->cool_dev_size; i++)
270 if (cdev == th_zone->cool_dev[i])
271 break;
272
273 /* No matching cooling device */
274 if (i == th_zone->cool_dev_size)
275 return 0;
276
277 /* Bind the thermal zone to the cpufreq cooling device */
278 for (i = 0; i < tab_size; i++) {
279 clip_data = (struct freq_clip_table *)&(tab_ptr[i]);
280 level = exynos_get_frequency_level(0, clip_data->freq_clip_max);
281 if (level < 0)
282 return 0;
283 switch (GET_ZONE(i)) {
284 case MONITOR_ZONE:
285 case WARN_ZONE:
286 if (thermal_zone_bind_cooling_device(thermal, i, cdev,
287 level, level)) {
288 pr_err("error binding cdev inst %d\n", i);
289 ret = -EINVAL;
290 }
291 th_zone->bind = true;
292 break;
293 default:
294 ret = -EINVAL;
295 }
296 }
297
298 return ret;
299}
300
301/* Unbind callback functions for thermal zone */
302static int exynos_unbind(struct thermal_zone_device *thermal,
303 struct thermal_cooling_device *cdev)
304{
305 int ret = 0, i, tab_size;
306 struct thermal_sensor_conf *data = th_zone->sensor_conf;
307
308 if (th_zone->bind == false)
309 return 0;
310
311 tab_size = data->cooling_data.freq_clip_count;
312
313 if (tab_size == 0)
314 return -EINVAL;
315
316 /* find the cooling device registered*/
317 for (i = 0; i < th_zone->cool_dev_size; i++)
318 if (cdev == th_zone->cool_dev[i])
319 break;
320
321 /* No matching cooling device */
322 if (i == th_zone->cool_dev_size)
323 return 0;
324
325 /* Bind the thermal zone to the cpufreq cooling device */
326 for (i = 0; i < tab_size; i++) {
327 switch (GET_ZONE(i)) {
328 case MONITOR_ZONE:
329 case WARN_ZONE:
330 if (thermal_zone_unbind_cooling_device(thermal, i,
331 cdev)) {
332 pr_err("error unbinding cdev inst=%d\n", i);
333 ret = -EINVAL;
334 }
335 th_zone->bind = false;
336 break;
337 default:
338 ret = -EINVAL;
339 }
340 }
341 return ret;
342}
343
344/* Get temperature callback functions for thermal zone */
345static int exynos_get_temp(struct thermal_zone_device *thermal,
346 unsigned long *temp)
347{
348 void *data;
349
350 if (!th_zone->sensor_conf) {
351 pr_info("Temperature sensor not initialised\n");
352 return -EINVAL;
353 }
354 data = th_zone->sensor_conf->private_data;
355 *temp = th_zone->sensor_conf->read_temperature(data);
356 /* convert the temperature into millicelsius */
357 *temp = *temp * MCELSIUS;
358 return 0;
359}
360
361/* Get the temperature trend */
362static int exynos_get_trend(struct thermal_zone_device *thermal,
363 int trip, enum thermal_trend *trend)
364{
365 if (thermal->temperature >= trip)
366 *trend = THERMAL_TREND_RAISING;
367 else
368 *trend = THERMAL_TREND_DROPPING;
369
370 return 0;
371}
372/* Operation callback functions for thermal zone */
373static struct thermal_zone_device_ops const exynos_dev_ops = {
374 .bind = exynos_bind,
375 .unbind = exynos_unbind,
376 .get_temp = exynos_get_temp,
377 .get_trend = exynos_get_trend,
378 .get_mode = exynos_get_mode,
379 .set_mode = exynos_set_mode,
380 .get_trip_type = exynos_get_trip_type,
381 .get_trip_temp = exynos_get_trip_temp,
382 .get_crit_temp = exynos_get_crit_temp,
383};
384
385/*
386 * This function may be called from interrupt based temperature sensor
387 * when threshold is changed.
388 */
389static void exynos_report_trigger(void)
390{
391 unsigned int i;
392 char data[10];
393 char *envp[] = { data, NULL };
394
395 if (!th_zone || !th_zone->therm_dev)
396 return;
397 if (th_zone->bind == false) {
398 for (i = 0; i < th_zone->cool_dev_size; i++) {
399 if (!th_zone->cool_dev[i])
400 continue;
401 exynos_bind(th_zone->therm_dev,
402 th_zone->cool_dev[i]);
403 }
404 }
405
406 thermal_zone_device_update(th_zone->therm_dev);
407
408 mutex_lock(&th_zone->therm_dev->lock);
409 /* Find the level for which trip happened */
410 for (i = 0; i < th_zone->sensor_conf->trip_data.trip_count; i++) {
411 if (th_zone->therm_dev->last_temperature <
412 th_zone->sensor_conf->trip_data.trip_val[i] * MCELSIUS)
413 break;
414 }
415
416 if (th_zone->mode == THERMAL_DEVICE_ENABLED) {
417 if (i > 0)
418 th_zone->therm_dev->polling_delay = ACTIVE_INTERVAL;
419 else
420 th_zone->therm_dev->polling_delay = IDLE_INTERVAL;
421 }
422
423 snprintf(data, sizeof(data), "%u", i);
424 kobject_uevent_env(&th_zone->therm_dev->device.kobj, KOBJ_CHANGE, envp);
425 mutex_unlock(&th_zone->therm_dev->lock);
426}
427
428/* Register with the in-kernel thermal management */
429static int exynos_register_thermal(struct thermal_sensor_conf *sensor_conf)
430{
431 int ret;
432 struct cpumask mask_val;
433
434 if (!sensor_conf || !sensor_conf->read_temperature) {
435 pr_err("Temperature sensor not initialised\n");
436 return -EINVAL;
437 }
438
439 th_zone = kzalloc(sizeof(struct exynos_thermal_zone), GFP_KERNEL);
440 if (!th_zone)
441 return -ENOMEM;
442
443 th_zone->sensor_conf = sensor_conf;
444 cpumask_set_cpu(0, &mask_val);
445 th_zone->cool_dev[0] = cpufreq_cooling_register(&mask_val);
446 if (IS_ERR(th_zone->cool_dev[0])) {
447 pr_err("Failed to register cpufreq cooling device\n");
448 ret = -EINVAL;
449 goto err_unregister;
450 }
451 th_zone->cool_dev_size++;
452
453 th_zone->therm_dev = thermal_zone_device_register(sensor_conf->name,
454 EXYNOS_ZONE_COUNT, 0, NULL, &exynos_dev_ops, 0,
455 IDLE_INTERVAL);
456
457 if (IS_ERR(th_zone->therm_dev)) {
458 pr_err("Failed to register thermal zone device\n");
459 ret = -EINVAL;
460 goto err_unregister;
461 }
462 th_zone->mode = THERMAL_DEVICE_ENABLED;
463
464 pr_info("Exynos: Kernel Thermal management registered\n");
465
466 return 0;
467
468err_unregister:
469 exynos_unregister_thermal();
470 return ret;
471}
472
473/* Un-Register with the in-kernel thermal management */
474static void exynos_unregister_thermal(void)
475{
476 int i;
477
478 if (th_zone && th_zone->therm_dev)
479 thermal_zone_device_unregister(th_zone->therm_dev);
480
481 for (i = 0; i < th_zone->cool_dev_size; i++) {
482 if (th_zone && th_zone->cool_dev[i])
483 cpufreq_cooling_unregister(th_zone->cool_dev[i]);
484 }
485
486 kfree(th_zone);
487 pr_info("Exynos: Kernel Thermal management unregistered\n");
488}
489
119/* 490/*
120 * TMU treats temperature as a mapped temperature code. 491 * TMU treats temperature as a mapped temperature code.
121 * The temperature is converted differently depending on the calibration type. 492 * The temperature is converted differently depending on the calibration type.
@@ -336,6 +707,7 @@ static void exynos_tmu_work(struct work_struct *work)
336 707
337 clk_disable(data->clk); 708 clk_disable(data->clk);
338 mutex_unlock(&data->lock); 709 mutex_unlock(&data->lock);
710 exynos_report_trigger();
339 enable_irq(data->irq); 711 enable_irq(data->irq);
340} 712}
341 713
@@ -348,12 +720,16 @@ static irqreturn_t exynos_tmu_irq(int irq, void *id)
348 720
349 return IRQ_HANDLED; 721 return IRQ_HANDLED;
350} 722}
351 723static struct thermal_sensor_conf exynos_sensor_conf = {
724 .name = "exynos-therm",
725 .read_temperature = (int (*)(void *))exynos_tmu_read,
726}
727;
352static int __devinit exynos_tmu_probe(struct platform_device *pdev) 728static int __devinit exynos_tmu_probe(struct platform_device *pdev)
353{ 729{
354 struct exynos_tmu_data *data; 730 struct exynos_tmu_data *data;
355 struct exynos_tmu_platform_data *pdata = pdev->dev.platform_data; 731 struct exynos_tmu_platform_data *pdata = pdev->dev.platform_data;
356 int ret; 732 int ret, i;
357 733
358 if (!pdata) { 734 if (!pdata) {
359 dev_err(&pdev->dev, "No platform init data supplied.\n"); 735 dev_err(&pdev->dev, "No platform init data supplied.\n");
@@ -431,6 +807,30 @@ static int __devinit exynos_tmu_probe(struct platform_device *pdev)
431 807
432 exynos_tmu_control(pdev, true); 808 exynos_tmu_control(pdev, true);
433 809
810 /* Register the sensor with thermal management interface */
811 (&exynos_sensor_conf)->private_data = data;
812 exynos_sensor_conf.trip_data.trip_count = pdata->trigger_level0_en +
813 pdata->trigger_level1_en + pdata->trigger_level2_en +
814 pdata->trigger_level3_en;
815
816 for (i = 0; i < exynos_sensor_conf.trip_data.trip_count; i++)
817 exynos_sensor_conf.trip_data.trip_val[i] =
818 pdata->threshold + pdata->trigger_levels[i];
819
820 exynos_sensor_conf.cooling_data.freq_clip_count =
821 pdata->freq_tab_count;
822 for (i = 0; i < pdata->freq_tab_count; i++) {
823 exynos_sensor_conf.cooling_data.freq_data[i].freq_clip_max =
824 pdata->freq_tab[i].freq_clip_max;
825 exynos_sensor_conf.cooling_data.freq_data[i].temp_level =
826 pdata->freq_tab[i].temp_level;
827 }
828
829 ret = exynos_register_thermal(&exynos_sensor_conf);
830 if (ret) {
831 dev_err(&pdev->dev, "Failed to register thermal interface\n");
832 goto err_clk;
833 }
434 return 0; 834 return 0;
435err_clk: 835err_clk:
436 platform_set_drvdata(pdev, NULL); 836 platform_set_drvdata(pdev, NULL);
@@ -453,6 +853,8 @@ static int __devexit exynos_tmu_remove(struct platform_device *pdev)
453 853
454 exynos_tmu_control(pdev, false); 854 exynos_tmu_control(pdev, false);
455 855
856 exynos_unregister_thermal();
857
456 clk_put(data->clk); 858 clk_put(data->clk);
457 859
458 free_irq(data->irq, data); 860 free_irq(data->irq, data);