Module Name: src Committed By: cliff Date: Mon Nov 9 10:03:04 UTC 2009
Modified Files: src/sys/arch/mips/rmi [matt-nb5-mips64]: rmixl_intr.c Log Message: - multiple changes; make interrupts work To generate a diff of this commit: cvs rdiff -u -r1.1.2.3 -r1.1.2.4 src/sys/arch/mips/rmi/rmixl_intr.c Please note that diffs are not public domain; they are subject to the copyright notices on the relevant files.
Modified files: Index: src/sys/arch/mips/rmi/rmixl_intr.c diff -u src/sys/arch/mips/rmi/rmixl_intr.c:1.1.2.3 src/sys/arch/mips/rmi/rmixl_intr.c:1.1.2.4 --- src/sys/arch/mips/rmi/rmixl_intr.c:1.1.2.3 Fri Sep 25 22:27:02 2009 +++ src/sys/arch/mips/rmi/rmixl_intr.c Mon Nov 9 10:03:04 2009 @@ -1,4 +1,4 @@ -/* $NetBSD: rmixl_intr.c,v 1.1.2.3 2009/09/25 22:27:02 cliff Exp $ */ +/* $NetBSD: rmixl_intr.c,v 1.1.2.4 2009/11/09 10:03:04 cliff Exp $ */ /*- * Copyright (c) 2007 Ruslan Ermilov and Vsevolod Lobko. @@ -64,7 +64,7 @@ */ #include <sys/cdefs.h> -__KERNEL_RCSID(0, "$NetBSD: rmixl_intr.c,v 1.1.2.3 2009/09/25 22:27:02 cliff Exp $"); +__KERNEL_RCSID(0, "$NetBSD: rmixl_intr.c,v 1.1.2.4 2009/11/09 10:03:04 cliff Exp $"); #include "opt_ddb.h" @@ -85,6 +85,17 @@ #include <dev/pci/pcireg.h> #include <dev/pci/pcivar.h> +#ifdef IOINTR_DEBUG +int iointr_debug = IOINTR_DEBUG; +# define DPRINTF(x) do { if (iointr_debug) printf x ; } while(0) +#else +# define DPRINTF(x) +#endif + +#define RMIXL_PICREG_READ(off) \ + RMIXL_IOREG_READ(RMIXL_IO_DEV_PIC + (off)) +#define RMIXL_PICREG_WRITE(off, val) \ + RMIXL_IOREG_WRITE(RMIXL_IO_DEV_PIC + (off), (val)) /* * This is a mask of bits to clear in the SR when we go to a * given hardware interrupt priority level. @@ -101,14 +112,7 @@ | MIPS_SOFT_INT_MASK_1 | MIPS_INT_MASK_0, [IPL_SCHED] = - MIPS_SOFT_INT_MASK_0 - | MIPS_SOFT_INT_MASK_1 - | MIPS_INT_MASK_0 - | MIPS_INT_MASK_1 - | MIPS_INT_MASK_2 - | MIPS_INT_MASK_3 - | MIPS_INT_MASK_4 - | MIPS_INT_MASK_5, + MIPS_INT_MASK, }; /* @@ -118,10 +122,15 @@ * * NOTE: many irq sources depend on the chip family * XLS1xx vs. XLS2xx vs. XLS3xx vs. XLS6xx - * so just use generic names where they diverge + * use the right table for the CPU that's running. + */ + +/* + * rmixl_irqnames_xls1xx + * - use for XLS1xx, XLS2xx, XLS4xx-Lite */ #define NIRQS 32 -static const char *rmixl_irqnames[NIRQS] = { +static const char *rmixl_irqnames_xls1xx[NIRQS] = { "int 0 (watchdog)", /* 0 */ "int 1 (timer0)", /* 1 */ "int 2 (timer1)", /* 2 */ @@ -157,6 +166,84 @@ }; /* + * rmixl_irqnames_xls4xx: + * - use for XLS4xx, XLS6xx + */ +static const char *rmixl_irqnames_xls4xx[NIRQS] = { + "int 0 (watchdog)", /* 0 */ + "int 1 (timer0)", /* 1 */ + "int 2 (timer1)", /* 2 */ + "int 3 (timer2)", /* 3 */ + "int 4 (timer3)", /* 4 */ + "int 5 (timer4)", /* 5 */ + "int 6 (timer5)", /* 6 */ + "int 7 (timer6)", /* 7 */ + "int 8 (timer7)", /* 8 */ + "int 9 (uart0)", /* 9 */ + "int 10 (uart1)", /* 10 */ + "int 11 (i2c0)", /* 11 */ + "int 12 (i2c1)", /* 12 */ + "int 13 (pcmcia)", /* 13 */ + "int 14 (gpio_a)", /* 14 */ + "int 15 (irq15)", /* 15 */ + "int 16 (bridge_tb)", /* 16 */ + "int 17 (gmac0)", /* 17 */ + "int 18 (gmac1)", /* 18 */ + "int 19 (gmac2)", /* 19 */ + "int 20 (gmac3)", /* 20 */ + "int 21 (irq21)", /* 21 */ + "int 22 (irq22)", /* 22 */ + "int 23 (irq23)", /* 23 */ + "int 24 (irq24)", /* 24 */ + "int 25 (bridge_err)", /* 25 */ + "int 26 (pcie_link0)", /* 26 */ + "int 27 (pcie_link1)", /* 27 */ + "int 28 (pcie_link2)", /* 28 */ + "int 29 (pcie_link3)", /* 29 */ + "int 30 (gpio_b)", /* 30 */ + "int 31 (usb)", /* 31 */ +}; + +/* + * rmixl_irqnames_xls4xx: + * - use for unknown cpu implementation + */ +static const char *rmixl_irqnames_generic[NIRQS] = { + "int 0", /* 0 */ + "int 1", /* 1 */ + "int 2", /* 2 */ + "int 3", /* 3 */ + "int 4", /* 4 */ + "int 5", /* 5 */ + "int 6", /* 6 */ + "int 7", /* 7 */ + "int 8", /* 8 */ + "int 9", /* 9 */ + "int 10", /* 10 */ + "int 11", /* 11 */ + "int 12", /* 12 */ + "int 13", /* 13 */ + "int 14", /* 14 */ + "int 15", /* 15 */ + "int 16", /* 16 */ + "int 17", /* 17 */ + "int 18", /* 18 */ + "int 19", /* 19 */ + "int 20", /* 20 */ + "int 21", /* 21 */ + "int 22", /* 22 */ + "int 23", /* 23 */ + "int 24", /* 24 */ + "int 25", /* 25 */ + "int 26", /* 26 */ + "int 27", /* 27 */ + "int 28", /* 28 */ + "int 29", /* 29 */ + "int 30", /* 30 */ + "int 31", /* 31 */ +}; + +/* * per-IRQ event stats */ struct rmixl_irqtab { @@ -198,21 +285,28 @@ */ struct rmixl_intrvec { LIST_HEAD(, evbmips_intrhand) iv_list; + uint32_t iv_ack; + rmixl_intr_trigger_t iv_trigger; + rmixl_intr_polarity_t iv_polarity; u_int iv_refcnt; }; static struct rmixl_intrvec rmixl_intrvec[NINTRVECS]; +#ifdef DIAGNOSTIC +static int evbmips_intr_init_done; +#endif -/* - * register byte order is BIG ENDIAN regardless of code model - */ -#define REG_DEREF(o) \ - *((volatile uint32_t *)MIPS_PHYS_TO_KSEG1( \ - rmixl_configuration.rc_io_pbase \ - + RMIXL_IO_DEV_PIC + (o))) +static inline void +pic_irt_print(const char *s, const int n, u_int irq) +{ +#ifdef IOINTR_DEBUG + uint32_t c0, c1; -#define REG_READ(o) be32toh(REG_DEREF(o)) -#define REG_WRITE(o,v) REG_DEREF(o) = htobe32(v) + c0 = RMIXL_PICREG_READ(RMIXL_PIC_IRTENTRYC0(irq)); + c1 = RMIXL_PICREG_READ(RMIXL_PIC_IRTENTRYC1(irq)); + printf("%s:%d: irq %d: c0 %#x, c1 %#x\n", s, n, irq, c0, c1); +#endif +} void evbmips_intr_init(void) @@ -220,23 +314,34 @@ uint32_t r; int i; +#ifdef DIAGNOSTIC + if (evbmips_intr_init_done != 0) + panic("%s: evbmips_intr_init_done %d", + __func__, evbmips_intr_init_done); +#endif + for (i=0; i < NIRQS; i++) { evcnt_attach_dynamic(&rmixl_irqtab[i].irq_count, - EVCNT_TYPE_INTR, NULL, "rmixl", rmixl_irqnames[i]); + EVCNT_TYPE_INTR, NULL, "rmixl", rmixl_intr_string(i)); rmixl_irqtab[i].irq_ih = NULL; } for (i=0; i < NINTRVECS; i++) { LIST_INIT(&rmixl_intrvec[i].iv_list); + rmixl_intrvec[i].iv_ack = 0; rmixl_intrvec[i].iv_refcnt = 0; } /* - * disable watchdog, watchdog NMI, timers - */ - r = REG_READ(RMIXL_PIC_CONTROL); - r &= RMIXL_PIC_CONTROL_RESV; - REG_WRITE(RMIXL_PIC_CONTROL, r); + * disable watchdog NMI, timers + * + * XXX + * WATCHDOG_ENB is preserved because clearing it causes + * hang on the XLS616 (but not on the XLS408) + */ + r = RMIXL_PICREG_READ(RMIXL_PIC_CONTROL); + r &= RMIXL_PIC_CONTROL_RESV|RMIXL_PIC_CONTROL_WATCHDOG_ENB; + RMIXL_PICREG_WRITE(RMIXL_PIC_CONTROL, r); /* * invalidate all IRT Entries @@ -244,18 +349,46 @@ * (assume we only have 1 thread) */ for (i=0; i < NIRQS; i++) { - - /* high word */ - r = REG_READ(RMIXL_PIC_IRTENTRYC1(i)); - r &= RMIXL_PIC_IRTENTRYC1_RESV; - REG_WRITE(RMIXL_PIC_IRTENTRYC1(i), r); - - /* low word */ - r = REG_READ(RMIXL_PIC_IRTENTRYC0(i)); - r &= RMIXL_PIC_IRTENTRYC0_RESV; - r |= 1; /* Thread Mask */ - REG_WRITE(RMIXL_PIC_IRTENTRYC0(i), r); + RMIXL_PICREG_WRITE(RMIXL_PIC_IRTENTRYC1(i), 0); /* high word */ + RMIXL_PICREG_WRITE(RMIXL_PIC_IRTENTRYC0(i), 1); /* low word */ } + +#ifdef DIAGNOSTIC + evbmips_intr_init_done = 1; +#endif +} + +const char * +rmixl_intr_string(int irq) +{ + const char *name; + + if (irq < 0 || irq >= NIRQS) + panic("%s: irq %d out of range, max %d", + __func__, irq, NIRQS - 1); + + switch (MIPS_PRID_IMPL(cpu_id)) { + case MIPS_XLS104: + case MIPS_XLS108: + case MIPS_XLS204: + case MIPS_XLS208: + case MIPS_XLS404LITE: + case MIPS_XLS408LITE: + name = rmixl_irqnames_xls1xx[irq]; + break; + case MIPS_XLS404: + case MIPS_XLS408: + case MIPS_XLS416: + case MIPS_XLS608: + case MIPS_XLS616: + name = rmixl_irqnames_xls4xx[irq]; + break; + default: + name = rmixl_irqnames_generic[irq]; + break; + } + + return name; } void * @@ -263,19 +396,25 @@ rmixl_intr_polarity_t polarity, int (*func)(void *), void *arg) { struct evbmips_intrhand *ih; + struct rmixl_intrvec *ivp; uint32_t irtc1; int vec; int s; +#ifdef DIAGNOSTIC + if (evbmips_intr_init_done == 0) + panic("%s: called before evbmips_intr_init", __func__); +#endif + /* * check args and assemble an IRT Entry */ if (irq < 0 || irq >= NIRQS) panic("%s: irq %d out of range, max %d", __func__, irq, NIRQS - 1); - if (ipl < 0 || ipl >= _IPL_N) - panic("%s: ipl %d out of range, max %d", - __func__, ipl, _IPL_N - 1); + if (ipl <= 0 || ipl >= _IPL_N) + panic("%s: ipl %d out of range, min %d, max %d", + __func__, ipl, 1, _IPL_N - 1); if (rmixl_irqtab[irq].irq_ih != NULL) panic("%s: irq %d busy", __func__, irq); @@ -308,10 +447,44 @@ * ipl determines which vector to use */ vec = rmixl_iplvec[ipl]; -printf("%s: ipl=%d, vec=%d\n", __func__, ipl, vec); + DPRINTF(("%s: irq %d, ipl %d, vec %d\n", __func__, irq, ipl, vec)); KASSERT((vec & ~RMIXL_PIC_IRTENTRYC1_INTVEC) == 0); irtc1 |= vec; + s = splhigh(); + + ivp = &rmixl_intrvec[vec]; + if (ivp->iv_refcnt == 0) { + ivp->iv_trigger = trigger; + ivp->iv_polarity = polarity; + } else { + if (ivp->iv_trigger != trigger) { +#ifdef DIAGNOSTIC + printf("%s: vec %d, irqs {", __func__, vec); + LIST_FOREACH(ih, &ivp->iv_list, ih_q) { + printf(" %d", ih->ih_irq); + } + printf(" } trigger type %d; irq %d wants type %d\n", + ivp->iv_trigger, irq, trigger); +#endif + panic("%s: trigger mismatch at vec %d\n", + __func__, vec); + } + if (ivp->iv_polarity != polarity) { +#ifdef DIAGNOSTIC + printf("%s: vec %d, irqs {", __func__, vec); + LIST_FOREACH(ih, &ivp->iv_list, ih_q) { + printf(" %d", ih->ih_irq); + } + printf(" } polarity type %d; irq %d wants type %d\n", + ivp->iv_polarity, irq, polarity); +#endif + panic("%s: polarity mismatch at vec %d\n", + __func__, vec); + } + } + ivp->iv_ack |= (1 << irq); + /* * allocate and initialize an interrupt handle */ @@ -324,8 +497,6 @@ ih->ih_irq = irq; ih->ih_ipl = ipl; - s = splhigh(); - /* * mark this irq as established, busy */ @@ -334,13 +505,14 @@ /* * link this ih into the tables and bump reference count */ - LIST_INSERT_HEAD(&rmixl_intrvec[vec].iv_list, ih, ih_q); - rmixl_intrvec[vec].iv_refcnt++; + LIST_INSERT_HEAD(&ivp->iv_list, ih, ih_q); + ivp->iv_refcnt++; /* - * establish IRT Entry (low word only) + * establish IRT Entry (high word only) */ - REG_WRITE(RMIXL_PIC_IRTENTRYC1(irq), irtc1); + DPRINTF(("%s: irq %d, irtc1 %#x\n", __func__, irq, irtc1)); + RMIXL_PICREG_WRITE(RMIXL_PIC_IRTENTRYC1(irq), irtc1); splx(s); @@ -351,28 +523,28 @@ rmixl_intr_disestablish(void *cookie) { struct evbmips_intrhand *ih = cookie; - uint32_t r; + struct rmixl_intrvec *ivp; int irq; int vec; int s; irq = ih->ih_irq; vec = rmixl_iplvec[ih->ih_ipl]; + ivp = &rmixl_intrvec[vec]; s = splhigh(); /* - * remove from the table and adjust the reference count + * disable the IRT Entry (high word only) */ - LIST_REMOVE(ih, ih_q); - rmixl_intrvec[vec].iv_refcnt--; + RMIXL_PICREG_WRITE(RMIXL_PIC_IRTENTRYC1(irq), 0); /* - * disable the IRT Entry (low word only) + * remove from the table and adjust the reference count */ - r = REG_READ(RMIXL_PIC_IRTENTRYC1(irq)); - r &= RMIXL_PIC_IRTENTRYC1_RESV; - REG_WRITE(RMIXL_PIC_IRTENTRYC1(irq), r); + LIST_REMOVE(ih, ih_q); + ivp->iv_refcnt--; + ivp->iv_ack &= ~(1 << irq); /* * this irq now disestablished, not busy @@ -384,28 +556,87 @@ free(ih, M_DEVBUF); } +static inline void +pci_int_status(const char *s, const int n) +{ +#ifdef IOINTR_DEBUG + uint32_t r; + r = RMIXL_IOREG_READ(RMIXL_IO_DEV_PCIE_LE + 0xa0); + printf("%s:%d: PCIE_LINK0_INT_STATUS0 %#x\n", s, n, r); +#endif +} + void evbmips_iointr(uint32_t status, uint32_t cause, uint32_t pc, uint32_t ipending) { struct evbmips_intrhand *ih; - uint64_t eirr; + struct rmixl_intrvec *ivp; int vec; + uint64_t eirr; +#ifdef IOINTR_DEBUG + uint64_t eimr; + + printf("%s: status %#x, cause %#x, pc %#x, ipending %#x\n", + __func__, status, cause, pc, ipending); - for (vec = NINTRVECS - 1; vec >= 0; vec--) { + asm volatile("dmfc0 %0, $9, 6;" : "=r"(eirr)); + asm volatile("dmfc0 %0, $9, 7;" : "=r"(eimr)); + printf("%s:%d: eirr %#lx, eimr %#lx\n", __func__, __LINE__, eirr, eimr); + pci_int_status(__func__, __LINE__); +#endif + + for (vec = NINTRVECS - 1; vec >= 2; vec--) { if ((ipending & (MIPS_SOFT_INT_MASK_0 << vec)) == 0) continue; - /* ack this vec in the EIRR */ - eirr = (1 << vec); - asm volatile ("dmtc0 %0, $9, 6;" :: "r"(eirr)); + ivp = &rmixl_intrvec[vec]; + + eirr = 1ULL << vec; + asm volatile("dmtc0 %0, $9, 6;" :: "r"(eirr)); - LIST_FOREACH(ih, &rmixl_intrvec[vec].iv_list, ih_q) { - if ((*ih->ih_func)(ih->ih_arg) != 0) +#ifdef IOINTR_DEBUG + printf("%s: interrupt at vec %d\n", + __func__, vec); + if (LIST_EMPTY(&ivp->iv_list)) + printf("%s: unexpected interrupt at vec %d\n", + __func__, vec); +#endif + LIST_FOREACH(ih, &ivp->iv_list, ih_q) { + pic_irt_print(__func__, __LINE__, ih->ih_irq); + RMIXL_PICREG_WRITE(RMIXL_PIC_INTRACK, + (1 << ih->ih_irq)); + if ((*ih->ih_func)(ih->ih_arg) != 0) { rmixl_irqtab[ih->ih_irq].irq_count.ev_count++; + } } + + pci_int_status(__func__, __LINE__); + cause &= ~(MIPS_SOFT_INT_MASK_0 << vec); } + /* Re-enable anything that we have processed. */ _splset(MIPS_SR_INT_IE | ((status & ~cause) & MIPS_HARD_INT_MASK)); } + +#ifdef DEBUG +int rmixl_intrvec_print(void); +int +rmixl_intrvec_print(void) +{ + struct evbmips_intrhand *ih; + struct rmixl_intrvec *ivp; + int vec; + + ivp = &rmixl_intrvec[0]; + for (vec=0; vec < NINTRVECS ; vec++) { + printf("vec %d, irqs {", vec); + LIST_FOREACH(ih, &ivp->iv_list, ih_q) + printf(" %d", ih->ih_irq); + printf(" } trigger type %d\n", ivp->iv_trigger); + ivp++; + } + return 0; +} +#endif