2009-03-17 00:33:49 +03:00
/*
* linux / kernel / irq / pm . c
*
* Copyright ( C ) 2009 Rafael J . Wysocki < rjw @ sisk . pl > , Novell Inc .
*
* This file contains power management functions related to interrupts .
*/
# include <linux/irq.h>
# include <linux/module.h>
# include <linux/interrupt.h>
2011-10-03 18:37:00 +04:00
# include <linux/syscore_ops.h>
2009-03-17 00:33:49 +03:00
# include "internals.h"
/**
* suspend_device_irqs - disable all currently enabled interrupt lines
*
2009-07-05 02:22:34 +04:00
* During system - wide suspend or hibernation device drivers need to be prevented
* from receiving interrupts and this function is provided for this purpose .
* It marks all interrupt lines in use , except for the timer ones , as disabled
2011-02-08 14:44:58 +03:00
* and sets the IRQS_SUSPENDED flag for each of them .
2009-03-17 00:33:49 +03:00
*/
void suspend_device_irqs ( void )
{
struct irq_desc * desc ;
int irq ;
for_each_irq_desc ( irq , desc ) {
unsigned long flags ;
2009-11-17 18:46:45 +03:00
raw_spin_lock_irqsave ( & desc - > lock , flags ) ;
2009-03-17 00:33:49 +03:00
__disable_irq ( desc , irq , true ) ;
2009-11-17 18:46:45 +03:00
raw_spin_unlock_irqrestore ( & desc - > lock , flags ) ;
2009-03-17 00:33:49 +03:00
}
for_each_irq_desc ( irq , desc )
2011-02-08 14:44:58 +03:00
if ( desc - > istate & IRQS_SUSPENDED )
2009-03-17 00:33:49 +03:00
synchronize_irq ( irq ) ;
}
EXPORT_SYMBOL_GPL ( suspend_device_irqs ) ;
2011-10-03 18:37:00 +04:00
static void resume_irqs ( bool want_early )
2009-03-17 00:33:49 +03:00
{
struct irq_desc * desc ;
int irq ;
for_each_irq_desc ( irq , desc ) {
unsigned long flags ;
2011-10-03 18:37:00 +04:00
bool is_early = desc - > action & &
desc - > action - > flags & IRQF_EARLY_RESUME ;
2013-11-25 18:09:47 +04:00
if ( ! is_early & & want_early )
2011-10-03 18:37:00 +04:00
continue ;
2009-03-17 00:33:49 +03:00
2009-11-17 18:46:45 +03:00
raw_spin_lock_irqsave ( & desc - > lock , flags ) ;
2009-03-17 00:33:49 +03:00
__enable_irq ( desc , irq , true ) ;
2009-11-17 18:46:45 +03:00
raw_spin_unlock_irqrestore ( & desc - > lock , flags ) ;
2009-03-17 00:33:49 +03:00
}
}
2011-10-03 18:37:00 +04:00
/**
* irq_pm_syscore_ops - enable interrupt lines early
*
* Enable all interrupt lines with % IRQF_EARLY_RESUME set .
*/
static void irq_pm_syscore_resume ( void )
{
resume_irqs ( true ) ;
}
static struct syscore_ops irq_pm_syscore_ops = {
. resume = irq_pm_syscore_resume ,
} ;
static int __init irq_pm_init_ops ( void )
{
register_syscore_ops ( & irq_pm_syscore_ops ) ;
return 0 ;
}
device_initcall ( irq_pm_init_ops ) ;
/**
* resume_device_irqs - enable interrupt lines disabled by suspend_device_irqs ( )
*
* Enable all non - % IRQF_EARLY_RESUME interrupt lines previously
* disabled by suspend_device_irqs ( ) that have the IRQS_SUSPENDED flag
* set as well as those with % IRQF_FORCE_RESUME .
*/
void resume_device_irqs ( void )
{
resume_irqs ( false ) ;
}
2009-03-17 00:33:49 +03:00
EXPORT_SYMBOL_GPL ( resume_device_irqs ) ;
/**
* check_wakeup_irqs - check if any wake - up interrupts are pending
*/
int check_wakeup_irqs ( void )
{
struct irq_desc * desc ;
int irq ;
2011-03-11 23:22:14 +03:00
for_each_irq_desc ( irq , desc ) {
2012-05-04 19:56:16 +04:00
/*
* Only interrupts which are marked as wakeup source
* and have not been disabled before the suspend check
* can abort suspend .
*/
2011-03-11 23:22:14 +03:00
if ( irqd_is_wakeup_set ( & desc - > irq_data ) ) {
2012-05-04 19:56:16 +04:00
if ( desc - > depth = = 1 & & desc - > istate & IRQS_PENDING )
2011-03-11 23:22:14 +03:00
return - EBUSY ;
continue ;
}
/*
* Check the non wakeup interrupts whether they need
* to be masked before finally going into suspend
* state . That ' s for hardware which has no wakeup
* source configuration facility . The chip
* implementation indicates that with
* IRQCHIP_MASK_ON_SUSPEND .
*/
if ( desc - > istate & IRQS_SUSPENDED & &
irq_desc_get_chip ( desc ) - > flags & IRQCHIP_MASK_ON_SUSPEND )
mask_irq ( desc ) ;
}
2009-03-17 00:33:49 +03:00
return 0 ;
}