Module Name: src
Committed By: cliff
Date: Fri Sep 25 22:22:09 UTC 2009
Modified Files:
src/sys/arch/mips/rmi [matt-nb5-mips64]: rmixl_intr.c
Log Message:
rmixl gets interrupt support
To generate a diff of this commit:
cvs rdiff -u -r1.1.2.1 -r1.1.2.2 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.1 src/sys/arch/mips/rmi/rmixl_intr.c:1.1.2.2
--- src/sys/arch/mips/rmi/rmixl_intr.c:1.1.2.1 Sun Sep 13 03:27:38 2009
+++ src/sys/arch/mips/rmi/rmixl_intr.c Fri Sep 25 22:22:09 2009
@@ -1,4 +1,4 @@
-/* $NetBSD: rmixl_intr.c,v 1.1.2.1 2009/09/13 03:27:38 cliff Exp $ */
+/* $NetBSD: rmixl_intr.c,v 1.1.2.2 2009/09/25 22:22:09 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.1 2009/09/13 03:27:38 cliff Exp $");
+__KERNEL_RCSID(0, "$NetBSD: rmixl_intr.c,v 1.1.2.2 2009/09/25 22:22:09 cliff Exp $");
#include "opt_ddb.h"
@@ -90,118 +90,231 @@
* given hardware interrupt priority level.
*/
const uint32_t ipl_sr_bits[_IPL_N] = {
- 0, /* 0: IPL_NONE */
- MIPS_SOFT_INT_MASK_0, /* 1: IPL_SOFTCLOCK */
- MIPS_SOFT_INT_MASK_0, /* 2: IPL_SOFTNET */
-
- MIPS_SOFT_INT_MASK_0|
- MIPS_SOFT_INT_MASK_1|
- MIPS_INT_MASK_0, /* 3: IPL_VM */
-
- 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, /* 4: IPL_{SCHED,HIGH} */
+ [IPL_NONE] = 0,
+ [IPL_SOFTCLOCK] =
+ MIPS_SOFT_INT_MASK_0,
+ [IPL_SOFTNET] =
+ MIPS_SOFT_INT_MASK_0
+ | MIPS_SOFT_INT_MASK_1,
+ [IPL_VM] =
+ MIPS_SOFT_INT_MASK_0
+ | 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,
};
-#define NIRQS 32
-const char *rmixl_intrnames[NIRQS] = {
- "irq0", /* 3 */
- "irq1", /* 1 */
- "irq2", /* 2 */
- "irq3", /* 3 */
- "irq4", /* 4 */
- "irq5", /* 5 */
- "irq6", /* 6 */
- "irq7", /* 7 */
- "irq8", /* 8 */
- "irq9", /* 9 */
- "irq10", /* 10 */
- "irq11", /* 11 */
- "irq12", /* 12 */
- "irq13", /* 13 */
- "irq14", /* 14 */
- "irq15", /* 15 */
- "irq16", /* 16 */
- "irq17", /* 17 */
- "irq18", /* 18 */
- "irq19", /* 19 */
- "irq20", /* 20 */
- "irq21", /* 21 */
- "irq22", /* 22 */
- "irq23", /* 23 */
- "irq24", /* 24 */
- "irq25", /* 25 */
- "irq26", /* 26 */
- "irq27", /* 27 */
- "irq28", /* 28 */
- "irq29", /* 29 */
- "irq30", /* 30 */
- "irq31", /* 31 */
+/*
+ * 'IRQs' here are indiividual interrupt sources
+ * each has a slot in the Interrupt Redirection Table (IRT)
+ * in the order listed
+ *
+ * NOTE: many irq sources depend on the chip family
+ * XLS1xx vs. XLS2xx vs. XLS3xx vs. XLS6xx
+ * so just use generic names where they diverge
+ */
+#define NIRQS 32
+static const char *rmixl_irqnames[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 (irq28)", /* 28 */
+ "int 29 (irq29)", /* 29 */
+ "int 30 (gpio_b)", /* 30 */
+ "int 31 (usb)", /* 31 */
};
-struct rmixl_intrhead {
- struct evcnt intr_count;
- int intr_refcnt;
+/*
+ * per-IRQ event stats
+ */
+struct rmixl_irqtab {
+ struct evcnt irq_count;
+ void *irq_ih;
};
-struct rmixl_intrhead rmixl_intrtab[NIRQS];
+static struct rmixl_irqtab rmixl_irqtab[NIRQS];
-#define NINTRS 2 /* MIPS INT0 - INT1 */
-struct rmixl_cpuintr {
- LIST_HEAD(, evbmips_intrhand) cintr_list;
- struct evcnt cintr_count;
+/*
+ * 'vectors' here correspond to IRT Entry vector numbers
+ * - IRT Entry vector# is bit# in EIRR
+ * - note that EIRR[7:0] == CAUSE[15:8]
+ * - we actually only use the first _IPL_N bits
+ * (less than 8)
+ *
+ * each IRT entry gets routed to a vector
+ * (if and when that interrupt is established)
+ * the vectors are shared on a per-IPL basis
+ * which simplifies dispatch
+ *
+ * XXX use of mips64 extended IRQs is TBD
+ */
+#define NINTRVECS _IPL_N
+
+/*
+ * translate IPL to vector number
+ */
+static const int rmixl_iplvec[_IPL_N] = {
+ [IPL_NONE] = -1, /* XXX */
+ [IPL_SOFTCLOCK] = 0,
+ [IPL_SOFTNET] = 1,
+ [IPL_VM] = 2,
+ [IPL_SCHED] = 3,
};
-struct rmixl_cpuintr rmixl_cpuintrs[NINTRS];
-const char *rmixl_cpuintrnames[NINTRS] = {
- "int 0 (irq)",
- "int 1 (fiq)",
+/*
+ * list and ref count manage sharing of each vector
+ */
+struct rmixl_intrvec {
+ LIST_HEAD(, evbmips_intrhand) iv_list;
+ u_int iv_refcnt;
};
+static struct rmixl_intrvec rmixl_intrvec[NINTRVECS];
+
+
+/*
+ * 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)))
-#define REG_READ(o) *((volatile uint32_t *)MIPS_PHYS_TO_KSEG1(ADM5120_BASE_ICU + (o)))
-#define REG_WRITE(o,v) (REG_READ(o)) = (v)
+#define REG_READ(o) be32toh(REG_DEREF(o))
+#define REG_WRITE(o,v) REG_DEREF(o) = htobe32(v)
void
evbmips_intr_init(void)
{
-#ifdef NOTYET
+ uint32_t r;
int i;
- for (i = 0; i < NINTRS; i++) {
- LIST_INIT(&rmixl_cpuintrs[i].cintr_list);
- evcnt_attach_dynamic(&rmixl_cpuintrs[i].cintr_count,
- EVCNT_TYPE_INTR, NULL, "mips", rmixl_cpuintrnames[i]);
+ for (i=0; i < NIRQS; i++) {
+ evcnt_attach_dynamic(&rmixl_irqtab[i].irq_count,
+ EVCNT_TYPE_INTR, NULL, "rmixl", rmixl_irqnames[i]);
+ rmixl_irqtab[i].irq_ih = NULL;
}
- for (i = 0; i < NIRQS; i++) {
- /* XXX steering - use an irqmap array? */
-
- rmixl_intrtab[i].intr_refcnt = 0;
- evcnt_attach_dynamic(&rmixl_intrtab[i].intr_count,
- EVCNT_TYPE_INTR, NULL, "rmixl", rmixl_intrnames[i]);
+ for (i=0; i < NINTRVECS; i++) {
+ LIST_INIT(&rmixl_intrvec[i].iv_list);
+ rmixl_intrvec[i].iv_refcnt = 0;
}
- /* disable all interrupts */
- REG_WRITE(ICU_DISABLE_REG, ICU_INT_MASK);
-#endif
+ /*
+ * disable watchdog, watchdog NMI, timers
+ */
+ r = REG_READ(RMIXL_PIC_CONTROL);
+ r &= RMIXL_PIC_CONTROL_RESV;
+ REG_WRITE(RMIXL_PIC_CONTROL, r);
+
+ /*
+ * invalidate all IRT Entries
+ * permanently unmask Thread#0 in low word
+ * (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);
+ }
}
void *
-rmixl_intr_establish(int irq, int priority, int (*func)(void *), void *arg)
+rmixl_intr_establish(int irq, int ipl, rmixl_intr_trigger_t trigger,
+ rmixl_intr_polarity_t polarity, int (*func)(void *), void *arg)
{
-#ifdef NOTYET
struct evbmips_intrhand *ih;
- uint32_t irqmask;
- int cpu_int, s;
+ uint32_t irtc1;
+ int vec;
+ int s;
+ /*
+ * check args and assemble an IRT Entry
+ */
if (irq < 0 || irq >= NIRQS)
- panic("rmixl_intr_establish: bogus IRQ %d", irq);
+ 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 (rmixl_irqtab[irq].irq_ih != NULL)
+ panic("%s: irq %d busy", __func__, irq);
+
+ irtc1 = RMIXL_PIC_IRTENTRYC1_VALID;
+ irtc1 |= RMIXL_PIC_IRTENTRYC1_GL; /* local */
+
+ switch (trigger) {
+ case RMIXL_INTR_EDGE:
+ break;
+ case RMIXL_INTR_LEVEL:
+ irtc1 |= RMIXL_PIC_IRTENTRYC1_TRG;
+ break;
+ default:
+ panic("%s: bad trigger %d\n", __func__, trigger);
+ }
+ switch (polarity) {
+ case RMIXL_INTR_RISING:
+ case RMIXL_INTR_HIGH:
+ break;
+ case RMIXL_INTR_FALLING:
+ case RMIXL_INTR_LOW:
+ irtc1 |= RMIXL_PIC_IRTENTRYC1_P;
+ break;
+ default:
+ panic("%s: bad polarity %d\n", __func__, polarity);
+ }
+
+ /*
+ * ipl determines which vector to use
+ */
+ vec = rmixl_iplvec[ipl];
+printf("%s: ipl=%d, vec=%d\n", __func__, ipl, vec);
+ KASSERT((vec & ~RMIXL_PIC_IRTENTRYC1_INTVEC) == 0);
+ irtc1 |= vec;
+
+ /*
+ * allocate and initialize an interrupt handle
+ */
ih = malloc(sizeof(*ih), M_DEVBUF, M_NOWAIT);
if (ih == NULL)
return NULL;
@@ -209,107 +322,109 @@
ih->ih_func = func;
ih->ih_arg = arg;
ih->ih_irq = irq;
+ ih->ih_ipl = ipl;
s = splhigh();
/*
- * First, link it into the tables.
- * XXX do we want a separate list (really, should only be one item, not
- * a list anyway) per irq, not per CPU interrupt?
+ * mark this irq as established, busy
*/
+ rmixl_irqtab[irq].irq_ih = ih;
- cpu_int = (priority == INTR_FIQ) ? 1 : 0;
-
- LIST_INSERT_HEAD(&rmixl_cpuintrs[cpu_int].cintr_list, ih, ih_q);
+ /*
+ * 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++;
/*
- * Now enable it.
+ * establish IRT Entry (low word only)
*/
- if (rmixl_intrtab[irq].intr_refcnt++ == 0) {
- irqmask = 1 << irq;
+ REG_WRITE(RMIXL_PIC_IRTENTRYC1(irq), irtc1);
- /* configure as IRQ or FIQ */
- if (priority == INTR_FIQ) {
- REG_WRITE(ICU_MODE_REG,
- REG_READ(ICU_MODE_REG) | irqmask);
- } else {
- REG_WRITE(ICU_MODE_REG,
- REG_READ(ICU_MODE_REG) & ~irqmask);
- }
- /* enable */
- REG_WRITE(ICU_ENABLE_REG, irqmask);
- }
splx(s);
return ih;
-#else
- return NULL;
-#endif
}
void
rmixl_intr_disestablish(void *cookie)
{
-#ifdef NOTYET
struct evbmips_intrhand *ih = cookie;
- int irq, s;
- uint32_t irqmask;
+ uint32_t r;
+ int irq;
+ int vec;
+ int s;
irq = ih->ih_irq;
+ vec = rmixl_iplvec[ih->ih_ipl];
s = splhigh();
/*
- * First, remove it from the table.
+ * remove from the table and adjust the reference count
*/
LIST_REMOVE(ih, ih_q);
+ rmixl_intrvec[vec].iv_refcnt--;
/*
- * Now, disable it, if there is nothing remaining on the
- * list.
+ * disable the IRT Entry (low word only)
*/
- if (rmixl_intrtab[irq].intr_refcnt-- == 1) {
- irqmask = 1 << irq; /* only used as a mask from here on */
+ r = REG_READ(RMIXL_PIC_IRTENTRYC1(irq));
+ r &= RMIXL_PIC_IRTENTRYC1_RESV;
+ REG_WRITE(RMIXL_PIC_IRTENTRYC1(irq), r);
- /* disable this irq in HW */
- REG_WRITE(ICU_DISABLE_REG, irqmask);
- }
+ /*
+ * this irq now disestablished, not busy
+ */
+ rmixl_irqtab[irq].irq_ih = NULL;
splx(s);
free(ih, M_DEVBUF);
-#endif
}
void
evbmips_iointr(uint32_t status, uint32_t cause, uint32_t pc, uint32_t ipending)
{
-#ifdef NOTYET
struct evbmips_intrhand *ih;
- int level;
- uint32_t irqmask, irqstat;
+ uint64_t eirr;
+ uint64_t eimr;
+ uint32_t sr;
+ int vec;
+
+ printf("\n%s: status: %#x, cause %#x\n", __func__, status, cause);
+ asm volatile ("mfc0 %0, $9, 6;" :"=r"(sr));
+ printf("%s:%d: SR: %#x\n", __func__, __LINE__, sr);
+ asm volatile ("dmfc0 %0, $9, 7;" :"=r"(eimr));
+ printf("%s: EIMR: %#lx\n", __func__, eimr);
- for (level = NINTRS - 1; level >= 0; level--) {
- if ((ipending & (MIPS_INT_MASK_0 << level)) == 0)
+ for (vec = NINTRVECS - 1; vec >= 0; vec--) {
+ if ((ipending & (MIPS_SOFT_INT_MASK_0 << vec)) == 0)
continue;
- if (level)
- irqstat = REG_READ(ICU_FIQ_STATUS_REG);
- else
- irqstat = REG_READ(ICU_STATUS_REG);
-
- rmixl_cpuintrs[level].cintr_count.ev_count++;
- LIST_FOREACH(ih, &rmixl_cpuintrs[level].cintr_list, ih_q) {
- irqmask = 1 << ih->ih_irq;
- if (irqmask & irqstat) {
- rmixl_intrtab[ih->ih_irq].intr_count.ev_count++;
- (*ih->ih_func)(ih->ih_arg);
- }
+ /* ack this vec in the EIRR */
+ eirr = (1 << 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)
+ rmixl_irqtab[ih->ih_irq].irq_count.ev_count++;
}
- cause &= ~(MIPS_INT_MASK_0 << level);
+ cause &= ~(MIPS_SOFT_INT_MASK_0 << vec);
}
/* Re-enable anything that we have processed. */
+ printf("%s:%d: re-enable: %#x\n", __func__, __LINE__,
+ MIPS_SR_INT_IE | ((status & ~cause) & MIPS_HARD_INT_MASK));
_splset(MIPS_SR_INT_IE | ((status & ~cause) & MIPS_HARD_INT_MASK));
-#endif
+
+ asm volatile ("mfc0 %0, $9, 6;" :"=r"(sr));
+ printf("%s: SR: %#x\n", __func__, sr);
+
+ asm volatile ("dmfc0 %0, $9, 6;" :"=r"(eirr));
+ printf("%s: EIRR: %#lx\n", __func__, eirr);
+
+ asm volatile ("dmfc0 %0, $9, 7;" :"=r"(eimr));
+ printf("%s: EIMR: %#lx\n", __func__, eimr);
}