2005-04-16 15:20:36 -07: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 19:39:45 +01: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-16 15:20:36 -07: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-01 22:30:09 +01:00
# include <linux/irq.h>
2005-04-16 15:20:36 -07:00
# 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>
2010-10-07 20:51:58 +05:30
# include <linux/ftrace.h>
2005-04-16 15:20:36 -07:00
# include <asm/system.h>
2010-12-20 10:18:36 +00:00
# include <asm/mach/arch.h>
2008-08-03 15:04:04 +01:00
# include <asm/mach/irq.h>
2005-06-25 19:39:45 +01:00
# include <asm/mach/time.h>
2005-04-16 15:20:36 -07: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-01 22:30:09 +01:00
unsigned long irq_err_count ;
2005-04-16 15:20:36 -07:00
int show_interrupts ( struct seq_file * p , void * v )
{
int i = * ( loff_t * ) v , cpu ;
2010-06-25 09:46:09 +01:00
struct irq_desc * desc ;
2005-04-16 15:20:36 -07:00
struct irqaction * action ;
unsigned long flags ;
2010-11-15 14:33:51 +00:00
int prec , n ;
for ( prec = 3 , n = 1000 ; prec < 10 & & n < = nr_irqs ; prec + + )
n * = 10 ;
2005-04-16 15:20:36 -07:00
2010-11-15 14:40:29 +00:00
# ifdef CONFIG_SMP
if ( prec < 4 )
prec = 4 ;
# endif
2005-04-16 15:20:36 -07:00
if ( i = = 0 ) {
char cpuname [ 12 ] ;
2010-11-15 14:33:51 +00:00
seq_printf ( p , " %*s " , prec , " " ) ;
2005-04-16 15:20:36 -07:00
for_each_present_cpu ( cpu ) {
sprintf ( cpuname , " CPU%d " , cpu ) ;
seq_printf ( p , " %10s " , cpuname ) ;
}
seq_putc ( p , ' \n ' ) ;
}
2010-06-25 09:46:09 +01:00
if ( i < nr_irqs ) {
desc = irq_to_desc ( i ) ;
raw_spin_lock_irqsave ( & desc - > lock , flags ) ;
action = desc - > action ;
2005-04-16 15:20:36 -07:00
if ( ! action )
goto unlock ;
2010-11-15 14:33:51 +00:00
seq_printf ( p , " %*d: " , prec , i ) ;
2005-04-16 15:20:36 -07:00
for_each_present_cpu ( cpu )
2009-01-11 13:35:56 -08:00
seq_printf ( p , " %10u " , kstat_irqs_cpu ( i , cpu ) ) ;
2010-11-29 10:21:48 +01:00
seq_printf ( p , " %10s " , desc - > irq_data . chip - > name ? : " - " ) ;
2005-04-16 15:20:36 -07: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 :
2010-06-25 09:46:09 +01:00
raw_spin_unlock_irqrestore ( & desc - > lock , flags ) ;
} else if ( i = = nr_irqs ) {
2009-08-03 15:11:29 +01:00
# ifdef CONFIG_FIQ
2010-11-15 14:33:51 +00:00
show_fiq_list ( p , prec ) ;
2005-04-16 15:20:36 -07:00
# endif
# ifdef CONFIG_SMP
2010-11-15 14:33:51 +00:00
show_ipi_list ( p , prec ) ;
2010-11-15 13:38:06 +00:00
# endif
# ifdef CONFIG_LOCAL_TIMERS
2010-11-15 14:33:51 +00:00
show_local_irqs ( p , prec ) ;
2005-04-16 15:20:36 -07:00
# endif
2010-11-15 14:33:51 +00:00
seq_printf ( p , " %*s: %10lu \n " , prec , " Err " , irq_err_count ) ;
2005-04-16 15:20:36 -07:00
}
return 0 ;
}
/*
* do_IRQ handles all hardware IRQ ' s . Decoded IRQs should not
* come via this function . Instead , they should provide their
* own ' handler '
*/
2010-10-07 20:51:58 +05:30
asmlinkage void __exception_irq_entry
asm_do_IRQ ( unsigned int irq , struct pt_regs * regs )
2005-04-16 15:20:36 -07:00
{
2006-10-06 13:11:15 -07:00
struct pt_regs * old_regs = set_irq_regs ( regs ) ;
2008-10-09 13:36:24 +01:00
irq_enter ( ) ;
2005-04-16 15:20:36 -07:00
/*
* Some hardware gives randomly wrong interrupts . Rather
* than crashing , do something sensible .
*/
2010-06-25 09:46:09 +01:00
if ( unlikely ( irq > = nr_irqs ) ) {
2009-06-22 09:23:36 +01:00
if ( printk_ratelimit ( ) )
printk ( KERN_WARNING " Bad IRQ%u \n " , irq ) ;
ack_bad_irq ( irq ) ;
} else {
2008-10-09 13:36:24 +01:00
generic_handle_irq ( irq ) ;
2009-06-22 09:23:36 +01:00
}
2005-04-16 15:20:36 -07:00
2006-07-01 22:30:09 +01:00
/* AT91 specific workaround */
2005-04-16 15:20:36 -07:00
irq_finish ( irq ) ;
irq_exit ( ) ;
2006-10-06 13:11:15 -07:00
set_irq_regs ( old_regs ) ;
2005-04-16 15:20:36 -07:00
}
void set_irq_flags ( unsigned int irq , unsigned int iflags )
{
2006-11-23 11:41:32 +00:00
struct irq_desc * desc ;
2005-04-16 15:20:36 -07:00
unsigned long flags ;
2010-06-25 09:46:09 +01:00
if ( irq > = nr_irqs ) {
2005-04-16 15:20:36 -07:00
printk ( KERN_ERR " Trying to set irq flags for IRQ%d \n " , irq ) ;
return ;
}
2010-06-25 09:46:09 +01:00
desc = irq_to_desc ( irq ) ;
2009-11-17 16:46:45 +01:00
raw_spin_lock_irqsave ( & desc - > lock , flags ) ;
2006-07-01 22:30:09 +01:00
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 ;
2009-11-17 16:46:45 +01:00
raw_spin_unlock_irqrestore ( & desc - > lock , flags ) ;
2005-04-16 15:20:36 -07:00
}
void __init init_IRQ ( void )
{
2010-12-20 10:18:36 +00:00
machine_desc - > init_irq ( ) ;
2005-04-16 15:20:36 -07:00
}
2010-06-25 09:46:09 +01:00
# ifdef CONFIG_SPARSE_IRQ
int __init arch_probe_nr_irqs ( void )
{
2010-12-20 10:18:36 +00:00
nr_irqs = machine_desc - > nr_irqs ? machine_desc - > nr_irqs : NR_IRQS ;
2010-09-27 20:55:03 +02:00
return nr_irqs ;
2010-06-25 09:46:09 +01:00
}
# endif
2005-11-02 22:24:33 +00:00
# ifdef CONFIG_HOTPLUG_CPU
2006-07-11 22:54:34 +01:00
2006-11-23 11:41:32 +00:00
static void route_irq ( struct irq_desc * desc , unsigned int irq , unsigned int cpu )
2006-07-11 22:54:34 +01:00
{
2010-11-29 10:21:48 +01:00
pr_debug ( " IRQ%u: moving from cpu%u to cpu%u \n " , irq , desc - > irq_data . node , cpu ) ;
2006-07-11 22:54:34 +01:00
2009-11-17 16:46:45 +01:00
raw_spin_lock_irq ( & desc - > lock ) ;
2010-11-29 10:21:48 +01:00
desc - > irq_data . chip - > irq_set_affinity ( & desc - > irq_data ,
2011-01-23 12:08:16 +00:00
cpumask_of ( cpu ) , true ) ;
2009-11-17 16:46:45 +01:00
raw_spin_unlock_irq ( & desc - > lock ) ;
2006-07-11 22:54:34 +01:00
}
2005-11-02 22:24:33 +00: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 ( ) ;
2010-06-25 09:46:09 +01:00
struct irq_desc * desc ;
2005-11-02 22:24:33 +00:00
2010-06-25 09:46:09 +01:00
for_each_irq_desc ( i , desc ) {
2010-11-29 10:21:48 +01:00
struct irq_data * d = & desc - > irq_data ;
if ( d - > node = = cpu ) {
unsigned int newcpu = cpumask_any_and ( d - > affinity ,
2009-01-12 15:27:13 -08:00
cpu_online_mask ) ;
if ( newcpu > = nr_cpu_ids ) {
2005-11-02 22:24:33 +00:00
if ( printk_ratelimit ( ) )
printk ( KERN_INFO " IRQ%u no longer affine to CPU%u \n " ,
i , cpu ) ;
2010-11-29 10:21:48 +01:00
cpumask_setall ( d - > affinity ) ;
newcpu = cpumask_any_and ( d - > affinity ,
2009-01-12 15:27:13 -08:00
cpu_online_mask ) ;
2005-11-02 22:24:33 +00:00
}
route_irq ( desc , i , newcpu ) ;
}
}
}
# endif /* CONFIG_HOTPLUG_CPU */