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; |
