2005-04-16 15:20:36 -07:00
/*
* ip27 - irq . c : Highlevel interrupt handling for IP27 architecture .
*
* Copyright ( C ) 1999 , 2000 Ralf Baechle ( ralf @ gnu . org )
* Copyright ( C ) 1999 , 2000 Silicon Graphics , Inc .
* Copyright ( C ) 1999 - 2001 Kanoj Sarcar
*/
2005-08-08 12:42:26 +00:00
# undef DEBUG
2005-04-16 15:20:36 -07:00
# include <linux/init.h>
# include <linux/irq.h>
# include <linux/errno.h>
# include <linux/signal.h>
# include <linux/sched.h>
# include <linux/types.h>
# include <linux/interrupt.h>
# include <linux/ioport.h>
# include <linux/timex.h>
2009-06-19 14:05:26 +01:00
# include <linux/smp.h>
2005-04-16 15:20:36 -07:00
# include <linux/random.h>
2005-08-08 12:42:26 +00:00
# include <linux/kernel.h>
2005-04-16 15:20:36 -07:00
# include <linux/kernel_stat.h>
# include <linux/delay.h>
# include <linux/bitops.h>
# include <asm/bootinfo.h>
# include <asm/io.h>
# include <asm/mipsregs.h>
# include <asm/processor.h>
# include <asm/sn/addrs.h>
# include <asm/sn/agent.h>
# include <asm/sn/arch.h>
# include <asm/sn/hub.h>
# include <asm/sn/intr.h>
/*
* Linux has a controller - independent x86 interrupt architecture .
* every controller has a ' controller - template ' , that is used
* by the main code to do the right thing . Each driver - visible
2011-03-30 22:57:33 -03:00
* interrupt source is transparently wired to the appropriate
2005-04-16 15:20:36 -07:00
* controller . Thus drivers need not be aware of the
* interrupt - controller .
*
* Various interrupt controllers we handle : 8259 PIC , SMP IO - APIC ,
* PIIX4 ' s internal 8259 PIC and SGI ' s Visual Workstation Cobalt ( IO - ) APIC .
* ( IO - APICs assumed to be messaging to Pentium local - APICs )
*
* the code is designed to be easily extended with new / different
* interrupt controllers , without having to do assembly magic .
*/
extern asmlinkage void ip27_irq ( void ) ;
/*
* Find first bit set
*/
static int ms1bit ( unsigned long x )
{
int b = 0 , s ;
s = 16 ; if ( x > > 16 = = 0 ) s = 0 ; b + = s ; x > > = s ;
s = 8 ; if ( x > > 8 = = 0 ) s = 0 ; b + = s ; x > > = s ;
s = 4 ; if ( x > > 4 = = 0 ) s = 0 ; b + = s ; x > > = s ;
s = 2 ; if ( x > > 2 = = 0 ) s = 0 ; b + = s ; x > > = s ;
s = 1 ; if ( x > > 1 = = 0 ) s = 0 ; b + = s ;
return b ;
}
/*
2011-11-22 14:38:03 +00:00
* This code is unnecessarily complex , because we do
2005-04-16 15:20:36 -07:00
* intr enabling . Basically , once we grab the set of intrs we need
* to service , we must mask _all_ these interrupts ; firstly , to make
* sure the same intr does not intr again , causing recursion that
* can lead to stack overflow . Secondly , we can not just mask the
* one intr we are do_IRQing , because the non - masked intrs in the
* first set might intr again , causing multiple servicings of the
* same intr . This effect is mostly seen for intercpu intrs .
* Kanoj 05.13 .00
*/
2006-10-07 19:44:33 +01:00
static void ip27_do_irq_mask0 ( void )
2005-04-16 15:20:36 -07:00
{
int irq , swlevel ;
hubreg_t pend0 , mask0 ;
cpuid_t cpu = smp_processor_id ( ) ;
int pi_int_mask0 =
( cputoslice ( cpu ) = = 0 ) ? PI_INT_MASK0_A : PI_INT_MASK0_B ;
/* copied from Irix intpend0() */
pend0 = LOCAL_HUB_L ( PI_INT_PEND0 ) ;
mask0 = LOCAL_HUB_L ( pi_int_mask0 ) ;
pend0 & = mask0 ; /* Pick intrs we should look at */
if ( ! pend0 )
return ;
swlevel = ms1bit ( pend0 ) ;
# ifdef CONFIG_SMP
if ( pend0 & ( 1UL < < CPU_RESCHED_A_IRQ ) ) {
LOCAL_HUB_CLR_INTR ( CPU_RESCHED_A_IRQ ) ;
2011-04-05 17:23:39 +02:00
scheduler_ipi ( ) ;
2005-04-16 15:20:36 -07:00
} else if ( pend0 & ( 1UL < < CPU_RESCHED_B_IRQ ) ) {
LOCAL_HUB_CLR_INTR ( CPU_RESCHED_B_IRQ ) ;
2011-04-05 17:23:39 +02:00
scheduler_ipi ( ) ;
2005-04-16 15:20:36 -07:00
} else if ( pend0 & ( 1UL < < CPU_CALL_A_IRQ ) ) {
LOCAL_HUB_CLR_INTR ( CPU_CALL_A_IRQ ) ;
smp_call_function_interrupt ( ) ;
} else if ( pend0 & ( 1UL < < CPU_CALL_B_IRQ ) ) {
LOCAL_HUB_CLR_INTR ( CPU_CALL_B_IRQ ) ;
smp_call_function_interrupt ( ) ;
} else
# endif
{
/* "map" swlevel to irq */
struct slice_data * si = cpu_data [ cpu ] . data ;
irq = si - > level_to_irq [ swlevel ] ;
2006-10-07 19:44:33 +01:00
do_IRQ ( irq ) ;
2005-04-16 15:20:36 -07:00
}
LOCAL_HUB_L ( PI_INT_PEND0 ) ;
}
2006-10-07 19:44:33 +01:00
static void ip27_do_irq_mask1 ( void )
2005-04-16 15:20:36 -07:00
{
int irq , swlevel ;
hubreg_t pend1 , mask1 ;
cpuid_t cpu = smp_processor_id ( ) ;
int pi_int_mask1 = ( cputoslice ( cpu ) = = 0 ) ? PI_INT_MASK1_A : PI_INT_MASK1_B ;
struct slice_data * si = cpu_data [ cpu ] . data ;
/* copied from Irix intpend0() */
pend1 = LOCAL_HUB_L ( PI_INT_PEND1 ) ;
mask1 = LOCAL_HUB_L ( pi_int_mask1 ) ;
pend1 & = mask1 ; /* Pick intrs we should look at */
if ( ! pend1 )
return ;
swlevel = ms1bit ( pend1 ) ;
/* "map" swlevel to irq */
irq = si - > level_to_irq [ swlevel ] ;
LOCAL_HUB_CLR_INTR ( swlevel ) ;
2006-10-07 19:44:33 +01:00
do_IRQ ( irq ) ;
2005-04-16 15:20:36 -07:00
LOCAL_HUB_L ( PI_INT_PEND1 ) ;
}
2006-10-07 19:44:33 +01:00
static void ip27_prof_timer ( void )
2005-04-16 15:20:36 -07:00
{
panic ( " CPU %d got a profiling interrupt " , smp_processor_id ( ) ) ;
}
2006-10-07 19:44:33 +01:00
static void ip27_hub_error ( void )
2005-04-16 15:20:36 -07:00
{
panic ( " CPU %d got a hub error interrupt " , smp_processor_id ( ) ) ;
}
2006-10-07 19:44:33 +01:00
asmlinkage void plat_irq_dispatch ( void )
2006-04-03 17:56:36 +01:00
{
unsigned long pending = read_c0_cause ( ) & read_c0_status ( ) ;
2007-10-18 13:34:12 +01:00
extern unsigned int rt_timer_irq ;
2006-04-03 17:56:36 +01:00
if ( pending & CAUSEF_IP4 )
2007-10-18 13:34:12 +01:00
do_IRQ ( rt_timer_irq ) ;
2006-04-03 17:56:36 +01:00
else if ( pending & CAUSEF_IP2 ) /* PI_INT_PEND_0 or CC_PEND_ { A | B } */
2006-10-07 19:44:33 +01:00
ip27_do_irq_mask0 ( ) ;
2006-04-03 17:56:36 +01:00
else if ( pending & CAUSEF_IP3 ) /* PI_INT_PEND_1 */
2006-10-07 19:44:33 +01:00
ip27_do_irq_mask1 ( ) ;
2006-04-03 17:56:36 +01:00
else if ( pending & CAUSEF_IP5 )
2006-10-07 19:44:33 +01:00
ip27_prof_timer ( ) ;
2006-04-03 17:56:36 +01:00
else if ( pending & CAUSEF_IP6 )
2006-10-07 19:44:33 +01:00
ip27_hub_error ( ) ;
2006-04-03 17:56:36 +01:00
}
2005-04-16 15:20:36 -07:00
void __init arch_init_irq ( void )
{
}
void install_ipi ( void )
{
int slice = LOCAL_HUB_L ( PI_CPU_NUM ) ;
int cpu = smp_processor_id ( ) ;
struct slice_data * si = cpu_data [ cpu ] . data ;
2005-03-21 18:59:38 +00:00
struct hub_data * hub = hub_data ( cpu_to_node ( cpu ) ) ;
int resched , call ;
resched = CPU_RESCHED_A_IRQ + slice ;
__set_bit ( resched , hub - > irq_alloc_mask ) ;
__set_bit ( resched , si - > irq_enable_mask ) ;
LOCAL_HUB_CLR_INTR ( resched ) ;
call = CPU_CALL_A_IRQ + slice ;
__set_bit ( call , hub - > irq_alloc_mask ) ;
__set_bit ( call , si - > irq_enable_mask ) ;
LOCAL_HUB_CLR_INTR ( call ) ;
2005-04-16 15:20:36 -07:00
if ( slice = = 0 ) {
2005-03-21 18:59:38 +00:00
LOCAL_HUB_S ( PI_INT_MASK0_A , si - > irq_enable_mask [ 0 ] ) ;
LOCAL_HUB_S ( PI_INT_MASK1_A , si - > irq_enable_mask [ 1 ] ) ;
2005-04-16 15:20:36 -07:00
} else {
2005-03-21 18:59:38 +00:00
LOCAL_HUB_S ( PI_INT_MASK0_B , si - > irq_enable_mask [ 0 ] ) ;
LOCAL_HUB_S ( PI_INT_MASK1_B , si - > irq_enable_mask [ 1 ] ) ;
2005-04-16 15:20:36 -07:00
}
}