diff options
-rw-r--r-- | drivers/thermal/rcar_thermal.c | 159 |
1 files changed, 150 insertions, 9 deletions
diff --git a/drivers/thermal/rcar_thermal.c b/drivers/thermal/rcar_thermal.c index cf6aa98956b9..80aae3cf65eb 100644 --- a/drivers/thermal/rcar_thermal.c +++ b/drivers/thermal/rcar_thermal.c | |||
@@ -19,6 +19,8 @@ | |||
19 | */ | 19 | */ |
20 | #include <linux/delay.h> | 20 | #include <linux/delay.h> |
21 | #include <linux/err.h> | 21 | #include <linux/err.h> |
22 | #include <linux/irq.h> | ||
23 | #include <linux/interrupt.h> | ||
22 | #include <linux/io.h> | 24 | #include <linux/io.h> |
23 | #include <linux/module.h> | 25 | #include <linux/module.h> |
24 | #include <linux/platform_device.h> | 26 | #include <linux/platform_device.h> |
@@ -29,8 +31,15 @@ | |||
29 | 31 | ||
30 | #define IDLE_INTERVAL 5000 | 32 | #define IDLE_INTERVAL 5000 |
31 | 33 | ||
34 | #define COMMON_STR 0x00 | ||
35 | #define COMMON_ENR 0x04 | ||
36 | #define COMMON_INTMSK 0x0c | ||
37 | |||
38 | #define REG_POSNEG 0x20 | ||
39 | #define REG_FILONOFF 0x28 | ||
32 | #define REG_THSCR 0x2c | 40 | #define REG_THSCR 0x2c |
33 | #define REG_THSSR 0x30 | 41 | #define REG_THSSR 0x30 |
42 | #define REG_INTCTRL 0x34 | ||
34 | 43 | ||
35 | /* THSCR */ | 44 | /* THSCR */ |
36 | #define CPCTL (1 << 12) | 45 | #define CPCTL (1 << 12) |
@@ -42,14 +51,18 @@ struct rcar_thermal_common { | |||
42 | void __iomem *base; | 51 | void __iomem *base; |
43 | struct device *dev; | 52 | struct device *dev; |
44 | struct list_head head; | 53 | struct list_head head; |
54 | spinlock_t lock; | ||
45 | }; | 55 | }; |
46 | 56 | ||
47 | struct rcar_thermal_priv { | 57 | struct rcar_thermal_priv { |
48 | void __iomem *base; | 58 | void __iomem *base; |
49 | struct rcar_thermal_common *common; | 59 | struct rcar_thermal_common *common; |
50 | struct thermal_zone_device *zone; | 60 | struct thermal_zone_device *zone; |
61 | struct delayed_work work; | ||
51 | struct mutex lock; | 62 | struct mutex lock; |
52 | struct list_head list; | 63 | struct list_head list; |
64 | int id; | ||
65 | int ctemp; | ||
53 | }; | 66 | }; |
54 | 67 | ||
55 | #define rcar_thermal_for_each_priv(pos, common) \ | 68 | #define rcar_thermal_for_each_priv(pos, common) \ |
@@ -59,11 +72,17 @@ struct rcar_thermal_priv { | |||
59 | #define rcar_zone_to_priv(zone) ((zone)->devdata) | 72 | #define rcar_zone_to_priv(zone) ((zone)->devdata) |
60 | #define rcar_priv_to_dev(priv) ((priv)->common->dev) | 73 | #define rcar_priv_to_dev(priv) ((priv)->common->dev) |
61 | #define rcar_has_irq_support(priv) ((priv)->common->base) | 74 | #define rcar_has_irq_support(priv) ((priv)->common->base) |
75 | #define rcar_id_to_shift(priv) ((priv)->id * 8) | ||
76 | |||
77 | #ifdef DEBUG | ||
78 | # define rcar_force_update_temp(priv) 1 | ||
79 | #else | ||
80 | # define rcar_force_update_temp(priv) 0 | ||
81 | #endif | ||
62 | 82 | ||
63 | /* | 83 | /* |
64 | * basic functions | 84 | * basic functions |
65 | */ | 85 | */ |
66 | #if 0 | ||
67 | #define rcar_thermal_common_read(c, r) \ | 86 | #define rcar_thermal_common_read(c, r) \ |
68 | _rcar_thermal_common_read(c, COMMON_ ##r) | 87 | _rcar_thermal_common_read(c, COMMON_ ##r) |
69 | static u32 _rcar_thermal_common_read(struct rcar_thermal_common *common, | 88 | static u32 _rcar_thermal_common_read(struct rcar_thermal_common *common, |
@@ -92,7 +111,6 @@ static void _rcar_thermal_common_bset(struct rcar_thermal_common *common, | |||
92 | val |= (data & mask); | 111 | val |= (data & mask); |
93 | iowrite32(val, common->base + reg); | 112 | iowrite32(val, common->base + reg); |
94 | } | 113 | } |
95 | #endif | ||
96 | 114 | ||
97 | #define rcar_thermal_read(p, r) _rcar_thermal_read(p, REG_ ##r) | 115 | #define rcar_thermal_read(p, r) _rcar_thermal_read(p, REG_ ##r) |
98 | static u32 _rcar_thermal_read(struct rcar_thermal_priv *priv, u32 reg) | 116 | static u32 _rcar_thermal_read(struct rcar_thermal_priv *priv, u32 reg) |
@@ -100,14 +118,12 @@ static u32 _rcar_thermal_read(struct rcar_thermal_priv *priv, u32 reg) | |||
100 | return ioread32(priv->base + reg); | 118 | return ioread32(priv->base + reg); |
101 | } | 119 | } |
102 | 120 | ||
103 | #if 0 /* no user at this point */ | ||
104 | #define rcar_thermal_write(p, r, d) _rcar_thermal_write(p, REG_ ##r, d) | 121 | #define rcar_thermal_write(p, r, d) _rcar_thermal_write(p, REG_ ##r, d) |
105 | static void _rcar_thermal_write(struct rcar_thermal_priv *priv, | 122 | static void _rcar_thermal_write(struct rcar_thermal_priv *priv, |
106 | u32 reg, u32 data) | 123 | u32 reg, u32 data) |
107 | { | 124 | { |
108 | iowrite32(data, priv->base + reg); | 125 | iowrite32(data, priv->base + reg); |
109 | } | 126 | } |
110 | #endif | ||
111 | 127 | ||
112 | #define rcar_thermal_bset(p, r, m, d) _rcar_thermal_bset(p, REG_ ##r, m, d) | 128 | #define rcar_thermal_bset(p, r, m, d) _rcar_thermal_bset(p, REG_ ##r, m, d) |
113 | static void _rcar_thermal_bset(struct rcar_thermal_priv *priv, u32 reg, | 129 | static void _rcar_thermal_bset(struct rcar_thermal_priv *priv, u32 reg, |
@@ -124,10 +140,8 @@ static void _rcar_thermal_bset(struct rcar_thermal_priv *priv, u32 reg, | |||
124 | /* | 140 | /* |
125 | * zone device functions | 141 | * zone device functions |
126 | */ | 142 | */ |
127 | static int rcar_thermal_get_temp(struct thermal_zone_device *zone, | 143 | static int rcar_thermal_update_temp(struct rcar_thermal_priv *priv) |
128 | unsigned long *temp) | ||
129 | { | 144 | { |
130 | struct rcar_thermal_priv *priv = rcar_zone_to_priv(zone); | ||
131 | struct device *dev = rcar_priv_to_dev(priv); | 145 | struct device *dev = rcar_priv_to_dev(priv); |
132 | int i; | 146 | int i; |
133 | int ctemp, old, new; | 147 | int ctemp, old, new; |
@@ -163,13 +177,42 @@ static int rcar_thermal_get_temp(struct thermal_zone_device *zone, | |||
163 | return -EINVAL; | 177 | return -EINVAL; |
164 | } | 178 | } |
165 | 179 | ||
166 | *temp = MCELSIUS((ctemp * 5) - 65); | 180 | /* |
181 | * enable IRQ | ||
182 | */ | ||
183 | if (rcar_has_irq_support(priv)) { | ||
184 | rcar_thermal_write(priv, FILONOFF, 0); | ||
185 | |||
186 | /* enable Rising/Falling edge interrupt */ | ||
187 | rcar_thermal_write(priv, POSNEG, 0x1); | ||
188 | rcar_thermal_write(priv, INTCTRL, (((ctemp - 0) << 8) | | ||
189 | ((ctemp - 1) << 0))); | ||
190 | } | ||
191 | |||
192 | dev_dbg(dev, "thermal%d %d -> %d\n", priv->id, priv->ctemp, ctemp); | ||
193 | |||
194 | priv->ctemp = ctemp; | ||
167 | 195 | ||
168 | mutex_unlock(&priv->lock); | 196 | mutex_unlock(&priv->lock); |
169 | 197 | ||
170 | return 0; | 198 | return 0; |
171 | } | 199 | } |
172 | 200 | ||
201 | static int rcar_thermal_get_temp(struct thermal_zone_device *zone, | ||
202 | unsigned long *temp) | ||
203 | { | ||
204 | struct rcar_thermal_priv *priv = rcar_zone_to_priv(zone); | ||
205 | |||
206 | if (!rcar_has_irq_support(priv) || rcar_force_update_temp(priv)) | ||
207 | rcar_thermal_update_temp(priv); | ||
208 | |||
209 | mutex_lock(&priv->lock); | ||
210 | *temp = MCELSIUS((priv->ctemp * 5) - 65); | ||
211 | mutex_unlock(&priv->lock); | ||
212 | |||
213 | return 0; | ||
214 | } | ||
215 | |||
173 | static int rcar_thermal_get_trip_type(struct thermal_zone_device *zone, | 216 | static int rcar_thermal_get_trip_type(struct thermal_zone_device *zone, |
174 | int trip, enum thermal_trip_type *type) | 217 | int trip, enum thermal_trip_type *type) |
175 | { | 218 | { |
@@ -235,6 +278,82 @@ static struct thermal_zone_device_ops rcar_thermal_zone_ops = { | |||
235 | }; | 278 | }; |
236 | 279 | ||
237 | /* | 280 | /* |
281 | * interrupt | ||
282 | */ | ||
283 | #define rcar_thermal_irq_enable(p) _rcar_thermal_irq_ctrl(p, 1) | ||
284 | #define rcar_thermal_irq_disable(p) _rcar_thermal_irq_ctrl(p, 0) | ||
285 | static void _rcar_thermal_irq_ctrl(struct rcar_thermal_priv *priv, int enable) | ||
286 | { | ||
287 | struct rcar_thermal_common *common = priv->common; | ||
288 | unsigned long flags; | ||
289 | u32 mask = 0x3 << rcar_id_to_shift(priv); /* enable Rising/Falling */ | ||
290 | |||
291 | spin_lock_irqsave(&common->lock, flags); | ||
292 | |||
293 | rcar_thermal_common_bset(common, INTMSK, mask, enable ? 0 : mask); | ||
294 | |||
295 | spin_unlock_irqrestore(&common->lock, flags); | ||
296 | } | ||
297 | |||
298 | static void rcar_thermal_work(struct work_struct *work) | ||
299 | { | ||
300 | struct rcar_thermal_priv *priv; | ||
301 | |||
302 | priv = container_of(work, struct rcar_thermal_priv, work.work); | ||
303 | |||
304 | rcar_thermal_update_temp(priv); | ||
305 | rcar_thermal_irq_enable(priv); | ||
306 | thermal_zone_device_update(priv->zone); | ||
307 | } | ||
308 | |||
309 | static u32 rcar_thermal_had_changed(struct rcar_thermal_priv *priv, u32 status) | ||
310 | { | ||
311 | struct device *dev = rcar_priv_to_dev(priv); | ||
312 | |||
313 | status = (status >> rcar_id_to_shift(priv)) & 0x3; | ||
314 | |||
315 | if (status & 0x3) { | ||
316 | dev_dbg(dev, "thermal%d %s%s\n", | ||
317 | priv->id, | ||
318 | (status & 0x2) ? "Rising " : "", | ||
319 | (status & 0x1) ? "Falling" : ""); | ||
320 | } | ||
321 | |||
322 | return status; | ||
323 | } | ||
324 | |||
325 | static irqreturn_t rcar_thermal_irq(int irq, void *data) | ||
326 | { | ||
327 | struct rcar_thermal_common *common = data; | ||
328 | struct rcar_thermal_priv *priv; | ||
329 | unsigned long flags; | ||
330 | u32 status, mask; | ||
331 | |||
332 | spin_lock_irqsave(&common->lock, flags); | ||
333 | |||
334 | mask = rcar_thermal_common_read(common, INTMSK); | ||
335 | status = rcar_thermal_common_read(common, STR); | ||
336 | rcar_thermal_common_write(common, STR, 0x000F0F0F & mask); | ||
337 | |||
338 | spin_unlock_irqrestore(&common->lock, flags); | ||
339 | |||
340 | status = status & ~mask; | ||
341 | |||
342 | /* | ||
343 | * check the status | ||
344 | */ | ||
345 | rcar_thermal_for_each_priv(priv, common) { | ||
346 | if (rcar_thermal_had_changed(priv, status)) { | ||
347 | rcar_thermal_irq_disable(priv); | ||
348 | schedule_delayed_work(&priv->work, | ||
349 | msecs_to_jiffies(300)); | ||
350 | } | ||
351 | } | ||
352 | |||
353 | return IRQ_HANDLED; | ||
354 | } | ||
355 | |||
356 | /* | ||
238 | * platform functions | 357 | * platform functions |
239 | */ | 358 | */ |
240 | static int rcar_thermal_probe(struct platform_device *pdev) | 359 | static int rcar_thermal_probe(struct platform_device *pdev) |
@@ -245,6 +364,7 @@ static int rcar_thermal_probe(struct platform_device *pdev) | |||
245 | struct resource *res, *irq; | 364 | struct resource *res, *irq; |
246 | int mres = 0; | 365 | int mres = 0; |
247 | int i; | 366 | int i; |
367 | int idle = IDLE_INTERVAL; | ||
248 | 368 | ||
249 | common = devm_kzalloc(dev, sizeof(*common), GFP_KERNEL); | 369 | common = devm_kzalloc(dev, sizeof(*common), GFP_KERNEL); |
250 | if (!common) { | 370 | if (!common) { |
@@ -253,10 +373,13 @@ static int rcar_thermal_probe(struct platform_device *pdev) | |||
253 | } | 373 | } |
254 | 374 | ||
255 | INIT_LIST_HEAD(&common->head); | 375 | INIT_LIST_HEAD(&common->head); |
376 | spin_lock_init(&common->lock); | ||
256 | common->dev = dev; | 377 | common->dev = dev; |
257 | 378 | ||
258 | irq = platform_get_resource(pdev, IORESOURCE_IRQ, 0); | 379 | irq = platform_get_resource(pdev, IORESOURCE_IRQ, 0); |
259 | if (irq) { | 380 | if (irq) { |
381 | int ret; | ||
382 | |||
260 | /* | 383 | /* |
261 | * platform has IRQ support. | 384 | * platform has IRQ support. |
262 | * Then, drier use common register | 385 | * Then, drier use common register |
@@ -267,6 +390,13 @@ static int rcar_thermal_probe(struct platform_device *pdev) | |||
267 | return -ENODEV; | 390 | return -ENODEV; |
268 | } | 391 | } |
269 | 392 | ||
393 | ret = devm_request_irq(dev, irq->start, rcar_thermal_irq, 0, | ||
394 | dev_name(dev), common); | ||
395 | if (ret) { | ||
396 | dev_err(dev, "irq request failed\n "); | ||
397 | return ret; | ||
398 | } | ||
399 | |||
270 | /* | 400 | /* |
271 | * rcar_has_irq_support() will be enabled | 401 | * rcar_has_irq_support() will be enabled |
272 | */ | 402 | */ |
@@ -275,6 +405,11 @@ static int rcar_thermal_probe(struct platform_device *pdev) | |||
275 | dev_err(dev, "Unable to ioremap thermal register\n"); | 405 | dev_err(dev, "Unable to ioremap thermal register\n"); |
276 | return -ENOMEM; | 406 | return -ENOMEM; |
277 | } | 407 | } |
408 | |||
409 | /* enable temperature comparation */ | ||
410 | rcar_thermal_common_write(common, ENR, 0x00030303); | ||
411 | |||
412 | idle = 0; /* polling delaye is not needed */ | ||
278 | } | 413 | } |
279 | 414 | ||
280 | for (i = 0;; i++) { | 415 | for (i = 0;; i++) { |
@@ -295,19 +430,25 @@ static int rcar_thermal_probe(struct platform_device *pdev) | |||
295 | } | 430 | } |
296 | 431 | ||
297 | priv->common = common; | 432 | priv->common = common; |
433 | priv->id = i; | ||
298 | mutex_init(&priv->lock); | 434 | mutex_init(&priv->lock); |
299 | INIT_LIST_HEAD(&priv->list); | 435 | INIT_LIST_HEAD(&priv->list); |
436 | INIT_DELAYED_WORK(&priv->work, rcar_thermal_work); | ||
437 | rcar_thermal_update_temp(priv); | ||
300 | 438 | ||
301 | priv->zone = thermal_zone_device_register("rcar_thermal", | 439 | priv->zone = thermal_zone_device_register("rcar_thermal", |
302 | 1, 0, priv, | 440 | 1, 0, priv, |
303 | &rcar_thermal_zone_ops, NULL, 0, | 441 | &rcar_thermal_zone_ops, NULL, 0, |
304 | IDLE_INTERVAL); | 442 | idle); |
305 | if (IS_ERR(priv->zone)) { | 443 | if (IS_ERR(priv->zone)) { |
306 | dev_err(dev, "can't register thermal zone\n"); | 444 | dev_err(dev, "can't register thermal zone\n"); |
307 | goto error_unregister; | 445 | goto error_unregister; |
308 | } | 446 | } |
309 | 447 | ||
310 | list_move_tail(&priv->list, &common->head); | 448 | list_move_tail(&priv->list, &common->head); |
449 | |||
450 | if (rcar_has_irq_support(priv)) | ||
451 | rcar_thermal_irq_enable(priv); | ||
311 | } | 452 | } |
312 | 453 | ||
313 | platform_set_drvdata(pdev, common); | 454 | platform_set_drvdata(pdev, common); |