linux/kernel/irq/pm.c
<<
>>
Prefs
   1/*
   2 * linux/kernel/irq/pm.c
   3 *
   4 * Copyright (C) 2009 Rafael J. Wysocki <rjw@sisk.pl>, Novell Inc.
   5 *
   6 * This file contains power management functions related to interrupts.
   7 */
   8
   9#include <linux/irq.h>
  10#include <linux/module.h>
  11#include <linux/interrupt.h>
  12#include <linux/syscore_ops.h>
  13
  14#include "internals.h"
  15
  16/**
  17 * suspend_device_irqs - disable all currently enabled interrupt lines
  18 *
  19 * During system-wide suspend or hibernation device drivers need to be prevented
  20 * from receiving interrupts and this function is provided for this purpose.
  21 * It marks all interrupt lines in use, except for the timer ones, as disabled
  22 * and sets the IRQS_SUSPENDED flag for each of them.
  23 */
  24void suspend_device_irqs(void)
  25{
  26        struct irq_desc *desc;
  27        int irq;
  28
  29        for_each_irq_desc(irq, desc) {
  30                unsigned long flags;
  31
  32                raw_spin_lock_irqsave(&desc->lock, flags);
  33                __disable_irq(desc, irq, true);
  34                raw_spin_unlock_irqrestore(&desc->lock, flags);
  35        }
  36
  37        for_each_irq_desc(irq, desc)
  38                if (desc->istate & IRQS_SUSPENDED)
  39                        synchronize_irq(irq);
  40}
  41EXPORT_SYMBOL_GPL(suspend_device_irqs);
  42
  43static void resume_irqs(bool want_early)
  44{
  45        struct irq_desc *desc;
  46        int irq;
  47
  48        for_each_irq_desc(irq, desc) {
  49                unsigned long flags;
  50                bool is_early = desc->action &&
  51                        desc->action->flags & IRQF_EARLY_RESUME;
  52
  53                if (!is_early && want_early)
  54                        continue;
  55
  56                raw_spin_lock_irqsave(&desc->lock, flags);
  57                __enable_irq(desc, irq, true);
  58                raw_spin_unlock_irqrestore(&desc->lock, flags);
  59        }
  60}
  61
  62/**
  63 * irq_pm_syscore_ops - enable interrupt lines early
  64 *
  65 * Enable all interrupt lines with %IRQF_EARLY_RESUME set.
  66 */
  67static void irq_pm_syscore_resume(void)
  68{
  69        resume_irqs(true);
  70}
  71
  72static struct syscore_ops irq_pm_syscore_ops = {
  73        .resume         = irq_pm_syscore_resume,
  74};
  75
  76static int __init irq_pm_init_ops(void)
  77{
  78        register_syscore_ops(&irq_pm_syscore_ops);
  79        return 0;
  80}
  81
  82device_initcall(irq_pm_init_ops);
  83
  84/**
  85 * resume_device_irqs - enable interrupt lines disabled by suspend_device_irqs()
  86 *
  87 * Enable all non-%IRQF_EARLY_RESUME interrupt lines previously
  88 * disabled by suspend_device_irqs() that have the IRQS_SUSPENDED flag
  89 * set as well as those with %IRQF_FORCE_RESUME.
  90 */
  91void resume_device_irqs(void)
  92{
  93        resume_irqs(false);
  94}
  95EXPORT_SYMBOL_GPL(resume_device_irqs);
  96
  97/**
  98 * check_wakeup_irqs - check if any wake-up interrupts are pending
  99 */
 100int check_wakeup_irqs(void)
 101{
 102        struct irq_desc *desc;
 103        int irq;
 104
 105        for_each_irq_desc(irq, desc) {
 106                /*
 107                 * Only interrupts which are marked as wakeup source
 108                 * and have not been disabled before the suspend check
 109                 * can abort suspend.
 110                 */
 111                if (irqd_is_wakeup_set(&desc->irq_data)) {
 112                        if (desc->depth == 1 && desc->istate & IRQS_PENDING)
 113                                return -EBUSY;
 114                        continue;
 115                }
 116                /*
 117                 * Check the non wakeup interrupts whether they need
 118                 * to be masked before finally going into suspend
 119                 * state. That's for hardware which has no wakeup
 120                 * source configuration facility. The chip
 121                 * implementation indicates that with
 122                 * IRQCHIP_MASK_ON_SUSPEND.
 123                 */
 124                if (desc->istate & IRQS_SUSPENDED &&
 125                    irq_desc_get_chip(desc)->flags & IRQCHIP_MASK_ON_SUSPEND)
 126                        mask_irq(desc);
 127        }
 128
 129        return 0;
 130}
 131