Port-cobalt archive

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index][Old Index]

interrupt fix for multi-function PCI devices



I'm playing with misc devices on Qube2700 and it seems
all interrupt PINs (at least PIN_A and PIN_C) on the PCI slot
are connected to ICU irq 9.

To support these multi function or multi interrupt devices
we have to change interrupt establish code to allow multiple
interrupts on each irq as Kiyohara-san posted before, but
I've rewritten interrupt code based on evbmips/malt/pci/pcib.c
rather than patching current one.

The attached patch contains the following changes:

- split out interrupt stuff from machdep.c into new interrupt.c
- prepare struct *_intrhead to handle cpu and icu interrupts
- use LIST and malloc(9) to handle multiple interrupts per icu irq
- move evcnt(9) stuff into *_intrhead from cobalt_intrhand
  (i.e. make it per interrupt rather than per device)
- in pci_intr_map(9) handle CPU interrupt separately and add a sanity check
- fixup interrupt lines for devices on the PCI slot in pci_conf_interrupt(9)
- move some device address definitions into <machine/cpu.h> and
  remove <machine/leds.h>
- misc cosmetics

and a compiled GENERIC kernel is here:
http://www.ceres.dti.ne.jp/~tsutsui/netbsd/netbsd-cobalt-GENERIC-20080302.gz

I've tested this patch on RaQ2 and Qube2700, and it seems working fine.
(though I haven't tested with multi function devices very well)

If there is no objection, I'll commit this in a next few days.
---
Izumi Tsutsui


Index: cobalt/autoconf.c
===================================================================
RCS file: /cvsroot/src/sys/arch/cobalt/cobalt/autoconf.c,v
retrieving revision 1.27
diff -u -r1.27 autoconf.c
--- cobalt/autoconf.c   3 Dec 2007 15:33:26 -0000       1.27
+++ cobalt/autoconf.c   2 Mar 2008 05:58:59 -0000
@@ -51,7 +51,7 @@
 
        (void)splhigh();
 
-       icu_init();
+       intr_init();
 
        if (config_rootfound("mainbus", NULL) == NULL)
                panic("no mainbus found");
Index: cobalt/machdep.c
===================================================================
RCS file: /cvsroot/src/sys/arch/cobalt/cobalt/machdep.c,v
retrieving revision 1.92
diff -u -r1.92 machdep.c
--- cobalt/machdep.c    9 Jan 2008 20:38:35 -0000       1.92
+++ cobalt/machdep.c    2 Mar 2008 05:58:59 -0000
@@ -70,26 +70,17 @@
 #include <sys/boot_flag.h>
 #include <sys/ksyms.h>
 #include <sys/cpu.h>
-#include <sys/intr.h>
 
 #include <uvm/uvm_extern.h>
 
-#include <mips/mips3_clock.h>
 #include <machine/bootinfo.h>
-#include <machine/bus.h>
-#include <machine/leds.h>
 #include <machine/psl.h>
 
 #include <mips/locore.h>
 
 #include <dev/cons.h>
 
-#include <dev/ic/i8259reg.h>
-#include <dev/isa/isareg.h>
-
 #include <cobalt/dev/gtreg.h>
-#define GT_BASE                0x14000000      /* XXX */
-#define PCIB_BASE      0x10000000      /* XXX */
 
 #ifdef KGDB
 #include <sys/kgdb.h>
@@ -439,338 +430,6 @@
                ;
 }
 
-
-#define NCPU_INT       6
-#define NICU_INT       16
-#define IRQ_SLAVE      2
-
-#define IO_ELCR                0x4d0
-#define IO_ELCRSIZE    2
-#define ELCR0          0
-#define ELCR1          1
-
-#define ICU1_READ(reg)         \
-    bus_space_read_1(icu_bst, icu1_bsh, (reg))
-#define ICU1_WRITE(reg, val)   \
-    bus_space_write_1(icu_bst, icu1_bsh, (reg), (val))
-#define ICU2_READ(reg)         \
-    bus_space_read_1(icu_bst, icu2_bsh, (reg))
-#define ICU2_WRITE(reg, val)   \
-    bus_space_write_1(icu_bst, icu2_bsh, (reg), (val))
-#define ELCR_READ(reg)         \
-    bus_space_read_1(icu_bst, elcr_bsh, (reg))
-#define ELCR_WRITE(reg, val)   \
-    bus_space_write_1(icu_bst, elcr_bsh, (reg), (val))
-
-u_int icu_imen;
-
-static bus_space_tag_t icu_bst;
-static bus_space_handle_t icu1_bsh, icu2_bsh, elcr_bsh;
-static struct cobalt_intrhand cpu_intrtab[NCPU_INT];
-static struct cobalt_intrhand icu_intrtab[NICU_INT];
-
-static int     icu_intr(void *);
-static void    icu_reinit_irqs(void);
-static u_int   icu_setmask(u_int);
-
-
-void
-icu_init(void)
-{
-
-       icu_bst = 0;    /* XXX unused on cobalt */
-       bus_space_map(icu_bst, PCIB_BASE + IO_ICU1, IO_ICUSIZE, 0, &icu1_bsh);
-       bus_space_map(icu_bst, PCIB_BASE + IO_ICU2, IO_ICUSIZE, 0, &icu2_bsh);
-       bus_space_map(icu_bst, PCIB_BASE + IO_ELCR, IO_ELCRSIZE, 0, &elcr_bsh);
-
-       /* Initialize master PIC */
-
-       /* reset; program device, four bytes */
-       ICU1_WRITE(PIC_ICW1, ICW1_SELECT | ICW1_IC4);
-       /* starting at this vector index */
-       ICU1_WRITE(PIC_ICW2, 0);                        /* XXX */
-       /* slave on line 2 */
-       ICU1_WRITE(PIC_ICW3, ICW3_CASCADE(IRQ_SLAVE));
-       /* special fully nested mode, 8086 mode */
-       ICU1_WRITE(PIC_ICW4, ICW4_SFNM | ICW4_8086);
-       /* special mask mode */
-       ICU1_WRITE(PIC_OCW3, OCW3_SELECT | OCW3_SSMM | OCW3_SMM);
-       /* read IRR by default */
-       ICU1_WRITE(PIC_OCW3, OCW3_SELECT | OCW3_RR);
-
-       /* Initialize slave PIC */
-
-       /* reset; program device, four bytes */
-       ICU2_WRITE(PIC_ICW1, ICW1_SELECT | ICW1_IC4);
-       /* starting at this vector index */
-       ICU2_WRITE(PIC_ICW2, 8);                        /* XXX */
-       /* slave connected to line 2 of master */
-       ICU2_WRITE(PIC_ICW3, ICW3_SIC(IRQ_SLAVE));
-       /* special fully nested mode, 8086 mode */
-       ICU2_WRITE(PIC_ICW4, ICW4_SFNM | ICW4_8086);
-       /* special mask mode */
-       ICU2_WRITE(PIC_OCW3, OCW3_SELECT | OCW3_SSMM | OCW3_SMM);
-       /* read IRR by default */
-       ICU2_WRITE(PIC_OCW3, OCW3_SELECT | OCW3_RR);
-
-       icu_setmask(0xffff);    /* mask all interrupts */
-
-       /* default to edge-triggered */
-       ELCR_WRITE(ELCR0, 0);
-       ELCR_WRITE(ELCR1, 0);
-
-       wbflush();
-
-       cpu_intr_establish(4, IPL_NONE, icu_intr, NULL);
-}
-
-void *
-icu_intr_establish(int irq, int type, int ipl, int (*func)(void *),
-    void *arg)
-{
-       struct cobalt_intrhand *ih;
-
-       ih = &icu_intrtab[irq];
-       if (ih->ih_func != NULL) {
-               printf("%s: irq %d is already in use\n", __func__, irq);
-               return NULL;
-       }
-
-       ih->ih_cookie_type = COBALT_COOKIE_TYPE_ICU;
-       ih->ih_func = func;
-       ih->ih_arg = arg;
-       ih->ih_type = type;
-       snprintf(ih->ih_evname, sizeof(ih->ih_evname), "irq %d", irq);
-       evcnt_attach_dynamic(&ih->ih_evcnt, EVCNT_TYPE_INTR, NULL, "icu",
-           ih->ih_evname);
-
-       icu_reinit_irqs();
-
-       return ih;
-}
-
-void
-icu_intr_disestablish(void *cookie)
-{
-       struct cobalt_intrhand *ih = cookie;
-
-       if (ih->ih_cookie_type == COBALT_COOKIE_TYPE_ICU) {
-               ih->ih_func = NULL;
-               ih->ih_arg = NULL;
-               ih->ih_cookie_type = 0;
-               ih->ih_type = IST_NONE;
-               evcnt_detach(&ih->ih_evcnt);
-
-               icu_reinit_irqs();
-       }
-}
-
-void
-icu_reinit_irqs(void)
-{
-       u_int i, irqs, elcr;
-
-       /* unmask interrupts */
-       irqs = 0;
-       elcr = 0;
-       for (i = 0; i < NICU_INT; i++) {
-               if (icu_intrtab[i].ih_func) {
-                       irqs |= 1 << i;
-                       if (icu_intrtab[i].ih_type == IST_LEVEL)
-                               elcr |= 1 << i;
-               }
-       }
-       if (irqs & 0xff00) /* any slave IRQs in use */
-               irqs |= 1 << IRQ_SLAVE;
-       icu_imen = ~irqs;
-
-       ICU1_WRITE(PIC_OCW1, icu_imen);
-       ICU2_WRITE(PIC_OCW1, icu_imen >> 8);
-
-       ELCR_WRITE(ELCR0, elcr);
-       ELCR_WRITE(ELCR1, elcr >> 8);
-}
-
-u_int
-icu_setmask(u_int mask)
-{
-       u_int old;
-
-       old = icu_imen;
-       icu_imen = mask;
-       ICU1_WRITE(PIC_OCW1, icu_imen);
-       ICU2_WRITE(PIC_OCW1, icu_imen >> 8);
-
-       return old;
-}
-
-int
-icu_intr(void *arg)
-{
-       struct cobalt_intrhand *ih;
-       int irq, handled;
-
-       handled = 0;
-
-       /* check requested irq */
-       ICU1_WRITE(PIC_OCW3, OCW3_SELECT | OCW3_POLL);
-       irq = ICU1_READ(PIC_OCW3);
-       if ((irq & OCW3_POLL_PENDING) == 0)
-               goto out;
-
-       irq = OCW3_POLL_IRQ(irq);
-       if (irq == IRQ_SLAVE) {
-               ICU2_WRITE(PIC_OCW3, OCW3_SELECT | OCW3_POLL);
-               irq = OCW3_POLL_IRQ(ICU2_READ(PIC_OCW3)) + 8;
-       }
-
-       ih = &icu_intrtab[irq];
-       if (__predict_false(ih->ih_func == NULL)) {
-               printf("icu_intr(): spurious interrupt (irq = %d)\n", irq);
-       } else if (__predict_true((*ih->ih_func)(ih->ih_arg))) {
-               ih->ih_evcnt.ev_count++;
-               handled = 1;
-       }
-
-       /* issue EOI to ack */
-       if (irq >= 8) {
-               ICU2_WRITE(PIC_OCW2,
-                   OCW2_SELECT | OCW2_SL | OCW2_EOI | OCW2_ILS(irq - 8));
-               irq = IRQ_SLAVE;
-       }
-       ICU1_WRITE(PIC_OCW2, OCW2_SELECT | OCW2_SL | OCW2_EOI | OCW2_ILS(irq));
-
- out:
-       return handled;
-}
-
-void *
-cpu_intr_establish(int level, int ipl, int (*func)(void *), void *arg)
-{
-       struct cobalt_intrhand *ih;
-
-       if (level < 0 || level >= NCPU_INT)
-               panic("invalid interrupt level");
-
-       ih = &cpu_intrtab[level];
-
-       if (ih->ih_func != NULL)
-               panic("cannot share CPU interrupts");
-
-       ih->ih_cookie_type = COBALT_COOKIE_TYPE_CPU;
-       ih->ih_func = func;
-       ih->ih_arg = arg;
-       snprintf(ih->ih_evname, sizeof(ih->ih_evname), "int %d", level);
-       evcnt_attach_dynamic(&ih->ih_evcnt, EVCNT_TYPE_INTR, NULL,
-           "cpu", ih->ih_evname);
-
-       return ih;
-}
-
-void
-cpu_intr_disestablish(void *cookie)
-{
-       struct cobalt_intrhand *ih = cookie;
-
-       if (ih->ih_cookie_type == COBALT_COOKIE_TYPE_CPU) {
-               ih->ih_func = NULL;
-               ih->ih_arg = NULL;
-               ih->ih_cookie_type = 0;
-               evcnt_detach(&ih->ih_evcnt);
-       }
-}
-
-void
-cpu_intr(uint32_t status, uint32_t cause, uint32_t pc, uint32_t ipending)
-{
-       struct clockframe cf;
-       struct cobalt_intrhand *ih;
-       struct cpu_info *ci;
-
-       ci = curcpu();
-       ci->ci_idepth++;
-       uvmexp.intrs++;
-
-       if (ipending & MIPS_INT_MASK_5) {
-               /* call the common MIPS3 clock interrupt handler */
-               cf.pc = pc;
-               cf.sr = status;
-               mips3_clockintr(&cf);
-
-               cause &= ~MIPS_INT_MASK_5;
-       }
-       _splset((status & MIPS_INT_MASK_5) | MIPS_SR_INT_IE);
-
-       if (__predict_false(ipending & MIPS_INT_MASK_0)) {
-               /* GT64x11 timer0 */
-               volatile uint32_t *irq_src =
-                   (uint32_t *)MIPS_PHYS_TO_KSEG1(GT_BASE + GT_INTR_CAUSE);
-
-               if (__predict_true((*irq_src & T0EXP) != 0)) {
-                       /* GT64x11 timer is no longer used for hardclock(9) */
-                       *irq_src = 0;
-               }
-               cause &= ~MIPS_INT_MASK_0;
-       }
-       _splset((status & ~cause & MIPS_HARD_INT_MASK) | MIPS_SR_INT_IE);
-
-       if (ipending & MIPS_INT_MASK_3) {
-               /* 16650 serial */
-               ih = &cpu_intrtab[3];
-               if (__predict_true(ih->ih_func != NULL)) {
-                       if (__predict_true((*ih->ih_func)(ih->ih_arg))) {
-                               cause &= ~MIPS_INT_MASK_3;
-                               ih->ih_evcnt.ev_count++;
-                       }
-               }
-       }
-       _splset((status & ~cause & MIPS_HARD_INT_MASK) | MIPS_SR_INT_IE);
-
-       if (ipending & MIPS_INT_MASK_1) {
-               /* tulip primary */
-               ih = &cpu_intrtab[1];
-               if (__predict_true(ih->ih_func != NULL)) {
-                       if (__predict_true((*ih->ih_func)(ih->ih_arg))) {
-                               cause &= ~MIPS_INT_MASK_1;
-                               ih->ih_evcnt.ev_count++;
-                       }
-               }
-       }
-       if (ipending & MIPS_INT_MASK_2) {
-               /* tulip secondary */
-               ih = &cpu_intrtab[2];
-               if (__predict_true(ih->ih_func != NULL)) {
-                       if (__predict_true((*ih->ih_func)(ih->ih_arg))) {
-                               cause &= ~MIPS_INT_MASK_2;
-                               ih->ih_evcnt.ev_count++;
-                       }
-               }
-       }
-       _splset((status & ~cause & MIPS_HARD_INT_MASK) | MIPS_SR_INT_IE);
-
-       if (ipending & MIPS_INT_MASK_4) {
-               /* ICU interrupts */
-               ih = &cpu_intrtab[4];
-               if (__predict_true(ih->ih_func != NULL)) {
-                       if (__predict_true((*ih->ih_func)(ih->ih_arg))) {
-                               cause &= ~MIPS_INT_MASK_4;
-                               /* evcnt for ICU is done in icu_intr() */
-                       }
-               }
-       }
-       _splset((status & ~cause & MIPS_HARD_INT_MASK) | MIPS_SR_INT_IE);
-       ci->ci_idepth--;
-
-#ifdef __HAVE_FAST_SOFTINTS
-       /* software interrupt */
-       ipending &= (MIPS_SOFT_INT_MASK_1|MIPS_SOFT_INT_MASK_0);
-       if (ipending == 0)
-               return;
-       _clrsoftintr(ipending);
-       softintr_dispatch(ipending);
-#endif
-}
-
 void
 decode_bootstring(void)
 {
@@ -909,19 +568,3 @@
 
        return COBALT_BOARD_ID(reg);
 }
-
-static const int ipl2spl_table[] = {
-       [IPL_NONE] = 0,
-       [IPL_SOFTCLOCK] = MIPS_SOFT_INT_MASK_0,
-       [IPL_SOFTNET] = MIPS_SOFT_INT_MASK_0|MIPS_SOFT_INT_MASK_1,
-       [IPL_VM] = SPLVM,
-       [IPL_SCHED] = SPLSCHED,
-       [IPL_HIGH] = MIPS_INT_MASK,
-};
-
-ipl_cookie_t
-makeiplcookie(ipl_t ipl)
-{
-
-       return (ipl_cookie_t){._spl = ipl2spl_table[ipl]};
-}
Index: conf/files.cobalt
===================================================================
RCS file: /cvsroot/src/sys/arch/cobalt/conf/files.cobalt,v
retrieving revision 1.30
diff -u -r1.30 files.cobalt
--- conf/files.cobalt   20 Feb 2008 21:43:33 -0000      1.30
+++ conf/files.cobalt   2 Mar 2008 05:58:59 -0000
@@ -34,6 +34,7 @@
 file arch/cobalt/cobalt/bus.c
 file arch/cobalt/cobalt/console.c
 file arch/cobalt/cobalt/disksubr.c
+file arch/cobalt/cobalt/interrupt.c
 file arch/cobalt/cobalt/machdep.c
 
 file arch/mips/mips/softintr.c
Index: dev/com_mainbus.c
===================================================================
RCS file: /cvsroot/src/sys/arch/cobalt/dev/com_mainbus.c,v
retrieving revision 1.14
diff -u -r1.14 com_mainbus.c
--- dev/com_mainbus.c   13 Jul 2006 22:56:00 -0000      1.14
+++ dev/com_mainbus.c   2 Mar 2008 05:58:59 -0000
@@ -130,6 +130,6 @@
 com_mainbus_cninit(struct consdev *cn)
 {
 
-       comcnattach(0, 0x1c800000, 115200, COM_MAINBUS_FREQ, COM_TYPE_NORMAL,
+       comcnattach(0, COM_BASE, 115200, COM_MAINBUS_FREQ, COM_TYPE_NORMAL,
            CONMODE);
 }
Index: dev/panel.c
===================================================================
RCS file: /cvsroot/src/sys/arch/cobalt/dev/panel.c,v
retrieving revision 1.14
diff -u -r1.14 panel.c
--- dev/panel.c 17 Oct 2007 19:54:08 -0000      1.14
+++ dev/panel.c 2 Mar 2008 05:58:59 -0000
@@ -133,7 +133,7 @@
        hd44780_attach_subr(&sc->sc_lcd);
 
        sc->sc_kp.sc_iot = maa->ma_iot;
-       sc->sc_kp.sc_ioh = MIPS_PHYS_TO_KSEG1(0x1d000000); /* XXX */
+       sc->sc_kp.sc_ioh = MIPS_PHYS_TO_KSEG1(PANEL_BASE); /* XXX */
 
        sc->sc_kp.sc_knum = sizeof(keys) / sizeof(struct lcdkp_xlate);
        sc->sc_kp.sc_kpad = keys;
Index: include/cpu.h
===================================================================
RCS file: /cvsroot/src/sys/arch/cobalt/include/cpu.h,v
retrieving revision 1.11
diff -u -r1.11 cpu.h
--- include/cpu.h       15 Apr 2006 11:28:52 -0000      1.11
+++ include/cpu.h       2 Mar 2008 05:58:59 -0000
@@ -14,6 +14,18 @@
 #define COBALT_ID_QUBE2                5
 #define COBALT_ID_RAQ2         6 
 
+/*
+ * Memory map and register definitions.
+ * XXX should be elsewhere?
+ */
+#define PCIB_BASE      0x10000000
+#define GT_BASE                0x14000000
+#define LED_ADDR       0x1c000000
+#define LED_RESET      0x0f            /* Resets machine. */
+#define LED_POWEROFF   3
+#define COM_BASE       0x1c800000
+#define PANEL_BASE     0x1d000000
+
 #endif /* !_LOCORE */
 #endif /* _KERNEL */
 
Index: include/intr.h
===================================================================
RCS file: /cvsroot/src/sys/arch/cobalt/include/intr.h,v
retrieving revision 1.30
diff -u -r1.30 intr.h
--- include/intr.h      4 Jan 2008 22:55:25 -0000       1.30
+++ include/intr.h      2 Mar 2008 05:58:59 -0000
@@ -81,26 +81,26 @@
        return _splraise(icookie._spl);
 }
 
+#define NCPU_INT       6
+#define NICU_INT       16
+
 struct cobalt_intrhand {
        LIST_ENTRY(cobalt_intrhand) ih_q;
        int (*ih_func)(void *);
        void *ih_arg;
-       int ih_type;
+       int ih_irq;
        int ih_cookie_type;
 #define        COBALT_COOKIE_TYPE_CPU  0x1
 #define        COBALT_COOKIE_TYPE_ICU  0x2
-
-       struct evcnt ih_evcnt;
-       char ih_evname[32];
 };
 
 #include <mips/softintr.h>
 
+void intr_init(void);
 void *cpu_intr_establish(int, int, int (*)(void *), void *);
 void *icu_intr_establish(int, int, int, int (*)(void *), void *);
 void cpu_intr_disestablish(void *);
 void icu_intr_disestablish(void *);
-void icu_init(void);
 
 #endif /* !_LOCORE */
 #endif /* _LOCORE */
Index: pci/pci_machdep.c
===================================================================
RCS file: /cvsroot/src/sys/arch/cobalt/pci/pci_machdep.c,v
retrieving revision 1.25
diff -u -r1.25 pci_machdep.c
--- pci/pci_machdep.c   18 Feb 2007 12:22:16 -0000      1.25
+++ pci/pci_machdep.c   2 Mar 2008 05:58:59 -0000
@@ -152,24 +152,33 @@
        pci_decompose_tag(pc, intrtag, &bus, &dev, &func);
 
        /*
-        * The interrupt lines of the two Tulips are connected
+        * The interrupt lines of the internal Tulips are connected
         * directly to the CPU.
         */
-
        if (cobalt_id == COBALT_ID_QUBE2700) {
-               if (bus == 0 && dev == 7 && pin == PCI_INTERRUPT_PIN_A)
-                       *ihp = 16 + 2;
-               else
-                       *ihp = line;
+               if (bus == 0 && dev == 7 && pin == PCI_INTERRUPT_PIN_A) {
+                       /* tulip is connected to CPU INT2 on Qube2700 */
+                       *ihp = NICU_INT + 2;
+                       return 0;
+               }
        } else {
-               if (bus == 0 && dev == 7 && pin == PCI_INTERRUPT_PIN_A)
-                       *ihp = 16 + 1;
-               else if (bus == 0 && dev == 12 && pin == PCI_INTERRUPT_PIN_A)
-                       *ihp = 16 + 2;
-               else
-                       *ihp = line;
+               if (bus == 0 && dev == 7 && pin == PCI_INTERRUPT_PIN_A) {
+                       /* the primary tulip is connected to CPU INT1 */
+                       *ihp = NICU_INT + 1;
+                       return 0;
+               }
+               if (bus == 0 && dev == 12 && pin == PCI_INTERRUPT_PIN_A) {
+                       /* the secondary tulip is connected to CPU INT2 */
+                       *ihp = NICU_INT + 2;
+                       return 0;
+               }
        }
 
+       /* sanity check */
+       if (line == 0 || line >= NICU_INT)
+               return -1;
+
+       *ihp = line;
        return 0;
 }
 
@@ -178,8 +187,8 @@
 {
        static char irqstr[8];
 
-       if (ih >= 16)
-               sprintf(irqstr, "level %d", ih - 16);
+       if (ih >= NICU_INT)
+               sprintf(irqstr, "level %d", ih - NICU_INT);
        else
                sprintf(irqstr, "irq %d", ih);
 
@@ -199,8 +208,8 @@
     int (*func)(void *), void *arg)
 {
 
-       if (ih >= 16)
-               return cpu_intr_establish(ih - 16, level, func, arg);
+       if (ih >= NICU_INT)
+               return cpu_intr_establish(ih - NICU_INT, level, func, arg);
        else
                return icu_intr_establish(ih, IST_LEVEL, level, func, arg);
 }
@@ -219,7 +228,12 @@
     int *iline)
 {
 
-       /* not yet... */
+       /*
+        * Use irq 9 on all devices on the Qube's PCI slot.
+        * XXX doesn't handle devices over PCI-PCI bridges
+        */
+       if (bus == 0 && dev == 10 && pin != PCI_INTERRUPT_PIN_NONE)
+               *iline = 9;
 }
 
 int
--- /dev/null   2008-03-02 14:41:44.000000000 +0900
+++ cobalt/interrupt.c  2008-03-01 15:01:02.000000000 +0900
@@ -0,0 +1,506 @@
+/*     $NetBSD$        */
+
+/*
+ * Copyright (c) 2006 Izumi Tsutsui.
+ * 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.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 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.
+ */
+
+/*-
+ * Copyright (c) 2001 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Jason R. Thorpe.
+ *
+ * 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.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *     This product includes software developed by the NetBSD
+ *     Foundation, Inc. and its contributors.
+ * 4. Neither the name of The NetBSD Foundation nor the names of its
+ *    contributors may be used to endorse or promote products derived
+ *    from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. 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 FOUNDATION 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.
+ */
+
+/*
+ * Copyright (c) 2000 Soren S. Jorvang.  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>
+__KERNEL_RCSID(0, "$NetBSD$");
+
+#include <sys/param.h>
+#include <sys/malloc.h>
+#include <sys/cpu.h>
+#include <sys/intr.h>
+
+#include <uvm/uvm_extern.h>
+
+#include <mips/mips3_clock.h>
+#include <machine/bus.h>
+
+#include <dev/ic/i8259reg.h>
+#include <dev/isa/isareg.h>
+
+#include <cobalt/dev/gtreg.h>
+
+#define ICU_LEVEL      4
+#define IRQ_SLAVE      2
+
+#define IO_ELCR                0x4d0
+#define IO_ELCRSIZE    2
+#define ELCR0          0
+#define ELCR1          1
+
+#define ICU1_READ(reg)         \
+    bus_space_read_1(icu_bst, icu1_bsh, (reg))
+#define ICU1_WRITE(reg, val)   \
+    bus_space_write_1(icu_bst, icu1_bsh, (reg), (val))
+#define ICU2_READ(reg)         \
+    bus_space_read_1(icu_bst, icu2_bsh, (reg))
+#define ICU2_WRITE(reg, val)   \
+    bus_space_write_1(icu_bst, icu2_bsh, (reg), (val))
+#define ELCR_READ(reg)         \
+    bus_space_read_1(icu_bst, elcr_bsh, (reg))
+#define ELCR_WRITE(reg, val)   \
+    bus_space_write_1(icu_bst, elcr_bsh, (reg), (val))
+
+static u_int icu_imask, icu_elcr;
+static bus_space_tag_t icu_bst;
+static bus_space_handle_t icu1_bsh, icu2_bsh, elcr_bsh;
+
+struct icu_intrhead {
+       LIST_HEAD(, cobalt_intrhand) intr_q;
+       int intr_type;
+       struct evcnt intr_evcnt;
+       char intr_evname[32];
+};
+static struct icu_intrhead icu_intrtab[NICU_INT];
+
+struct cpu_intrhead {
+       struct cobalt_intrhand intr_ih;
+       struct evcnt intr_evcnt;
+       char intr_evname[32];
+};
+static struct cpu_intrhead cpu_intrtab[NCPU_INT];
+
+static int     icu_intr(void *);
+static void    icu_set(void);
+
+void
+intr_init(void)
+{
+       int i;
+
+       /*
+        * Initialize CPU interrupts.
+        */
+       for (i = 0; i < NCPU_INT; i++) {
+               snprintf(cpu_intrtab[i].intr_evname,
+                   sizeof(cpu_intrtab[i].intr_evname), "int %d", i);
+               evcnt_attach_dynamic(&cpu_intrtab[i].intr_evcnt,
+                   EVCNT_TYPE_INTR, NULL, "mips", cpu_intrtab[i].intr_evname);
+       }
+
+       /*
+        * Initialize ICU interrupts.
+        */
+       icu_bst = 0;    /* XXX unused on cobalt */
+       bus_space_map(icu_bst, PCIB_BASE + IO_ICU1, IO_ICUSIZE, 0, &icu1_bsh);
+       bus_space_map(icu_bst, PCIB_BASE + IO_ICU2, IO_ICUSIZE, 0, &icu2_bsh);
+       bus_space_map(icu_bst, PCIB_BASE + IO_ELCR, IO_ELCRSIZE, 0, &elcr_bsh);
+
+       /* All interrupts default to "masked off". */
+       icu_imask = 0xffff;
+
+       /* All interrupts default to edge-triggered. */
+       icu_elcr = 0;
+
+       /* Initialize master PIC */
+
+       /* reset; program device, four bytes */
+       ICU1_WRITE(PIC_ICW1, ICW1_SELECT | ICW1_IC4);
+       /* starting at this vector index */
+       ICU1_WRITE(PIC_ICW2, 0);                        /* XXX */
+       /* slave on line 2 */
+       ICU1_WRITE(PIC_ICW3, ICW3_CASCADE(IRQ_SLAVE));
+       /* special fully nested mode, 8086 mode */
+       ICU1_WRITE(PIC_ICW4, ICW4_SFNM | ICW4_8086);
+       /* mask all interrupts */
+       ICU1_WRITE(PIC_OCW1, icu_imask & 0xff);
+       /* special mask mode */
+       ICU1_WRITE(PIC_OCW3, OCW3_SELECT | OCW3_SSMM | OCW3_SMM);
+       /* read IRR by default */
+       ICU1_WRITE(PIC_OCW3, OCW3_SELECT | OCW3_RR);
+
+       /* Initialize slave PIC */
+
+       /* reset; program device, four bytes */
+       ICU2_WRITE(PIC_ICW1, ICW1_SELECT | ICW1_IC4);
+       /* starting at this vector index */
+       ICU2_WRITE(PIC_ICW2, 8);                        /* XXX */
+       /* slave connected to line 2 of master */
+       ICU2_WRITE(PIC_ICW3, ICW3_SIC(IRQ_SLAVE));
+       /* special fully nested mode, 8086 mode */
+       ICU2_WRITE(PIC_ICW4, ICW4_SFNM | ICW4_8086);
+       /* mask all interrupts */
+       ICU1_WRITE(PIC_OCW1, (icu_imask >> 8) & 0xff);
+       /* special mask mode */
+       ICU2_WRITE(PIC_OCW3, OCW3_SELECT | OCW3_SSMM | OCW3_SMM);
+       /* read IRR by default */
+       ICU2_WRITE(PIC_OCW3, OCW3_SELECT | OCW3_RR);
+
+       /* default to edge-triggered */
+       ELCR_WRITE(ELCR0, icu_elcr & 0xff);
+       ELCR_WRITE(ELCR1, (icu_elcr >> 8) & 0xff);
+
+       wbflush();
+
+       /* Initialize our interrupt table. */
+       for (i = 0; i < NICU_INT; i++) {
+               LIST_INIT(&icu_intrtab[i].intr_q);
+               snprintf(icu_intrtab[i].intr_evname,
+                   sizeof(icu_intrtab[i].intr_evname), "irq %d", i);
+               evcnt_attach_dynamic(&icu_intrtab[i].intr_evcnt,
+                   EVCNT_TYPE_INTR, &cpu_intrtab[ICU_LEVEL].intr_evcnt,
+                   "icu", icu_intrtab[i].intr_evname);
+               icu_intrtab[i].intr_type = IST_NONE;
+       }
+
+       cpu_intr_establish(ICU_LEVEL, IPL_NONE, icu_intr, NULL);
+}
+
+void *
+icu_intr_establish(int irq, int type, int ipl, int (*func)(void *),
+    void *arg)
+{
+       struct cobalt_intrhand *ih;
+       int s;
+
+       if (irq >= NICU_INT || irq == IRQ_SLAVE || type == IST_NONE)
+               panic("%s: bad irq or type", __func__);
+
+       switch (icu_intrtab[irq].intr_type) {
+       case IST_NONE:
+               icu_intrtab[irq].intr_type = type;
+               break;
+
+       case IST_EDGE:
+       case IST_LEVEL:
+               if (type == icu_intrtab[irq].intr_type)
+                       break;
+               /* FALLTHROUGH */
+       case IST_PULSE:
+               /*
+                * We can't share interrupts in this case.
+                */
+               return NULL;
+       }
+
+       ih = malloc(sizeof(*ih), M_DEVBUF, M_NOWAIT);
+       if (ih == NULL)
+               return NULL;
+
+       ih->ih_func = func;
+       ih->ih_arg = arg;
+       ih->ih_irq = irq;
+       ih->ih_cookie_type = COBALT_COOKIE_TYPE_ICU;
+
+       s = splhigh();
+
+       /* Insert the handler into the table. */
+       LIST_INSERT_HEAD(&icu_intrtab[irq].intr_q, ih, ih_q);
+
+       /* Enable it, set trigger mode. */
+       icu_imask &= ~(1U << irq);
+       if (icu_intrtab[irq].intr_type == IST_LEVEL)
+               icu_elcr |= (1U << irq);
+       else
+               icu_elcr &= ~(1U << irq);
+
+       icu_set();
+
+       splx(s);
+
+       return ih;
+}
+
+void
+icu_intr_disestablish(void *cookie)
+{
+       struct cobalt_intrhand *ih = cookie;
+       int s;
+
+       if (ih->ih_cookie_type == COBALT_COOKIE_TYPE_ICU) {
+               s = splhigh();
+
+               LIST_REMOVE(ih, ih_q);
+
+               if (LIST_FIRST(&icu_intrtab[ih->ih_irq].intr_q) == NULL) {
+                       icu_imask |= (1U << ih->ih_irq);
+                       icu_set();
+               }
+               splx(s);
+               free(ih, M_DEVBUF);
+       }
+}
+
+void
+icu_set(void)
+{
+
+       if ((icu_imask & 0xff00) != 0xff00)
+               icu_imask &= ~(1U << IRQ_SLAVE);
+       else
+               icu_imask |= (1U << IRQ_SLAVE);
+
+       ICU1_WRITE(PIC_OCW1, icu_imask);
+       ICU2_WRITE(PIC_OCW1, icu_imask >> 8);
+
+       ELCR_WRITE(ELCR0, icu_elcr);
+       ELCR_WRITE(ELCR1, icu_elcr >> 8);
+}
+
+int
+icu_intr(void *arg)
+{
+       struct cobalt_intrhand *ih;
+       int irq, handled;
+
+       handled = 0;
+
+       for (;;) {
+               /* check requested irq */
+               ICU1_WRITE(PIC_OCW3, OCW3_SELECT | OCW3_POLL);
+               irq = ICU1_READ(PIC_OCW3);
+               if ((irq & OCW3_POLL_PENDING) == 0)
+                       return handled;
+
+               irq = OCW3_POLL_IRQ(irq);
+               if (irq == IRQ_SLAVE) {
+                       ICU2_WRITE(PIC_OCW3, OCW3_SELECT | OCW3_POLL);
+                       irq = OCW3_POLL_IRQ(ICU2_READ(PIC_OCW3)) + 8;
+               }
+
+               icu_intrtab[irq].intr_evcnt.ev_count++;
+               LIST_FOREACH(ih, &icu_intrtab[irq].intr_q, ih_q) {
+                       if (__predict_false(ih->ih_func == NULL))
+                               printf("%s: spurious interrupt (irq = %d)\n",
+                                   __func__, irq);
+                       else if (__predict_true((*ih->ih_func)(ih->ih_arg))) {
+                               handled = 1;
+                       }
+               }
+
+               /* issue EOI to ack */
+               if (irq >= 8) {
+                       ICU2_WRITE(PIC_OCW2,
+                           OCW2_SELECT | OCW2_SL | OCW2_EOI |
+                           OCW2_ILS(irq - 8));
+                       irq = IRQ_SLAVE;
+               }
+               ICU1_WRITE(PIC_OCW2,
+                   OCW2_SELECT | OCW2_SL | OCW2_EOI | OCW2_ILS(irq));
+       }
+}
+
+void *
+cpu_intr_establish(int level, int ipl, int (*func)(void *), void *arg)
+{
+       struct cobalt_intrhand *ih;
+
+       if (level < 0 || level >= NCPU_INT)
+               panic("invalid interrupt level");
+
+       ih = &cpu_intrtab[level].intr_ih;
+
+       if (ih->ih_func != NULL)
+               panic("cannot share CPU interrupts");
+
+       ih->ih_cookie_type = COBALT_COOKIE_TYPE_CPU;
+       ih->ih_func = func;
+       ih->ih_arg = arg;
+       ih->ih_irq = NICU_INT + level;
+
+       return ih;
+}
+
+void
+cpu_intr_disestablish(void *cookie)
+{
+       struct cobalt_intrhand *ih = cookie;
+
+       if (ih->ih_cookie_type == COBALT_COOKIE_TYPE_CPU) {
+               ih->ih_func = NULL;
+               ih->ih_arg = NULL;
+               ih->ih_cookie_type = 0;
+       }
+}
+
+void
+cpu_intr(uint32_t status, uint32_t cause, uint32_t pc, uint32_t ipending)
+{
+       struct clockframe cf;
+       struct cobalt_intrhand *ih;
+       struct cpu_info *ci;
+
+       ci = curcpu();
+       ci->ci_idepth++;
+       uvmexp.intrs++;
+
+       if (ipending & MIPS_INT_MASK_5) {
+               /* call the common MIPS3 clock interrupt handler */
+               cf.pc = pc;
+               cf.sr = status;
+               mips3_clockintr(&cf);
+
+               cause &= ~MIPS_INT_MASK_5;
+       }
+       _splset((status & MIPS_INT_MASK_5) | MIPS_SR_INT_IE);
+
+       if (__predict_false(ipending & MIPS_INT_MASK_0)) {
+               /* GT64x11 timer0 */
+               volatile uint32_t *irq_src =
+                   (uint32_t *)MIPS_PHYS_TO_KSEG1(GT_BASE + GT_INTR_CAUSE);
+
+               if (__predict_true((*irq_src & T0EXP) != 0)) {
+                       /* GT64x11 timer is no longer used for hardclock(9) */
+                       *irq_src = 0;
+               }
+               cause &= ~MIPS_INT_MASK_0;
+       }
+       _splset((status & ~cause & MIPS_HARD_INT_MASK) | MIPS_SR_INT_IE);
+
+       if (ipending & MIPS_INT_MASK_3) {
+               /* 16650 serial */
+               ih = &cpu_intrtab[3].intr_ih;
+               if (__predict_true(ih->ih_func != NULL)) {
+                       if (__predict_true((*ih->ih_func)(ih->ih_arg))) {
+                               cause &= ~MIPS_INT_MASK_3;
+                               cpu_intrtab[3].intr_evcnt.ev_count++;
+                       }
+               }
+       }
+       _splset((status & ~cause & MIPS_HARD_INT_MASK) | MIPS_SR_INT_IE);
+
+       if (ipending & MIPS_INT_MASK_1) {
+               /* tulip primary */
+               ih = &cpu_intrtab[1].intr_ih;
+               if (__predict_true(ih->ih_func != NULL)) {
+                       if (__predict_true((*ih->ih_func)(ih->ih_arg))) {
+                               cause &= ~MIPS_INT_MASK_1;
+                               cpu_intrtab[1].intr_evcnt.ev_count++;
+                       }
+               }
+       }
+       if (ipending & MIPS_INT_MASK_2) {
+               /* tulip secondary */
+               ih = &cpu_intrtab[2].intr_ih;
+               if (__predict_true(ih->ih_func != NULL)) {
+                       if (__predict_true((*ih->ih_func)(ih->ih_arg))) {
+                               cause &= ~MIPS_INT_MASK_2;
+                               cpu_intrtab[2].intr_evcnt.ev_count++;
+                       }
+               }
+       }
+       if (ipending & MIPS_INT_MASK_4) {
+               /* ICU interrupts */
+               ih = &cpu_intrtab[4].intr_ih;
+               if (__predict_true(ih->ih_func != NULL)) {
+                       if (__predict_true((*ih->ih_func)(ih->ih_arg))) {
+                               cause &= ~MIPS_INT_MASK_4;
+                               cpu_intrtab[4].intr_evcnt.ev_count++;
+                       }
+               }
+       }
+       _splset((status & ~cause & MIPS_HARD_INT_MASK) | MIPS_SR_INT_IE);
+       ci->ci_idepth--;
+
+#ifdef __HAVE_FAST_SOFTINTS
+       /* software interrupt */
+       ipending &= (MIPS_SOFT_INT_MASK_1|MIPS_SOFT_INT_MASK_0);
+       if (ipending == 0)
+               return;
+       _clrsoftintr(ipending);
+       softintr_dispatch(ipending);
+#endif
+}
+
+
+static const int ipl2spl_table[] = {
+       [IPL_NONE] = 0,
+       [IPL_SOFTCLOCK] = MIPS_SOFT_INT_MASK_0,
+       [IPL_SOFTNET] = MIPS_SOFT_INT_MASK_0|MIPS_SOFT_INT_MASK_1,
+       [IPL_VM] = SPLVM,
+       [IPL_SCHED] = SPLSCHED,
+       [IPL_HIGH] = MIPS_INT_MASK,
+};
+
+ipl_cookie_t
+makeiplcookie(ipl_t ipl)
+{
+
+       return (ipl_cookie_t){._spl = ipl2spl_table[ipl]};
+}


Home | Main Index | Thread Index | Old Index