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");