diff options
| author | Rafael J. Wysocki <rafael.j.wysocki@intel.com> | 2015-03-05 19:29:05 -0500 |
|---|---|---|
| committer | Rafael J. Wysocki <rafael.j.wysocki@intel.com> | 2015-03-05 19:29:05 -0500 |
| commit | 79d223646baa14272dc90044a0e798c552b72eda (patch) | |
| tree | a4f92bc30b14595242d1b60705b93e3f86afc59f | |
| parent | eef16e4362703f213c40175c4adb6f00f6eb9735 (diff) | |
| parent | 7438b633a6b073d66a3fa3678ec0dd5928caa4af (diff) | |
Merge branch 'irq-pm'
* irq-pm:
genirq / PM: describe IRQF_COND_SUSPEND
tty: serial: atmel: rework interrupt and wakeup handling
watchdog: at91sam9: request the irq with IRQF_NO_SUSPEND
clk: at91: implement suspend/resume for the PMC irqchip
rtc: at91rm9200: rework wakeup and interrupt handling
rtc: at91sam9: rework wakeup and interrupt handling
PM / wakeup: export pm_system_wakeup symbol
genirq / PM: Add flag for shared NO_SUSPEND interrupt lines
genirq / PM: better describe IRQF_NO_SUSPEND semantics
| -rw-r--r-- | Documentation/power/suspend-and-interrupts.txt | 22 | ||||
| -rw-r--r-- | drivers/base/power/wakeup.c | 1 | ||||
| -rw-r--r-- | drivers/clk/at91/pmc.c | 20 | ||||
| -rw-r--r-- | drivers/clk/at91/pmc.h | 1 | ||||
| -rw-r--r-- | drivers/rtc/rtc-at91rm9200.c | 62 | ||||
| -rw-r--r-- | drivers/rtc/rtc-at91sam9.c | 73 | ||||
| -rw-r--r-- | drivers/tty/serial/atmel_serial.c | 49 | ||||
| -rw-r--r-- | drivers/watchdog/at91sam9_wdt.c | 3 | ||||
| -rw-r--r-- | include/linux/interrupt.h | 9 | ||||
| -rw-r--r-- | include/linux/irqdesc.h | 1 | ||||
| -rw-r--r-- | kernel/irq/manage.c | 7 | ||||
| -rw-r--r-- | kernel/irq/pm.c | 7 |
12 files changed, 215 insertions, 40 deletions
diff --git a/Documentation/power/suspend-and-interrupts.txt b/Documentation/power/suspend-and-interrupts.txt index 2f9c5a5fcb25..8afb29a8604a 100644 --- a/Documentation/power/suspend-and-interrupts.txt +++ b/Documentation/power/suspend-and-interrupts.txt | |||
| @@ -40,8 +40,10 @@ but also to IPIs and to some other special-purpose interrupts. | |||
| 40 | 40 | ||
| 41 | The IRQF_NO_SUSPEND flag is used to indicate that to the IRQ subsystem when | 41 | The IRQF_NO_SUSPEND flag is used to indicate that to the IRQ subsystem when |
| 42 | requesting a special-purpose interrupt. It causes suspend_device_irqs() to | 42 | requesting a special-purpose interrupt. It causes suspend_device_irqs() to |
| 43 | leave the corresponding IRQ enabled so as to allow the interrupt to work all | 43 | leave the corresponding IRQ enabled so as to allow the interrupt to work as |
| 44 | the time as expected. | 44 | expected during the suspend-resume cycle, but does not guarantee that the |
| 45 | interrupt will wake the system from a suspended state -- for such cases it is | ||
| 46 | necessary to use enable_irq_wake(). | ||
| 45 | 47 | ||
| 46 | Note that the IRQF_NO_SUSPEND flag affects the entire IRQ and not just one | 48 | Note that the IRQF_NO_SUSPEND flag affects the entire IRQ and not just one |
| 47 | user of it. Thus, if the IRQ is shared, all of the interrupt handlers installed | 49 | user of it. Thus, if the IRQ is shared, all of the interrupt handlers installed |
| @@ -110,8 +112,9 @@ any special interrupt handling logic for it to work. | |||
| 110 | IRQF_NO_SUSPEND and enable_irq_wake() | 112 | IRQF_NO_SUSPEND and enable_irq_wake() |
| 111 | ------------------------------------- | 113 | ------------------------------------- |
| 112 | 114 | ||
| 113 | There are no valid reasons to use both enable_irq_wake() and the IRQF_NO_SUSPEND | 115 | There are very few valid reasons to use both enable_irq_wake() and the |
| 114 | flag on the same IRQ. | 116 | IRQF_NO_SUSPEND flag on the same IRQ, and it is never valid to use both for the |
| 117 | same device. | ||
| 115 | 118 | ||
| 116 | First of all, if the IRQ is not shared, the rules for handling IRQF_NO_SUSPEND | 119 | First of all, if the IRQ is not shared, the rules for handling IRQF_NO_SUSPEND |
| 117 | interrupts (interrupt handlers are invoked after suspend_device_irqs()) are | 120 | interrupts (interrupt handlers are invoked after suspend_device_irqs()) are |
| @@ -120,4 +123,13 @@ handlers are not invoked after suspend_device_irqs()). | |||
| 120 | 123 | ||
| 121 | Second, both enable_irq_wake() and IRQF_NO_SUSPEND apply to entire IRQs and not | 124 | Second, both enable_irq_wake() and IRQF_NO_SUSPEND apply to entire IRQs and not |
| 122 | to individual interrupt handlers, so sharing an IRQ between a system wakeup | 125 | to individual interrupt handlers, so sharing an IRQ between a system wakeup |
| 123 | interrupt source and an IRQF_NO_SUSPEND interrupt source does not make sense. | 126 | interrupt source and an IRQF_NO_SUSPEND interrupt source does not generally |
| 127 | make sense. | ||
| 128 | |||
| 129 | In rare cases an IRQ can be shared between a wakeup device driver and an | ||
| 130 | IRQF_NO_SUSPEND user. In order for this to be safe, the wakeup device driver | ||
| 131 | must be able to discern spurious IRQs from genuine wakeup events (signalling | ||
| 132 | the latter to the core with pm_system_wakeup()), must use enable_irq_wake() to | ||
| 133 | ensure that the IRQ will function as a wakeup source, and must request the IRQ | ||
| 134 | with IRQF_COND_SUSPEND to tell the core that it meets these requirements. If | ||
| 135 | these requirements are not met, it is not valid to use IRQF_COND_SUSPEND. | ||
diff --git a/drivers/base/power/wakeup.c b/drivers/base/power/wakeup.c index c2744b30d5d9..aab7158d2afe 100644 --- a/drivers/base/power/wakeup.c +++ b/drivers/base/power/wakeup.c | |||
| @@ -730,6 +730,7 @@ void pm_system_wakeup(void) | |||
| 730 | pm_abort_suspend = true; | 730 | pm_abort_suspend = true; |
| 731 | freeze_wake(); | 731 | freeze_wake(); |
| 732 | } | 732 | } |
| 733 | EXPORT_SYMBOL_GPL(pm_system_wakeup); | ||
| 733 | 734 | ||
| 734 | void pm_wakeup_clear(void) | 735 | void pm_wakeup_clear(void) |
| 735 | { | 736 | { |
diff --git a/drivers/clk/at91/pmc.c b/drivers/clk/at91/pmc.c index f07c8152e5cc..3f27d21fb729 100644 --- a/drivers/clk/at91/pmc.c +++ b/drivers/clk/at91/pmc.c | |||
| @@ -89,12 +89,29 @@ static int pmc_irq_set_type(struct irq_data *d, unsigned type) | |||
| 89 | return 0; | 89 | return 0; |
| 90 | } | 90 | } |
| 91 | 91 | ||
| 92 | static void pmc_irq_suspend(struct irq_data *d) | ||
| 93 | { | ||
| 94 | struct at91_pmc *pmc = irq_data_get_irq_chip_data(d); | ||
| 95 | |||
| 96 | pmc->imr = pmc_read(pmc, AT91_PMC_IMR); | ||
| 97 | pmc_write(pmc, AT91_PMC_IDR, pmc->imr); | ||
| 98 | } | ||
| 99 | |||
| 100 | static void pmc_irq_resume(struct irq_data *d) | ||
| 101 | { | ||
| 102 | struct at91_pmc *pmc = irq_data_get_irq_chip_data(d); | ||
| 103 | |||
| 104 | pmc_write(pmc, AT91_PMC_IER, pmc->imr); | ||
| 105 | } | ||
| 106 | |||
| 92 | static struct irq_chip pmc_irq = { | 107 | static struct irq_chip pmc_irq = { |
| 93 | .name = "PMC", | 108 | .name = "PMC", |
| 94 | .irq_disable = pmc_irq_mask, | 109 | .irq_disable = pmc_irq_mask, |
| 95 | .irq_mask = pmc_irq_mask, | 110 | .irq_mask = pmc_irq_mask, |
| 96 | .irq_unmask = pmc_irq_unmask, | 111 | .irq_unmask = pmc_irq_unmask, |
| 97 | .irq_set_type = pmc_irq_set_type, | 112 | .irq_set_type = pmc_irq_set_type, |
| 113 | .irq_suspend = pmc_irq_suspend, | ||
| 114 | .irq_resume = pmc_irq_resume, | ||
| 98 | }; | 115 | }; |
| 99 | 116 | ||
| 100 | static struct lock_class_key pmc_lock_class; | 117 | static struct lock_class_key pmc_lock_class; |
| @@ -224,7 +241,8 @@ static struct at91_pmc *__init at91_pmc_init(struct device_node *np, | |||
| 224 | goto out_free_pmc; | 241 | goto out_free_pmc; |
| 225 | 242 | ||
| 226 | pmc_write(pmc, AT91_PMC_IDR, 0xffffffff); | 243 | pmc_write(pmc, AT91_PMC_IDR, 0xffffffff); |
| 227 | if (request_irq(pmc->virq, pmc_irq_handler, IRQF_SHARED, "pmc", pmc)) | 244 | if (request_irq(pmc->virq, pmc_irq_handler, |
| 245 | IRQF_SHARED | IRQF_COND_SUSPEND, "pmc", pmc)) | ||
| 228 | goto out_remove_irqdomain; | 246 | goto out_remove_irqdomain; |
| 229 | 247 | ||
| 230 | return pmc; | 248 | return pmc; |
diff --git a/drivers/clk/at91/pmc.h b/drivers/clk/at91/pmc.h index 52d2041fa3f6..69abb08cf146 100644 --- a/drivers/clk/at91/pmc.h +++ b/drivers/clk/at91/pmc.h | |||
| @@ -33,6 +33,7 @@ struct at91_pmc { | |||
| 33 | spinlock_t lock; | 33 | spinlock_t lock; |
| 34 | const struct at91_pmc_caps *caps; | 34 | const struct at91_pmc_caps *caps; |
| 35 | struct irq_domain *irqdomain; | 35 | struct irq_domain *irqdomain; |
| 36 | u32 imr; | ||
| 36 | }; | 37 | }; |
| 37 | 38 | ||
| 38 | static inline void pmc_lock(struct at91_pmc *pmc) | 39 | static inline void pmc_lock(struct at91_pmc *pmc) |
diff --git a/drivers/rtc/rtc-at91rm9200.c b/drivers/rtc/rtc-at91rm9200.c index 70a5d94cc766..b4f7744f6751 100644 --- a/drivers/rtc/rtc-at91rm9200.c +++ b/drivers/rtc/rtc-at91rm9200.c | |||
| @@ -31,6 +31,7 @@ | |||
| 31 | #include <linux/io.h> | 31 | #include <linux/io.h> |
| 32 | #include <linux/of.h> | 32 | #include <linux/of.h> |
| 33 | #include <linux/of_device.h> | 33 | #include <linux/of_device.h> |
| 34 | #include <linux/suspend.h> | ||
| 34 | #include <linux/uaccess.h> | 35 | #include <linux/uaccess.h> |
| 35 | 36 | ||
| 36 | #include "rtc-at91rm9200.h" | 37 | #include "rtc-at91rm9200.h" |
| @@ -54,6 +55,10 @@ static void __iomem *at91_rtc_regs; | |||
| 54 | static int irq; | 55 | static int irq; |
| 55 | static DEFINE_SPINLOCK(at91_rtc_lock); | 56 | static DEFINE_SPINLOCK(at91_rtc_lock); |
| 56 | static u32 at91_rtc_shadow_imr; | 57 | static u32 at91_rtc_shadow_imr; |
| 58 | static bool suspended; | ||
| 59 | static DEFINE_SPINLOCK(suspended_lock); | ||
| 60 | static unsigned long cached_events; | ||
| 61 | static u32 at91_rtc_imr; | ||
| 57 | 62 | ||
| 58 | static void at91_rtc_write_ier(u32 mask) | 63 | static void at91_rtc_write_ier(u32 mask) |
| 59 | { | 64 | { |
| @@ -290,7 +295,9 @@ static irqreturn_t at91_rtc_interrupt(int irq, void *dev_id) | |||
| 290 | struct rtc_device *rtc = platform_get_drvdata(pdev); | 295 | struct rtc_device *rtc = platform_get_drvdata(pdev); |
| 291 | unsigned int rtsr; | 296 | unsigned int rtsr; |
| 292 | unsigned long events = 0; | 297 | unsigned long events = 0; |
| 298 | int ret = IRQ_NONE; | ||
| 293 | 299 | ||
| 300 | spin_lock(&suspended_lock); | ||
| 294 | rtsr = at91_rtc_read(AT91_RTC_SR) & at91_rtc_read_imr(); | 301 | rtsr = at91_rtc_read(AT91_RTC_SR) & at91_rtc_read_imr(); |
| 295 | if (rtsr) { /* this interrupt is shared! Is it ours? */ | 302 | if (rtsr) { /* this interrupt is shared! Is it ours? */ |
| 296 | if (rtsr & AT91_RTC_ALARM) | 303 | if (rtsr & AT91_RTC_ALARM) |
| @@ -304,14 +311,22 @@ static irqreturn_t at91_rtc_interrupt(int irq, void *dev_id) | |||
| 304 | 311 | ||
| 305 | at91_rtc_write(AT91_RTC_SCCR, rtsr); /* clear status reg */ | 312 | at91_rtc_write(AT91_RTC_SCCR, rtsr); /* clear status reg */ |
| 306 | 313 | ||
| 307 | rtc_update_irq(rtc, 1, events); | 314 | if (!suspended) { |
| 315 | rtc_update_irq(rtc, 1, events); | ||
| 308 | 316 | ||
| 309 | dev_dbg(&pdev->dev, "%s(): num=%ld, events=0x%02lx\n", __func__, | 317 | dev_dbg(&pdev->dev, "%s(): num=%ld, events=0x%02lx\n", |
| 310 | events >> 8, events & 0x000000FF); | 318 | __func__, events >> 8, events & 0x000000FF); |
| 319 | } else { | ||
| 320 | cached_events |= events; | ||
| 321 | at91_rtc_write_idr(at91_rtc_imr); | ||
| 322 | pm_system_wakeup(); | ||
| 323 | } | ||
| 311 | 324 | ||
| 312 | return IRQ_HANDLED; | 325 | ret = IRQ_HANDLED; |
| 313 | } | 326 | } |
| 314 | return IRQ_NONE; /* not handled */ | 327 | spin_lock(&suspended_lock); |
| 328 | |||
| 329 | return ret; | ||
| 315 | } | 330 | } |
| 316 | 331 | ||
| 317 | static const struct at91_rtc_config at91rm9200_config = { | 332 | static const struct at91_rtc_config at91rm9200_config = { |
| @@ -401,8 +416,8 @@ static int __init at91_rtc_probe(struct platform_device *pdev) | |||
| 401 | AT91_RTC_CALEV); | 416 | AT91_RTC_CALEV); |
| 402 | 417 | ||
| 403 | ret = devm_request_irq(&pdev->dev, irq, at91_rtc_interrupt, | 418 | ret = devm_request_irq(&pdev->dev, irq, at91_rtc_interrupt, |
| 404 | IRQF_SHARED, | 419 | IRQF_SHARED | IRQF_COND_SUSPEND, |
| 405 | "at91_rtc", pdev); | 420 | "at91_rtc", pdev); |
| 406 | if (ret) { | 421 | if (ret) { |
| 407 | dev_err(&pdev->dev, "IRQ %d already in use.\n", irq); | 422 | dev_err(&pdev->dev, "IRQ %d already in use.\n", irq); |
| 408 | return ret; | 423 | return ret; |
| @@ -454,8 +469,6 @@ static void at91_rtc_shutdown(struct platform_device *pdev) | |||
| 454 | 469 | ||
| 455 | /* AT91RM9200 RTC Power management control */ | 470 | /* AT91RM9200 RTC Power management control */ |
| 456 | 471 | ||
| 457 | static u32 at91_rtc_imr; | ||
| 458 | |||
| 459 | static int at91_rtc_suspend(struct device *dev) | 472 | static int at91_rtc_suspend(struct device *dev) |
| 460 | { | 473 | { |
| 461 | /* this IRQ is shared with DBGU and other hardware which isn't | 474 | /* this IRQ is shared with DBGU and other hardware which isn't |
| @@ -464,21 +477,42 @@ static int at91_rtc_suspend(struct device *dev) | |||
| 464 | at91_rtc_imr = at91_rtc_read_imr() | 477 | at91_rtc_imr = at91_rtc_read_imr() |
| 465 | & (AT91_RTC_ALARM|AT91_RTC_SECEV); | 478 | & (AT91_RTC_ALARM|AT91_RTC_SECEV); |
| 466 | if (at91_rtc_imr) { | 479 | if (at91_rtc_imr) { |
| 467 | if (device_may_wakeup(dev)) | 480 | if (device_may_wakeup(dev)) { |
| 481 | unsigned long flags; | ||
| 482 | |||
| 468 | enable_irq_wake(irq); | 483 | enable_irq_wake(irq); |
| 469 | else | 484 | |
| 485 | spin_lock_irqsave(&suspended_lock, flags); | ||
| 486 | suspended = true; | ||
| 487 | spin_unlock_irqrestore(&suspended_lock, flags); | ||
| 488 | } else { | ||
| 470 | at91_rtc_write_idr(at91_rtc_imr); | 489 | at91_rtc_write_idr(at91_rtc_imr); |
| 490 | } | ||
| 471 | } | 491 | } |
| 472 | return 0; | 492 | return 0; |
| 473 | } | 493 | } |
| 474 | 494 | ||
| 475 | static int at91_rtc_resume(struct device *dev) | 495 | static int at91_rtc_resume(struct device *dev) |
| 476 | { | 496 | { |
| 497 | struct rtc_device *rtc = dev_get_drvdata(dev); | ||
| 498 | |||
| 477 | if (at91_rtc_imr) { | 499 | if (at91_rtc_imr) { |
| 478 | if (device_may_wakeup(dev)) | 500 | if (device_may_wakeup(dev)) { |
| 501 | unsigned long flags; | ||
| 502 | |||
| 503 | spin_lock_irqsave(&suspended_lock, flags); | ||
| 504 | |||
| 505 | if (cached_events) { | ||
| 506 | rtc_update_irq(rtc, 1, cached_events); | ||
| 507 | cached_events = 0; | ||
| 508 | } | ||
| 509 | |||
| 510 | suspended = false; | ||
| 511 | spin_unlock_irqrestore(&suspended_lock, flags); | ||
| 512 | |||
| 479 | disable_irq_wake(irq); | 513 | disable_irq_wake(irq); |
| 480 | else | 514 | } |
| 481 | at91_rtc_write_ier(at91_rtc_imr); | 515 | at91_rtc_write_ier(at91_rtc_imr); |
| 482 | } | 516 | } |
| 483 | return 0; | 517 | return 0; |
| 484 | } | 518 | } |
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; |
diff --git a/drivers/tty/serial/atmel_serial.c b/drivers/tty/serial/atmel_serial.c index 846552bff67d..4e959c43f680 100644 --- a/drivers/tty/serial/atmel_serial.c +++ b/drivers/tty/serial/atmel_serial.c | |||
| @@ -47,6 +47,7 @@ | |||
| 47 | #include <linux/gpio/consumer.h> | 47 | #include <linux/gpio/consumer.h> |
| 48 | #include <linux/err.h> | 48 | #include <linux/err.h> |
| 49 | #include <linux/irq.h> | 49 | #include <linux/irq.h> |
| 50 | #include <linux/suspend.h> | ||
| 50 | 51 | ||
| 51 | #include <asm/io.h> | 52 | #include <asm/io.h> |
| 52 | #include <asm/ioctls.h> | 53 | #include <asm/ioctls.h> |
| @@ -173,6 +174,12 @@ struct atmel_uart_port { | |||
| 173 | bool ms_irq_enabled; | 174 | bool ms_irq_enabled; |
| 174 | bool is_usart; /* usart or uart */ | 175 | bool is_usart; /* usart or uart */ |
| 175 | struct timer_list uart_timer; /* uart timer */ | 176 | struct timer_list uart_timer; /* uart timer */ |
| 177 | |||
| 178 | bool suspended; | ||
| 179 | unsigned int pending; | ||
| 180 | unsigned int pending_status; | ||
| 181 | spinlock_t lock_suspended; | ||
| 182 | |||
| 176 | int (*prepare_rx)(struct uart_port *port); | 183 | int (*prepare_rx)(struct uart_port *port); |
| 177 | int (*prepare_tx)(struct uart_port *port); | 184 | int (*prepare_tx)(struct uart_port *port); |
| 178 | void (*schedule_rx)(struct uart_port *port); | 185 | void (*schedule_rx)(struct uart_port *port); |
| @@ -1179,12 +1186,15 @@ static irqreturn_t atmel_interrupt(int irq, void *dev_id) | |||
| 1179 | { | 1186 | { |
| 1180 | struct uart_port *port = dev_id; | 1187 | struct uart_port *port = dev_id; |
| 1181 | struct atmel_uart_port *atmel_port = to_atmel_uart_port(port); | 1188 | struct atmel_uart_port *atmel_port = to_atmel_uart_port(port); |
| 1182 | unsigned int status, pending, pass_counter = 0; | 1189 | unsigned int status, pending, mask, pass_counter = 0; |
| 1183 | bool gpio_handled = false; | 1190 | bool gpio_handled = false; |
| 1184 | 1191 | ||
| 1192 | spin_lock(&atmel_port->lock_suspended); | ||
| 1193 | |||
| 1185 | do { | 1194 | do { |
| 1186 | status = atmel_get_lines_status(port); | 1195 | status = atmel_get_lines_status(port); |
| 1187 | pending = status & UART_GET_IMR(port); | 1196 | mask = UART_GET_IMR(port); |
| 1197 | pending = status & mask; | ||
| 1188 | if (!gpio_handled) { | 1198 | if (!gpio_handled) { |
| 1189 | /* | 1199 | /* |
| 1190 | * Dealing with GPIO interrupt | 1200 | * Dealing with GPIO interrupt |
| @@ -1206,11 +1216,21 @@ static irqreturn_t atmel_interrupt(int irq, void *dev_id) | |||
| 1206 | if (!pending) | 1216 | if (!pending) |
| 1207 | break; | 1217 | break; |
| 1208 | 1218 | ||
| 1219 | if (atmel_port->suspended) { | ||
| 1220 | atmel_port->pending |= pending; | ||
| 1221 | atmel_port->pending_status = status; | ||
| 1222 | UART_PUT_IDR(port, mask); | ||
| 1223 | pm_system_wakeup(); | ||
| 1224 | break; | ||
| 1225 | } | ||
| 1226 | |||
| 1209 | atmel_handle_receive(port, pending); | 1227 | atmel_handle_receive(port, pending); |
| 1210 | atmel_handle_status(port, pending, status); | 1228 | atmel_handle_status(port, pending, status); |
| 1211 | atmel_handle_transmit(port, pending); | 1229 | atmel_handle_transmit(port, pending); |
| 1212 | } while (pass_counter++ < ATMEL_ISR_PASS_LIMIT); | 1230 | } while (pass_counter++ < ATMEL_ISR_PASS_LIMIT); |
| 1213 | 1231 | ||
| 1232 | spin_unlock(&atmel_port->lock_suspended); | ||
| 1233 | |||
| 1214 | return pass_counter ? IRQ_HANDLED : IRQ_NONE; | 1234 | return pass_counter ? IRQ_HANDLED : IRQ_NONE; |
| 1215 | } | 1235 | } |
| 1216 | 1236 | ||
| @@ -1742,7 +1762,8 @@ static int atmel_startup(struct uart_port *port) | |||
| 1742 | /* | 1762 | /* |
| 1743 | * Allocate the IRQ | 1763 | * Allocate the IRQ |
| 1744 | */ | 1764 | */ |
| 1745 | retval = request_irq(port->irq, atmel_interrupt, IRQF_SHARED, | 1765 | retval = request_irq(port->irq, atmel_interrupt, |
| 1766 | IRQF_SHARED | IRQF_COND_SUSPEND, | ||
| 1746 | tty ? tty->name : "atmel_serial", port); | 1767 | tty ? tty->name : "atmel_serial", port); |
| 1747 | if (retval) { | 1768 | if (retval) { |
| 1748 | dev_err(port->dev, "atmel_startup - Can't get irq\n"); | 1769 | dev_err(port->dev, "atmel_startup - Can't get irq\n"); |
| @@ -2513,8 +2534,14 @@ static int atmel_serial_suspend(struct platform_device *pdev, | |||
| 2513 | 2534 | ||
| 2514 | /* we can not wake up if we're running on slow clock */ | 2535 | /* we can not wake up if we're running on slow clock */ |
| 2515 | atmel_port->may_wakeup = device_may_wakeup(&pdev->dev); | 2536 | atmel_port->may_wakeup = device_may_wakeup(&pdev->dev); |
| 2516 | if (atmel_serial_clk_will_stop()) | 2537 | if (atmel_serial_clk_will_stop()) { |
| 2538 | unsigned long flags; | ||
| 2539 | |||
| 2540 | spin_lock_irqsave(&atmel_port->lock_suspended, flags); | ||
| 2541 | atmel_port->suspended = true; | ||
| 2542 | spin_unlock_irqrestore(&atmel_port->lock_suspended, flags); | ||
| 2517 | device_set_wakeup_enable(&pdev->dev, 0); | 2543 | device_set_wakeup_enable(&pdev->dev, 0); |
| 2544 | } | ||
| 2518 | 2545 | ||
| 2519 | uart_suspend_port(&atmel_uart, port); | 2546 | uart_suspend_port(&atmel_uart, port); |
| 2520 | 2547 | ||
| @@ -2525,6 +2552,18 @@ static int atmel_serial_resume(struct platform_device *pdev) | |||
| 2525 | { | 2552 | { |
| 2526 | struct uart_port *port = platform_get_drvdata(pdev); | 2553 | struct uart_port *port = platform_get_drvdata(pdev); |
| 2527 | struct atmel_uart_port *atmel_port = to_atmel_uart_port(port); | 2554 | struct atmel_uart_port *atmel_port = to_atmel_uart_port(port); |
| 2555 | unsigned long flags; | ||
| 2556 | |||
| 2557 | spin_lock_irqsave(&atmel_port->lock_suspended, flags); | ||
| 2558 | if (atmel_port->pending) { | ||
| 2559 | atmel_handle_receive(port, atmel_port->pending); | ||
| 2560 | atmel_handle_status(port, atmel_port->pending, | ||
| 2561 | atmel_port->pending_status); | ||
| 2562 | atmel_handle_transmit(port, atmel_port->pending); | ||
| 2563 | atmel_port->pending = 0; | ||
| 2564 | } | ||
| 2565 | atmel_port->suspended = false; | ||
| 2566 | spin_unlock_irqrestore(&atmel_port->lock_suspended, flags); | ||
| 2528 | 2567 | ||
| 2529 | uart_resume_port(&atmel_uart, port); | 2568 | uart_resume_port(&atmel_uart, port); |
| 2530 | device_set_wakeup_enable(&pdev->dev, atmel_port->may_wakeup); | 2569 | device_set_wakeup_enable(&pdev->dev, atmel_port->may_wakeup); |
| @@ -2593,6 +2632,8 @@ static int atmel_serial_probe(struct platform_device *pdev) | |||
| 2593 | port->backup_imr = 0; | 2632 | port->backup_imr = 0; |
| 2594 | port->uart.line = ret; | 2633 | port->uart.line = ret; |
| 2595 | 2634 | ||
| 2635 | spin_lock_init(&port->lock_suspended); | ||
| 2636 | |||
| 2596 | ret = atmel_init_gpios(port, &pdev->dev); | 2637 | ret = atmel_init_gpios(port, &pdev->dev); |
| 2597 | if (ret < 0) | 2638 | if (ret < 0) |
| 2598 | dev_err(&pdev->dev, "%s", | 2639 | dev_err(&pdev->dev, "%s", |
diff --git a/drivers/watchdog/at91sam9_wdt.c b/drivers/watchdog/at91sam9_wdt.c index 6df940528fd2..1443b3c391de 100644 --- a/drivers/watchdog/at91sam9_wdt.c +++ b/drivers/watchdog/at91sam9_wdt.c | |||
| @@ -208,7 +208,8 @@ static int at91_wdt_init(struct platform_device *pdev, struct at91wdt *wdt) | |||
| 208 | 208 | ||
| 209 | if ((tmp & AT91_WDT_WDFIEN) && wdt->irq) { | 209 | if ((tmp & AT91_WDT_WDFIEN) && wdt->irq) { |
| 210 | err = request_irq(wdt->irq, wdt_interrupt, | 210 | err = request_irq(wdt->irq, wdt_interrupt, |
| 211 | IRQF_SHARED | IRQF_IRQPOLL, | 211 | IRQF_SHARED | IRQF_IRQPOLL | |
| 212 | IRQF_NO_SUSPEND, | ||
| 212 | pdev->name, wdt); | 213 | pdev->name, wdt); |
| 213 | if (err) | 214 | if (err) |
| 214 | return err; | 215 | return err; |
diff --git a/include/linux/interrupt.h b/include/linux/interrupt.h index d9b05b5bf8c7..2e88580194f0 100644 --- a/include/linux/interrupt.h +++ b/include/linux/interrupt.h | |||
| @@ -52,11 +52,17 @@ | |||
| 52 | * IRQF_ONESHOT - Interrupt is not reenabled after the hardirq handler finished. | 52 | * IRQF_ONESHOT - Interrupt is not reenabled after the hardirq handler finished. |
| 53 | * Used by threaded interrupts which need to keep the | 53 | * Used by threaded interrupts which need to keep the |
| 54 | * irq line disabled until the threaded handler has been run. | 54 | * irq line disabled until the threaded handler has been run. |
| 55 | * IRQF_NO_SUSPEND - Do not disable this IRQ during suspend | 55 | * IRQF_NO_SUSPEND - Do not disable this IRQ during suspend. Does not guarantee |
| 56 | * that this interrupt will wake the system from a suspended | ||
| 57 | * state. See Documentation/power/suspend-and-interrupts.txt | ||
| 56 | * IRQF_FORCE_RESUME - Force enable it on resume even if IRQF_NO_SUSPEND is set | 58 | * IRQF_FORCE_RESUME - Force enable it on resume even if IRQF_NO_SUSPEND is set |
| 57 | * IRQF_NO_THREAD - Interrupt cannot be threaded | 59 | * IRQF_NO_THREAD - Interrupt cannot be threaded |
| 58 | * IRQF_EARLY_RESUME - Resume IRQ early during syscore instead of at device | 60 | * IRQF_EARLY_RESUME - Resume IRQ early during syscore instead of at device |
| 59 | * resume time. | 61 | * resume time. |
| 62 | * IRQF_COND_SUSPEND - If the IRQ is shared with a NO_SUSPEND user, execute this | ||
| 63 | * interrupt handler after suspending interrupts. For system | ||
| 64 | * wakeup devices users need to implement wakeup detection in | ||
| 65 | * their interrupt handlers. | ||
| 60 | */ | 66 | */ |
| 61 | #define IRQF_DISABLED 0x00000020 | 67 | #define IRQF_DISABLED 0x00000020 |
| 62 | #define IRQF_SHARED 0x00000080 | 68 | #define IRQF_SHARED 0x00000080 |
| @@ -70,6 +76,7 @@ | |||
| 70 | #define IRQF_FORCE_RESUME 0x00008000 | 76 | #define IRQF_FORCE_RESUME 0x00008000 |
| 71 | #define IRQF_NO_THREAD 0x00010000 | 77 | #define IRQF_NO_THREAD 0x00010000 |
| 72 | #define IRQF_EARLY_RESUME 0x00020000 | 78 | #define IRQF_EARLY_RESUME 0x00020000 |
| 79 | #define IRQF_COND_SUSPEND 0x00040000 | ||
| 73 | 80 | ||
| 74 | #define IRQF_TIMER (__IRQF_TIMER | IRQF_NO_SUSPEND | IRQF_NO_THREAD) | 81 | #define IRQF_TIMER (__IRQF_TIMER | IRQF_NO_SUSPEND | IRQF_NO_THREAD) |
| 75 | 82 | ||
diff --git a/include/linux/irqdesc.h b/include/linux/irqdesc.h index faf433af425e..dd1109fb241e 100644 --- a/include/linux/irqdesc.h +++ b/include/linux/irqdesc.h | |||
| @@ -78,6 +78,7 @@ struct irq_desc { | |||
| 78 | #ifdef CONFIG_PM_SLEEP | 78 | #ifdef CONFIG_PM_SLEEP |
| 79 | unsigned int nr_actions; | 79 | unsigned int nr_actions; |
| 80 | unsigned int no_suspend_depth; | 80 | unsigned int no_suspend_depth; |
| 81 | unsigned int cond_suspend_depth; | ||
| 81 | unsigned int force_resume_depth; | 82 | unsigned int force_resume_depth; |
| 82 | #endif | 83 | #endif |
| 83 | #ifdef CONFIG_PROC_FS | 84 | #ifdef CONFIG_PROC_FS |
diff --git a/kernel/irq/manage.c b/kernel/irq/manage.c index 196a06fbc122..886d09e691d5 100644 --- a/kernel/irq/manage.c +++ b/kernel/irq/manage.c | |||
| @@ -1474,8 +1474,13 @@ int request_threaded_irq(unsigned int irq, irq_handler_t handler, | |||
| 1474 | * otherwise we'll have trouble later trying to figure out | 1474 | * otherwise we'll have trouble later trying to figure out |
| 1475 | * which interrupt is which (messes up the interrupt freeing | 1475 | * which interrupt is which (messes up the interrupt freeing |
| 1476 | * logic etc). | 1476 | * logic etc). |
| 1477 | * | ||
| 1478 | * Also IRQF_COND_SUSPEND only makes sense for shared interrupts and | ||
| 1479 | * it cannot be set along with IRQF_NO_SUSPEND. | ||
| 1477 | */ | 1480 | */ |
| 1478 | if ((irqflags & IRQF_SHARED) && !dev_id) | 1481 | if (((irqflags & IRQF_SHARED) && !dev_id) || |
| 1482 | (!(irqflags & IRQF_SHARED) && (irqflags & IRQF_COND_SUSPEND)) || | ||
| 1483 | ((irqflags & IRQF_NO_SUSPEND) && (irqflags & IRQF_COND_SUSPEND))) | ||
| 1479 | return -EINVAL; | 1484 | return -EINVAL; |
| 1480 | 1485 | ||
| 1481 | desc = irq_to_desc(irq); | 1486 | desc = irq_to_desc(irq); |
diff --git a/kernel/irq/pm.c b/kernel/irq/pm.c index 3ca532592704..5204a6d1b985 100644 --- a/kernel/irq/pm.c +++ b/kernel/irq/pm.c | |||
| @@ -43,9 +43,12 @@ void irq_pm_install_action(struct irq_desc *desc, struct irqaction *action) | |||
| 43 | 43 | ||
| 44 | if (action->flags & IRQF_NO_SUSPEND) | 44 | if (action->flags & IRQF_NO_SUSPEND) |
| 45 | desc->no_suspend_depth++; | 45 | desc->no_suspend_depth++; |
| 46 | else if (action->flags & IRQF_COND_SUSPEND) | ||
| 47 | desc->cond_suspend_depth++; | ||
| 46 | 48 | ||
| 47 | WARN_ON_ONCE(desc->no_suspend_depth && | 49 | WARN_ON_ONCE(desc->no_suspend_depth && |
| 48 | desc->no_suspend_depth != desc->nr_actions); | 50 | (desc->no_suspend_depth + |
| 51 | desc->cond_suspend_depth) != desc->nr_actions); | ||
| 49 | } | 52 | } |
| 50 | 53 | ||
| 51 | /* | 54 | /* |
| @@ -61,6 +64,8 @@ void irq_pm_remove_action(struct irq_desc *desc, struct irqaction *action) | |||
| 61 | 64 | ||
| 62 | if (action->flags & IRQF_NO_SUSPEND) | 65 | if (action->flags & IRQF_NO_SUSPEND) |
| 63 | desc->no_suspend_depth--; | 66 | desc->no_suspend_depth--; |
| 67 | else if (action->flags & IRQF_COND_SUSPEND) | ||
| 68 | desc->cond_suspend_depth--; | ||
| 64 | } | 69 | } |
| 65 | 70 | ||
| 66 | static bool suspend_device_irq(struct irq_desc *desc, int irq) | 71 | static bool suspend_device_irq(struct irq_desc *desc, int irq) |
