Commit | Line | Data |
---|---|---|
c9545790 | 1 | // SPDX-License-Identifier: GPL-2.0 |
1e426ffd KM |
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> | |
1e426ffd KM |
7 | */ |
8 | #include <linux/delay.h> | |
9 | #include <linux/err.h> | |
e0a5172e KM |
10 | #include <linux/irq.h> |
11 | #include <linux/interrupt.h> | |
1e426ffd KM |
12 | #include <linux/io.h> |
13 | #include <linux/module.h> | |
8b477ea5 | 14 | #include <linux/of_device.h> |
1e426ffd | 15 | #include <linux/platform_device.h> |
51d45d25 | 16 | #include <linux/pm_runtime.h> |
d2a73e22 | 17 | #include <linux/reboot.h> |
1e426ffd KM |
18 | #include <linux/slab.h> |
19 | #include <linux/spinlock.h> | |
20 | #include <linux/thermal.h> | |
21 | ||
64a411e8 KM |
22 | #include "thermal_hwmon.h" |
23 | ||
d2a73e22 | 24 | #define IDLE_INTERVAL 5000 |
25 | ||
e0a5172e KM |
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 | |
e9137a58 KM |
32 | #define REG_THSCR 0x2c |
33 | #define REG_THSSR 0x30 | |
e0a5172e | 34 | #define REG_INTCTRL 0x34 |
1e426ffd KM |
35 | |
36 | /* THSCR */ | |
f8f53e18 | 37 | #define CPCTL (1 << 12) |
1e426ffd KM |
38 | |
39 | /* THSSR */ | |
40 | #define CTEMP 0x3f | |
41 | ||
3676d1dd KM |
42 | struct rcar_thermal_common { |
43 | void __iomem *base; | |
44 | struct device *dev; | |
45 | struct list_head head; | |
e0a5172e | 46 | spinlock_t lock; |
3676d1dd | 47 | }; |
1e426ffd | 48 | |
1969d9dc YK |
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; | |
20386f0d | 55 | unsigned int ctemp_bands; |
1969d9dc YK |
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, | |
20386f0d | 64 | .ctemp_bands = 1, |
1969d9dc YK |
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, | |
20386f0d | 73 | .ctemp_bands = 1, |
1969d9dc YK |
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, | |
20386f0d | 86 | .ctemp_bands = 2, |
1969d9dc YK |
87 | }; |
88 | ||
1e426ffd KM |
89 | struct rcar_thermal_priv { |
90 | void __iomem *base; | |
3676d1dd KM |
91 | struct rcar_thermal_common *common; |
92 | struct thermal_zone_device *zone; | |
1969d9dc | 93 | const struct rcar_thermal_chip *chip; |
e0a5172e | 94 | struct delayed_work work; |
b2bbc6a2 | 95 | struct mutex lock; |
3676d1dd | 96 | struct list_head list; |
e0a5172e | 97 | int id; |
1e426ffd KM |
98 | }; |
99 | ||
3676d1dd KM |
100 | #define rcar_thermal_for_each_priv(pos, common) \ |
101 | list_for_each_entry(pos, &common->head, list) | |
102 | ||
c499703e | 103 | #define MCELSIUS(temp) ((temp) * 1000) |
9dde8f86 | 104 | #define rcar_zone_to_priv(zone) ((zone)->devdata) |
3676d1dd KM |
105 | #define rcar_priv_to_dev(priv) ((priv)->common->dev) |
106 | #define rcar_has_irq_support(priv) ((priv)->common->base) | |
e0a5172e KM |
107 | #define rcar_id_to_shift(priv) ((priv)->id * 8) |
108 | ||
ca1e4558 | 109 | static const struct of_device_id rcar_thermal_dt_ids[] = { |
1969d9dc YK |
110 | { |
111 | .compatible = "renesas,rcar-thermal", | |
112 | .data = &rcar_thermal, | |
113 | }, | |
114 | { | |
115 | .compatible = "renesas,rcar-gen2-thermal", | |
116 | .data = &rcar_gen2_thermal, | |
117 | }, | |
b8d3d112 FC |
118 | { |
119 | .compatible = "renesas,thermal-r8a774c0", | |
120 | .data = &rcar_gen3_thermal, | |
121 | }, | |
92ca366e SS |
122 | { |
123 | .compatible = "renesas,thermal-r8a77970", | |
124 | .data = &rcar_gen3_thermal, | |
125 | }, | |
e36e1300 YK |
126 | { |
127 | .compatible = "renesas,thermal-r8a77990", | |
128 | .data = &rcar_gen3_thermal, | |
129 | }, | |
1969d9dc YK |
130 | { |
131 | .compatible = "renesas,thermal-r8a77995", | |
132 | .data = &rcar_gen3_thermal, | |
133 | }, | |
ca1e4558 KM |
134 | {}, |
135 | }; | |
136 | MODULE_DEVICE_TABLE(of, rcar_thermal_dt_ids); | |
137 | ||
1e426ffd KM |
138 | /* |
139 | * basic functions | |
140 | */ | |
e9137a58 KM |
141 | #define rcar_thermal_common_read(c, r) \ |
142 | _rcar_thermal_common_read(c, COMMON_ ##r) | |
143 | static u32 _rcar_thermal_common_read(struct rcar_thermal_common *common, | |
144 | u32 reg) | |
145 | { | |
146 | return ioread32(common->base + reg); | |
147 | } | |
148 | ||
149 | #define rcar_thermal_common_write(c, r, d) \ | |
150 | _rcar_thermal_common_write(c, COMMON_ ##r, d) | |
151 | static void _rcar_thermal_common_write(struct rcar_thermal_common *common, | |
152 | u32 reg, u32 data) | |
153 | { | |
154 | iowrite32(data, common->base + reg); | |
155 | } | |
156 | ||
157 | #define rcar_thermal_common_bset(c, r, m, d) \ | |
158 | _rcar_thermal_common_bset(c, COMMON_ ##r, m, d) | |
159 | static void _rcar_thermal_common_bset(struct rcar_thermal_common *common, | |
160 | u32 reg, u32 mask, u32 data) | |
161 | { | |
162 | u32 val; | |
163 | ||
164 | val = ioread32(common->base + reg); | |
165 | val &= ~mask; | |
166 | val |= (data & mask); | |
167 | iowrite32(val, common->base + reg); | |
168 | } | |
e9137a58 KM |
169 | |
170 | #define rcar_thermal_read(p, r) _rcar_thermal_read(p, REG_ ##r) | |
171 | static u32 _rcar_thermal_read(struct rcar_thermal_priv *priv, u32 reg) | |
1e426ffd | 172 | { |
b2bbc6a2 | 173 | return ioread32(priv->base + reg); |
1e426ffd KM |
174 | } |
175 | ||
e9137a58 KM |
176 | #define rcar_thermal_write(p, r, d) _rcar_thermal_write(p, REG_ ##r, d) |
177 | static void _rcar_thermal_write(struct rcar_thermal_priv *priv, | |
178 | u32 reg, u32 data) | |
1e426ffd | 179 | { |
1e426ffd | 180 | iowrite32(data, priv->base + reg); |
1e426ffd | 181 | } |
1e426ffd | 182 | |
e9137a58 KM |
183 | #define rcar_thermal_bset(p, r, m, d) _rcar_thermal_bset(p, REG_ ##r, m, d) |
184 | static void _rcar_thermal_bset(struct rcar_thermal_priv *priv, u32 reg, | |
185 | u32 mask, u32 data) | |
1e426ffd | 186 | { |
1e426ffd KM |
187 | u32 val; |
188 | ||
1e426ffd KM |
189 | val = ioread32(priv->base + reg); |
190 | val &= ~mask; | |
191 | val |= (data & mask); | |
192 | iowrite32(val, priv->base + reg); | |
1e426ffd KM |
193 | } |
194 | ||
195 | /* | |
196 | * zone device functions | |
197 | */ | |
e0a5172e | 198 | static int rcar_thermal_update_temp(struct rcar_thermal_priv *priv) |
1e426ffd | 199 | { |
f8f53e18 | 200 | struct device *dev = rcar_priv_to_dev(priv); |
b03628b7 NS |
201 | int old, new, ctemp = -EINVAL; |
202 | unsigned int i; | |
f8f53e18 | 203 | |
b2bbc6a2 KM |
204 | mutex_lock(&priv->lock); |
205 | ||
f8f53e18 KM |
206 | /* |
207 | * TSC decides a value of CPTAP automatically, | |
208 | * and this is the conditions which validate interrupt. | |
209 | */ | |
210 | rcar_thermal_bset(priv, THSCR, CPCTL, CPCTL); | |
211 | ||
f8f53e18 KM |
212 | old = ~0; |
213 | for (i = 0; i < 128; i++) { | |
1e426ffd KM |
214 | /* |
215 | * we need to wait 300us after changing comparator offset | |
216 | * to get stable temperature. | |
217 | * see "Usage Notes" on datasheet | |
218 | */ | |
263c8c4c | 219 | usleep_range(300, 400); |
1e426ffd | 220 | |
f8f53e18 KM |
221 | new = rcar_thermal_read(priv, THSSR) & CTEMP; |
222 | if (new == old) { | |
223 | ctemp = new; | |
1e426ffd KM |
224 | break; |
225 | } | |
f8f53e18 | 226 | old = new; |
1e426ffd KM |
227 | } |
228 | ||
b03628b7 | 229 | if (ctemp < 0) { |
f8f53e18 | 230 | dev_err(dev, "thermal sensor was broken\n"); |
f0e68fc3 | 231 | goto err_out_unlock; |
f8f53e18 KM |
232 | } |
233 | ||
e0a5172e KM |
234 | /* |
235 | * enable IRQ | |
236 | */ | |
237 | if (rcar_has_irq_support(priv)) { | |
1969d9dc YK |
238 | if (priv->chip->has_filonoff) |
239 | rcar_thermal_write(priv, FILONOFF, 0); | |
e0a5172e KM |
240 | |
241 | /* enable Rising/Falling edge interrupt */ | |
242 | rcar_thermal_write(priv, POSNEG, 0x1); | |
243 | rcar_thermal_write(priv, INTCTRL, (((ctemp - 0) << 8) | | |
244 | ((ctemp - 1) << 0))); | |
245 | } | |
246 | ||
f0e68fc3 | 247 | err_out_unlock: |
b2bbc6a2 | 248 | mutex_unlock(&priv->lock); |
57ed737f | 249 | |
b03628b7 | 250 | return ctemp; |
1e426ffd KM |
251 | } |
252 | ||
8b477ea5 KM |
253 | static int rcar_thermal_get_current_temp(struct rcar_thermal_priv *priv, |
254 | int *temp) | |
e0a5172e | 255 | { |
0fa04202 | 256 | int ctemp; |
e0a5172e | 257 | |
57ed737f NS |
258 | ctemp = rcar_thermal_update_temp(priv); |
259 | if (ctemp < 0) | |
260 | return ctemp; | |
e0a5172e | 261 | |
0fa04202 NS |
262 | /* Guaranteed operating range is -45C to 125C. */ |
263 | ||
20386f0d | 264 | if (priv->chip->ctemp_bands == 1) |
0fa04202 | 265 | *temp = MCELSIUS((ctemp * 5) - 65); |
57ed737f | 266 | else if (ctemp < 24) |
0fa04202 | 267 | *temp = MCELSIUS(((ctemp * 55) - 720) / 10); |
20386f0d | 268 | else |
0fa04202 | 269 | *temp = MCELSIUS((ctemp * 5) - 60); |
5440c40b | 270 | |
e0a5172e KM |
271 | return 0; |
272 | } | |
273 | ||
8b477ea5 KM |
274 | static int rcar_thermal_of_get_temp(void *data, int *temp) |
275 | { | |
276 | struct rcar_thermal_priv *priv = data; | |
277 | ||
278 | return rcar_thermal_get_current_temp(priv, temp); | |
279 | } | |
280 | ||
281 | static int rcar_thermal_get_temp(struct thermal_zone_device *zone, int *temp) | |
282 | { | |
283 | struct rcar_thermal_priv *priv = rcar_zone_to_priv(zone); | |
284 | ||
285 | return rcar_thermal_get_current_temp(priv, temp); | |
286 | } | |
287 | ||
d2a73e22 | 288 | static int rcar_thermal_get_trip_type(struct thermal_zone_device *zone, |
289 | int trip, enum thermal_trip_type *type) | |
290 | { | |
291 | struct rcar_thermal_priv *priv = rcar_zone_to_priv(zone); | |
3676d1dd | 292 | struct device *dev = rcar_priv_to_dev(priv); |
d2a73e22 | 293 | |
294 | /* see rcar_thermal_get_temp() */ | |
295 | switch (trip) { | |
296 | case 0: /* +90 <= temp */ | |
297 | *type = THERMAL_TRIP_CRITICAL; | |
298 | break; | |
299 | default: | |
3676d1dd | 300 | dev_err(dev, "rcar driver trip error\n"); |
d2a73e22 | 301 | return -EINVAL; |
302 | } | |
303 | ||
304 | return 0; | |
305 | } | |
306 | ||
307 | static int rcar_thermal_get_trip_temp(struct thermal_zone_device *zone, | |
17e8351a | 308 | int trip, int *temp) |
d2a73e22 | 309 | { |
310 | struct rcar_thermal_priv *priv = rcar_zone_to_priv(zone); | |
3676d1dd | 311 | struct device *dev = rcar_priv_to_dev(priv); |
d2a73e22 | 312 | |
313 | /* see rcar_thermal_get_temp() */ | |
314 | switch (trip) { | |
315 | case 0: /* +90 <= temp */ | |
316 | *temp = MCELSIUS(90); | |
317 | break; | |
318 | default: | |
3676d1dd | 319 | dev_err(dev, "rcar driver trip error\n"); |
d2a73e22 | 320 | return -EINVAL; |
321 | } | |
322 | ||
323 | return 0; | |
324 | } | |
325 | ||
8b477ea5 KM |
326 | static const struct thermal_zone_of_device_ops rcar_thermal_zone_of_ops = { |
327 | .get_temp = rcar_thermal_of_get_temp, | |
328 | }; | |
329 | ||
1e426ffd | 330 | static struct thermal_zone_device_ops rcar_thermal_zone_ops = { |
d2a73e22 | 331 | .get_temp = rcar_thermal_get_temp, |
332 | .get_trip_type = rcar_thermal_get_trip_type, | |
333 | .get_trip_temp = rcar_thermal_get_trip_temp, | |
1e426ffd KM |
334 | }; |
335 | ||
e0a5172e KM |
336 | /* |
337 | * interrupt | |
338 | */ | |
339 | #define rcar_thermal_irq_enable(p) _rcar_thermal_irq_ctrl(p, 1) | |
340 | #define rcar_thermal_irq_disable(p) _rcar_thermal_irq_ctrl(p, 0) | |
341 | static void _rcar_thermal_irq_ctrl(struct rcar_thermal_priv *priv, int enable) | |
342 | { | |
343 | struct rcar_thermal_common *common = priv->common; | |
344 | unsigned long flags; | |
345 | u32 mask = 0x3 << rcar_id_to_shift(priv); /* enable Rising/Falling */ | |
346 | ||
ffbcdf8a KM |
347 | if (!rcar_has_irq_support(priv)) |
348 | return; | |
349 | ||
e0a5172e KM |
350 | spin_lock_irqsave(&common->lock, flags); |
351 | ||
352 | rcar_thermal_common_bset(common, INTMSK, mask, enable ? 0 : mask); | |
353 | ||
354 | spin_unlock_irqrestore(&common->lock, flags); | |
355 | } | |
356 | ||
357 | static void rcar_thermal_work(struct work_struct *work) | |
358 | { | |
359 | struct rcar_thermal_priv *priv; | |
a1ade565 | 360 | int ret; |
e0a5172e KM |
361 | |
362 | priv = container_of(work, struct rcar_thermal_priv, work.work); | |
363 | ||
a1ade565 KM |
364 | ret = rcar_thermal_update_temp(priv); |
365 | if (ret < 0) | |
366 | return; | |
367 | ||
e0a5172e | 368 | rcar_thermal_irq_enable(priv); |
9477165e | 369 | |
7617e771 | 370 | thermal_zone_device_update(priv->zone, THERMAL_EVENT_UNSPECIFIED); |
e0a5172e KM |
371 | } |
372 | ||
373 | static u32 rcar_thermal_had_changed(struct rcar_thermal_priv *priv, u32 status) | |
374 | { | |
375 | struct device *dev = rcar_priv_to_dev(priv); | |
376 | ||
377 | status = (status >> rcar_id_to_shift(priv)) & 0x3; | |
378 | ||
206c0cba | 379 | if (status) { |
e0a5172e KM |
380 | dev_dbg(dev, "thermal%d %s%s\n", |
381 | priv->id, | |
382 | (status & 0x2) ? "Rising " : "", | |
383 | (status & 0x1) ? "Falling" : ""); | |
384 | } | |
385 | ||
386 | return status; | |
387 | } | |
388 | ||
389 | static irqreturn_t rcar_thermal_irq(int irq, void *data) | |
390 | { | |
391 | struct rcar_thermal_common *common = data; | |
392 | struct rcar_thermal_priv *priv; | |
e0a5172e KM |
393 | u32 status, mask; |
394 | ||
4eb7d0cd | 395 | spin_lock(&common->lock); |
e0a5172e KM |
396 | |
397 | mask = rcar_thermal_common_read(common, INTMSK); | |
398 | status = rcar_thermal_common_read(common, STR); | |
399 | rcar_thermal_common_write(common, STR, 0x000F0F0F & mask); | |
400 | ||
4eb7d0cd | 401 | spin_unlock(&common->lock); |
e0a5172e KM |
402 | |
403 | status = status & ~mask; | |
404 | ||
405 | /* | |
406 | * check the status | |
407 | */ | |
408 | rcar_thermal_for_each_priv(priv, common) { | |
409 | if (rcar_thermal_had_changed(priv, status)) { | |
410 | rcar_thermal_irq_disable(priv); | |
3a313862 GU |
411 | queue_delayed_work(system_freezable_wq, &priv->work, |
412 | msecs_to_jiffies(300)); | |
e0a5172e KM |
413 | } |
414 | } | |
415 | ||
416 | return IRQ_HANDLED; | |
417 | } | |
418 | ||
1e426ffd KM |
419 | /* |
420 | * platform functions | |
421 | */ | |
84f0e490 KM |
422 | static int rcar_thermal_remove(struct platform_device *pdev) |
423 | { | |
424 | struct rcar_thermal_common *common = platform_get_drvdata(pdev); | |
425 | struct device *dev = &pdev->dev; | |
426 | struct rcar_thermal_priv *priv; | |
427 | ||
428 | rcar_thermal_for_each_priv(priv, common) { | |
ffbcdf8a | 429 | rcar_thermal_irq_disable(priv); |
697ee786 | 430 | cancel_delayed_work_sync(&priv->work); |
1969d9dc | 431 | if (priv->chip->use_of_thermal) |
64a411e8 | 432 | thermal_remove_hwmon_sysfs(priv->zone); |
d4b23c5c BDP |
433 | else |
434 | thermal_zone_device_unregister(priv->zone); | |
84f0e490 KM |
435 | } |
436 | ||
437 | pm_runtime_put(dev); | |
438 | pm_runtime_disable(dev); | |
439 | ||
440 | return 0; | |
441 | } | |
442 | ||
1e426ffd KM |
443 | static int rcar_thermal_probe(struct platform_device *pdev) |
444 | { | |
3676d1dd | 445 | struct rcar_thermal_common *common; |
1e426ffd | 446 | struct rcar_thermal_priv *priv; |
3676d1dd KM |
447 | struct device *dev = &pdev->dev; |
448 | struct resource *res, *irq; | |
1969d9dc | 449 | const struct rcar_thermal_chip *chip = of_device_get_match_data(dev); |
3676d1dd KM |
450 | int mres = 0; |
451 | int i; | |
fb84d990 | 452 | int ret = -ENODEV; |
e0a5172e | 453 | int idle = IDLE_INTERVAL; |
11313746 | 454 | u32 enr_bits = 0; |
1e426ffd | 455 | |
3676d1dd | 456 | common = devm_kzalloc(dev, sizeof(*common), GFP_KERNEL); |
b0a60d88 | 457 | if (!common) |
1e426ffd | 458 | return -ENOMEM; |
1e426ffd | 459 | |
84f0e490 KM |
460 | platform_set_drvdata(pdev, common); |
461 | ||
3676d1dd | 462 | INIT_LIST_HEAD(&common->head); |
e0a5172e | 463 | spin_lock_init(&common->lock); |
3676d1dd KM |
464 | common->dev = dev; |
465 | ||
51d45d25 KM |
466 | pm_runtime_enable(dev); |
467 | pm_runtime_get_sync(dev); | |
468 | ||
1969d9dc | 469 | for (i = 0; i < chip->nirqs; i++) { |
df016bbb | 470 | irq = platform_get_resource(pdev, IORESOURCE_IRQ, i); |
1969d9dc YK |
471 | if (!irq) |
472 | continue; | |
473 | if (!common->base) { | |
474 | /* | |
475 | * platform has IRQ support. | |
476 | * Then, driver uses common registers | |
477 | * rcar_has_irq_support() will be enabled | |
478 | */ | |
479 | res = platform_get_resource(pdev, IORESOURCE_MEM, | |
480 | mres++); | |
481 | common->base = devm_ioremap_resource(dev, res); | |
39056e8a NS |
482 | if (IS_ERR(common->base)) { |
483 | ret = PTR_ERR(common->base); | |
484 | goto error_unregister; | |
485 | } | |
1969d9dc YK |
486 | |
487 | idle = 0; /* polling delay is not needed */ | |
488 | } | |
e0a5172e | 489 | |
1969d9dc YK |
490 | ret = devm_request_irq(dev, irq->start, rcar_thermal_irq, |
491 | IRQF_SHARED, dev_name(dev), common); | |
492 | if (ret) { | |
493 | dev_err(dev, "irq request failed\n "); | |
494 | goto error_unregister; | |
495 | } | |
496 | ||
497 | /* update ENR bits */ | |
498 | if (chip->irq_per_ch) | |
499 | enr_bits |= 1 << i; | |
1e426ffd KM |
500 | } |
501 | ||
3676d1dd KM |
502 | for (i = 0;; i++) { |
503 | res = platform_get_resource(pdev, IORESOURCE_MEM, mres++); | |
504 | if (!res) | |
505 | break; | |
506 | ||
507 | priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); | |
508 | if (!priv) { | |
1dc20828 KM |
509 | ret = -ENOMEM; |
510 | goto error_unregister; | |
3676d1dd KM |
511 | } |
512 | ||
5095526f | 513 | priv->base = devm_ioremap_resource(dev, res); |
1dc20828 KM |
514 | if (IS_ERR(priv->base)) { |
515 | ret = PTR_ERR(priv->base); | |
516 | goto error_unregister; | |
517 | } | |
3676d1dd KM |
518 | |
519 | priv->common = common; | |
e0a5172e | 520 | priv->id = i; |
1969d9dc | 521 | priv->chip = chip; |
3676d1dd KM |
522 | mutex_init(&priv->lock); |
523 | INIT_LIST_HEAD(&priv->list); | |
e0a5172e | 524 | INIT_DELAYED_WORK(&priv->work, rcar_thermal_work); |
a1ade565 KM |
525 | ret = rcar_thermal_update_temp(priv); |
526 | if (ret < 0) | |
527 | goto error_unregister; | |
3676d1dd | 528 | |
392573b7 | 529 | if (chip->use_of_thermal) { |
5e325868 | 530 | priv->zone = devm_thermal_zone_of_sensor_register( |
8b477ea5 KM |
531 | dev, i, priv, |
532 | &rcar_thermal_zone_of_ops); | |
392573b7 | 533 | } else { |
8b477ea5 KM |
534 | priv->zone = thermal_zone_device_register( |
535 | "rcar_thermal", | |
3676d1dd KM |
536 | 1, 0, priv, |
537 | &rcar_thermal_zone_ops, NULL, 0, | |
e0a5172e | 538 | idle); |
bbcf90c0 AP |
539 | |
540 | ret = thermal_zone_device_enable(priv->zone); | |
541 | if (ret) { | |
542 | thermal_zone_device_unregister(priv->zone); | |
543 | priv->zone = ERR_PTR(ret); | |
544 | } | |
545 | } | |
3676d1dd KM |
546 | if (IS_ERR(priv->zone)) { |
547 | dev_err(dev, "can't register thermal zone\n"); | |
fb84d990 | 548 | ret = PTR_ERR(priv->zone); |
87260d3f | 549 | priv->zone = NULL; |
3676d1dd KM |
550 | goto error_unregister; |
551 | } | |
552 | ||
1969d9dc | 553 | if (chip->use_of_thermal) { |
64a411e8 KM |
554 | /* |
555 | * thermal_zone doesn't enable hwmon as default, | |
556 | * but, enable it here to keep compatible | |
557 | */ | |
558 | priv->zone->tzp->no_hwmon = false; | |
559 | ret = thermal_add_hwmon_sysfs(priv->zone); | |
560 | if (ret) | |
561 | goto error_unregister; | |
562 | } | |
563 | ||
ffbcdf8a | 564 | rcar_thermal_irq_enable(priv); |
1dc20828 KM |
565 | |
566 | list_move_tail(&priv->list, &common->head); | |
11313746 YS |
567 | |
568 | /* update ENR bits */ | |
1969d9dc YK |
569 | if (!chip->irq_per_ch) |
570 | enr_bits |= 3 << (i * 8); | |
1e426ffd KM |
571 | } |
572 | ||
542cdf40 | 573 | if (common->base && enr_bits) |
11313746 YS |
574 | rcar_thermal_common_write(common, ENR, enr_bits); |
575 | ||
3db46c93 | 576 | dev_info(dev, "%d sensor probed\n", i); |
1e426ffd KM |
577 | |
578 | return 0; | |
3676d1dd KM |
579 | |
580 | error_unregister: | |
84f0e490 | 581 | rcar_thermal_remove(pdev); |
51d45d25 | 582 | |
fb84d990 | 583 | return ret; |
1e426ffd KM |
584 | } |
585 | ||
1969d9dc YK |
586 | #ifdef CONFIG_PM_SLEEP |
587 | static int rcar_thermal_suspend(struct device *dev) | |
588 | { | |
589 | struct rcar_thermal_common *common = dev_get_drvdata(dev); | |
590 | struct rcar_thermal_priv *priv = list_first_entry(&common->head, | |
591 | typeof(*priv), list); | |
592 | ||
593 | if (priv->chip->needs_suspend_resume) { | |
594 | rcar_thermal_common_write(common, ENR, 0); | |
595 | rcar_thermal_irq_disable(priv); | |
596 | rcar_thermal_bset(priv, THSCR, CPCTL, 0); | |
597 | } | |
598 | ||
599 | return 0; | |
600 | } | |
601 | ||
602 | static int rcar_thermal_resume(struct device *dev) | |
603 | { | |
604 | struct rcar_thermal_common *common = dev_get_drvdata(dev); | |
605 | struct rcar_thermal_priv *priv = list_first_entry(&common->head, | |
606 | typeof(*priv), list); | |
607 | int ret; | |
608 | ||
609 | if (priv->chip->needs_suspend_resume) { | |
610 | ret = rcar_thermal_update_temp(priv); | |
611 | if (ret < 0) | |
612 | return ret; | |
613 | rcar_thermal_irq_enable(priv); | |
614 | rcar_thermal_common_write(common, ENR, 0x03); | |
615 | } | |
616 | ||
617 | return 0; | |
618 | } | |
619 | #endif | |
620 | ||
621 | static SIMPLE_DEV_PM_OPS(rcar_thermal_pm_ops, rcar_thermal_suspend, | |
622 | rcar_thermal_resume); | |
623 | ||
1e426ffd KM |
624 | static struct platform_driver rcar_thermal_driver = { |
625 | .driver = { | |
626 | .name = "rcar_thermal", | |
1969d9dc | 627 | .pm = &rcar_thermal_pm_ops, |
76cc1887 | 628 | .of_match_table = rcar_thermal_dt_ids, |
1e426ffd KM |
629 | }, |
630 | .probe = rcar_thermal_probe, | |
631 | .remove = rcar_thermal_remove, | |
632 | }; | |
633 | module_platform_driver(rcar_thermal_driver); | |
634 | ||
c9545790 | 635 | MODULE_LICENSE("GPL v2"); |
1e426ffd KM |
636 | MODULE_DESCRIPTION("R-Car THS/TSC thermal sensor driver"); |
637 | MODULE_AUTHOR("Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>"); |