2 * db8500_thermal.c - DB8500 Thermal Management Implementation
4 * Copyright (C) 2012 ST-Ericsson
5 * Copyright (C) 2012 Linaro Ltd.
7 * Author: Hongbo Zhang <hongbo.zhang@linaro.com>
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 2 of the License, or
12 * (at your option) any later version.
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
20 #include <linux/cpu_cooling.h>
21 #include <linux/interrupt.h>
22 #include <linux/mfd/dbx500-prcmu.h>
23 #include <linux/module.h>
25 #include <linux/platform_data/db8500_thermal.h>
26 #include <linux/platform_device.h>
27 #include <linux/slab.h>
28 #include <linux/thermal.h>
30 #define PRCMU_DEFAULT_MEASURE_TIME 0xFFF
31 #define PRCMU_DEFAULT_LOW_TEMP 0
33 struct db8500_thermal_zone
{
34 struct thermal_zone_device
*therm_dev
;
36 struct work_struct therm_work
;
37 struct db8500_thsens_platform_data
*trip_tab
;
38 enum thermal_device_mode mode
;
39 enum thermal_trend trend
;
40 unsigned long cur_temp_pseudo
;
41 unsigned int cur_index
;
44 /* Local function to check if thermal zone matches cooling devices */
45 static int db8500_thermal_match_cdev(struct thermal_cooling_device
*cdev
,
46 struct db8500_trip_point
*trip_point
)
50 if (!strlen(cdev
->type
))
53 for (i
= 0; i
< COOLING_DEV_MAX
; i
++) {
54 if (!strcmp(trip_point
->cdev_name
[i
], cdev
->type
))
61 /* Callback to bind cooling device to thermal zone */
62 static int db8500_cdev_bind(struct thermal_zone_device
*thermal
,
63 struct thermal_cooling_device
*cdev
)
65 struct db8500_thermal_zone
*pzone
= thermal
->devdata
;
66 struct db8500_thsens_platform_data
*ptrips
= pzone
->trip_tab
;
67 unsigned long max_state
, upper
, lower
;
70 cdev
->ops
->get_max_state(cdev
, &max_state
);
72 for (i
= 0; i
< ptrips
->num_trips
; i
++) {
73 if (db8500_thermal_match_cdev(cdev
, &ptrips
->trip_points
[i
]))
76 upper
= lower
= i
> max_state
? max_state
: i
;
78 ret
= thermal_zone_bind_cooling_device(thermal
, i
, cdev
,
81 dev_info(&cdev
->device
, "%s bind to %d: %d-%s\n", cdev
->type
,
82 i
, ret
, ret
? "fail" : "succeed");
88 /* Callback to unbind cooling device from thermal zone */
89 static int db8500_cdev_unbind(struct thermal_zone_device
*thermal
,
90 struct thermal_cooling_device
*cdev
)
92 struct db8500_thermal_zone
*pzone
= thermal
->devdata
;
93 struct db8500_thsens_platform_data
*ptrips
= pzone
->trip_tab
;
96 for (i
= 0; i
< ptrips
->num_trips
; i
++) {
97 if (db8500_thermal_match_cdev(cdev
, &ptrips
->trip_points
[i
]))
100 ret
= thermal_zone_unbind_cooling_device(thermal
, i
, cdev
);
102 dev_info(&cdev
->device
, "%s unbind from %d: %s\n", cdev
->type
,
103 i
, ret
? "fail" : "succeed");
109 /* Callback to get current temperature */
110 static int db8500_sys_get_temp(struct thermal_zone_device
*thermal
,
113 struct db8500_thermal_zone
*pzone
= thermal
->devdata
;
116 * TODO: There is no PRCMU interface to get temperature data currently,
117 * so a pseudo temperature is returned , it works for thermal framework
118 * and this will be fixed when the PRCMU interface is available.
120 *temp
= pzone
->cur_temp_pseudo
;
125 /* Callback to get temperature changing trend */
126 static int db8500_sys_get_trend(struct thermal_zone_device
*thermal
,
127 int trip
, enum thermal_trend
*trend
)
129 struct db8500_thermal_zone
*pzone
= thermal
->devdata
;
131 *trend
= pzone
->trend
;
136 /* Callback to get thermal zone mode */
137 static int db8500_sys_get_mode(struct thermal_zone_device
*thermal
,
138 enum thermal_device_mode
*mode
)
140 struct db8500_thermal_zone
*pzone
= thermal
->devdata
;
142 mutex_lock(&pzone
->th_lock
);
144 mutex_unlock(&pzone
->th_lock
);
149 /* Callback to set thermal zone mode */
150 static int db8500_sys_set_mode(struct thermal_zone_device
*thermal
,
151 enum thermal_device_mode mode
)
153 struct db8500_thermal_zone
*pzone
= thermal
->devdata
;
155 mutex_lock(&pzone
->th_lock
);
158 if (mode
== THERMAL_DEVICE_ENABLED
)
159 schedule_work(&pzone
->therm_work
);
161 mutex_unlock(&pzone
->th_lock
);
166 /* Callback to get trip point type */
167 static int db8500_sys_get_trip_type(struct thermal_zone_device
*thermal
,
168 int trip
, enum thermal_trip_type
*type
)
170 struct db8500_thermal_zone
*pzone
= thermal
->devdata
;
171 struct db8500_thsens_platform_data
*ptrips
= pzone
->trip_tab
;
173 if (trip
>= ptrips
->num_trips
)
176 *type
= ptrips
->trip_points
[trip
].type
;
181 /* Callback to get trip point temperature */
182 static int db8500_sys_get_trip_temp(struct thermal_zone_device
*thermal
,
183 int trip
, unsigned long *temp
)
185 struct db8500_thermal_zone
*pzone
= thermal
->devdata
;
186 struct db8500_thsens_platform_data
*ptrips
= pzone
->trip_tab
;
188 if (trip
>= ptrips
->num_trips
)
191 *temp
= ptrips
->trip_points
[trip
].temp
;
196 /* Callback to get critical trip point temperature */
197 static int db8500_sys_get_crit_temp(struct thermal_zone_device
*thermal
,
200 struct db8500_thermal_zone
*pzone
= thermal
->devdata
;
201 struct db8500_thsens_platform_data
*ptrips
= pzone
->trip_tab
;
204 for (i
= ptrips
->num_trips
- 1; i
> 0; i
--) {
205 if (ptrips
->trip_points
[i
].type
== THERMAL_TRIP_CRITICAL
) {
206 *temp
= ptrips
->trip_points
[i
].temp
;
214 static struct thermal_zone_device_ops thdev_ops
= {
215 .bind
= db8500_cdev_bind
,
216 .unbind
= db8500_cdev_unbind
,
217 .get_temp
= db8500_sys_get_temp
,
218 .get_trend
= db8500_sys_get_trend
,
219 .get_mode
= db8500_sys_get_mode
,
220 .set_mode
= db8500_sys_set_mode
,
221 .get_trip_type
= db8500_sys_get_trip_type
,
222 .get_trip_temp
= db8500_sys_get_trip_temp
,
223 .get_crit_temp
= db8500_sys_get_crit_temp
,
226 static void db8500_thermal_update_config(struct db8500_thermal_zone
*pzone
,
227 unsigned int idx
, enum thermal_trend trend
,
228 unsigned long next_low
, unsigned long next_high
)
230 prcmu_stop_temp_sense();
232 pzone
->cur_index
= idx
;
233 pzone
->cur_temp_pseudo
= (next_low
+ next_high
)/2;
234 pzone
->trend
= trend
;
236 prcmu_config_hotmon((u8
)(next_low
/1000), (u8
)(next_high
/1000));
237 prcmu_start_temp_sense(PRCMU_DEFAULT_MEASURE_TIME
);
240 static irqreturn_t
prcmu_low_irq_handler(int irq
, void *irq_data
)
242 struct db8500_thermal_zone
*pzone
= irq_data
;
243 struct db8500_thsens_platform_data
*ptrips
= pzone
->trip_tab
;
244 unsigned int idx
= pzone
->cur_index
;
245 unsigned long next_low
, next_high
;
247 if (unlikely(idx
== 0))
248 /* Meaningless for thermal management, ignoring it */
252 next_high
= ptrips
->trip_points
[0].temp
;
253 next_low
= PRCMU_DEFAULT_LOW_TEMP
;
255 next_high
= ptrips
->trip_points
[idx
-1].temp
;
256 next_low
= ptrips
->trip_points
[idx
-2].temp
;
260 db8500_thermal_update_config(pzone
, idx
, THERMAL_TREND_DROPPING
,
261 next_low
, next_high
);
263 dev_dbg(&pzone
->therm_dev
->device
,
264 "PRCMU set max %ld, min %ld\n", next_high
, next_low
);
266 schedule_work(&pzone
->therm_work
);
271 static irqreturn_t
prcmu_high_irq_handler(int irq
, void *irq_data
)
273 struct db8500_thermal_zone
*pzone
= irq_data
;
274 struct db8500_thsens_platform_data
*ptrips
= pzone
->trip_tab
;
275 unsigned int idx
= pzone
->cur_index
;
276 unsigned long next_low
, next_high
;
278 if (idx
< ptrips
->num_trips
- 1) {
279 next_high
= ptrips
->trip_points
[idx
+1].temp
;
280 next_low
= ptrips
->trip_points
[idx
].temp
;
283 db8500_thermal_update_config(pzone
, idx
, THERMAL_TREND_RAISING
,
284 next_low
, next_high
);
286 dev_dbg(&pzone
->therm_dev
->device
,
287 "PRCMU set max %ld, min %ld\n", next_high
, next_low
);
288 } else if (idx
== ptrips
->num_trips
- 1)
289 pzone
->cur_temp_pseudo
= ptrips
->trip_points
[idx
].temp
+ 1;
291 schedule_work(&pzone
->therm_work
);
296 static void db8500_thermal_work(struct work_struct
*work
)
298 enum thermal_device_mode cur_mode
;
299 struct db8500_thermal_zone
*pzone
;
301 pzone
= container_of(work
, struct db8500_thermal_zone
, therm_work
);
303 mutex_lock(&pzone
->th_lock
);
304 cur_mode
= pzone
->mode
;
305 mutex_unlock(&pzone
->th_lock
);
307 if (cur_mode
== THERMAL_DEVICE_DISABLED
)
310 thermal_zone_device_update(pzone
->therm_dev
);
311 dev_dbg(&pzone
->therm_dev
->device
, "thermal work finished.\n");
315 static struct db8500_thsens_platform_data
*
316 db8500_thermal_parse_dt(struct platform_device
*pdev
)
318 struct db8500_thsens_platform_data
*ptrips
;
319 struct device_node
*np
= pdev
->dev
.of_node
;
325 ptrips
= devm_kzalloc(&pdev
->dev
, sizeof(*ptrips
), GFP_KERNEL
);
329 if (of_property_read_u32(np
, "num-trips", &tmp_data
))
332 if (tmp_data
> THERMAL_MAX_TRIPS
)
335 ptrips
->num_trips
= tmp_data
;
337 for (i
= 0; i
< ptrips
->num_trips
; i
++) {
338 sprintf(prop_name
, "trip%d-temp", i
);
339 if (of_property_read_u32(np
, prop_name
, &tmp_data
))
342 ptrips
->trip_points
[i
].temp
= tmp_data
;
344 sprintf(prop_name
, "trip%d-type", i
);
345 if (of_property_read_string(np
, prop_name
, &tmp_str
))
348 if (!strcmp(tmp_str
, "active"))
349 ptrips
->trip_points
[i
].type
= THERMAL_TRIP_ACTIVE
;
350 else if (!strcmp(tmp_str
, "passive"))
351 ptrips
->trip_points
[i
].type
= THERMAL_TRIP_PASSIVE
;
352 else if (!strcmp(tmp_str
, "hot"))
353 ptrips
->trip_points
[i
].type
= THERMAL_TRIP_HOT
;
354 else if (!strcmp(tmp_str
, "critical"))
355 ptrips
->trip_points
[i
].type
= THERMAL_TRIP_CRITICAL
;
359 sprintf(prop_name
, "trip%d-cdev-num", i
);
360 if (of_property_read_u32(np
, prop_name
, &tmp_data
))
363 if (tmp_data
> COOLING_DEV_MAX
)
366 for (j
= 0; j
< tmp_data
; j
++) {
367 sprintf(prop_name
, "trip%d-cdev-name%d", i
, j
);
368 if (of_property_read_string(np
, prop_name
, &tmp_str
))
371 if (strlen(tmp_str
) >= THERMAL_NAME_LENGTH
)
374 strcpy(ptrips
->trip_points
[i
].cdev_name
[j
], tmp_str
);
380 dev_err(&pdev
->dev
, "Parsing device tree data error.\n");
384 static inline struct db8500_thsens_platform_data
*
385 db8500_thermal_parse_dt(struct platform_device
*pdev
)
391 static int db8500_thermal_probe(struct platform_device
*pdev
)
393 struct db8500_thermal_zone
*pzone
= NULL
;
394 struct db8500_thsens_platform_data
*ptrips
= NULL
;
395 struct device_node
*np
= pdev
->dev
.of_node
;
396 int low_irq
, high_irq
, ret
= 0;
397 unsigned long dft_low
, dft_high
;
400 ptrips
= db8500_thermal_parse_dt(pdev
);
402 ptrips
= dev_get_platdata(&pdev
->dev
);
407 pzone
= devm_kzalloc(&pdev
->dev
, sizeof(*pzone
), GFP_KERNEL
);
411 mutex_init(&pzone
->th_lock
);
412 mutex_lock(&pzone
->th_lock
);
414 pzone
->mode
= THERMAL_DEVICE_DISABLED
;
415 pzone
->trip_tab
= ptrips
;
417 INIT_WORK(&pzone
->therm_work
, db8500_thermal_work
);
419 low_irq
= platform_get_irq_byname(pdev
, "IRQ_HOTMON_LOW");
421 dev_err(&pdev
->dev
, "Get IRQ_HOTMON_LOW failed.\n");
426 ret
= devm_request_threaded_irq(&pdev
->dev
, low_irq
, NULL
,
427 prcmu_low_irq_handler
, IRQF_NO_SUSPEND
| IRQF_ONESHOT
,
428 "dbx500_temp_low", pzone
);
430 dev_err(&pdev
->dev
, "Failed to allocate temp low irq.\n");
434 high_irq
= platform_get_irq_byname(pdev
, "IRQ_HOTMON_HIGH");
436 dev_err(&pdev
->dev
, "Get IRQ_HOTMON_HIGH failed.\n");
441 ret
= devm_request_threaded_irq(&pdev
->dev
, high_irq
, NULL
,
442 prcmu_high_irq_handler
, IRQF_NO_SUSPEND
| IRQF_ONESHOT
,
443 "dbx500_temp_high", pzone
);
445 dev_err(&pdev
->dev
, "Failed to allocate temp high irq.\n");
449 pzone
->therm_dev
= thermal_zone_device_register("db8500_thermal_zone",
450 ptrips
->num_trips
, 0, pzone
, &thdev_ops
, NULL
, 0, 0);
452 if (IS_ERR(pzone
->therm_dev
)) {
453 dev_err(&pdev
->dev
, "Register thermal zone device failed.\n");
454 ret
= PTR_ERR(pzone
->therm_dev
);
457 dev_info(&pdev
->dev
, "Thermal zone device registered.\n");
459 dft_low
= PRCMU_DEFAULT_LOW_TEMP
;
460 dft_high
= ptrips
->trip_points
[0].temp
;
462 db8500_thermal_update_config(pzone
, 0, THERMAL_TREND_STABLE
,
465 platform_set_drvdata(pdev
, pzone
);
466 pzone
->mode
= THERMAL_DEVICE_ENABLED
;
469 mutex_unlock(&pzone
->th_lock
);
474 static int db8500_thermal_remove(struct platform_device
*pdev
)
476 struct db8500_thermal_zone
*pzone
= platform_get_drvdata(pdev
);
478 thermal_zone_device_unregister(pzone
->therm_dev
);
479 cancel_work_sync(&pzone
->therm_work
);
480 mutex_destroy(&pzone
->th_lock
);
485 static int db8500_thermal_suspend(struct platform_device
*pdev
,
488 struct db8500_thermal_zone
*pzone
= platform_get_drvdata(pdev
);
490 flush_work(&pzone
->therm_work
);
491 prcmu_stop_temp_sense();
496 static int db8500_thermal_resume(struct platform_device
*pdev
)
498 struct db8500_thermal_zone
*pzone
= platform_get_drvdata(pdev
);
499 struct db8500_thsens_platform_data
*ptrips
= pzone
->trip_tab
;
500 unsigned long dft_low
, dft_high
;
502 dft_low
= PRCMU_DEFAULT_LOW_TEMP
;
503 dft_high
= ptrips
->trip_points
[0].temp
;
505 db8500_thermal_update_config(pzone
, 0, THERMAL_TREND_STABLE
,
512 static const struct of_device_id db8500_thermal_match
[] = {
513 { .compatible
= "stericsson,db8500-thermal" },
518 static struct platform_driver db8500_thermal_driver
= {
520 .name
= "db8500-thermal",
521 .of_match_table
= of_match_ptr(db8500_thermal_match
),
523 .probe
= db8500_thermal_probe
,
524 .suspend
= db8500_thermal_suspend
,
525 .resume
= db8500_thermal_resume
,
526 .remove
= db8500_thermal_remove
,
529 module_platform_driver(db8500_thermal_driver
);
531 MODULE_AUTHOR("Hongbo Zhang <hongbo.zhang@stericsson.com>");
532 MODULE_DESCRIPTION("DB8500 thermal driver");
533 MODULE_LICENSE("GPL");