summaryrefslogtreecommitdiffstats
path: root/bsps/powerpc/shared/irq/i8259.c
diff options
context:
space:
mode:
Diffstat (limited to 'bsps/powerpc/shared/irq/i8259.c')
-rw-r--r--bsps/powerpc/shared/irq/i8259.c152
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 */