 9bab0b7fba
			
		
	
	
	9bab0b7fba
	
	
	
		
			
			This adds a mechanism to resume selected IRQs during syscore_resume
instead of dpm_resume_noirq.
Under Xen we need to resume IRQs associated with IPIs early enough
that the resched IPI is unmasked and we can therefore schedule
ourselves out of the stop_machine where the suspend/resume takes
place.
This issue was introduced by 676dc3cf5b "xen: Use IRQF_FORCE_RESUME".
Signed-off-by: Ian Campbell <ian.campbell@citrix.com>
Cc: Rafael J. Wysocki <rjw@sisk.pl>
Cc: Jeremy Fitzhardinge <Jeremy.Fitzhardinge@citrix.com>
Cc: xen-devel <xen-devel@lists.xensource.com>
Cc: Konrad Rzeszutek Wilk <konrad.wilk@oracle.com>
Link: http://lkml.kernel.org/r/1318713254.11016.52.camel@dagon.hellion.org.uk
Cc: stable@kernel.org (at least to 2.6.32.y)
Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
		
	
			
		
			
				
	
	
		
			125 lines
		
	
	
	
		
			2.9 KiB
			
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			125 lines
		
	
	
	
		
			2.9 KiB
			
		
	
	
	
		
			C
		
	
	
	
	
	
| /*
 | |
|  * 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>
 | |
| #include <linux/syscore_ops.h>
 | |
| 
 | |
| #include "internals.h"
 | |
| 
 | |
| /**
 | |
|  * suspend_device_irqs - disable all currently enabled interrupt lines
 | |
|  *
 | |
|  * 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
 | |
|  * and sets the IRQS_SUSPENDED flag for each of them.
 | |
|  */
 | |
| void suspend_device_irqs(void)
 | |
| {
 | |
| 	struct irq_desc *desc;
 | |
| 	int irq;
 | |
| 
 | |
| 	for_each_irq_desc(irq, desc) {
 | |
| 		unsigned long flags;
 | |
| 
 | |
| 		raw_spin_lock_irqsave(&desc->lock, flags);
 | |
| 		__disable_irq(desc, irq, true);
 | |
| 		raw_spin_unlock_irqrestore(&desc->lock, flags);
 | |
| 	}
 | |
| 
 | |
| 	for_each_irq_desc(irq, desc)
 | |
| 		if (desc->istate & IRQS_SUSPENDED)
 | |
| 			synchronize_irq(irq);
 | |
| }
 | |
| EXPORT_SYMBOL_GPL(suspend_device_irqs);
 | |
| 
 | |
| static void resume_irqs(bool want_early)
 | |
| {
 | |
| 	struct irq_desc *desc;
 | |
| 	int irq;
 | |
| 
 | |
| 	for_each_irq_desc(irq, desc) {
 | |
| 		unsigned long flags;
 | |
| 		bool is_early = desc->action &&
 | |
| 			desc->action->flags & IRQF_EARLY_RESUME;
 | |
| 
 | |
| 		if (is_early != want_early)
 | |
| 			continue;
 | |
| 
 | |
| 		raw_spin_lock_irqsave(&desc->lock, flags);
 | |
| 		__enable_irq(desc, irq, true);
 | |
| 		raw_spin_unlock_irqrestore(&desc->lock, flags);
 | |
| 	}
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * 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);
 | |
| }
 | |
| 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;
 | |
| 
 | |
| 	for_each_irq_desc(irq, desc) {
 | |
| 		if (irqd_is_wakeup_set(&desc->irq_data)) {
 | |
| 			if (desc->istate & IRQS_PENDING)
 | |
| 				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);
 | |
| 	}
 | |
| 
 | |
| 	return 0;
 | |
| }
 |