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