Rewrite i386 interrupt handling
authorGraeme Russ <graeme.russ@gmail.com>
Tue, 24 Feb 2009 10:13:40 +0000 (21:13 +1100)
committerWolfgang Denk <wd@denx.de>
Fri, 20 Mar 2009 21:39:13 +0000 (22:39 +0100)
Rewrite interrupt handling functionality for the i386 port. Separated
functionality into separate CPU and Architecture components.

It appears as if the i386 interrupt handler functionality was intended
to allow multiple handlers to be installed for a given interrupt.
Unfortunately, this functionality was not fully implemented and also
had the problem that irq_free_handler() does not allow the passing
of the handler function pointer and therefore could never be used to
free specific handlers that had been installed for a given IRQ.

There were also various issues with array bounds not being fully
tested.

I had two objectives in mind for the new implementation:

1) Keep the implementation as similar as possible to existing
   implementations. To that end, I have used the leon2/3
   implementations as the reference

2) Seperate CPU and Architecture specific elements. All specific i386
   interrupt functionality is now in cpu/i386/ with the high level
   API and architecture specific code in lib_i386. Functionality
   specific to the PC/AT architecture (i.e. cascaded i8259 PICs) has
   been further split out into an individual file to allow for the
   implementation of the PIC architecture of the SC520 CPU (supports
   more IRQs)

Signed-off-by: Graeme Russ <graeme.russ at gmail.com>
12 files changed:
cpu/i386/Makefile
cpu/i386/cpu.c
cpu/i386/exceptions.c [new file with mode: 0644]
cpu/i386/interrupts.c
include/asm-i386/interrupt.h
include/asm-i386/u-boot-i386.h
include/configs/eNET.h
include/configs/sc520_cdp.h
include/configs/sc520_spunk.h
lib_i386/Makefile
lib_i386/interrupts.c [new file with mode: 0644]
lib_i386/pcat_interrupts.c [new file with mode: 0644]

index 761c4f6fcc42382e6c54736909b5b20a86a852f9..f72cd6ea16eb1313c0154b8561c24b08da834c80 100644 (file)
@@ -29,7 +29,7 @@ include $(TOPDIR)/config.mk
 LIB    = $(obj)lib$(CPU).a
 
 START  = start.o start16.o resetvec.o
-COBJS  = serial.o interrupts.o cpu.o timer.o
+COBJS  = serial.o interrupts.o exceptions.o cpu.o timer.o
 
 SRCS   := $(START:.o=.S) $(SOBJS:.o=.S) $(COBJS:.o=.c)
 OBJS   := $(addprefix $(obj),$(SOBJS) $(COBJS))
index b9af5f89d532c11356b2f31f9bfe1a78e9604e98..d91e33b947ade5e48936ca2c605f4be2054a39df 100644 (file)
@@ -46,6 +46,10 @@ int cpu_init(void)
             "orl  $0x22, %eax\n" \
             "movl %eax, %cr0\n" );
 
+       /* Initialize core interrupt and exception functionality of CPU */
+       cpu_init_interrupts ();
+       cpu_init_exceptions ();
+
        return 0;
 }
 
diff --git a/cpu/i386/exceptions.c b/cpu/i386/exceptions.c
new file mode 100644 (file)
index 0000000..bc3d434
--- /dev/null
@@ -0,0 +1,229 @@
+/*
+ * (C) Copyright 2002
+ * Daniel Engström, Omicron Ceti AB, daniel@omicron.se.
+ *
+ * See file CREDITS for list of people who contributed to this
+ * project.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
+ * MA 02111-1307 USA
+ */
+
+#include <common.h>
+#include <asm/interrupt.h>
+
+asm (".globl exp_return\n"
+     "exp_return:\n"
+     "     addl  $12, %esp\n"
+     "     pop   %esp\n"
+     "     popa\n"
+     "     iret\n");
+
+char exception_stack[4096];
+
+/*
+ * For detailed description of each exception, refer to:
+ * Intel® 64 and IA-32 Architectures Software Developer's Manual
+ * Volume 1: Basic Architecture
+ * Order Number: 253665-029US, November 2008
+ * Table 6-1. Exceptions and Interrupts
+ */
+DECLARE_EXCEPTION(0, divide_error_entry);
+DECLARE_EXCEPTION(1, debug_entry);
+DECLARE_EXCEPTION(2, nmi_interrupt_entry);
+DECLARE_EXCEPTION(3, breakpoint_entry);
+DECLARE_EXCEPTION(4, overflow_entry);
+DECLARE_EXCEPTION(5, bound_range_exceeded_entry);
+DECLARE_EXCEPTION(6, invalid_opcode_entry);
+DECLARE_EXCEPTION(7, device_not_available_entry);
+DECLARE_EXCEPTION(8, double_fault_entry);
+DECLARE_EXCEPTION(9, coprocessor_segment_overrun_entry);
+DECLARE_EXCEPTION(10, invalid_tss_entry);
+DECLARE_EXCEPTION(11, segment_not_present_entry);
+DECLARE_EXCEPTION(12, stack_segment_fault_entry);
+DECLARE_EXCEPTION(13, general_protection_entry);
+DECLARE_EXCEPTION(14, page_fault_entry);
+DECLARE_EXCEPTION(15, reserved_exception_entry);
+DECLARE_EXCEPTION(16, floating_point_error_entry);
+DECLARE_EXCEPTION(17, alignment_check_entry);
+DECLARE_EXCEPTION(18, machine_check_entry);
+DECLARE_EXCEPTION(19, simd_floating_point_exception_entry);
+DECLARE_EXCEPTION(20, reserved_exception_entry);
+DECLARE_EXCEPTION(21, reserved_exception_entry);
+DECLARE_EXCEPTION(22, reserved_exception_entry);
+DECLARE_EXCEPTION(23, reserved_exception_entry);
+DECLARE_EXCEPTION(24, reserved_exception_entry);
+DECLARE_EXCEPTION(25, reserved_exception_entry);
+DECLARE_EXCEPTION(26, reserved_exception_entry);
+DECLARE_EXCEPTION(27, reserved_exception_entry);
+DECLARE_EXCEPTION(28, reserved_exception_entry);
+DECLARE_EXCEPTION(29, reserved_exception_entry);
+DECLARE_EXCEPTION(30, reserved_exception_entry);
+DECLARE_EXCEPTION(31, reserved_exception_entry);
+
+__isr__ reserved_exception_entry(int cause, int ip, int seg)
+{
+       printf("Reserved Exception %d at %04x:%08x\n", cause, seg, ip);
+}
+
+__isr__ divide_error_entry(int cause, int ip, int seg)
+{
+       printf("Divide Error (Division by zero) at %04x:%08x\n", seg, ip);
+       while(1);
+}
+
+__isr__ debug_entry(int cause, int ip, int seg)
+{
+       printf("Debug Interrupt (Single step) at %04x:%08x\n", seg, ip);
+}
+
+__isr__ nmi_interrupt_entry(int cause, int ip, int seg)
+{
+       printf("NMI Interrupt at %04x:%08x\n", seg, ip);
+}
+
+__isr__ breakpoint_entry(int cause, int ip, int seg)
+{
+       printf("Breakpoint at %04x:%08x\n", seg, ip);
+}
+
+__isr__ overflow_entry(int cause, int ip, int seg)
+{
+       printf("Overflow at %04x:%08x\n", seg, ip);
+       while(1);
+}
+
+__isr__ bound_range_exceeded_entry(int cause, int ip, int seg)
+{
+       printf("BOUND Range Exceeded at %04x:%08x\n", seg, ip);
+       while(1);
+}
+
+__isr__ invalid_opcode_entry(int cause, int ip, int seg)
+{
+       printf("Invalid Opcode (UnDefined Opcode) at %04x:%08x\n", seg, ip);
+       while(1);
+}
+
+__isr__ device_not_available_entry(int cause, int ip, int seg)
+{
+       printf("Device Not Available (No Math Coprocessor) at %04x:%08x\n", seg, ip);
+       while(1);
+}
+
+__isr__ double_fault_entry(int cause, int ip, int seg)
+{
+       printf("Double fault at %04x:%08x\n", seg, ip);
+       while(1);
+}
+
+__isr__ coprocessor_segment_overrun_entry(int cause, int ip, int seg)
+{
+       printf("Co-processor segment overrun at %04x:%08x\n", seg, ip);
+       while(1);
+}
+
+__isr__ invalid_tss_entry(int cause, int ip, int seg)
+{
+       printf("Invalid TSS at %04x:%08x\n", seg, ip);
+}
+
+__isr__ segment_not_present_entry(int cause, int ip, int seg)
+{
+       printf("Segment Not Present at %04x:%08x\n", seg, ip);
+       while(1);
+}
+
+__isr__ stack_segment_fault_entry(int cause, int ip, int seg)
+{
+       printf("Stack Segment Fault at %04x:%08x\n", seg, ip);
+       while(1);
+}
+
+__isr__ general_protection_entry(int cause, int ip, int seg)
+{
+       printf("General Protection at %04x:%08x\n", seg, ip);
+}
+
+__isr__ page_fault_entry(int cause, int ip, int seg)
+{
+       printf("Page fault at %04x:%08x\n", seg, ip);
+       while(1);
+}
+
+__isr__ floating_point_error_entry(int cause, int ip, int seg)
+{
+       printf("Floating-Point Error (Math Fault) at %04x:%08x\n", seg, ip);
+}
+
+__isr__ alignment_check_entry(int cause, int ip, int seg)
+{
+       printf("Alignment check at %04x:%08x\n", seg, ip);
+}
+
+__isr__ machine_check_entry(int cause, int ip, int seg)
+{
+       printf("Machine Check at %04x:%08x\n", seg, ip);
+}
+
+__isr__ simd_floating_point_exception_entry(int cause, int ip, int seg)
+{
+       printf("SIMD Floating-Point Exception at %04x:%08x\n", seg, ip);
+}
+
+int cpu_init_exceptions(void)
+{
+       /* Just in case... */
+       disable_interrupts();
+
+       /* Setup exceptions */
+       set_vector(0x00, exp_0);
+       set_vector(0x01, exp_1);
+       set_vector(0x02, exp_2);
+       set_vector(0x03, exp_3);
+       set_vector(0x04, exp_4);
+       set_vector(0x05, exp_5);
+       set_vector(0x06, exp_6);
+       set_vector(0x07, exp_7);
+       set_vector(0x08, exp_8);
+       set_vector(0x09, exp_9);
+       set_vector(0x0a, exp_10);
+       set_vector(0x0b, exp_11);
+       set_vector(0x0c, exp_12);
+       set_vector(0x0d, exp_13);
+       set_vector(0x0e, exp_14);
+       set_vector(0x0f, exp_15);
+       set_vector(0x10, exp_16);
+       set_vector(0x11, exp_17);
+       set_vector(0x12, exp_18);
+       set_vector(0x13, exp_19);
+       set_vector(0x14, exp_20);
+       set_vector(0x15, exp_21);
+       set_vector(0x16, exp_22);
+       set_vector(0x17, exp_23);
+       set_vector(0x18, exp_24);
+       set_vector(0x19, exp_25);
+       set_vector(0x1a, exp_26);
+       set_vector(0x1b, exp_27);
+       set_vector(0x1c, exp_28);
+       set_vector(0x1d, exp_29);
+       set_vector(0x1e, exp_30);
+       set_vector(0x1f, exp_31);
+
+       /* It is now safe to enable interrupts */
+       enable_interrupts();
+
+       return 0;
+}
index badb30bb8a1b3a044c0b59d2a3803a0ec71227ed..026a21bd21711cdd991b761fe23119d8e9e5de00 100644 (file)
  */
 
 #include <common.h>
-#include <malloc.h>
-#include <asm/io.h>
-#include <asm/i8259.h>
-#include <asm/ibmpc.h>
 #include <asm/interrupt.h>
 
 
@@ -41,361 +37,34 @@ struct idt_entry {
 struct idt_entry idt[256];
 
 
-#define MAX_IRQ 16
-
-typedef struct irq_handler {
-       struct irq_handler *next;
-       interrupt_handler_t* isr_func;
-       void *isr_data;
-} irq_handler_t;
-
-#define IRQ_DISABLED   1
-
-typedef struct {
-       irq_handler_t *handler;
-       unsigned long status;
-} irq_desc_t;
-
-static irq_desc_t irq_table[MAX_IRQ];
-
-asm ("irq_return:\n"
+asm (".globl irq_return\n"
+     "irq_return:\n"
      "     addl  $4, %esp\n"
      "     popa\n"
      "     iret\n");
 
-asm ("exp_return:\n"
-     "     addl  $12, %esp\n"
-     "     pop   %esp\n"
-     "     popa\n"
-     "     iret\n");
-
-char exception_stack[4096];
-
-#define DECLARE_INTERRUPT(x) \
-       asm(".globl irq_"#x"\n" \
-                   "irq_"#x":\n" \
-                   "pusha \n" \
-                   "pushl $"#x"\n" \
-                   "pushl $irq_return\n" \
-                   "jmp   do_irq\n"); \
-       void __attribute__ ((regparm(0))) irq_##x(void)
-
-#define DECLARE_EXCEPTION(x, f) \
-       asm(".globl exp_"#x"\n" \
-                   "exp_"#x":\n" \
-                   "pusha \n" \
-                   "movl     %esp, %ebx\n" \
-                   "movl     $exception_stack, %eax\n" \
-                   "movl     %eax, %esp \n" \
-                   "pushl    %ebx\n" \
-                   "movl     32(%esp), %ebx\n" \
-                   "xorl     %edx, %edx\n" \
-                   "movw     36(%esp), %dx\n" \
-                   "pushl    %edx\n" \
-                   "pushl    %ebx\n" \
-                   "pushl    $"#x"\n" \
-                   "pushl    $exp_return\n" \
-                   "jmp      "#f"\n"); \
-       void __attribute__ ((regparm(0))) exp_##x(void)
-
-DECLARE_EXCEPTION(0, divide_exception_entry);      /* Divide exception */
-DECLARE_EXCEPTION(1, debug_exception_entry);       /* Debug exception */
-DECLARE_EXCEPTION(2, nmi_entry);                   /* NMI */
-DECLARE_EXCEPTION(3, unknown_exception_entry);     /* Breakpoint/Coprocessor Error */
-DECLARE_EXCEPTION(4, unknown_exception_entry);     /* Overflow */
-DECLARE_EXCEPTION(5, unknown_exception_entry);     /* Bounds */
-DECLARE_EXCEPTION(6, invalid_instruction_entry);   /* Invalid instruction */
-DECLARE_EXCEPTION(7, unknown_exception_entry);     /* Device not present */
-DECLARE_EXCEPTION(8, double_fault_entry);          /* Double fault */
-DECLARE_EXCEPTION(9, unknown_exception_entry);     /* Co-processor segment overrun */
-DECLARE_EXCEPTION(10, invalid_tss_exception_entry);/* Invalid TSS */
-DECLARE_EXCEPTION(11, seg_fault_entry);            /* Segment not present */
-DECLARE_EXCEPTION(12, stack_fault_entry);          /* Stack overflow */
-DECLARE_EXCEPTION(13, gpf_entry);                  /* GPF */
-DECLARE_EXCEPTION(14, page_fault_entry);           /* PF */
-DECLARE_EXCEPTION(15, unknown_exception_entry);    /* Reserved */
-DECLARE_EXCEPTION(16, fp_exception_entry);         /* Floating point */
-DECLARE_EXCEPTION(17, alignment_check_entry);      /* alignment check */
-DECLARE_EXCEPTION(18, machine_check_entry);        /* machine check */
-DECLARE_EXCEPTION(19, unknown_exception_entry);    /* Reserved */
-DECLARE_EXCEPTION(20, unknown_exception_entry);    /* Reserved */
-DECLARE_EXCEPTION(21, unknown_exception_entry);    /* Reserved */
-DECLARE_EXCEPTION(22, unknown_exception_entry);    /* Reserved */
-DECLARE_EXCEPTION(23, unknown_exception_entry);    /* Reserved */
-DECLARE_EXCEPTION(24, unknown_exception_entry);    /* Reserved */
-DECLARE_EXCEPTION(25, unknown_exception_entry);    /* Reserved */
-DECLARE_EXCEPTION(26, unknown_exception_entry);    /* Reserved */
-DECLARE_EXCEPTION(27, unknown_exception_entry);    /* Reserved */
-DECLARE_EXCEPTION(28, unknown_exception_entry);    /* Reserved */
-DECLARE_EXCEPTION(29, unknown_exception_entry);    /* Reserved */
-DECLARE_EXCEPTION(30, unknown_exception_entry);    /* Reserved */
-DECLARE_EXCEPTION(31, unknown_exception_entry);    /* Reserved */
-
-DECLARE_INTERRUPT(0);
-DECLARE_INTERRUPT(1);
-DECLARE_INTERRUPT(3);
-DECLARE_INTERRUPT(4);
-DECLARE_INTERRUPT(5);
-DECLARE_INTERRUPT(6);
-DECLARE_INTERRUPT(7);
-DECLARE_INTERRUPT(8);
-DECLARE_INTERRUPT(9);
-DECLARE_INTERRUPT(10);
-DECLARE_INTERRUPT(11);
-DECLARE_INTERRUPT(12);
-DECLARE_INTERRUPT(13);
-DECLARE_INTERRUPT(14);
-DECLARE_INTERRUPT(15);
-
 void __attribute__ ((regparm(0))) default_isr(void);
 asm ("default_isr: iret\n");
 
-void disable_irq(int irq)
-{
-       if (irq >= MAX_IRQ) {
-               return;
-       }
-       irq_table[irq].status |= IRQ_DISABLED;
-
-}
-
-void enable_irq(int irq)
-{
-       if (irq >= MAX_IRQ) {
-               return;
-       }
-       irq_table[irq].status &= ~IRQ_DISABLED;
-}
-
-/* masks one specific IRQ in the PIC */
-static void unmask_irq(int irq)
-{
-       int imr_port;
-
-       if (irq >= MAX_IRQ) {
-               return;
-       }
-       if (irq > 7) {
-               imr_port = SLAVE_PIC + IMR;
-       } else {
-               imr_port = MASTER_PIC + IMR;
-       }
-
-       outb(inb(imr_port)&~(1<<(irq&7)), imr_port);
-}
-
-
-/* unmasks one specific IRQ in the PIC */
-static void mask_irq(int irq)
-{
-       int imr_port;
-
-       if (irq >= MAX_IRQ) {
-               return;
-       }
-       if (irq > 7) {
-               imr_port = SLAVE_PIC + IMR;
-       } else {
-               imr_port = MASTER_PIC + IMR;
-       }
-
-       outb(inb(imr_port)|(1<<(irq&7)), imr_port);
-}
-
-
-/* issue a Specific End Of Interrupt instruciton */
-static void specific_eoi(int irq)
-{
-       /* If it is on the slave PIC this have to be performed on
-        * both the master and the slave PICs */
-       if (irq > 7) {
-               outb(OCW2_SEOI|(irq&7), SLAVE_PIC + OCW2);
-               irq = SEOI_IR2;               /* also do IR2 on master */
-       }
-       outb(OCW2_SEOI|irq, MASTER_PIC + OCW2);
-}
-
-void __attribute__ ((regparm(0))) do_irq(int irq)
-{
-
-       mask_irq(irq);
-
-       if (irq_table[irq].status & IRQ_DISABLED) {
-               unmask_irq(irq);
-               specific_eoi(irq);
-               return;
-       }
-
-
-       if (NULL != irq_table[irq].handler) {
-               irq_handler_t *handler;
-               for (handler = irq_table[irq].handler;
-                    NULL!= handler; handler = handler->next) {
-                       handler->isr_func(handler->isr_data);
-               }
-       } else {
-               if ((irq & 7) != 7) {
-                       printf("Spurious irq %d\n", irq);
-               }
-       }
-       unmask_irq(irq);
-       specific_eoi(irq);
-}
-
-
-void __attribute__ ((regparm(0))) unknown_exception_entry(int cause, int ip, int seg)
-{
-       printf("Unknown Exception %d at %04x:%08x\n", cause, seg, ip);
-}
-
-void __attribute__ ((regparm(0))) divide_exception_entry(int cause, int ip, int seg)
-{
-       printf("Divide Error (Division by zero) at %04x:%08x\n", seg, ip);
-       while(1);
-}
-
-void __attribute__ ((regparm(0))) debug_exception_entry(int cause, int ip, int seg)
-{
-       printf("Debug Interrupt (Single step) at %04x:%08x\n", seg, ip);
-}
-
-void __attribute__ ((regparm(0))) nmi_entry(int cause, int ip, int seg)
-{
-       printf("NMI Interrupt at %04x:%08x\n", seg, ip);
-}
-
-void __attribute__ ((regparm(0))) invalid_instruction_entry(int cause, int ip, int seg)
-{
-       printf("Invalid Instruction at %04x:%08x\n", seg, ip);
-       while(1);
-}
-
-void __attribute__ ((regparm(0))) double_fault_entry(int cause, int ip, int seg)
-{
-       printf("Double fault at %04x:%08x\n", seg, ip);
-       while(1);
-}
-
-void __attribute__ ((regparm(0))) invalid_tss_exception_entry(int cause, int ip, int seg)
-{
-       printf("Invalid TSS at %04x:%08x\n", seg, ip);
-}
-
-void __attribute__ ((regparm(0))) seg_fault_entry(int cause, int ip, int seg)
-{
-       printf("Segmentation fault at %04x:%08x\n", seg, ip);
-       while(1);
-}
-
-void __attribute__ ((regparm(0))) stack_fault_entry(int cause, int ip, int seg)
-{
-       printf("Stack fault at %04x:%08x\n", seg, ip);
-       while(1);
-}
-
-void __attribute__ ((regparm(0))) gpf_entry(int cause, int ip, int seg)
-{
-       printf("General protection fault at %04x:%08x\n", seg, ip);
-}
-
-void __attribute__ ((regparm(0))) page_fault_entry(int cause, int ip, int seg)
-{
-       printf("Page fault at %04x:%08x\n", seg, ip);
-       while(1);
-}
-
-void __attribute__ ((regparm(0))) fp_exception_entry(int cause, int ip, int seg)
-{
-       printf("Floating point exception at %04x:%08x\n", seg, ip);
-}
-
-void __attribute__ ((regparm(0))) alignment_check_entry(int cause, int ip, int seg)
-{
-       printf("Alignment check at %04x:%08x\n", seg, ip);
-}
-
-void __attribute__ ((regparm(0))) machine_check_entry(int cause, int ip, int seg)
-{
-       printf("Machine check exception at %04x:%08x\n", seg, ip);
-}
-
-
-void irq_install_handler(int ino, interrupt_handler_t *func, void *pdata)
-{
-       int status;
-
-       if (ino>MAX_IRQ) {
-               return;
-       }
-
-       if (NULL != irq_table[ino].handler) {
-               return;
-       }
-
-       status = disable_interrupts();
-       irq_table[ino].handler = malloc(sizeof(irq_handler_t));
-       if (NULL == irq_table[ino].handler) {
-               return;
-       }
-
-       memset(irq_table[ino].handler, 0, sizeof(irq_handler_t));
-
-       irq_table[ino].handler->isr_func = func;
-       irq_table[ino].handler->isr_data = pdata;
-       if (status) {
-               enable_interrupts();
-       }
-
-       unmask_irq(ino);
-
-       return;
-}
-
-void irq_free_handler(int ino)
-{
-       int status;
-       if (ino>MAX_IRQ) {
-               return;
-       }
-
-       status = disable_interrupts();
-       mask_irq(ino);
-       if (NULL == irq_table[ino].handler) {
-               return;
-       }
-       free(irq_table[ino].handler);
-       irq_table[ino].handler=NULL;
-       if (status) {
-               enable_interrupts();
-       }
-       return;
-}
-
-
 asm ("idt_ptr:\n"
        ".word  0x800\n" /* size of the table 8*256 bytes */
        ".long  idt\n"   /* offset */
        ".word  0x18\n");/* data segment */
 
-void set_vector(int intnum, void *routine)
+void set_vector(u8 intnum, void *routine)
 {
        idt[intnum].base_high = (u16)((u32)(routine)>>16);
        idt[intnum].base_low = (u16)((u32)(routine)&0xffff);
 }
 
 
-int interrupt_init(void)
+int cpu_init_interrupts(void)
 {
        int i;
 
        /* Just in case... */
        disable_interrupts();
 
-       /* Initialize the IDT and stuff */
-
-
-       memset(irq_table, 0, sizeof(irq_table));
-
        /* Setup the IDT */
        for (i=0;i<256;i++) {
                idt[i].access = 0x8e;
@@ -406,89 +75,6 @@ int interrupt_init(void)
 
        asm ("cs lidt idt_ptr\n");
 
-       /* Setup exceptions */
-       set_vector(0x00, exp_0);
-       set_vector(0x01, exp_1);
-       set_vector(0x02, exp_2);
-       set_vector(0x03, exp_3);
-       set_vector(0x04, exp_4);
-       set_vector(0x05, exp_5);
-       set_vector(0x06, exp_6);
-       set_vector(0x07, exp_7);
-       set_vector(0x08, exp_8);
-       set_vector(0x09, exp_9);
-       set_vector(0x0a, exp_10);
-       set_vector(0x0b, exp_11);
-       set_vector(0x0c, exp_12);
-       set_vector(0x0d, exp_13);
-       set_vector(0x0e, exp_14);
-       set_vector(0x0f, exp_15);
-       set_vector(0x10, exp_16);
-       set_vector(0x11, exp_17);
-       set_vector(0x12, exp_18);
-       set_vector(0x13, exp_19);
-       set_vector(0x14, exp_20);
-       set_vector(0x15, exp_21);
-       set_vector(0x16, exp_22);
-       set_vector(0x17, exp_23);
-       set_vector(0x18, exp_24);
-       set_vector(0x19, exp_25);
-       set_vector(0x1a, exp_26);
-       set_vector(0x1b, exp_27);
-       set_vector(0x1c, exp_28);
-       set_vector(0x1d, exp_29);
-       set_vector(0x1e, exp_30);
-       set_vector(0x1f, exp_31);
-
-
-       /* Setup interrupts */
-       set_vector(0x20, irq_0);
-       set_vector(0x21, irq_1);
-       set_vector(0x23, irq_3);
-       set_vector(0x24, irq_4);
-       set_vector(0x25, irq_5);
-       set_vector(0x26, irq_6);
-       set_vector(0x27, irq_7);
-       set_vector(0x28, irq_8);
-       set_vector(0x29, irq_9);
-       set_vector(0x2a, irq_10);
-       set_vector(0x2b, irq_11);
-       set_vector(0x2c, irq_12);
-       set_vector(0x2d, irq_13);
-       set_vector(0x2e, irq_14);
-       set_vector(0x2f, irq_15);
-       /* vectors 0x30-0x3f are reserved for irq 16-31 */
-
-
-       /* Mask all interrupts */
-       outb(0xff, MASTER_PIC + IMR);
-       outb(0xff, SLAVE_PIC + IMR);
-
-       /* Master PIC */
-       outb(ICW1_SEL|ICW1_EICW4, MASTER_PIC + ICW1);
-       outb(0x20, MASTER_PIC + ICW2);          /* Place master PIC interrupts at INT20 */
-       outb(IR2, MASTER_PIC + ICW3);           /* ICW3, One slevc PIC is present */
-       outb(ICW4_PM, MASTER_PIC + ICW4);
-
-       for (i=0;i<8;i++) {
-               outb(OCW2_SEOI|i, MASTER_PIC + OCW2);
-       }
-
-       /* Slave PIC */
-       outb(ICW1_SEL|ICW1_EICW4, SLAVE_PIC + ICW1);
-       outb(0x28, SLAVE_PIC + ICW2);           /* Place slave PIC interrupts at INT28 */
-       outb(0x02, SLAVE_PIC + ICW3);           /* Slave ID */
-       outb(ICW4_PM, SLAVE_PIC + ICW4);
-
-       for (i=0;i<8;i++) {
-               outb(OCW2_SEOI|i, SLAVE_PIC + OCW2);
-       }
-
-
-       /* enable cascade interrerupt */
-       outb(0xfb, MASTER_PIC + IMR);
-       outb(0xff, SLAVE_PIC + IMR);
-
        /* It is now safe to enable interrupts */
        enable_interrupts();
 
index 315b4001ca560439606366c9c62f7b6b90f6e74e..7f408cb944e3b06304c0866f257586ae66e2e483 100644 (file)
@@ -1,7 +1,10 @@
 /*
- * (C) Copyright 2008
+ * (C) Copyright 2009
  * Graeme Russ, graeme.russ@gmail.com
  *
+ * (C) Copyright 2002
+ * Daniel Engström, Omicron Ceti AB, daniel@omicron.se
+ *
  * See file CREDITS for list of people who contributed to this
  * project.
  *
 #ifndef __ASM_INTERRUPT_H_
 #define __ASM_INTERRUPT_H_ 1
 
-void set_vector(int intnum, void *routine);
+/* cpu/i386/interrupts.c */
+void set_vector(u8 intnum, void *routine);
+
+/* lib_i386/interupts.c */
+void disable_irq(int irq);
+void enable_irq(int irq);
+
+/* Architecture specific functions */
+void mask_irq(int irq);
+void unmask_irq(int irq);
+void specific_eoi(int irq);
+
+extern char exception_stack[];
+
+#define __isr__ void __attribute__ ((regparm(0)))
+
+#define DECLARE_INTERRUPT(x) \
+       asm(".globl irq_"#x"\n" \
+                   "irq_"#x":\n" \
+                   "pusha \n" \
+                   "pushl $"#x"\n" \
+                   "pushl $irq_return\n" \
+                   "jmp   do_irq\n"); \
+       __isr__ irq_##x(void)
+
+#define DECLARE_EXCEPTION(x, f) \
+       asm(".globl exp_"#x"\n" \
+                   "exp_"#x":\n" \
+                   "pusha \n" \
+                   "movl     %esp, %ebx\n" \
+                   "movl     $exception_stack, %eax\n" \
+                   "movl     %eax, %esp \n" \
+                   "pushl    %ebx\n" \
+                   "movl     32(%esp), %ebx\n" \
+                   "xorl     %edx, %edx\n" \
+                   "movw     36(%esp), %dx\n" \
+                   "pushl    %edx\n" \
+                   "pushl    %ebx\n" \
+                   "pushl    $"#x"\n" \
+                   "pushl    $exp_return\n" \
+                   "jmp      "#f"\n"); \
+       __isr__ exp_##x(void)
 
 #endif
index 12d10a78a0502bbce4362af57c914ad21aa8af50..9a60cacd193e56cf352806d5a2ef4b2b0bda87b0 100644 (file)
@@ -45,6 +45,12 @@ extern ulong i386boot_bios_size;    /* size of BIOS emulation code */
 int cpu_init(void);
 int timer_init(void);
 
+/* cpu/.../interrupts.c */
+int cpu_init_interrupts(void);
+
+/* cpu/.../exceptions.c */
+int cpu_init_exceptions(void);
+
 /* board/.../... */
 int board_init(void);
 int dram_init(void);
index 2ae79d89f46947f45a03c18dd55cb6774bbb9fdb..84e1aefe1f873f53b8a5fda54e9ff403601f80d2 100644 (file)
@@ -90,7 +90,7 @@
 #define CONFIG_CMD_RUN         /* run command in env variable  */
 #define CONFIG_CMD_SETGETDCR   /* DCR support on 4xx           */
 #define CONFIG_CMD_XIMG                /* Load part of Multi Image     */
-#undef CONFIG_CMD_IRQ          /* IRQ Information              */
+#define CONFIG_CMD_IRQ         /* IRQ Information              */
 
 #define CONFIG_BOOTDELAY               15
 #define CONFIG_BOOTARGS                        "root=/dev/mtdblock0 console=ttyS0,9600"
 #undef  CONFIG_SYS_TSC_TIMER                   /* use the Pentium TSC timers */
 #define CONFIG_SYS_USE_SIO_UART                0       /* prefer the uarts on the SIO to those
                                         * in the SC520 on the CDP */
+#define CONFIG_SYS_PCAT_INTERRUPTS
+#define CONFIG_SYS_NUM_IRQS            16
 
 /*-----------------------------------------------------------------------
  * Memory organization
index cb0de421a2bea9e8966adf5c87b6226953c09e03..19e5889ff66d5760731c1aa8792cb146218e804e 100644 (file)
@@ -53,6 +53,8 @@
 #undef  CONFIG_SYS_TSC_TIMER                   /* use the Pentium TSC timers */
 #define  CONFIG_SYS_USE_SIO_UART       0       /* prefer the uarts on the SIO to those
                                         * in the SC520 on the CDP */
+#define CONFIG_SYS_PCAT_INTERRUPTS
+#define CONFIG_SYS_NUM_IRQS            16
 
 #define CONFIG_SYS_STACK_SIZE          0x8000  /* Size of bootloader stack */
 
index 3e8618498f3238a4627eb438cfdc1f6061bae39d..20481bd499d5402e28b2565e2067e927ab6416db 100644 (file)
@@ -49,6 +49,8 @@
 #undef  CONFIG_SYS_SC520_TIMER                 /* use SC520 swtimers */
 #define CONFIG_SYS_GENERIC_TIMER       1       /* use the i8254 PIT timers */
 #undef  CONFIG_SYS_TSC_TIMER                   /* use the Pentium TSC timers */
+#define CONFIG_SYS_PCAT_INTERRUPTS
+#define CONFIG_SYS_NUM_IRQS            16
 
 #define CONFIG_SYS_STACK_SIZE          0x8000  /* Size of bootloader stack */
 
index 4fbcd08b8869a149ebf9ebdae8d39159730bab8c..fb4184b3676a8ebc459187c3fcc2426d9c95efd0 100644 (file)
@@ -38,6 +38,8 @@ COBJS-y       += realmode.o
 COBJS-y        += video_bios.o
 COBJS-y        += video.o
 COBJS-y        += zimage.o
+COBJS-y        += interrupts.o
+COBJS-$(CONFIG_SYS_PCAT_INTERRUPTS) += pcat_interrupts.o
 
 SRCS   := $(SOBJS-y:.o=.S) $(COBJS-y:.o=.c)
 OBJS   := $(addprefix $(obj),$(SOBJS-y) $(COBJS-y))
diff --git a/lib_i386/interrupts.c b/lib_i386/interrupts.c
new file mode 100644 (file)
index 0000000..b0f84de
--- /dev/null
@@ -0,0 +1,159 @@
+/*
+ * (C) Copyright 2009
+ * Graeme Russ, graeme.russ@gmail.com
+ *
+ * (C) Copyright 2007
+ * Daniel Hellstrom, Gaisler Research, daniel@gaisler.com
+ *
+ * (C) Copyright 2006
+ * Detlev Zundel, DENX Software Engineering, dzu@denx.de
+ *
+ * (C) Copyright -2003
+ * Wolfgang Denk, DENX Software Engineering, wd@denx.de.
+ *
+ * (C) Copyright 2002
+ * Daniel Engström, Omicron Ceti AB, daniel@omicron.se
+ *
+ * (C) Copyright 2001
+ * Josh Huber <huber@mclx.com>, Mission Critical Linux, Inc.
+ *
+ * See file CREDITS for list of people who contributed to this
+ * project.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
+ * MA 02111-1307 USA
+ */
+
+/*
+ * This file contains the high-level API for the interrupt sub-system
+ * of the i386 port of U-Boot. Most of the functionality has been
+ * shamelessly stolen from the leon2 / leon3 ports of U-Boot.
+ * Daniel Hellstrom, Detlev Zundel, Wolfgang Denk and Josh Huber are
+ * credited for the corresponding work on those ports. The original
+ * interrupt handling routines for the i386 port were written by
+ * Daniel Engström
+ */
+
+#include <common.h>
+#include <asm/interrupt.h>
+
+struct irq_action {
+       interrupt_handler_t *handler;
+       void *arg;
+       unsigned int count;
+};
+
+static struct irq_action irq_handlers[CONFIG_SYS_NUM_IRQS] = { {0} };
+static int spurious_irq_cnt = 0;
+static int spurious_irq = 0;
+
+void irq_install_handler(int irq, interrupt_handler_t *handler, void *arg)
+{
+       int status;
+
+       if (irq < 0 || irq >= CONFIG_SYS_NUM_IRQS) {
+               printf("irq_install_handler: bad irq number %d\n", irq);
+               return;
+       }
+
+       if (irq_handlers[irq].handler != NULL)
+               printf("irq_install_handler: 0x%08lx replacing 0x%08lx\n",
+                      (ulong) handler,
+                      (ulong) irq_handlers[irq].handler);
+
+       status = disable_interrupts ();
+
+       irq_handlers[irq].handler = handler;
+       irq_handlers[irq].arg = arg;
+       irq_handlers[irq].count = 0;
+
+       unmask_irq(irq);
+
+       if (status)
+               enable_interrupts();
+
+       return;
+}
+
+void irq_free_handler(int irq)
+{
+       int status;
+
+       if (irq < 0 || irq >= CONFIG_SYS_NUM_IRQS) {
+               printf("irq_free_handler: bad irq number %d\n", irq);
+               return;
+       }
+
+       status = disable_interrupts ();
+
+       mask_irq(irq);
+
+       irq_handlers[irq].handler = NULL;
+       irq_handlers[irq].arg = NULL;
+
+       if (status)
+               enable_interrupts();
+
+       return;
+}
+
+__isr__ do_irq(int irq)
+{
+       if (irq < 0 || irq >= CONFIG_SYS_NUM_IRQS) {
+               printf("do_irq: bad irq number %d\n", irq);
+               return;
+       }
+
+       if (irq_handlers[irq].handler) {
+               mask_irq(irq);
+
+               irq_handlers[irq].handler(irq_handlers[irq].arg);
+               irq_handlers[irq].count++;
+
+               unmask_irq(irq);
+               specific_eoi(irq);
+
+       } else {
+               if ((irq & 7) != 7) {
+                       spurious_irq_cnt++;
+                       spurious_irq = irq;
+               }
+       }
+}
+
+#if defined(CONFIG_CMD_IRQ)
+int do_irqinfo(cmd_tbl_t *cmdtp, int flag, int argc, char *argv[])
+{
+       int irq;
+
+       printf("Spurious IRQ: %u, last unknown IRQ: %d\n",
+              spurious_irq_cnt, spurious_irq);
+
+       printf ("Interrupt-Information:\n");
+       printf ("Nr  Routine   Arg       Count\n");
+
+       for (irq = 0; irq <= CONFIG_SYS_NUM_IRQS; irq++) {
+               if (irq_handlers[irq].handler != NULL) {
+                       printf ("%02d  %08lx  %08lx  %d\n",
+                                       irq,
+                                       (ulong)irq_handlers[irq].handler,
+                                       (ulong)irq_handlers[irq].arg,
+                                       irq_handlers[irq].count);
+               }
+       }
+
+       return 0;
+}
+#endif
diff --git a/lib_i386/pcat_interrupts.c b/lib_i386/pcat_interrupts.c
new file mode 100644 (file)
index 0000000..f01298e
--- /dev/null
@@ -0,0 +1,165 @@
+/*
+ * (C) Copyright 2009
+ * Graeme Russ, graeme.russ@gmail.com
+ *
+ * (C) Copyright 2002
+ * Daniel Engström, Omicron Ceti AB, daniel@omicron.se.
+ *
+ * See file CREDITS for list of people who contributed to this
+ * project.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
+ * MA 02111-1307 USA
+ */
+
+/*
+ * 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
+
+DECLARE_INTERRUPT(0);
+DECLARE_INTERRUPT(1);
+DECLARE_INTERRUPT(3);
+DECLARE_INTERRUPT(4);
+DECLARE_INTERRUPT(5);
+DECLARE_INTERRUPT(6);
+DECLARE_INTERRUPT(7);
+DECLARE_INTERRUPT(8);
+DECLARE_INTERRUPT(9);
+DECLARE_INTERRUPT(10);
+DECLARE_INTERRUPT(11);
+DECLARE_INTERRUPT(12);
+DECLARE_INTERRUPT(13);
+DECLARE_INTERRUPT(14);
+DECLARE_INTERRUPT(15);
+
+int interrupt_init(void)
+{
+       u8 i;
+
+       disable_interrupts();
+
+       /* Setup interrupts */
+       set_vector(0x20, irq_0);
+       set_vector(0x21, irq_1);
+       set_vector(0x23, irq_3);
+       set_vector(0x24, irq_4);
+       set_vector(0x25, irq_5);
+       set_vector(0x26, irq_6);
+       set_vector(0x27, irq_7);
+       set_vector(0x28, irq_8);
+       set_vector(0x29, irq_9);
+       set_vector(0x2a, irq_10);
+       set_vector(0x2b, irq_11);
+       set_vector(0x2c, irq_12);
+       set_vector(0x2d, irq_13);
+       set_vector(0x2e, irq_14);
+       set_vector(0x2f, irq_15);
+
+       /* 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);
+
+       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);
+}