[PATCH 5/8] i386/pc386: Fix interrupt support.
Chris Johns
chrisj at rtems.org
Fri May 6 22:23:49 UTC 2016
Fix the interrupt and stop the spurious interrupt from happening.
The fix moves the EOI to C code and cleans that functionality out
of the asm part of the ISR handler.
The code checks the ISR and IRR registers on the enable.
Only ack the master for a slave IRQ if the slave has no other pending
requests.
---
c/src/lib/libbsp/i386/Makefile.am | 1 +
c/src/lib/libbsp/i386/pc386/Makefile.am | 1 +
c/src/lib/libbsp/i386/shared/irq/elcr.c | 170 +++++++++++++++++
c/src/lib/libbsp/i386/shared/irq/elcr.h | 37 ++++
c/src/lib/libbsp/i386/shared/irq/irq.c | 278 ++++++++++++++++++++--------
c/src/lib/libbsp/i386/shared/irq/irq.h | 55 +++---
c/src/lib/libbsp/i386/shared/irq/irq_asm.S | 81 +-------
c/src/lib/libbsp/i386/shared/irq/irq_asm.h | 16 +-
c/src/lib/libbsp/i386/shared/irq/irq_init.c | 4 +-
9 files changed, 471 insertions(+), 172 deletions(-)
create mode 100644 c/src/lib/libbsp/i386/shared/irq/elcr.c
create mode 100644 c/src/lib/libbsp/i386/shared/irq/elcr.h
diff --git a/c/src/lib/libbsp/i386/Makefile.am b/c/src/lib/libbsp/i386/Makefile.am
index 3b6df65..567f75f 100644
--- a/c/src/lib/libbsp/i386/Makefile.am
+++ b/c/src/lib/libbsp/i386/Makefile.am
@@ -19,6 +19,7 @@ EXTRA_DIST += shared/irq/irq.h shared/irq/irq.c
EXTRA_DIST += shared/irq/irq_asm.h shared/irq/irq_asm.S
EXTRA_DIST += shared/irq/idt.c
EXTRA_DIST += shared/irq/irq_init.c
+EXTRA_DIST += shared/irq/elcr.c
# shared/pci
EXTRA_DIST += shared/pci/pcibios.c
diff --git a/c/src/lib/libbsp/i386/pc386/Makefile.am b/c/src/lib/libbsp/i386/pc386/Makefile.am
index 22d4bf1..d051da8 100644
--- a/c/src/lib/libbsp/i386/pc386/Makefile.am
+++ b/c/src/lib/libbsp/i386/pc386/Makefile.am
@@ -161,6 +161,7 @@ libbsp_a_SOURCES += startup/bspreset.c
libbsp_a_SOURCES += ../../i386/shared/irq/idt.c
libbsp_a_SOURCES += ../../i386/shared/irq/irq.c
libbsp_a_SOURCES += ../../i386/shared/irq/irq_init.c
+libbsp_a_SOURCES += ../../i386/shared/irq/elcr.c
libbsp_a_SOURCES += ../../shared/bootcard.c
libbsp_a_SOURCES += ../../shared/sbrk.c
libbsp_a_SOURCES += startup/ldsegs.S
diff --git a/c/src/lib/libbsp/i386/shared/irq/elcr.c b/c/src/lib/libbsp/i386/shared/irq/elcr.c
new file mode 100644
index 0000000..f72e36c
--- /dev/null
+++ b/c/src/lib/libbsp/i386/shared/irq/elcr.c
@@ -0,0 +1,170 @@
+/*-
+ * Copyright (c) 2004 John Baldwin <jhb at FreeBSD.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+#ifndef __rtems__
+__FBSDID("$FreeBSD$");
+#endif /* __rtems__ */
+
+/*
+ * The ELCR is a register that controls the trigger mode and polarity of
+ * EISA and ISA interrupts. In FreeBSD 3.x and 4.x, the ELCR was only
+ * consulted for determining the appropriate trigger mode of EISA
+ * interrupts when using an APIC. However, it seems that almost all
+ * systems that include PCI also include an ELCR that manages the ISA
+ * IRQs 0 through 15. Thus, we check for the presence of an ELCR on
+ * every machine by checking to see if the values found at bootup are
+ * sane. Note that the polarity of ISA and EISA IRQs are linked to the
+ * trigger mode. All edge triggered IRQs use active-hi polarity, and
+ * all level triggered interrupts use active-lo polarity.
+ *
+ * The format of the ELCR is simple: it is a 16-bit bitmap where bit 0
+ * controls IRQ 0, bit 1 controls IRQ 1, etc. If the bit is zero, the
+ * associated IRQ is edge triggered. If the bit is one, the IRQ is
+ * level triggered.
+ */
+
+#ifndef __rtems__
+#include <sys/param.h>
+#include <sys/bus.h>
+#include <sys/systm.h>
+#include <machine/intr_machdep.h>
+#endif /* __rtems__ */
+
+#ifdef __rtems__
+#include <bsp.h>
+#include "i386_io.h"
+#include <errno.h>
+#include "elcr.h"
+#endif /* __rtems__ */
+
+#define ELCR_PORT 0x4d0
+#define ELCR_MASK(irq) (1 << (irq))
+
+static int elcr_status;
+#ifdef __rtems__
+static
+#endif /* __rtems__ */
+int elcr_found;
+
+#ifdef __rtems__
+#undef printf
+#define printf printk
+#define bootverbose 1
+#define KASSERT(...)
+#endif /* __rtems__ */
+
+/*
+ * Check to see if we have what looks like a valid ELCR. We do this by
+ * verifying that IRQs 0, 1, 2, and 13 are all edge triggered.
+ */
+int
+elcr_probe(void)
+{
+ int i;
+
+ elcr_status = inb(ELCR_PORT) | inb(ELCR_PORT + 1) << 8;
+ if ((elcr_status & (ELCR_MASK(0) | ELCR_MASK(1) | ELCR_MASK(2) |
+ ELCR_MASK(8) | ELCR_MASK(13))) != 0)
+ return (ENXIO);
+ if (bootverbose) {
+ printf("ELCR Found. ISA IRQs programmed as:\n");
+ for (i = 0; i < 16; i++)
+ printf(" %2d", i);
+ printf("\n");
+ for (i = 0; i < 16; i++)
+ if (elcr_status & ELCR_MASK(i))
+ printf(" L");
+ else
+ printf(" E");
+ printf("\n");
+ }
+#ifndef __rtems__
+ if (resource_disabled("elcr", 0))
+ return (ENXIO);
+#endif /* __rtems__ */
+ elcr_found = 1;
+ return (0);
+}
+
+/*
+ * Returns 1 for level trigger, 0 for edge.
+ */
+enum intr_trigger
+elcr_read_trigger(u_int irq)
+{
+#ifdef __rtems__
+ if (!elcr_found)
+ return INTR_TRIGGER_EDGE;
+#endif /* __rtems__ */
+ KASSERT(elcr_found, ("%s: no ELCR was found!", __func__));
+ KASSERT(irq <= 15, ("%s: invalid IRQ %u", __func__, irq));
+ if (elcr_status & ELCR_MASK(irq))
+ return (INTR_TRIGGER_LEVEL);
+ else
+ return (INTR_TRIGGER_EDGE);
+}
+
+/*
+ * Set the trigger mode for a specified IRQ. Mode of 0 means edge triggered,
+ * and a mode of 1 means level triggered.
+ */
+void
+elcr_write_trigger(u_int irq, enum intr_trigger trigger)
+{
+ int new_status;
+
+#ifdef __rtems__
+ if (!elcr_found)
+ return;
+#endif /* __rtems__ */
+ KASSERT(elcr_found, ("%s: no ELCR was found!", __func__));
+ KASSERT(irq <= 15, ("%s: invalid IRQ %u", __func__, irq));
+ if (trigger == INTR_TRIGGER_LEVEL)
+ new_status = elcr_status | ELCR_MASK(irq);
+ else
+ new_status = elcr_status & ~ELCR_MASK(irq);
+ if (new_status == elcr_status)
+ return;
+ elcr_status = new_status;
+ if (irq >= 8)
+ outb(ELCR_PORT + 1, elcr_status >> 8);
+ else
+ outb(ELCR_PORT, elcr_status & 0xff);
+}
+
+void
+elcr_resume(void)
+{
+#ifdef __rtems__
+ if (!elcr_found)
+ return;
+#endif /* __rtems__ */
+
+ KASSERT(elcr_found, ("%s: no ELCR was found!", __func__));
+ outb(ELCR_PORT, elcr_status & 0xff);
+ outb(ELCR_PORT + 1, elcr_status >> 8);
+}
diff --git a/c/src/lib/libbsp/i386/shared/irq/elcr.h b/c/src/lib/libbsp/i386/shared/irq/elcr.h
new file mode 100644
index 0000000..a006d4f
--- /dev/null
+++ b/c/src/lib/libbsp/i386/shared/irq/elcr.h
@@ -0,0 +1,37 @@
+/*
+ * Copyright 2016 Chris Johns <chrisj at rtems.org>
+ *
+ * Header for the FreeBSD ported elcr.c
+ */
+
+#ifndef _IRQ_ELCR_H_
+#define _IRQ_ELCR_H_
+
+#include <sys/cdefs.h>
+
+enum intr_trigger {
+ INTR_TRIGGER_EDGE,
+ INTR_TRIGGER_LEVEL
+};
+
+/*
+ * Check to see if we have what looks like a valid ELCR. We do this by
+ * verifying that IRQs 0, 1, 2, and 13 are all edge triggered.
+ */
+int elcr_probe(void);
+
+/*
+ * Returns 1 for level trigger, 0 for edge.
+ */
+enum intr_trigger elcr_read_trigger(u_int irq);
+
+/*
+ * Set the trigger mode for a specified IRQ. Mode of 0 means edge triggered,
+ * and a mode of 1 means level triggered.
+ */
+void elcr_write_trigger(u_int irq, enum intr_trigger trigger);
+
+void elcr_resume(void);
+
+
+#endif
diff --git a/c/src/lib/libbsp/i386/shared/irq/irq.c b/c/src/lib/libbsp/i386/shared/irq/irq.c
index 2bee764..0511257 100644
--- a/c/src/lib/libbsp/i386/shared/irq/irq.c
+++ b/c/src/lib/libbsp/i386/shared/irq/irq.c
@@ -19,6 +19,9 @@
#include <stdio.h>
#include <inttypes.h>
+
+#include "elcr.h"
+
/*
* pointer to the mask representing the additionnal irq vectors
* that must be disabled when a particular entry is activated.
@@ -27,138 +30,174 @@
* CAUTION : this table is accessed directly by interrupt routine
* prologue.
*/
-rtems_i8259_masks irq_mask_or_tbl[BSP_IRQ_LINES_NUMBER];
+static rtems_i8259_masks irq_mask_or_tbl[BSP_IRQ_LINES_NUMBER];
-uint32_t irq_count[BSP_IRQ_LINES_NUMBER] = {0};
+/*
+ * Stats of interrupts dispatched.
+ */
+static uint32_t irq_count[BSP_IRQ_VECTOR_NUMBER] = {0};
+static uint32_t spurious_count;
-uint32_t
-BSP_irq_count_dump(FILE *f)
-{
-uint32_t tot = 0;
-int i;
- if ( !f )
- f = stdout;
- for ( i=0; i<BSP_IRQ_LINES_NUMBER; i++ ) {
- tot += irq_count[i];
- fprintf(f,"IRQ %2u: %9"PRIu32"\n", i, irq_count[i]);
- }
- return tot;
-}
+/*
+ * Edge or level trigger interrupts.
+ */
+static enum intr_trigger irq_trigger[BSP_IRQ_LINES_NUMBER];
/*-------------------------------------------------------------------------+
-| Cache for 1st and 2nd PIC IRQ line's status (enabled or disabled) register.
+| Cache for 1st and 2nd PIC IRQ line's mssk (enabled or disabled) register.
+--------------------------------------------------------------------------*/
/*
* lower byte is interrupt mask on the master PIC.
* while upper bits are interrupt on the slave PIC.
* This cache is initialized in ldseg.s
*/
-rtems_i8259_masks i8259s_cache = 0xFFFB;
-rtems_i8259_masks i8259s_super_imr = 0xFFFB;
+static rtems_i8259_masks i8259a_cache = 0xFFFB;
+
+/*
+ * Print the stats.
+ */
+uint32_t BSP_irq_count_dump(FILE *f)
+{
+ uint32_t tot = 0;
+ int i;
+ if ( !f )
+ f = stdout;
+ fprintf(f,"SPURIOUS: %9"PRIu32"\n", spurious_count);
+ for ( i = 0; i < BSP_IRQ_VECTOR_NUMBER; i++ ) {
+ char type = '-';
+ if (i < BSP_IRQ_LINES_NUMBER)
+ type = irq_trigger[i] == INTR_TRIGGER_EDGE ? 'E' : 'L';
+ tot += irq_count[i];
+ fprintf(f,"IRQ %2u: %c %9"PRIu32"\n", i, type, irq_count[i]);
+ }
+ return tot;
+}
+
+/*
+ * Is the IRQ valid?
+ */
+static inline bool BSP_i8259a_irq_valid(const rtems_irq_number irqLine)
+{
+ return ((int)irqLine >= BSP_IRQ_VECTOR_LOWEST_OFFSET) &&
+ ((int)irqLine <= BSP_IRQ_MAX_ON_i8259A);
+}
+
+/*
+ * Read the IRR register. The default.
+ */
+static inline uint8_t BSP_i8259a_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_i8259a_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
+| Function: BSP_irq_disable_at_i8259a
| Description: Mask IRQ line in appropriate PIC chip.
-| Global Variables: i8259s_cache
+| Global Variables: i8259a_cache
| Arguments: vector_offset - number of IRQ line to mask.
-| Returns: Nothing.
+| Returns: 0 is OK.
+--------------------------------------------------------------------------*/
-int BSP_irq_disable_at_i8259s (const rtems_irq_number irqLine)
+static int BSP_irq_disable_at_i8259a(const rtems_irq_number irqLine)
{
unsigned short mask;
rtems_interrupt_level level;
- if ( ((int)irqLine < BSP_LOWEST_OFFSET) ||
- ((int)irqLine > BSP_MAX_ON_i8259S )
- )
- return 1;
-
rtems_interrupt_disable(level);
mask = 1 << irqLine;
- i8259s_cache |= mask;
- i8259s_super_imr |= mask;
+ i8259a_cache |= mask;
if (irqLine < 8)
{
- outport_byte(PIC_MASTER_IMR_IO_PORT, i8259s_cache & 0xff);
+ outport_byte(PIC_MASTER_IMR_IO_PORT, i8259a_cache & 0xff);
}
else
{
- outport_byte(PIC_SLAVE_IMR_IO_PORT, ((i8259s_cache & 0xff00) >> 8));
+ outport_byte(PIC_SLAVE_IMR_IO_PORT, (i8259a_cache >> 8) & 0xff);
}
+
rtems_interrupt_enable(level);
return 0;
}
/*-------------------------------------------------------------------------+
-| Function: BSP_irq_enable_at_i8259s
+| Function: BSP_irq_enable_at_i8259a
| Description: Unmask IRQ line in appropriate PIC chip.
-| Global Variables: i8259s_cache
+| Global Variables: i8259a_cache
| Arguments: irqLine - number of IRQ line to mask.
| Returns: Nothing.
+--------------------------------------------------------------------------*/
-int BSP_irq_enable_at_i8259s (const rtems_irq_number irqLine)
+static int BSP_irq_enable_at_i8259a(const rtems_irq_number irqLine)
{
unsigned short mask;
rtems_interrupt_level level;
-
- if ( ((int)irqLine < BSP_LOWEST_OFFSET) ||
- ((int)irqLine > BSP_MAX_ON_i8259S )
- )
- return 1;
+ uint8_t isr;
+ uint8_t irr;
rtems_interrupt_disable(level);
- mask = ~(1 << irqLine);
- i8259s_cache &= mask;
- i8259s_super_imr &= mask;
+ mask = 1 << irqLine;
+ i8259a_cache &= ~mask;
if (irqLine < 8)
{
- outport_byte(PIC_MASTER_IMR_IO_PORT, i8259s_cache & 0xff);
+ isr = BSP_i8259a_irq_in_service_reg(PIC_MASTER_COMMAND_IO_PORT);
+ irr = BSP_i8259a_irq_int_request_reg(PIC_MASTER_COMMAND_IO_PORT);
+ outport_byte(PIC_MASTER_IMR_IO_PORT, i8259a_cache & 0xff);
}
else
{
- outport_byte(PIC_SLAVE_IMR_IO_PORT, ((i8259s_cache & 0xff00) >> 8));
+ isr = BSP_i8259a_irq_in_service_reg(PIC_SLAVE_COMMAND_IO_PORT);
+ irr = BSP_i8259a_irq_int_request_reg(PIC_SLAVE_COMMAND_IO_PORT);
+ outport_byte(PIC_SLAVE_IMR_IO_PORT, (i8259a_cache >> 8) & 0xff);
}
+
+ if (((isr ^ irr) & mask) != 0)
+ printk("i386: isr=%x irr=%x\n", isr, irr);
+
rtems_interrupt_enable(level);
return 0;
} /* mask_irq */
-int BSP_irq_enabled_at_i8259s (const rtems_irq_number irqLine)
-{
- unsigned short mask;
-
- if ( ((int)irqLine < BSP_LOWEST_OFFSET) ||
- ((int)irqLine > BSP_MAX_ON_i8259S )
- )
- return 1;
-
- mask = (1 << irqLine);
- return (~(i8259s_cache & mask));
-}
-
/*-------------------------------------------------------------------------+
-| Function: BSP_irq_ack_at_i8259s
+| Function: BSP_irq_ack_at_i8259a
| Description: Signal generic End Of Interrupt (EOI) to appropriate PIC.
| Global Variables: None.
| Arguments: irqLine - number of IRQ line to acknowledge.
| Returns: Nothing.
+--------------------------------------------------------------------------*/
-int BSP_irq_ack_at_i8259s (const rtems_irq_number irqLine)
+static int BSP_irq_ack_at_i8259a(const rtems_irq_number irqLine)
{
- if ( ((int)irqLine < BSP_LOWEST_OFFSET) ||
- ((int)irqLine > BSP_MAX_ON_i8259S )
- )
- return 1;
+ uint8_t slave_isr = 0;
if (irqLine >= 8) {
outport_byte(PIC_SLAVE_COMMAND_IO_PORT, PIC_EOI);
+ slave_isr = BSP_i8259a_irq_in_service_reg(PIC_SLAVE_COMMAND_IO_PORT);
}
- outport_byte(PIC_MASTER_COMMAND_IO_PORT, PIC_EOI);
+
+ /*
+ * 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;
@@ -179,7 +218,7 @@ static rtems_irq_prio irqPrioTable[BSP_IRQ_LINES_NUMBER]={
*/
0,0,
255,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
};
static void compute_i8259_masks_from_prio (void)
@@ -208,22 +247,29 @@ static void compute_i8259_masks_from_prio (void)
rtems_interrupt_enable(level);
}
-rtems_status_code bsp_interrupt_vector_enable(rtems_vector_number vector)
+static inline bool bsp_interrupt_vector_is_valid(rtems_vector_number vector)
{
- BSP_irq_enable_at_i8259s(vector);
+ return BSP_i8259a_irq_valid((const rtems_irq_number) vector);
+}
+rtems_status_code bsp_interrupt_vector_enable(rtems_vector_number vector)
+{
+ if (bsp_interrupt_vector_is_valid(vector))
+ BSP_irq_enable_at_i8259a(vector);
return RTEMS_SUCCESSFUL;
}
rtems_status_code bsp_interrupt_vector_disable(rtems_vector_number vector)
{
- BSP_irq_disable_at_i8259s(vector);
-
+ if (bsp_interrupt_vector_is_valid(vector))
+ BSP_irq_disable_at_i8259a(vector);
return RTEMS_SUCCESSFUL;
}
rtems_status_code bsp_interrupt_facility_initialize(void)
{
+ int i;
+
/*
* set up internal tables used by rtems interrupt prologue
*/
@@ -232,13 +278,99 @@ rtems_status_code bsp_interrupt_facility_initialize(void)
/*
* must enable slave pic anyway
*/
- BSP_irq_enable_at_i8259s(2);
+ BSP_irq_enable_at_i8259a(2);
+
+ /*
+ * Probe the ELCR.
+ */
+ elcr_probe();
+
+ for (i = 0; i < BSP_IRQ_LINES_NUMBER; i++)
+ irq_trigger[i] = elcr_read_trigger(i);
return RTEMS_SUCCESSFUL;
}
-void C_dispatch_isr(int vector)
+/*
+ * Global so the asm handler can call it.
+ */
+void BSP_dispatch_isr(int vector);
+
+void BSP_dispatch_isr(int vector)
{
- irq_count[vector]++;
- bsp_interrupt_handler_dispatch(vector);
+ uint16_t old_imr = 0;
+
+ if (vector < BSP_IRQ_VECTOR_NUMBER) {
+ /*
+ * Hardware?
+ */
+ if (vector <= BSP_IRQ_MAX_ON_i8259A) {
+ /*
+ * See if this is a spurious interrupt.
+ */
+ if ((vector == 7 || vector == 15)) {
+ /*
+ * Only check it there no handler for 7 or 15.
+ */
+ if (bsp_interrupt_handler_is_empty(vector)) {
+ /*
+ * Read the ISR register to see if IRQ 7/15 is really pending.
+ */
+ uint8_t isr = BSP_i8259a_irq_in_service_reg(PIC_MASTER_COMMAND_IO_PORT);
+ if ((isr & (1 << 7)) == 0) {
+ ++spurious_count;
+ return;
+ }
+ }
+ }
+
+ /*
+ * Save the current cached value for the IMR. It will have the bit for this
+ * vector clear.
+ */
+ if (vector <= BSP_IRQ_MAX_ON_i8259A) {
+ old_imr = i8259a_cache;
+ i8259a_cache |= irq_mask_or_tbl[vector];
+ outport_byte(PIC_MASTER_IMR_IO_PORT, i8259a_cache & 0xff);
+ outport_byte(PIC_SLAVE_IMR_IO_PORT, (i8259a_cache >> 8) & 0xff);
+ }
+
+ /*
+ * Do not use auto-EOI as some slave PIC do not work correctly.
+ */
+ BSP_irq_ack_at_i8259a(vector);
+ }
+
+ /*
+ * Count the interrupt.
+ */
+ irq_count[vector]++;
+
+ /*
+ * Allow nesting.
+ */
+ __asm__ __volatile__("sti");
+
+ bsp_interrupt_handler_dispatch(vector);
+
+ /*
+ * Disallow nesting.
+ */
+ __asm__ __volatile__("cli");
+
+ if (vector <= BSP_IRQ_MAX_ON_i8259A) {
+ /*
+ * Put the mask back but keep this vector masked if the trigger type is
+ * level. The driver or a thread level interrupt server needs to enable it
+ * again.
+ */
+ if (vector <= BSP_IRQ_MAX_ON_i8259A) {
+ if (irq_trigger[vector] == INTR_TRIGGER_LEVEL)
+ old_imr |= 1 << vector;
+ i8259a_cache = old_imr;
+ outport_byte(PIC_MASTER_IMR_IO_PORT, i8259a_cache & 0xff);
+ outport_byte(PIC_SLAVE_IMR_IO_PORT, (i8259a_cache >> 8) & 0xff);
+ }
+ }
+ }
}
diff --git a/c/src/lib/libbsp/i386/shared/irq/irq.h b/c/src/lib/libbsp/i386/shared/irq/irq.h
index 095af42..953774e 100644
--- a/c/src/lib/libbsp/i386/shared/irq/irq.h
+++ b/c/src/lib/libbsp/i386/shared/irq/irq.h
@@ -50,30 +50,37 @@ extern "C" {
| Constants
+--------------------------------------------------------------------------*/
- /** @brief Base vector for our IRQ handlers. */
+/** @brief Base vector for our IRQ handlers. */
#define BSP_IRQ_VECTOR_BASE BSP_ASM_IRQ_VECTOR_BASE
-#define BSP_IRQ_LINES_NUMBER 17
-#define BSP_LOWEST_OFFSET 0
-#define BSP_MAX_ON_i8259S (BSP_IRQ_LINES_NUMBER - 2)
-#define BSP_MAX_OFFSET (BSP_IRQ_LINES_NUMBER - 1)
- /** @brief
- * Interrupt offset in comparison to BSP_ASM_IRQ_VECTOR_BASE
- * NB : 1) Interrupt vector number in IDT = offset + BSP_ASM_IRQ_VECTOR_BASE
- * 2) The same name should be defined on all architecture
- * so that handler connection can be unchanged.
- */
-#define BSP_PERIODIC_TIMER 0
-#define BSP_KEYBOARD 1
-#define BSP_UART_COM2_IRQ 3
-#define BSP_UART_COM1_IRQ 4
+#define BSP_IRQ_LINES_NUMBER 16
+#define BSP_IRQ_MAX_ON_i8259A (BSP_IRQ_LINES_NUMBER - 1)
+
+/*
+ * Define the number of valid vectors. This is different to the number of IRQ
+ * signals supported. Use this value to allocation vector data or range check.
+ */
+#define BSP_IRQ_VECTOR_NUMBER 17
+#define BSP_IRQ_VECTOR_LOWEST_OFFSET 0
+#define BSP_IRQ_VECTOR_MAX_OFFSET (BSP_IRQ_VECTOR_NUMBER - 1)
+
+/** @brief
+ * Interrupt offset in comparison to BSP_ASM_IRQ_VECTOR_BASE
+ * NB : 1) Interrupt vector number in IDT = offset + BSP_ASM_IRQ_VECTOR_BASE
+ * 2) The same name should be defined on all architecture
+ * so that handler connection can be unchanged.
+ */
+#define BSP_PERIODIC_TIMER 0 /* fixed on all builds of PC */
+#define BSP_KEYBOARD 1 /* fixed on all builds of PC */
+#define BSP_UART_COM2_IRQ 3 /* fixed for ISA bus */
+#define BSP_UART_COM1_IRQ 4 /* fixed for ISA bus */
#define BSP_UART_COM3_IRQ 5
#define BSP_UART_COM4_IRQ 6
#define BSP_RT_TIMER1 8
#define BSP_RT_TIMER3 10
-#define BSP_SMP_IPI 16
+#define BSP_SMP_IPI 16 /* not part of the ATPIC */
-#define BSP_INTERRUPT_VECTOR_MIN BSP_LOWEST_OFFSET
-#define BSP_INTERRUPT_VECTOR_MAX BSP_MAX_OFFSET
+#define BSP_INTERRUPT_VECTOR_MIN BSP_IRQ_VECTOR_LOWEST_OFFSET
+#define BSP_INTERRUPT_VECTOR_MAX BSP_IRQ_VECTOR_MAX_OFFSET
/** @brief
* Type definition for RTEMS managed interrupts
@@ -83,7 +90,7 @@ typedef unsigned short rtems_i8259_masks;
/**
* @brief Contains the current IMR of both i8259s.
*/
-extern rtems_i8259_masks i8259s_cache;
+//extern rtems_i8259_masks i8259s_cache;
/**
* @brief Contains the super IMR of both i8259s to overrule i8259s_cache during
@@ -92,7 +99,7 @@ extern rtems_i8259_masks i8259s_cache;
* This enables a bsp_interrupt_vector_disable() in interrupt handlers. This
* is required for the interrupt server support used by the new network stack.
*/
-extern rtems_i8259_masks i8259s_super_imr;
+//extern rtems_i8259_masks i8259s_super_imr;
/*-------------------------------------------------------------------------+
| Function Prototypes.
@@ -106,13 +113,13 @@ extern rtems_i8259_masks i8259s_super_imr;
* this function, even if the device asserts the interrupt line it will
* not be propagated further to the processor
*/
-int BSP_irq_disable_at_i8259s (const rtems_irq_number irqLine);
+//int BSP_irq_disable_at_i8259s (const rtems_irq_number irqLine);
/** @brief
* function to enable a particular irq at 8259 level. After calling
* this function, if the device asserts the interrupt line it will
* be propagated further to the processor
*/
-int BSP_irq_enable_at_i8259s (const rtems_irq_number irqLine);
+//int BSP_irq_enable_at_i8259s (const rtems_irq_number irqLine);
/** @brief
* function to acknoledge a particular irq at 8259 level. After calling
* this function, if a device asserts an enabled interrupt line it will
@@ -120,11 +127,11 @@ int BSP_irq_enable_at_i8259s (const rtems_irq_number irqLine);
* writting raw handlers as this is automagically done for rtems managed
* handlers.
*/
-int BSP_irq_ack_at_i8259s (const rtems_irq_number irqLine);
+//int BSP_irq_ack_at_i8259s (const rtems_irq_number irqLine);
/** @brief
* function to check if a particular irq is enabled at 8259 level. After calling
*/
-int BSP_irq_enabled_at_i8259s (const rtems_irq_number irqLine);
+//int BSP_irq_enabled_at_i8259s (const rtems_irq_number irqLine);
/** @} */
diff --git a/c/src/lib/libbsp/i386/shared/irq/irq_asm.S b/c/src/lib/libbsp/i386/shared/irq/irq_asm.S
index d0ed9b6..1812933 100644
--- a/c/src/lib/libbsp/i386/shared/irq/irq_asm.S
+++ b/c/src/lib/libbsp/i386/shared/irq/irq_asm.S
@@ -16,7 +16,6 @@
#include <rtems/asm.h>
#include <rtems/system.h>
#include <bspopts.h>
-#include <bsp/irq_asm.h>
#include <rtems/score/cpu.h>
#include <rtems/score/percpu.h>
@@ -28,7 +27,7 @@
/* Stack frame we use for intermediate storage */
#define ARG_OFF 0
-#define MSK_OFF 4
+#define MSK_OFF 4 /* not used any more */
#define EBX_OFF 8 /* ebx */
#define EBP_OFF 12 /* code restoring ebp/esp relies on */
#define ESP_OFF 16 /* esp being on top of ebp! */
@@ -77,7 +76,7 @@ SYM (_ISR_Handler):
* saved ebx
* saved ebp
* saved irq mask
- * vector arg to C_dispatch_isr <- aligned SP
+ * vector arg to BSP_dispatch_isr <- aligned SP
*/
movl esp, eax
subl $FRM_SIZ, esp
@@ -86,6 +85,13 @@ SYM (_ISR_Handler):
movl eax, ESP_OFF(esp)
movl ebp, EBP_OFF(esp)
+ /*
+ * GCC versions starting with 4.3 no longer place the cld
+ * instruction before string operations. We need to ensure
+ * it is set correctly for ISR handlers.
+ */
+ cld
+
#ifdef __SSE__
/* NOTE: SSE only is supported if the BSP enables fxsave/fxrstor
* to save/restore SSE context! This is so far only implemented
@@ -105,39 +111,6 @@ SYM (_ISR_Handler):
ldmxcsr ARG_OFF(esp) /* clean-slate MXCSR */
#endif
- /* Do not disable any 8259 interrupts if this isn't from one */
- cmp ecx, 16 /* is this a PIC IRQ? */
- jge .check_stack_switch
-
- /*
- * acknowledge the interrupt
- */
- movw SYM (i8259s_cache), ax /* save current i8259 interrupt mask */
- movl eax, MSK_OFF(esp) /* save in stack frame */
-
- /*
- * compute the new PIC mask:
- *
- * <new mask> = <old mask> | irq_mask_or_tbl[<intr number aka ecx>]
- */
- movw SYM (irq_mask_or_tbl) (,ecx,2), dx
- orw dx, ax
- /*
- * Install new computed value on the i8259 and update cache
- * accordingly
- */
- movw ax, SYM (i8259s_cache)
- outb $PIC_MASTER_IMR_IO_PORT
- movb ah, al
- outb $PIC_SLAVE_IMR_IO_PORT
-
- movb $PIC_EOI, al
- cmpl $7, ecx
- jbe .master
- outb $PIC_SLAVE_COMMAND_IO_PORT
-.master:
- outb $PIC_MASTER_COMMAND_IO_PORT
-
/*
* Now switch stacks if necessary
*/
@@ -172,19 +145,6 @@ nested:
incl PER_CPU_THREAD_DISPATCH_DISABLE_LEVEL(ebx) /* disable
multitasking */
/*
- * GCC versions starting with 4.3 no longer place the cld
- * instruction before string operations. We need to ensure
- * it is set correctly for ISR handlers.
- */
- cld
-
- /*
- * re-enable interrupts at processor level as the current
- * interrupt source is now masked via i8259
- */
- sti
-
- /*
* ECX is preloaded with the vector number; store as arg
* on top of stack. Note that _CPU_Interrupt_stack_high
* was adjusted in _CPU_Interrupt_stack_setup() (score/rtems/cpu.h)
@@ -192,12 +152,7 @@ nested:
*/
movl ecx, ARG_OFF(esp) /* store vector arg in stack */
- call C_dispatch_isr
-
- /*
- * disable interrupts_again
- */
- cli
+ call BSP_dispatch_isr
movl ARG_OFF(esp), ecx /* grab vector arg from stack */
@@ -207,22 +162,6 @@ nested:
*/
movl ebp, esp
- /*
- * restore the original i8259 masks
- */
- /* Do not touch 8259 interrupts if this isn't from one */
- cmp ecx, 16 /* is this a PIC IRQ? */
- jge .dont_restore_i8259
-
- movw SYM (i8259s_super_imr), dx
- movl MSK_OFF(esp), eax
- orw dx, ax
- movw ax, SYM (i8259s_cache)
- outb $PIC_MASTER_IMR_IO_PORT
- movb ah, al
- outb $PIC_SLAVE_IMR_IO_PORT
-
-.dont_restore_i8259:
decl PER_CPU_ISR_NEST_LEVEL(ebx) /* one less ISR nest level */
/* If interrupts are nested, */
/* then dispatching is disabled */
diff --git a/c/src/lib/libbsp/i386/shared/irq/irq_asm.h b/c/src/lib/libbsp/i386/shared/irq/irq_asm.h
index 31575ad..05cb4e6 100644
--- a/c/src/lib/libbsp/i386/shared/irq/irq_asm.h
+++ b/c/src/lib/libbsp/i386/shared/irq/irq_asm.h
@@ -12,12 +12,14 @@
*
* COPYRIGHT (c) 1998 valette at crf.canon.fr
*
+ * Copyright (c) 2016 Chris Johns <chrisj at rtems.org>
+ *
* The license and distribution terms for this file may be
* found in the file LICENSE in this distribution or at
* http://www.rtems.org/license/LICENSE.
*/
-#ifndef __IRQ_ASM_H__
-#define __IRQ_ASM_H__
+#ifndef __I8259S_H__
+#define __I8259S_H__
#define BSP_ASM_IRQ_VECTOR_BASE 0x20
/** @brief PIC's command and mask registers */
@@ -30,4 +32,14 @@
#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 */
+
#endif
diff --git a/c/src/lib/libbsp/i386/shared/irq/irq_init.c b/c/src/lib/libbsp/i386/shared/irq/irq_init.c
index fe8d7e7..c401f29 100644
--- a/c/src/lib/libbsp/i386/shared/irq/irq_init.c
+++ b/c/src/lib/libbsp/i386/shared/irq/irq_init.c
@@ -66,7 +66,7 @@ static int raw_not_connected(
static rtems_raw_irq_connect_data idtHdl[IDT_SIZE];
-static rtems_raw_irq_hdl rtemsIrq[BSP_IRQ_LINES_NUMBER] = {
+static rtems_raw_irq_hdl rtemsIrq[BSP_IRQ_VECTOR_NUMBER] = {
rtems_irq_prologue_0,
rtems_irq_prologue_1,
rtems_irq_prologue_2,
@@ -149,7 +149,7 @@ void rtems_irq_mngt_init(void)
* Patch the entry that will be used by RTEMS for interrupt management
* with RTEMS prologue.
*/
- for (i = 0; i < BSP_IRQ_LINES_NUMBER; i++) {
+ for (i = 0; i < BSP_IRQ_VECTOR_NUMBER; i++) {
create_interrupt_gate_descriptor(&idtEntry, rtemsIrq[i]);
idt_entry_tbl[i + BSP_ASM_IRQ_VECTOR_BASE] = idtEntry;
}
--
2.4.6
More information about the devel
mailing list