2005-04-17 02:20:36 +04:00
/*
* This file is subject to the terms and conditions of the GNU General Public
* License . See the file " COPYING " in the main directory of this archive
* for more details .
*
* Code to handle x86 style IRQs plus some generic interrupt stuff .
*
* Copyright ( C ) 1992 Linus Torvalds
* Copyright ( C ) 1994 - 2000 Ralf Baechle
*/
# include <linux/delay.h>
# include <linux/init.h>
# include <linux/ioport.h>
# include <linux/interrupt.h>
# include <linux/kernel.h>
# include <linux/spinlock.h>
# include <linux/sysdev.h>
# include <asm/i8259.h>
# include <asm/io.h>
void enable_8259A_irq ( unsigned int irq ) ;
void disable_8259A_irq ( unsigned int irq ) ;
/*
* This is the ' legacy ' 8259 A Programmable Interrupt Controller ,
* present in the majority of PC / AT boxes .
* plus some generic x86 specific things if generic specifics makes
* any sense at all .
* this file should become arch / i386 / kernel / irq . c when the old irq . c
* moves to arch independent land
*/
2005-01-14 06:03:23 +03:00
DEFINE_SPINLOCK ( i8259A_lock ) ;
2005-04-17 02:20:36 +04:00
static void end_8259A_irq ( unsigned int irq )
{
if ( ! ( irq_desc [ irq ] . status & ( IRQ_DISABLED | IRQ_INPROGRESS ) ) & &
irq_desc [ irq ] . action )
enable_8259A_irq ( irq ) ;
}
# define shutdown_8259A_irq disable_8259A_irq
void mask_and_ack_8259A ( unsigned int ) ;
static unsigned int startup_8259A_irq ( unsigned int irq )
{
enable_8259A_irq ( irq ) ;
return 0 ; /* never anything pending */
}
static struct hw_interrupt_type i8259A_irq_type = {
2005-02-28 16:39:57 +03:00
. typename = " XT-PIC " ,
. startup = startup_8259A_irq ,
. shutdown = shutdown_8259A_irq ,
. enable = enable_8259A_irq ,
. disable = disable_8259A_irq ,
. ack = mask_and_ack_8259A ,
. end = end_8259A_irq ,
2005-04-17 02:20:36 +04:00
} ;
/*
* 8259 A PIC functions to handle ISA devices :
*/
/*
* This contains the irq mask for both 8259 A irq controllers ,
*/
static unsigned int cached_irq_mask = 0xffff ;
# define cached_21 (cached_irq_mask)
# define cached_A1 (cached_irq_mask >> 8)
void disable_8259A_irq ( unsigned int irq )
{
unsigned int mask = 1 < < irq ;
unsigned long flags ;
spin_lock_irqsave ( & i8259A_lock , flags ) ;
cached_irq_mask | = mask ;
if ( irq & 8 )
outb ( cached_A1 , 0xA1 ) ;
else
outb ( cached_21 , 0x21 ) ;
spin_unlock_irqrestore ( & i8259A_lock , flags ) ;
}
void enable_8259A_irq ( unsigned int irq )
{
unsigned int mask = ~ ( 1 < < irq ) ;
unsigned long flags ;
spin_lock_irqsave ( & i8259A_lock , flags ) ;
cached_irq_mask & = mask ;
if ( irq & 8 )
outb ( cached_A1 , 0xA1 ) ;
else
outb ( cached_21 , 0x21 ) ;
spin_unlock_irqrestore ( & i8259A_lock , flags ) ;
}
int i8259A_irq_pending ( unsigned int irq )
{
unsigned int mask = 1 < < irq ;
unsigned long flags ;
int ret ;
spin_lock_irqsave ( & i8259A_lock , flags ) ;
if ( irq < 8 )
ret = inb ( 0x20 ) & mask ;
else
ret = inb ( 0xA0 ) & ( mask > > 8 ) ;
spin_unlock_irqrestore ( & i8259A_lock , flags ) ;
return ret ;
}
void make_8259A_irq ( unsigned int irq )
{
disable_irq_nosync ( irq ) ;
irq_desc [ irq ] . handler = & i8259A_irq_type ;
enable_irq ( irq ) ;
}
/*
* This function assumes to be called rarely . Switching between
* 8259 A registers is slow .
* This has to be protected by the irq controller spinlock
* before being called .
*/
static inline int i8259A_irq_real ( unsigned int irq )
{
int value ;
int irqmask = 1 < < irq ;
if ( irq < 8 ) {
outb ( 0x0B , 0x20 ) ; /* ISR register */
value = inb ( 0x20 ) & irqmask ;
outb ( 0x0A , 0x20 ) ; /* back to the IRR register */
return value ;
}
outb ( 0x0B , 0xA0 ) ; /* ISR register */
value = inb ( 0xA0 ) & ( irqmask > > 8 ) ;
outb ( 0x0A , 0xA0 ) ; /* back to the IRR register */
return value ;
}
/*
* Careful ! The 8259 A is a fragile beast , it pretty
* much _has_ to be done exactly like this ( mask it
* first , _then_ send the EOI , and the order of EOI
* to the two 8259 s is important !
*/
void mask_and_ack_8259A ( unsigned int irq )
{
unsigned int irqmask = 1 < < irq ;
unsigned long flags ;
spin_lock_irqsave ( & i8259A_lock , flags ) ;
/*
* Lightweight spurious IRQ detection . We do not want to overdo
* spurious IRQ handling - it ' s usually a sign of hardware problems , so
* we only do the checks we can do without slowing down good hardware
* nnecesserily .
*
* Note that IRQ7 and IRQ15 ( the two spurious IRQs usually resulting
* rom the 8259 A - 1 | 2 PICs ) occur even if the IRQ is masked in the 8259 A .
* Thus we can check spurious 8259 A IRQs without doing the quite slow
* i8259A_irq_real ( ) call for every IRQ . This does not cover 100 % of
* spurious interrupts , but should be enough to warn the user that
* there is something bad going on . . .
*/
if ( cached_irq_mask & irqmask )
goto spurious_8259A_irq ;
cached_irq_mask | = irqmask ;
handle_real_irq :
if ( irq & 8 ) {
inb ( 0xA1 ) ; /* DUMMY - (do we need this?) */
outb ( cached_A1 , 0xA1 ) ;
outb ( 0x60 + ( irq & 7 ) , 0xA0 ) ; /* 'Specific EOI' to slave */
outb ( 0x62 , 0x20 ) ; /* 'Specific EOI' to master-IRQ2 */
} else {
inb ( 0x21 ) ; /* DUMMY - (do we need this?) */
outb ( cached_21 , 0x21 ) ;
outb ( 0x60 + irq , 0x20 ) ; /* 'Specific EOI' to master */
}
2006-04-05 12:45:45 +04:00
# ifdef CONFIG_MIPS_MT_SMTC
if ( irq_hwmask [ irq ] & ST0_IM )
set_c0_status ( irq_hwmask [ irq ] & ST0_IM ) ;
# endif /* CONFIG_MIPS_MT_SMTC */
2005-04-17 02:20:36 +04:00
spin_unlock_irqrestore ( & i8259A_lock , flags ) ;
return ;
spurious_8259A_irq :
/*
* this is the slow path - should happen rarely .
*/
if ( i8259A_irq_real ( irq ) )
/*
* oops , the IRQ _is_ in service according to the
* 8259 A - not spurious , go handle it .
*/
goto handle_real_irq ;
{
static int spurious_irq_mask = 0 ;
/*
* At this point we can be sure the IRQ is spurious ,
* lets ACK and report it . [ once per IRQ ]
*/
if ( ! ( spurious_irq_mask & irqmask ) ) {
printk ( KERN_DEBUG " spurious 8259A interrupt: IRQ%d. \n " , irq ) ;
spurious_irq_mask | = irqmask ;
}
atomic_inc ( & irq_err_count ) ;
/*
* Theoretically we do not have to handle this IRQ ,
* but in Linux this does not cause problems and is
* simpler for us .
*/
goto handle_real_irq ;
}
}
static int i8259A_resume ( struct sys_device * dev )
{
init_8259A ( 0 ) ;
return 0 ;
}
static struct sysdev_class i8259_sysdev_class = {
set_kset_name ( " i8259 " ) ,
. resume = i8259A_resume ,
} ;
static struct sys_device device_i8259A = {
. id = 0 ,
. cls = & i8259_sysdev_class ,
} ;
static int __init i8259A_init_sysfs ( void )
{
int error = sysdev_class_register ( & i8259_sysdev_class ) ;
if ( ! error )
error = sysdev_register ( & device_i8259A ) ;
return error ;
}
device_initcall ( i8259A_init_sysfs ) ;
void __init init_8259A ( int auto_eoi )
{
unsigned long flags ;
spin_lock_irqsave ( & i8259A_lock , flags ) ;
outb ( 0xff , 0x21 ) ; /* mask all of 8259A-1 */
outb ( 0xff , 0xA1 ) ; /* mask all of 8259A-2 */
/*
* outb_p - this has to work on a wide range of PC hardware .
*/
outb_p ( 0x11 , 0x20 ) ; /* ICW1: select 8259A-1 init */
outb_p ( 0x00 , 0x21 ) ; /* ICW2: 8259A-1 IR0-7 mapped to 0x00-0x07 */
outb_p ( 0x04 , 0x21 ) ; /* 8259A-1 (the master) has a slave on IR2 */
if ( auto_eoi )
outb_p ( 0x03 , 0x21 ) ; /* master does Auto EOI */
else
outb_p ( 0x01 , 0x21 ) ; /* master expects normal EOI */
outb_p ( 0x11 , 0xA0 ) ; /* ICW1: select 8259A-2 init */
outb_p ( 0x08 , 0xA1 ) ; /* ICW2: 8259A-2 IR0-7 mapped to 0x08-0x0f */
outb_p ( 0x02 , 0xA1 ) ; /* 8259A-2 is a slave on master's IR2 */
outb_p ( 0x01 , 0xA1 ) ; /* (slave's support for AEOI in flat mode
is to be investigated ) */
if ( auto_eoi )
/*
* in AEOI mode we just have to mask the interrupt
* when acking .
*/
i8259A_irq_type . ack = disable_8259A_irq ;
else
i8259A_irq_type . ack = mask_and_ack_8259A ;
udelay ( 100 ) ; /* wait for 8259A to initialize */
outb ( cached_21 , 0x21 ) ; /* restore master IRQ mask */
outb ( cached_A1 , 0xA1 ) ; /* restore slave IRQ mask */
spin_unlock_irqrestore ( & i8259A_lock , flags ) ;
}
/*
* IRQ2 is cascade interrupt to second interrupt controller
*/
static struct irqaction irq2 = {
no_action , 0 , CPU_MASK_NONE , " cascade " , NULL , NULL
} ;
static struct resource pic1_io_resource = {
2006-06-05 02:14:05 +04:00
. name = " pic1 " , . start = 0x20 , . end = 0x3f , . flags = IORESOURCE_BUSY
2005-04-17 02:20:36 +04:00
} ;
static struct resource pic2_io_resource = {
2006-06-05 02:14:05 +04:00
. name = " pic2 " , . start = 0xa0 , . end = 0xbf , . flags = IORESOURCE_BUSY
2005-04-17 02:20:36 +04:00
} ;
/*
* On systems with i8259 - style interrupt controllers we assume for
2005-08-16 19:46:05 +04:00
* driver compatibility reasons interrupts 0 - 15 to be the i8259
2005-04-17 02:20:36 +04:00
* interrupts even if the hardware uses a different interrupt numbering .
*/
void __init init_i8259_irqs ( void )
{
int i ;
request_resource ( & ioport_resource , & pic1_io_resource ) ;
request_resource ( & ioport_resource , & pic2_io_resource ) ;
init_8259A ( 0 ) ;
for ( i = 0 ; i < 16 ; i + + ) {
irq_desc [ i ] . status = IRQ_DISABLED ;
2005-03-01 22:22:29 +03:00
irq_desc [ i ] . action = NULL ;
2005-04-17 02:20:36 +04:00
irq_desc [ i ] . depth = 1 ;
irq_desc [ i ] . handler = & i8259A_irq_type ;
}
setup_irq ( 2 , & irq2 ) ;
}