aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Documentation/devicetree/bindings/thermal/db8500-thermal.txt44
-rw-r--r--drivers/thermal/Kconfig20
-rw-r--r--drivers/thermal/Makefile2
-rw-r--r--drivers/thermal/db8500_cpufreq_cooling.c108
-rw-r--r--drivers/thermal/db8500_thermal.c531
-rw-r--r--include/linux/platform_data/db8500_thermal.h38
6 files changed, 743 insertions, 0 deletions
diff --git a/Documentation/devicetree/bindings/thermal/db8500-thermal.txt b/Documentation/devicetree/bindings/thermal/db8500-thermal.txt
new file mode 100644
index 000000000000..2e1c06fad81f
--- /dev/null
+++ b/Documentation/devicetree/bindings/thermal/db8500-thermal.txt
@@ -0,0 +1,44 @@
1* ST-Ericsson DB8500 Thermal
2
3** Thermal node properties:
4
5- compatible : "stericsson,db8500-thermal";
6- reg : address range of the thermal sensor registers;
7- interrupts : interrupts generated from PRCMU;
8- interrupt-names : "IRQ_HOTMON_LOW" and "IRQ_HOTMON_HIGH";
9- num-trips : number of total trip points, this is required, set it 0 if none,
10 if greater than 0, the following properties must be defined;
11- tripN-temp : temperature of trip point N, should be in ascending order;
12- tripN-type : type of trip point N, should be one of "active" "passive" "hot"
13 "critical";
14- tripN-cdev-num : number of the cooling devices which can be bound to trip
15 point N, this is required if trip point N is defined, set it 0 if none,
16 otherwise the following cooling device names must be defined;
17- tripN-cdev-nameM : name of the No. M cooling device of trip point N;
18
19Usually the num-trips and tripN-*** are separated in board related dts files.
20
21Example:
22thermal@801573c0 {
23 compatible = "stericsson,db8500-thermal";
24 reg = <0x801573c0 0x40>;
25 interrupts = <21 0x4>, <22 0x4>;
26 interrupt-names = "IRQ_HOTMON_LOW", "IRQ_HOTMON_HIGH";
27
28 num-trips = <3>;
29
30 trip0-temp = <75000>;
31 trip0-type = "active";
32 trip0-cdev-num = <1>;
33 trip0-cdev-name0 = "thermal-cpufreq-0";
34
35 trip1-temp = <80000>;
36 trip1-type = "active";
37 trip1-cdev-num = <2>;
38 trip1-cdev-name0 = "thermal-cpufreq-0";
39 trip1-cdev-name1 = "thermal-fan";
40
41 trip2-temp = <85000>;
42 trip2-type = "critical";
43 trip2-cdev-num = <0>;
44}
diff --git a/drivers/thermal/Kconfig b/drivers/thermal/Kconfig
index 99b6587ab8b3..d96da075c9f6 100644
--- a/drivers/thermal/Kconfig
+++ b/drivers/thermal/Kconfig
@@ -101,5 +101,25 @@ config EXYNOS_THERMAL
101 If you say yes here you get support for TMU (Thermal Managment 101 If you say yes here you get support for TMU (Thermal Managment
102 Unit) on SAMSUNG EXYNOS series of SoC. 102 Unit) on SAMSUNG EXYNOS series of SoC.
103 103
104config DB8500_THERMAL
105 bool "DB8500 thermal management"
106 depends on ARCH_U8500
107 default y
108 help
109 Adds DB8500 thermal management implementation according to the thermal
110 management framework. A thermal zone with several trip points will be
111 created. Cooling devices can be bound to the trip points to cool this
112 thermal zone if trip points reached.
113
114config DB8500_CPUFREQ_COOLING
115 tristate "DB8500 cpufreq cooling"
116 depends on ARCH_U8500
117 depends on CPU_THERMAL
118 default y
119 help
120 Adds DB8500 cpufreq cooling devices, and these cooling devices can be
121 bound to thermal zone trip points. When a trip point reached, the
122 bound cpufreq cooling device turns active to set CPU frequency low to
123 cool down the CPU.
104 124
105endif 125endif
diff --git a/drivers/thermal/Makefile b/drivers/thermal/Makefile
index 0b6b048f236d..d8da683245fc 100644
--- a/drivers/thermal/Makefile
+++ b/drivers/thermal/Makefile
@@ -16,3 +16,5 @@ obj-$(CONFIG_CPU_THERMAL) += cpu_cooling.o
16obj-$(CONFIG_SPEAR_THERMAL) += spear_thermal.o 16obj-$(CONFIG_SPEAR_THERMAL) += spear_thermal.o
17obj-$(CONFIG_RCAR_THERMAL) += rcar_thermal.o 17obj-$(CONFIG_RCAR_THERMAL) += rcar_thermal.o
18obj-$(CONFIG_EXYNOS_THERMAL) += exynos_thermal.o 18obj-$(CONFIG_EXYNOS_THERMAL) += exynos_thermal.o
19obj-$(CONFIG_DB8500_THERMAL) += db8500_thermal.o
20obj-$(CONFIG_DB8500_CPUFREQ_COOLING) += db8500_cpufreq_cooling.o
diff --git a/drivers/thermal/db8500_cpufreq_cooling.c b/drivers/thermal/db8500_cpufreq_cooling.c
new file mode 100644
index 000000000000..4cf8e72af90a
--- /dev/null
+++ b/drivers/thermal/db8500_cpufreq_cooling.c
@@ -0,0 +1,108 @@
1/*
2 * db8500_cpufreq_cooling.c - DB8500 cpufreq works as cooling device.
3 *
4 * Copyright (C) 2012 ST-Ericsson
5 * Copyright (C) 2012 Linaro Ltd.
6 *
7 * Author: Hongbo Zhang <hongbo.zhang@linaro.com>
8 *
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.
13 *
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.
18 */
19
20#include <linux/cpu_cooling.h>
21#include <linux/cpufreq.h>
22#include <linux/err.h>
23#include <linux/module.h>
24#include <linux/platform_device.h>
25#include <linux/slab.h>
26
27static int db8500_cpufreq_cooling_probe(struct platform_device *pdev)
28{
29 struct thermal_cooling_device *cdev;
30 struct cpumask mask_val;
31
32 /* make sure cpufreq driver has been initialized */
33 if (!cpufreq_frequency_get_table(0))
34 return -EPROBE_DEFER;
35
36 cpumask_set_cpu(0, &mask_val);
37 cdev = cpufreq_cooling_register(&mask_val);
38
39 if (IS_ERR_OR_NULL(cdev)) {
40 dev_err(&pdev->dev, "Failed to register cooling device\n");
41 return PTR_ERR(cdev);
42 }
43
44 platform_set_drvdata(pdev, cdev);
45
46 dev_info(&pdev->dev, "Cooling device registered: %s\n", cdev->type);
47
48 return 0;
49}
50
51static int db8500_cpufreq_cooling_remove(struct platform_device *pdev)
52{
53 struct thermal_cooling_device *cdev = platform_get_drvdata(pdev);
54
55 cpufreq_cooling_unregister(cdev);
56
57 return 0;
58}
59
60static int db8500_cpufreq_cooling_suspend(struct platform_device *pdev,
61 pm_message_t state)
62{
63 return -ENOSYS;
64}
65
66static int db8500_cpufreq_cooling_resume(struct platform_device *pdev)
67{
68 return -ENOSYS;
69}
70
71#ifdef CONFIG_OF
72static const struct of_device_id db8500_cpufreq_cooling_match[] = {
73 { .compatible = "stericsson,db8500-cpufreq-cooling" },
74 {},
75};
76#else
77#define db8500_cpufreq_cooling_match NULL
78#endif
79
80static struct platform_driver db8500_cpufreq_cooling_driver = {
81 .driver = {
82 .owner = THIS_MODULE,
83 .name = "db8500-cpufreq-cooling",
84 .of_match_table = db8500_cpufreq_cooling_match,
85 },
86 .probe = db8500_cpufreq_cooling_probe,
87 .suspend = db8500_cpufreq_cooling_suspend,
88 .resume = db8500_cpufreq_cooling_resume,
89 .remove = db8500_cpufreq_cooling_remove,
90};
91
92static int __init db8500_cpufreq_cooling_init(void)
93{
94 return platform_driver_register(&db8500_cpufreq_cooling_driver);
95}
96
97static void __exit db8500_cpufreq_cooling_exit(void)
98{
99 platform_driver_unregister(&db8500_cpufreq_cooling_driver);
100}
101
102/* Should be later than db8500_cpufreq_register */
103late_initcall(db8500_cpufreq_cooling_init);
104module_exit(db8500_cpufreq_cooling_exit);
105
106MODULE_AUTHOR("Hongbo Zhang <hongbo.zhang@stericsson.com>");
107MODULE_DESCRIPTION("DB8500 cpufreq cooling driver");
108MODULE_LICENSE("GPL");
diff --git a/drivers/thermal/db8500_thermal.c b/drivers/thermal/db8500_thermal.c
new file mode 100644
index 000000000000..ec71ade3e317
--- /dev/null
+++ b/drivers/thermal/db8500_thermal.c
@@ -0,0 +1,531 @@
1/*
2 * db8500_thermal.c - DB8500 Thermal Management Implementation
3 *
4 * Copyright (C) 2012 ST-Ericsson
5 * Copyright (C) 2012 Linaro Ltd.
6 *
7 * Author: Hongbo Zhang <hongbo.zhang@linaro.com>
8 *
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.
13 *
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.
18 */
19
20#include <linux/cpu_cooling.h>
21#include <linux/interrupt.h>
22#include <linux/mfd/dbx500-prcmu.h>
23#include <linux/module.h>
24#include <linux/of.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>
29
30#define PRCMU_DEFAULT_MEASURE_TIME 0xFFF
31#define PRCMU_DEFAULT_LOW_TEMP 0
32
33struct db8500_thermal_zone {
34 struct thermal_zone_device *therm_dev;
35 struct mutex th_lock;
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;
42};
43
44/* Local function to check if thermal zone matches cooling devices */
45static int db8500_thermal_match_cdev(struct thermal_cooling_device *cdev,
46 struct db8500_trip_point *trip_point)
47{
48 int i;
49
50 if (!strlen(cdev->type))
51 return -EINVAL;
52
53 for (i = 0; i < COOLING_DEV_MAX; i++) {
54 if (!strcmp(trip_point->cdev_name[i], cdev->type))
55 return 0;
56 }
57
58 return -ENODEV;
59}
60
61/* Callback to bind cooling device to thermal zone */
62static int db8500_cdev_bind(struct thermal_zone_device *thermal,
63 struct thermal_cooling_device *cdev)
64{
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;
68 int i, ret = -EINVAL;
69
70 cdev->ops->get_max_state(cdev, &max_state);
71
72 for (i = 0; i < ptrips->num_trips; i++) {
73 if (db8500_thermal_match_cdev(cdev, &ptrips->trip_points[i]))
74 continue;
75
76 upper = lower = i > max_state ? max_state : i;
77
78 ret = thermal_zone_bind_cooling_device(thermal, i, cdev,
79 upper, lower);
80
81 dev_info(&cdev->device, "%s bind to %d: %d-%s\n", cdev->type,
82 i, ret, ret ? "fail" : "succeed");
83 }
84
85 return ret;
86}
87
88/* Callback to unbind cooling device from thermal zone */
89static int db8500_cdev_unbind(struct thermal_zone_device *thermal,
90 struct thermal_cooling_device *cdev)
91{
92 struct db8500_thermal_zone *pzone = thermal->devdata;
93 struct db8500_thsens_platform_data *ptrips = pzone->trip_tab;
94 int i, ret = -EINVAL;
95
96 for (i = 0; i < ptrips->num_trips; i++) {
97 if (db8500_thermal_match_cdev(cdev, &ptrips->trip_points[i]))
98 continue;
99
100 ret = thermal_zone_unbind_cooling_device(thermal, i, cdev);
101
102 dev_info(&cdev->device, "%s unbind from %d: %s\n", cdev->type,
103 i, ret ? "fail" : "succeed");
104 }
105
106 return ret;
107}
108
109/* Callback to get current temperature */
110static int db8500_sys_get_temp(struct thermal_zone_device *thermal,
111 unsigned long *temp)
112{
113 struct db8500_thermal_zone *pzone = thermal->devdata;
114
115 /*
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.
119 */
120 *temp = pzone->cur_temp_pseudo;
121
122 return 0;
123}
124
125/* Callback to get temperature changing trend */
126static int db8500_sys_get_trend(struct thermal_zone_device *thermal,
127 int trip, enum thermal_trend *trend)
128{
129 struct db8500_thermal_zone *pzone = thermal->devdata;
130
131 *trend = pzone->trend;
132
133 return 0;
134}
135
136/* Callback to get thermal zone mode */
137static int db8500_sys_get_mode(struct thermal_zone_device *thermal,
138 enum thermal_device_mode *mode)
139{
140 struct db8500_thermal_zone *pzone = thermal->devdata;
141
142 mutex_lock(&pzone->th_lock);
143 *mode = pzone->mode;
144 mutex_unlock(&pzone->th_lock);
145
146 return 0;
147}
148
149/* Callback to set thermal zone mode */
150static int db8500_sys_set_mode(struct thermal_zone_device *thermal,
151 enum thermal_device_mode mode)
152{
153 struct db8500_thermal_zone *pzone = thermal->devdata;
154
155 mutex_lock(&pzone->th_lock);
156
157 pzone->mode = mode;
158 if (mode == THERMAL_DEVICE_ENABLED)
159 schedule_work(&pzone->therm_work);
160
161 mutex_unlock(&pzone->th_lock);
162
163 return 0;
164}
165
166/* Callback to get trip point type */
167static int db8500_sys_get_trip_type(struct thermal_zone_device *thermal,
168 int trip, enum thermal_trip_type *type)
169{
170 struct db8500_thermal_zone *pzone = thermal->devdata;
171 struct db8500_thsens_platform_data *ptrips = pzone->trip_tab;
172
173 if (trip >= ptrips->num_trips)
174 return -EINVAL;
175
176 *type = ptrips->trip_points[trip].type;
177
178 return 0;
179}
180
181/* Callback to get trip point temperature */
182static int db8500_sys_get_trip_temp(struct thermal_zone_device *thermal,
183 int trip, unsigned long *temp)
184{
185 struct db8500_thermal_zone *pzone = thermal->devdata;
186 struct db8500_thsens_platform_data *ptrips = pzone->trip_tab;
187
188 if (trip >= ptrips->num_trips)
189 return -EINVAL;
190
191 *temp = ptrips->trip_points[trip].temp;
192
193 return 0;
194}
195
196/* Callback to get critical trip point temperature */
197static int db8500_sys_get_crit_temp(struct thermal_zone_device *thermal,
198 unsigned long *temp)
199{
200 struct db8500_thermal_zone *pzone = thermal->devdata;
201 struct db8500_thsens_platform_data *ptrips = pzone->trip_tab;
202 int i;
203
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;
207 return 0;
208 }
209 }
210
211 return -EINVAL;
212}
213
214static 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,
224};
225
226static 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)
229{
230 prcmu_stop_temp_sense();
231
232 pzone->cur_index = idx;
233 pzone->cur_temp_pseudo = (next_low + next_high)/2;
234 pzone->trend = trend;
235
236 prcmu_config_hotmon((u8)(next_low/1000), (u8)(next_high/1000));
237 prcmu_start_temp_sense(PRCMU_DEFAULT_MEASURE_TIME);
238}
239
240static irqreturn_t prcmu_low_irq_handler(int irq, void *irq_data)
241{
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;
246
247 if (unlikely(idx == 0))
248 /* Meaningless for thermal management, ignoring it */
249 return IRQ_HANDLED;
250
251 if (idx == 1) {
252 next_high = ptrips->trip_points[0].temp;
253 next_low = PRCMU_DEFAULT_LOW_TEMP;
254 } else {
255 next_high = ptrips->trip_points[idx-1].temp;
256 next_low = ptrips->trip_points[idx-2].temp;
257 }
258 idx -= 1;
259
260 db8500_thermal_update_config(pzone, idx, THERMAL_TREND_DROPPING,
261 next_low, next_high);
262
263 dev_dbg(&pzone->therm_dev->device,
264 "PRCMU set max %ld, min %ld\n", next_high, next_low);
265
266 schedule_work(&pzone->therm_work);
267
268 return IRQ_HANDLED;
269}
270
271static irqreturn_t prcmu_high_irq_handler(int irq, void *irq_data)
272{
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;
277
278 if (idx < ptrips->num_trips - 1) {
279 next_high = ptrips->trip_points[idx+1].temp;
280 next_low = ptrips->trip_points[idx].temp;
281 idx += 1;
282
283 db8500_thermal_update_config(pzone, idx, THERMAL_TREND_RAISING,
284 next_low, next_high);
285
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;
290
291 schedule_work(&pzone->therm_work);
292
293 return IRQ_HANDLED;
294}
295
296static void db8500_thermal_work(struct work_struct *work)
297{
298 enum thermal_device_mode cur_mode;
299 struct db8500_thermal_zone *pzone;
300
301 pzone = container_of(work, struct db8500_thermal_zone, therm_work);
302
303 mutex_lock(&pzone->th_lock);
304 cur_mode = pzone->mode;
305 mutex_unlock(&pzone->th_lock);
306
307 if (cur_mode == THERMAL_DEVICE_DISABLED)
308 return;
309
310 thermal_zone_device_update(pzone->therm_dev);
311 dev_dbg(&pzone->therm_dev->device, "thermal work finished.\n");
312}
313
314#ifdef CONFIG_OF
315static struct db8500_thsens_platform_data*
316 db8500_thermal_parse_dt(struct platform_device *pdev)
317{
318 struct db8500_thsens_platform_data *ptrips;
319 struct device_node *np = pdev->dev.of_node;
320 char prop_name[32];
321 const char *tmp_str;
322 u32 tmp_data;
323 int i, j;
324
325 ptrips = devm_kzalloc(&pdev->dev, sizeof(*ptrips), GFP_KERNEL);
326 if (!ptrips)
327 return NULL;
328
329 if (of_property_read_u32(np, "num-trips", &tmp_data))
330 goto err_parse_dt;
331
332 if (tmp_data > THERMAL_MAX_TRIPS)
333 goto err_parse_dt;
334
335 ptrips->num_trips = tmp_data;
336
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))
340 goto err_parse_dt;
341
342 ptrips->trip_points[i].temp = tmp_data;
343
344 sprintf(prop_name, "trip%d-type", i);
345 if (of_property_read_string(np, prop_name, &tmp_str))
346 goto err_parse_dt;
347
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;
356 else
357 goto err_parse_dt;
358
359 sprintf(prop_name, "trip%d-cdev-num", i);
360 if (of_property_read_u32(np, prop_name, &tmp_data))
361 goto err_parse_dt;
362
363 if (tmp_data > COOLING_DEV_MAX)
364 goto err_parse_dt;
365
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))
369 goto err_parse_dt;
370
371 if (strlen(tmp_str) >= THERMAL_NAME_LENGTH)
372 goto err_parse_dt;
373
374 strcpy(ptrips->trip_points[i].cdev_name[j], tmp_str);
375 }
376 }
377 return ptrips;
378
379err_parse_dt:
380 dev_err(&pdev->dev, "Parsing device tree data error.\n");
381 return NULL;
382}
383#else
384static inline struct db8500_thsens_platform_data*
385 db8500_thermal_parse_dt(struct platform_device *pdev)
386{
387 return NULL;
388}
389#endif
390
391static int db8500_thermal_probe(struct platform_device *pdev)
392{
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;
398
399 if (np)
400 ptrips = db8500_thermal_parse_dt(pdev);
401 else
402 ptrips = dev_get_platdata(&pdev->dev);
403
404 if (!ptrips)
405 return -EINVAL;
406
407 pzone = devm_kzalloc(&pdev->dev, sizeof(*pzone), GFP_KERNEL);
408 if (!pzone)
409 return -ENOMEM;
410
411 mutex_init(&pzone->th_lock);
412 mutex_lock(&pzone->th_lock);
413
414 pzone->mode = THERMAL_DEVICE_DISABLED;
415 pzone->trip_tab = ptrips;
416
417 INIT_WORK(&pzone->therm_work, db8500_thermal_work);
418
419 low_irq = platform_get_irq_byname(pdev, "IRQ_HOTMON_LOW");
420 if (low_irq < 0) {
421 dev_err(&pdev->dev, "Get IRQ_HOTMON_LOW failed.\n");
422 return low_irq;
423 }
424
425 ret = devm_request_threaded_irq(&pdev->dev, low_irq, NULL,
426 prcmu_low_irq_handler, IRQF_NO_SUSPEND | IRQF_ONESHOT,
427 "dbx500_temp_low", pzone);
428 if (ret < 0) {
429 dev_err(&pdev->dev, "Failed to allocate temp low irq.\n");
430 return ret;
431 }
432
433 high_irq = platform_get_irq_byname(pdev, "IRQ_HOTMON_HIGH");
434 if (high_irq < 0) {
435 dev_err(&pdev->dev, "Get IRQ_HOTMON_HIGH failed.\n");
436 return high_irq;
437 }
438
439 ret = devm_request_threaded_irq(&pdev->dev, high_irq, NULL,
440 prcmu_high_irq_handler, IRQF_NO_SUSPEND | IRQF_ONESHOT,
441 "dbx500_temp_high", pzone);
442 if (ret < 0) {
443 dev_err(&pdev->dev, "Failed to allocate temp high irq.\n");
444 return ret;
445 }
446
447 pzone->therm_dev = thermal_zone_device_register("db8500_thermal_zone",
448 ptrips->num_trips, 0, pzone, &thdev_ops, NULL, 0, 0);
449
450 if (IS_ERR_OR_NULL(pzone->therm_dev)) {
451 dev_err(&pdev->dev, "Register thermal zone device failed.\n");
452 return PTR_ERR(pzone->therm_dev);
453 }
454 dev_info(&pdev->dev, "Thermal zone device registered.\n");
455
456 dft_low = PRCMU_DEFAULT_LOW_TEMP;
457 dft_high = ptrips->trip_points[0].temp;
458
459 db8500_thermal_update_config(pzone, 0, THERMAL_TREND_STABLE,
460 dft_low, dft_high);
461
462 platform_set_drvdata(pdev, pzone);
463 pzone->mode = THERMAL_DEVICE_ENABLED;
464 mutex_unlock(&pzone->th_lock);
465
466 return 0;
467}
468
469static int db8500_thermal_remove(struct platform_device *pdev)
470{
471 struct db8500_thermal_zone *pzone = platform_get_drvdata(pdev);
472
473 thermal_zone_device_unregister(pzone->therm_dev);
474 cancel_work_sync(&pzone->therm_work);
475 mutex_destroy(&pzone->th_lock);
476
477 return 0;
478}
479
480static int db8500_thermal_suspend(struct platform_device *pdev,
481 pm_message_t state)
482{
483 struct db8500_thermal_zone *pzone = platform_get_drvdata(pdev);
484
485 flush_work(&pzone->therm_work);
486 prcmu_stop_temp_sense();
487
488 return 0;
489}
490
491static int db8500_thermal_resume(struct platform_device *pdev)
492{
493 struct db8500_thermal_zone *pzone = platform_get_drvdata(pdev);
494 struct db8500_thsens_platform_data *ptrips = pzone->trip_tab;
495 unsigned long dft_low, dft_high;
496
497 dft_low = PRCMU_DEFAULT_LOW_TEMP;
498 dft_high = ptrips->trip_points[0].temp;
499
500 db8500_thermal_update_config(pzone, 0, THERMAL_TREND_STABLE,
501 dft_low, dft_high);
502
503 return 0;
504}
505
506#ifdef CONFIG_OF
507static const struct of_device_id db8500_thermal_match[] = {
508 { .compatible = "stericsson,db8500-thermal" },
509 {},
510};
511#else
512#define db8500_thermal_match NULL
513#endif
514
515static struct platform_driver db8500_thermal_driver = {
516 .driver = {
517 .owner = THIS_MODULE,
518 .name = "db8500-thermal",
519 .of_match_table = db8500_thermal_match,
520 },
521 .probe = db8500_thermal_probe,
522 .suspend = db8500_thermal_suspend,
523 .resume = db8500_thermal_resume,
524 .remove = db8500_thermal_remove,
525};
526
527module_platform_driver(db8500_thermal_driver);
528
529MODULE_AUTHOR("Hongbo Zhang <hongbo.zhang@stericsson.com>");
530MODULE_DESCRIPTION("DB8500 thermal driver");
531MODULE_LICENSE("GPL");
diff --git a/include/linux/platform_data/db8500_thermal.h b/include/linux/platform_data/db8500_thermal.h
new file mode 100644
index 000000000000..3bf60902e902
--- /dev/null
+++ b/include/linux/platform_data/db8500_thermal.h
@@ -0,0 +1,38 @@
1/*
2 * db8500_thermal.h - DB8500 Thermal Management Implementation
3 *
4 * Copyright (C) 2012 ST-Ericsson
5 * Copyright (C) 2012 Linaro Ltd.
6 *
7 * Author: Hongbo Zhang <hongbo.zhang@linaro.com>
8 *
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.
13 *
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.
18 */
19
20#ifndef _DB8500_THERMAL_H_
21#define _DB8500_THERMAL_H_
22
23#include <linux/thermal.h>
24
25#define COOLING_DEV_MAX 8
26
27struct db8500_trip_point {
28 unsigned long temp;
29 enum thermal_trip_type type;
30 char cdev_name[COOLING_DEV_MAX][THERMAL_NAME_LENGTH];
31};
32
33struct db8500_thsens_platform_data {
34 struct db8500_trip_point trip_points[THERMAL_MAX_TRIPS];
35 int num_trips;
36};
37
38#endif /* _DB8500_THERMAL_H_ */