2005-04-17 02:20:36 +04:00
/*
* linux / kernel / irq / handle . c
*
* Copyright ( C ) 1992 , 1998 - 2004 Linus Torvalds , Ingo Molnar
*
* This file contains the core interrupt handling code .
*/
# include <linux/irq.h>
# include <linux/module.h>
# include <linux/random.h>
# include <linux/interrupt.h>
# include <linux/kernel_stat.h>
# include "internals.h"
/*
* Linux has a controller - independent interrupt architecture .
* Every controller has a ' controller - template ' , that is used
* by the main code to do the right thing . Each driver - visible
* interrupt source is transparently wired to the apropriate
* controller . Thus drivers need not be aware of the
* interrupt - controller .
*
* The code is designed to be easily extended with new / different
* interrupt controllers , without having to do assembly magic or
* having to touch the generic code .
*
* Controller mappings for all interrupt sources :
*/
irq_desc_t irq_desc [ NR_IRQS ] __cacheline_aligned = {
[ 0 . . . NR_IRQS - 1 ] = {
2005-05-17 08:53:43 +04:00
. status = IRQ_DISABLED ,
2005-04-17 02:20:36 +04:00
. handler = & no_irq_type ,
. lock = SPIN_LOCK_UNLOCKED
}
} ;
/*
* Generic ' no controller ' code
*/
static void end_none ( unsigned int irq ) { }
static void enable_none ( unsigned int irq ) { }
static void disable_none ( unsigned int irq ) { }
static void shutdown_none ( unsigned int irq ) { }
static unsigned int startup_none ( unsigned int irq ) { return 0 ; }
static void ack_none ( unsigned int irq )
{
/*
* ' what should we do if we get a hw irq event on an illegal vector ' .
* each architecture has to answer this themself .
*/
ack_bad_irq ( irq ) ;
}
struct hw_interrupt_type no_irq_type = {
. typename = " none " ,
. startup = startup_none ,
. shutdown = shutdown_none ,
. enable = enable_none ,
. disable = disable_none ,
. ack = ack_none ,
. end = end_none ,
. set_affinity = NULL
} ;
/*
* Special , empty irq handler :
*/
irqreturn_t no_action ( int cpl , void * dev_id , struct pt_regs * regs )
{
return IRQ_NONE ;
}
/*
* Have got an event to handle :
*/
2006-06-23 13:06:00 +04:00
fastcall irqreturn_t handle_IRQ_event ( unsigned int irq , struct pt_regs * regs ,
2005-04-17 02:20:36 +04:00
struct irqaction * action )
{
2006-06-23 13:06:00 +04:00
irqreturn_t ret , retval = IRQ_NONE ;
unsigned int status = 0 ;
2005-04-17 02:20:36 +04:00
if ( ! ( action - > flags & SA_INTERRUPT ) )
local_irq_enable ( ) ;
do {
ret = action - > handler ( irq , action - > dev_id , regs ) ;
if ( ret = = IRQ_HANDLED )
status | = action - > flags ;
retval | = ret ;
action = action - > next ;
} while ( action ) ;
if ( status & SA_SAMPLE_RANDOM )
add_interrupt_randomness ( irq ) ;
local_irq_disable ( ) ;
return retval ;
}
/*
* do_IRQ handles all normal device IRQ ' s ( the special
* SMP cross - CPU interrupts have their own specific
* handlers ) .
*/
fastcall unsigned int __do_IRQ ( unsigned int irq , struct pt_regs * regs )
{
irq_desc_t * desc = irq_desc + irq ;
struct irqaction * action ;
unsigned int status ;
kstat_this_cpu . irqs [ irq ] + + ;
2005-09-07 02:17:25 +04:00
if ( CHECK_IRQ_PER_CPU ( desc - > status ) ) {
2005-04-17 02:20:36 +04:00
irqreturn_t action_ret ;
/*
* No locking required for CPU - local interrupts :
*/
2005-09-28 14:29:44 +04:00
if ( desc - > handler - > ack )
desc - > handler - > ack ( irq ) ;
2005-04-17 02:20:36 +04:00
action_ret = handle_IRQ_event ( irq , regs , desc - > action ) ;
desc - > handler - > end ( irq ) ;
return 1 ;
}
spin_lock ( & desc - > lock ) ;
2005-09-28 14:29:44 +04:00
if ( desc - > handler - > ack )
desc - > handler - > ack ( irq ) ;
2005-04-17 02:20:36 +04:00
/*
* REPLAY is when Linux resends an IRQ that was dropped earlier
* WAITING is used by probe to mark irqs that are being tested
*/
status = desc - > status & ~ ( IRQ_REPLAY | IRQ_WAITING ) ;
status | = IRQ_PENDING ; /* we _want_ to handle it */
/*
* If the IRQ is disabled for whatever reason , we cannot
* use the action we have .
*/
action = NULL ;
if ( likely ( ! ( status & ( IRQ_DISABLED | IRQ_INPROGRESS ) ) ) ) {
action = desc - > action ;
status & = ~ IRQ_PENDING ; /* we commit to handling */
status | = IRQ_INPROGRESS ; /* we are handling it */
}
desc - > status = status ;
/*
* If there is no IRQ handler or it was disabled , exit early .
* Since we set PENDING , if another processor is handling
* a different instance of this same irq , the other processor
* will take care of it .
*/
if ( unlikely ( ! action ) )
goto out ;
/*
* Edge triggered interrupts need to remember
* pending events .
* This applies to any hw interrupts that allow a second
* instance of the same irq to arrive while we are in do_IRQ
* or in the handler . But the code here only handles the _second_
* instance of the irq , not the third or fourth . So it is mostly
* useful for irq hardware that does not mask cleanly in an
* SMP environment .
*/
for ( ; ; ) {
irqreturn_t action_ret ;
spin_unlock ( & desc - > lock ) ;
action_ret = handle_IRQ_event ( irq , regs , action ) ;
spin_lock ( & desc - > lock ) ;
if ( ! noirqdebug )
2005-06-29 07:45:18 +04:00
note_interrupt ( irq , desc , action_ret , regs ) ;
2005-04-17 02:20:36 +04:00
if ( likely ( ! ( desc - > status & IRQ_PENDING ) ) )
break ;
desc - > status & = ~ IRQ_PENDING ;
}
desc - > status & = ~ IRQ_INPROGRESS ;
out :
/*
* The - > end ( ) handler has to deal with interrupts which got
* disabled while the handler was running .
*/
desc - > handler - > end ( irq ) ;
spin_unlock ( & desc - > lock ) ;
return 1 ;
}