diff options
Diffstat (limited to 'bsps/powerpc/shared/irq/i8259.c')
-rw-r--r-- | bsps/powerpc/shared/irq/i8259.c | 152 |
1 files changed, 117 insertions, 35 deletions
diff --git a/bsps/powerpc/shared/irq/i8259.c b/bsps/powerpc/shared/irq/i8259.c index 7363e87ba0..6a80e24946 100644 --- a/bsps/powerpc/shared/irq/i8259.c +++ b/bsps/powerpc/shared/irq/i8259.c @@ -12,6 +12,19 @@ #include <bsp.h> #include <bsp/irq.h> +#define PIC_EOSI 0x60 ///< End of Specific Interrupt (EOSI) +#define PIC_EOI 0x20 ///< Generic End of Interrupt (EOI) + +/* Operation control word type 3. Bit 3 (0x08) must be set. Even address. */ +#define PIC_OCW3_RIS 0x01 /* 1 = read IS, 0 = read IR */ +#define PIC_OCW3_RR 0x02 /* register read */ +#define PIC_OCW3_P 0x04 /* poll mode command */ +/* 0x08 must be 1 to select OCW3 vs OCW2 */ +#define PIC_OCW3_SEL 0x08 /* must be 1 */ +/* 0x10 must be 0 to select OCW3 vs ICW1 */ +#define PIC_OCW3_SMM 0x20 /* special mode mask */ +#define PIC_OCW3_ESMM 0x40 /* enable SMM */ + /*-------------------------------------------------------------------------+ | Cache for 1st and 2nd PIC IRQ line's status (enabled or disabled) register. +--------------------------------------------------------------------------*/ @@ -19,91 +32,137 @@ * lower byte is interrupt mask on the master PIC. * while upper bits are interrupt on the slave PIC. */ -volatile rtems_i8259_masks i8259s_cache = 0xfffb; +static rtems_i8259_masks i8259s_imr_cache = 0xFFFB; +static rtems_i8259_masks i8259s_in_progress = 0; + +static inline +void BSP_i8259s_irq_update_master_imr( void ) +{ + rtems_i8259_masks mask = i8259s_in_progress | i8259s_imr_cache; + outport_byte( PIC_MASTER_IMR_IO_PORT, mask & 0xff ); +} + +static inline +void BSP_i8259s_irq_update_slave_imr( void ) +{ + rtems_i8259_masks mask = i8259s_in_progress | i8259s_imr_cache; + outport_byte( PIC_SLAVE_IMR_IO_PORT, ( mask >> 8 ) & 0xff ); +} + +/* + * Is the IRQ valid? + */ +static inline bool BSP_i8259s_irq_valid(const rtems_irq_number irqLine) +{ + return ((int)irqLine >= BSP_ISA_IRQ_LOWEST_OFFSET) && + ((int)irqLine <= BSP_ISA_IRQ_MAX_OFFSET); +} + +/* + * Read the IRR register. The default. + */ +static inline uint8_t BSP_i8259s_irq_int_request_reg(uint32_t ioport) +{ + uint8_t isr; + inport_byte(ioport, isr); + return isr; +} + +/* + * Read the ISR register. Keep the default of the IRR. + */ +static inline uint8_t BSP_i8259s_irq_in_service_reg(uint32_t ioport) +{ + uint8_t isr; + outport_byte(ioport, PIC_OCW3_SEL | PIC_OCW3_RR | PIC_OCW3_RIS); + inport_byte(ioport, isr); + outport_byte(ioport, PIC_OCW3_SEL | PIC_OCW3_RR); + return isr; +} /*-------------------------------------------------------------------------+ | Function: BSP_irq_disable_at_i8259s | Description: Mask IRQ line in appropriate PIC chip. -| Global Variables: i8259s_cache +| Global Variables: i8259s_imr_cache, i8259s_in_progress | Arguments: vector_offset - number of IRQ line to mask. -| Returns: original state or -1 on error. +| Returns: 0 is OK. +--------------------------------------------------------------------------*/ -int BSP_irq_disable_at_i8259s (const rtems_irq_number irqLine) +int BSP_irq_disable_at_i8259s(const rtems_irq_number irqLine) { unsigned short mask; rtems_interrupt_level level; - int rval; - if ( ((int)irqLine < BSP_ISA_IRQ_LOWEST_OFFSET) || - ((int)irqLine > BSP_ISA_IRQ_MAX_OFFSET) - ) + if (!BSP_i8259s_irq_valid(irqLine)) return -1; rtems_interrupt_disable(level); mask = 1 << irqLine; - rval = i8259s_cache & mask ? 0 : 1; - i8259s_cache |= mask; + i8259s_imr_cache |= mask; if (irqLine < 8) { - outport_byte(PIC_MASTER_IMR_IO_PORT, i8259s_cache & 0xff); + BSP_i8259s_irq_update_master_imr(); } else { - outport_byte(PIC_SLAVE_IMR_IO_PORT, ((i8259s_cache & 0xff00) >> 8)); + BSP_i8259s_irq_update_slave_imr(); } + rtems_interrupt_enable(level); - return rval; + return 0; } /*-------------------------------------------------------------------------+ | Function: BSP_irq_enable_at_i8259s | Description: Unmask IRQ line in appropriate PIC chip. -| Global Variables: i8259s_cache +| Global Variables: i8259s_imr_cache, i8259s_in_progress | Arguments: irqLine - number of IRQ line to mask. | Returns: Nothing. +--------------------------------------------------------------------------*/ -int BSP_irq_enable_at_i8259s (const rtems_irq_number irqLine) +int BSP_irq_enable_at_i8259s(const rtems_irq_number irqLine) { - unsigned short mask; rtems_interrupt_level level; + unsigned short mask; + uint8_t isr; + uint8_t irr; - if ( ((int)irqLine < BSP_ISA_IRQ_LOWEST_OFFSET) || - ((int)irqLine > BSP_ISA_IRQ_MAX_OFFSET ) - ) + if (!BSP_i8259s_irq_valid(irqLine)) return 1; rtems_interrupt_disable(level); - mask = ~(1 << irqLine); - i8259s_cache &= mask; + mask = 1 << irqLine; + i8259s_imr_cache &= ~mask; if (irqLine < 8) { - outport_byte(PIC_MASTER_IMR_IO_PORT, i8259s_cache & 0xff); + isr = BSP_i8259s_irq_in_service_reg(PIC_MASTER_COMMAND_IO_PORT); + irr = BSP_i8259s_irq_int_request_reg(PIC_MASTER_COMMAND_IO_PORT); + BSP_i8259s_irq_update_master_imr(); } else { - outport_byte(PIC_SLAVE_IMR_IO_PORT, ((i8259s_cache & 0xff00) >> 8)); + isr = BSP_i8259s_irq_in_service_reg(PIC_SLAVE_COMMAND_IO_PORT); + irr = BSP_i8259s_irq_int_request_reg(PIC_SLAVE_COMMAND_IO_PORT); + BSP_i8259s_irq_update_slave_imr(); } + rtems_interrupt_enable(level); return 0; } /* mask_irq */ -int BSP_irq_enabled_at_i8259s (const rtems_irq_number irqLine) +int BSP_irq_enabled_at_i8259s(const rtems_irq_number irqLine) { unsigned short mask; - if ( ((int)irqLine < BSP_ISA_IRQ_LOWEST_OFFSET) || - ((int)irqLine > BSP_ISA_IRQ_MAX_OFFSET) - ) + if (!BSP_i8259s_irq_valid(irqLine)) return 1; mask = (1 << irqLine); - return (~(i8259s_cache & mask)); + return (~(i8259s_imr_cache & mask)); } /*-------------------------------------------------------------------------+ @@ -113,24 +172,47 @@ int BSP_irq_enabled_at_i8259s (const rtems_irq_number irqLine) | Arguments: irqLine - number of IRQ line to acknowledge. | Returns: Nothing. +--------------------------------------------------------------------------*/ -int BSP_irq_ack_at_i8259s (const rtems_irq_number irqLine) +int BSP_irq_ack_at_i8259s(const rtems_irq_number irqLine) { + uint8_t slave_isr = 0; + if (irqLine >= 8) { - outport_byte(PIC_MASTER_COMMAND_IO_PORT, SLAVE_PIC_EOSI); - outport_byte(PIC_SLAVE_COMMAND_IO_PORT, (PIC_EOSI | (irqLine - 8))); - } - else { - outport_byte(PIC_MASTER_COMMAND_IO_PORT, (PIC_EOSI | irqLine)); + outport_byte(PIC_SLAVE_COMMAND_IO_PORT, PIC_EOI); + slave_isr = BSP_i8259s_irq_in_service_reg(PIC_SLAVE_COMMAND_IO_PORT); } + /* + * Only issue the EOI to the master if there are no more interrupts in + * service for the slave. i8259a data sheet page 18, The Special Fully Nested + * Mode, b. + */ + if (slave_isr == 0) + outport_byte(PIC_MASTER_COMMAND_IO_PORT, PIC_EOI); + return 0; } /* ackIRQ */ +unsigned short BSP_irq_suspend_i8259s(unsigned short mask) +{ + unsigned short in_progress_save = i8259s_in_progress; + i8259s_in_progress |= mask; + BSP_i8259s_irq_update_master_imr(); + BSP_i8259s_irq_update_slave_imr(); + return in_progress_save; +} + +void BSP_irq_resume_i8259s(unsigned short in_progress_save) +{ + i8259s_in_progress = in_progress_save; + BSP_i8259s_irq_update_master_imr(); + BSP_i8259s_irq_update_slave_imr(); +} + void BSP_i8259s_init(void) { /* - * init master 8259 interrupt controller + * Always mask at least current interrupt to prevent re-entrance */ outport_byte(PIC_MASTER_COMMAND_IO_PORT, 0x11); /* Start init sequence */ outport_byte(PIC_MASTER_IMR_IO_PORT, 0x00);/* Vector base = 0 */ |