|  | System Suspend and Device Interrupts | 
|  |  | 
|  | Copyright (C) 2014 Intel Corp. | 
|  | Author: Rafael J. Wysocki <rafael.j.wysocki@intel.com> | 
|  |  | 
|  |  | 
|  | Suspending and Resuming Device IRQs | 
|  | ----------------------------------- | 
|  |  | 
|  | Device interrupt request lines (IRQs) are generally disabled during system | 
|  | suspend after the "late" phase of suspending devices (that is, after all of the | 
|  | ->prepare, ->suspend and ->suspend_late callbacks have been executed for all | 
|  | devices).  That is done by suspend_device_irqs(). | 
|  |  | 
|  | The rationale for doing so is that after the "late" phase of device suspend | 
|  | there is no legitimate reason why any interrupts from suspended devices should | 
|  | trigger and if any devices have not been suspended properly yet, it is better to | 
|  | block interrupts from them anyway.  Also, in the past we had problems with | 
|  | interrupt handlers for shared IRQs that device drivers implementing them were | 
|  | not prepared for interrupts triggering after their devices had been suspended. | 
|  | In some cases they would attempt to access, for example, memory address spaces | 
|  | of suspended devices and cause unpredictable behavior to ensue as a result. | 
|  | Unfortunately, such problems are very difficult to debug and the introduction | 
|  | of suspend_device_irqs(), along with the "noirq" phase of device suspend and | 
|  | resume, was the only practical way to mitigate them. | 
|  |  | 
|  | Device IRQs are re-enabled during system resume, right before the "early" phase | 
|  | of resuming devices (that is, before starting to execute ->resume_early | 
|  | callbacks for devices).  The function doing that is resume_device_irqs(). | 
|  |  | 
|  |  | 
|  | The IRQF_NO_SUSPEND Flag | 
|  | ------------------------ | 
|  |  | 
|  | There are interrupts that can legitimately trigger during the entire system | 
|  | suspend-resume cycle, including the "noirq" phases of suspending and resuming | 
|  | devices as well as during the time when nonboot CPUs are taken offline and | 
|  | brought back online.  That applies to timer interrupts in the first place, | 
|  | but also to IPIs and to some other special-purpose interrupts. | 
|  |  | 
|  | The IRQF_NO_SUSPEND flag is used to indicate that to the IRQ subsystem when | 
|  | requesting a special-purpose interrupt.  It causes suspend_device_irqs() to | 
|  | leave the corresponding IRQ enabled so as to allow the interrupt to work as | 
|  | expected during the suspend-resume cycle, but does not guarantee that the | 
|  | interrupt will wake the system from a suspended state -- for such cases it is | 
|  | necessary to use enable_irq_wake(). | 
|  |  | 
|  | Note that the IRQF_NO_SUSPEND flag affects the entire IRQ and not just one | 
|  | user of it.  Thus, if the IRQ is shared, all of the interrupt handlers installed | 
|  | for it will be executed as usual after suspend_device_irqs(), even if the | 
|  | IRQF_NO_SUSPEND flag was not passed to request_irq() (or equivalent) by some of | 
|  | the IRQ's users.  For this reason, using IRQF_NO_SUSPEND and IRQF_SHARED at the | 
|  | same time should be avoided. | 
|  |  | 
|  |  | 
|  | System Wakeup Interrupts, enable_irq_wake() and disable_irq_wake() | 
|  | ------------------------------------------------------------------ | 
|  |  | 
|  | System wakeup interrupts generally need to be configured to wake up the system | 
|  | from sleep states, especially if they are used for different purposes (e.g. as | 
|  | I/O interrupts) in the working state. | 
|  |  | 
|  | That may involve turning on a special signal handling logic within the platform | 
|  | (such as an SoC) so that signals from a given line are routed in a different way | 
|  | during system sleep so as to trigger a system wakeup when needed.  For example, | 
|  | the platform may include a dedicated interrupt controller used specifically for | 
|  | handling system wakeup events.  Then, if a given interrupt line is supposed to | 
|  | wake up the system from sleep sates, the corresponding input of that interrupt | 
|  | controller needs to be enabled to receive signals from the line in question. | 
|  | After wakeup, it generally is better to disable that input to prevent the | 
|  | dedicated controller from triggering interrupts unnecessarily. | 
|  |  | 
|  | The IRQ subsystem provides two helper functions to be used by device drivers for | 
|  | those purposes.  Namely, enable_irq_wake() turns on the platform's logic for | 
|  | handling the given IRQ as a system wakeup interrupt line and disable_irq_wake() | 
|  | turns that logic off. | 
|  |  | 
|  | Calling enable_irq_wake() causes suspend_device_irqs() to treat the given IRQ | 
|  | in a special way.  Namely, the IRQ remains enabled, by on the first interrupt | 
|  | it will be disabled, marked as pending and "suspended" so that it will be | 
|  | re-enabled by resume_device_irqs() during the subsequent system resume.  Also | 
|  | the PM core is notified about the event which causes the system suspend in | 
|  | progress to be aborted (that doesn't have to happen immediately, but at one | 
|  | of the points where the suspend thread looks for pending wakeup events). | 
|  |  | 
|  | This way every interrupt from a wakeup interrupt source will either cause the | 
|  | system suspend currently in progress to be aborted or wake up the system if | 
|  | already suspended.  However, after suspend_device_irqs() interrupt handlers are | 
|  | not executed for system wakeup IRQs.  They are only executed for IRQF_NO_SUSPEND | 
|  | IRQs at that time, but those IRQs should not be configured for system wakeup | 
|  | using enable_irq_wake(). | 
|  |  | 
|  |  | 
|  | Interrupts and Suspend-to-Idle | 
|  | ------------------------------ | 
|  |  | 
|  | Suspend-to-idle (also known as the "freeze" sleep state) is a relatively new | 
|  | system sleep state that works by idling all of the processors and waiting for | 
|  | interrupts right after the "noirq" phase of suspending devices. | 
|  |  | 
|  | Of course, this means that all of the interrupts with the IRQF_NO_SUSPEND flag | 
|  | set will bring CPUs out of idle while in that state, but they will not cause the | 
|  | IRQ subsystem to trigger a system wakeup. | 
|  |  | 
|  | System wakeup interrupts, in turn, will trigger wakeup from suspend-to-idle in | 
|  | analogy with what they do in the full system suspend case.  The only difference | 
|  | is that the wakeup from suspend-to-idle is signaled using the usual working | 
|  | state interrupt delivery mechanisms and doesn't require the platform to use | 
|  | any special interrupt handling logic for it to work. | 
|  |  | 
|  |  | 
|  | IRQF_NO_SUSPEND and enable_irq_wake() | 
|  | ------------------------------------- | 
|  |  | 
|  | There are very few valid reasons to use both enable_irq_wake() and the | 
|  | IRQF_NO_SUSPEND flag on the same IRQ, and it is never valid to use both for the | 
|  | same device. | 
|  |  | 
|  | First of all, if the IRQ is not shared, the rules for handling IRQF_NO_SUSPEND | 
|  | interrupts (interrupt handlers are invoked after suspend_device_irqs()) are | 
|  | directly at odds with the rules for handling system wakeup interrupts (interrupt | 
|  | handlers are not invoked after suspend_device_irqs()). | 
|  |  | 
|  | Second, both enable_irq_wake() and IRQF_NO_SUSPEND apply to entire IRQs and not | 
|  | to individual interrupt handlers, so sharing an IRQ between a system wakeup | 
|  | interrupt source and an IRQF_NO_SUSPEND interrupt source does not generally | 
|  | make sense. | 
|  |  | 
|  | In rare cases an IRQ can be shared between a wakeup device driver and an | 
|  | IRQF_NO_SUSPEND user. In order for this to be safe, the wakeup device driver | 
|  | must be able to discern spurious IRQs from genuine wakeup events (signalling | 
|  | the latter to the core with pm_system_wakeup()), must use enable_irq_wake() to | 
|  | ensure that the IRQ will function as a wakeup source, and must request the IRQ | 
|  | with IRQF_COND_SUSPEND to tell the core that it meets these requirements. If | 
|  | these requirements are not met, it is not valid to use IRQF_COND_SUSPEND. |