Commit | Line | Data |
---|---|---|
554fdbaf FL |
1 | // SPDX-License-Identifier: GPL-2.0 |
2 | // Copyright (C) 2020 Spreadtrum Communications Inc. | |
3 | ||
4 | #include <linux/clk.h> | |
5 | #include <linux/io.h> | |
6 | #include <linux/iopoll.h> | |
7 | #include <linux/module.h> | |
8 | #include <linux/nvmem-consumer.h> | |
9 | #include <linux/of_device.h> | |
10 | #include <linux/platform_device.h> | |
11 | #include <linux/slab.h> | |
12 | #include <linux/thermal.h> | |
13 | ||
14 | #define SPRD_THM_CTL 0x0 | |
15 | #define SPRD_THM_INT_EN 0x4 | |
16 | #define SPRD_THM_INT_STS 0x8 | |
17 | #define SPRD_THM_INT_RAW_STS 0xc | |
18 | #define SPRD_THM_DET_PERIOD 0x10 | |
19 | #define SPRD_THM_INT_CLR 0x14 | |
20 | #define SPRD_THM_INT_CLR_ST 0x18 | |
21 | #define SPRD_THM_MON_PERIOD 0x4c | |
22 | #define SPRD_THM_MON_CTL 0x50 | |
23 | #define SPRD_THM_INTERNAL_STS1 0x54 | |
24 | #define SPRD_THM_RAW_READ_MSK 0x3ff | |
25 | ||
26 | #define SPRD_THM_OFFSET(id) ((id) * 0x4) | |
27 | #define SPRD_THM_TEMP(id) (SPRD_THM_OFFSET(id) + 0x5c) | |
28 | #define SPRD_THM_THRES(id) (SPRD_THM_OFFSET(id) + 0x2c) | |
29 | ||
30 | #define SPRD_THM_SEN(id) BIT((id) + 2) | |
31 | #define SPRD_THM_SEN_OVERHEAT_EN(id) BIT((id) + 8) | |
32 | #define SPRD_THM_SEN_OVERHEAT_ALARM_EN(id) BIT((id) + 0) | |
33 | ||
34 | /* bits definitions for register THM_CTL */ | |
35 | #define SPRD_THM_SET_RDY_ST BIT(13) | |
36 | #define SPRD_THM_SET_RDY BIT(12) | |
37 | #define SPRD_THM_MON_EN BIT(1) | |
38 | #define SPRD_THM_EN BIT(0) | |
39 | ||
40 | /* bits definitions for register THM_INT_CTL */ | |
41 | #define SPRD_THM_BIT_INT_EN BIT(26) | |
42 | #define SPRD_THM_OVERHEAT_EN BIT(25) | |
43 | #define SPRD_THM_OTP_TRIP_SHIFT 10 | |
44 | ||
45 | /* bits definitions for register SPRD_THM_INTERNAL_STS1 */ | |
46 | #define SPRD_THM_TEMPER_RDY BIT(0) | |
47 | ||
48 | #define SPRD_THM_DET_PERIOD_DATA 0x800 | |
49 | #define SPRD_THM_DET_PERIOD_MASK GENMASK(19, 0) | |
50 | #define SPRD_THM_MON_MODE 0x7 | |
51 | #define SPRD_THM_MON_MODE_MASK GENMASK(3, 0) | |
52 | #define SPRD_THM_MON_PERIOD_DATA 0x10 | |
53 | #define SPRD_THM_MON_PERIOD_MASK GENMASK(15, 0) | |
54 | #define SPRD_THM_THRES_MASK GENMASK(19, 0) | |
55 | #define SPRD_THM_INT_CLR_MASK GENMASK(24, 0) | |
56 | ||
57 | /* thermal sensor calibration parameters */ | |
58 | #define SPRD_THM_TEMP_LOW -40000 | |
59 | #define SPRD_THM_TEMP_HIGH 120000 | |
60 | #define SPRD_THM_OTP_TEMP 120000 | |
61 | #define SPRD_THM_HOT_TEMP 75000 | |
62 | #define SPRD_THM_RAW_DATA_LOW 0 | |
63 | #define SPRD_THM_RAW_DATA_HIGH 1000 | |
64 | #define SPRD_THM_SEN_NUM 8 | |
65 | #define SPRD_THM_DT_OFFSET 24 | |
66 | #define SPRD_THM_RATION_OFFSET 17 | |
67 | #define SPRD_THM_RATION_SIGN 16 | |
68 | ||
69 | #define SPRD_THM_RDYST_POLLING_TIME 10 | |
70 | #define SPRD_THM_RDYST_TIMEOUT 700 | |
71 | #define SPRD_THM_TEMP_READY_POLL_TIME 10000 | |
72 | #define SPRD_THM_TEMP_READY_TIMEOUT 600000 | |
73 | #define SPRD_THM_MAX_SENSOR 8 | |
74 | ||
75 | struct sprd_thermal_sensor { | |
76 | struct thermal_zone_device *tzd; | |
77 | struct sprd_thermal_data *data; | |
78 | struct device *dev; | |
79 | int cal_slope; | |
80 | int cal_offset; | |
81 | int id; | |
82 | }; | |
83 | ||
84 | struct sprd_thermal_data { | |
85 | const struct sprd_thm_variant_data *var_data; | |
86 | struct sprd_thermal_sensor *sensor[SPRD_THM_MAX_SENSOR]; | |
87 | struct clk *clk; | |
88 | void __iomem *base; | |
89 | u32 ratio_off; | |
90 | int ratio_sign; | |
91 | int nr_sensors; | |
92 | }; | |
93 | ||
94 | /* | |
95 | * The conversion between ADC and temperature is based on linear relationship, | |
96 | * and use idea_k to specify the slope and ideal_b to specify the offset. | |
97 | * | |
98 | * Since different Spreadtrum SoCs have different ideal_k and ideal_b, | |
99 | * we should save ideal_k and ideal_b in the device data structure. | |
100 | */ | |
101 | struct sprd_thm_variant_data { | |
102 | u32 ideal_k; | |
103 | u32 ideal_b; | |
104 | }; | |
105 | ||
106 | static const struct sprd_thm_variant_data ums512_data = { | |
107 | .ideal_k = 262, | |
108 | .ideal_b = 66400, | |
109 | }; | |
110 | ||
111 | static inline void sprd_thm_update_bits(void __iomem *reg, u32 mask, u32 val) | |
112 | { | |
113 | u32 tmp, orig; | |
114 | ||
115 | orig = readl(reg); | |
116 | tmp = orig & ~mask; | |
117 | tmp |= val & mask; | |
118 | writel(tmp, reg); | |
119 | } | |
120 | ||
121 | static int sprd_thm_cal_read(struct device_node *np, const char *cell_id, | |
122 | u32 *val) | |
123 | { | |
124 | struct nvmem_cell *cell; | |
125 | void *buf; | |
126 | size_t len; | |
127 | ||
128 | cell = of_nvmem_cell_get(np, cell_id); | |
129 | if (IS_ERR(cell)) | |
130 | return PTR_ERR(cell); | |
131 | ||
132 | buf = nvmem_cell_read(cell, &len); | |
133 | nvmem_cell_put(cell); | |
134 | if (IS_ERR(buf)) | |
135 | return PTR_ERR(buf); | |
136 | ||
137 | if (len > sizeof(u32)) { | |
138 | kfree(buf); | |
139 | return -EINVAL; | |
140 | } | |
141 | ||
142 | memcpy(val, buf, len); | |
143 | ||
144 | kfree(buf); | |
145 | return 0; | |
146 | } | |
147 | ||
148 | static int sprd_thm_sensor_calibration(struct device_node *np, | |
149 | struct sprd_thermal_data *thm, | |
150 | struct sprd_thermal_sensor *sen) | |
151 | { | |
152 | int ret; | |
153 | /* | |
154 | * According to thermal datasheet, the default calibration offset is 64, | |
155 | * and the default ratio is 1000. | |
156 | */ | |
157 | int dt_offset = 64, ratio = 1000; | |
158 | ||
159 | ret = sprd_thm_cal_read(np, "sen_delta_cal", &dt_offset); | |
160 | if (ret) | |
161 | return ret; | |
162 | ||
163 | ratio += thm->ratio_sign * thm->ratio_off; | |
164 | ||
165 | /* | |
166 | * According to the ideal slope K and ideal offset B, combined with | |
167 | * calibration value of thermal from efuse, then calibrate the real | |
168 | * slope k and offset b: | |
169 | * k_cal = (k * ratio) / 1000. | |
170 | * b_cal = b + (dt_offset - 64) * 500. | |
171 | */ | |
172 | sen->cal_slope = (thm->var_data->ideal_k * ratio) / 1000; | |
173 | sen->cal_offset = thm->var_data->ideal_b + (dt_offset - 128) * 250; | |
174 | ||
175 | return 0; | |
176 | } | |
177 | ||
178 | static int sprd_thm_rawdata_to_temp(struct sprd_thermal_sensor *sen, | |
179 | u32 rawdata) | |
180 | { | |
181 | clamp(rawdata, (u32)SPRD_THM_RAW_DATA_LOW, (u32)SPRD_THM_RAW_DATA_HIGH); | |
182 | ||
183 | /* | |
184 | * According to the thermal datasheet, the formula of converting | |
185 | * adc value to the temperature value should be: | |
186 | * T_final = k_cal * x - b_cal. | |
187 | */ | |
188 | return sen->cal_slope * rawdata - sen->cal_offset; | |
189 | } | |
190 | ||
191 | static int sprd_thm_temp_to_rawdata(int temp, struct sprd_thermal_sensor *sen) | |
192 | { | |
193 | u32 val; | |
194 | ||
195 | clamp(temp, (int)SPRD_THM_TEMP_LOW, (int)SPRD_THM_TEMP_HIGH); | |
196 | ||
197 | /* | |
198 | * According to the thermal datasheet, the formula of converting | |
199 | * adc value to the temperature value should be: | |
200 | * T_final = k_cal * x - b_cal. | |
201 | */ | |
202 | val = (temp + sen->cal_offset) / sen->cal_slope; | |
203 | ||
204 | return clamp(val, val, (u32)(SPRD_THM_RAW_DATA_HIGH - 1)); | |
205 | } | |
206 | ||
7f689a2e | 207 | static int sprd_thm_read_temp(struct thermal_zone_device *tz, int *temp) |
554fdbaf | 208 | { |
5f68d078 | 209 | struct sprd_thermal_sensor *sen = thermal_zone_device_priv(tz); |
554fdbaf FL |
210 | u32 data; |
211 | ||
212 | data = readl(sen->data->base + SPRD_THM_TEMP(sen->id)) & | |
213 | SPRD_THM_RAW_READ_MSK; | |
214 | ||
215 | *temp = sprd_thm_rawdata_to_temp(sen, data); | |
216 | ||
217 | return 0; | |
218 | } | |
219 | ||
7f689a2e | 220 | static const struct thermal_zone_device_ops sprd_thm_ops = { |
554fdbaf FL |
221 | .get_temp = sprd_thm_read_temp, |
222 | }; | |
223 | ||
224 | static int sprd_thm_poll_ready_status(struct sprd_thermal_data *thm) | |
225 | { | |
226 | u32 val; | |
227 | int ret; | |
228 | ||
229 | /* | |
230 | * Wait for thermal ready status before configuring thermal parameters. | |
231 | */ | |
232 | ret = readl_poll_timeout(thm->base + SPRD_THM_CTL, val, | |
233 | !(val & SPRD_THM_SET_RDY_ST), | |
234 | SPRD_THM_RDYST_POLLING_TIME, | |
235 | SPRD_THM_RDYST_TIMEOUT); | |
236 | if (ret) | |
237 | return ret; | |
238 | ||
239 | sprd_thm_update_bits(thm->base + SPRD_THM_CTL, SPRD_THM_MON_EN, | |
240 | SPRD_THM_MON_EN); | |
241 | sprd_thm_update_bits(thm->base + SPRD_THM_CTL, SPRD_THM_SET_RDY, | |
242 | SPRD_THM_SET_RDY); | |
243 | return 0; | |
244 | } | |
245 | ||
246 | static int sprd_thm_wait_temp_ready(struct sprd_thermal_data *thm) | |
247 | { | |
248 | u32 val; | |
249 | ||
250 | /* Wait for first temperature data ready before reading temperature */ | |
251 | return readl_poll_timeout(thm->base + SPRD_THM_INTERNAL_STS1, val, | |
252 | !(val & SPRD_THM_TEMPER_RDY), | |
253 | SPRD_THM_TEMP_READY_POLL_TIME, | |
254 | SPRD_THM_TEMP_READY_TIMEOUT); | |
255 | } | |
256 | ||
257 | static int sprd_thm_set_ready(struct sprd_thermal_data *thm) | |
258 | { | |
259 | int ret; | |
260 | ||
261 | ret = sprd_thm_poll_ready_status(thm); | |
262 | if (ret) | |
263 | return ret; | |
264 | ||
265 | /* | |
266 | * Clear interrupt status, enable thermal interrupt and enable thermal. | |
267 | * | |
268 | * The SPRD thermal controller integrates a hardware interrupt signal, | |
269 | * which means if the temperature is overheat, it will generate an | |
270 | * interrupt and notify the event to PMIC automatically to shutdown the | |
271 | * system. So here we should enable the interrupt bits, though we have | |
272 | * not registered an irq handler. | |
273 | */ | |
274 | writel(SPRD_THM_INT_CLR_MASK, thm->base + SPRD_THM_INT_CLR); | |
275 | sprd_thm_update_bits(thm->base + SPRD_THM_INT_EN, | |
276 | SPRD_THM_BIT_INT_EN, SPRD_THM_BIT_INT_EN); | |
277 | sprd_thm_update_bits(thm->base + SPRD_THM_CTL, | |
278 | SPRD_THM_EN, SPRD_THM_EN); | |
279 | return 0; | |
280 | } | |
281 | ||
282 | static void sprd_thm_sensor_init(struct sprd_thermal_data *thm, | |
283 | struct sprd_thermal_sensor *sen) | |
284 | { | |
285 | u32 otp_rawdata, hot_rawdata; | |
286 | ||
287 | otp_rawdata = sprd_thm_temp_to_rawdata(SPRD_THM_OTP_TEMP, sen); | |
288 | hot_rawdata = sprd_thm_temp_to_rawdata(SPRD_THM_HOT_TEMP, sen); | |
289 | ||
290 | /* Enable the sensor' overheat temperature protection interrupt */ | |
291 | sprd_thm_update_bits(thm->base + SPRD_THM_INT_EN, | |
292 | SPRD_THM_SEN_OVERHEAT_ALARM_EN(sen->id), | |
293 | SPRD_THM_SEN_OVERHEAT_ALARM_EN(sen->id)); | |
294 | ||
295 | /* Set the sensor' overheat and hot threshold temperature */ | |
296 | sprd_thm_update_bits(thm->base + SPRD_THM_THRES(sen->id), | |
297 | SPRD_THM_THRES_MASK, | |
298 | (otp_rawdata << SPRD_THM_OTP_TRIP_SHIFT) | | |
299 | hot_rawdata); | |
300 | ||
301 | /* Enable the corresponding sensor */ | |
302 | sprd_thm_update_bits(thm->base + SPRD_THM_CTL, SPRD_THM_SEN(sen->id), | |
303 | SPRD_THM_SEN(sen->id)); | |
304 | } | |
305 | ||
306 | static void sprd_thm_para_config(struct sprd_thermal_data *thm) | |
307 | { | |
308 | /* Set the period of two valid temperature detection action */ | |
309 | sprd_thm_update_bits(thm->base + SPRD_THM_DET_PERIOD, | |
310 | SPRD_THM_DET_PERIOD_MASK, SPRD_THM_DET_PERIOD); | |
311 | ||
312 | /* Set the sensors' monitor mode */ | |
313 | sprd_thm_update_bits(thm->base + SPRD_THM_MON_CTL, | |
314 | SPRD_THM_MON_MODE_MASK, SPRD_THM_MON_MODE); | |
315 | ||
316 | /* Set the sensors' monitor period */ | |
317 | sprd_thm_update_bits(thm->base + SPRD_THM_MON_PERIOD, | |
318 | SPRD_THM_MON_PERIOD_MASK, SPRD_THM_MON_PERIOD); | |
319 | } | |
320 | ||
321 | static void sprd_thm_toggle_sensor(struct sprd_thermal_sensor *sen, bool on) | |
322 | { | |
323 | struct thermal_zone_device *tzd = sen->tzd; | |
324 | ||
7f4957be AP |
325 | if (on) |
326 | thermal_zone_device_enable(tzd); | |
327 | else | |
328 | thermal_zone_device_disable(tzd); | |
554fdbaf FL |
329 | } |
330 | ||
331 | static int sprd_thm_probe(struct platform_device *pdev) | |
332 | { | |
333 | struct device_node *np = pdev->dev.of_node; | |
334 | struct device_node *sen_child; | |
335 | struct sprd_thermal_data *thm; | |
336 | struct sprd_thermal_sensor *sen; | |
337 | const struct sprd_thm_variant_data *pdata; | |
338 | int ret, i; | |
339 | u32 val; | |
340 | ||
341 | pdata = of_device_get_match_data(&pdev->dev); | |
342 | if (!pdata) { | |
343 | dev_err(&pdev->dev, "No matching driver data found\n"); | |
344 | return -EINVAL; | |
345 | } | |
346 | ||
347 | thm = devm_kzalloc(&pdev->dev, sizeof(*thm), GFP_KERNEL); | |
348 | if (!thm) | |
349 | return -ENOMEM; | |
350 | ||
351 | thm->var_data = pdata; | |
352 | thm->base = devm_platform_ioremap_resource(pdev, 0); | |
b4147917 TY |
353 | if (IS_ERR(thm->base)) |
354 | return PTR_ERR(thm->base); | |
554fdbaf FL |
355 | |
356 | thm->nr_sensors = of_get_child_count(np); | |
357 | if (thm->nr_sensors == 0 || thm->nr_sensors > SPRD_THM_MAX_SENSOR) { | |
358 | dev_err(&pdev->dev, "incorrect sensor count\n"); | |
359 | return -EINVAL; | |
360 | } | |
361 | ||
362 | thm->clk = devm_clk_get(&pdev->dev, "enable"); | |
363 | if (IS_ERR(thm->clk)) { | |
364 | dev_err(&pdev->dev, "failed to get enable clock\n"); | |
365 | return PTR_ERR(thm->clk); | |
366 | } | |
367 | ||
368 | ret = clk_prepare_enable(thm->clk); | |
369 | if (ret) | |
370 | return ret; | |
371 | ||
372 | sprd_thm_para_config(thm); | |
373 | ||
374 | ret = sprd_thm_cal_read(np, "thm_sign_cal", &val); | |
375 | if (ret) | |
376 | goto disable_clk; | |
377 | ||
378 | if (val > 0) | |
379 | thm->ratio_sign = -1; | |
380 | else | |
381 | thm->ratio_sign = 1; | |
382 | ||
383 | ret = sprd_thm_cal_read(np, "thm_ratio_cal", &thm->ratio_off); | |
384 | if (ret) | |
385 | goto disable_clk; | |
386 | ||
387 | for_each_child_of_node(np, sen_child) { | |
388 | sen = devm_kzalloc(&pdev->dev, sizeof(*sen), GFP_KERNEL); | |
389 | if (!sen) { | |
390 | ret = -ENOMEM; | |
d8ac5bb4 | 391 | goto of_put; |
554fdbaf FL |
392 | } |
393 | ||
394 | sen->data = thm; | |
395 | sen->dev = &pdev->dev; | |
396 | ||
397 | ret = of_property_read_u32(sen_child, "reg", &sen->id); | |
398 | if (ret) { | |
399 | dev_err(&pdev->dev, "get sensor reg failed"); | |
d8ac5bb4 | 400 | goto of_put; |
554fdbaf FL |
401 | } |
402 | ||
403 | ret = sprd_thm_sensor_calibration(sen_child, thm, sen); | |
404 | if (ret) { | |
405 | dev_err(&pdev->dev, "efuse cal analysis failed"); | |
d8ac5bb4 | 406 | goto of_put; |
554fdbaf FL |
407 | } |
408 | ||
409 | sprd_thm_sensor_init(thm, sen); | |
410 | ||
7f689a2e DL |
411 | sen->tzd = devm_thermal_of_zone_register(sen->dev, |
412 | sen->id, | |
413 | sen, | |
414 | &sprd_thm_ops); | |
554fdbaf FL |
415 | if (IS_ERR(sen->tzd)) { |
416 | dev_err(&pdev->dev, "register thermal zone failed %d\n", | |
417 | sen->id); | |
418 | ret = PTR_ERR(sen->tzd); | |
d8ac5bb4 | 419 | goto of_put; |
554fdbaf FL |
420 | } |
421 | ||
422 | thm->sensor[sen->id] = sen; | |
423 | } | |
d8ac5bb4 | 424 | /* sen_child set to NULL at this point */ |
554fdbaf FL |
425 | |
426 | ret = sprd_thm_set_ready(thm); | |
427 | if (ret) | |
d8ac5bb4 | 428 | goto of_put; |
554fdbaf FL |
429 | |
430 | ret = sprd_thm_wait_temp_ready(thm); | |
431 | if (ret) | |
d8ac5bb4 | 432 | goto of_put; |
554fdbaf FL |
433 | |
434 | for (i = 0; i < thm->nr_sensors; i++) | |
435 | sprd_thm_toggle_sensor(thm->sensor[i], true); | |
436 | ||
437 | platform_set_drvdata(pdev, thm); | |
438 | return 0; | |
439 | ||
d8ac5bb4 KK |
440 | of_put: |
441 | of_node_put(sen_child); | |
554fdbaf FL |
442 | disable_clk: |
443 | clk_disable_unprepare(thm->clk); | |
444 | return ret; | |
445 | } | |
446 | ||
447 | #ifdef CONFIG_PM_SLEEP | |
448 | static void sprd_thm_hw_suspend(struct sprd_thermal_data *thm) | |
449 | { | |
450 | int i; | |
451 | ||
452 | for (i = 0; i < thm->nr_sensors; i++) { | |
453 | sprd_thm_update_bits(thm->base + SPRD_THM_CTL, | |
454 | SPRD_THM_SEN(thm->sensor[i]->id), 0); | |
455 | } | |
456 | ||
457 | sprd_thm_update_bits(thm->base + SPRD_THM_CTL, | |
458 | SPRD_THM_EN, 0x0); | |
459 | } | |
460 | ||
461 | static int sprd_thm_suspend(struct device *dev) | |
462 | { | |
463 | struct sprd_thermal_data *thm = dev_get_drvdata(dev); | |
464 | int i; | |
465 | ||
466 | for (i = 0; i < thm->nr_sensors; i++) | |
467 | sprd_thm_toggle_sensor(thm->sensor[i], false); | |
468 | ||
469 | sprd_thm_hw_suspend(thm); | |
470 | clk_disable_unprepare(thm->clk); | |
471 | ||
472 | return 0; | |
473 | } | |
474 | ||
475 | static int sprd_thm_hw_resume(struct sprd_thermal_data *thm) | |
476 | { | |
477 | int ret, i; | |
478 | ||
479 | for (i = 0; i < thm->nr_sensors; i++) { | |
480 | sprd_thm_update_bits(thm->base + SPRD_THM_CTL, | |
481 | SPRD_THM_SEN(thm->sensor[i]->id), | |
482 | SPRD_THM_SEN(thm->sensor[i]->id)); | |
483 | } | |
484 | ||
485 | ret = sprd_thm_poll_ready_status(thm); | |
486 | if (ret) | |
487 | return ret; | |
488 | ||
489 | writel(SPRD_THM_INT_CLR_MASK, thm->base + SPRD_THM_INT_CLR); | |
490 | sprd_thm_update_bits(thm->base + SPRD_THM_CTL, | |
491 | SPRD_THM_EN, SPRD_THM_EN); | |
492 | return sprd_thm_wait_temp_ready(thm); | |
493 | } | |
494 | ||
495 | static int sprd_thm_resume(struct device *dev) | |
496 | { | |
497 | struct sprd_thermal_data *thm = dev_get_drvdata(dev); | |
498 | int ret, i; | |
499 | ||
500 | ret = clk_prepare_enable(thm->clk); | |
501 | if (ret) | |
502 | return ret; | |
503 | ||
504 | ret = sprd_thm_hw_resume(thm); | |
505 | if (ret) | |
506 | goto disable_clk; | |
507 | ||
508 | for (i = 0; i < thm->nr_sensors; i++) | |
509 | sprd_thm_toggle_sensor(thm->sensor[i], true); | |
510 | ||
511 | return 0; | |
512 | ||
513 | disable_clk: | |
514 | clk_disable_unprepare(thm->clk); | |
515 | return ret; | |
516 | } | |
517 | #endif | |
518 | ||
519 | static int sprd_thm_remove(struct platform_device *pdev) | |
520 | { | |
521 | struct sprd_thermal_data *thm = platform_get_drvdata(pdev); | |
522 | int i; | |
523 | ||
524 | for (i = 0; i < thm->nr_sensors; i++) { | |
525 | sprd_thm_toggle_sensor(thm->sensor[i], false); | |
7f689a2e DL |
526 | devm_thermal_of_zone_unregister(&pdev->dev, |
527 | thm->sensor[i]->tzd); | |
554fdbaf FL |
528 | } |
529 | ||
530 | clk_disable_unprepare(thm->clk); | |
531 | return 0; | |
532 | } | |
533 | ||
534 | static const struct of_device_id sprd_thermal_of_match[] = { | |
535 | { .compatible = "sprd,ums512-thermal", .data = &ums512_data }, | |
536 | { }, | |
537 | }; | |
4d57fd9a | 538 | MODULE_DEVICE_TABLE(of, sprd_thermal_of_match); |
554fdbaf FL |
539 | |
540 | static const struct dev_pm_ops sprd_thermal_pm_ops = { | |
541 | SET_SYSTEM_SLEEP_PM_OPS(sprd_thm_suspend, sprd_thm_resume) | |
542 | }; | |
543 | ||
544 | static struct platform_driver sprd_thermal_driver = { | |
545 | .probe = sprd_thm_probe, | |
546 | .remove = sprd_thm_remove, | |
547 | .driver = { | |
548 | .name = "sprd-thermal", | |
549 | .pm = &sprd_thermal_pm_ops, | |
550 | .of_match_table = sprd_thermal_of_match, | |
551 | }, | |
552 | }; | |
553 | ||
554 | module_platform_driver(sprd_thermal_driver); | |
555 | ||
556 | MODULE_AUTHOR("Freeman Liu <freeman.liu@unisoc.com>"); | |
557 | MODULE_DESCRIPTION("Spreadtrum thermal driver"); | |
558 | MODULE_LICENSE("GPL v2"); |