• Grygorii Strashko's avatar
    PM / wakeirq: Fix spurious wake-up events for dedicated wakeirqs · 0bf0ee8e
    Grygorii Strashko authored
    Dedicated wakeirq is a one time event to wake-up the system from
    low-power state and then call pm_runtime_resume() on the device wired
    with the dedicated wakeirq.
    
    Sometimes dedicated wakeirqs can get deferred if they trigger after we
    call disable_irq_nosync() in dev_pm_disable_wake_irq(). This can happen
    if pm_runtime_get() is called around the same time a wakeirq fires.
    
    If an interrupt fires after disable_irq_nosync(), by default it will get
    tagged with IRQS_PENDING and will run later on when the interrupt is
    enabled again.
    
    Deferred wakeirqs usually just produce pointless wake-up events. But they
    can also cause suspend to fail if the deferred wakeirq fires during
    dpm_suspend_noirq() for example. So we really don't want to see the
    deferred wakeirqs triggering after the device has resumed.
    
    Let's fix the issue by setting IRQ_DISABLE_UNLAZY flag for the dedicated
    wakeirqs. The other option would be to implement irq_disable() in the
    dedicated wakeirq controller, but that's not a generic solution.
    
    For reference below is what happens with a IRQ_TYPE_EDGE_BOTH IRQ
    type wakeirq:
    
    - resume by dedicated IRQ (EDGE_FALLING)
     - suspend_enter()
      ....
     - arch_suspend_enable_irqs()
       |- dedicated IRQ armed and fired
       |- irq_pm_check_wakeup()
          |- disarm, disable IRQ and mark as IRQS_PENDING
      ....
     - dpm_resume_noirq()
       |- resume_device_irqs()
          |- __enable_irq()
             |- check_irq_resend()
                |- handle_threaded_wake_irq()
     	       |- dedicated IRQ processed
       |- device_wakeup_disarm_wake_irqs()
          |- disable_irq_wake()
      ....
     !-> dedicated IRQ (EDGE_RISING)
         -| handle_edge_irq()
            |- IRQ disabled: mask_ack_irq and mark as IRQS_PENDING
      ....
    - subsequent suspend
      ....
      |- dpm_suspend_noirq()
         |- device_wakeup_arm_wake_irqs()
            |- __enable_irq()
               |- check_irq_resend()
    (a)           |- handle_threaded_wake_irq()
                     |- pm_wakeup_event() --> abort suspend
      ....
         |- suspend_device_irqs()
            |- suspend_device_irq()
               |-  dedicated IRQ armed
      ....
    (b)  |- resend_irqs
            |- irq_pm_check_wakeup()
               |- IRQ armed -> abort suspend
    
    because of pending IRQ System suspend can be aborted at points
    (a)-not armed or (b)-armed.
    
    Fixes: 4990d4fe (PM / Wakeirq: Add automated device wake IRQ handling)
    Signed-off-by: default avatarGrygorii Strashko <grygorii.strashko@ti.com>
    [ tony@atomide.com: added a comment, updated the description ]
    Tested-by: default avatarTony Lindgren <tony@atomide.com>
    Signed-off-by: default avatarTony Lindgren <tony@atomide.com>
    Signed-off-by: default avatarRafael J. Wysocki <rafael.j.wysocki@intel.com>
    0bf0ee8e
wakeirq.c 8.96 KB