2005-04-17 02:20:36 +04:00
/*
* linux / kernel / irq / spurious . c
*
* Copyright ( C ) 1992 , 1998 - 2004 Linus Torvalds , Ingo Molnar
*
* This file contains spurious interrupt handling .
*/
# include <linux/irq.h>
# include <linux/module.h>
# include <linux/kallsyms.h>
# include <linux/interrupt.h>
2006-06-23 13:05:32 +04:00
static int irqfixup __read_mostly ;
2005-06-29 07:45:18 +04:00
/*
* Recovery handler for misrouted interrupts .
*/
static int misrouted_irq ( int irq , struct pt_regs * regs )
{
int i ;
irq_desc_t * desc ;
int ok = 0 ;
int work = 0 ; /* Did we do work for a real IRQ */
for ( i = 1 ; i < NR_IRQS ; i + + ) {
struct irqaction * action ;
if ( i = = irq ) /* Already tried */
continue ;
desc = & irq_desc [ i ] ;
spin_lock ( & desc - > lock ) ;
action = desc - > action ;
/* Already running on another processor */
if ( desc - > status & IRQ_INPROGRESS ) {
/*
* Already running : If it is shared get the other
* CPU to go looking for our mystery interrupt too
*/
if ( desc - > action & & ( desc - > action - > flags & SA_SHIRQ ) )
desc - > status | = IRQ_PENDING ;
spin_unlock ( & desc - > lock ) ;
continue ;
}
/* Honour the normal IRQ locking */
desc - > status | = IRQ_INPROGRESS ;
spin_unlock ( & desc - > lock ) ;
while ( action ) {
/* Only shared IRQ handlers are safe to call */
if ( action - > flags & SA_SHIRQ ) {
if ( action - > handler ( i , action - > dev_id , regs ) = =
IRQ_HANDLED )
ok = 1 ;
}
action = action - > next ;
}
local_irq_disable ( ) ;
/* Now clean up the flags */
spin_lock ( & desc - > lock ) ;
action = desc - > action ;
/*
* While we were looking for a fixup someone queued a real
* IRQ clashing with our walk
*/
while ( ( desc - > status & IRQ_PENDING ) & & action ) {
/*
* Perform real IRQ processing for the IRQ we deferred
*/
work = 1 ;
spin_unlock ( & desc - > lock ) ;
handle_IRQ_event ( i , regs , action ) ;
spin_lock ( & desc - > lock ) ;
desc - > status & = ~ IRQ_PENDING ;
}
desc - > status & = ~ IRQ_INPROGRESS ;
/*
* If we did actual work for the real IRQ line we must let the
* IRQ controller clean up too
*/
if ( work )
desc - > handler - > end ( i ) ;
spin_unlock ( & desc - > lock ) ;
}
/* So the caller can adjust the irq error counts */
return ok ;
}
2005-04-17 02:20:36 +04:00
/*
* If 99 , 900 of the previous 100 , 000 interrupts have not been handled
* then assume that the IRQ is stuck in some manner . Drop a diagnostic
* and try to turn the IRQ off .
*
* ( The other 100 - of - 100 , 000 interrupts may have been a correctly
* functioning device sharing an IRQ with the failing one )
*
* Called under desc - > lock
*/
static void
__report_bad_irq ( unsigned int irq , irq_desc_t * desc , irqreturn_t action_ret )
{
struct irqaction * action ;
if ( action_ret ! = IRQ_HANDLED & & action_ret ! = IRQ_NONE ) {
printk ( KERN_ERR " irq event %d: bogus return value %x \n " ,
irq , action_ret ) ;
} else {
2005-06-29 07:45:18 +04:00
printk ( KERN_ERR " irq %d: nobody cared (try booting with "
" the \" irqpoll \" option) \n " , irq ) ;
2005-04-17 02:20:36 +04:00
}
dump_stack ( ) ;
printk ( KERN_ERR " handlers: \n " ) ;
action = desc - > action ;
while ( action ) {
printk ( KERN_ERR " [<%p>] " , action - > handler ) ;
print_symbol ( " (%s) " ,
( unsigned long ) action - > handler ) ;
printk ( " \n " ) ;
action = action - > next ;
}
}
2005-06-24 09:05:33 +04:00
static void report_bad_irq ( unsigned int irq , irq_desc_t * desc , irqreturn_t action_ret )
2005-04-17 02:20:36 +04:00
{
static int count = 100 ;
if ( count > 0 ) {
count - - ;
__report_bad_irq ( irq , desc , action_ret ) ;
}
}
2005-06-29 07:45:18 +04:00
void note_interrupt ( unsigned int irq , irq_desc_t * desc , irqreturn_t action_ret ,
struct pt_regs * regs )
2005-04-17 02:20:36 +04:00
{
2006-06-23 13:05:32 +04:00
if ( unlikely ( action_ret ! = IRQ_HANDLED ) ) {
2005-04-17 02:20:36 +04:00
desc - > irqs_unhandled + + ;
2006-06-23 13:05:32 +04:00
if ( unlikely ( action_ret ! = IRQ_NONE ) )
2005-04-17 02:20:36 +04:00
report_bad_irq ( irq , desc , action_ret ) ;
}
2005-06-29 07:45:18 +04:00
if ( unlikely ( irqfixup ) ) {
/* Don't punish working computers */
if ( ( irqfixup = = 2 & & irq = = 0 ) | | action_ret = = IRQ_NONE ) {
int ok = misrouted_irq ( irq , regs ) ;
if ( action_ret = = IRQ_NONE )
desc - > irqs_unhandled - = ok ;
}
}
2005-04-17 02:20:36 +04:00
desc - > irq_count + + ;
2006-06-23 13:05:32 +04:00
if ( likely ( desc - > irq_count < 100000 ) )
2005-04-17 02:20:36 +04:00
return ;
desc - > irq_count = 0 ;
2006-06-23 13:05:32 +04:00
if ( unlikely ( desc - > irqs_unhandled > 99900 ) ) {
2005-04-17 02:20:36 +04:00
/*
* The interrupt is stuck
*/
__report_bad_irq ( irq , desc , action_ret ) ;
/*
* Now kill the IRQ
*/
printk ( KERN_EMERG " Disabling IRQ #%d \n " , irq ) ;
desc - > status | = IRQ_DISABLED ;
desc - > handler - > disable ( irq ) ;
}
desc - > irqs_unhandled = 0 ;
}
2006-06-23 13:05:32 +04:00
int noirqdebug __read_mostly ;
2005-04-17 02:20:36 +04:00
int __init noirqdebug_setup ( char * str )
{
noirqdebug = 1 ;
printk ( KERN_INFO " IRQ lockup detection disabled \n " ) ;
return 1 ;
}
__setup ( " noirqdebug " , noirqdebug_setup ) ;
2005-06-29 07:45:18 +04:00
static int __init irqfixup_setup ( char * str )
{
irqfixup = 1 ;
printk ( KERN_WARNING " Misrouted IRQ fixup support enabled. \n " ) ;
printk ( KERN_WARNING " This may impact system performance. \n " ) ;
return 1 ;
}
__setup ( " irqfixup " , irqfixup_setup ) ;
static int __init irqpoll_setup ( char * str )
{
irqfixup = 2 ;
printk ( KERN_WARNING " Misrouted IRQ fixup and polling support "
" enabled \n " ) ;
printk ( KERN_WARNING " This may significantly impact system "
" performance \n " ) ;
return 1 ;
}
__setup ( " irqpoll " , irqpoll_setup ) ;