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 systemsuspend after the “late” phase of suspending devices (that is, after all of the->prepare, ->suspend and ->suspend_late callbacks have been executed for alldevices). That is done bysuspend_device_irqs().
The rationale for doing so is that after the “late” phase of device suspendthere is no legitimate reason why any interrupts from suspended devices shouldtrigger and if any devices have not been suspended properly yet, it is better toblock interrupts from them anyway. Also, in the past we had problems withinterrupt handlers for shared IRQs that device drivers implementing them werenot prepared for interrupts triggering after their devices had been suspended.In some cases they would attempt to access, for example, memory address spacesof suspended devices and cause unpredictable behavior to ensue as a result.Unfortunately, such problems are very difficult to debug and the introductionofsuspend_device_irqs(), along with the “noirq” phase of device suspend andresume, was the only practical way to mitigate them.
Device IRQs are re-enabled during system resume, right before the “early” phaseof resuming devices (that is, before starting to execute ->resume_earlycallbacks for devices). The function doing that isresume_device_irqs().
The IRQF_NO_SUSPEND Flag¶
There are interrupts that can legitimately trigger during the entire systemsuspend-resume cycle, including the “noirq” phases of suspending and resumingdevices as well as during the time when nonboot CPUs are taken offline andbrought 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 whenrequesting a special-purpose interrupt. It causessuspend_device_irqs() toleave the corresponding IRQ enabled so as to allow the interrupt to work asexpected during the suspend-resume cycle, but does not guarantee that theinterrupt will wake the system from a suspended state -- for such cases it isnecessary to useenable_irq_wake().
Note that the IRQF_NO_SUSPEND flag affects the entire IRQ and not just oneuser of it. Thus, if the IRQ is shared, all of the interrupt handlers installedfor it will be executed as usual aftersuspend_device_irqs(), even if theIRQF_NO_SUSPEND flag was not passed torequest_irq() (or equivalent) by some ofthe IRQ’s users. For this reason, using IRQF_NO_SUSPEND and IRQF_SHARED at thesame 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 systemfrom sleep states, especially if they are used for different purposes (e.g. asI/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 wayduring system sleep so as to trigger a system wakeup when needed. For example,the platform may include a dedicated interrupt controller used specifically forhandling system wakeup events. Then, if a given interrupt line is supposed towake up the system from sleep states, the corresponding input of that interruptcontroller needs to be enabled to receive signals from the line in question.After wakeup, it generally is better to disable that input to prevent thededicated controller from triggering interrupts unnecessarily.
The IRQ subsystem provides two helper functions to be used by device drivers forthose purposes. Namely,enable_irq_wake() turns on the platform’s logic forhandling the given IRQ as a system wakeup interrupt line anddisable_irq_wake()turns that logic off.
Callingenable_irq_wake() causessuspend_device_irqs() to treat the given IRQin a special way. Namely, the IRQ remains enabled, but on the first interruptit will be disabled, marked as pending and “suspended” so that it will bere-enabled byresume_device_irqs() during the subsequent system resume. Alsothe PM core is notified about the event which causes the system suspend inprogress to be aborted (that doesn’t have to happen immediately, but at oneof the points where the suspend thread looks for pending wakeup events).
This way every interrupt from a wakeup interrupt source will either cause thesystem suspend currently in progress to be aborted or wake up the system ifalready suspended. However, aftersuspend_device_irqs() interrupt handlers arenot executed for system wakeup IRQs. They are only executed for IRQF_NO_SUSPENDIRQs at that time, but those IRQs should not be configured for system wakeupusingenable_irq_wake().
Interrupts and Suspend-to-Idle¶
Suspend-to-idle (also known as the “freeze” sleep state) is a relatively newsystem sleep state that works by idling all of the processors and waiting forinterrupts right after the “noirq” phase of suspending devices.
Of course, this means that all of the interrupts with the IRQF_NO_SUSPEND flagset will bring CPUs out of idle while in that state, but they will not cause theIRQ subsystem to trigger a system wakeup.
System wakeup interrupts, in turn, will trigger wakeup from suspend-to-idle inanalogy with what they do in the full system suspend case. The only differenceis that the wakeup from suspend-to-idle is signaled using the usual workingstate interrupt delivery mechanisms and doesn’t require the platform to useany special interrupt handling logic for it to work.
IRQF_NO_SUSPEND and enable_irq_wake()¶
There are very few valid reasons to use bothenable_irq_wake() and theIRQF_NO_SUSPEND flag on the same IRQ, and it is never valid to use both for thesame device.
First of all, if the IRQ is not shared, the rules for handling IRQF_NO_SUSPENDinterrupts (interrupt handlers are invoked aftersuspend_device_irqs()) aredirectly at odds with the rules for handling system wakeup interrupts (interrupthandlers are not invoked aftersuspend_device_irqs()).
Second, bothenable_irq_wake() and IRQF_NO_SUSPEND apply to entire IRQs and notto individual interrupt handlers, so sharing an IRQ between a system wakeupinterrupt source and an IRQF_NO_SUSPEND interrupt source does not generallymake sense.
In rare cases an IRQ can be shared between a wakeup device driver and anIRQF_NO_SUSPEND user. In order for this to be safe, the wakeup device drivermust be able to discern spurious IRQs from genuine wakeup events (signallingthe latter to the core withpm_system_wakeup()), must useenable_irq_wake() toensure that the IRQ will function as a wakeup source, and must request the IRQwith IRQF_COND_SUSPEND to tell the core that it meets these requirements. Ifthese requirements are not met, it is not valid to use IRQF_COND_SUSPEND.