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>
2015-07-08 15:46:08 +03:00
# include <linux/irqchip.h>
2014-09-19 01:47:11 +04:00
# include <linux/irqdomain.h>
2005-04-17 02:20:36 +04:00
# include <linux/kernel.h>
2015-05-22 18:51:03 +03:00
# include <linux/of_irq.h>
2005-04-17 02:20:36 +04:00
# include <linux/spinlock.h>
2011-07-16 02:59:54 +04:00
# include <linux/syscore_ops.h>
2010-10-07 17:08:54 +04:00
# include <linux/irq.h>
2005-04-17 02:20:36 +04:00
# include <asm/i8259.h>
# include <asm/io.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 .
* this file should become arch / i386 / kernel / irq . c when the old irq . c
* moves to arch independent land
*/
2007-02-20 14:08:45 +03:00
static int i8259A_auto_eoi = - 1 ;
2010-02-27 14:53:38 +03:00
DEFINE_RAW_SPINLOCK ( i8259A_lock ) ;
2011-03-24 00:08:57 +03:00
static void disable_8259A_irq ( struct irq_data * d ) ;
static void enable_8259A_irq ( struct irq_data * d ) ;
static void mask_and_ack_8259A ( struct irq_data * d ) ;
2007-09-13 06:04:04 +04:00
static void init_8259A ( int auto_eoi ) ;
2016-09-20 00:21:19 +03:00
static int ( * i8259_poll ) ( void ) = i8259_irq ;
2005-04-17 02:20:36 +04:00
2006-12-06 20:04:17 +03:00
static struct irq_chip i8259A_chip = {
2011-03-24 00:08:57 +03:00
. 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 ,
2005-04-17 02:20:36 +04:00
} ;
/*
* 8259 A PIC functions to handle ISA devices :
*/
2016-09-20 00:21:19 +03:00
void i8259_set_poll ( int ( * poll ) ( void ) )
{
i8259_poll = poll ;
}
2005-04-17 02:20:36 +04:00
/*
* This contains the irq mask for both 8259 A irq controllers ,
*/
static unsigned int cached_irq_mask = 0xffff ;
2006-12-06 20:04:17 +03:00
# define cached_master_mask (cached_irq_mask)
# define cached_slave_mask (cached_irq_mask >> 8)
2005-04-17 02:20:36 +04:00
2011-03-24 00:08:57 +03:00
static void disable_8259A_irq ( struct irq_data * d )
2005-04-17 02:20:36 +04:00
{
2011-03-24 00:08:57 +03:00
unsigned int mask , irq = d - > irq - I8259A_IRQ_BASE ;
2005-04-17 02:20:36 +04:00
unsigned long flags ;
2007-01-14 17:41:42 +03:00
mask = 1 < < irq ;
2010-02-27 14:53:38 +03:00
raw_spin_lock_irqsave ( & i8259A_lock , flags ) ;
2005-04-17 02:20:36 +04:00
cached_irq_mask | = mask ;
if ( irq & 8 )
2006-12-06 20:04:17 +03:00
outb ( cached_slave_mask , PIC_SLAVE_IMR ) ;
2005-04-17 02:20:36 +04:00
else
2006-12-06 20:04:17 +03:00
outb ( cached_master_mask , PIC_MASTER_IMR ) ;
2010-02-27 14:53:38 +03:00
raw_spin_unlock_irqrestore ( & i8259A_lock , flags ) ;
2005-04-17 02:20:36 +04:00
}
2011-03-24 00:08:57 +03:00
static void enable_8259A_irq ( struct irq_data * d )
2005-04-17 02:20:36 +04:00
{
2011-03-24 00:08:57 +03:00
unsigned int mask , irq = d - > irq - I8259A_IRQ_BASE ;
2005-04-17 02:20:36 +04:00
unsigned long flags ;
2007-01-14 17:41:42 +03:00
mask = ~ ( 1 < < irq ) ;
2010-02-27 14:53:38 +03:00
raw_spin_lock_irqsave ( & i8259A_lock , flags ) ;
2005-04-17 02:20:36 +04:00
cached_irq_mask & = mask ;
if ( irq & 8 )
2006-12-06 20:04:17 +03:00
outb ( cached_slave_mask , PIC_SLAVE_IMR ) ;
2005-04-17 02:20:36 +04:00
else
2006-12-06 20:04:17 +03:00
outb ( cached_master_mask , PIC_MASTER_IMR ) ;
2010-02-27 14:53:38 +03:00
raw_spin_unlock_irqrestore ( & i8259A_lock , flags ) ;
2005-04-17 02:20:36 +04:00
}
void make_8259A_irq ( unsigned int irq )
{
disable_irq_nosync ( irq ) ;
2011-03-27 17:19:28 +04:00
irq_set_chip_and_handler ( irq , & i8259A_chip , handle_level_irq ) ;
2005-04-17 02:20:36 +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 ) {
2007-10-12 02:46:15 +04:00
outb ( 0x0B , PIC_MASTER_CMD ) ; /* ISR register */
2006-12-06 20:04:17 +03:00
value = inb ( PIC_MASTER_CMD ) & irqmask ;
2007-10-12 02:46:15 +04:00
outb ( 0x0A , PIC_MASTER_CMD ) ; /* back to the IRR register */
2005-04-17 02:20:36 +04:00
return value ;
}
2007-10-12 02:46:15 +04:00
outb ( 0x0B , PIC_SLAVE_CMD ) ; /* ISR register */
2006-12-06 20:04:17 +03:00
value = inb ( PIC_SLAVE_CMD ) & ( irqmask > > 8 ) ;
2007-10-12 02:46:15 +04:00
outb ( 0x0A , PIC_SLAVE_CMD ) ; /* back to the IRR register */
2005-04-17 02:20:36 +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 !
*/
2011-03-24 00:08:57 +03:00
static void mask_and_ack_8259A ( struct irq_data * d )
2005-04-17 02:20:36 +04:00
{
2011-03-24 00:08:57 +03:00
unsigned int irqmask , irq = d - > irq - I8259A_IRQ_BASE ;
2005-04-17 02:20:36 +04:00
unsigned long flags ;
2007-01-14 17:41:42 +03:00
irqmask = 1 < < irq ;
2010-02-27 14:53:38 +03:00
raw_spin_lock_irqsave ( & i8259A_lock , flags ) ;
2005-04-17 02:20:36 +04:00
/*
2006-12-06 20:04:17 +03: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 .
2005-04-17 02:20:36 +04:00
*
2006-12-06 20:04:17 +03:00
* 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 . . .
2005-04-17 02:20:36 +04:00
*/
if ( cached_irq_mask & irqmask )
goto spurious_8259A_irq ;
cached_irq_mask | = irqmask ;
handle_real_irq :
if ( irq & 8 ) {
2006-12-06 20:04:17 +03:00
inb ( PIC_SLAVE_IMR ) ; /* DUMMY - (do we need this?) */
outb ( cached_slave_mask , PIC_SLAVE_IMR ) ;
2007-10-12 02:46:15 +04:00
outb ( 0x60 + ( irq & 7 ) , PIC_SLAVE_CMD ) ; /* 'Specific EOI' to slave */
outb ( 0x60 + PIC_CASCADE_IR , PIC_MASTER_CMD ) ; /* 'Specific EOI' to master-IRQ2 */
2005-04-17 02:20:36 +04:00
} else {
2006-12-06 20:04:17 +03:00
inb ( PIC_MASTER_IMR ) ; /* DUMMY - (do we need this?) */
outb ( cached_master_mask , PIC_MASTER_IMR ) ;
2013-01-22 15:59:30 +04:00
outb ( 0x60 + irq , PIC_MASTER_CMD ) ; /* 'Specific EOI to master */
2005-04-17 02:20:36 +04:00
}
2010-02-27 14:53:38 +03:00
raw_spin_unlock_irqrestore ( & i8259A_lock , flags ) ;
2005-04-17 02:20:36 +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 ;
{
2006-12-06 20:04:17 +03:00
static int spurious_irq_mask ;
2005-04-17 02:20:36 +04:00
/*
* 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 ;
}
}
2011-07-16 02:59:54 +04:00
static void i8259A_resume ( void )
2005-04-17 02:20:36 +04:00
{
2007-02-20 14:08:45 +03:00
if ( i8259A_auto_eoi > = 0 )
init_8259A ( i8259A_auto_eoi ) ;
2006-12-06 20:04:17 +03:00
}
2011-07-16 02:59:54 +04:00
static void i8259A_shutdown ( void )
2006-12-06 20:04:17 +03:00
{
/* Put the i8259A into a quiescent state that
* the kernel initialization code can get it
* out of .
*/
2007-02-20 14:08:45 +03:00
if ( i8259A_auto_eoi > = 0 ) {
outb ( 0xff , PIC_MASTER_IMR ) ; /* mask all of 8259A-1 */
2011-08-04 18:46:41 +04:00
outb ( 0xff , PIC_SLAVE_IMR ) ; /* mask all of 8259A-2 */
2007-02-20 14:08:45 +03:00
}
2005-04-17 02:20:36 +04:00
}
2011-07-16 02:59:54 +04:00
static struct syscore_ops i8259_syscore_ops = {
2005-04-17 02:20:36 +04:00
. resume = i8259A_resume ,
2006-12-06 20:04:17 +03:00
. shutdown = i8259A_shutdown ,
2005-04-17 02:20:36 +04:00
} ;
2007-09-13 06:04:04 +04:00
static void init_8259A ( int auto_eoi )
2005-04-17 02:20:36 +04:00
{
unsigned long flags ;
2006-12-06 20:04:17 +03:00
i8259A_auto_eoi = auto_eoi ;
2010-02-27 14:53:38 +03:00
raw_spin_lock_irqsave ( & i8259A_lock , flags ) ;
2005-04-17 02:20:36 +04:00
2006-12-06 20:04:17 +03:00
outb ( 0xff , PIC_MASTER_IMR ) ; /* mask all of 8259A-1 */
outb ( 0xff , PIC_SLAVE_IMR ) ; /* mask all of 8259A-2 */
2005-04-17 02:20:36 +04:00
/*
* outb_p - this has to work on a wide range of PC hardware .
*/
2006-12-06 20:04:17 +03:00
outb_p ( 0x11 , PIC_MASTER_CMD ) ; /* ICW1: select 8259A-1 init */
outb_p ( I8259A_IRQ_BASE + 0 , PIC_MASTER_IMR ) ; /* ICW2: 8259A-1 IR0 mapped to I8259A_IRQ_BASE + 0x00 */
outb_p ( 1U < < PIC_CASCADE_IR , PIC_MASTER_IMR ) ; /* 8259A-1 (the master) has a slave on IR2 */
if ( auto_eoi ) /* master does Auto EOI */
outb_p ( MASTER_ICW4_DEFAULT | PIC_ICW4_AEOI , PIC_MASTER_IMR ) ;
else /* master expects normal EOI */
outb_p ( MASTER_ICW4_DEFAULT , PIC_MASTER_IMR ) ;
outb_p ( 0x11 , PIC_SLAVE_CMD ) ; /* ICW1: select 8259A-2 init */
outb_p ( I8259A_IRQ_BASE + 8 , PIC_SLAVE_IMR ) ; /* ICW2: 8259A-2 IR0 mapped to I8259A_IRQ_BASE + 0x08 */
outb_p ( PIC_CASCADE_IR , PIC_SLAVE_IMR ) ; /* 8259A-2 is a slave on master's IR2 */
outb_p ( SLAVE_ICW4_DEFAULT , PIC_SLAVE_IMR ) ; /* (slave's support for AEOI in flat mode is to be investigated) */
2005-04-17 02:20:36 +04:00
if ( auto_eoi )
/*
2006-12-06 20:04:17 +03:00
* In AEOI mode we just have to mask the interrupt
2005-04-17 02:20:36 +04:00
* when acking .
*/
2011-03-24 00:08:57 +03:00
i8259A_chip . irq_mask_ack = disable_8259A_irq ;
2005-04-17 02:20:36 +04:00
else
2011-03-24 00:08:57 +03:00
i8259A_chip . irq_mask_ack = mask_and_ack_8259A ;
2005-04-17 02:20:36 +04:00
udelay ( 100 ) ; /* wait for 8259A to initialize */
2006-12-06 20:04:17 +03:00
outb ( cached_master_mask , PIC_MASTER_IMR ) ; /* restore master IRQ mask */
outb ( cached_slave_mask , PIC_SLAVE_IMR ) ; /* restore slave IRQ mask */
2005-04-17 02:20:36 +04:00
2010-02-27 14:53:38 +03:00
raw_spin_unlock_irqrestore ( & i8259A_lock , flags ) ;
2005-04-17 02:20:36 +04:00
}
/*
* IRQ2 is cascade interrupt to second interrupt controller
*/
static struct irqaction irq2 = {
2007-08-28 13:03:01 +04:00
. handler = no_action ,
. name = " cascade " ,
2011-08-26 03:00:04 +04:00
. flags = IRQF_NO_THREAD ,
2005-04-17 02:20:36 +04:00
} ;
static struct resource pic1_io_resource = {
2006-12-06 20:04:17 +03:00
. name = " pic1 " ,
. start = PIC_MASTER_CMD ,
. end = PIC_MASTER_IMR ,
2017-12-05 03:08:39 +03:00
. flags = IORESOURCE_IO | IORESOURCE_BUSY
2005-04-17 02:20:36 +04:00
} ;
static struct resource pic2_io_resource = {
2006-12-06 20:04:17 +03:00
. name = " pic2 " ,
. start = PIC_SLAVE_CMD ,
. end = PIC_SLAVE_IMR ,
2017-12-05 03:08:39 +03:00
. flags = IORESOURCE_IO | IORESOURCE_BUSY
2005-04-17 02:20:36 +04:00
} ;
2014-09-19 01:47:11 +04:00
static int i8259A_irq_domain_map ( struct irq_domain * d , unsigned int virq ,
irq_hw_number_t hw )
{
irq_set_chip_and_handler ( virq , & i8259A_chip , handle_level_irq ) ;
irq_set_probe ( virq ) ;
return 0 ;
}
2017-06-02 11:20:53 +03:00
static const struct irq_domain_ops i8259A_ops = {
2014-09-19 01:47:11 +04:00
. map = i8259A_irq_domain_map ,
. xlate = irq_domain_xlate_onecell ,
} ;
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 .
*/
2015-05-22 18:51:03 +03:00
struct irq_domain * __init __init_i8259_irqs ( struct device_node * node )
2005-04-17 02:20:36 +04:00
{
2014-09-19 01:47:11 +04:00
struct irq_domain * domain ;
2005-04-17 02:20:36 +04:00
2007-04-08 15:28:44 +04:00
insert_resource ( & ioport_resource , & pic1_io_resource ) ;
insert_resource ( & ioport_resource , & pic2_io_resource ) ;
2005-04-17 02:20:36 +04:00
init_8259A ( 0 ) ;
2015-05-22 18:51:03 +03:00
domain = irq_domain_add_legacy ( node , 16 , I8259A_IRQ_BASE , 0 ,
2014-09-19 01:47:11 +04:00
& i8259A_ops , NULL ) ;
if ( ! domain )
panic ( " Failed to add i8259 IRQ domain " ) ;
2005-04-17 02:20:36 +04:00
2007-01-14 17:41:42 +03:00
setup_irq ( I8259A_IRQ_BASE + PIC_CASCADE_IR , & irq2 ) ;
2019-02-07 00:26:08 +03:00
register_syscore_ops ( & i8259_syscore_ops ) ;
2015-05-22 18:51:03 +03:00
return domain ;
}
void __init init_i8259_irqs ( void )
{
__init_i8259_irqs ( NULL ) ;
}
2015-09-14 11:42:37 +03:00
static void i8259_irq_dispatch ( struct irq_desc * desc )
2015-05-22 18:51:03 +03:00
{
2015-07-31 22:59:10 +03:00
struct irq_domain * domain = irq_desc_get_handler_data ( desc ) ;
2016-09-20 00:21:19 +03:00
int hwirq = i8259_poll ( ) ;
2015-07-31 22:59:10 +03:00
unsigned int irq ;
2015-05-22 18:51:03 +03:00
if ( hwirq < 0 )
return ;
irq = irq_linear_revmap ( domain , hwirq ) ;
generic_handle_irq ( irq ) ;
}
int __init i8259_of_init ( struct device_node * node , struct device_node * parent )
{
struct irq_domain * domain ;
unsigned int parent_irq ;
2016-09-20 00:21:18 +03:00
domain = __init_i8259_irqs ( node ) ;
2015-05-22 18:51:03 +03:00
parent_irq = irq_of_parse_and_map ( node , 0 ) ;
if ( ! parent_irq ) {
pr_err ( " Failed to map i8259 parent IRQ \n " ) ;
2016-09-20 00:21:18 +03:00
irq_domain_remove ( domain ) ;
2015-05-22 18:51:03 +03:00
return - ENODEV ;
}
2015-10-01 17:26:44 +03:00
irq_set_chained_handler_and_data ( parent_irq , i8259_irq_dispatch ,
domain ) ;
2015-05-22 18:51:03 +03:00
return 0 ;
2005-04-17 02:20:36 +04:00
}
2015-05-22 18:51:03 +03:00
IRQCHIP_DECLARE ( i8259 , " intel,i8259 " , i8259_of_init ) ;