2005-04-17 02:20:36 +04:00
/*
* ip22 - int . c : Routines for generic manipulation of the INT [ 23 ] ASIC
* found on INDY and Indigo2 workstations .
*
2011-04-05 01:15:29 +04:00
* Copyright ( C ) 1996 David S . Miller ( davem @ davemloft . net )
2005-04-17 02:20:36 +04:00
* Copyright ( C ) 1997 , 1998 Ralf Baechle ( ralf @ gnu . org )
* Copyright ( C ) 1999 Andrew R . Baker ( andrewb @ uab . edu )
* - Indigo2 changes
* - Interrupt handling fixes
* Copyright ( C ) 2001 , 2003 Ladislav Michl ( ladis @ linux - mips . org )
*/
# include <linux/types.h>
# include <linux/init.h>
# include <linux/kernel_stat.h>
# include <linux/interrupt.h>
2009-11-20 15:34:33 +03:00
# include <linux/ftrace.h>
2005-04-17 02:20:36 +04:00
2007-01-07 20:14:29 +03:00
# include <asm/irq_cpu.h>
2005-04-17 02:20:36 +04:00
# include <asm/sgi/hpc3.h>
# include <asm/sgi/ip22.h>
/* So far nothing hangs here */
2005-09-04 02:56:17 +04:00
# undef USE_LIO3_IRQ
2005-04-17 02:20:36 +04:00
struct sgint_regs * sgint ;
static char lc0msk_to_irqnr [ 256 ] ;
static char lc1msk_to_irqnr [ 256 ] ;
static char lc2msk_to_irqnr [ 256 ] ;
static char lc3msk_to_irqnr [ 256 ] ;
extern int ip22_eisa_init ( void ) ;
2011-03-24 00:09:11 +03:00
static void enable_local0_irq ( struct irq_data * d )
2005-04-17 02:20:36 +04:00
{
/* don't allow mappable interrupt to be enabled from setup_irq,
* we have our own way to do so */
2011-03-24 00:09:11 +03:00
if ( d - > irq ! = SGI_MAP_0_IRQ )
sgint - > imask0 | = ( 1 < < ( d - > irq - SGINT_LOCAL0 ) ) ;
2005-04-17 02:20:36 +04:00
}
2011-03-24 00:09:11 +03:00
static void disable_local0_irq ( struct irq_data * d )
2005-04-17 02:20:36 +04:00
{
2011-03-24 00:09:11 +03:00
sgint - > imask0 & = ~ ( 1 < < ( d - > irq - SGINT_LOCAL0 ) ) ;
2005-04-17 02:20:36 +04:00
}
2006-07-02 17:41:42 +04:00
static struct irq_chip ip22_local0_irq_type = {
2007-01-14 18:07:25 +03:00
. name = " IP22 local 0 " ,
2011-03-24 00:09:11 +03:00
. irq_mask = disable_local0_irq ,
. irq_unmask = enable_local0_irq ,
2005-04-17 02:20:36 +04:00
} ;
2011-03-24 00:09:11 +03:00
static void enable_local1_irq ( struct irq_data * d )
2005-04-17 02:20:36 +04:00
{
/* don't allow mappable interrupt to be enabled from setup_irq,
* we have our own way to do so */
2011-03-24 00:09:11 +03:00
if ( d - > irq ! = SGI_MAP_1_IRQ )
sgint - > imask1 | = ( 1 < < ( d - > irq - SGINT_LOCAL1 ) ) ;
2005-04-17 02:20:36 +04:00
}
2011-03-24 00:09:11 +03:00
static void disable_local1_irq ( struct irq_data * d )
2005-04-17 02:20:36 +04:00
{
2011-03-24 00:09:11 +03:00
sgint - > imask1 & = ~ ( 1 < < ( d - > irq - SGINT_LOCAL1 ) ) ;
2005-04-17 02:20:36 +04:00
}
2006-07-02 17:41:42 +04:00
static struct irq_chip ip22_local1_irq_type = {
2007-01-14 18:07:25 +03:00
. name = " IP22 local 1 " ,
2011-03-24 00:09:11 +03:00
. irq_mask = disable_local1_irq ,
. irq_unmask = enable_local1_irq ,
2005-04-17 02:20:36 +04:00
} ;
2011-03-24 00:09:11 +03:00
static void enable_local2_irq ( struct irq_data * d )
2005-04-17 02:20:36 +04:00
{
sgint - > imask0 | = ( 1 < < ( SGI_MAP_0_IRQ - SGINT_LOCAL0 ) ) ;
2011-03-24 00:09:11 +03:00
sgint - > cmeimask0 | = ( 1 < < ( d - > irq - SGINT_LOCAL2 ) ) ;
2005-04-17 02:20:36 +04:00
}
2011-03-24 00:09:11 +03:00
static void disable_local2_irq ( struct irq_data * d )
2005-04-17 02:20:36 +04:00
{
2011-03-24 00:09:11 +03:00
sgint - > cmeimask0 & = ~ ( 1 < < ( d - > irq - SGINT_LOCAL2 ) ) ;
2005-04-17 02:20:36 +04:00
if ( ! sgint - > cmeimask0 )
sgint - > imask0 & = ~ ( 1 < < ( SGI_MAP_0_IRQ - SGINT_LOCAL0 ) ) ;
}
2006-07-02 17:41:42 +04:00
static struct irq_chip ip22_local2_irq_type = {
2007-01-14 18:07:25 +03:00
. name = " IP22 local 2 " ,
2011-03-24 00:09:11 +03:00
. irq_mask = disable_local2_irq ,
. irq_unmask = enable_local2_irq ,
2005-04-17 02:20:36 +04:00
} ;
2011-03-24 00:09:11 +03:00
static void enable_local3_irq ( struct irq_data * d )
2005-04-17 02:20:36 +04:00
{
sgint - > imask1 | = ( 1 < < ( SGI_MAP_1_IRQ - SGINT_LOCAL1 ) ) ;
2011-03-24 00:09:11 +03:00
sgint - > cmeimask1 | = ( 1 < < ( d - > irq - SGINT_LOCAL3 ) ) ;
2005-04-17 02:20:36 +04:00
}
2011-03-24 00:09:11 +03:00
static void disable_local3_irq ( struct irq_data * d )
2005-04-17 02:20:36 +04:00
{
2011-03-24 00:09:11 +03:00
sgint - > cmeimask1 & = ~ ( 1 < < ( d - > irq - SGINT_LOCAL3 ) ) ;
2005-04-17 02:20:36 +04:00
if ( ! sgint - > cmeimask1 )
sgint - > imask1 & = ~ ( 1 < < ( SGI_MAP_1_IRQ - SGINT_LOCAL1 ) ) ;
}
2006-07-02 17:41:42 +04:00
static struct irq_chip ip22_local3_irq_type = {
2007-01-14 18:07:25 +03:00
. name = " IP22 local 3 " ,
2011-03-24 00:09:11 +03:00
. irq_mask = disable_local3_irq ,
. irq_unmask = enable_local3_irq ,
2005-04-17 02:20:36 +04:00
} ;
2006-10-07 22:44:33 +04:00
static void indy_local0_irqdispatch ( void )
2005-04-17 02:20:36 +04:00
{
u8 mask = sgint - > istat0 & sgint - > imask0 ;
u8 mask2 ;
int irq ;
if ( mask & SGINT_ISTAT0_LIO2 ) {
mask2 = sgint - > vmeistat & sgint - > cmeimask0 ;
irq = lc2msk_to_irqnr [ mask2 ] ;
} else
irq = lc0msk_to_irqnr [ mask ] ;
/* if irq == 0, then the interrupt has already been cleared */
if ( irq )
2006-10-07 22:44:33 +04:00
do_IRQ ( irq ) ;
2005-04-17 02:20:36 +04:00
}
2006-10-07 22:44:33 +04:00
static void indy_local1_irqdispatch ( void )
2005-04-17 02:20:36 +04:00
{
u8 mask = sgint - > istat1 & sgint - > imask1 ;
u8 mask2 ;
int irq ;
if ( mask & SGINT_ISTAT1_LIO3 ) {
mask2 = sgint - > vmeistat & sgint - > cmeimask1 ;
irq = lc3msk_to_irqnr [ mask2 ] ;
} else
irq = lc1msk_to_irqnr [ mask ] ;
/* if irq == 0, then the interrupt has already been cleared */
if ( irq )
2006-10-07 22:44:33 +04:00
do_IRQ ( irq ) ;
2005-04-17 02:20:36 +04:00
}
2006-10-07 22:44:33 +04:00
extern void ip22_be_interrupt ( int irq ) ;
2005-04-17 02:20:36 +04:00
2009-11-20 15:34:33 +03:00
static void __irq_entry indy_buserror_irq ( void )
2005-04-17 02:20:36 +04:00
{
int irq = SGI_BUSERR_IRQ ;
irq_enter ( ) ;
2009-01-15 02:43:54 +03:00
kstat_incr_irqs_this_cpu ( irq , irq_to_desc ( irq ) ) ;
2006-10-07 22:44:33 +04:00
ip22_be_interrupt ( irq ) ;
2005-04-17 02:20:36 +04:00
irq_exit ( ) ;
}
2005-09-04 02:56:17 +04:00
static struct irqaction local0_cascade = {
2005-04-17 02:20:36 +04:00
. handler = no_action ,
2011-11-22 18:38:03 +04:00
. flags = IRQF_NO_THREAD ,
2005-04-17 02:20:36 +04:00
. name = " local0 cascade " ,
} ;
2005-09-04 02:56:17 +04:00
static struct irqaction local1_cascade = {
2005-04-17 02:20:36 +04:00
. handler = no_action ,
2011-11-22 18:38:03 +04:00
. flags = IRQF_NO_THREAD ,
2005-04-17 02:20:36 +04:00
. name = " local1 cascade " ,
} ;
2005-09-04 02:56:17 +04:00
static struct irqaction buserr = {
2005-04-17 02:20:36 +04:00
. handler = no_action ,
2011-11-22 18:38:03 +04:00
. flags = IRQF_NO_THREAD ,
2005-04-17 02:20:36 +04:00
. name = " Bus Error " ,
} ;
2005-09-04 02:56:17 +04:00
static struct irqaction map0_cascade = {
2005-04-17 02:20:36 +04:00
. handler = no_action ,
2011-11-22 18:38:03 +04:00
. flags = IRQF_NO_THREAD ,
2005-04-17 02:20:36 +04:00
. name = " mapable0 cascade " ,
} ;
# ifdef USE_LIO3_IRQ
2005-09-04 02:56:17 +04:00
static struct irqaction map1_cascade = {
2005-04-17 02:20:36 +04:00
. handler = no_action ,
2011-11-22 18:38:03 +04:00
. flags = IRQF_NO_THREAD ,
2005-04-17 02:20:36 +04:00
. name = " mapable1 cascade " ,
} ;
# define SGI_INTERRUPTS SGINT_END
# else
# define SGI_INTERRUPTS SGINT_LOCAL3
# endif
2006-10-07 22:44:33 +04:00
extern void indy_8254timer_irq ( void ) ;
2006-04-03 20:56:36 +04:00
/*
* IRQs on the INDY look basically ( barring software IRQs which we don ' t use
* at all ) like :
*
* MIPS IRQ Source
* - - - - - - - - - - - - - -
* 0 Software ( ignored )
* 1 Software ( ignored )
* 2 Local IRQ level zero
* 3 Local IRQ level one
* 4 8254 Timer zero
* 5 8254 Timer one
* 6 Bus Error
* 7 R4k timer ( what we use )
*
* We handle the IRQ according to _our_ priority which is :
*
* Highest - - - - R4k Timer
* Local IRQ zero
* Local IRQ one
* Bus Error
* 8254 Timer zero
* Lowest - - - - 8254 Timer one
*
* then we just return , if multiple IRQs are pending then we will just take
* another exception , big deal .
*/
2006-10-07 22:44:33 +04:00
asmlinkage void plat_irq_dispatch ( void )
2006-04-03 20:56:36 +04:00
{
2007-03-19 03:13:37 +03:00
unsigned int pending = read_c0_status ( ) & read_c0_cause ( ) ;
2006-04-03 20:56:36 +04:00
/*
* First we check for r4k counter / timer IRQ .
*/
if ( pending & CAUSEF_IP7 )
2007-10-12 02:46:09 +04:00
do_IRQ ( SGI_TIMER_IRQ ) ;
2006-04-03 20:56:36 +04:00
else if ( pending & CAUSEF_IP2 )
2006-10-07 22:44:33 +04:00
indy_local0_irqdispatch ( ) ;
2006-04-03 20:56:36 +04:00
else if ( pending & CAUSEF_IP3 )
2006-10-07 22:44:33 +04:00
indy_local1_irqdispatch ( ) ;
2006-04-03 20:56:36 +04:00
else if ( pending & CAUSEF_IP6 )
2006-10-07 22:44:33 +04:00
indy_buserror_irq ( ) ;
2006-04-03 20:56:36 +04:00
else if ( pending & ( CAUSEF_IP4 | CAUSEF_IP5 ) )
2006-10-07 22:44:33 +04:00
indy_8254timer_irq ( ) ;
2006-04-03 20:56:36 +04:00
}
2005-04-17 02:20:36 +04:00
void __init arch_init_irq ( void )
{
int i ;
/* Init local mask --> irq tables. */
for ( i = 0 ; i < 256 ; i + + ) {
if ( i & 0x80 ) {
lc0msk_to_irqnr [ i ] = SGINT_LOCAL0 + 7 ;
lc1msk_to_irqnr [ i ] = SGINT_LOCAL1 + 7 ;
lc2msk_to_irqnr [ i ] = SGINT_LOCAL2 + 7 ;
lc3msk_to_irqnr [ i ] = SGINT_LOCAL3 + 7 ;
} else if ( i & 0x40 ) {
lc0msk_to_irqnr [ i ] = SGINT_LOCAL0 + 6 ;
lc1msk_to_irqnr [ i ] = SGINT_LOCAL1 + 6 ;
lc2msk_to_irqnr [ i ] = SGINT_LOCAL2 + 6 ;
lc3msk_to_irqnr [ i ] = SGINT_LOCAL3 + 6 ;
} else if ( i & 0x20 ) {
lc0msk_to_irqnr [ i ] = SGINT_LOCAL0 + 5 ;
lc1msk_to_irqnr [ i ] = SGINT_LOCAL1 + 5 ;
lc2msk_to_irqnr [ i ] = SGINT_LOCAL2 + 5 ;
lc3msk_to_irqnr [ i ] = SGINT_LOCAL3 + 5 ;
} else if ( i & 0x10 ) {
lc0msk_to_irqnr [ i ] = SGINT_LOCAL0 + 4 ;
lc1msk_to_irqnr [ i ] = SGINT_LOCAL1 + 4 ;
lc2msk_to_irqnr [ i ] = SGINT_LOCAL2 + 4 ;
lc3msk_to_irqnr [ i ] = SGINT_LOCAL3 + 4 ;
} else if ( i & 0x08 ) {
lc0msk_to_irqnr [ i ] = SGINT_LOCAL0 + 3 ;
lc1msk_to_irqnr [ i ] = SGINT_LOCAL1 + 3 ;
lc2msk_to_irqnr [ i ] = SGINT_LOCAL2 + 3 ;
lc3msk_to_irqnr [ i ] = SGINT_LOCAL3 + 3 ;
} else if ( i & 0x04 ) {
lc0msk_to_irqnr [ i ] = SGINT_LOCAL0 + 2 ;
lc1msk_to_irqnr [ i ] = SGINT_LOCAL1 + 2 ;
lc2msk_to_irqnr [ i ] = SGINT_LOCAL2 + 2 ;
lc3msk_to_irqnr [ i ] = SGINT_LOCAL3 + 2 ;
} else if ( i & 0x02 ) {
lc0msk_to_irqnr [ i ] = SGINT_LOCAL0 + 1 ;
lc1msk_to_irqnr [ i ] = SGINT_LOCAL1 + 1 ;
lc2msk_to_irqnr [ i ] = SGINT_LOCAL2 + 1 ;
lc3msk_to_irqnr [ i ] = SGINT_LOCAL3 + 1 ;
} else if ( i & 0x01 ) {
lc0msk_to_irqnr [ i ] = SGINT_LOCAL0 + 0 ;
lc1msk_to_irqnr [ i ] = SGINT_LOCAL1 + 0 ;
lc2msk_to_irqnr [ i ] = SGINT_LOCAL2 + 0 ;
lc3msk_to_irqnr [ i ] = SGINT_LOCAL3 + 0 ;
} else {
lc0msk_to_irqnr [ i ] = 0 ;
lc1msk_to_irqnr [ i ] = 0 ;
lc2msk_to_irqnr [ i ] = 0 ;
lc3msk_to_irqnr [ i ] = 0 ;
}
}
/* Mask out all interrupts. */
sgint - > imask0 = 0 ;
sgint - > imask1 = 0 ;
sgint - > cmeimask0 = 0 ;
sgint - > cmeimask1 = 0 ;
/* init CPU irqs */
2007-01-07 20:14:29 +03:00
mips_cpu_irq_init ( ) ;
2005-04-17 02:20:36 +04:00
for ( i = SGINT_LOCAL0 ; i < SGI_INTERRUPTS ; i + + ) {
2006-07-02 17:41:42 +04:00
struct irq_chip * handler ;
2005-04-17 02:20:36 +04:00
if ( i < SGINT_LOCAL1 )
handler = & ip22_local0_irq_type ;
else if ( i < SGINT_LOCAL2 )
handler = & ip22_local1_irq_type ;
else if ( i < SGINT_LOCAL3 )
handler = & ip22_local2_irq_type ;
else
handler = & ip22_local3_irq_type ;
2011-03-27 17:19:28 +04:00
irq_set_chip_and_handler ( i , handler , handle_level_irq ) ;
2005-04-17 02:20:36 +04:00
}
/* vector handler. this register the IRQ as non-sharable */
setup_irq ( SGI_LOCAL_0_IRQ , & local0_cascade ) ;
setup_irq ( SGI_LOCAL_1_IRQ , & local1_cascade ) ;
setup_irq ( SGI_BUSERR_IRQ , & buserr ) ;
/* cascade in cascade. i love Indy ;-) */
setup_irq ( SGI_MAP_0_IRQ , & map0_cascade ) ;
# ifdef USE_LIO3_IRQ
setup_irq ( SGI_MAP_1_IRQ , & map1_cascade ) ;
# endif
# ifdef CONFIG_EISA
if ( ip22_is_fullhouse ( ) ) /* Only Indigo-2 has EISA stuff */
2008-10-25 02:46:56 +04:00
ip22_eisa_init ( ) ;
2005-04-17 02:20:36 +04:00
# endif
}