2005-04-17 02:20:36 +04:00
/*
* linux / arch / arm / kernel / irq . c
*
* Copyright ( C ) 1992 Linus Torvalds
* Modifications for ARM processor Copyright ( C ) 1995 - 2000 Russell King .
*
2005-06-25 22:39:45 +04:00
* Support for Dynamic Tick Timer Copyright ( C ) 2004 - 2005 Nokia Corporation .
* Dynamic Tick Timer written by Tony Lindgren < tony @ atomide . com > and
* Tuukka Tikkanen < tuukka . tikkanen @ elektrobit . com > .
*
2005-04-17 02:20:36 +04:00
* This program is free software ; you can redistribute it and / or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation .
*
* This file contains the code used by various IRQ handling routines :
* asking for different IRQ ' s should be done through these routines
* instead of just grabbing them . Thus setups with different IRQ numbers
* shouldn ' t result in any weird surprises , and installing new handlers
* should be easier .
*
* IRQ ' s are in fact implemented a bit like signal handlers for the kernel .
* Naturally it ' s not a 1 : 1 relation , but there are similarities .
*/
# include <linux/kernel_stat.h>
# include <linux/module.h>
# include <linux/signal.h>
# include <linux/ioport.h>
# include <linux/interrupt.h>
2006-07-02 01:30:09 +04:00
# include <linux/irq.h>
2005-04-17 02:20:36 +04:00
# include <linux/slab.h>
# include <linux/random.h>
# include <linux/smp.h>
# include <linux/init.h>
# include <linux/seq_file.h>
# include <linux/errno.h>
# include <linux/list.h>
# include <linux/kallsyms.h>
# include <linux/proc_fs.h>
# include <asm/system.h>
2008-08-03 18:04:04 +04:00
# include <asm/mach/irq.h>
2005-06-25 22:39:45 +04:00
# include <asm/mach/time.h>
2005-04-17 02:20:36 +04:00
/*
* No architecture - specific irq_finish function defined in arm / arch / irqs . h .
*/
# ifndef irq_finish
# define irq_finish(irq) do { } while (0)
# endif
2006-07-02 01:30:09 +04:00
void ( * init_arch_irq ) ( void ) __initdata = NULL ;
unsigned long irq_err_count ;
2005-04-17 02:20:36 +04:00
int show_interrupts ( struct seq_file * p , void * v )
{
int i = * ( loff_t * ) v , cpu ;
struct irqaction * action ;
unsigned long flags ;
if ( i = = 0 ) {
char cpuname [ 12 ] ;
seq_printf ( p , " " ) ;
for_each_present_cpu ( cpu ) {
sprintf ( cpuname , " CPU%d " , cpu ) ;
seq_printf ( p , " %10s " , cpuname ) ;
}
seq_putc ( p , ' \n ' ) ;
}
if ( i < NR_IRQS ) {
2006-07-02 01:30:09 +04:00
spin_lock_irqsave ( & irq_desc [ i ] . lock , flags ) ;
action = irq_desc [ i ] . action ;
2005-04-17 02:20:36 +04:00
if ( ! action )
goto unlock ;
seq_printf ( p , " %3d: " , i ) ;
for_each_present_cpu ( cpu )
2009-01-12 00:35:56 +03:00
seq_printf ( p , " %10u " , kstat_irqs_cpu ( i , cpu ) ) ;
2006-08-02 01:26:25 +04:00
seq_printf ( p , " %10s " , irq_desc [ i ] . chip - > name ? : " - " ) ;
2005-04-17 02:20:36 +04:00
seq_printf ( p , " %s " , action - > name ) ;
for ( action = action - > next ; action ; action = action - > next )
seq_printf ( p , " , %s " , action - > name ) ;
seq_putc ( p , ' \n ' ) ;
unlock :
2006-07-02 01:30:09 +04:00
spin_unlock_irqrestore ( & irq_desc [ i ] . lock , flags ) ;
2005-04-17 02:20:36 +04:00
} else if ( i = = NR_IRQS ) {
# ifdef CONFIG_ARCH_ACORN
show_fiq_list ( p , v ) ;
# endif
# ifdef CONFIG_SMP
show_ipi_list ( p ) ;
2005-11-08 22:08:05 +03:00
show_local_irqs ( p ) ;
2005-04-17 02:20:36 +04:00
# endif
seq_printf ( p , " Err: %10lu \n " , irq_err_count ) ;
}
return 0 ;
}
/*
* do_IRQ handles all hardware IRQ ' s . Decoded IRQs should not
* come via this function . Instead , they should provide their
* own ' handler '
*/
2007-03-02 18:01:36 +03:00
asmlinkage void __exception asm_do_IRQ ( unsigned int irq , struct pt_regs * regs )
2005-04-17 02:20:36 +04:00
{
2006-10-07 00:11:15 +04:00
struct pt_regs * old_regs = set_irq_regs ( regs ) ;
2008-10-09 16:36:24 +04:00
irq_enter ( ) ;
2005-04-17 02:20:36 +04:00
/*
* Some hardware gives randomly wrong interrupts . Rather
* than crashing , do something sensible .
*/
2009-06-22 12:23:36 +04:00
if ( unlikely ( irq > = NR_IRQS ) ) {
if ( printk_ratelimit ( ) )
printk ( KERN_WARNING " Bad IRQ%u \n " , irq ) ;
ack_bad_irq ( irq ) ;
} else {
2008-10-09 16:36:24 +04:00
generic_handle_irq ( irq ) ;
2009-06-22 12:23:36 +04:00
}
2005-04-17 02:20:36 +04:00
2006-07-02 01:30:09 +04:00
/* AT91 specific workaround */
2005-04-17 02:20:36 +04:00
irq_finish ( irq ) ;
irq_exit ( ) ;
2006-10-07 00:11:15 +04:00
set_irq_regs ( old_regs ) ;
2005-04-17 02:20:36 +04:00
}
void set_irq_flags ( unsigned int irq , unsigned int iflags )
{
2006-11-23 14:41:32 +03:00
struct irq_desc * desc ;
2005-04-17 02:20:36 +04:00
unsigned long flags ;
if ( irq > = NR_IRQS ) {
printk ( KERN_ERR " Trying to set irq flags for IRQ%d \n " , irq ) ;
return ;
}
desc = irq_desc + irq ;
2006-07-02 01:30:09 +04:00
spin_lock_irqsave ( & desc - > lock , flags ) ;
desc - > status | = IRQ_NOREQUEST | IRQ_NOPROBE | IRQ_NOAUTOEN ;
if ( iflags & IRQF_VALID )
desc - > status & = ~ IRQ_NOREQUEST ;
if ( iflags & IRQF_PROBE )
desc - > status & = ~ IRQ_NOPROBE ;
if ( ! ( iflags & IRQF_NOAUTOEN ) )
desc - > status & = ~ IRQ_NOAUTOEN ;
spin_unlock_irqrestore ( & desc - > lock , flags ) ;
2005-04-17 02:20:36 +04:00
}
void __init init_IRQ ( void )
{
int irq ;
2006-07-02 01:30:09 +04:00
for ( irq = 0 ; irq < NR_IRQS ; irq + + )
2007-02-16 12:28:24 +03:00
irq_desc [ irq ] . status | = IRQ_NOREQUEST | IRQ_NOPROBE ;
2006-07-02 01:30:09 +04:00
2005-04-17 02:20:36 +04:00
init_arch_irq ( ) ;
}
2005-11-03 01:24:33 +03:00
# ifdef CONFIG_HOTPLUG_CPU
2006-07-12 01:54:34 +04:00
2006-11-23 14:41:32 +03:00
static void route_irq ( struct irq_desc * desc , unsigned int irq , unsigned int cpu )
2006-07-12 01:54:34 +04:00
{
2009-06-19 14:30:12 +04:00
pr_debug ( " IRQ%u: moving from cpu%u to cpu%u \n " , irq , desc - > node , cpu ) ;
2006-07-12 01:54:34 +04:00
spin_lock_irq ( & desc - > lock ) ;
2008-12-13 13:50:26 +03:00
desc - > chip - > set_affinity ( irq , cpumask_of ( cpu ) ) ;
2006-07-12 01:54:34 +04:00
spin_unlock_irq ( & desc - > lock ) ;
}
2005-11-03 01:24:33 +03:00
/*
* The CPU has been marked offline . Migrate IRQs off this CPU . If
* the affinity settings do not allow other CPUs , force them onto any
* available CPU .
*/
void migrate_irqs ( void )
{
unsigned int i , cpu = smp_processor_id ( ) ;
for ( i = 0 ; i < NR_IRQS ; i + + ) {
2006-11-23 14:41:32 +03:00
struct irq_desc * desc = irq_desc + i ;
2005-11-03 01:24:33 +03:00
2009-06-19 14:30:12 +04:00
if ( desc - > node = = cpu ) {
2009-01-13 02:27:13 +03:00
unsigned int newcpu = cpumask_any_and ( desc - > affinity ,
cpu_online_mask ) ;
if ( newcpu > = nr_cpu_ids ) {
2005-11-03 01:24:33 +03:00
if ( printk_ratelimit ( ) )
printk ( KERN_INFO " IRQ%u no longer affine to CPU%u \n " ,
i , cpu ) ;
2009-01-13 02:27:13 +03:00
cpumask_setall ( desc - > affinity ) ;
newcpu = cpumask_any_and ( desc - > affinity ,
cpu_online_mask ) ;
2005-11-03 01:24:33 +03:00
}
route_irq ( desc , i , newcpu ) ;
}
}
}
# endif /* CONFIG_HOTPLUG_CPU */