diff options
author | Dale Rahn <drahn@cvs.openbsd.org> | 2008-09-18 03:56:26 +0000 |
---|---|---|
committer | Dale Rahn <drahn@cvs.openbsd.org> | 2008-09-18 03:56:26 +0000 |
commit | ac9178859bad2dfb8a089ace0ca2594821702c01 (patch) | |
tree | 992557fe2a1c5c09d102dd4bca38f8ecffb6c1d8 /sys | |
parent | 856abc40f5e309364d84e9e75693686f63551c07 (diff) |
Redesign of the powerpc interrupt architecture, use true levels intead of
blocking specific interrupts. Needs signficant testing to prove that
one remaining elusive bug has been squashed.
Diffstat (limited to 'sys')
-rw-r--r-- | sys/arch/macppc/dev/macintr.c | 550 | ||||
-rw-r--r-- | sys/arch/macppc/dev/openpic.c | 715 | ||||
-rw-r--r-- | sys/arch/macppc/include/intr.h | 4 | ||||
-rw-r--r-- | sys/arch/macppc/macppc/clock.c | 4 | ||||
-rw-r--r-- | sys/arch/macppc/macppc/cpu.c | 4 | ||||
-rw-r--r-- | sys/arch/macppc/macppc/genassym.cf | 3 | ||||
-rw-r--r-- | sys/arch/macppc/macppc/machdep.c | 4 | ||||
-rw-r--r-- | sys/arch/powerpc/include/intr.h | 123 | ||||
-rw-r--r-- | sys/arch/powerpc/powerpc/intr.c | 104 | ||||
-rw-r--r-- | sys/arch/powerpc/powerpc/mutex.S | 46 |
10 files changed, 755 insertions, 802 deletions
diff --git a/sys/arch/macppc/dev/macintr.c b/sys/arch/macppc/dev/macintr.c index c0ca3e684f4..e327b99c2c1 100644 --- a/sys/arch/macppc/dev/macintr.c +++ b/sys/arch/macppc/dev/macintr.c @@ -1,6 +1,7 @@ -/* $OpenBSD: macintr.c,v 1.33 2007/05/29 18:10:42 miod Exp $ */ +/* $OpenBSD: macintr.c,v 1.34 2008/09/18 03:56:25 drahn Exp $ */ /*- + * Copyright (c) 2008 Dale Rahn <drahn@openbsd.org> * Copyright (c) 1995 Per Fogelstrom * Copyright (c) 1993, 1994 Charles M. Hannum. * Copyright (c) 1990 The Regents of the University of California. @@ -58,25 +59,17 @@ #define ICU_LEN 64 #define LEGAL_IRQ(x) ((x >= 0) && (x < ICU_LEN)) -int m_intrtype[ICU_LEN], m_intrmask[ICU_LEN], m_intrlevel[ICU_LEN]; -struct intrhand *m_intrhand[ICU_LEN]; -int m_hwirq[ICU_LEN], m_virq[64]; -unsigned int imen_m = 0xffffffff; -int m_virq_max = 0; - -static int fakeintr(void *); -static char *intr_typename(int type); -static void intr_calculatemasks(void); -static void enable_irq(int x); -static __inline int cntlzw(int x); -static int mapirq(int irq); -static int read_irq(void); -static void mac_intr_do_pending_int(void); +int macintr_ienable_l[IPL_NUM], macintr_ienable_h[IPL_NUM]; +int macintr_pri_share[IPL_NUM]; -extern u_int32_t *heathrow_FCR; +struct intrq macintr_handler[ICU_LEN]; + +void macintr_calc_mask(void); +void macintr_eoi(int irq); +int macintr_read_irq(void); +static void macintr_do_pending_int(void); -#define HWIRQ_MAX 27 -#define HWIRQ_MASK 0x0fffffff +extern u_int32_t *heathrow_FCR; #define INT_STATE_REG0 (interrupt_reg + 0x20) #define INT_ENABLE_REG0 (interrupt_reg + 0x24) @@ -95,6 +88,8 @@ int macintr_match(struct device *parent, void *cf, void *aux); void macintr_attach(struct device *, struct device *, void *); void mac_do_pending_int(void); void mac_ext_intr(void); +void macintr_collect_preconf_intr(void); +void macintr_setipl(int ipl); struct cfattach macintr_ca = { sizeof(struct macintr_softc), @@ -140,31 +135,84 @@ intr_establish_t macintr_establish; intr_disestablish_t macintr_disestablish; extern intr_establish_t *mac_intr_establish_func; extern intr_disestablish_t *mac_intr_disestablish_func; -void macintr_collect_preconf_intr(void); + +ppc_splraise_t macintr_splraise; +ppc_spllower_t macintr_spllower; +ppc_splx_t macintr_splx; + + +int +macintr_splraise(int newcpl) +{ + struct cpu_info *ci = curcpu(); + newcpl = macintr_pri_share[newcpl]; + int ocpl = ci->ci_cpl; + if (ocpl > newcpl) + newcpl = ocpl; + + macintr_setipl(newcpl); + + return ocpl; +} + +int +macintr_spllower(int newcpl) +{ + struct cpu_info *ci = curcpu(); + int ocpl = ci->ci_cpl; + + macintr_splx(newcpl); + + return ocpl; +} + +void +macintr_splx(int newcpl) +{ + struct cpu_info *ci = curcpu(); + + macintr_setipl(newcpl); + if (ci->ci_ipending & ppc_smask[newcpl]) + macintr_do_pending_int(); +} void macintr_attach(struct device *parent, struct device *self, void *aux) { + struct cpu_info *ci = curcpu(); struct confargs *ca = aux; extern intr_establish_t *intr_establish_func; extern intr_disestablish_t *intr_disestablish_func; + struct intrq *iq; + int i; interrupt_reg = (void *)mapiodev(ca->ca_baseaddr,0x100); /* XXX */ + for (i = 0; i < ICU_LEN; i++) { + iq = &macintr_handler[i]; + TAILQ_INIT(&iq->iq_list); + } + ppc_smask_init(); + install_extint(mac_ext_intr); - pending_int_f = mac_intr_do_pending_int; + pending_int_f = macintr_do_pending_int; intr_establish_func = macintr_establish; intr_disestablish_func = macintr_disestablish; mac_intr_establish_func = macintr_establish; mac_intr_disestablish_func = macintr_disestablish; + ppc_intr_func.raise = macintr_splraise; + ppc_intr_func.lower = macintr_spllower; + ppc_intr_func.x = macintr_splx; + + ci->ci_iactive = 0; + macintr_collect_preconf_intr(); mac_intr_establish(parent, 0x14, IST_LEVEL, IPL_HIGH, macintr_prog_button, (void *)0x14, "progbutton"); ppc_intr_enable(1); - printf("\n"); } @@ -209,11 +257,19 @@ macintr_prog_button (void *arg) return 1; } -static int -fakeintr(void *arg) +void +macintr_setipl(int ipl) { + struct cpu_info *ci = curcpu(); + int s; + s = ppc_intr_disable(); + ci->ci_cpl = ipl; + if (heathrow_FCR) + out32rb(INT_ENABLE_REG1, + macintr_ienable_h[macintr_pri_share[ipl]]); - return 0; + out32rb(INT_ENABLE_REG0, macintr_ienable_l[macintr_pri_share[ipl]]); + ppc_intr_enable(s); } /* @@ -223,19 +279,13 @@ void * macintr_establish(void * lcv, int irq, int type, int level, int (*ih_fun)(void *), void *ih_arg, char *name) { - struct intrhand **p, *q, *ih; - static struct intrhand fakehand; - - fakehand.ih_next = NULL; - fakehand.ih_fun = fakeintr; + struct cpu_info *ci = curcpu(); + struct intrq *iq; + struct intrhand *ih; + int s; #if 0 -printf("macintr_establish, hI %d L %d ", irq, type); -printf("addr reg0 %x\n", INT_STATE_REG0); -#endif - irq = mapirq(irq); -#if 0 -printf("vI %d ", irq); +printf("macintr_establish, hI %d L %d %s", irq, level, ppc_intr_typename(type)); #endif /* no point in sleeping unless someone can free memory. */ @@ -246,51 +296,40 @@ printf("vI %d ", irq); if (!LEGAL_IRQ(irq) || type == IST_NONE) panic("intr_establish: bogus irq or type"); - switch (m_intrtype[irq]) { + iq = &macintr_handler[irq]; + switch (iq->iq_ist) { case IST_NONE: - m_intrtype[irq] = type; + iq->iq_ist = type; break; case IST_EDGE: case IST_LEVEL: - if (type == m_intrtype[irq]) + if (type == iq->iq_ist) break; case IST_PULSE: if (type != IST_NONE) panic("intr_establish: can't share %s with %s", - intr_typename(m_intrtype[irq]), - intr_typename(type)); + ppc_intr_typename(iq->iq_ist), + ppc_intr_typename(type)); break; } - /* - * Figure out where to put the handler. - * This is O(N^2), but we want to preserve the order, and N is - * generally small. - */ - for (p = &m_intrhand[irq]; (q = *p) != NULL; p = &q->ih_next) - ; - - /* - * Actually install a fake handler momentarily, since we might be doing - * this with interrupts enabled and DON'T WANt the real routine called - * until masking is set up. - */ - fakehand.ih_level = level; - *p = &fakehand; - - intr_calculatemasks(); - - /* - * Poke the real handler in now. - */ ih->ih_fun = ih_fun; ih->ih_arg = ih_arg; - ih->ih_next = NULL; ih->ih_level = level; ih->ih_irq = irq; - evcount_attach(&ih->ih_count, name, (void *)&m_hwirq[irq], + evcount_attach(&ih->ih_count, name, (void *)&ih->ih_irq, &evcount_intr); - *p = ih; + + /* + * Append handler to end of list + */ + s = ppc_intr_disable(); + + TAILQ_INSERT_TAIL(&iq->iq_list, ih, ih_list); + macintr_calc_mask(); + + macintr_setipl(ci->ci_cpl); + ppc_intr_enable(s); return (ih); } @@ -301,195 +340,93 @@ printf("vI %d ", irq); void macintr_disestablish(void *lcp, void *arg) { + struct cpu_info *ci = curcpu(); struct intrhand *ih = arg; int irq = ih->ih_irq; - struct intrhand **p, *q; + int s; + struct intrq *iq; if (!LEGAL_IRQ(irq)) panic("intr_disestablish: bogus irq"); /* * Remove the handler from the chain. - * This is O(n^2), too. */ - for (p = &m_intrhand[irq]; (q = *p) != NULL && q != ih; p = &q->ih_next) - ; - if (q) - *p = q->ih_next; - else - panic("intr_disestablish: handler not registered"); - evcount_detach(&ih->ih_count); - free((void *)ih, M_DEVBUF); + iq = &macintr_handler[irq]; + s = ppc_intr_disable(); - intr_calculatemasks(); + TAILQ_REMOVE(&iq->iq_list, ih, ih_list); + macintr_calc_mask(); - if (m_intrhand[irq] == NULL) - m_intrtype[irq] = IST_NONE; -} + macintr_setipl(ci->ci_cpl); + ppc_intr_enable(s); + evcount_detach(&ih->ih_count); + free((void *)ih, M_DEVBUF); -static char * -intr_typename(int type) -{ - switch (type) { - case IST_NONE : - return ("none"); - case IST_PULSE: - return ("pulsed"); - case IST_EDGE: - return ("edge-triggered"); - case IST_LEVEL: - return ("level-triggered"); - default: - panic("intr_typename: invalid type %d", type); -#if 1 /* XXX */ - return ("unknown"); -#endif - } + if (TAILQ_EMPTY(&iq->iq_list)) + iq->iq_ist = IST_NONE; } + /* * Recalculate the interrupt masks from scratch. * We could code special registry and deregistry versions of this function that * would be faster, but the code would be nastier, and we don't expect this to * happen very much anyway. */ -static void -intr_calculatemasks() +void +macintr_calc_mask() { - int irq, level; - struct intrhand *q; - - /* First, figure out which levels each IRQ uses. */ - for (irq = 0; irq < ICU_LEN; irq++) { - register int levels = 0; - for (q = m_intrhand[irq]; q; q = q->ih_next) - levels |= 1 << q->ih_level; - m_intrlevel[irq] = levels; - } + int irq; + struct intrhand *ih; + int i; - /* Then figure out which IRQs use each level. */ - for (level = IPL_NONE; level < IPL_NUM; level++) { - register int irqs = 0; - for (irq = 0; irq < ICU_LEN; irq++) - if (m_intrlevel[irq] & (1 << level)) - irqs |= 1 << irq; - imask[level] = irqs | SINT_MASK; + for (i = IPL_NONE; i < IPL_NUM; i++) { + macintr_pri_share[i] = i; } - /* - * There are tty, network and disk drivers that use free() at interrupt - * time, so vm > (tty | net | bio). - * - * Enforce a hierarchy that gives slow devices a better chance at not - * dropping data. - */ - imask[IPL_NET] |= imask[IPL_BIO]; - imask[IPL_TTY] |= imask[IPL_NET]; - imask[IPL_VM] |= imask[IPL_TTY]; - imask[IPL_CLOCK] |= imask[IPL_VM] | SPL_CLOCK; - - /* - * These are pseudo-levels. - */ - imask[IPL_NONE] = 0x00000000; - imask[IPL_HIGH] = 0xffffffff; - - /* And eventually calculate the complete masks. */ for (irq = 0; irq < ICU_LEN; irq++) { - register int irqs = 1 << irq; - for (q = m_intrhand[irq]; q; q = q->ih_next) - irqs |= imask[q->ih_level]; - m_intrmask[irq] = irqs | SINT_MASK; - } - - /* Lastly, determine which IRQs are actually in use. */ - { - register int irqs = 0; - for (irq = 0; irq < ICU_LEN; irq++) { - if (m_intrhand[irq]) - irqs |= 1 << irq; + int maxipl = IPL_NONE; + int minipl = IPL_HIGH; + struct intrq *iq = &macintr_handler[irq]; + + TAILQ_FOREACH(ih, &iq->iq_list, ih_list) { + if (ih->ih_level > maxipl) + maxipl = ih->ih_level; + if (ih->ih_level < minipl) + minipl = ih->ih_level; } - imen_m = ~irqs; - enable_irq(~imen_m); - } -} -static void -enable_irq(int x) -{ - int state0, state1, v; - int irq; - - x &= HWIRQ_MASK; /* XXX Higher bits are software interrupts. */ - - state0 = state1 = 0; - while (x) { - v = 31 - cntlzw(x); - irq = m_hwirq[v]; - if (irq < 32) - state0 |= 1 << irq; - else - state1 |= 1 << (irq - 32); - - x &= ~(1 << v); - } - - if (heathrow_FCR) - out32rb(INT_ENABLE_REG1, state1); - - out32rb(INT_ENABLE_REG0, state0); -} -int m_virq_inited = 0; + iq->iq_ipl = maxipl; -/* - * Map 64 irqs into 32 (bits). - */ -static int -mapirq(int irq) -{ - int v; - int i; + if (maxipl == IPL_NONE) { + minipl = IPL_NONE; /* Interrupt not enabled */ + } else { + for (i = minipl; i <= maxipl; i++) + macintr_pri_share[i] = i; + } - if (m_virq_inited == 0) { - m_virq_max = 0; - for (i = 0; i < ICU_LEN; i++) { - m_virq[i] = 0; + /* Enable interrupts at lower levels */ + + if (irq < 32) { + for (i = IPL_NONE; i < minipl; i++) + macintr_ienable_l[i] |= (1 << irq); + for (; i <= IPL_HIGH; i++) + macintr_ienable_l[i] &= ~(1 << irq); + } else { + for (i = IPL_NONE; i < minipl; i++) + macintr_ienable_h[i] |= (1 << (irq-32)); + for (; i <= IPL_HIGH; i++) + macintr_ienable_h[i] &= ~(1 << (irq-32)); } - m_virq_inited = 1; } - /* irq in table already? */ - if (m_virq[irq] != 0) - return m_virq[irq]; - - if (irq < 0 || irq >= 64) - panic("invalid irq %d", irq); - m_virq_max++; - v = m_virq_max; - if (v > HWIRQ_MAX) - panic("virq overflow"); - - m_hwirq[v] = irq; - m_virq[irq] = v; #if 0 -printf("\nmapirq %x to %x\n", irq, v); + for (i = 0; i < IPL_NUM; i++) + printf("imask[%d] %x %x\n", i, macintr_ienable_l[i], + macintr_ienable_h[i]); #endif - - return v; -} - -/* - * Count leading zeros. - */ -static __inline int -cntlzw(int x) -{ - int a; - - __asm __volatile ("cntlzw %0,%1" : "=r"(a) : "r"(x)); - - return a; } /* @@ -499,143 +436,136 @@ void mac_ext_intr() { int irq = 0; - int o_imen, r_imen; int pcpl; struct cpu_info *ci = curcpu(); + struct intrq *iq; struct intrhand *ih; - volatile unsigned long int_state; pcpl = ci->ci_cpl; /* Turn off all */ - int_state = read_irq(); - if (int_state == 0) - goto out; - -start: - irq = 31 - cntlzw(int_state); - - o_imen = imen_m; - r_imen = 1 << irq; + irq = macintr_read_irq(); + while (irq != 255) { + iq = &macintr_handler[irq]; + macintr_setipl(iq->iq_ipl); - if ((ci->ci_cpl & r_imen) != 0) { - /* Masked! Mark this as pending. */ - ci->ci_ipending |= r_imen; - imen_m |= r_imen; - enable_irq(~imen_m); - } else { - splraise(m_intrmask[irq]); - - ih = m_intrhand[irq]; - while (ih) { + TAILQ_FOREACH(ih, &iq->iq_list, ih_list) { + ppc_intr_enable(1); if ((*ih->ih_fun)(ih->ih_arg)) ih->ih_count.ec_count++; - ih = ih->ih_next; + (void)ppc_intr_disable(); } + macintr_eoi(irq); + macintr_setipl(pcpl); uvmexp.intrs++; + + irq = macintr_read_irq(); } - int_state &= ~r_imen; - if (int_state) - goto start; -out: + ppc_intr_enable(1); splx(pcpl); /* Process pendings. */ } void -mac_intr_do_pending_int() +macintr_do_pending_int() { struct cpu_info *ci = curcpu(); - struct intrhand *ih; - int irq; - int pcpl; - int hwpend; - int s; - - if (ci->ci_iactive) - return; - - ci->ci_iactive = 1; - pcpl = splhigh(); /* Turn off all */ + int pcpl = ci->ci_cpl; /* XXX */ + int s, s2; s = ppc_intr_disable(); - - hwpend = ci->ci_ipending & ~pcpl; /* Do now unmasked pendings */ - imen_m &= ~hwpend; - enable_irq(~imen_m); - hwpend &= HWIRQ_MASK; - while (hwpend) { - irq = 31 - cntlzw(hwpend); - hwpend &= ~(1L << irq); - ih = m_intrhand[irq]; - while(ih) { - if ((*ih->ih_fun)(ih->ih_arg)) - ih->ih_count.ec_count++; - ih = ih->ih_next; - } + if (ci->ci_iactive & CI_IACTIVE_PROCESSING_SOFT) { + ppc_intr_enable(s); + return; } - - /*out32rb(INT_ENABLE_REG, ~imen_m);*/ + atomic_setbits_int(&ci->ci_iactive, CI_IACTIVE_PROCESSING_SOFT); do { - if((ci->ci_ipending & SINT_CLOCK) & ~pcpl) { - ci->ci_ipending &= ~SINT_CLOCK; - softclock(); + if((ci->ci_ipending & SI_TO_IRQBIT(SI_SOFTTTY)) && (pcpl < IPL_SOFTTTY)) { + ci->ci_ipending &= ~SI_TO_IRQBIT(SI_SOFTTTY); + s2 = ci->ci_cpl; + ci->ci_cpl = IPL_SOFTTTY; + ppc_intr_enable(1); + KERNEL_LOCK(); + softtty(); + KERNEL_UNLOCK(); + ppc_intr_disable(); + ci->ci_cpl = pcpl; + continue; } - if((ci->ci_ipending & SINT_NET) & ~pcpl) { + if((ci->ci_ipending & SI_TO_IRQBIT(SI_SOFTNET)) && (pcpl < IPL_SOFTNET)) { extern int netisr; int pisr; - ci->ci_ipending &= ~SINT_NET; + ci->ci_ipending &= ~SI_TO_IRQBIT(SI_SOFTNET); while ((pisr = netisr) != 0) { atomic_clearbits_int(&netisr, pisr); + ci->ci_cpl = IPL_SOFTNET; + ppc_intr_enable(1); + KERNEL_LOCK(); softnet(pisr); + KERNEL_UNLOCK(); + ppc_intr_disable(); + ci->ci_cpl = pcpl; } + continue; } - if((ci->ci_ipending & SINT_TTY) & ~pcpl) { - ci->ci_ipending &= ~SINT_TTY; - softtty(); + if((ci->ci_ipending & SI_TO_IRQBIT(SI_SOFTCLOCK)) && (pcpl < IPL_SOFTCLOCK)) { + ci->ci_ipending &= ~SI_TO_IRQBIT(SI_SOFTCLOCK); + ci->ci_cpl = IPL_SOFTCLOCK; + ppc_intr_enable(1); + KERNEL_LOCK(); + softclock(); + KERNEL_UNLOCK(); + ppc_intr_disable(); + ci->ci_cpl = pcpl; + continue; } - } while ((ci->ci_ipending & SINT_MASK) & ~pcpl); - ci->ci_ipending &= pcpl; - ci->ci_cpl = pcpl; /* Don't use splx... we are here already! */ - ppc_intr_enable(s); - ci->ci_iactive = 0; + } while (ci->ci_ipending & ppc_smask[pcpl]); + macintr_setipl(pcpl); + atomic_clearbits_int(&ci->ci_iactive, CI_IACTIVE_PROCESSING_SOFT); } -static int -read_irq() +void +macintr_eoi(int irq) { - int rv = 0; - int state0, state1, p; - int state0save, state1save; + u_int32_t state0, state1; - state0 = in32rb(INT_STATE_REG0); - if (state0) + if (irq < 32) { + state0 = 1 << irq; out32rb(INT_CLEAR_REG0, state0); - state0save = state0; - while (state0) { - p = 31 - cntlzw(state0); - rv |= 1 << m_virq[p]; - state0 &= ~(1 << p); + } else { + if (heathrow_FCR) { /* has heathrow? */ + state1 = 1 << (irq - 32); + out32rb(INT_CLEAR_REG1, state1); + } } +} + +int +macintr_read_irq() +{ + struct cpu_info *ci = curcpu(); + u_int32_t state0, state1, irq_mask; + int ipl, irq; + + state0 = in32rb(INT_STATE_REG0); if (heathrow_FCR) /* has heathrow? */ state1 = in32rb(INT_STATE_REG1); else state1 = 0; - if (state1) - out32rb(INT_CLEAR_REG1, state1); - state1save = state1; - while (state1) { - p = 31 - cntlzw(state1); - rv |= 1 << m_virq[p + 32]; - state1 &= ~(1 << p); + for (ipl = IPL_HIGH; ipl >= ci->ci_cpl; ipl --) { + irq_mask = state0 & macintr_ienable_l[ipl]; + if (irq_mask) { + irq = ffs(irq_mask) - 1; + return irq; + } + irq_mask = state1 & macintr_ienable_h[ipl]; + if (irq_mask) { + irq = ffs(irq_mask) + 31; + return irq; + } } -#if 0 -printf("mac_intr int_stat 0:%x 1:%x\n", state0save, state1save); -#endif - - /* 1 << 0 is invalid. */ - return rv & ~1; + return 255; } diff --git a/sys/arch/macppc/dev/openpic.c b/sys/arch/macppc/dev/openpic.c index a7038df1b34..63fd8e9e3b3 100644 --- a/sys/arch/macppc/dev/openpic.c +++ b/sys/arch/macppc/dev/openpic.c @@ -1,6 +1,7 @@ -/* $OpenBSD: openpic.c,v 1.48 2008/09/16 04:20:42 drahn Exp $ */ +/* $OpenBSD: openpic.c,v 1.49 2008/09/18 03:56:25 drahn Exp $ */ /*- + * Copyright (c) 2008 Dale Rahn <drahn@openbsd.org> * Copyright (c) 1995 Per Fogelstrom * Copyright (c) 1993, 1994 Charles M. Hannum. * Copyright (c) 1990 The Regents of the University of California. @@ -56,23 +57,19 @@ #include <dev/ofw/openfirm.h> #define ICU_LEN 128 +int openpic_numirq = ICU_LEN; #define LEGAL_IRQ(x) ((x >= 0) && (x < ICU_LEN)) -int o_intrtype[ICU_LEN], o_intrmaxlvl[ICU_LEN]; -struct intrhand *o_intrhand[ICU_LEN] = { 0 }; -int o_hwirq[ICU_LEN], o_virq[ICU_LEN]; -int o_virq_max; +int openpic_pri_share[IPL_NUM]; + +struct intrq openpic_handler[ICU_LEN]; -static int fakeintr(void *); -static char *intr_typename(int type); void openpic_calc_mask(void); -static __inline int cntlzw(int x); -static int mapirq(int irq); int openpic_prog_button(void *arg); -void openpic_enable_irq_mask(int irq_mask); -#define HWIRQ_MAX 27 -#define HWIRQ_MASK 0x0fffffff +ppc_splraise_t openpic_splraise; +ppc_spllower_t openpic_spllower; +ppc_splx_t openpic_splx; /* IRQ vector used for inter-processor interrupts. */ #define IPI_VECTOR_NOP 64 @@ -83,17 +80,30 @@ static struct evcount ipi_nop[PPC_MAXPROCS]; static int ipi_nopirq = IPI_VECTOR_NOP; static int ipi_ddbirq = IPI_VECTOR_DDB; #endif +struct evcount openpic_spurious; +int openpic_spurious_irq = 255; -static __inline u_int openpic_read(int); -static __inline void openpic_write(int, u_int); -void openpic_set_enable_irq(int, int); -void openpic_enable_irq(int); -void openpic_disable_irq(int); -void openpic_init(void); -void openpic_set_priority(int, int); +void openpic_enable_irq(int, int); +void openpic_disable_irq(int); +void openpic_init(void); +void openpic_set_priority(int); void openpic_ipi_ddb(void); -static __inline int openpic_read_irq(int); -static __inline void openpic_eoi(int); + +typedef void (void_f) (void); +extern void_f *pending_int_f; + +vaddr_t openpic_base; +void * openpic_intr_establish( void * lcv, int irq, int type, int level, + int (*ih_fun)(void *), void *ih_arg, char *name); +void openpic_intr_disestablish( void *lcp, void *arg); +void openpic_collect_preconf_intr(void); +int openpic_big_endian; +#ifdef MULTIPROCESSOR +intr_send_ipi_t openpic_send_ipi; +#endif + +u_int openpic_read(int reg); +void openpic_write(int reg, u_int val); struct openpic_softc { struct device sc_dev; @@ -101,9 +111,9 @@ struct openpic_softc { int openpic_match(struct device *parent, void *cf, void *aux); void openpic_attach(struct device *, struct device *, void *); -void openpic_do_pending_int(void); +void openpic_do_pending_int(int pcpl); void openpic_collect_preconf_intr(void); -void ext_intr_openpic(void); +void openpic_ext_intr(void); struct cfattach openpic_ca = { sizeof(struct openpic_softc), @@ -115,6 +125,42 @@ struct cfdriver openpic_cd = { NULL, "openpic", DV_DULL }; +u_int +openpic_read(int reg) +{ + char *addr = (void *)(openpic_base + reg); + + asm volatile("eieio"); + if (openpic_big_endian) + return in32(addr); + else + return in32rb(addr); +} + +void +openpic_write(int reg, u_int val) +{ + char *addr = (void *)(openpic_base + reg); + + if (openpic_big_endian) + out32(addr, val); + else + out32rb(addr, val); + asm volatile("eieio"); +} + +static inline int +openpic_read_irq(int cpu) +{ + return openpic_read(OPENPIC_IACK(cpu)) & OPENPIC_VECTOR_MASK; +} + +static inline void +openpic_eoi(int cpu) +{ + openpic_write(OPENPIC_EOI(cpu), 0); +} + int openpic_match(struct device *parent, void *cf, void *aux) { @@ -142,22 +188,10 @@ openpic_match(struct device *parent, void *cf, void *aux) return 1; } -typedef void (void_f) (void); -extern void_f *pending_int_f; - -vaddr_t openpic_base; -void * openpic_intr_establish( void * lcv, int irq, int type, int level, - int (*ih_fun)(void *), void *ih_arg, char *name); -void openpic_intr_disestablish( void *lcp, void *arg); -#ifdef MULTIPROCESSOR -intr_send_ipi_t openpic_send_ipi; -#endif -void openpic_collect_preconf_intr(void); -int openpic_big_endian; - void openpic_attach(struct device *parent, struct device *self, void *aux) { + struct cpu_info *ci = curcpu(); struct confargs *ca = aux; u_int32_t reg; @@ -168,12 +202,16 @@ openpic_attach(struct device *parent, struct device *self, void *aux) openpic_base = (vaddr_t) mapiodev (ca->ca_baseaddr + ca->ca_reg[0], 0x40000); - printf(": version 0x%x %s endian", openpic_read(OPENPIC_VENDOR_ID), - openpic_big_endian ? "big" : "little" ); + /* openpic may support more than 128 interupts but driver doesn't */ + openpic_numirq = ((openpic_read(OPENPIC_FEATURE) >> 16) & 0x7f)+1; + + printf(": version 0x%x feature %x %s", + openpic_read(OPENPIC_VENDOR_ID), + openpic_read(OPENPIC_FEATURE), + openpic_big_endian ? "BE" : "LE" ); openpic_init(); - pending_int_f = openpic_do_pending_int; intr_establish_func = openpic_intr_establish; intr_disestablish_func = openpic_intr_disestablish; mac_intr_establish_func = openpic_intr_establish; @@ -181,22 +219,73 @@ openpic_attach(struct device *parent, struct device *self, void *aux) #ifdef MULTIPROCESSOR intr_send_ipi_func = openpic_send_ipi; #endif - install_extint(ext_intr_openpic); -#if 1 + ppc_smask_init(); + openpic_collect_preconf_intr(); -#endif + + evcount_attach(&openpic_spurious, "spurious", + (void *)&openpic_spurious_irq, &evcount_intr); #if 1 mac_intr_establish(parent, 0x37, IST_LEVEL, IPL_HIGH, openpic_prog_button, (void *)0x37, "progbutton"); #endif + ppc_intr_func.raise = openpic_splraise; + ppc_intr_func.lower = openpic_spllower; + ppc_intr_func.x = openpic_splx; + + openpic_set_priority(ci->ci_cpl); + ppc_intr_enable(1); printf("\n"); } +static inline void +openpic_setipl(int newcpl) +{ + struct cpu_info *ci = curcpu(); + int s; + /* XXX - try do to this without the disable */ + s = ppc_intr_disable(); + ci->ci_cpl = newcpl; + openpic_set_priority(newcpl); + ppc_intr_enable(s); +} + +int +openpic_splraise(int newcpl) +{ + struct cpu_info *ci = curcpu(); + newcpl = openpic_pri_share[newcpl]; + int ocpl = ci->ci_cpl; + if (ocpl > newcpl) + newcpl = ocpl; + + openpic_setipl(newcpl); + + return ocpl; +} + +int +openpic_spllower(int newcpl) +{ + struct cpu_info *ci = curcpu(); + int ocpl = ci->ci_cpl; + + openpic_splx(newcpl); + + return ocpl; +} + +void +openpic_splx(int newcpl) +{ + openpic_do_pending_int(newcpl); +} + void openpic_collect_preconf_intr() { @@ -215,13 +304,6 @@ openpic_collect_preconf_intr() } } -static int -fakeintr(void *arg) -{ - - return 0; -} - /* * Register an interrupt handler. */ @@ -229,74 +311,52 @@ void * openpic_intr_establish(void *lcv, int irq, int type, int level, int (*ih_fun)(void *), void *ih_arg, char *name) { - struct intrhand **p, *q, *ih; - static struct intrhand fakehand; - - fakehand.ih_next = NULL; - fakehand.ih_fun = fakeintr; - -#if 0 -printf("mac_intr_establish, hI %d L %d ", irq, type); -#endif - - irq = mapirq(irq); -#if 0 -printf("vI %d ", irq); -#endif + struct intrhand *ih; + struct intrq *iq; + int s; /* no point in sleeping unless someone can free memory. */ ih = malloc(sizeof *ih, M_DEVBUF, cold ? M_NOWAIT : M_WAITOK); if (ih == NULL) panic("intr_establish: can't malloc handler info"); + iq = &openpic_handler[irq]; if (!LEGAL_IRQ(irq) || type == IST_NONE) panic("intr_establish: bogus irq or type"); - switch (o_intrtype[irq]) { + switch (iq->iq_ist) { case IST_NONE: - o_intrtype[irq] = type; + iq->iq_ist = type; break; case IST_EDGE: case IST_LEVEL: - if (type == o_intrtype[irq]) + if (type == iq->iq_ist) break; case IST_PULSE: if (type != IST_NONE) panic("intr_establish: can't share %s with %s", - intr_typename(o_intrtype[irq]), - intr_typename(type)); + ppc_intr_typename(iq->iq_ist), + ppc_intr_typename(type)); break; } - /* - * Figure out where to put the handler. - * This is O(N^2), but we want to preserve the order, and N is - * generally small. - */ - for (p = &o_intrhand[irq]; (q = *p) != NULL; p = &q->ih_next) - ; + ih->ih_fun = ih_fun; + ih->ih_arg = ih_arg; + ih->ih_level = level; + ih->ih_irq = irq; + + evcount_attach(&ih->ih_count, name, (void *)&ih->ih_irq, + &evcount_intr); /* - * Actually install a fake handler momentarily, since we might be doing - * this with interrupts enabled and DON'T WANt the real routine called - * until masking is set up. + * Append handler to end of list */ - fakehand.ih_level = level; - *p = &fakehand; + s = ppc_intr_disable(); + TAILQ_INSERT_TAIL(&iq->iq_list, ih, ih_list); openpic_calc_mask(); - /* - * Poke the real handler in now. - */ - ih->ih_fun = ih_fun; - ih->ih_arg = ih_arg; - ih->ih_next = NULL; - ih->ih_level = level; - ih->ih_irq = irq; - evcount_attach(&ih->ih_count, name, (void *)&o_hwirq[irq], - &evcount_intr); - *p = ih; + ppc_intr_enable(s); return (ih); } @@ -309,51 +369,27 @@ openpic_intr_disestablish(void *lcp, void *arg) { struct intrhand *ih = arg; int irq = ih->ih_irq; - struct intrhand **p, *q; + struct intrq *iq = &openpic_handler[irq]; + int s; if (!LEGAL_IRQ(irq)) panic("intr_disestablish: bogus irq"); /* * Remove the handler from the chain. - * This is O(n^2), too. */ - for (p = &o_intrhand[irq]; (q = *p) != NULL && q != ih; p = &q->ih_next) - ; - if (q) - *p = q->ih_next; - else - panic("intr_disestablish: handler not registered"); - - evcount_detach(&ih->ih_count); - free((void *)ih, M_DEVBUF); + s = ppc_intr_disable(); + TAILQ_REMOVE(&iq->iq_list, ih, ih_list); openpic_calc_mask(); - if (o_intrhand[irq] == NULL) - o_intrtype[irq] = IST_NONE; -} - + ppc_intr_enable(s); -static char * -intr_typename(int type) -{ + evcount_detach(&ih->ih_count); + free((void *)ih, M_DEVBUF); - switch (type) { - case IST_NONE: - return ("none"); - case IST_PULSE: - return ("pulsed"); - case IST_EDGE: - return ("edge-triggered"); - case IST_LEVEL: - return ("level-triggered"); - default: - panic("intr_typename: invalid type %d", type); -#if 1 /* XXX */ - return ("unknown"); -#endif - } + if (TAILQ_EMPTY(&iq->iq_list)) + iq->iq_ist = IST_NONE; } /* @@ -366,268 +402,129 @@ intr_typename(int type) void openpic_calc_mask() { + struct cpu_info *ci = curcpu(); int irq; struct intrhand *ih; int i; /* disable all openpic interrupts */ - openpic_set_priority(0, 15); + openpic_set_priority(15); - for (irq = 0; irq < ICU_LEN; irq++) { - int max = IPL_NONE; - int min = IPL_HIGH; - int reg; - if (o_virq[irq] != 0) { - for (ih = o_intrhand[o_virq[irq]]; ih; - ih = ih->ih_next) { - if (ih->ih_level > max) - max = ih->ih_level; - if (ih->ih_level < min) - min = ih->ih_level; - } - } + for (i = IPL_NONE; i < IPL_NUM; i++) { + openpic_pri_share[i] = i; + } - o_intrmaxlvl[irq] = max; + for (irq = 0; irq < openpic_numirq; irq++) { + int maxipl = IPL_NONE; + int minipl = IPL_HIGH; + struct intrq *iq = &openpic_handler[irq]; - /* adjust priority if it changes */ - reg = openpic_read(OPENPIC_SRC_VECTOR(irq)); - if (max != ((reg >> OPENPIC_PRIORITY_SHIFT) & 0xf)) { - openpic_write(OPENPIC_SRC_VECTOR(irq), - (reg & ~(0xf << OPENPIC_PRIORITY_SHIFT)) | - (max << OPENPIC_PRIORITY_SHIFT) ); + TAILQ_FOREACH(ih, &iq->iq_list, ih_list) { + if (ih->ih_level > maxipl) + maxipl = ih->ih_level; + if (ih->ih_level < minipl) + minipl = ih->ih_level; } - if (max == IPL_NONE) - min = IPL_NONE; /* Interrupt not enabled */ + if (maxipl == IPL_NONE) { + minipl = IPL_NONE; /* Interrupt not enabled */ - if (o_virq[irq] != 0) { - /* Enable (dont mask) interrupts at lower levels */ - for (i = IPL_NONE; i < min; i++) - imask[i] &= ~(1 << o_virq[irq]); - for (; i <= IPL_HIGH; i++) - imask[i] |= (1 << o_virq[irq]); + openpic_disable_irq(irq); + } else { + for (i = minipl; i <= maxipl; i++) { + openpic_pri_share[i] = maxipl; + } + openpic_enable_irq(irq, maxipl); } - } - /* restore interrupts */ - openpic_set_priority(0, 0); - - for (i = IPL_NONE; i <= IPL_HIGH; i++) { - if (i > IPL_NONE) - imask[i] |= SINT_MASK; + iq->iq_ipl = maxipl; } - imask[IPL_HIGH] = 0xffffffff; -} - -/* - * Map 64 irqs into 32 (bits). - */ -static int -mapirq(int irq) -{ - int v; - - /* irq in table already? */ - if (o_virq[irq] != 0) - return o_virq[irq]; - - if (irq < 0 || irq >= ICU_LEN) - panic("invalid irq %d", irq); - o_virq_max++; - v = o_virq_max; - if (v > HWIRQ_MAX) - panic("virq overflow"); - - o_hwirq[v] = irq; - o_virq[irq] = v; -#if 0 -printf("\nmapirq %x to %x\n", irq, v); -#endif - - return v; -} - -/* - * Count leading zeros. - */ -static __inline int -cntlzw(int x) -{ - int a; - - __asm __volatile ("cntlzw %0,%1" : "=r"(a) : "r"(x)); - - return a; + /* restore interrupts */ + openpic_set_priority(ci->ci_cpl); } -void openpic_do_pending_softint(int pcpl); - void -openpic_do_pending_int() +openpic_do_pending_int(int pcpl) { struct cpu_info *ci = curcpu(); - struct intrhand *ih; - int irq; - int pcpl; - int hwpend; - int pri, pripending; int s; + int loopcount = 0; - if (ci->ci_iactive & CI_IACTIVE_PROCESSING_HARD) - return; - - atomic_setbits_int(&ci->ci_iactive, CI_IACTIVE_PROCESSING_HARD); s = ppc_intr_disable(); - pcpl = ci->ci_cpl; - - hwpend = ci->ci_ipending & ~pcpl; /* Do now unmasked pendings */ - hwpend &= HWIRQ_MASK; - while (hwpend) { - /* this still doesn't handle the interrupts in priority order */ - for (pri = IPL_HIGH; pri >= IPL_NONE; pri--) { - pripending = hwpend & ~imask[pri]; - irq = 31 - cntlzw(pripending); - ci->ci_ipending &= ~(1L << irq); - ci->ci_cpl = imask[o_intrmaxlvl[o_hwirq[irq]]]; - openpic_enable_irq_mask(~ci->ci_cpl); - ih = o_intrhand[irq]; - while(ih) { - ppc_intr_enable(1); - - KERNEL_LOCK(); - if ((*ih->ih_fun)(ih->ih_arg)) - ih->ih_count.ec_count++; - KERNEL_UNLOCK(); - - (void)ppc_intr_disable(); - - ih = ih->ih_next; - } - } - hwpend = ci->ci_ipending & ~pcpl;/* Catch new pendings */ - hwpend &= HWIRQ_MASK; - } - ci->ci_cpl = pcpl | SINT_MASK; - openpic_enable_irq_mask(~ci->ci_cpl); - atomic_clearbits_int(&ci->ci_iactive, CI_IACTIVE_PROCESSING_HARD); - - openpic_do_pending_softint(pcpl); - - ppc_intr_enable(s); -} - -void -openpic_do_pending_softint(int pcpl) -{ - struct cpu_info *ci = curcpu(); - - if (ci->ci_iactive & CI_IACTIVE_PROCESSING_SOFT) + if (ci->ci_iactive & CI_IACTIVE_PROCESSING_SOFT) { + ppc_intr_enable(s); return; + } atomic_setbits_int(&ci->ci_iactive, CI_IACTIVE_PROCESSING_SOFT); do { - if((ci->ci_ipending & SINT_CLOCK) & ~pcpl) { - ci->ci_ipending &= ~SINT_CLOCK; - ci->ci_cpl = SINT_CLOCK|SINT_NET|SINT_TTY; - ppc_intr_enable(1); + loopcount ++; + if (loopcount > 5) + printf("do_pending looping %d pcpl %x %x\n", loopcount, + pcpl, ci->ci_cpl); + if((ci->ci_ipending & SI_TO_IRQBIT(SI_SOFTTTY)) && + (pcpl < IPL_SOFTTTY)) { + ci->ci_ipending &= ~SI_TO_IRQBIT(SI_SOFTTTY); + + openpic_setipl(IPL_SOFTTTY); + ppc_intr_enable(s); KERNEL_LOCK(); - softclock(); + softtty(); KERNEL_UNLOCK(); ppc_intr_disable(); continue; } - if((ci->ci_ipending & SINT_NET) & ~pcpl) { + if((ci->ci_ipending & SI_TO_IRQBIT(SI_SOFTNET)) && + (pcpl < IPL_SOFTNET)) { extern int netisr; int pisr; - - ci->ci_ipending &= ~SINT_NET; - ci->ci_cpl = SINT_NET|SINT_TTY; + + ci->ci_ipending &= ~SI_TO_IRQBIT(SI_SOFTNET); + openpic_setipl(IPL_SOFTNET); + ppc_intr_enable(s); + KERNEL_LOCK(); while ((pisr = netisr) != 0) { atomic_clearbits_int(&netisr, pisr); - ppc_intr_enable(1); - KERNEL_LOCK(); softnet(pisr); - KERNEL_UNLOCK(); - ppc_intr_disable(); } + KERNEL_UNLOCK(); + ppc_intr_disable(); continue; } - if((ci->ci_ipending & SINT_TTY) & ~pcpl) { - ci->ci_ipending &= ~SINT_TTY; - ci->ci_cpl = SINT_TTY; - ppc_intr_enable(1); + if((ci->ci_ipending & SI_TO_IRQBIT(SI_SOFTCLOCK)) && + (pcpl < IPL_SOFTCLOCK)) { + ci->ci_ipending &= ~SI_TO_IRQBIT(SI_SOFTCLOCK); + openpic_setipl(IPL_SOFTCLOCK); + ppc_intr_enable(s); KERNEL_LOCK(); - softtty(); + softclock(); KERNEL_UNLOCK(); ppc_intr_disable(); continue; } - } while ((ci->ci_ipending & SINT_MASK) & ~pcpl); - ci->ci_cpl = pcpl; /* Don't use splx... we are here already! */ + break; + } while (ci->ci_ipending & ppc_smask[pcpl]); + openpic_setipl(pcpl); /* Don't use splx... we are here already! */ atomic_clearbits_int(&ci->ci_iactive, CI_IACTIVE_PROCESSING_SOFT); + ppc_intr_enable(s); } -u_int -openpic_read(int reg) -{ - char *addr = (void *)(openpic_base + reg); - - if (openpic_big_endian) - return in32(addr); - else - return in32rb(addr); -} - -void -openpic_write(int reg, u_int val) -{ - char *addr = (void *)(openpic_base + reg); - - if (openpic_big_endian) - out32(addr, val); - else - out32rb(addr, val); -} - -void -openpic_enable_irq_mask(int irq_mask) -{ - int irq; - for ( irq = 0; irq <= o_virq_max; irq++) { - if (irq_mask & (1 << irq)) - openpic_enable_irq(o_hwirq[irq]); - else - openpic_disable_irq(o_hwirq[irq]); - } -} - -void -openpic_set_enable_irq(int irq, int type) -{ - u_int x; - - x = openpic_read(OPENPIC_SRC_VECTOR(irq)); - x &= ~(OPENPIC_IMASK|OPENPIC_SENSE_LEVEL|OPENPIC_SENSE_EDGE); - if (type == IST_LEVEL) - x |= OPENPIC_SENSE_LEVEL; - else - x |= OPENPIC_SENSE_EDGE; - openpic_write(OPENPIC_SRC_VECTOR(irq), x); -} void -openpic_enable_irq(int irq) +openpic_enable_irq(int irq, int pri) { u_int x; + struct intrq *iq = &openpic_handler[irq]; - x = openpic_read(OPENPIC_SRC_VECTOR(irq)); - x &= ~(OPENPIC_IMASK|OPENPIC_SENSE_LEVEL|OPENPIC_SENSE_EDGE); - if (o_intrtype[o_virq[irq]] == IST_LEVEL) + x = irq; + if (iq->iq_ist == IST_LEVEL) x |= OPENPIC_SENSE_LEVEL; else x |= OPENPIC_SENSE_EDGE; + x |= OPENPIC_POLARITY_POSITIVE; + x |= pri << OPENPIC_PRIORITY_SHIFT; openpic_write(OPENPIC_SRC_VECTOR(irq), x); } @@ -642,31 +539,13 @@ openpic_disable_irq(int irq) } void -openpic_set_priority(int cpu, int pri) -{ - u_int x; - - x = openpic_read(OPENPIC_CPU_PRIORITY(cpu)); - x &= ~OPENPIC_CPU_PRIORITY_MASK; - x |= pri; - openpic_write(OPENPIC_CPU_PRIORITY(cpu), x); -} - -int -openpic_read_irq(int cpu) -{ - return openpic_read(OPENPIC_IACK(cpu)) & OPENPIC_VECTOR_MASK; -} - -void -openpic_eoi(int cpu) +openpic_set_priority(int pri) { - openpic_write(OPENPIC_EOI(cpu), 0); - openpic_read(OPENPIC_EOI(cpu)); + struct cpu_info *ci = curcpu(); + openpic_write(OPENPIC_CPU_PRIORITY(ci->ci_cpuid), pri); } #ifdef MULTIPROCESSOR - void openpic_send_ipi(struct cpu_info *ci, int id) { @@ -681,80 +560,80 @@ openpic_send_ipi(struct cpu_info *ci, int id) panic("invalid ipi send to cpu %d %d\n", ci->ci_cpuid, id); } - openpic_write(OPENPIC_IPI(curcpu()->ci_cpuid, id), 1 << ci->ci_cpuid); } #endif void -ext_intr_openpic() +openpic_ext_intr() { struct cpu_info *ci = curcpu(); - int irq, realirq; - int r_imen; - int pcpl, ocpl; + int irq; + int pcpl; struct intrhand *ih; + struct intrq *iq; + int irqloop = 0; + static int irqnest = 0; + int spurious; pcpl = ci->ci_cpl; - realirq = openpic_read_irq(ci->ci_cpuid); + irq = openpic_read_irq(ci->ci_cpuid); + irqnest++; - while (realirq != 255) { + while (irq != 255) { + irqloop++; + if (irqloop > 20 || irqnest > 3) { + printf("irqloop %d irqnest %d\n", irqloop, irqnest); + } #ifdef MULTIPROCESSOR - if (realirq == IPI_VECTOR_NOP) { + if (irq == IPI_VECTOR_NOP) { ipi_nop[ci->ci_cpuid].ec_count++; openpic_eoi(ci->ci_cpuid); - realirq = openpic_read_irq(ci->ci_cpuid); + irq = openpic_read_irq(ci->ci_cpuid); continue; } - if (realirq == IPI_VECTOR_DDB) { + if (irq == IPI_VECTOR_DDB) { ipi_ddb[ci->ci_cpuid].ec_count++; openpic_eoi(ci->ci_cpuid); openpic_ipi_ddb(); - realirq = openpic_read_irq(ci->ci_cpuid); + irq = openpic_read_irq(ci->ci_cpuid); continue; } #endif + iq = &openpic_handler[irq]; - irq = o_virq[realirq]; + if (iq->iq_ipl <= ci->ci_cpl) + printf("invalid interrupt %d lvl %d at %d hw %d\n", + irq, iq->iq_ipl, ci->ci_cpl, + openpic_read(OPENPIC_CPU_PRIORITY(ci->ci_cpuid))); + splraise(iq->iq_ipl); + openpic_eoi(ci->ci_cpuid); - /* XXX check range */ - - r_imen = 1 << irq; - - if ((pcpl & r_imen) != 0) { - /* Masked! Mark this as pending. */ - ci->ci_ipending |= r_imen; - openpic_enable_irq_mask(~imask[o_intrmaxlvl[realirq]]); - openpic_eoi(ci->ci_cpuid); - } else { - openpic_enable_irq_mask(~imask[o_intrmaxlvl[realirq]]); - openpic_eoi(ci->ci_cpuid); - ocpl = splraise(imask[o_intrmaxlvl[realirq]]); - - ih = o_intrhand[irq]; - while (ih) { - ppc_intr_enable(1); - - KERNEL_LOCK(); - if ((*ih->ih_fun)(ih->ih_arg)) - ih->ih_count.ec_count++; - KERNEL_UNLOCK(); - - (void)ppc_intr_disable(); - ih = ih->ih_next; - } + spurious = 1; + TAILQ_FOREACH(ih, &iq->iq_list, ih_list) { + ppc_intr_enable(1); + KERNEL_LOCK(); + if ((*ih->ih_fun)(ih->ih_arg)) { + ih->ih_count.ec_count++; + spurious = 0; + } + KERNEL_UNLOCK(); - uvmexp.intrs++; - __asm__ volatile("":::"memory"); /* don't reorder.... */ - ci->ci_cpl = ocpl; - __asm__ volatile("":::"memory"); /* don't reorder.... */ - openpic_enable_irq_mask(~pcpl); + (void)ppc_intr_disable(); + } + if (spurious) { + openpic_spurious.ec_count++; + printf("spurious intr %d\n", irq); } - realirq = openpic_read_irq(ci->ci_cpuid); + uvmexp.intrs++; + openpic_setipl(pcpl); + + irq = openpic_read_irq(ci->ci_cpuid); } + irqnest--; ppc_intr_enable(1); splx(pcpl); /* Process pendings. */ @@ -763,22 +642,29 @@ ext_intr_openpic() void openpic_init() { + struct cpu_info *ci = curcpu(); + struct intrq *iq; int irq; u_int x; + int i; + + openpic_set_priority(15); /* disable all interrupts */ - for (irq = 0; irq < 255; irq++) + for (irq = 0; irq < openpic_numirq; irq++) openpic_write(OPENPIC_SRC_VECTOR(irq), OPENPIC_IMASK); - openpic_set_priority(0, 15); + + for (i = 0; i < openpic_numirq; i++) { + iq = &openpic_handler[i]; + TAILQ_INIT(&iq->iq_list); + } /* we don't need 8259 pass through mode */ x = openpic_read(OPENPIC_CONFIG); x |= OPENPIC_CONFIG_8259_PASSTHRU_DISABLE; openpic_write(OPENPIC_CONFIG, x); - /* send all interrupts to cpu 0 */ - for (irq = 0; irq < ICU_LEN; irq++) - openpic_write(OPENPIC_IDEST(irq), 1 << 0); + /* initialize all vectors to something sane */ for (irq = 0; irq < ICU_LEN; irq++) { x = irq; x |= OPENPIC_IMASK; @@ -788,6 +674,17 @@ openpic_init() openpic_write(OPENPIC_SRC_VECTOR(irq), x); } + /* send all interrupts to cpu 0 */ + for (irq = 0; irq < openpic_numirq; irq++) + openpic_write(OPENPIC_IDEST(irq), 1 << 0); + + /* clear all pending interrunts */ + for (irq = 0; irq < ICU_LEN; irq++) { + openpic_read_irq(ci->ci_cpuid); + openpic_eoi(ci->ci_cpuid); + } + + #ifdef MULTIPROCESSOR /* Set up inter-processor interrupts. */ /* IPI0 - NOP */ @@ -801,6 +698,7 @@ openpic_init() x |= (15 << OPENPIC_PRIORITY_SHIFT) | IPI_VECTOR_DDB; openpic_write(OPENPIC_IPI_VECTOR(1), x); + /* XXX - ncpus */ evcount_attach(&ipi_nop[0], "ipi_nop0", (void *)&ipi_nopirq, &evcount_intr); evcount_attach(&ipi_nop[1], "ipi_nop1", (void *)&ipi_nopirq, @@ -811,21 +709,21 @@ openpic_init() &evcount_intr); #endif - /* XXX set spurious intr vector */ - - openpic_set_priority(0, 0); - /* clear all pending interrunts */ for (irq = 0; irq < ICU_LEN; irq++) { openpic_read_irq(0); openpic_eoi(0); } - for (irq = 0; irq < ICU_LEN; irq++) - openpic_disable_irq(irq); +#if 0 + openpic_write(OPENPIC_SPURIOUS_VECTOR, 255); +#endif - install_extint(ext_intr_openpic); + install_extint(openpic_ext_intr); + + openpic_set_priority(0); } + /* * programmer_button function to fix args to Debugger. * deal with any enables/disables, if necessary. @@ -842,10 +740,9 @@ openpic_prog_button (void *arg) return 1; } - void -openpic_ipi_ddb(void) +openpic_ipi_ddb() { + printf("ipi_ddb() called\n"); Debugger(); } - diff --git a/sys/arch/macppc/include/intr.h b/sys/arch/macppc/include/intr.h index 5278758ac65..ee424cb7224 100644 --- a/sys/arch/macppc/include/intr.h +++ b/sys/arch/macppc/include/intr.h @@ -1,9 +1,9 @@ -/* $OpenBSD: intr.h,v 1.4 2008/09/16 04:20:42 drahn Exp $ */ +/* $OpenBSD: intr.h,v 1.5 2008/09/18 03:56:25 drahn Exp $ */ #include <powerpc/intr.h> #ifndef _LOCORE void softtty(void); -void openpic_set_priority(int, int); +void openpic_set_priority(int); #endif diff --git a/sys/arch/macppc/macppc/clock.c b/sys/arch/macppc/macppc/clock.c index 6c7f3f889ec..215e8c46b17 100644 --- a/sys/arch/macppc/macppc/clock.c +++ b/sys/arch/macppc/macppc/clock.c @@ -1,4 +1,4 @@ -/* $OpenBSD: clock.c,v 1.22 2008/04/30 17:59:33 kettenis Exp $ */ +/* $OpenBSD: clock.c,v 1.23 2008/09/18 03:56:25 drahn Exp $ */ /* $NetBSD: clock.c,v 1.1 1996/09/30 16:34:40 ws Exp $ */ /* @@ -225,7 +225,7 @@ decr_intr(struct clockframe *frame) */ ppc_mtdec(nextevent - tb); - if (curcpu()->ci_cpl & SPL_CLOCK) { + if (ci->ci_cpl >= IPL_CLOCK) { ci->ci_statspending += nstats; } else { KERNEL_LOCK(); diff --git a/sys/arch/macppc/macppc/cpu.c b/sys/arch/macppc/macppc/cpu.c index 5d0efe0079a..1c7e58ea528 100644 --- a/sys/arch/macppc/macppc/cpu.c +++ b/sys/arch/macppc/macppc/cpu.c @@ -1,4 +1,4 @@ -/* $OpenBSD: cpu.c,v 1.57 2008/05/02 19:10:01 drahn Exp $ */ +/* $OpenBSD: cpu.c,v 1.58 2008/09/18 03:56:25 drahn Exp $ */ /* * Copyright (c) 1997 Per Fogelstrom @@ -788,7 +788,7 @@ cpu_hatch(void) ppc_intr_enable(intrstate); /* Enable inter-processor interrupts. */ - openpic_set_priority(curcpu()->ci_cpuid, 14); + openpic_set_priority(14); SCHED_LOCK(s); cpu_switchto(NULL, sched_chooseproc()); diff --git a/sys/arch/macppc/macppc/genassym.cf b/sys/arch/macppc/macppc/genassym.cf index 78f4defa294..edd3bd8a851 100644 --- a/sys/arch/macppc/macppc/genassym.cf +++ b/sys/arch/macppc/macppc/genassym.cf @@ -1,4 +1,4 @@ -# $OpenBSD: genassym.cf,v 1.17 2008/04/27 15:59:49 drahn Exp $ +# $OpenBSD: genassym.cf,v 1.18 2008/09/18 03:56:25 drahn Exp $ # # Copyright (c) 1982, 1990 The Regents of the University of California. # All rights reserved. @@ -86,7 +86,6 @@ member ci_curproc member ci_curpcb member ci_curpm member ci_want_resched -member ci_cpl member ci_intrdepth member ci_intstk member ci_tempsave diff --git a/sys/arch/macppc/macppc/machdep.c b/sys/arch/macppc/macppc/machdep.c index befb3f7bd19..d7a7dae07c6 100644 --- a/sys/arch/macppc/macppc/machdep.c +++ b/sys/arch/macppc/macppc/machdep.c @@ -1,4 +1,4 @@ -/* $OpenBSD: machdep.c,v 1.100 2008/09/16 04:20:42 drahn Exp $ */ +/* $OpenBSD: machdep.c,v 1.101 2008/09/18 03:56:25 drahn Exp $ */ /* $NetBSD: machdep.c,v 1.4 1996/10/16 19:33:11 ws Exp $ */ /* @@ -877,8 +877,6 @@ dumpsys() } -int imask[IPL_NUM]; - /* * this is a hack interface to allow zs to work better until * a true soft interrupt mechanism is created. diff --git a/sys/arch/powerpc/include/intr.h b/sys/arch/powerpc/include/intr.h index f05d41e7189..aeacf7eac6d 100644 --- a/sys/arch/powerpc/include/intr.h +++ b/sys/arch/powerpc/include/intr.h @@ -1,4 +1,4 @@ -/* $OpenBSD: intr.h,v 1.35 2008/09/16 04:20:42 drahn Exp $ */ +/* $OpenBSD: intr.h,v 1.36 2008/09/18 03:56:25 drahn Exp $ */ /* * Copyright (c) 1997 Per Fogelstrom, Opsycon AB and RTMX Inc, USA. @@ -36,14 +36,18 @@ #define _POWERPC_INTR_H_ #define IPL_NONE 0 -#define IPL_BIO 1 +#define IPL_SOFT 1 +#define IPL_SOFTCLOCK 2 +#define IPL_SOFTNET 3 +#define IPL_SOFTTTY 4 +#define IPL_BIO 5 #define IPL_AUDIO IPL_BIO /* XXX - was defined this val in audio_if.h */ -#define IPL_NET 2 -#define IPL_TTY 3 -#define IPL_VM 4 -#define IPL_CLOCK 5 -#define IPL_HIGH 6 -#define IPL_NUM 7 +#define IPL_NET 6 +#define IPL_TTY 7 +#define IPL_VM 8 +#define IPL_CLOCK 9 +#define IPL_HIGH 10 +#define IPL_NUM 11 #define IST_NONE 0 #define IST_PULSE 1 @@ -55,9 +59,9 @@ #include <sys/evcount.h> #include <machine/atomic.h> -#define PPC_NIRQ 66 -#define PPC_CLK_IRQ 64 -#define PPC_STAT_IRQ 65 +#define PPC_NIRQ 66 +#define PPC_CLK_IRQ 64 +#define PPC_STAT_IRQ 65 void setsoftclock(void); void clearsoftclock(void); @@ -70,48 +74,69 @@ int splraise(int); int spllower(int); void splx(int); +typedef int (ppc_splraise_t) (int); +typedef int (ppc_spllower_t) (int); +typedef void (ppc_splx_t) (int); -void do_pending_int(void); +extern struct ppc_intr_func { + ppc_splraise_t *raise; + ppc_spllower_t *lower; + ppc_splx_t *x; +}ppc_intr_func; + +#if 0 +/* does it make sense to call directly ?? */ +#define splraise(x) ppc_intr.raise(x) +#define spllower(x) ppc_intr.lower(x) +#define splx(x) ppc_intr.x(x) +#endif + +extern int ppc_smask[IPL_NUM]; -extern int imask[IPL_NUM]; +void ppc_smask_init(void); +char *ppc_intr_typename(int type); + +void do_pending_int(void); /* SPL asserts */ #define splassert(wantipl) /* nothing */ #define set_sint(p) atomic_setbits_int(&curcpu()->ci_ipending, p) +#if 0 #define SINT_CLOCK 0x10000000 #define SINT_NET 0x20000000 #define SINT_TTY 0x40000000 #define SPL_CLOCK 0x80000000 #define SINT_MASK (SINT_CLOCK|SINT_NET|SINT_TTY) - -#define splbio() splraise(imask[IPL_BIO]) -#define splnet() splraise(imask[IPL_NET]) -#define spltty() splraise(imask[IPL_TTY]) -#define splaudio() splraise(imask[IPL_AUDIO]) -#define splclock() splraise(imask[IPL_CLOCK]) -#define splvm() splraise(imask[IPL_VM]) -#define splsched() splhigh() -#define spllock() splhigh() -#define splstatclock() splhigh() -#define splsoftclock() splraise(SINT_CLOCK) -#define splsoftnet() splraise(SINT_NET|SINT_CLOCK) -#define splsofttty() splraise(SINT_TTY|SINT_NET|SINT_CLOCK) - -#define setsoftclock() set_sint(SINT_CLOCK); -#define setsoftnet() set_sint(SINT_NET); -#define setsofttty() set_sint(SINT_TTY); - -#define splhigh() splraise(0xffffffff) -#define spl0() spllower(0) +#endif + +#define splbio() splraise(IPL_BIO) +#define splnet() splraise(IPL_NET) +#define spltty() splraise(IPL_TTY) +#define splaudio() splraise(IPL_AUDIO) +#define splclock() splraise(IPL_CLOCK) +#define splvm() splraise(IPL_VM) +#define splsched() splhigh() +#define spllock() splhigh() +#define splstatclock() splhigh() +#define splsoftclock() splraise(IPL_SOFTCLOCK) +#define splsoftnet() splraise(IPL_SOFTNET) +#define splsofttty() splraise(IPL_SOFTTTY) + +#define setsoftclock() set_sint(SI_TO_IRQBIT(SI_SOFTCLOCK)) +#define setsoftnet() set_sint(SI_TO_IRQBIT(SI_SOFTNET)) +#define setsofttty() set_sint(SI_TO_IRQBIT(SI_SOFTTTY)) + +#define splhigh() splraise(IPL_HIGH) +#define spl0() spllower(IPL_NONE) /* * Interrupt control struct used to control the ICU setup. */ struct intrhand { - struct intrhand *ih_next; + TAILQ_ENTRY(intrhand) ih_list; int (*ih_fun)(void *); void *ih_arg; struct evcount ih_count; @@ -119,11 +144,39 @@ struct intrhand { int ih_irq; char *ih_what; }; + +struct intrq { + TAILQ_HEAD(, intrhand) iq_list; /* handler list */ + int iq_ipl; /* IPL_ to mask while handling */ + int iq_ist; /* share type */ +}; + extern int ppc_configed_intr_cnt; -#define MAX_PRECONF_INTR 16 +#define MAX_PRECONF_INTR 16 extern struct intrhand ppc_configed_intr[MAX_PRECONF_INTR]; void softnet(int isr); +#define SI_TO_IRQBIT(x) (1 << (x)) + +#define SI_SOFT 0 /* for IPL_SOFT */ +#define SI_SOFTCLOCK 1 /* for IPL_SOFTCLOCK */ +#define SI_SOFTNET 2 /* for IPL_SOFTNET */ +#define SI_SOFTTTY 3 /* for IPL_SOFTSERIAL */ + +#define SI_NQUEUES 4 + +#define SI_QUEUENAMES { \ + "generic", \ + "clock", \ + "net", \ + "serial", \ +} + +#define PPC_IPI_NOP 0 +#define PPC_IPI_DDB 1 + +void ppc_send_ipi(struct cpu_info *, int); + #define PPC_IPI_NOP 0 #define PPC_IPI_DDB 1 diff --git a/sys/arch/powerpc/powerpc/intr.c b/sys/arch/powerpc/powerpc/intr.c index 622d1558db3..ed5f5dd84fe 100644 --- a/sys/arch/powerpc/powerpc/intr.c +++ b/sys/arch/powerpc/powerpc/intr.c @@ -1,4 +1,4 @@ -/* $OpenBSD: intr.c,v 1.2 2007/11/14 20:47:34 thib Exp $ */ +/* $OpenBSD: intr.c,v 1.3 2008/09/18 03:56:25 drahn Exp $ */ /* * Copyright (c) 1997 Per Fogelstrom, Opsycon AB and RTMX Inc, USA. @@ -35,45 +35,121 @@ #include <machine/cpu.h> #include <machine/intr.h> +#include <machine/lock.h> + +int ppc_dflt_splraise(int); +int ppc_dflt_spllower(int); +void ppc_dflt_splx(int); + +/* provide a function for asm code to call */ +#undef splraise +#undef spllower +#undef splx + +int ppc_smask[IPL_NUM]; + +void +ppc_smask_init() +{ + int i; + + for (i = IPL_NONE; i <= IPL_HIGH; i++) { + ppc_smask[i] = 0; +#if 0 + /* NOT YET */ + if (i < IPL_SOFT) + ppc_smask[i] |= SI_TO_IRQBIT(SI_SOFT); +#endif + if (i < IPL_SOFTCLOCK) + ppc_smask[i] |= SI_TO_IRQBIT(SI_SOFTCLOCK); + if (i < IPL_SOFTNET) + ppc_smask[i] |= SI_TO_IRQBIT(SI_SOFTNET); + if (i < IPL_SOFTTTY) + ppc_smask[i] |= SI_TO_IRQBIT(SI_SOFTTTY); + } +} + int splraise(int newcpl) { + return ppc_intr_func.raise(newcpl); +} + +int +spllower(int newcpl) +{ + return ppc_intr_func.lower(newcpl); +} + +void +splx(int newcpl) +{ + ppc_intr_func.x(newcpl); +} + +int +ppc_dflt_splraise(int newcpl) +{ struct cpu_info *ci = curcpu(); int oldcpl; - __asm__ volatile("":::"memory"); /* reorder protect */ oldcpl = ci->ci_cpl; - ci->ci_cpl = oldcpl | newcpl; - __asm__ volatile("":::"memory"); + if (newcpl < oldcpl) + newcpl = oldcpl; + ci->ci_cpl = newcpl; return (oldcpl); } +/* + * functions with 'default' behavior to use before the real + * interrupt controller attaches + */ int -spllower(int newcpl) +ppc_dflt_spllower(int newcpl) { struct cpu_info *ci = curcpu(); int oldcpl; - __asm__ volatile("":::"memory"); /* reorder protect */ oldcpl = ci->ci_cpl; - ci->ci_cpl = newcpl; - if (ci->ci_ipending & ~newcpl) - do_pending_int(); - __asm__ volatile("":::"memory"); + + splx(newcpl); return (oldcpl); } void -splx(int newcpl) +ppc_dflt_splx(int newcpl) { struct cpu_info *ci = curcpu(); - __asm__ volatile("":::"memory"); /* reorder protect */ ci->ci_cpl = newcpl; - if (ci->ci_ipending & ~newcpl) + + if (ci->ci_ipending & ppc_smask[newcpl]) do_pending_int(); - __asm__ volatile("":::"memory"); +} + +struct ppc_intr_func ppc_intr_func = +{ + ppc_dflt_splraise, + ppc_dflt_spllower, + ppc_dflt_splx +}; + +char * +ppc_intr_typename(int type) +{ + switch (type) { + case IST_NONE : + return ("none"); + case IST_PULSE: + return ("pulsed"); + case IST_EDGE: + return ("edge-triggered"); + case IST_LEVEL: + return ("level-triggered"); + default: + return ("unknown"); + } } diff --git a/sys/arch/powerpc/powerpc/mutex.S b/sys/arch/powerpc/powerpc/mutex.S index 34185e4f3be..5ec8f84462e 100644 --- a/sys/arch/powerpc/powerpc/mutex.S +++ b/sys/arch/powerpc/powerpc/mutex.S @@ -1,4 +1,4 @@ -/* $OpenBSD: mutex.S,v 1.3 2007/05/31 23:50:25 drahn Exp $ */ +/* $OpenBSD: mutex.S,v 1.4 2008/09/18 03:56:25 drahn Exp $ */ /* * Copyright (c) 2007 Dale Rahn @@ -22,7 +22,11 @@ #include <machine/asm.h> /* XXX */ +#ifdef USERLAND +#define GET_CPUINFO(r) mr r, %r4 +#else #define GET_CPUINFO(r) mfsprg r,0 +#endif ENTRY(mtx_init) li %r5,0 @@ -32,44 +36,44 @@ ENTRY(mtx_init) blr +/* + * mtx_enter(struct mutex *mtx[%r3]) + */ ENTRY(mtx_enter) - stwu %r1,-32(%r1) # reserve stack + stwu %r1,-16(%r1) # reserve stack mflr %r0 - stw %r0,36(%r1) # save return address + stw %r0,20(%r1) # save return address .L_retry: + stw %r3, 12(%r1) + lwz %r3,MTX_WANTIPL(%r3) # load new ipl + bl _C_LABEL(splraise) + mr %r7, %r3 GET_CPUINFO(%r4) - lwz %r5,MTX_WANTIPL(%r3) # load new ipl - lis %r6,_C_LABEL(imask)@ha # convert into cpl - slwi %r5,%r5,2 - addi %r5,%r5,_C_LABEL(imask)@l - lwzx %r5,%r5,%r6 - lwz %r7,CI_CPL(%r4) # load current cpl - or %r6,%r5,%r7 # raise cpl - stw %r6,CI_CPL(%r4) # store new cpl + lwz %r3,12(%r1) li %r5,MTX_OWNER # load offset constant lwarx %r6,%r5,%r3 # load reserve owner cmpwi 0,%r6,0 # test owner == 0 beq+ 0,.L_mutex_free # if owner == 0 branch free -.L_mutex_locked: #ifdef DIAGNOSTIC cmpl 0,%r4,%r6 beq- .L_mutex_selflocked #endif - stw %r3,28(%r1) # save mtx during lcsplx - la %r4,28(%r1) +.L_mutex_busy: + stw %r3,12(%r1) # save mtx during lcsplx + la %r4,12(%r1) stwcx. %r3,0,%r4 # unreserve owner mr %r3,%r7 # move old cpl to arg0 bl _C_LABEL(lcsplx) # call splx on old cpl - lwz %r3,28(%r1) + lwz %r3,12(%r1) b .L_retry .L_mutex_free: stwcx. %r4,%r5,%r3 # old owner was 0 cond store - bne- .L_mutex_locked # branch if reserve cancelled + bne- .L_mutex_busy # branch if reserve cancelled stw %r7,MTX_OLDCPL(%r3) # save old ipl - lwz %r0,36(%r1) # load return address + lwz %r0,20(%r1) # load return address mtlr %r0 - addi %r1,%r1,32 # restore stack + addi %r1,%r1,16 # restore stack blr #ifdef DIAGNOSTIC @@ -91,14 +95,10 @@ ENTRY(mtx_leave) beq- .L_mutex_notlocked #endif li %r4,0 - lwz %r5,MTX_OLDCPL(%r3) + lwz % r5,MTX_OLDCPL(%r3) stw %r4,MTX_OLDCPL(%r3) stw %r4,MTX_OWNER(%r3) - GET_CPUINFO(%r4) mr %r3,%r5 - lwz %r5,CI_CPL(%r4) - cmpl 0,%r3,%r5 - beq 1f b _C_LABEL(lcsplx) 1: blr |