Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0
0002 /*
0003  * Copyright (C) 2009 Rafael J. Wysocki <rjw@sisk.pl>, Novell Inc.
0004  *
0005  * This file contains power management functions related to interrupts.
0006  */
0007 
0008 #include <linux/irq.h>
0009 #include <linux/module.h>
0010 #include <linux/interrupt.h>
0011 #include <linux/suspend.h>
0012 #include <linux/syscore_ops.h>
0013 
0014 #include "internals.h"
0015 
0016 bool irq_pm_check_wakeup(struct irq_desc *desc)
0017 {
0018     if (irqd_is_wakeup_armed(&desc->irq_data)) {
0019         irqd_clear(&desc->irq_data, IRQD_WAKEUP_ARMED);
0020         desc->istate |= IRQS_SUSPENDED | IRQS_PENDING;
0021         desc->depth++;
0022         irq_disable(desc);
0023         pm_system_irq_wakeup(irq_desc_get_irq(desc));
0024         return true;
0025     }
0026     return false;
0027 }
0028 
0029 /*
0030  * Called from __setup_irq() with desc->lock held after @action has
0031  * been installed in the action chain.
0032  */
0033 void irq_pm_install_action(struct irq_desc *desc, struct irqaction *action)
0034 {
0035     desc->nr_actions++;
0036 
0037     if (action->flags & IRQF_FORCE_RESUME)
0038         desc->force_resume_depth++;
0039 
0040     WARN_ON_ONCE(desc->force_resume_depth &&
0041              desc->force_resume_depth != desc->nr_actions);
0042 
0043     if (action->flags & IRQF_NO_SUSPEND)
0044         desc->no_suspend_depth++;
0045     else if (action->flags & IRQF_COND_SUSPEND)
0046         desc->cond_suspend_depth++;
0047 
0048     WARN_ON_ONCE(desc->no_suspend_depth &&
0049              (desc->no_suspend_depth +
0050             desc->cond_suspend_depth) != desc->nr_actions);
0051 }
0052 
0053 /*
0054  * Called from __free_irq() with desc->lock held after @action has
0055  * been removed from the action chain.
0056  */
0057 void irq_pm_remove_action(struct irq_desc *desc, struct irqaction *action)
0058 {
0059     desc->nr_actions--;
0060 
0061     if (action->flags & IRQF_FORCE_RESUME)
0062         desc->force_resume_depth--;
0063 
0064     if (action->flags & IRQF_NO_SUSPEND)
0065         desc->no_suspend_depth--;
0066     else if (action->flags & IRQF_COND_SUSPEND)
0067         desc->cond_suspend_depth--;
0068 }
0069 
0070 static bool suspend_device_irq(struct irq_desc *desc)
0071 {
0072     unsigned long chipflags = irq_desc_get_chip(desc)->flags;
0073     struct irq_data *irqd = &desc->irq_data;
0074 
0075     if (!desc->action || irq_desc_is_chained(desc) ||
0076         desc->no_suspend_depth)
0077         return false;
0078 
0079     if (irqd_is_wakeup_set(irqd)) {
0080         irqd_set(irqd, IRQD_WAKEUP_ARMED);
0081 
0082         if ((chipflags & IRQCHIP_ENABLE_WAKEUP_ON_SUSPEND) &&
0083              irqd_irq_disabled(irqd)) {
0084             /*
0085              * Interrupt marked for wakeup is in disabled state.
0086              * Enable interrupt here to unmask/enable in irqchip
0087              * to be able to resume with such interrupts.
0088              */
0089             __enable_irq(desc);
0090             irqd_set(irqd, IRQD_IRQ_ENABLED_ON_SUSPEND);
0091         }
0092         /*
0093          * We return true here to force the caller to issue
0094          * synchronize_irq(). We need to make sure that the
0095          * IRQD_WAKEUP_ARMED is visible before we return from
0096          * suspend_device_irqs().
0097          */
0098         return true;
0099     }
0100 
0101     desc->istate |= IRQS_SUSPENDED;
0102     __disable_irq(desc);
0103 
0104     /*
0105      * Hardware which has no wakeup source configuration facility
0106      * requires that the non wakeup interrupts are masked at the
0107      * chip level. The chip implementation indicates that with
0108      * IRQCHIP_MASK_ON_SUSPEND.
0109      */
0110     if (chipflags & IRQCHIP_MASK_ON_SUSPEND)
0111         mask_irq(desc);
0112     return true;
0113 }
0114 
0115 /**
0116  * suspend_device_irqs - disable all currently enabled interrupt lines
0117  *
0118  * During system-wide suspend or hibernation device drivers need to be
0119  * prevented from receiving interrupts and this function is provided
0120  * for this purpose.
0121  *
0122  * So we disable all interrupts and mark them IRQS_SUSPENDED except
0123  * for those which are unused, those which are marked as not
0124  * suspendable via an interrupt request with the flag IRQF_NO_SUSPEND
0125  * set and those which are marked as active wakeup sources.
0126  *
0127  * The active wakeup sources are handled by the flow handler entry
0128  * code which checks for the IRQD_WAKEUP_ARMED flag, suspends the
0129  * interrupt and notifies the pm core about the wakeup.
0130  */
0131 void suspend_device_irqs(void)
0132 {
0133     struct irq_desc *desc;
0134     int irq;
0135 
0136     for_each_irq_desc(irq, desc) {
0137         unsigned long flags;
0138         bool sync;
0139 
0140         if (irq_settings_is_nested_thread(desc))
0141             continue;
0142         raw_spin_lock_irqsave(&desc->lock, flags);
0143         sync = suspend_device_irq(desc);
0144         raw_spin_unlock_irqrestore(&desc->lock, flags);
0145 
0146         if (sync)
0147             synchronize_irq(irq);
0148     }
0149 }
0150 
0151 static void resume_irq(struct irq_desc *desc)
0152 {
0153     struct irq_data *irqd = &desc->irq_data;
0154 
0155     irqd_clear(irqd, IRQD_WAKEUP_ARMED);
0156 
0157     if (irqd_is_enabled_on_suspend(irqd)) {
0158         /*
0159          * Interrupt marked for wakeup was enabled during suspend
0160          * entry. Disable such interrupts to restore them back to
0161          * original state.
0162          */
0163         __disable_irq(desc);
0164         irqd_clear(irqd, IRQD_IRQ_ENABLED_ON_SUSPEND);
0165     }
0166 
0167     if (desc->istate & IRQS_SUSPENDED)
0168         goto resume;
0169 
0170     /* Force resume the interrupt? */
0171     if (!desc->force_resume_depth)
0172         return;
0173 
0174     /* Pretend that it got disabled ! */
0175     desc->depth++;
0176     irq_state_set_disabled(desc);
0177     irq_state_set_masked(desc);
0178 resume:
0179     desc->istate &= ~IRQS_SUSPENDED;
0180     __enable_irq(desc);
0181 }
0182 
0183 static void resume_irqs(bool want_early)
0184 {
0185     struct irq_desc *desc;
0186     int irq;
0187 
0188     for_each_irq_desc(irq, desc) {
0189         unsigned long flags;
0190         bool is_early = desc->action &&
0191             desc->action->flags & IRQF_EARLY_RESUME;
0192 
0193         if (!is_early && want_early)
0194             continue;
0195         if (irq_settings_is_nested_thread(desc))
0196             continue;
0197 
0198         raw_spin_lock_irqsave(&desc->lock, flags);
0199         resume_irq(desc);
0200         raw_spin_unlock_irqrestore(&desc->lock, flags);
0201     }
0202 }
0203 
0204 /**
0205  * rearm_wake_irq - rearm a wakeup interrupt line after signaling wakeup
0206  * @irq: Interrupt to rearm
0207  */
0208 void rearm_wake_irq(unsigned int irq)
0209 {
0210     unsigned long flags;
0211     struct irq_desc *desc = irq_get_desc_buslock(irq, &flags, IRQ_GET_DESC_CHECK_GLOBAL);
0212 
0213     if (!desc)
0214         return;
0215 
0216     if (!(desc->istate & IRQS_SUSPENDED) ||
0217         !irqd_is_wakeup_set(&desc->irq_data))
0218         goto unlock;
0219 
0220     desc->istate &= ~IRQS_SUSPENDED;
0221     irqd_set(&desc->irq_data, IRQD_WAKEUP_ARMED);
0222     __enable_irq(desc);
0223 
0224 unlock:
0225     irq_put_desc_busunlock(desc, flags);
0226 }
0227 
0228 /**
0229  * irq_pm_syscore_resume - enable interrupt lines early
0230  *
0231  * Enable all interrupt lines with %IRQF_EARLY_RESUME set.
0232  */
0233 static void irq_pm_syscore_resume(void)
0234 {
0235     resume_irqs(true);
0236 }
0237 
0238 static struct syscore_ops irq_pm_syscore_ops = {
0239     .resume     = irq_pm_syscore_resume,
0240 };
0241 
0242 static int __init irq_pm_init_ops(void)
0243 {
0244     register_syscore_ops(&irq_pm_syscore_ops);
0245     return 0;
0246 }
0247 
0248 device_initcall(irq_pm_init_ops);
0249 
0250 /**
0251  * resume_device_irqs - enable interrupt lines disabled by suspend_device_irqs()
0252  *
0253  * Enable all non-%IRQF_EARLY_RESUME interrupt lines previously
0254  * disabled by suspend_device_irqs() that have the IRQS_SUSPENDED flag
0255  * set as well as those with %IRQF_FORCE_RESUME.
0256  */
0257 void resume_device_irqs(void)
0258 {
0259     resume_irqs(false);
0260 }