u-boot/arch/x86/lib/pcat_interrupts.c
Bin Meng 4372a9eca5 x86: Call cpu_init_interrupts() from interrupt_init()
Currently cpu_init_interrupts() is called from cpu_init_r() to
setup the interrupt and exception of the cpu core, but at that
time the i8259 has not been initialized to mask all the irqs
and remap the master i8259 interrupt vector base, so the whole
system is at risk of being interrupted, and if interrupted,
wrong interrupt/exception message is shown.

Signed-off-by: Bin Meng <bmeng.cn@gmail.com>
Acked-by: Simon Glass <sjg@chromium.org>
Tested-by: Simon Glass <sjg@chromium.org>
2014-11-25 06:34:02 -07:00

157 lines
3.4 KiB
C

/*
* (C) Copyright 2009
* Graeme Russ, <graeme.russ@gmail.com>
*
* (C) Copyright 2002
* Daniel Engström, Omicron Ceti AB, <daniel@omicron.se>
*
* SPDX-License-Identifier: GPL-2.0+
*/
/*
* This file provides the interrupt handling functionality for systems
* based on the standard PC/AT architecture using two cascaded i8259
* Programmable Interrupt Controllers.
*/
#include <common.h>
#include <asm/io.h>
#include <asm/i8259.h>
#include <asm/ibmpc.h>
#include <asm/interrupt.h>
#if CONFIG_SYS_NUM_IRQS != 16
#error "CONFIG_SYS_NUM_IRQS must equal 16 if CONFIG_SYS_NUM_IRQS is defined"
#endif
int interrupt_init(void)
{
u8 i;
disable_interrupts();
/* Mask all interrupts */
outb(0xff, MASTER_PIC + IMR);
outb(0xff, SLAVE_PIC + IMR);
/* Master PIC */
/* Place master PIC interrupts at INT20 */
/* ICW3, One slave PIC is present */
outb(ICW1_SEL|ICW1_EICW4, MASTER_PIC + ICW1);
outb(0x20, MASTER_PIC + ICW2);
outb(IR2, MASTER_PIC + ICW3);
outb(ICW4_PM, MASTER_PIC + ICW4);
for (i = 0; i < 8; i++)
outb(OCW2_SEOI | i, MASTER_PIC + OCW2);
/* Slave PIC */
/* Place slave PIC interrupts at INT28 */
/* Slave ID */
outb(ICW1_SEL|ICW1_EICW4, SLAVE_PIC + ICW1);
outb(0x28, SLAVE_PIC + ICW2);
outb(0x02, SLAVE_PIC + ICW3);
outb(ICW4_PM, SLAVE_PIC + ICW4);
for (i = 0; i < 8; i++)
outb(OCW2_SEOI | i, SLAVE_PIC + OCW2);
/*
* Enable cascaded interrupts by unmasking the cascade IRQ pin of
* the master PIC
*/
unmask_irq(2);
/* Interrupt 9 should be level triggered (SCI). The OS might do this */
configure_irq_trigger(9, true);
/* Initialize core interrupt and exception functionality of CPU */
cpu_init_interrupts();
enable_interrupts();
return 0;
}
void mask_irq(int irq)
{
int imr_port;
if (irq >= CONFIG_SYS_NUM_IRQS)
return;
if (irq > 7)
imr_port = SLAVE_PIC + IMR;
else
imr_port = MASTER_PIC + IMR;
outb(inb(imr_port) | (1 << (irq & 7)), imr_port);
}
void unmask_irq(int irq)
{
int imr_port;
if (irq >= CONFIG_SYS_NUM_IRQS)
return;
if (irq > 7)
imr_port = SLAVE_PIC + IMR;
else
imr_port = MASTER_PIC + IMR;
outb(inb(imr_port) & ~(1 << (irq & 7)), imr_port);
}
void specific_eoi(int irq)
{
if (irq >= CONFIG_SYS_NUM_IRQS)
return;
if (irq > 7) {
/*
* IRQ is on the slave - Issue a corresponding EOI to the
* slave PIC and an EOI for IRQ2 (the cascade interrupt)
* on the master PIC
*/
outb(OCW2_SEOI | (irq & 7), SLAVE_PIC + OCW2);
irq = SEOI_IR2;
}
outb(OCW2_SEOI | irq, MASTER_PIC + OCW2);
}
#define ELCR1 0x4d0
#define ELCR2 0x4d1
void 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
}