1 // SPDX-License-Identifier: GPL-2.0-or-later
3 * db8500_thermal.c - DB8500 Thermal Management Implementation
5 * Copyright (C) 2012 ST-Ericsson
6 * Copyright (C) 2012 Linaro Ltd.
8 * Author: Hongbo Zhang <hongbo.zhang@linaro.com>
11 #include <linux/cpu_cooling.h>
12 #include <linux/interrupt.h>
13 #include <linux/mfd/dbx500-prcmu.h>
14 #include <linux/module.h>
16 #include <linux/platform_device.h>
17 #include <linux/slab.h>
18 #include <linux/thermal.h>
20 #define PRCMU_DEFAULT_MEASURE_TIME 0xFFF
21 #define PRCMU_DEFAULT_LOW_TEMP 0
22 #define COOLING_DEV_MAX 8
24 struct db8500_trip_point
{
26 enum thermal_trip_type type
;
27 char cdev_name
[COOLING_DEV_MAX
][THERMAL_NAME_LENGTH
];
30 struct db8500_thsens_platform_data
{
31 struct db8500_trip_point trip_points
[THERMAL_MAX_TRIPS
];
35 struct db8500_thermal_zone
{
36 struct thermal_zone_device
*therm_dev
;
38 struct work_struct therm_work
;
39 struct db8500_thsens_platform_data
*trip_tab
;
40 enum thermal_device_mode mode
;
41 enum thermal_trend trend
;
42 unsigned long cur_temp_pseudo
;
43 unsigned int cur_index
;
46 /* Local function to check if thermal zone matches cooling devices */
47 static int db8500_thermal_match_cdev(struct thermal_cooling_device
*cdev
,
48 struct db8500_trip_point
*trip_point
)
52 if (!strlen(cdev
->type
))
55 for (i
= 0; i
< COOLING_DEV_MAX
; i
++) {
56 if (!strcmp(trip_point
->cdev_name
[i
], cdev
->type
))
63 /* Callback to bind cooling device to thermal zone */
64 static int db8500_cdev_bind(struct thermal_zone_device
*thermal
,
65 struct thermal_cooling_device
*cdev
)
67 struct db8500_thermal_zone
*pzone
= thermal
->devdata
;
68 struct db8500_thsens_platform_data
*ptrips
= pzone
->trip_tab
;
69 unsigned long max_state
, upper
, lower
;
72 cdev
->ops
->get_max_state(cdev
, &max_state
);
74 for (i
= 0; i
< ptrips
->num_trips
; i
++) {
75 if (db8500_thermal_match_cdev(cdev
, &ptrips
->trip_points
[i
]))
78 upper
= lower
= i
> max_state
? max_state
: i
;
80 ret
= thermal_zone_bind_cooling_device(thermal
, i
, cdev
,
81 upper
, lower
, THERMAL_WEIGHT_DEFAULT
);
83 dev_info(&cdev
->device
, "%s bind to %d: %d-%s\n", cdev
->type
,
84 i
, ret
, ret
? "fail" : "succeed");
90 /* Callback to unbind cooling device from thermal zone */
91 static int db8500_cdev_unbind(struct thermal_zone_device
*thermal
,
92 struct thermal_cooling_device
*cdev
)
94 struct db8500_thermal_zone
*pzone
= thermal
->devdata
;
95 struct db8500_thsens_platform_data
*ptrips
= pzone
->trip_tab
;
98 for (i
= 0; i
< ptrips
->num_trips
; i
++) {
99 if (db8500_thermal_match_cdev(cdev
, &ptrips
->trip_points
[i
]))
102 ret
= thermal_zone_unbind_cooling_device(thermal
, i
, cdev
);
104 dev_info(&cdev
->device
, "%s unbind from %d: %s\n", cdev
->type
,
105 i
, ret
? "fail" : "succeed");
111 /* Callback to get current temperature */
112 static int db8500_sys_get_temp(struct thermal_zone_device
*thermal
, int *temp
)
114 struct db8500_thermal_zone
*pzone
= thermal
->devdata
;
117 * TODO: There is no PRCMU interface to get temperature data currently,
118 * so a pseudo temperature is returned , it works for thermal framework
119 * and this will be fixed when the PRCMU interface is available.
121 *temp
= pzone
->cur_temp_pseudo
;
126 /* Callback to get temperature changing trend */
127 static int db8500_sys_get_trend(struct thermal_zone_device
*thermal
,
128 int trip
, enum thermal_trend
*trend
)
130 struct db8500_thermal_zone
*pzone
= thermal
->devdata
;
132 *trend
= pzone
->trend
;
137 /* Callback to get thermal zone mode */
138 static int db8500_sys_get_mode(struct thermal_zone_device
*thermal
,
139 enum thermal_device_mode
*mode
)
141 struct db8500_thermal_zone
*pzone
= thermal
->devdata
;
143 mutex_lock(&pzone
->th_lock
);
145 mutex_unlock(&pzone
->th_lock
);
150 /* Callback to set thermal zone mode */
151 static int db8500_sys_set_mode(struct thermal_zone_device
*thermal
,
152 enum thermal_device_mode mode
)
154 struct db8500_thermal_zone
*pzone
= thermal
->devdata
;
156 mutex_lock(&pzone
->th_lock
);
159 if (mode
== THERMAL_DEVICE_ENABLED
)
160 schedule_work(&pzone
->therm_work
);
162 mutex_unlock(&pzone
->th_lock
);
167 /* Callback to get trip point type */
168 static int db8500_sys_get_trip_type(struct thermal_zone_device
*thermal
,
169 int trip
, enum thermal_trip_type
*type
)
171 struct db8500_thermal_zone
*pzone
= thermal
->devdata
;
172 struct db8500_thsens_platform_data
*ptrips
= pzone
->trip_tab
;
174 if (trip
>= ptrips
->num_trips
)
177 *type
= ptrips
->trip_points
[trip
].type
;
182 /* Callback to get trip point temperature */
183 static int db8500_sys_get_trip_temp(struct thermal_zone_device
*thermal
,
186 struct db8500_thermal_zone
*pzone
= thermal
->devdata
;
187 struct db8500_thsens_platform_data
*ptrips
= pzone
->trip_tab
;
189 if (trip
>= ptrips
->num_trips
)
192 *temp
= ptrips
->trip_points
[trip
].temp
;
197 /* Callback to get critical trip point temperature */
198 static int db8500_sys_get_crit_temp(struct thermal_zone_device
*thermal
,
201 struct db8500_thermal_zone
*pzone
= thermal
->devdata
;
202 struct db8500_thsens_platform_data
*ptrips
= pzone
->trip_tab
;
205 for (i
= ptrips
->num_trips
- 1; i
> 0; i
--) {
206 if (ptrips
->trip_points
[i
].type
== THERMAL_TRIP_CRITICAL
) {
207 *temp
= ptrips
->trip_points
[i
].temp
;
215 static struct thermal_zone_device_ops thdev_ops
= {
216 .bind
= db8500_cdev_bind
,
217 .unbind
= db8500_cdev_unbind
,
218 .get_temp
= db8500_sys_get_temp
,
219 .get_trend
= db8500_sys_get_trend
,
220 .get_mode
= db8500_sys_get_mode
,
221 .set_mode
= db8500_sys_set_mode
,
222 .get_trip_type
= db8500_sys_get_trip_type
,
223 .get_trip_temp
= db8500_sys_get_trip_temp
,
224 .get_crit_temp
= db8500_sys_get_crit_temp
,
227 static void db8500_thermal_update_config(struct db8500_thermal_zone
*pzone
,
228 unsigned int idx
, enum thermal_trend trend
,
229 unsigned long next_low
, unsigned long next_high
)
231 prcmu_stop_temp_sense();
233 pzone
->cur_index
= idx
;
234 pzone
->cur_temp_pseudo
= (next_low
+ next_high
)/2;
235 pzone
->trend
= trend
;
237 prcmu_config_hotmon((u8
)(next_low
/1000), (u8
)(next_high
/1000));
238 prcmu_start_temp_sense(PRCMU_DEFAULT_MEASURE_TIME
);
241 static irqreturn_t
prcmu_low_irq_handler(int irq
, void *irq_data
)
243 struct db8500_thermal_zone
*pzone
= irq_data
;
244 struct db8500_thsens_platform_data
*ptrips
= pzone
->trip_tab
;
245 unsigned int idx
= pzone
->cur_index
;
246 unsigned long next_low
, next_high
;
248 if (unlikely(idx
== 0))
249 /* Meaningless for thermal management, ignoring it */
253 next_high
= ptrips
->trip_points
[0].temp
;
254 next_low
= PRCMU_DEFAULT_LOW_TEMP
;
256 next_high
= ptrips
->trip_points
[idx
-1].temp
;
257 next_low
= ptrips
->trip_points
[idx
-2].temp
;
261 db8500_thermal_update_config(pzone
, idx
, THERMAL_TREND_DROPPING
,
262 next_low
, next_high
);
264 dev_dbg(&pzone
->therm_dev
->device
,
265 "PRCMU set max %ld, min %ld\n", next_high
, next_low
);
267 schedule_work(&pzone
->therm_work
);
272 static irqreturn_t
prcmu_high_irq_handler(int irq
, void *irq_data
)
274 struct db8500_thermal_zone
*pzone
= irq_data
;
275 struct db8500_thsens_platform_data
*ptrips
= pzone
->trip_tab
;
276 unsigned int idx
= pzone
->cur_index
;
277 unsigned long next_low
, next_high
;
279 if (idx
< ptrips
->num_trips
- 1) {
280 next_high
= ptrips
->trip_points
[idx
+1].temp
;
281 next_low
= ptrips
->trip_points
[idx
].temp
;
284 db8500_thermal_update_config(pzone
, idx
, THERMAL_TREND_RAISING
,
285 next_low
, next_high
);
287 dev_dbg(&pzone
->therm_dev
->device
,
288 "PRCMU set max %ld, min %ld\n", next_high
, next_low
);
289 } else if (idx
== ptrips
->num_trips
- 1)
290 pzone
->cur_temp_pseudo
= ptrips
->trip_points
[idx
].temp
+ 1;
292 schedule_work(&pzone
->therm_work
);
297 static void db8500_thermal_work(struct work_struct
*work
)
299 enum thermal_device_mode cur_mode
;
300 struct db8500_thermal_zone
*pzone
;
302 pzone
= container_of(work
, struct db8500_thermal_zone
, therm_work
);
304 mutex_lock(&pzone
->th_lock
);
305 cur_mode
= pzone
->mode
;
306 mutex_unlock(&pzone
->th_lock
);
308 if (cur_mode
== THERMAL_DEVICE_DISABLED
)
311 thermal_zone_device_update(pzone
->therm_dev
, THERMAL_EVENT_UNSPECIFIED
);
312 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 int db8500_thermal_probe(struct platform_device
*pdev
)
386 struct db8500_thermal_zone
*pzone
= NULL
;
387 struct db8500_thsens_platform_data
*ptrips
= NULL
;
388 struct device_node
*np
= pdev
->dev
.of_node
;
389 int low_irq
, high_irq
, ret
= 0;
390 unsigned long dft_low
, dft_high
;
395 ptrips
= db8500_thermal_parse_dt(pdev
);
399 pzone
= devm_kzalloc(&pdev
->dev
, sizeof(*pzone
), GFP_KERNEL
);
403 mutex_init(&pzone
->th_lock
);
404 mutex_lock(&pzone
->th_lock
);
406 pzone
->mode
= THERMAL_DEVICE_DISABLED
;
407 pzone
->trip_tab
= ptrips
;
409 INIT_WORK(&pzone
->therm_work
, db8500_thermal_work
);
411 low_irq
= platform_get_irq_byname(pdev
, "IRQ_HOTMON_LOW");
413 dev_err(&pdev
->dev
, "Get IRQ_HOTMON_LOW failed.\n");
418 ret
= devm_request_threaded_irq(&pdev
->dev
, low_irq
, NULL
,
419 prcmu_low_irq_handler
, IRQF_NO_SUSPEND
| IRQF_ONESHOT
,
420 "dbx500_temp_low", pzone
);
422 dev_err(&pdev
->dev
, "Failed to allocate temp low irq.\n");
426 high_irq
= platform_get_irq_byname(pdev
, "IRQ_HOTMON_HIGH");
428 dev_err(&pdev
->dev
, "Get IRQ_HOTMON_HIGH failed.\n");
433 ret
= devm_request_threaded_irq(&pdev
->dev
, high_irq
, NULL
,
434 prcmu_high_irq_handler
, IRQF_NO_SUSPEND
| IRQF_ONESHOT
,
435 "dbx500_temp_high", pzone
);
437 dev_err(&pdev
->dev
, "Failed to allocate temp high irq.\n");
441 pzone
->therm_dev
= thermal_zone_device_register("db8500_thermal_zone",
442 ptrips
->num_trips
, 0, pzone
, &thdev_ops
, NULL
, 0, 0);
444 if (IS_ERR(pzone
->therm_dev
)) {
445 dev_err(&pdev
->dev
, "Register thermal zone device failed.\n");
446 ret
= PTR_ERR(pzone
->therm_dev
);
449 dev_info(&pdev
->dev
, "Thermal zone device registered.\n");
451 dft_low
= PRCMU_DEFAULT_LOW_TEMP
;
452 dft_high
= ptrips
->trip_points
[0].temp
;
454 db8500_thermal_update_config(pzone
, 0, THERMAL_TREND_STABLE
,
457 platform_set_drvdata(pdev
, pzone
);
458 pzone
->mode
= THERMAL_DEVICE_ENABLED
;
461 mutex_unlock(&pzone
->th_lock
);
466 static int db8500_thermal_remove(struct platform_device
*pdev
)
468 struct db8500_thermal_zone
*pzone
= platform_get_drvdata(pdev
);
470 thermal_zone_device_unregister(pzone
->therm_dev
);
471 cancel_work_sync(&pzone
->therm_work
);
472 mutex_destroy(&pzone
->th_lock
);
477 static int db8500_thermal_suspend(struct platform_device
*pdev
,
480 struct db8500_thermal_zone
*pzone
= platform_get_drvdata(pdev
);
482 flush_work(&pzone
->therm_work
);
483 prcmu_stop_temp_sense();
488 static int db8500_thermal_resume(struct platform_device
*pdev
)
490 struct db8500_thermal_zone
*pzone
= platform_get_drvdata(pdev
);
491 struct db8500_thsens_platform_data
*ptrips
= pzone
->trip_tab
;
492 unsigned long dft_low
, dft_high
;
494 dft_low
= PRCMU_DEFAULT_LOW_TEMP
;
495 dft_high
= ptrips
->trip_points
[0].temp
;
497 db8500_thermal_update_config(pzone
, 0, THERMAL_TREND_STABLE
,
503 static const struct of_device_id db8500_thermal_match
[] = {
504 { .compatible
= "stericsson,db8500-thermal" },
507 MODULE_DEVICE_TABLE(of
, db8500_thermal_match
);
509 static struct platform_driver db8500_thermal_driver
= {
511 .name
= "db8500-thermal",
512 .of_match_table
= of_match_ptr(db8500_thermal_match
),
514 .probe
= db8500_thermal_probe
,
515 .suspend
= db8500_thermal_suspend
,
516 .resume
= db8500_thermal_resume
,
517 .remove
= db8500_thermal_remove
,
520 module_platform_driver(db8500_thermal_driver
);
522 MODULE_AUTHOR("Hongbo Zhang <hongbo.zhang@stericsson.com>");
523 MODULE_DESCRIPTION("DB8500 thermal driver");
524 MODULE_LICENSE("GPL");