summaryrefslogtreecommitdiff
path: root/sys/arch/macppc/dev/macintr.c
diff options
context:
space:
mode:
Diffstat (limited to 'sys/arch/macppc/dev/macintr.c')
-rw-r--r--sys/arch/macppc/dev/macintr.c550
1 files changed, 310 insertions, 240 deletions
diff --git a/sys/arch/macppc/dev/macintr.c b/sys/arch/macppc/dev/macintr.c
index e327b99c2c1..73235f28e77 100644
--- a/sys/arch/macppc/dev/macintr.c
+++ b/sys/arch/macppc/dev/macintr.c
@@ -1,7 +1,6 @@
-/* $OpenBSD: macintr.c,v 1.34 2008/09/18 03:56:25 drahn Exp $ */
+/* $OpenBSD: macintr.c,v 1.35 2008/11/21 17:35:52 deraadt 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.
@@ -59,18 +58,26 @@
#define ICU_LEN 64
#define LEGAL_IRQ(x) ((x >= 0) && (x < ICU_LEN))
-int macintr_ienable_l[IPL_NUM], macintr_ienable_h[IPL_NUM];
-int macintr_pri_share[IPL_NUM];
-
-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);
+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);
extern u_int32_t *heathrow_FCR;
+#define HWIRQ_MAX 27
+#define HWIRQ_MASK 0x0fffffff
+
#define INT_STATE_REG0 (interrupt_reg + 0x20)
#define INT_ENABLE_REG0 (interrupt_reg + 0x24)
#define INT_CLEAR_REG0 (interrupt_reg + 0x28)
@@ -88,8 +95,6 @@ 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),
@@ -135,84 +140,31 @@ 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;
-
-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_collect_preconf_intr(void);
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 = macintr_do_pending_int;
+ pending_int_f = mac_intr_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");
}
@@ -257,19 +209,11 @@ macintr_prog_button (void *arg)
return 1;
}
-void
-macintr_setipl(int ipl)
+static int
+fakeintr(void *arg)
{
- 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]]);
- out32rb(INT_ENABLE_REG0, macintr_ienable_l[macintr_pri_share[ipl]]);
- ppc_intr_enable(s);
+ return 0;
}
/*
@@ -279,13 +223,19 @@ void *
macintr_establish(void * lcv, int irq, int type, int level,
int (*ih_fun)(void *), void *ih_arg, char *name)
{
- struct cpu_info *ci = curcpu();
- struct intrq *iq;
- struct intrhand *ih;
- int s;
+ struct intrhand **p, *q, *ih;
+ static struct intrhand fakehand;
+
+ fakehand.ih_next = NULL;
+ fakehand.ih_fun = fakeintr;
#if 0
-printf("macintr_establish, hI %d L %d %s", irq, level, ppc_intr_typename(type));
+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);
#endif
/* no point in sleeping unless someone can free memory. */
@@ -296,40 +246,51 @@ printf("macintr_establish, hI %d L %d %s", irq, level, ppc_intr_typename(type));
if (!LEGAL_IRQ(irq) || type == IST_NONE)
panic("intr_establish: bogus irq or type");
- iq = &macintr_handler[irq];
- switch (iq->iq_ist) {
+ switch (m_intrtype[irq]) {
case IST_NONE:
- iq->iq_ist = type;
+ m_intrtype[irq] = type;
break;
case IST_EDGE:
case IST_LEVEL:
- if (type == iq->iq_ist)
+ if (type == m_intrtype[irq])
break;
case IST_PULSE:
if (type != IST_NONE)
panic("intr_establish: can't share %s with %s",
- ppc_intr_typename(iq->iq_ist),
- ppc_intr_typename(type));
+ intr_typename(m_intrtype[irq]),
+ intr_typename(type));
break;
}
- 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);
+ /*
+ * 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)
+ ;
/*
- * Append handler to end of list
+ * 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.
*/
- s = ppc_intr_disable();
+ fakehand.ih_level = level;
+ *p = &fakehand;
- TAILQ_INSERT_TAIL(&iq->iq_list, ih, ih_list);
- macintr_calc_mask();
+ intr_calculatemasks();
- macintr_setipl(ci->ci_cpl);
- ppc_intr_enable(s);
+ /*
+ * 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_intr);
+ *p = ih;
return (ih);
}
@@ -340,93 +301,195 @@ printf("macintr_establish, hI %d L %d %s", irq, level, ppc_intr_typename(type));
void
macintr_disestablish(void *lcp, void *arg)
{
- struct cpu_info *ci = curcpu();
struct intrhand *ih = arg;
int irq = ih->ih_irq;
- int s;
- struct intrq *iq;
+ struct intrhand **p, *q;
if (!LEGAL_IRQ(irq))
panic("intr_disestablish: bogus irq");
/*
* Remove the handler from the chain.
+ * This is O(n^2), too.
*/
-
- iq = &macintr_handler[irq];
- s = ppc_intr_disable();
-
- TAILQ_REMOVE(&iq->iq_list, ih, ih_list);
- macintr_calc_mask();
-
- macintr_setipl(ci->ci_cpl);
- ppc_intr_enable(s);
+ 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);
- if (TAILQ_EMPTY(&iq->iq_list))
- iq->iq_ist = IST_NONE;
+ intr_calculatemasks();
+
+ if (m_intrhand[irq] == NULL)
+ m_intrtype[irq] = IST_NONE;
}
+
+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
+ }
+}
/*
* 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.
*/
-void
-macintr_calc_mask()
+static void
+intr_calculatemasks()
{
- int irq;
- struct intrhand *ih;
- int i;
+ int irq, level;
+ struct intrhand *q;
- for (i = IPL_NONE; i < IPL_NUM; i++) {
- macintr_pri_share[i] = i;
+ /* 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;
+ }
+
+ /* 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;
}
+ /*
+ * 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++) {
- 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;
+ 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;
}
+ imen_m = ~irqs;
+ enable_irq(~imen_m);
+ }
+}
+static void
+enable_irq(int x)
+{
+ int state0, state1, v;
+ int irq;
- iq->iq_ipl = maxipl;
+ x &= HWIRQ_MASK; /* XXX Higher bits are software interrupts. */
- if (maxipl == IPL_NONE) {
- minipl = IPL_NONE; /* Interrupt not enabled */
- } else {
- for (i = minipl; i <= maxipl; i++)
- macintr_pri_share[i] = i;
- }
+ 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);
- /* 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));
+ out32rb(INT_ENABLE_REG0, state0);
+}
+
+int m_virq_inited = 0;
+
+/*
+ * Map 64 irqs into 32 (bits).
+ */
+static int
+mapirq(int irq)
+{
+ int v;
+ int i;
+
+ if (m_virq_inited == 0) {
+ m_virq_max = 0;
+ for (i = 0; i < ICU_LEN; i++) {
+ m_virq[i] = 0;
}
+ 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
- for (i = 0; i < IPL_NUM; i++)
- printf("imask[%d] %x %x\n", i, macintr_ienable_l[i],
- macintr_ienable_h[i]);
+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;
}
/*
@@ -436,136 +499,143 @@ 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 */
- irq = macintr_read_irq();
- while (irq != 255) {
- iq = &macintr_handler[irq];
- macintr_setipl(iq->iq_ipl);
+ int_state = read_irq();
+ if (int_state == 0)
+ goto out;
+
+start:
+ irq = 31 - cntlzw(int_state);
+
+ o_imen = imen_m;
+ r_imen = 1 << irq;
- TAILQ_FOREACH(ih, &iq->iq_list, ih_list) {
- ppc_intr_enable(1);
+ 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) {
if ((*ih->ih_fun)(ih->ih_arg))
ih->ih_count.ec_count++;
- (void)ppc_intr_disable();
+ ih = ih->ih_next;
}
- macintr_eoi(irq);
- macintr_setipl(pcpl);
uvmexp.intrs++;
-
- irq = macintr_read_irq();
}
+ int_state &= ~r_imen;
+ if (int_state)
+ goto start;
- ppc_intr_enable(1);
+out:
splx(pcpl); /* Process pendings. */
}
void
-macintr_do_pending_int()
+mac_intr_do_pending_int()
{
struct cpu_info *ci = curcpu();
- int pcpl = ci->ci_cpl; /* XXX */
- int s, s2;
- s = ppc_intr_disable();
- if (ci->ci_iactive & CI_IACTIVE_PROCESSING_SOFT) {
- ppc_intr_enable(s);
+ 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 */
+ 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;
+ }
}
- atomic_setbits_int(&ci->ci_iactive, CI_IACTIVE_PROCESSING_SOFT);
+
+ /*out32rb(INT_ENABLE_REG, ~imen_m);*/
do {
- 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_CLOCK) & ~pcpl) {
+ ci->ci_ipending &= ~SINT_CLOCK;
+ softclock();
}
- if((ci->ci_ipending & SI_TO_IRQBIT(SI_SOFTNET)) && (pcpl < IPL_SOFTNET)) {
+ if((ci->ci_ipending & SINT_NET) & ~pcpl) {
extern int netisr;
int pisr;
- ci->ci_ipending &= ~SI_TO_IRQBIT(SI_SOFTNET);
+ ci->ci_ipending &= ~SINT_NET;
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 & 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;
+ if((ci->ci_ipending & SINT_TTY) & ~pcpl) {
+ ci->ci_ipending &= ~SINT_TTY;
+ softtty();
}
- } while (ci->ci_ipending & ppc_smask[pcpl]);
- macintr_setipl(pcpl);
- atomic_clearbits_int(&ci->ci_iactive, CI_IACTIVE_PROCESSING_SOFT);
+ } 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;
}
-void
-macintr_eoi(int irq)
+static int
+read_irq()
{
- u_int32_t state0, state1;
+ int rv = 0;
+ int state0, state1, p;
+ int state0save, state1save;
- if (irq < 32) {
- state0 = 1 << irq;
+ state0 = in32rb(INT_STATE_REG0);
+ if (state0)
out32rb(INT_CLEAR_REG0, state0);
- } else {
- if (heathrow_FCR) { /* has heathrow? */
- state1 = 1 << (irq - 32);
- out32rb(INT_CLEAR_REG1, state1);
- }
+ state0save = state0;
+ while (state0) {
+ p = 31 - cntlzw(state0);
+ rv |= 1 << m_virq[p];
+ state0 &= ~(1 << p);
}
-}
-
-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;
- 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 (state1)
+ out32rb(INT_CLEAR_REG1, state1);
+ state1save = state1;
+ while (state1) {
+ p = 31 - cntlzw(state1);
+ rv |= 1 << m_virq[p + 32];
+ state1 &= ~(1 << p);
}
- return 255;
+#if 0
+printf("mac_intr int_stat 0:%x 1:%x\n", state0save, state1save);
+#endif
+
+ /* 1 << 0 is invalid. */
+ return rv & ~1;
}