2008-05-21 13:44:02 +04:00
# include <linux/linkage.h>
# include <linux/errno.h>
# include <linux/signal.h>
# include <linux/sched.h>
# include <linux/ioport.h>
# include <linux/interrupt.h>
# include <linux/timex.h>
# include <linux/random.h>
# include <linux/init.h>
# include <linux/kernel_stat.h>
2011-03-24 00:15:54 +03:00
# include <linux/syscore_ops.h>
2008-05-21 13:44:02 +04:00
# include <linux/bitops.h>
2009-01-04 14:03:52 +03:00
# include <linux/acpi.h>
# include <linux/io.h>
# include <linux/delay.h>
2008-05-21 13:44:02 +04:00
2011-07-27 03:09:06 +04:00
# include <linux/atomic.h>
2008-05-21 13:44:02 +04:00
# include <asm/timer.h>
# include <asm/hw_irq.h>
# include <asm/pgtable.h>
# include <asm/desc.h>
# include <asm/apic.h>
# include <asm/i8259.h>
/*
* 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 .
*/
2010-09-28 17:01:33 +04:00
static void init_8259A ( int auto_eoi ) ;
2008-05-21 13:44:02 +04:00
static int i8259A_auto_eoi ;
2009-07-25 20:35:11 +04:00
DEFINE_RAW_SPINLOCK ( i8259A_lock ) ;
2008-05-21 13:44:02 +04:00
/*
* 8259 A PIC functions to handle ISA devices :
*/
/*
* This contains the irq mask for both 8259 A irq controllers ,
*/
unsigned int cached_irq_mask = 0xffff ;
/*
* Not all IRQs can be routed through the IO - APIC , eg . on certain ( older )
* boards the timer interrupt is not really connected to any IO - APIC pin ,
* it ' s fed to the master 8259 A ' s IR0 line only .
*
* Any ' 1 ' bit in this mask means the IRQ is routed through the IO - APIC .
* this ' mixed mode ' IRQ handling costs nothing because it ' s only used
* at IRQ setup time .
*/
unsigned long io_apic_irqs ;
2010-09-28 17:01:33 +04:00
static void mask_8259A_irq ( unsigned int irq )
2008-05-21 13:44:02 +04:00
{
unsigned int mask = 1 < < irq ;
unsigned long flags ;
2009-07-25 20:35:11 +04:00
raw_spin_lock_irqsave ( & i8259A_lock , flags ) ;
2008-05-21 13:44:02 +04:00
cached_irq_mask | = mask ;
if ( irq & 8 )
outb ( cached_slave_mask , PIC_SLAVE_IMR ) ;
else
outb ( cached_master_mask , PIC_MASTER_IMR ) ;
2009-07-25 20:35:11 +04:00
raw_spin_unlock_irqrestore ( & i8259A_lock , flags ) ;
2008-05-21 13:44:02 +04:00
}
2010-09-28 17:01:33 +04:00
static void disable_8259A_irq ( struct irq_data * data )
{
mask_8259A_irq ( data - > irq ) ;
}
static void unmask_8259A_irq ( unsigned int irq )
2008-05-21 13:44:02 +04:00
{
unsigned int mask = ~ ( 1 < < irq ) ;
unsigned long flags ;
2009-07-25 20:35:11 +04:00
raw_spin_lock_irqsave ( & i8259A_lock , flags ) ;
2008-05-21 13:44:02 +04:00
cached_irq_mask & = mask ;
if ( irq & 8 )
outb ( cached_slave_mask , PIC_SLAVE_IMR ) ;
else
outb ( cached_master_mask , PIC_MASTER_IMR ) ;
2009-07-25 20:35:11 +04:00
raw_spin_unlock_irqrestore ( & i8259A_lock , flags ) ;
2008-05-21 13:44:02 +04:00
}
2010-09-28 17:01:33 +04:00
static void enable_8259A_irq ( struct irq_data * data )
{
unmask_8259A_irq ( data - > irq ) ;
}
2009-11-09 22:27:04 +03:00
static int i8259A_irq_pending ( unsigned int irq )
2008-05-21 13:44:02 +04:00
{
unsigned int mask = 1 < < irq ;
unsigned long flags ;
int ret ;
2009-07-25 20:35:11 +04:00
raw_spin_lock_irqsave ( & i8259A_lock , flags ) ;
2008-05-21 13:44:02 +04:00
if ( irq < 8 )
ret = inb ( PIC_MASTER_CMD ) & mask ;
else
ret = inb ( PIC_SLAVE_CMD ) & ( mask > > 8 ) ;
2009-07-25 20:35:11 +04:00
raw_spin_unlock_irqrestore ( & i8259A_lock , flags ) ;
2008-05-21 13:44:02 +04:00
return ret ;
}
2009-11-09 22:27:04 +03:00
static void make_8259A_irq ( unsigned int irq )
2008-05-21 13:44:02 +04:00
{
disable_irq_nosync ( irq ) ;
io_apic_irqs & = ~ ( 1 < < irq ) ;
2011-03-12 14:20:43 +03:00
irq_set_chip_and_handler_name ( irq , & i8259A_chip , handle_level_irq ,
2010-09-28 17:01:33 +04:00
i8259A_chip . name ) ;
2008-05-21 13:44:02 +04:00
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 ) {
2008-05-21 13:57:52 +04:00
outb ( 0x0B , PIC_MASTER_CMD ) ; /* ISR register */
2008-05-21 13:44:02 +04:00
value = inb ( PIC_MASTER_CMD ) & irqmask ;
2008-05-21 13:57:52 +04:00
outb ( 0x0A , PIC_MASTER_CMD ) ; /* back to the IRR register */
2008-05-21 13:44:02 +04:00
return value ;
}
2008-05-21 13:57:52 +04:00
outb ( 0x0B , PIC_SLAVE_CMD ) ; /* ISR register */
2008-05-21 13:44:02 +04:00
value = inb ( PIC_SLAVE_CMD ) & ( irqmask > > 8 ) ;
2008-05-21 13:57:52 +04:00
outb ( 0x0A , PIC_SLAVE_CMD ) ; /* back to the IRR register */
2008-05-21 13:44:02 +04:00
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 !
*/
2010-09-28 17:01:33 +04:00
static void mask_and_ack_8259A ( struct irq_data * data )
2008-05-21 13:44:02 +04:00
{
2010-09-28 17:01:33 +04:00
unsigned int irq = data - > irq ;
2008-05-21 13:44:02 +04:00
unsigned int irqmask = 1 < < irq ;
unsigned long flags ;
2009-07-25 20:35:11 +04:00
raw_spin_lock_irqsave ( & i8259A_lock , flags ) ;
2008-05-21 13:44:02 +04:00
/*
* 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 unnecessarily .
*
* Note that IRQ7 and IRQ15 ( the two spurious IRQs
* usually resulting from 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 ( PIC_SLAVE_IMR ) ; /* DUMMY - (do we need this?) */
outb ( cached_slave_mask , PIC_SLAVE_IMR ) ;
/* 'Specific EOI' to slave */
2008-05-21 13:52:52 +04:00
outb ( 0x60 + ( irq & 7 ) , PIC_SLAVE_CMD ) ;
2008-05-21 13:44:02 +04:00
/* 'Specific EOI' to master-IRQ2 */
2008-05-21 13:52:52 +04:00
outb ( 0x60 + PIC_CASCADE_IR , PIC_MASTER_CMD ) ;
2008-05-21 13:44:02 +04:00
} else {
inb ( PIC_MASTER_IMR ) ; /* DUMMY - (do we need this?) */
outb ( cached_master_mask , PIC_MASTER_IMR ) ;
2008-05-21 13:52:52 +04:00
outb ( 0x60 + irq , PIC_MASTER_CMD ) ; /* 'Specific EOI to master */
2008-05-21 13:44:02 +04:00
}
2009-07-25 20:35:11 +04:00
raw_spin_unlock_irqrestore ( & i8259A_lock , flags ) ;
2008-05-21 13:44:02 +04:00
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 ;
/*
* 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 ;
}
}
2010-09-28 17:01:33 +04:00
struct irq_chip i8259A_chip = {
. name = " XT-PIC " ,
. irq_mask = disable_8259A_irq ,
. irq_disable = disable_8259A_irq ,
. irq_unmask = enable_8259A_irq ,
. irq_mask_ack = mask_and_ack_8259A ,
} ;
2008-05-21 13:44:02 +04:00
static char irq_trigger [ 2 ] ;
/**
* ELCR registers ( 0x4d0 , 0x4d1 ) control edge / level of IRQ
*/
static void restore_ELCR ( char * trigger )
{
outb ( trigger [ 0 ] , 0x4d0 ) ;
outb ( trigger [ 1 ] , 0x4d1 ) ;
}
static void save_ELCR ( char * trigger )
{
/* IRQ 0,1,2,8,13 are marked as reserved */
trigger [ 0 ] = inb ( 0x4d0 ) & 0xF8 ;
trigger [ 1 ] = inb ( 0x4d1 ) & 0xDE ;
}
2011-03-24 00:15:54 +03:00
static void i8259A_resume ( void )
2008-05-21 13:44:02 +04:00
{
init_8259A ( i8259A_auto_eoi ) ;
restore_ELCR ( irq_trigger ) ;
}
2011-03-24 00:15:54 +03:00
static int i8259A_suspend ( void )
2008-05-21 13:44:02 +04:00
{
save_ELCR ( irq_trigger ) ;
return 0 ;
}
2011-03-24 00:15:54 +03:00
static void i8259A_shutdown ( void )
2008-05-21 13:44:02 +04:00
{
/* Put the i8259A into a quiescent state that
* the kernel initialization code can get it
* out of .
*/
outb ( 0xff , PIC_MASTER_IMR ) ; /* mask all of 8259A-1 */
2012-08-06 18:13:00 +04:00
outb ( 0xff , PIC_SLAVE_IMR ) ; /* mask all of 8259A-2 */
2008-05-21 13:44:02 +04:00
}
2011-03-24 00:15:54 +03:00
static struct syscore_ops i8259_syscore_ops = {
2008-05-21 13:44:02 +04:00
. suspend = i8259A_suspend ,
. resume = i8259A_resume ,
. shutdown = i8259A_shutdown ,
} ;
2009-11-09 22:27:04 +03:00
static void mask_8259A ( void )
2008-07-10 22:16:46 +04:00
{
unsigned long flags ;
2009-07-25 20:35:11 +04:00
raw_spin_lock_irqsave ( & i8259A_lock , flags ) ;
2008-07-10 22:16:46 +04:00
outb ( 0xff , PIC_MASTER_IMR ) ; /* mask all of 8259A-1 */
outb ( 0xff , PIC_SLAVE_IMR ) ; /* mask all of 8259A-2 */
2009-07-25 20:35:11 +04:00
raw_spin_unlock_irqrestore ( & i8259A_lock , flags ) ;
2008-07-10 22:16:46 +04:00
}
2009-11-09 22:27:04 +03:00
static void unmask_8259A ( void )
2008-07-10 22:16:46 +04:00
{
unsigned long flags ;
2009-07-25 20:35:11 +04:00
raw_spin_lock_irqsave ( & i8259A_lock , flags ) ;
2008-07-10 22:16:46 +04:00
outb ( cached_master_mask , PIC_MASTER_IMR ) ; /* restore master IRQ mask */
outb ( cached_slave_mask , PIC_SLAVE_IMR ) ; /* restore slave IRQ mask */
2009-07-25 20:35:11 +04:00
raw_spin_unlock_irqrestore ( & i8259A_lock , flags ) ;
2008-07-10 22:16:46 +04:00
}
2009-11-09 22:27:04 +03:00
static void init_8259A ( int auto_eoi )
2008-05-21 13:44:02 +04:00
{
unsigned long flags ;
i8259A_auto_eoi = auto_eoi ;
2009-07-25 20:35:11 +04:00
raw_spin_lock_irqsave ( & i8259A_lock , flags ) ;
2008-05-21 13:44:02 +04:00
outb ( 0xff , PIC_MASTER_IMR ) ; /* mask all of 8259A-1 */
outb ( 0xff , PIC_SLAVE_IMR ) ; /* mask all of 8259A-2 */
/*
* outb_pic - this has to work on a wide range of PC hardware .
*/
outb_pic ( 0x11 , PIC_MASTER_CMD ) ; /* ICW1: select 8259A-1 init */
2008-05-28 14:42:57 +04:00
/* ICW2: 8259A-1 IR0-7 mapped to 0x30-0x37 on x86-64,
2009-01-04 14:03:52 +03:00
to 0x20 - 0x27 on i386 */
2008-05-21 13:44:02 +04:00
outb_pic ( IRQ0_VECTOR , PIC_MASTER_IMR ) ;
2008-05-28 14:42:57 +04:00
2008-05-21 13:44:02 +04:00
/* 8259A-1 (the master) has a slave on IR2 */
2008-05-28 14:42:57 +04:00
outb_pic ( 1U < < PIC_CASCADE_IR , PIC_MASTER_IMR ) ;
2008-05-21 13:44:02 +04:00
if ( auto_eoi ) /* master does Auto EOI */
outb_pic ( MASTER_ICW4_DEFAULT | PIC_ICW4_AEOI , PIC_MASTER_IMR ) ;
else /* master expects normal EOI */
outb_pic ( MASTER_ICW4_DEFAULT , PIC_MASTER_IMR ) ;
outb_pic ( 0x11 , PIC_SLAVE_CMD ) ; /* ICW1: select 8259A-2 init */
2008-05-28 14:42:57 +04:00
/* ICW2: 8259A-2 IR0-7 mapped to IRQ8_VECTOR */
2008-05-21 13:44:02 +04:00
outb_pic ( IRQ8_VECTOR , PIC_SLAVE_IMR ) ;
/* 8259A-2 is a slave on master's IR2 */
outb_pic ( PIC_CASCADE_IR , PIC_SLAVE_IMR ) ;
/* (slave's support for AEOI in flat mode is to be investigated) */
outb_pic ( SLAVE_ICW4_DEFAULT , PIC_SLAVE_IMR ) ;
if ( auto_eoi )
/*
* In AEOI mode we just have to mask the interrupt
* when acking .
*/
2010-09-28 17:01:33 +04:00
i8259A_chip . irq_mask_ack = disable_8259A_irq ;
2008-05-21 13:44:02 +04:00
else
2010-09-28 17:01:33 +04:00
i8259A_chip . irq_mask_ack = mask_and_ack_8259A ;
2008-05-21 13:44:02 +04:00
udelay ( 100 ) ; /* wait for 8259A to initialize */
outb ( cached_master_mask , PIC_MASTER_IMR ) ; /* restore master IRQ mask */
outb ( cached_slave_mask , PIC_SLAVE_IMR ) ; /* restore slave IRQ mask */
2009-07-25 20:35:11 +04:00
raw_spin_unlock_irqrestore ( & i8259A_lock , flags ) ;
2008-05-21 13:44:02 +04:00
}
2009-11-09 22:27:04 +03:00
2009-11-09 22:24:14 +03:00
/*
* make i8259 a driver so that we can select pic functions at run time . the goal
* is to make x86 binary compatible among pc compatible and non - pc compatible
* platforms , such as x86 MID .
*/
2010-02-23 13:03:31 +03:00
static void legacy_pic_noop ( void ) { } ;
static void legacy_pic_uint_noop ( unsigned int unused ) { } ;
static void legacy_pic_int_noop ( int unused ) { } ;
2009-11-09 22:24:14 +03:00
static int legacy_pic_irq_pending_noop ( unsigned int irq )
{
return 0 ;
}
struct legacy_pic null_legacy_pic = {
. nr_legacy_irqs = 0 ,
2010-09-28 17:01:33 +04:00
. chip = & dummy_irq_chip ,
. mask = legacy_pic_uint_noop ,
. unmask = legacy_pic_uint_noop ,
2009-11-09 22:24:14 +03:00
. mask_all = legacy_pic_noop ,
. restore_mask = legacy_pic_noop ,
. init = legacy_pic_int_noop ,
. irq_pending = legacy_pic_irq_pending_noop ,
. make_irq = legacy_pic_uint_noop ,
} ;
struct legacy_pic default_legacy_pic = {
. nr_legacy_irqs = NR_IRQS_LEGACY ,
. chip = & i8259A_chip ,
2010-09-28 17:01:33 +04:00
. mask = mask_8259A_irq ,
. unmask = unmask_8259A_irq ,
. mask_all = mask_8259A ,
2009-11-09 22:24:14 +03:00
. restore_mask = unmask_8259A ,
. init = init_8259A ,
. irq_pending = i8259A_irq_pending ,
. make_irq = make_8259A_irq ,
} ;
struct legacy_pic * legacy_pic = & default_legacy_pic ;
2010-07-21 02:18:19 +04:00
2011-03-24 00:15:54 +03:00
static int __init i8259A_init_ops ( void )
2010-07-21 02:18:19 +04:00
{
2011-03-24 00:15:54 +03:00
if ( legacy_pic = = & default_legacy_pic )
register_syscore_ops ( & i8259_syscore_ops ) ;
2010-07-21 02:18:19 +04:00
2011-03-24 00:15:54 +03:00
return 0 ;
2010-07-21 02:18:19 +04:00
}
2011-03-24 00:15:54 +03:00
device_initcall ( i8259A_init_ops ) ;