2005-04-17 02:20:36 +04:00
/*
* linux / kernel / irq / autoprobe . c
*
* Copyright ( C ) 1992 , 1998 - 2004 Linus Torvalds , Ingo Molnar
*
* This file contains the interrupt probing code and driver APIs .
*/
# include <linux/irq.h>
# include <linux/module.h>
# include <linux/interrupt.h>
2005-06-29 07:44:42 +04:00
# include <linux/delay.h>
2009-01-07 19:45:46 +03:00
# include <linux/async.h>
2005-04-17 02:20:36 +04:00
2006-06-29 13:24:54 +04:00
# include "internals.h"
2005-04-17 02:20:36 +04:00
/*
* Autodetection depends on the fact that any interrupt that
* comes in on to an unassigned handler will get stuck with
* " IRQ_WAITING " cleared and the interrupt disabled .
*/
2006-06-29 13:24:37 +04:00
static DEFINE_MUTEX ( probing_active ) ;
2005-04-17 02:20:36 +04:00
/**
* probe_irq_on - begin an interrupt autodetect
*
* Commence probing for an interrupt . The interrupts are scanned
* and a mask of potential interrupt lines is returned .
*
*/
unsigned long probe_irq_on ( void )
{
2006-06-29 13:24:40 +04:00
struct irq_desc * desc ;
2008-10-16 16:19:04 +04:00
unsigned long mask = 0 ;
unsigned int status ;
int i ;
2005-04-17 02:20:36 +04:00
2009-01-07 19:45:46 +03:00
/*
* quiesce the kernel , or at least the asynchronous portion
*/
async_synchronize_full ( ) ;
2006-06-29 13:24:37 +04:00
mutex_lock ( & probing_active ) ;
2005-04-17 02:20:36 +04:00
/*
* something may have generated an irq long ago and we want to
* flush such a longstanding irq before considering it as spurious .
*/
2008-10-16 16:19:04 +04:00
for_each_irq_desc_reverse ( i , desc ) {
2009-11-17 18:46:45 +03:00
raw_spin_lock_irq ( & desc - > lock ) ;
2006-06-29 13:24:51 +04:00
if ( ! desc - > action & & ! ( desc - > status & IRQ_NOPROBE ) ) {
2006-06-29 13:24:54 +04:00
/*
* An old - style architecture might still have
* the handle_bad_irq handler there :
*/
compat_irq_chip_set_default_handler ( desc ) ;
2006-06-29 13:24:51 +04:00
/*
* Some chips need to know about probing in
* progress :
*/
2010-10-01 14:58:38 +04:00
if ( desc - > irq_data . chip - > set_type )
desc - > irq_data . chip - > set_type ( i , IRQ_TYPE_PROBE ) ;
2010-09-27 16:45:38 +04:00
desc - > irq_data . chip - > irq_startup ( & desc - > irq_data ) ;
2006-06-29 13:24:51 +04:00
}
2009-11-17 18:46:45 +03:00
raw_spin_unlock_irq ( & desc - > lock ) ;
2005-04-17 02:20:36 +04:00
}
/* Wait for longstanding interrupts to trigger. */
2005-06-29 07:44:42 +04:00
msleep ( 20 ) ;
2005-04-17 02:20:36 +04:00
/*
* enable any unassigned irqs
* ( we must startup again here because if a longstanding irq
* happened in the previous stage , it may have masked itself )
*/
2008-10-16 16:19:04 +04:00
for_each_irq_desc_reverse ( i , desc ) {
2009-11-17 18:46:45 +03:00
raw_spin_lock_irq ( & desc - > lock ) ;
2006-06-29 13:24:49 +04:00
if ( ! desc - > action & & ! ( desc - > status & IRQ_NOPROBE ) ) {
2005-04-17 02:20:36 +04:00
desc - > status | = IRQ_AUTODETECT | IRQ_WAITING ;
2010-09-27 16:45:38 +04:00
if ( desc - > irq_data . chip - > irq_startup ( & desc - > irq_data ) )
2005-04-17 02:20:36 +04:00
desc - > status | = IRQ_PENDING ;
}
2009-11-17 18:46:45 +03:00
raw_spin_unlock_irq ( & desc - > lock ) ;
2005-04-17 02:20:36 +04:00
}
/*
* Wait for spurious interrupts to trigger
*/
2005-06-29 07:44:42 +04:00
msleep ( 100 ) ;
2005-04-17 02:20:36 +04:00
/*
* Now filter out any obviously spurious interrupts
*/
2008-10-16 16:19:04 +04:00
for_each_irq_desc ( i , desc ) {
2009-11-17 18:46:45 +03:00
raw_spin_lock_irq ( & desc - > lock ) ;
2005-04-17 02:20:36 +04:00
status = desc - > status ;
if ( status & IRQ_AUTODETECT ) {
/* It triggered already - consider it spurious. */
if ( ! ( status & IRQ_WAITING ) ) {
desc - > status = status & ~ IRQ_AUTODETECT ;
2010-09-27 16:45:02 +04:00
desc - > irq_data . chip - > irq_shutdown ( & desc - > irq_data ) ;
2005-04-17 02:20:36 +04:00
} else
if ( i < 32 )
2006-06-29 13:24:40 +04:00
mask | = 1 < < i ;
2005-04-17 02:20:36 +04:00
}
2009-11-17 18:46:45 +03:00
raw_spin_unlock_irq ( & desc - > lock ) ;
2005-04-17 02:20:36 +04:00
}
2006-06-29 13:24:40 +04:00
return mask ;
2005-04-17 02:20:36 +04:00
}
EXPORT_SYMBOL ( probe_irq_on ) ;
/**
* probe_irq_mask - scan a bitmap of interrupt lines
* @ val : mask of interrupts to consider
*
* Scan the interrupt lines and return a bitmap of active
* autodetect interrupts . The interrupt probe logic state
* is then returned to its previous value .
*
* Note : we need to scan all the irq ' s even though we will
* only return autodetect irq numbers - just so that we reset
* them all to a known state .
*/
unsigned int probe_irq_mask ( unsigned long val )
{
2008-10-16 16:19:04 +04:00
unsigned int status , mask = 0 ;
struct irq_desc * desc ;
2005-04-17 02:20:36 +04:00
int i ;
2008-10-16 16:19:04 +04:00
for_each_irq_desc ( i , desc ) {
2009-11-17 18:46:45 +03:00
raw_spin_lock_irq ( & desc - > lock ) ;
2005-04-17 02:20:36 +04:00
status = desc - > status ;
if ( status & IRQ_AUTODETECT ) {
if ( i < 16 & & ! ( status & IRQ_WAITING ) )
mask | = 1 < < i ;
desc - > status = status & ~ IRQ_AUTODETECT ;
2010-09-27 16:45:02 +04:00
desc - > irq_data . chip - > irq_shutdown ( & desc - > irq_data ) ;
2005-04-17 02:20:36 +04:00
}
2009-11-17 18:46:45 +03:00
raw_spin_unlock_irq ( & desc - > lock ) ;
2005-04-17 02:20:36 +04:00
}
2006-06-29 13:24:37 +04:00
mutex_unlock ( & probing_active ) ;
2005-04-17 02:20:36 +04:00
return mask & val ;
}
EXPORT_SYMBOL ( probe_irq_mask ) ;
/**
* probe_irq_off - end an interrupt autodetect
* @ val : mask of potential interrupts ( unused )
*
* Scans the unused interrupt lines and returns the line which
* appears to have triggered the interrupt . If no interrupt was
* found then zero is returned . If more than one interrupt is
* found then minus the first candidate is returned to indicate
* their is doubt .
*
* The interrupt probe logic state is returned to its previous
* value .
*
* BUGS : When used in a module ( which arguably shouldn ' t happen )
* nothing prevents two IRQ probe callers from overlapping . The
* results of this are non - optimal .
*/
int probe_irq_off ( unsigned long val )
{
2008-10-16 17:33:18 +04:00
int i , irq_found = 0 , nr_of_irqs = 0 ;
2008-10-16 16:19:04 +04:00
struct irq_desc * desc ;
unsigned int status ;
2005-04-17 02:20:36 +04:00
2008-10-16 16:19:04 +04:00
for_each_irq_desc ( i , desc ) {
2009-11-17 18:46:45 +03:00
raw_spin_lock_irq ( & desc - > lock ) ;
2005-04-17 02:20:36 +04:00
status = desc - > status ;
if ( status & IRQ_AUTODETECT ) {
if ( ! ( status & IRQ_WAITING ) ) {
2008-10-16 17:33:18 +04:00
if ( ! nr_of_irqs )
2005-04-17 02:20:36 +04:00
irq_found = i ;
2008-10-16 17:33:18 +04:00
nr_of_irqs + + ;
2005-04-17 02:20:36 +04:00
}
desc - > status = status & ~ IRQ_AUTODETECT ;
2010-09-27 16:45:02 +04:00
desc - > irq_data . chip - > irq_shutdown ( & desc - > irq_data ) ;
2005-04-17 02:20:36 +04:00
}
2009-11-17 18:46:45 +03:00
raw_spin_unlock_irq ( & desc - > lock ) ;
2005-04-17 02:20:36 +04:00
}
2006-06-29 13:24:37 +04:00
mutex_unlock ( & probing_active ) ;
2005-04-17 02:20:36 +04:00
2008-10-16 17:33:18 +04:00
if ( nr_of_irqs > 1 )
2005-04-17 02:20:36 +04:00
irq_found = - irq_found ;
2006-06-29 13:24:37 +04:00
2005-04-17 02:20:36 +04:00
return irq_found ;
}
EXPORT_SYMBOL ( probe_irq_off ) ;