Subject: Old World interrupts again
To: netbsd-macppc macppc <port-macppc@netbsd.org>
From: Michael Lorenz <macallan@netbsd.org>
List: port-macppc
Date: 09/24/2006 18:45:16
--Apple-Mail-3-442090206
Content-Type: multipart/mixed; boundary=Apple-Mail-2-442090171
--Apple-Mail-2-442090171
Content-Transfer-Encoding: 7bit
Content-Type: text/plain;
charset=US-ASCII;
format=flowed
Hello,
since I'm hacking the PB3400c I ran into various issues, one of them
was that the built-in tlp didn't work, apparently because the interrupt
handler was never called.
Turned out the issue is a lot deeper than it looks like:
- OF lies about the IRQ pin used - claims 27 but uses 60
- the PB3400c has two interrupt controllers, our current code finds
only one and guess which one the tlp is hooked up to.
So what I did is to frob the tlp's IRQ line to 60 in pci_machdep.c when
finding a 2nd ohare interrupt controller and rewrite half of extintr.c
to support the following configurations:
- single PIC with 32 lines - Grand Central, ohare etc.
- single PIC with 64 lines - Heathrow
- two PICs with 32 lines each, cascaded - 2x ohare
other configurations can be easily added.
The patch is still a bit rough but it worked well for a couple days on
a beige G3 ( which has a Heathrow PIC ) and said PB3400c.
If nobody finds something wrong with the principle I'm going to commit.
have fun
Michael
--Apple-Mail-2-442090171
Content-Transfer-Encoding: 7bit
Content-Type: application/octet-stream;
x-unix-mode=0644;
name="macpps_irqs.patch"
Content-Disposition: attachment;
filename=macpps_irqs.patch
Index: pci/pci_machdep.c
===================================================================
RCS file: /cvsroot/src/sys/arch/macppc/pci/pci_machdep.c,v
retrieving revision 1.31
diff -u -w -r1.31 pci_machdep.c
--- pci/pci_machdep.c 24 Sep 2006 19:17:56 -0000 1.31
+++ pci/pci_machdep.c 24 Sep 2006 22:43:32 -0000
@@ -381,6 +381,19 @@
if (len <= 0)
continue;
}
+
+ /*
+ * check if we're on a PowerBook 3400, if so we need to frob
+ * the built-in ethernet controller's IRQ to 60
+ */
+ if (tag == 0x80006800) {
+
+ if (OF_finddevice("/bandit/pci106b,7") > 0) {
+ irqs[0] = 60;
+ printf("\nohare: frobbing tlp IRQ to 60");
+ }
+ }
+
intr = pci_conf_read(pc, tag, PCI_INTERRUPT_REG);
intr &= ~PCI_INTERRUPT_LINE_MASK;
intr |= irqs[0] & PCI_INTERRUPT_LINE_MASK;
Index: macppc/extintr.c
===================================================================
RCS file: /cvsroot/src/sys/arch/macppc/macppc/extintr.c,v
retrieving revision 1.58
diff -u -w -r1.58 extintr.c
--- macppc/extintr.c 14 Aug 2006 11:17:59 -0000 1.58
+++ macppc/extintr.c 24 Sep 2006 22:43:33 -0000
@@ -101,18 +101,28 @@
#define HWIRQ_MAX (NIRQ - 4 - 1)
#define HWIRQ_MASK 0x0fffffff
-void intr_calculatemasks __P((void));
-int fakeintr __P((void *));
+//#define LPIC_DEBUG
-static inline uint32_t gc_read_irq __P((void));
-static inline int mapirq __P((int));
-static void gc_enable_irq __P((int));
-static void gc_reenable_irq __P((int));
-static void gc_disable_irq __P((int));
-
-static void do_pending_int __P((void));
-static void ext_intr_openpic __P((void));
-static void legacy_int_init __P((void));
+void intr_calculatemasks(void);
+int fakeintr(void *);
+
+static inline uint32_t gc_read_irq(void);
+static inline int mapirq(uint32_t);
+static void gc_enable_irq(uint32_t);
+static void gc_reenable_irq(uint32_t);
+static void gc_disable_irq(uint32_t);
+
+static int lpic_add(uint32_t, uint32_t, uint32_t, uint32_t, uint32_t);
+static uint32_t lpic_read_events(int);
+static void lpic_enable_irq(int, uint32_t);
+static void lpic_disable_irq(int, uint32_t);
+static void lpic_disable_all(void);
+static void lpic_clear_all(void);
+static void lpic_dump(int);
+
+static void do_pending_int(void);
+static void ext_intr_openpic(void);
+static void legacy_int_init(void);
int imask[NIPL];
@@ -125,42 +135,158 @@
struct evcnt is_ev;
char is_source[16];
};
-
static struct intrsource intrsources[NIRQ];
static u_char virq[ICU_LEN];
static int virq_max = 0;
-static u_char *obio_base;
-
extern u_int *heathrow_FCR;
-#define IPI_VECTOR 64
+typedef struct __lpic_regs {
+ uint32_t lpic_state;
+ uint32_t lpic_enable;
+ uint32_t lpic_clear;
+ uint32_t lpic_level;
+ uint32_t lpic_level_mask;
+ uint32_t lpic_enable_mask;
+ uint32_t lpic_cascade_mask;
+} lpic_regs;
-#define interrupt_reg (obio_base + 0x10)
+static lpic_regs lpics[4];
-#define INT_STATE_REG_H (interrupt_reg + 0x00)
-#define INT_ENABLE_REG_H (interrupt_reg + 0x04)
-#define INT_CLEAR_REG_H (interrupt_reg + 0x08)
-#define INT_LEVEL_REG_H (interrupt_reg + 0x0c)
-#define INT_STATE_REG_L (interrupt_reg + 0x10)
-#define INT_ENABLE_REG_L (interrupt_reg + 0x14)
-#define INT_CLEAR_REG_L (interrupt_reg + 0x18)
-#define INT_LEVEL_REG_L (interrupt_reg + 0x1c)
+#define IPI_VECTOR 64
+
+#define INT_STATE_REG_H 0x10
+#define INT_ENABLE_REG_H 0x14
+#define INT_CLEAR_REG_H 0x18
+#define INT_LEVEL_REG_H 0x1c
+#define INT_STATE_REG_L 0x20
+#define INT_ENABLE_REG_L 0x24
+#define INT_CLEAR_REG_L 0x28
+#define INT_LEVEL_REG_L 0x2c
#define have_openpic (openpic_base != NULL)
-static uint32_t intr_level_mask;
+static uint32_t num_hw_irqs = 0;
+static uint32_t num_lpics = 0;
#define INT_LEVEL_MASK_GC 0x3ff00000
-#define INT_LEVEL_MASK_OHARE 0x1ff00000
+#define INT_LEVEL_MASK_OHARE 0x1ff00000 /* also for Heathrow */
+
+static int
+lpic_add(uint32_t state, uint32_t enable, uint32_t clear, uint32_t level,
+ uint32_t mask)
+{
+ lpic_regs *this;
+
+ if (num_lpics >= 4)
+ return -1;
+ this = &lpics[num_lpics];
+ this->lpic_state = state;
+ this->lpic_enable = enable;
+ this->lpic_clear = clear;
+ this->lpic_level = level;
+ this->lpic_level_mask = mask;
+ this->lpic_cascade_mask = 0;
+ this->lpic_enable_mask = 0;
+ num_lpics++;
+ num_hw_irqs += 32;
+ return 0;
+}
+
+static inline uint32_t
+lpic_read_events(int num)
+{
+ lpic_regs *this = &lpics[num];
+ uint32_t irqs, levels, events;
+
+ irqs = in32rb(this->lpic_state);
+ events = irqs & ~this->lpic_level_mask;
+
+ levels = in32rb(this->lpic_level) & this->lpic_enable_mask;
+ events |= levels & this->lpic_level_mask;
+
+ /* Clear any interrupts that we've read and all level ones */
+ out32rb(this->lpic_clear, events | this->lpic_level_mask);
+
+ /* don't return cascade interrupts */
+ events &= ~this->lpic_cascade_mask;
+#ifdef LPIC_DEBUG
+ printf("lpic %d: %08x %08x %08x ", num, irqs, levels, events);
+#endif
+ return events;
+}
+
+static inline void
+lpic_enable_irq(int num, uint32_t irq)
+{
+ lpic_regs *this = &lpics[num];
+
+#ifdef DIAGNOSTIC
+ if (irq > 31)
+ printf("bogus irq %d\n", irq);
+#endif
+ this->lpic_enable_mask |= (1 << irq) | this->lpic_cascade_mask;
+ out32rb(this->lpic_enable, this->lpic_enable_mask); /* unmask */
+#ifdef LPIC_DEBUG
+ printf("enabling %d %d %08x ", num, irq, mask);
+#endif
+}
+
+static void
+lpic_disable_irq(int num, uint32_t irq)
+{
+ lpic_regs *this = &lpics[num];
+
+#ifdef DIAGNOSTIC
+ if (irq > 31)
+ printf("bogus irq %d\n", irq);
+#endif
+ this->lpic_enable_mask &= ~(1 << irq);
+ out32rb(this->lpic_enable, this->lpic_enable_mask); /* unmask */
+}
+
+#ifdef DIAGNOSTIC
+static void
+lpic_dump(int num)
+{
+ lpic_regs *this = &lpics[num];
+
+ printf("lpic %d:\n", num);
+ printf("state: %08x\n", in32rb(this->lpic_state));
+ printf("enable: %08x\n", in32rb(this->lpic_enable));
+ printf("clear: %08x\n", in32rb(this->lpic_clear));
+ printf("level: %08x\n", in32rb(this->lpic_level));
+ printf("level_mask: %08x\n", this->lpic_level_mask);
+ printf("cascade: %08x\n", this->lpic_cascade_mask);
+}
+#endif
+
+static void
+lpic_disable_all()
+{
+ int i;
+
+ for (i = 0; i < num_lpics; i++) {
+ lpics[i].lpic_enable_mask = 0;
+ out32rb(lpics[i].lpic_enable, 0);
+ }
+}
+
+static void
+lpic_clear_all()
+{
+ int i;
+
+ for (i = 0; i < num_lpics; i++)
+ out32rb(lpics[i].lpic_clear, 0xffffffff);
+}
/*
* Map 64 irqs into 32 (bits).
*/
int
-mapirq(irq)
- int irq;
+mapirq(uint32_t irq)
{
int v;
@@ -177,7 +303,9 @@
intrsources[v].is_hwirq = irq;
virq[irq] = v;
-
+#ifdef LPIC_DEBUG
+ printf("mapping irq %d to virq %d\n", irq, v);
+#endif
return v;
}
@@ -185,123 +313,70 @@
gc_read_irq()
{
uint32_t rv = 0;
- uint32_t int_state;
- uint32_t events, e;
- uint32_t levels;
-
- /* Get the internal interrupts */
- int_state = in32rb(INT_STATE_REG_L);
- events = int_state & ~intr_level_mask;
-
- /* Get the enabled external interrupts */
- levels = in32rb(INT_LEVEL_REG_L) & in32rb(INT_ENABLE_REG_L);
- events = events | (levels & intr_level_mask);
-
- /* Clear any interrupts that we've read */
- out32rb(INT_CLEAR_REG_L, events | int_state);
- while (events) {
- e = 31 - cntlzw(events);
- rv |= 1 << virq[e];
- events &= ~(1 << e);
- }
-
- /* If we're on Heathrow, repeat for the secondary */
- if (heathrow_FCR) {
- events = in32rb(INT_STATE_REG_H);
-
- if (events)
- out32rb(INT_CLEAR_REG_H, events);
+ uint32_t e;
+ uint32_t events;
+ int i;
+ for (i = 0; i < num_lpics; i++) {
+ events = lpic_read_events(i);
while (events) {
e = 31 - cntlzw(events);
- rv |= 1 << virq[e + 32];
+ rv |= 1 << virq[e + (i << 5)];
events &= ~(1 << e);
}
}
+#ifdef LPIC_DEBUG
+ printf("rv: %08x ", rv);
+#endif
/* 1 << 0 is invalid because virq will always be at least 1. */
return rv & ~1;
}
void
-gc_reenable_irq(irq)
- int irq;
+gc_reenable_irq(uint32_t irq)
{
+ lpic_regs *this;
struct cpu_info *ci = curcpu();
- u_int levels, vi;
- u_int mask, irqbit;
+ uint32_t levels, irqbit, vi;
+ uint32_t pic;
- if (irq < 32) {
- mask = in32rb(INT_ENABLE_REG_L);
- irqbit = 1 << irq;
-
- /* Already enabled? */
- if (mask & irqbit)
- return;
+ pic = irq >> 5;
+ this = &lpics[pic];
- mask |= irqbit;
- out32rb(INT_ENABLE_REG_L, mask); /* unmask */
-
- /* look for lost level interrupts */
- levels = in32rb(INT_LEVEL_REG_L);
- if (levels & irqbit) {
- vi = virq[irq]; /* map to virtual irq */
- ci->ci_ipending |= (1<<vi);
- out32rb(INT_CLEAR_REG_L, irqbit);
- }
- } else {
- mask = in32rb(INT_ENABLE_REG_H);
- irqbit = 1 << (irq - 32);
+ irqbit = 1 << (irq & 0x1f);
/* Already enabled? */
- if (mask & irqbit)
+ if (this->lpic_enable_mask & irqbit)
return;
- mask |= irqbit;
- out32rb(INT_ENABLE_REG_H, mask); /* unmask */
+ lpic_enable_irq(pic, irq & 0x1f);
/* look for lost level interrupts */
- levels = in32rb(INT_LEVEL_REG_H);
+ levels = in32rb(this->lpic_level);
if (levels & irqbit) {
vi = virq[irq]; /* map to virtual irq */
ci->ci_ipending |= (1<<vi);
- out32rb(INT_CLEAR_REG_H, irqbit);
- }
+ out32rb(this->lpic_clear, irqbit);
}
}
void
-gc_enable_irq(irq)
- int irq;
+gc_enable_irq(uint32_t irq)
{
- u_int mask;
+ uint32_t pic;
- if (irq < 32) {
- mask = in32rb(INT_ENABLE_REG_L);
- mask |= 1 << irq;
- out32rb(INT_ENABLE_REG_L, mask); /* unmask */
- } else {
- mask = in32rb(INT_ENABLE_REG_H);
- mask |= 1 << (irq - 32);
- out32rb(INT_ENABLE_REG_H, mask); /* unmask */
- }
+ pic = (irq >> 5);
+ lpic_enable_irq(pic, irq & 0x1f);
}
void
-gc_disable_irq(irq)
- int irq;
+gc_disable_irq(uint32_t irq)
{
- u_int x;
+ uint32_t pic;
- if (irq < 32) {
- x = in32rb(INT_ENABLE_REG_L);
- x &= ~(1 << irq);
- out32rb(INT_ENABLE_REG_L, x);
- } else {
- x = in32rb(INT_ENABLE_REG_H);
- x &= ~(1 << (irq - 32));
- out32rb(INT_ENABLE_REG_H, x);
- }
+ pic = (irq >> 5);
+ lpic_disable_irq(pic, irq & 0x1f);
}
/*
@@ -406,9 +481,7 @@
openpic_enable_irq(is->is_hwirq, is->is_type);
}
} else {
- out32rb(INT_ENABLE_REG_L, 0);
- if (heathrow_FCR)
- out32rb(INT_ENABLE_REG_H, 0);
+ lpic_disable_all();
for (irq = 0, is = intrsources; irq < NIRQ; irq++, is++) {
if (is->is_hand)
gc_enable_irq(is->is_hwirq);
@@ -584,9 +657,8 @@
struct intrsource *is;
struct intrhand *ih;
uint32_t int_state;
-#if DIAGNOSTIC
- uint32_t oint_state;
- int spincount=0;
+#ifdef DIAGNOSTIC
+ int intr_spin = 0;
#endif
#ifdef MULTIPROCESSOR
@@ -601,38 +673,18 @@
pcpl = ci->ci_cpl;
msr = mfmsr();
-#if DIAGNOSTIC
- oint_state = 0;
-#endif
while ((int_state = gc_read_irq()) != 0) {
-#if DIAGNOSTIC
- /*
- * Paranoia....
- */
- if (int_state == oint_state) {
- if (spincount++ > 0x80) {
- uint32_t stuck;
- const char *comma="";
-
- stuck = int_state;
- printf("Disabling stuck interrupt(s): ");
- while (stuck) {
- irq = 31 - cntlzw(int_state);
- r_imen = 1 << irq;
- is = &intrsources[irq];
- printf("%s%d", comma, is->is_hwirq);
- gc_disable_irq(is->is_hwirq);
- ci->ci_ipending &= ~r_imen;
- stuck &= ~r_imen;
- comma = ", ";
+#ifdef DIAGNOSTIC
+ if (intr_spin > 100) {
+ int i;
+ printf("spinning in ext_intr! %08x\n", int_state);
+ for (i = 0; i < num_lpics; i++) {
+ lpic_dump(i);
}
- printf("\n");
- spincount = 0;
+ panic("stuck interrupt");
continue;
}
- }
- oint_state = int_state;
#endif
#ifdef MULTIPROCESSOR
@@ -688,6 +740,7 @@
uvmexp.intrs++;
is->is_ev.ev_count++;
}
+ intr_spin++;
}
mtmsr(msr | PSL_EE);
@@ -1010,33 +1063,72 @@
oea_install_extint(ext_intr_openpic);
}
+#define GC_OBIO_BASE 0xf3000000
+
void
legacy_int_init()
{
- int ohare;
+ uint32_t reg[5];
+ uint32_t obio_base;
+ int ohare, ohare2;
- out32rb(INT_ENABLE_REG_L, 0); /* disable all intr. */
- out32rb(INT_CLEAR_REG_L, 0xffffffff); /* clear pending intr. */
- intr_level_mask = INT_LEVEL_MASK_GC;
- if (heathrow_FCR) {
- out32rb(INT_ENABLE_REG_H, 0);
- out32rb(INT_CLEAR_REG_H, 0xffffffff);
- intr_level_mask = INT_LEVEL_MASK_OHARE;
- }
ohare = OF_finddevice("/bandit/ohare");
if (ohare != -1) {
- intr_level_mask = INT_LEVEL_MASK_OHARE;
+ if (OF_getprop(ohare, "assigned-addresses", reg, sizeof(reg))
+ == 20) {
+ obio_base = reg[2];
+ printf("found OHare at 0x%08x\n", obio_base);
+ lpic_add(obio_base + INT_STATE_REG_L,
+ obio_base + INT_ENABLE_REG_L,
+ obio_base + INT_CLEAR_REG_L,
+ obio_base + INT_LEVEL_REG_L,
+ INT_LEVEL_MASK_OHARE);
}
+ } else {
+ /* must be Grand Central */
+ printf("found Grand Central at 0x%08x\n", GC_OBIO_BASE);
+ lpic_add(GC_OBIO_BASE + INT_STATE_REG_L,
+ GC_OBIO_BASE + INT_ENABLE_REG_L,
+ GC_OBIO_BASE + INT_CLEAR_REG_L,
+ GC_OBIO_BASE + INT_LEVEL_REG_L,
+ INT_LEVEL_MASK_GC);
+ return;
+ }
+
+ ohare2 = OF_finddevice("/bandit/pci106b,7");
+ if (ohare2 != -1) {
+ uint32_t irq;
+
+ if (OF_getprop(ohare2, "assigned-addresses", reg, sizeof(reg))
+ < 20)
+ goto next;
+ if (OF_getprop(ohare2, "AAPL,interrupts", &irq, sizeof(irq))
+ < 4)
+ goto next;
+ obio_base = reg[2];
+ printf("found OHare2 at 0x%08x, irq %d\n", obio_base, irq);
+ lpic_add(obio_base + INT_STATE_REG_L,
+ obio_base + INT_ENABLE_REG_L,
+ obio_base + INT_CLEAR_REG_L,
+ obio_base + INT_LEVEL_REG_L,
+ INT_LEVEL_MASK_OHARE);
+ lpics[0].lpic_cascade_mask = 1 << irq;
+ }
+next:
+ lpic_disable_all();
+ lpic_clear_all();
+
+ printf("Handling %d interrupts\n", num_hw_irqs);
oea_install_extint(ext_intr);
}
#define HEATHROW_FCR_OFFSET 0x38 /* XXX should not here */
-#define GC_OBIO_BASE 0xf3000000
void
init_interrupt()
{
+ uint32_t obio_base;
int chosen __attribute__((unused));
int mac_io, reg[5];
int32_t ictlr;
@@ -1054,7 +1146,6 @@
/*
* No mac-io. Assume Grand-Central or OHare.
*/
- obio_base = (void *)GC_OBIO_BASE;
legacy_int_init();
return;
}
@@ -1062,8 +1153,7 @@
if (OF_getprop(mac_io, "assigned-addresses", reg, sizeof(reg)) < 20)
goto failed;
- obio_base = (void *)reg[2];
- printf("obio_base: %p\n", obio_base);
+ obio_base = reg[2];
heathrow_FCR = (void *)(obio_base + HEATHROW_FCR_OFFSET);
@@ -1096,7 +1186,24 @@
/*
* Not an open-pic. Must be a Heathrow (compatible).
*/
- legacy_int_init();
+ printf("Found Heathrow at 0x%08x\n", obio_base);
+ lpic_add(obio_base + INT_STATE_REG_L,
+ obio_base + INT_ENABLE_REG_L,
+ obio_base + INT_CLEAR_REG_L,
+ obio_base + INT_LEVEL_REG_L,
+ INT_LEVEL_MASK_OHARE);
+
+ lpic_add(obio_base + INT_STATE_REG_H,
+ obio_base + INT_ENABLE_REG_H,
+ obio_base + INT_CLEAR_REG_H,
+ obio_base + INT_LEVEL_REG_H,
+ 0);
+
+ lpic_disable_all();
+ lpic_clear_all();
+ oea_install_extint(ext_intr);
+ printf("Handling %d interrupts\n", num_hw_irqs);
+
return;
} else {
/*
@@ -1113,8 +1220,8 @@
openpic_base = (void *)(obio_base + reg[0]);
#elif defined (PPC_OEA64_BRIDGE)
/* There is no BAT mapping the OBIO region, use PTEs */
- openpic_base = (void *)mapiodev((u_int32_t)obio_base + (u_int32_t)reg[0],
- 0x40000);
+ openpic_base = (void *)mapiodev((u_int32_t)obio_base +
+ (u_int32_t)reg[0], 0x40000);
#endif
printf("%s: found OpenPIC @ pa 0x%08x, %p\n", __FUNCTION__,
(u_int32_t)obio_base + (u_int32_t)reg[0], openpic_base);
--Apple-Mail-2-442090171--
--Apple-Mail-3-442090206
content-type: application/pgp-signature; x-mac-type=70674453;
name=PGP.sig
content-description: This is a digitally signed message part
content-disposition: inline; filename=PGP.sig
content-transfer-encoding: 7bit
-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.2.4 (Darwin)
iQEVAwUBRRcKfMpnzkX8Yg2nAQKl9QgAtc3u2sB3Uj7Gzw5dEn4PE1+I+8hSInXP
MlZB5mUZqDj3dS952PoVKGAdP5DpmTnr+KsqqjIz5+mnEtj57raAkNPRl1ITPOlq
pU9VskSBsi/j3F3VrLRcPD5t+5cZZ1xAi5bOhYugEhe592zl+btIfNvrWbOgwI3g
cDwaeQr0e+fN6OAvzraxaVgyejuaz6Q/v1wnKYHP3fMMhTQcgAqyTqwoKOSyo6KC
2w43TCoLmydDZ8xEhU1WIZkFl1pgKeInUkKPM181RKa0AzoFK9QxgVKi6nQpmfip
v3B2XJVC/57ntLBL9dce/wgo1aBtOHk7NO7QB4+smqpnBqDo8cpuUQ==
=mYlT
-----END PGP SIGNATURE-----
--Apple-Mail-3-442090206--