diff options
Diffstat (limited to 'drivers/rtc/rtc-at91sam9.c')
-rw-r--r-- | drivers/rtc/rtc-at91sam9.c | 73 |
1 files changed, 61 insertions, 12 deletions
diff --git a/drivers/rtc/rtc-at91sam9.c b/drivers/rtc/rtc-at91sam9.c index 2183fd2750ab..5ccaee32df72 100644 --- a/drivers/rtc/rtc-at91sam9.c +++ b/drivers/rtc/rtc-at91sam9.c | |||
@@ -23,6 +23,7 @@ | |||
23 | #include <linux/io.h> | 23 | #include <linux/io.h> |
24 | #include <linux/mfd/syscon.h> | 24 | #include <linux/mfd/syscon.h> |
25 | #include <linux/regmap.h> | 25 | #include <linux/regmap.h> |
26 | #include <linux/suspend.h> | ||
26 | #include <linux/clk.h> | 27 | #include <linux/clk.h> |
27 | 28 | ||
28 | /* | 29 | /* |
@@ -77,6 +78,9 @@ struct sam9_rtc { | |||
77 | unsigned int gpbr_offset; | 78 | unsigned int gpbr_offset; |
78 | int irq; | 79 | int irq; |
79 | struct clk *sclk; | 80 | struct clk *sclk; |
81 | bool suspended; | ||
82 | unsigned long events; | ||
83 | spinlock_t lock; | ||
80 | }; | 84 | }; |
81 | 85 | ||
82 | #define rtt_readl(rtc, field) \ | 86 | #define rtt_readl(rtc, field) \ |
@@ -271,14 +275,9 @@ static int at91_rtc_proc(struct device *dev, struct seq_file *seq) | |||
271 | return 0; | 275 | return 0; |
272 | } | 276 | } |
273 | 277 | ||
274 | /* | 278 | static irqreturn_t at91_rtc_cache_events(struct sam9_rtc *rtc) |
275 | * IRQ handler for the RTC | ||
276 | */ | ||
277 | static irqreturn_t at91_rtc_interrupt(int irq, void *_rtc) | ||
278 | { | 279 | { |
279 | struct sam9_rtc *rtc = _rtc; | ||
280 | u32 sr, mr; | 280 | u32 sr, mr; |
281 | unsigned long events = 0; | ||
282 | 281 | ||
283 | /* Shared interrupt may be for another device. Note: reading | 282 | /* Shared interrupt may be for another device. Note: reading |
284 | * SR clears it, so we must only read it in this irq handler! | 283 | * SR clears it, so we must only read it in this irq handler! |
@@ -290,18 +289,54 @@ static irqreturn_t at91_rtc_interrupt(int irq, void *_rtc) | |||
290 | 289 | ||
291 | /* alarm status */ | 290 | /* alarm status */ |
292 | if (sr & AT91_RTT_ALMS) | 291 | if (sr & AT91_RTT_ALMS) |
293 | events |= (RTC_AF | RTC_IRQF); | 292 | rtc->events |= (RTC_AF | RTC_IRQF); |
294 | 293 | ||
295 | /* timer update/increment */ | 294 | /* timer update/increment */ |
296 | if (sr & AT91_RTT_RTTINC) | 295 | if (sr & AT91_RTT_RTTINC) |
297 | events |= (RTC_UF | RTC_IRQF); | 296 | rtc->events |= (RTC_UF | RTC_IRQF); |
297 | |||
298 | return IRQ_HANDLED; | ||
299 | } | ||
300 | |||
301 | static void at91_rtc_flush_events(struct sam9_rtc *rtc) | ||
302 | { | ||
303 | if (!rtc->events) | ||
304 | return; | ||
298 | 305 | ||
299 | rtc_update_irq(rtc->rtcdev, 1, events); | 306 | rtc_update_irq(rtc->rtcdev, 1, rtc->events); |
307 | rtc->events = 0; | ||
300 | 308 | ||
301 | pr_debug("%s: num=%ld, events=0x%02lx\n", __func__, | 309 | pr_debug("%s: num=%ld, events=0x%02lx\n", __func__, |
302 | events >> 8, events & 0x000000FF); | 310 | rtc->events >> 8, rtc->events & 0x000000FF); |
311 | } | ||
303 | 312 | ||
304 | return IRQ_HANDLED; | 313 | /* |
314 | * IRQ handler for the RTC | ||
315 | */ | ||
316 | static irqreturn_t at91_rtc_interrupt(int irq, void *_rtc) | ||
317 | { | ||
318 | struct sam9_rtc *rtc = _rtc; | ||
319 | int ret; | ||
320 | |||
321 | spin_lock(&rtc->lock); | ||
322 | |||
323 | ret = at91_rtc_cache_events(rtc); | ||
324 | |||
325 | /* We're called in suspended state */ | ||
326 | if (rtc->suspended) { | ||
327 | /* Mask irqs coming from this peripheral */ | ||
328 | rtt_writel(rtc, MR, | ||
329 | rtt_readl(rtc, MR) & | ||
330 | ~(AT91_RTT_ALMIEN | AT91_RTT_RTTINCIEN)); | ||
331 | /* Trigger a system wakeup */ | ||
332 | pm_system_wakeup(); | ||
333 | } else { | ||
334 | at91_rtc_flush_events(rtc); | ||
335 | } | ||
336 | |||
337 | spin_unlock(&rtc->lock); | ||
338 | |||
339 | return ret; | ||
305 | } | 340 | } |
306 | 341 | ||
307 | static const struct rtc_class_ops at91_rtc_ops = { | 342 | static const struct rtc_class_ops at91_rtc_ops = { |
@@ -421,7 +456,8 @@ static int at91_rtc_probe(struct platform_device *pdev) | |||
421 | 456 | ||
422 | /* register irq handler after we know what name we'll use */ | 457 | /* register irq handler after we know what name we'll use */ |
423 | ret = devm_request_irq(&pdev->dev, rtc->irq, at91_rtc_interrupt, | 458 | ret = devm_request_irq(&pdev->dev, rtc->irq, at91_rtc_interrupt, |
424 | IRQF_SHARED, dev_name(&rtc->rtcdev->dev), rtc); | 459 | IRQF_SHARED | IRQF_COND_SUSPEND, |
460 | dev_name(&rtc->rtcdev->dev), rtc); | ||
425 | if (ret) { | 461 | if (ret) { |
426 | dev_dbg(&pdev->dev, "can't share IRQ %d?\n", rtc->irq); | 462 | dev_dbg(&pdev->dev, "can't share IRQ %d?\n", rtc->irq); |
427 | return ret; | 463 | return ret; |
@@ -482,7 +518,12 @@ static int at91_rtc_suspend(struct device *dev) | |||
482 | rtc->imr = mr & (AT91_RTT_ALMIEN | AT91_RTT_RTTINCIEN); | 518 | rtc->imr = mr & (AT91_RTT_ALMIEN | AT91_RTT_RTTINCIEN); |
483 | if (rtc->imr) { | 519 | if (rtc->imr) { |
484 | if (device_may_wakeup(dev) && (mr & AT91_RTT_ALMIEN)) { | 520 | if (device_may_wakeup(dev) && (mr & AT91_RTT_ALMIEN)) { |
521 | unsigned long flags; | ||
522 | |||
485 | enable_irq_wake(rtc->irq); | 523 | enable_irq_wake(rtc->irq); |
524 | spin_lock_irqsave(&rtc->lock, flags); | ||
525 | rtc->suspended = true; | ||
526 | spin_unlock_irqrestore(&rtc->lock, flags); | ||
486 | /* don't let RTTINC cause wakeups */ | 527 | /* don't let RTTINC cause wakeups */ |
487 | if (mr & AT91_RTT_RTTINCIEN) | 528 | if (mr & AT91_RTT_RTTINCIEN) |
488 | rtt_writel(rtc, MR, mr & ~AT91_RTT_RTTINCIEN); | 529 | rtt_writel(rtc, MR, mr & ~AT91_RTT_RTTINCIEN); |
@@ -499,10 +540,18 @@ static int at91_rtc_resume(struct device *dev) | |||
499 | u32 mr; | 540 | u32 mr; |
500 | 541 | ||
501 | if (rtc->imr) { | 542 | if (rtc->imr) { |
543 | unsigned long flags; | ||
544 | |||
502 | if (device_may_wakeup(dev)) | 545 | if (device_may_wakeup(dev)) |
503 | disable_irq_wake(rtc->irq); | 546 | disable_irq_wake(rtc->irq); |
504 | mr = rtt_readl(rtc, MR); | 547 | mr = rtt_readl(rtc, MR); |
505 | rtt_writel(rtc, MR, mr | rtc->imr); | 548 | rtt_writel(rtc, MR, mr | rtc->imr); |
549 | |||
550 | spin_lock_irqsave(&rtc->lock, flags); | ||
551 | rtc->suspended = false; | ||
552 | at91_rtc_cache_events(rtc); | ||
553 | at91_rtc_flush_events(rtc); | ||
554 | spin_unlock_irqrestore(&rtc->lock, flags); | ||
506 | } | 555 | } |
507 | 556 | ||
508 | return 0; | 557 | return 0; |