Message ID | 1415751501-23407-17-git-send-email-sjg@chromium.org |
---|---|
State | Superseded |
Delegated to: | Simon Glass |
Headers | show |
Hi Simon, On Wed, Nov 12, 2014 at 8:18 AM, Simon Glass <sjg@chromium.org> wrote: > The i8259 is a basic interrupt controller from the 1970s which is still > present in modern Intel hardware. Add some code to set it up. > > Signed-off-by: Simon Glass <sjg@chromium.org> > --- > > arch/x86/include/asm/i8259.h | 3 + > arch/x86/lib/Makefile | 1 + > arch/x86/lib/i8259.c | 134 +++++++++++++++++++++++++++++++++++++++++++ > 3 files changed, 138 insertions(+) > create mode 100644 arch/x86/lib/i8259.c > > diff --git a/arch/x86/include/asm/i8259.h b/arch/x86/include/asm/i8259.h > index 73113f9..bac073d 100644 > --- a/arch/x86/include/asm/i8259.h > +++ b/arch/x86/include/asm/i8259.h > @@ -69,4 +69,7 @@ > #define ICW4_AEOI 0x02 /* Automatic EOI Mode */ > #define ICW4_PM 0x01 /* Microprocessor Mode */ > > +void i8259_setup(void); > +void i8259_configure_irq_trigger(int int_num, bool is_level_triggered); > + > #endif > diff --git a/arch/x86/lib/Makefile b/arch/x86/lib/Makefile > index d0c7f30..2d75f9f 100644 > --- a/arch/x86/lib/Makefile > +++ b/arch/x86/lib/Makefile > @@ -8,6 +8,7 @@ > obj-$(CONFIG_CMD_BOOTM) += bootm.o > obj-y += cmd_boot.o > obj-y += gcc.o > +obj-y += i8259.o > obj-y += init_helpers.o > obj-y += interrupts.o > obj-$(CONFIG_SYS_PCAT_INTERRUPTS) += pcat_interrupts.o > diff --git a/arch/x86/lib/i8259.c b/arch/x86/lib/i8259.c > new file mode 100644 > index 0000000..442d904 > --- /dev/null > +++ b/arch/x86/lib/i8259.c > @@ -0,0 +1,134 @@ > +/* > + * From Coreboot file of the same name > + * > + * Copyright (C) 2009 coresystems GmbH > + * > + * SPDX-License-Identifier: GPL-2.0 > + */ > + > +#include <common.h> > +#include <asm/io.h> > +#include <asm/i8259.h> > + > +#define MASTER_PIC_ICW1 0x20 > +#define SLAVE_PIC_ICW1 0xa0 > +#define ICW_SELECT (1 << 4) > +#define OCW_SELECT (0 << 4) > +#define ADI (1 << 2) > +#define SNGL (1 << 1) > +#define IC4 (1 << 0) > + > +#define MASTER_PIC_ICW2 0x21 > +#define SLAVE_PIC_ICW2 0xa1 > +#define INT_VECTOR_MASTER 0x20 > +#define IRQ0 0x00 > +#define IRQ1 0x01 > +#define INT_VECTOR_SLAVE 0x28 > +#define IRQ8 0x00 > +#define IRQ9 0x01 > + > +#define MASTER_PIC_ICW3 0x21 > +#define CASCADED_PIC (1 << 2) > + > +#define MASTER_PIC_ICW4 0x21 > +#define SLAVE_PIC_ICW4 0xa1 > +#define MICROPROCESSOR_MODE (1 << 0) > + > +#define SLAVE_PIC_ICW3 0xa1 > +#define SLAVE_ID 0x02 > + > +#define MASTER_PIC_OCW1 0x21 > +#define SLAVE_PIC_OCW1 0xa1 > +#define IRQ2 (1 << 2) > +#define ALL_IRQS 0xff > + > +#define ELCR1 0x4d0 > +#define ELCR2 0x4d1 > + > +void i8259_setup(void) > +{ > + /* A write to ICW1 starts the Interrupt Controller Initialization > + * Sequence. This implicitly causes the following to happen: > + * - Interrupt Mask register is cleared > + * - Priority 7 is assigned to IRQ7 input > + * - Slave mode address is set to 7 > + * - Special mask mode is cleared > + * > + * We send the initialization sequence to both the master and > + * slave i8259 controller. > + */ > + outb(ICW_SELECT|IC4, MASTER_PIC_ICW1); > + outb(ICW_SELECT|IC4, SLAVE_PIC_ICW1); > + > + /* Now the interrupt controller expects us to write to ICW2. */ > + outb(INT_VECTOR_MASTER | IRQ0, MASTER_PIC_ICW2); > + outb(INT_VECTOR_SLAVE | IRQ8, SLAVE_PIC_ICW2); > + > + /* Now the interrupt controller expects us to write to ICW3. > + * > + * The normal scenario is to set up cascading on IRQ2 on the master > + * i8259 and assign the slave ID 2 to the slave i8259. > + */ > + outb(CASCADED_PIC, MASTER_PIC_ICW3); > + outb(SLAVE_ID, SLAVE_PIC_ICW3); > + > + /* Now the interrupt controller expects us to write to ICW4. > + * > + * We switch both i8259 to microprocessor mode because they're > + * operating as part of an x86 architecture based chipset > + */ > + outb(MICROPROCESSOR_MODE, MASTER_PIC_ICW2); > + outb(MICROPROCESSOR_MODE, SLAVE_PIC_ICW2); > + > + /* Now clear the interrupts through OCW1. > + * First we mask off all interrupts on the slave interrupt controller > + * then we mask off all interrupts but interrupt 2 on the master > + * controller. This way the cascading stays alife. > + */ > + outb(ALL_IRQS, SLAVE_PIC_OCW1); > + outb(ALL_IRQS & ~IRQ2, MASTER_PIC_OCW1); > + debug("i8259 inited\n"); > +} > + > +/** > + * i8259_configure_irq_trigger() - Configure IRQ triggering > + * > + * Switch the given interrupt to be level / edge triggered > + * > + * @param int_num legacy interrupt number (3-7, 9-15) > + * @param is_level_triggered true for level triggered interrupt, false for > + * edge triggered interrupt > + */ > +void i8259_configure_irq_trigger(int int_num, bool is_level_triggered) > +{ > + u16 int_bits = inb(ELCR1) | (((u16)inb(ELCR2)) << 8); > + > + debug("%s: current interrupts are 0x%x\n", __func__, int_bits); > + if (is_level_triggered) > + int_bits |= (1 << int_num); > + else > + int_bits &= ~(1 << int_num); > + > + /* Write new values */ > + debug("%s: try to set interrupts 0x%x\n", __func__, int_bits); > + outb((u8)(int_bits & 0xff), ELCR1); > + outb((u8)(int_bits >> 8), ELCR2); > + > +#ifdef PARANOID_IRQ_TRIGGERS > + /* > + * Try reading back the new values. This seems like an error but is > + * not > + */ > + if (inb(ELCR1) != (int_bits & 0xff)) { > + printf("%s: lower order bits are wrong: want 0x%x, got 0x%x\n", > + __func__, (int_bits & 0xff), inb(ELCR1)); > + } > + > + if (inb(ELCR2) != (int_bits >> 8)) { > + printf("%s: higher order bits are wrong: want 0x%x, got 0x%x\n", > + __func__, (int_bits>>8), inb(ELCR2)); > + } > +#endif > +} > + > + > -- Can you make the changes to the existing arch/x86/lib/pcat_interrupts.c which is also the i8259 pic driver initialization? Regards, Bin
diff --git a/arch/x86/include/asm/i8259.h b/arch/x86/include/asm/i8259.h index 73113f9..bac073d 100644 --- a/arch/x86/include/asm/i8259.h +++ b/arch/x86/include/asm/i8259.h @@ -69,4 +69,7 @@ #define ICW4_AEOI 0x02 /* Automatic EOI Mode */ #define ICW4_PM 0x01 /* Microprocessor Mode */ +void i8259_setup(void); +void i8259_configure_irq_trigger(int int_num, bool is_level_triggered); + #endif diff --git a/arch/x86/lib/Makefile b/arch/x86/lib/Makefile index d0c7f30..2d75f9f 100644 --- a/arch/x86/lib/Makefile +++ b/arch/x86/lib/Makefile @@ -8,6 +8,7 @@ obj-$(CONFIG_CMD_BOOTM) += bootm.o obj-y += cmd_boot.o obj-y += gcc.o +obj-y += i8259.o obj-y += init_helpers.o obj-y += interrupts.o obj-$(CONFIG_SYS_PCAT_INTERRUPTS) += pcat_interrupts.o diff --git a/arch/x86/lib/i8259.c b/arch/x86/lib/i8259.c new file mode 100644 index 0000000..442d904 --- /dev/null +++ b/arch/x86/lib/i8259.c @@ -0,0 +1,134 @@ +/* + * From Coreboot file of the same name + * + * Copyright (C) 2009 coresystems GmbH + * + * SPDX-License-Identifier: GPL-2.0 + */ + +#include <common.h> +#include <asm/io.h> +#include <asm/i8259.h> + +#define MASTER_PIC_ICW1 0x20 +#define SLAVE_PIC_ICW1 0xa0 +#define ICW_SELECT (1 << 4) +#define OCW_SELECT (0 << 4) +#define ADI (1 << 2) +#define SNGL (1 << 1) +#define IC4 (1 << 0) + +#define MASTER_PIC_ICW2 0x21 +#define SLAVE_PIC_ICW2 0xa1 +#define INT_VECTOR_MASTER 0x20 +#define IRQ0 0x00 +#define IRQ1 0x01 +#define INT_VECTOR_SLAVE 0x28 +#define IRQ8 0x00 +#define IRQ9 0x01 + +#define MASTER_PIC_ICW3 0x21 +#define CASCADED_PIC (1 << 2) + +#define MASTER_PIC_ICW4 0x21 +#define SLAVE_PIC_ICW4 0xa1 +#define MICROPROCESSOR_MODE (1 << 0) + +#define SLAVE_PIC_ICW3 0xa1 +#define SLAVE_ID 0x02 + +#define MASTER_PIC_OCW1 0x21 +#define SLAVE_PIC_OCW1 0xa1 +#define IRQ2 (1 << 2) +#define ALL_IRQS 0xff + +#define ELCR1 0x4d0 +#define ELCR2 0x4d1 + +void i8259_setup(void) +{ + /* A write to ICW1 starts the Interrupt Controller Initialization + * Sequence. This implicitly causes the following to happen: + * - Interrupt Mask register is cleared + * - Priority 7 is assigned to IRQ7 input + * - Slave mode address is set to 7 + * - Special mask mode is cleared + * + * We send the initialization sequence to both the master and + * slave i8259 controller. + */ + outb(ICW_SELECT|IC4, MASTER_PIC_ICW1); + outb(ICW_SELECT|IC4, SLAVE_PIC_ICW1); + + /* Now the interrupt controller expects us to write to ICW2. */ + outb(INT_VECTOR_MASTER | IRQ0, MASTER_PIC_ICW2); + outb(INT_VECTOR_SLAVE | IRQ8, SLAVE_PIC_ICW2); + + /* Now the interrupt controller expects us to write to ICW3. + * + * The normal scenario is to set up cascading on IRQ2 on the master + * i8259 and assign the slave ID 2 to the slave i8259. + */ + outb(CASCADED_PIC, MASTER_PIC_ICW3); + outb(SLAVE_ID, SLAVE_PIC_ICW3); + + /* Now the interrupt controller expects us to write to ICW4. + * + * We switch both i8259 to microprocessor mode because they're + * operating as part of an x86 architecture based chipset + */ + outb(MICROPROCESSOR_MODE, MASTER_PIC_ICW2); + outb(MICROPROCESSOR_MODE, SLAVE_PIC_ICW2); + + /* Now clear the interrupts through OCW1. + * First we mask off all interrupts on the slave interrupt controller + * then we mask off all interrupts but interrupt 2 on the master + * controller. This way the cascading stays alife. + */ + outb(ALL_IRQS, SLAVE_PIC_OCW1); + outb(ALL_IRQS & ~IRQ2, MASTER_PIC_OCW1); + debug("i8259 inited\n"); +} + +/** + * i8259_configure_irq_trigger() - Configure IRQ triggering + * + * Switch the given interrupt to be level / edge triggered + * + * @param int_num legacy interrupt number (3-7, 9-15) + * @param is_level_triggered true for level triggered interrupt, false for + * edge triggered interrupt + */ +void i8259_configure_irq_trigger(int int_num, bool is_level_triggered) +{ + u16 int_bits = inb(ELCR1) | (((u16)inb(ELCR2)) << 8); + + debug("%s: current interrupts are 0x%x\n", __func__, int_bits); + if (is_level_triggered) + int_bits |= (1 << int_num); + else + int_bits &= ~(1 << int_num); + + /* Write new values */ + debug("%s: try to set interrupts 0x%x\n", __func__, int_bits); + outb((u8)(int_bits & 0xff), ELCR1); + outb((u8)(int_bits >> 8), ELCR2); + +#ifdef PARANOID_IRQ_TRIGGERS + /* + * Try reading back the new values. This seems like an error but is + * not + */ + if (inb(ELCR1) != (int_bits & 0xff)) { + printf("%s: lower order bits are wrong: want 0x%x, got 0x%x\n", + __func__, (int_bits & 0xff), inb(ELCR1)); + } + + if (inb(ELCR2) != (int_bits >> 8)) { + printf("%s: higher order bits are wrong: want 0x%x, got 0x%x\n", + __func__, (int_bits>>8), inb(ELCR2)); + } +#endif +} + +
The i8259 is a basic interrupt controller from the 1970s which is still present in modern Intel hardware. Add some code to set it up. Signed-off-by: Simon Glass <sjg@chromium.org> --- arch/x86/include/asm/i8259.h | 3 + arch/x86/lib/Makefile | 1 + arch/x86/lib/i8259.c | 134 +++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 138 insertions(+) create mode 100644 arch/x86/lib/i8259.c