Home  >  Article  >  System Tutorial  >  Linux system sleep (System Suspend) and device interrupt handling

Linux system sleep (System Suspend) and device interrupt handling

WBOY
WBOYforward
2024-02-11 19:00:301241browse

This section mainly solves a problem: How to suspend device interrupts (IRQ) when the system enters sleep state? How to restore device IRQ when waking up the system?

Normally, after the system enters sleep state, the IRQ (interrupt request line) of all devices will be disabled. The specific time point is after the late suspend phase of the device. Below is the relevant code (irrelevant code omitted).

static int suspend_enter(suspend_state_t state, bool *wakeup)

{……

error = dpm_suspend_late(PMSG_SUSPEND);-----late suspend phase

error = platform_suspend_prepare_late(state);

The following code will disable the irq of each device

error = dpm_suspend_noirq(PMSG_SUSPEND);——Entering the noirq stage

error = platform_suspend_prepare_noirq(state);

……

}

In the dpm_suspend_noirq function, device_suspend_noirq will be called in turn for each device in the system to execute the suspend callback function in the case of the device's noirq. Of course, the suspend_device_irqs function will be called before this to disable the irqs of all devices.

The reason for doing this is this: after each device driver completes late suspend, it stands to reason that these devices that have been suspended should no longer trigger interrupts. If there are still some devices that have not been suspended correctly, then our best strategy is to mask the irq of the device to prevent the interrupt from being submitted. In addition, in the past code (referring to the interrupt handler), we did not handle the situation of devices sharing IRQ very well. There is such a problem: after the devices sharing IRQ complete suspend, if an interrupt is triggered, the device driver will The interrupt handler is not ready. In some scenarios, the interrupt handler will access the IO address space of the suspended device, causing unpredictable issues. These issues are difficult to debug, so we introduced suspend_device_irqs() and the callback function in the device noirq phase.

During the system resume process, before the early resume process of each device, the IRQ of each device will be reopened. The specific code is as follows (some irrelevant codes have been deleted):

static int suspend_enter(suspend_state_t state, bool *wakeup)

{……

platform_resume_noirq(state);---First execute the resume of the noirq stage

dpm_resume_noirq(PMSG_RESUME);------The irq will be restored here and then enter the early resume stage

platform_resume_early(state);

dpm_resume_early(PMSG_RESUME);

……}

In the dpm_resume_noirq function, the noirq callback of each device driver will be called. After that, the resume_device_irqs function will be called to complete the enable of each device irq.

2. About IRQF_NO_SUSPEND Flag

Of course, some interrupts need to remain triggerable during the suspend-resume process of the entire system (including the noirq stage, including the stage of pushing the nonboot CPU to the offline state and resetting it to online after the system resumes). . A simple example is the timer interrupt, in addition to IPI and some special purpose device interrupts.

When requesting an interrupt, the IRQF_NO_SUSPEND flag can be used to inform the IRQ subsystem that this interrupt is the type of interrupt described in the previous paragraph: it needs to remain enabled during the system's suspend-resume process. With this flag, suspend_device_irqs will not disable the IRQ, allowing the interrupt to remain enabled during subsequent suspend and resume processes. Of course, this does not guarantee that the interrupt can wake the system. If you want to achieve the purpose of waking up, please call enable_irq_wake.

It should be noted that the IRQF_NO_SUSPEND flag affects all peripherals that use this IRQ (an IRQ can be shared by multiple peripherals, but this is not used in ARM). If an IRQ is shared by multiple peripherals, and each peripheral has registered a corresponding interrupt handler, and if one of them uses the IRQF_NO_SUSPEND flag when applying for an interrupt, then when the system is suspended (referring to after suspend_device_irqs), it stands to reason that each IRQ has been disabled), the interrupt handlers of all devices on the IRQ can be triggered and executed normally, even if some devices do not set the IRQF_NO_SUSPEND flag when calling request_irq (or other interrupt registration functions). Because of this, we should avoid using the two flags IRQF_NO_SUSPEND and IRQF_SHARED at the same time as much as possible.

3. System interrupt wake-up interface: enable_irq_wake() and disable_irq_wake()

Some interrupts can wake the system from sleep state, which we call "interrupts that can wake up the system". Of course, "interrupts that can wake up the system" need to be configured to start the function of waking up the system. Such interrupts generally appear as ordinary I/O interrupts during working conditions. Only when preparing to enable the wake-up system function will some special configurations and settings be initiated.

Such configuration and settings may be related to the signal processing logic on the hardware system (such as SOC). We can consider the following HW block diagram:

Linux系统休眠(System Suspend)和设备中断处理

Peripheral interrupt signals are sent to the "general interrupt signal processing module" and the "specific interrupt signal receiving module". When working normally, we will turn on the processing logic of the "general interrupt signal processing module" and turn off the processing logic of the "specific interrupt signal receiving module". However, when the system enters sleep state, it is possible that the "general interrupt signal processing module" has been turned off. At this time, we need to start the "specific interrupt signal receiving module" to receive the interrupt signal, so that the system suspend-resume module ( It is often the only HW block that can work in the suspended state) and can be woken up by the interrupt signal normally. Once woken up, we'd better turn off the "specific interrupt signal receiving module" to allow the peripheral's interrupt processing to return to the normal working mode. At the same time, it also avoids unnecessary interference to the system's suspend-resume module.

The IRQ subsystem provides two interface functions to complete this function: the enable_irq_wake() function is used to open the peripheral interrupt line leading to the system power management module (that is, the suspend-resume module above). In addition One interface is disable_irq_wake(), which is used to close various HW blocks on the path from the peripheral interrupt line to the system power management module.

Calling enable_irq_wake will affect the suspend_device_irqs processing in the system suspend process. The code is as follows:

static bool suspend_device_irq(struct irq_desc *desc)

{

……

if (irqd_is_wakeup_set(&desc->irq_data)) {

irqd_set(&desc->irq_data, IRQD_WAKEUP_ARMED);

return true;

}

Omit Disable interrupt code

}

In other words, once enable_irq_wake is called to set the device's interrupt as the wake-up source of the system suspend, the interrupt in the peripheral will not be disabled, but will be marked with an IRQD_WAKEUP_ARMED mark. For interrupts that are not wakeup sources, IRQS_SUSPENDED will be marked in the suspend_device_irq function and the irq of the device will be disabled. During the system wake-up process (resume_device_irqs), the interrupts that have been disabled will be re-enabled.

Of course, if certain events occur during the suspend process (for example, the wakeup source generates a valid signal), resulting in this suspend abort, then this abort event will also be notified to the PM core module. The event does not need to be notified to the PM core module immediately. Generally speaking, the suspend thread will check the pending wakeup event at some point.

During the system suspend process, each interrupt from the wakeup source will terminate the suspend process or wake up the system (if the system has entered the suspend state). However, after executing suspend_device_irqs, ordinary interrupts are blocked. At this time, even if the HW triggers the interrupt signal, it cannot execute its interrupt handler. What happens to the IRQ as the wakeup source? Although its interrupt has not been masked, its interrupt handler will not be executed (the HW Signal at this time is only used to wake up the system). The only interrupt handlers that have a chance to execute are those IRQs marked with the IRQF_NO_SUSPEND flag, because their interrupts are always enabled. Of course, these interrupts should not call enable_irq_wake to set the wake-up source.

4. Interrupts and Suspend-to-Idle

Suspend-to-idle (also known as "freeze" state) is a relatively new system power management state. The relevant code is as follows:

static int suspend_enter(suspend_state_t state, bool *wakeup)

{

……

Late suspend phase of each device

noirq suspend phase of each device

if (state == PM_SUSPEND_FREEZE) {

freeze_enter();

goto Platform_wake;

}

……

}

The previous operations of Freeze and suspend are basically the same: first freeze the processes in the system, and then suspend the various devices in the system. The difference is that after noirq suspend is completed, freeze will not disable those non-BSP processor and syscore suspend phase, but calls the freeze_enter function to push all processors to the idle state. At this time, any enabled interrupt can wake up the system. And this means that those flags IRQF_NO_SUSPEND (whose IRQ is not masked during the suspend_device_irqs process) are capable of waking the processor from the idle state (however, it should be noted that this signal does not trigger a system wake-up signal), and ordinary interrupts cannot wake up the processor in the idle state because their IRQ is disabled.

What about wakeup interrupts that can wake up the system? Since its interrupt is not masked, the system can also be woken up from the suspend-to-idle state. The whole process is the same as waking up the system from the suspended state. The only difference is: the interrupt processing path to wake the system from the freeze state, and the wake-up processing path to wake the system from the suspended state, require special power management HW BLOCK. Participation in interrupt handling logic.

5. The IRQF_NO_SUSPEND flag and the enable_irq_wake function cannot be used at the same time

For a device, it is unreasonable to use the IRQF_NO_SUSPEND flag when applying for an interrupt and call enable_irq_wake to set the wake-up source at the same time. The main reasons are as follows:

1. If the IRQ is not shared, use the IRQF_NO_SUSPEND flag to indicate that you want to keep the interrupt open during the suspend-resume process of the entire system (including the stage after suspend_device_irqs) so that its interrupt handler can be called normally. Calling the enable_irq_wake function indicates that you want to set the irq signal of the device as an interrupt source, so you do not expect to call its interrupt handler. These two requirements are obviously mutually exclusive.

2. Neither the IRQF_NO_SUSPEND flag nor the enable_irq_wake function is for one interrupt handler, but for all registered handlers on the IRQ. It is ridiculous to share the wake-up source and the no suspend interrupt source on the same IRQ.

However, in very special circumstances, an IRQ can be set as the wakeup source and the IRQF_NO_SUSPEND flag is also set. In order for the code logic to be correct, the device's driver code needs to meet some special requirements.

The above is the detailed content of Linux system sleep (System Suspend) and device interrupt handling. For more information, please follow other related articles on the PHP Chinese website!

Statement:
This article is reproduced at:lxlinux.net. If there is any infringement, please contact admin@php.cn delete