1 | // SPDX-License-Identifier: GPL-2.0 |
2 | /* |
3 | * R-Car THS/TSC thermal sensor driver |
4 | * |
5 | * Copyright (C) 2012 Renesas Solutions Corp. |
6 | * Kuninori Morimoto <kuninori.morimoto.gx@renesas.com> |
7 | */ |
8 | #include <linux/delay.h> |
9 | #include <linux/err.h> |
10 | #include <linux/irq.h> |
11 | #include <linux/interrupt.h> |
12 | #include <linux/io.h> |
13 | #include <linux/module.h> |
14 | #include <linux/of.h> |
15 | #include <linux/platform_device.h> |
16 | #include <linux/pm_runtime.h> |
17 | #include <linux/reboot.h> |
18 | #include <linux/slab.h> |
19 | #include <linux/spinlock.h> |
20 | #include <linux/thermal.h> |
21 | |
22 | #include "thermal_hwmon.h" |
23 | |
24 | #define IDLE_INTERVAL 5000 |
25 | |
26 | #define COMMON_STR 0x00 |
27 | #define COMMON_ENR 0x04 |
28 | #define COMMON_INTMSK 0x0c |
29 | |
30 | #define REG_POSNEG 0x20 |
31 | #define REG_FILONOFF 0x28 |
32 | #define REG_THSCR 0x2c |
33 | #define REG_THSSR 0x30 |
34 | #define REG_INTCTRL 0x34 |
35 | |
36 | /* THSCR */ |
37 | #define CPCTL (1 << 12) |
38 | |
39 | /* THSSR */ |
40 | #define CTEMP 0x3f |
41 | |
42 | struct rcar_thermal_common { |
43 | void __iomem *base; |
44 | struct device *dev; |
45 | struct list_head head; |
46 | spinlock_t lock; |
47 | }; |
48 | |
49 | struct rcar_thermal_chip { |
50 | unsigned int use_of_thermal : 1; |
51 | unsigned int has_filonoff : 1; |
52 | unsigned int irq_per_ch : 1; |
53 | unsigned int needs_suspend_resume : 1; |
54 | unsigned int nirqs; |
55 | unsigned int ctemp_bands; |
56 | }; |
57 | |
58 | static const struct rcar_thermal_chip rcar_thermal = { |
59 | .use_of_thermal = 0, |
60 | .has_filonoff = 1, |
61 | .irq_per_ch = 0, |
62 | .needs_suspend_resume = 0, |
63 | .nirqs = 1, |
64 | .ctemp_bands = 1, |
65 | }; |
66 | |
67 | static const struct rcar_thermal_chip rcar_gen2_thermal = { |
68 | .use_of_thermal = 1, |
69 | .has_filonoff = 1, |
70 | .irq_per_ch = 0, |
71 | .needs_suspend_resume = 0, |
72 | .nirqs = 1, |
73 | .ctemp_bands = 1, |
74 | }; |
75 | |
76 | static const struct rcar_thermal_chip rcar_gen3_thermal = { |
77 | .use_of_thermal = 1, |
78 | .has_filonoff = 0, |
79 | .irq_per_ch = 1, |
80 | .needs_suspend_resume = 1, |
81 | /* |
82 | * The Gen3 chip has 3 interrupts, but this driver uses only 2 |
83 | * interrupts to detect a temperature change, rise or fall. |
84 | */ |
85 | .nirqs = 2, |
86 | .ctemp_bands = 2, |
87 | }; |
88 | |
89 | struct rcar_thermal_priv { |
90 | void __iomem *base; |
91 | struct rcar_thermal_common *common; |
92 | struct thermal_zone_device *zone; |
93 | const struct rcar_thermal_chip *chip; |
94 | struct delayed_work work; |
95 | struct mutex lock; |
96 | struct list_head list; |
97 | int id; |
98 | }; |
99 | |
100 | #define rcar_thermal_for_each_priv(pos, common) \ |
101 | list_for_each_entry(pos, &common->head, list) |
102 | |
103 | #define MCELSIUS(temp) ((temp) * 1000) |
104 | #define rcar_priv_to_dev(priv) ((priv)->common->dev) |
105 | #define rcar_has_irq_support(priv) ((priv)->common->base) |
106 | #define rcar_id_to_shift(priv) ((priv)->id * 8) |
107 | |
108 | static const struct of_device_id rcar_thermal_dt_ids[] = { |
109 | { |
110 | .compatible = "renesas,rcar-thermal" , |
111 | .data = &rcar_thermal, |
112 | }, |
113 | { |
114 | .compatible = "renesas,rcar-gen2-thermal" , |
115 | .data = &rcar_gen2_thermal, |
116 | }, |
117 | { |
118 | .compatible = "renesas,thermal-r8a774c0" , |
119 | .data = &rcar_gen3_thermal, |
120 | }, |
121 | { |
122 | .compatible = "renesas,thermal-r8a77970" , |
123 | .data = &rcar_gen3_thermal, |
124 | }, |
125 | { |
126 | .compatible = "renesas,thermal-r8a77990" , |
127 | .data = &rcar_gen3_thermal, |
128 | }, |
129 | { |
130 | .compatible = "renesas,thermal-r8a77995" , |
131 | .data = &rcar_gen3_thermal, |
132 | }, |
133 | {}, |
134 | }; |
135 | MODULE_DEVICE_TABLE(of, rcar_thermal_dt_ids); |
136 | |
137 | /* |
138 | * basic functions |
139 | */ |
140 | #define rcar_thermal_common_read(c, r) \ |
141 | _rcar_thermal_common_read(c, COMMON_ ##r) |
142 | static u32 _rcar_thermal_common_read(struct rcar_thermal_common *common, |
143 | u32 reg) |
144 | { |
145 | return ioread32(common->base + reg); |
146 | } |
147 | |
148 | #define rcar_thermal_common_write(c, r, d) \ |
149 | _rcar_thermal_common_write(c, COMMON_ ##r, d) |
150 | static void _rcar_thermal_common_write(struct rcar_thermal_common *common, |
151 | u32 reg, u32 data) |
152 | { |
153 | iowrite32(data, common->base + reg); |
154 | } |
155 | |
156 | #define rcar_thermal_common_bset(c, r, m, d) \ |
157 | _rcar_thermal_common_bset(c, COMMON_ ##r, m, d) |
158 | static void _rcar_thermal_common_bset(struct rcar_thermal_common *common, |
159 | u32 reg, u32 mask, u32 data) |
160 | { |
161 | u32 val; |
162 | |
163 | val = ioread32(common->base + reg); |
164 | val &= ~mask; |
165 | val |= (data & mask); |
166 | iowrite32(val, common->base + reg); |
167 | } |
168 | |
169 | #define rcar_thermal_read(p, r) _rcar_thermal_read(p, REG_ ##r) |
170 | static u32 _rcar_thermal_read(struct rcar_thermal_priv *priv, u32 reg) |
171 | { |
172 | return ioread32(priv->base + reg); |
173 | } |
174 | |
175 | #define rcar_thermal_write(p, r, d) _rcar_thermal_write(p, REG_ ##r, d) |
176 | static void _rcar_thermal_write(struct rcar_thermal_priv *priv, |
177 | u32 reg, u32 data) |
178 | { |
179 | iowrite32(data, priv->base + reg); |
180 | } |
181 | |
182 | #define rcar_thermal_bset(p, r, m, d) _rcar_thermal_bset(p, REG_ ##r, m, d) |
183 | static void _rcar_thermal_bset(struct rcar_thermal_priv *priv, u32 reg, |
184 | u32 mask, u32 data) |
185 | { |
186 | u32 val; |
187 | |
188 | val = ioread32(priv->base + reg); |
189 | val &= ~mask; |
190 | val |= (data & mask); |
191 | iowrite32(val, priv->base + reg); |
192 | } |
193 | |
194 | /* |
195 | * zone device functions |
196 | */ |
197 | static int rcar_thermal_update_temp(struct rcar_thermal_priv *priv) |
198 | { |
199 | struct device *dev = rcar_priv_to_dev(priv); |
200 | int old, new, ctemp = -EINVAL; |
201 | unsigned int i; |
202 | |
203 | mutex_lock(&priv->lock); |
204 | |
205 | /* |
206 | * TSC decides a value of CPTAP automatically, |
207 | * and this is the conditions which validate interrupt. |
208 | */ |
209 | rcar_thermal_bset(priv, THSCR, CPCTL, CPCTL); |
210 | |
211 | old = ~0; |
212 | for (i = 0; i < 128; i++) { |
213 | /* |
214 | * we need to wait 300us after changing comparator offset |
215 | * to get stable temperature. |
216 | * see "Usage Notes" on datasheet |
217 | */ |
218 | usleep_range(min: 300, max: 400); |
219 | |
220 | new = rcar_thermal_read(priv, THSSR) & CTEMP; |
221 | if (new == old) { |
222 | ctemp = new; |
223 | break; |
224 | } |
225 | old = new; |
226 | } |
227 | |
228 | if (ctemp < 0) { |
229 | dev_err(dev, "thermal sensor was broken\n" ); |
230 | goto err_out_unlock; |
231 | } |
232 | |
233 | /* |
234 | * enable IRQ |
235 | */ |
236 | if (rcar_has_irq_support(priv)) { |
237 | if (priv->chip->has_filonoff) |
238 | rcar_thermal_write(priv, FILONOFF, 0); |
239 | |
240 | /* enable Rising/Falling edge interrupt */ |
241 | rcar_thermal_write(priv, POSNEG, 0x1); |
242 | rcar_thermal_write(priv, INTCTRL, (((ctemp - 0) << 8) | |
243 | ((ctemp - 1) << 0))); |
244 | } |
245 | |
246 | err_out_unlock: |
247 | mutex_unlock(lock: &priv->lock); |
248 | |
249 | return ctemp; |
250 | } |
251 | |
252 | static int rcar_thermal_get_current_temp(struct rcar_thermal_priv *priv, |
253 | int *temp) |
254 | { |
255 | int ctemp; |
256 | |
257 | ctemp = rcar_thermal_update_temp(priv); |
258 | if (ctemp < 0) |
259 | return ctemp; |
260 | |
261 | /* Guaranteed operating range is -45C to 125C. */ |
262 | |
263 | if (priv->chip->ctemp_bands == 1) |
264 | *temp = MCELSIUS((ctemp * 5) - 65); |
265 | else if (ctemp < 24) |
266 | *temp = MCELSIUS(((ctemp * 55) - 720) / 10); |
267 | else |
268 | *temp = MCELSIUS((ctemp * 5) - 60); |
269 | |
270 | return 0; |
271 | } |
272 | |
273 | static int rcar_thermal_get_temp(struct thermal_zone_device *zone, int *temp) |
274 | { |
275 | struct rcar_thermal_priv *priv = thermal_zone_device_priv(tzd: zone); |
276 | |
277 | return rcar_thermal_get_current_temp(priv, temp); |
278 | } |
279 | |
280 | static struct thermal_zone_device_ops rcar_thermal_zone_ops = { |
281 | .get_temp = rcar_thermal_get_temp, |
282 | }; |
283 | |
284 | static struct thermal_trip trips[] = { |
285 | { .type = THERMAL_TRIP_CRITICAL, .temperature = 90000 } |
286 | }; |
287 | |
288 | /* |
289 | * interrupt |
290 | */ |
291 | #define rcar_thermal_irq_enable(p) _rcar_thermal_irq_ctrl(p, 1) |
292 | #define rcar_thermal_irq_disable(p) _rcar_thermal_irq_ctrl(p, 0) |
293 | static void _rcar_thermal_irq_ctrl(struct rcar_thermal_priv *priv, int enable) |
294 | { |
295 | struct rcar_thermal_common *common = priv->common; |
296 | unsigned long flags; |
297 | u32 mask = 0x3 << rcar_id_to_shift(priv); /* enable Rising/Falling */ |
298 | |
299 | if (!rcar_has_irq_support(priv)) |
300 | return; |
301 | |
302 | spin_lock_irqsave(&common->lock, flags); |
303 | |
304 | rcar_thermal_common_bset(common, INTMSK, mask, enable ? 0 : mask); |
305 | |
306 | spin_unlock_irqrestore(lock: &common->lock, flags); |
307 | } |
308 | |
309 | static void rcar_thermal_work(struct work_struct *work) |
310 | { |
311 | struct rcar_thermal_priv *priv; |
312 | int ret; |
313 | |
314 | priv = container_of(work, struct rcar_thermal_priv, work.work); |
315 | |
316 | ret = rcar_thermal_update_temp(priv); |
317 | if (ret < 0) |
318 | return; |
319 | |
320 | rcar_thermal_irq_enable(priv); |
321 | |
322 | thermal_zone_device_update(priv->zone, THERMAL_EVENT_UNSPECIFIED); |
323 | } |
324 | |
325 | static u32 rcar_thermal_had_changed(struct rcar_thermal_priv *priv, u32 status) |
326 | { |
327 | struct device *dev = rcar_priv_to_dev(priv); |
328 | |
329 | status = (status >> rcar_id_to_shift(priv)) & 0x3; |
330 | |
331 | if (status) { |
332 | dev_dbg(dev, "thermal%d %s%s\n" , |
333 | priv->id, |
334 | (status & 0x2) ? "Rising " : "" , |
335 | (status & 0x1) ? "Falling" : "" ); |
336 | } |
337 | |
338 | return status; |
339 | } |
340 | |
341 | static irqreturn_t rcar_thermal_irq(int irq, void *data) |
342 | { |
343 | struct rcar_thermal_common *common = data; |
344 | struct rcar_thermal_priv *priv; |
345 | u32 status, mask; |
346 | |
347 | spin_lock(lock: &common->lock); |
348 | |
349 | mask = rcar_thermal_common_read(common, INTMSK); |
350 | status = rcar_thermal_common_read(common, STR); |
351 | rcar_thermal_common_write(common, STR, 0x000F0F0F & mask); |
352 | |
353 | spin_unlock(lock: &common->lock); |
354 | |
355 | status = status & ~mask; |
356 | |
357 | /* |
358 | * check the status |
359 | */ |
360 | rcar_thermal_for_each_priv(priv, common) { |
361 | if (rcar_thermal_had_changed(priv, status)) { |
362 | rcar_thermal_irq_disable(priv); |
363 | queue_delayed_work(wq: system_freezable_wq, dwork: &priv->work, |
364 | delay: msecs_to_jiffies(m: 300)); |
365 | } |
366 | } |
367 | |
368 | return IRQ_HANDLED; |
369 | } |
370 | |
371 | /* |
372 | * platform functions |
373 | */ |
374 | static void rcar_thermal_remove(struct platform_device *pdev) |
375 | { |
376 | struct rcar_thermal_common *common = platform_get_drvdata(pdev); |
377 | struct device *dev = &pdev->dev; |
378 | struct rcar_thermal_priv *priv; |
379 | |
380 | rcar_thermal_for_each_priv(priv, common) { |
381 | rcar_thermal_irq_disable(priv); |
382 | cancel_delayed_work_sync(dwork: &priv->work); |
383 | if (priv->chip->use_of_thermal) |
384 | thermal_remove_hwmon_sysfs(tz: priv->zone); |
385 | else |
386 | thermal_zone_device_unregister(tz: priv->zone); |
387 | } |
388 | |
389 | pm_runtime_put(dev); |
390 | pm_runtime_disable(dev); |
391 | } |
392 | |
393 | static int rcar_thermal_probe(struct platform_device *pdev) |
394 | { |
395 | struct rcar_thermal_common *common; |
396 | struct rcar_thermal_priv *priv; |
397 | struct device *dev = &pdev->dev; |
398 | struct resource *res; |
399 | const struct rcar_thermal_chip *chip = of_device_get_match_data(dev); |
400 | int mres = 0; |
401 | int i; |
402 | int ret = -ENODEV; |
403 | int idle = IDLE_INTERVAL; |
404 | u32 enr_bits = 0; |
405 | |
406 | common = devm_kzalloc(dev, size: sizeof(*common), GFP_KERNEL); |
407 | if (!common) |
408 | return -ENOMEM; |
409 | |
410 | platform_set_drvdata(pdev, data: common); |
411 | |
412 | INIT_LIST_HEAD(list: &common->head); |
413 | spin_lock_init(&common->lock); |
414 | common->dev = dev; |
415 | |
416 | pm_runtime_enable(dev); |
417 | pm_runtime_get_sync(dev); |
418 | |
419 | for (i = 0; i < chip->nirqs; i++) { |
420 | int irq; |
421 | |
422 | ret = platform_get_irq_optional(pdev, i); |
423 | if (ret < 0 && ret != -ENXIO) |
424 | goto error_unregister; |
425 | if (ret > 0) |
426 | irq = ret; |
427 | else |
428 | break; |
429 | |
430 | if (!common->base) { |
431 | /* |
432 | * platform has IRQ support. |
433 | * Then, driver uses common registers |
434 | * rcar_has_irq_support() will be enabled |
435 | */ |
436 | res = platform_get_resource(pdev, IORESOURCE_MEM, |
437 | mres++); |
438 | common->base = devm_ioremap_resource(dev, res); |
439 | if (IS_ERR(ptr: common->base)) { |
440 | ret = PTR_ERR(ptr: common->base); |
441 | goto error_unregister; |
442 | } |
443 | |
444 | idle = 0; /* polling delay is not needed */ |
445 | } |
446 | |
447 | ret = devm_request_irq(dev, irq, handler: rcar_thermal_irq, |
448 | IRQF_SHARED, devname: dev_name(dev), dev_id: common); |
449 | if (ret) { |
450 | dev_err(dev, "irq request failed\n " ); |
451 | goto error_unregister; |
452 | } |
453 | |
454 | /* update ENR bits */ |
455 | if (chip->irq_per_ch) |
456 | enr_bits |= 1 << i; |
457 | } |
458 | |
459 | for (i = 0;; i++) { |
460 | res = platform_get_resource(pdev, IORESOURCE_MEM, mres++); |
461 | if (!res) |
462 | break; |
463 | |
464 | priv = devm_kzalloc(dev, size: sizeof(*priv), GFP_KERNEL); |
465 | if (!priv) { |
466 | ret = -ENOMEM; |
467 | goto error_unregister; |
468 | } |
469 | |
470 | priv->base = devm_ioremap_resource(dev, res); |
471 | if (IS_ERR(ptr: priv->base)) { |
472 | ret = PTR_ERR(ptr: priv->base); |
473 | goto error_unregister; |
474 | } |
475 | |
476 | priv->common = common; |
477 | priv->id = i; |
478 | priv->chip = chip; |
479 | mutex_init(&priv->lock); |
480 | INIT_LIST_HEAD(list: &priv->list); |
481 | INIT_DELAYED_WORK(&priv->work, rcar_thermal_work); |
482 | ret = rcar_thermal_update_temp(priv); |
483 | if (ret < 0) |
484 | goto error_unregister; |
485 | |
486 | if (chip->use_of_thermal) { |
487 | priv->zone = devm_thermal_of_zone_register( |
488 | dev, id: i, data: priv, |
489 | ops: &rcar_thermal_zone_ops); |
490 | } else { |
491 | priv->zone = thermal_zone_device_register_with_trips( |
492 | type: "rcar_thermal" , trips, ARRAY_SIZE(trips), mask: 0, devdata: priv, |
493 | ops: &rcar_thermal_zone_ops, NULL, passive_delay: 0, |
494 | polling_delay: idle); |
495 | |
496 | ret = thermal_zone_device_enable(tz: priv->zone); |
497 | if (ret) { |
498 | thermal_zone_device_unregister(tz: priv->zone); |
499 | priv->zone = ERR_PTR(error: ret); |
500 | } |
501 | } |
502 | if (IS_ERR(ptr: priv->zone)) { |
503 | dev_err(dev, "can't register thermal zone\n" ); |
504 | ret = PTR_ERR(ptr: priv->zone); |
505 | priv->zone = NULL; |
506 | goto error_unregister; |
507 | } |
508 | |
509 | if (chip->use_of_thermal) { |
510 | ret = thermal_add_hwmon_sysfs(tz: priv->zone); |
511 | if (ret) |
512 | goto error_unregister; |
513 | } |
514 | |
515 | rcar_thermal_irq_enable(priv); |
516 | |
517 | list_move_tail(list: &priv->list, head: &common->head); |
518 | |
519 | /* update ENR bits */ |
520 | if (!chip->irq_per_ch) |
521 | enr_bits |= 3 << (i * 8); |
522 | } |
523 | |
524 | if (common->base && enr_bits) |
525 | rcar_thermal_common_write(common, ENR, enr_bits); |
526 | |
527 | dev_info(dev, "%d sensor probed\n" , i); |
528 | |
529 | return 0; |
530 | |
531 | error_unregister: |
532 | rcar_thermal_remove(pdev); |
533 | |
534 | return ret; |
535 | } |
536 | |
537 | #ifdef CONFIG_PM_SLEEP |
538 | static int rcar_thermal_suspend(struct device *dev) |
539 | { |
540 | struct rcar_thermal_common *common = dev_get_drvdata(dev); |
541 | struct rcar_thermal_priv *priv = list_first_entry(&common->head, |
542 | typeof(*priv), list); |
543 | |
544 | if (priv->chip->needs_suspend_resume) { |
545 | rcar_thermal_common_write(common, ENR, 0); |
546 | rcar_thermal_irq_disable(priv); |
547 | rcar_thermal_bset(priv, THSCR, CPCTL, 0); |
548 | } |
549 | |
550 | return 0; |
551 | } |
552 | |
553 | static int rcar_thermal_resume(struct device *dev) |
554 | { |
555 | struct rcar_thermal_common *common = dev_get_drvdata(dev); |
556 | struct rcar_thermal_priv *priv = list_first_entry(&common->head, |
557 | typeof(*priv), list); |
558 | int ret; |
559 | |
560 | if (priv->chip->needs_suspend_resume) { |
561 | ret = rcar_thermal_update_temp(priv); |
562 | if (ret < 0) |
563 | return ret; |
564 | rcar_thermal_irq_enable(priv); |
565 | rcar_thermal_common_write(common, ENR, 0x03); |
566 | } |
567 | |
568 | return 0; |
569 | } |
570 | #endif |
571 | |
572 | static SIMPLE_DEV_PM_OPS(rcar_thermal_pm_ops, rcar_thermal_suspend, |
573 | rcar_thermal_resume); |
574 | |
575 | static struct platform_driver rcar_thermal_driver = { |
576 | .driver = { |
577 | .name = "rcar_thermal" , |
578 | .pm = &rcar_thermal_pm_ops, |
579 | .of_match_table = rcar_thermal_dt_ids, |
580 | }, |
581 | .probe = rcar_thermal_probe, |
582 | .remove_new = rcar_thermal_remove, |
583 | }; |
584 | module_platform_driver(rcar_thermal_driver); |
585 | |
586 | MODULE_LICENSE("GPL v2" ); |
587 | MODULE_DESCRIPTION("R-Car THS/TSC thermal sensor driver" ); |
588 | MODULE_AUTHOR("Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>" ); |
589 | |