Linux Audio

Check our new training course

Loading...
v6.13.7
  1// SPDX-License-Identifier: GPL-2.0-or-later
  2/*
  3 * db8500_thermal.c - DB8500 Thermal Management Implementation
  4 *
  5 * Copyright (C) 2012 ST-Ericsson
  6 * Copyright (C) 2012-2019 Linaro Ltd.
  7 *
  8 * Authors: Hongbo Zhang, Linus Walleij
 
 
 
 
 
 
 
 
 
 
  9 */
 10
 11#include <linux/cpu_cooling.h>
 12#include <linux/interrupt.h>
 13#include <linux/mfd/dbx500-prcmu.h>
 14#include <linux/module.h>
 15#include <linux/of.h>
 
 16#include <linux/platform_device.h>
 17#include <linux/slab.h>
 18#include <linux/thermal.h>
 19
 20#define PRCMU_DEFAULT_MEASURE_TIME	0xFFF
 21#define PRCMU_DEFAULT_LOW_TEMP		0
 22
 23/**
 24 * db8500_thermal_points - the interpolation points that trigger
 25 * interrupts
 26 */
 27static const unsigned long db8500_thermal_points[] = {
 28	15000,
 29	20000,
 30	25000,
 31	30000,
 32	35000,
 33	40000,
 34	45000,
 35	50000,
 36	55000,
 37	60000,
 38	65000,
 39	70000,
 40	75000,
 41	80000,
 42	/*
 43	 * This is where things start to get really bad for the
 44	 * SoC and the thermal zones should be set up to trigger
 45	 * critical temperature at 85000 mC so we don't get above
 46	 * this point.
 47	 */
 48	85000,
 49	90000,
 50	95000,
 51	100000,
 52};
 53
 54struct db8500_thermal_zone {
 55	struct thermal_zone_device *tz;
 56	struct device *dev;
 57	unsigned long interpolated_temp;
 
 
 
 
 58	unsigned int cur_index;
 59};
 60
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 61/* Callback to get current temperature */
 62static int db8500_thermal_get_temp(struct thermal_zone_device *tz, int *temp)
 63{
 64	struct db8500_thermal_zone *th = thermal_zone_device_priv(tz);
 65
 66	/*
 67	 * TODO: There is no PRCMU interface to get temperature data currently,
 68	 * so a pseudo temperature is returned , it works for thermal framework
 69	 * and this will be fixed when the PRCMU interface is available.
 70	 */
 71	*temp = th->interpolated_temp;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 72
 73	return 0;
 74}
 75
 76static const struct thermal_zone_device_ops thdev_ops = {
 77	.get_temp = db8500_thermal_get_temp,
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 78};
 79
 80static void db8500_thermal_update_config(struct db8500_thermal_zone *th,
 81					 unsigned int idx,
 82					 unsigned long next_low,
 83					 unsigned long next_high)
 84{
 85	prcmu_stop_temp_sense();
 86
 87	th->cur_index = idx;
 88	th->interpolated_temp = (next_low + next_high)/2;
 
 89
 90	/*
 91	 * The PRCMU accept absolute temperatures in celsius so divide
 92	 * down the millicelsius with 1000
 93	 */
 94	prcmu_config_hotmon((u8)(next_low/1000), (u8)(next_high/1000));
 95	prcmu_start_temp_sense(PRCMU_DEFAULT_MEASURE_TIME);
 96}
 97
 98static irqreturn_t prcmu_low_irq_handler(int irq, void *irq_data)
 99{
100	struct db8500_thermal_zone *th = irq_data;
101	unsigned int idx = th->cur_index;
 
102	unsigned long next_low, next_high;
103
104	if (idx == 0)
105		/* Meaningless for thermal management, ignoring it */
106		return IRQ_HANDLED;
107
108	if (idx == 1) {
109		next_high = db8500_thermal_points[0];
110		next_low = PRCMU_DEFAULT_LOW_TEMP;
111	} else {
112		next_high = db8500_thermal_points[idx - 1];
113		next_low = db8500_thermal_points[idx - 2];
114	}
115	idx -= 1;
116
117	db8500_thermal_update_config(th, idx, next_low, next_high);
118	dev_dbg(th->dev,
 
 
119		"PRCMU set max %ld, min %ld\n", next_high, next_low);
120
121	thermal_zone_device_update(th->tz, THERMAL_EVENT_UNSPECIFIED);
122
123	return IRQ_HANDLED;
124}
125
126static irqreturn_t prcmu_high_irq_handler(int irq, void *irq_data)
127{
128	struct db8500_thermal_zone *th = irq_data;
129	unsigned int idx = th->cur_index;
 
130	unsigned long next_low, next_high;
131	int num_points = ARRAY_SIZE(db8500_thermal_points);
132
133	if (idx < num_points - 1) {
134		next_high = db8500_thermal_points[idx+1];
135		next_low = db8500_thermal_points[idx];
136		idx += 1;
137
138		db8500_thermal_update_config(th, idx, next_low, next_high);
 
139
140		dev_dbg(th->dev,
141			"PRCMU set max %ld, min %ld\n", next_high, next_low);
142	} else if (idx == num_points - 1)
143		/* So we roof out 1 degree over the max point */
144		th->interpolated_temp = db8500_thermal_points[idx] + 1;
145
146	thermal_zone_device_update(th->tz, THERMAL_EVENT_UNSPECIFIED);
147
148	return IRQ_HANDLED;
149}
150
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
151static int db8500_thermal_probe(struct platform_device *pdev)
152{
153	struct db8500_thermal_zone *th = NULL;
154	struct device *dev = &pdev->dev;
 
155	int low_irq, high_irq, ret = 0;
 
156
157	th = devm_kzalloc(dev, sizeof(*th), GFP_KERNEL);
158	if (!th)
 
 
 
 
 
 
 
 
159		return -ENOMEM;
160
161	th->dev = dev;
 
 
 
 
 
 
162
163	low_irq = platform_get_irq_byname(pdev, "IRQ_HOTMON_LOW");
164	if (low_irq < 0)
165		return low_irq;
 
 
 
166
167	ret = devm_request_threaded_irq(dev, low_irq, NULL,
168		prcmu_low_irq_handler, IRQF_NO_SUSPEND | IRQF_ONESHOT,
169		"dbx500_temp_low", th);
170	if (ret < 0) {
171		dev_err(dev, "failed to allocate temp low irq\n");
172		return ret;
173	}
174
175	high_irq = platform_get_irq_byname(pdev, "IRQ_HOTMON_HIGH");
176	if (high_irq < 0)
177		return high_irq;
 
 
 
178
179	ret = devm_request_threaded_irq(dev, high_irq, NULL,
180		prcmu_high_irq_handler, IRQF_NO_SUSPEND | IRQF_ONESHOT,
181		"dbx500_temp_high", th);
182	if (ret < 0) {
183		dev_err(dev, "failed to allocate temp high irq\n");
184		return ret;
185	}
186
187	/* register of thermal sensor and get info from DT */
188	th->tz = devm_thermal_of_zone_register(dev, 0, th, &thdev_ops);
189	if (IS_ERR(th->tz)) {
190		dev_err(dev, "register thermal zone sensor failed\n");
191		return PTR_ERR(th->tz);
 
 
192	}
193	dev_info(dev, "thermal zone sensor registered\n");
194
195	/* Start measuring at the lowest point */
196	db8500_thermal_update_config(th, 0, PRCMU_DEFAULT_LOW_TEMP,
197				     db8500_thermal_points[0]);
198
199	platform_set_drvdata(pdev, th);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
200
201	return 0;
202}
203
204static int db8500_thermal_suspend(struct platform_device *pdev,
205		pm_message_t state)
206{
 
 
 
207	prcmu_stop_temp_sense();
208
209	return 0;
210}
211
212static int db8500_thermal_resume(struct platform_device *pdev)
213{
214	struct db8500_thermal_zone *th = platform_get_drvdata(pdev);
 
 
 
 
 
215
216	/* Resume and start measuring at the lowest point */
217	db8500_thermal_update_config(th, 0, PRCMU_DEFAULT_LOW_TEMP,
218				     db8500_thermal_points[0]);
219
220	return 0;
221}
222
 
223static const struct of_device_id db8500_thermal_match[] = {
224	{ .compatible = "stericsson,db8500-thermal" },
225	{},
226};
227MODULE_DEVICE_TABLE(of, db8500_thermal_match);
 
228
229static struct platform_driver db8500_thermal_driver = {
230	.driver = {
231		.name = "db8500-thermal",
232		.of_match_table = db8500_thermal_match,
233	},
234	.probe = db8500_thermal_probe,
235	.suspend = db8500_thermal_suspend,
236	.resume = db8500_thermal_resume,
 
237};
238
239module_platform_driver(db8500_thermal_driver);
240
241MODULE_AUTHOR("Hongbo Zhang <hongbo.zhang@stericsson.com>");
242MODULE_DESCRIPTION("DB8500 thermal driver");
243MODULE_LICENSE("GPL");
v4.17
 
  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, THERMAL_WEIGHT_DEFAULT);
 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, int *temp)
111{
112	struct db8500_thermal_zone *pzone = thermal->devdata;
113
114	/*
115	 * TODO: There is no PRCMU interface to get temperature data currently,
116	 * so a pseudo temperature is returned , it works for thermal framework
117	 * and this will be fixed when the PRCMU interface is available.
118	 */
119	*temp = pzone->cur_temp_pseudo;
120
121	return 0;
122}
123
124/* Callback to get temperature changing trend */
125static int db8500_sys_get_trend(struct thermal_zone_device *thermal,
126		int trip, enum thermal_trend *trend)
127{
128	struct db8500_thermal_zone *pzone = thermal->devdata;
129
130	*trend = pzone->trend;
131
132	return 0;
133}
134
135/* Callback to get thermal zone mode */
136static int db8500_sys_get_mode(struct thermal_zone_device *thermal,
137		enum thermal_device_mode *mode)
138{
139	struct db8500_thermal_zone *pzone = thermal->devdata;
140
141	mutex_lock(&pzone->th_lock);
142	*mode = pzone->mode;
143	mutex_unlock(&pzone->th_lock);
144
145	return 0;
146}
147
148/* Callback to set thermal zone mode */
149static int db8500_sys_set_mode(struct thermal_zone_device *thermal,
150		enum thermal_device_mode mode)
151{
152	struct db8500_thermal_zone *pzone = thermal->devdata;
153
154	mutex_lock(&pzone->th_lock);
155
156	pzone->mode = mode;
157	if (mode == THERMAL_DEVICE_ENABLED)
158		schedule_work(&pzone->therm_work);
159
160	mutex_unlock(&pzone->th_lock);
161
162	return 0;
163}
164
165/* Callback to get trip point type */
166static int db8500_sys_get_trip_type(struct thermal_zone_device *thermal,
167		int trip, enum thermal_trip_type *type)
168{
169	struct db8500_thermal_zone *pzone = thermal->devdata;
170	struct db8500_thsens_platform_data *ptrips = pzone->trip_tab;
171
172	if (trip >= ptrips->num_trips)
173		return -EINVAL;
174
175	*type = ptrips->trip_points[trip].type;
176
177	return 0;
178}
179
180/* Callback to get trip point temperature */
181static int db8500_sys_get_trip_temp(struct thermal_zone_device *thermal,
182		int trip, int *temp)
183{
184	struct db8500_thermal_zone *pzone = thermal->devdata;
185	struct db8500_thsens_platform_data *ptrips = pzone->trip_tab;
186
187	if (trip >= ptrips->num_trips)
188		return -EINVAL;
189
190	*temp = ptrips->trip_points[trip].temp;
191
192	return 0;
193}
194
195/* Callback to get critical trip point temperature */
196static int db8500_sys_get_crit_temp(struct thermal_zone_device *thermal,
197		int *temp)
198{
199	struct db8500_thermal_zone *pzone = thermal->devdata;
200	struct db8500_thsens_platform_data *ptrips = pzone->trip_tab;
201	int i;
202
203	for (i = ptrips->num_trips - 1; i > 0; i--) {
204		if (ptrips->trip_points[i].type == THERMAL_TRIP_CRITICAL) {
205			*temp = ptrips->trip_points[i].temp;
206			return 0;
207		}
208	}
209
210	return -EINVAL;
211}
212
213static struct thermal_zone_device_ops thdev_ops = {
214	.bind = db8500_cdev_bind,
215	.unbind = db8500_cdev_unbind,
216	.get_temp = db8500_sys_get_temp,
217	.get_trend = db8500_sys_get_trend,
218	.get_mode = db8500_sys_get_mode,
219	.set_mode = db8500_sys_set_mode,
220	.get_trip_type = db8500_sys_get_trip_type,
221	.get_trip_temp = db8500_sys_get_trip_temp,
222	.get_crit_temp = db8500_sys_get_crit_temp,
223};
224
225static void db8500_thermal_update_config(struct db8500_thermal_zone *pzone,
226		unsigned int idx, enum thermal_trend trend,
227		unsigned long next_low, unsigned long next_high)
 
228{
229	prcmu_stop_temp_sense();
230
231	pzone->cur_index = idx;
232	pzone->cur_temp_pseudo = (next_low + next_high)/2;
233	pzone->trend = trend;
234
 
 
 
 
235	prcmu_config_hotmon((u8)(next_low/1000), (u8)(next_high/1000));
236	prcmu_start_temp_sense(PRCMU_DEFAULT_MEASURE_TIME);
237}
238
239static irqreturn_t prcmu_low_irq_handler(int irq, void *irq_data)
240{
241	struct db8500_thermal_zone *pzone = irq_data;
242	struct db8500_thsens_platform_data *ptrips = pzone->trip_tab;
243	unsigned int idx = pzone->cur_index;
244	unsigned long next_low, next_high;
245
246	if (unlikely(idx == 0))
247		/* Meaningless for thermal management, ignoring it */
248		return IRQ_HANDLED;
249
250	if (idx == 1) {
251		next_high = ptrips->trip_points[0].temp;
252		next_low = PRCMU_DEFAULT_LOW_TEMP;
253	} else {
254		next_high = ptrips->trip_points[idx-1].temp;
255		next_low = ptrips->trip_points[idx-2].temp;
256	}
257	idx -= 1;
258
259	db8500_thermal_update_config(pzone, idx, THERMAL_TREND_DROPPING,
260		next_low, next_high);
261
262	dev_dbg(&pzone->therm_dev->device,
263		"PRCMU set max %ld, min %ld\n", next_high, next_low);
264
265	schedule_work(&pzone->therm_work);
266
267	return IRQ_HANDLED;
268}
269
270static irqreturn_t prcmu_high_irq_handler(int irq, void *irq_data)
271{
272	struct db8500_thermal_zone *pzone = irq_data;
273	struct db8500_thsens_platform_data *ptrips = pzone->trip_tab;
274	unsigned int idx = pzone->cur_index;
275	unsigned long next_low, next_high;
 
276
277	if (idx < ptrips->num_trips - 1) {
278		next_high = ptrips->trip_points[idx+1].temp;
279		next_low = ptrips->trip_points[idx].temp;
280		idx += 1;
281
282		db8500_thermal_update_config(pzone, idx, THERMAL_TREND_RAISING,
283			next_low, next_high);
284
285		dev_dbg(&pzone->therm_dev->device,
286		"PRCMU set max %ld, min %ld\n", next_high, next_low);
287	} else if (idx == ptrips->num_trips - 1)
288		pzone->cur_temp_pseudo = ptrips->trip_points[idx].temp + 1;
 
289
290	schedule_work(&pzone->therm_work);
291
292	return IRQ_HANDLED;
293}
294
295static void db8500_thermal_work(struct work_struct *work)
296{
297	enum thermal_device_mode cur_mode;
298	struct db8500_thermal_zone *pzone;
299
300	pzone = container_of(work, struct db8500_thermal_zone, therm_work);
301
302	mutex_lock(&pzone->th_lock);
303	cur_mode = pzone->mode;
304	mutex_unlock(&pzone->th_lock);
305
306	if (cur_mode == THERMAL_DEVICE_DISABLED)
307		return;
308
309	thermal_zone_device_update(pzone->therm_dev, THERMAL_EVENT_UNSPECIFIED);
310	dev_dbg(&pzone->therm_dev->device, "thermal work finished.\n");
311}
312
313#ifdef CONFIG_OF
314static struct db8500_thsens_platform_data*
315		db8500_thermal_parse_dt(struct platform_device *pdev)
316{
317	struct db8500_thsens_platform_data *ptrips;
318	struct device_node *np = pdev->dev.of_node;
319	char prop_name[32];
320	const char *tmp_str;
321	u32 tmp_data;
322	int i, j;
323
324	ptrips = devm_kzalloc(&pdev->dev, sizeof(*ptrips), GFP_KERNEL);
325	if (!ptrips)
326		return NULL;
327
328	if (of_property_read_u32(np, "num-trips", &tmp_data))
329		goto err_parse_dt;
330
331	if (tmp_data > THERMAL_MAX_TRIPS)
332		goto err_parse_dt;
333
334	ptrips->num_trips = tmp_data;
335
336	for (i = 0; i < ptrips->num_trips; i++) {
337		sprintf(prop_name, "trip%d-temp", i);
338		if (of_property_read_u32(np, prop_name, &tmp_data))
339			goto err_parse_dt;
340
341		ptrips->trip_points[i].temp = tmp_data;
342
343		sprintf(prop_name, "trip%d-type", i);
344		if (of_property_read_string(np, prop_name, &tmp_str))
345			goto err_parse_dt;
346
347		if (!strcmp(tmp_str, "active"))
348			ptrips->trip_points[i].type = THERMAL_TRIP_ACTIVE;
349		else if (!strcmp(tmp_str, "passive"))
350			ptrips->trip_points[i].type = THERMAL_TRIP_PASSIVE;
351		else if (!strcmp(tmp_str, "hot"))
352			ptrips->trip_points[i].type = THERMAL_TRIP_HOT;
353		else if (!strcmp(tmp_str, "critical"))
354			ptrips->trip_points[i].type = THERMAL_TRIP_CRITICAL;
355		else
356			goto err_parse_dt;
357
358		sprintf(prop_name, "trip%d-cdev-num", i);
359		if (of_property_read_u32(np, prop_name, &tmp_data))
360			goto err_parse_dt;
361
362		if (tmp_data > COOLING_DEV_MAX)
363			goto err_parse_dt;
364
365		for (j = 0; j < tmp_data; j++) {
366			sprintf(prop_name, "trip%d-cdev-name%d", i, j);
367			if (of_property_read_string(np, prop_name, &tmp_str))
368				goto err_parse_dt;
369
370			if (strlen(tmp_str) >= THERMAL_NAME_LENGTH)
371				goto err_parse_dt;
372
373			strcpy(ptrips->trip_points[i].cdev_name[j], tmp_str);
374		}
375	}
376	return ptrips;
377
378err_parse_dt:
379	dev_err(&pdev->dev, "Parsing device tree data error.\n");
380	return NULL;
381}
382#else
383static inline struct db8500_thsens_platform_data*
384		db8500_thermal_parse_dt(struct platform_device *pdev)
385{
386	return NULL;
387}
388#endif
389
390static int db8500_thermal_probe(struct platform_device *pdev)
391{
392	struct db8500_thermal_zone *pzone = NULL;
393	struct db8500_thsens_platform_data *ptrips = NULL;
394	struct device_node *np = pdev->dev.of_node;
395	int low_irq, high_irq, ret = 0;
396	unsigned long dft_low, dft_high;
397
398	if (np)
399		ptrips = db8500_thermal_parse_dt(pdev);
400	else
401		ptrips = dev_get_platdata(&pdev->dev);
402
403	if (!ptrips)
404		return -EINVAL;
405
406	pzone = devm_kzalloc(&pdev->dev, sizeof(*pzone), GFP_KERNEL);
407	if (!pzone)
408		return -ENOMEM;
409
410	mutex_init(&pzone->th_lock);
411	mutex_lock(&pzone->th_lock);
412
413	pzone->mode = THERMAL_DEVICE_DISABLED;
414	pzone->trip_tab = ptrips;
415
416	INIT_WORK(&pzone->therm_work, db8500_thermal_work);
417
418	low_irq = platform_get_irq_byname(pdev, "IRQ_HOTMON_LOW");
419	if (low_irq < 0) {
420		dev_err(&pdev->dev, "Get IRQ_HOTMON_LOW failed.\n");
421		ret = low_irq;
422		goto out_unlock;
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		goto out_unlock;
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		ret = high_irq;
437		goto out_unlock;
438	}
439
440	ret = devm_request_threaded_irq(&pdev->dev, high_irq, NULL,
441		prcmu_high_irq_handler, IRQF_NO_SUSPEND | IRQF_ONESHOT,
442		"dbx500_temp_high", pzone);
443	if (ret < 0) {
444		dev_err(&pdev->dev, "Failed to allocate temp high irq.\n");
445		goto out_unlock;
446	}
447
448	pzone->therm_dev = thermal_zone_device_register("db8500_thermal_zone",
449		ptrips->num_trips, 0, pzone, &thdev_ops, NULL, 0, 0);
450
451	if (IS_ERR(pzone->therm_dev)) {
452		dev_err(&pdev->dev, "Register thermal zone device failed.\n");
453		ret = PTR_ERR(pzone->therm_dev);
454		goto out_unlock;
455	}
456	dev_info(&pdev->dev, "Thermal zone device registered.\n");
457
458	dft_low = PRCMU_DEFAULT_LOW_TEMP;
459	dft_high = ptrips->trip_points[0].temp;
 
460
461	db8500_thermal_update_config(pzone, 0, THERMAL_TREND_STABLE,
462		dft_low, dft_high);
463
464	platform_set_drvdata(pdev, pzone);
465	pzone->mode = THERMAL_DEVICE_ENABLED;
466
467out_unlock:
468	mutex_unlock(&pzone->th_lock);
469
470	return ret;
471}
472
473static int db8500_thermal_remove(struct platform_device *pdev)
474{
475	struct db8500_thermal_zone *pzone = platform_get_drvdata(pdev);
476
477	thermal_zone_device_unregister(pzone->therm_dev);
478	cancel_work_sync(&pzone->therm_work);
479	mutex_destroy(&pzone->th_lock);
480
481	return 0;
482}
483
484static int db8500_thermal_suspend(struct platform_device *pdev,
485		pm_message_t state)
486{
487	struct db8500_thermal_zone *pzone = platform_get_drvdata(pdev);
488
489	flush_work(&pzone->therm_work);
490	prcmu_stop_temp_sense();
491
492	return 0;
493}
494
495static int db8500_thermal_resume(struct platform_device *pdev)
496{
497	struct db8500_thermal_zone *pzone = platform_get_drvdata(pdev);
498	struct db8500_thsens_platform_data *ptrips = pzone->trip_tab;
499	unsigned long dft_low, dft_high;
500
501	dft_low = PRCMU_DEFAULT_LOW_TEMP;
502	dft_high = ptrips->trip_points[0].temp;
503
504	db8500_thermal_update_config(pzone, 0, THERMAL_TREND_STABLE,
505		dft_low, dft_high);
 
506
507	return 0;
508}
509
510#ifdef CONFIG_OF
511static const struct of_device_id db8500_thermal_match[] = {
512	{ .compatible = "stericsson,db8500-thermal" },
513	{},
514};
515MODULE_DEVICE_TABLE(of, db8500_thermal_match);
516#endif
517
518static struct platform_driver db8500_thermal_driver = {
519	.driver = {
520		.name = "db8500-thermal",
521		.of_match_table = of_match_ptr(db8500_thermal_match),
522	},
523	.probe = db8500_thermal_probe,
524	.suspend = db8500_thermal_suspend,
525	.resume = db8500_thermal_resume,
526	.remove = db8500_thermal_remove,
527};
528
529module_platform_driver(db8500_thermal_driver);
530
531MODULE_AUTHOR("Hongbo Zhang <hongbo.zhang@stericsson.com>");
532MODULE_DESCRIPTION("DB8500 thermal driver");
533MODULE_LICENSE("GPL");