diff options
author | Mark Kettenis <kettenis@cvs.openbsd.org> | 2006-02-03 10:21:19 +0000 |
---|---|---|
committer | Mark Kettenis <kettenis@cvs.openbsd.org> | 2006-02-03 10:21:19 +0000 |
commit | fd001f021fbd9743150c7c45334dae0e15284284 (patch) | |
tree | 602b4745eab1f6fee6ae12c12509c275830e103d /sys | |
parent | 5f1ec3a81438a46bbb187f6be65eb519a11d6e24 (diff) |
Implement interrupt mapping according to the OFW Interrupt Mapping recommended
practice.
tested by many, ok drahn@
Diffstat (limited to 'sys')
-rw-r--r-- | sys/arch/macppc/pci/mpcpcibus.c | 77 |
1 files changed, 46 insertions, 31 deletions
diff --git a/sys/arch/macppc/pci/mpcpcibus.c b/sys/arch/macppc/pci/mpcpcibus.c index 976d52a45fe..feff1c96e7c 100644 --- a/sys/arch/macppc/pci/mpcpcibus.c +++ b/sys/arch/macppc/pci/mpcpcibus.c @@ -1,4 +1,4 @@ -/* $OpenBSD: mpcpcibus.c,v 1.28 2005/12/16 21:39:42 miod Exp $ */ +/* $OpenBSD: mpcpcibus.c,v 1.29 2006/02/03 10:21:18 kettenis Exp $ */ /* * Copyright (c) 1997 Per Fogelstrom @@ -505,57 +505,72 @@ mpcpcibrattach(struct device *parent, struct device *self, void *aux) (((x) & OFW_PCI_PHYS_HI_FUNCTIONMASK) >> OFW_PCI_PHYS_HI_FUNCTIONSHIFT) /* - * Find PCI IRQ from OF + * Find PCI IRQ from OF. */ int find_node_intr(int parent, u_int32_t *addr, u_int32_t *intr) { - int iparent, len, mlen, n_mlen; + int iparent, len, mlen, alen, ilen; int match, i, step; u_int32_t map[144], *mp, *mp1; u_int32_t imask[8], maskedaddr[8]; + u_int32_t address_cells, interrupt_cells, mask_cells; len = OF_getprop(parent, "interrupt-map", map, sizeof(map)); mlen = OF_getprop(parent, "interrupt-map-mask", imask, sizeof(imask)); + alen = OF_getprop(parent, "#address-cells", + &address_cells, sizeof(address_cells)); + ilen = OF_getprop(parent, "#interrupt-cells", + &interrupt_cells, sizeof(interrupt_cells)); - if ((len == -1) || (mlen == -1)) + if (len == -1 || mlen == -1 || alen == -1 || ilen == -1) goto nomap; - n_mlen = mlen/sizeof(u_int32_t); - for (i = 0; i < n_mlen; i++) + mask_cells = address_cells + interrupt_cells; + if (mask_cells != (mlen / sizeof(u_int32_t))) + goto nomap; + for (i = 0; i < mask_cells; i++) maskedaddr[i] = addr[i] & imask[i]; - mp = map; - /* calculate step size of interrupt-map - * -- assumes that iparent will be same for all nodes + /* interrupt-map is formatted as follows + * int * #address-cells, int * #interrupt-cells, int, int, int + * eg + * address-cells = 3 + * interrupt-cells = 1 + * 00001000 00000000 00000000 00000000 ff911258 00000034 00000001 + * 00001800 00000000 00000000 00000000 ff911258 00000035 00000001 + * 00002000 00000000 00000000 00000000 ff911258 00000036 00000001 + * | address cells | | intr | |parent| | irq | |edge/level| + * | cells| | interrupt cells | + * | of parent | + * or at least something close to that. */ - iparent = mp[n_mlen]; - step = 0; - for (i = (n_mlen)+1; i < len; i++) - if (mp[i] == iparent) { - step = i - (n_mlen); - break; - } - if (step == 0) { - /* unable to determine step size */ - printf("find_node_intr unable to find step size\n"); - return -1; - } + mp = map; while (len > mlen) { - match = bcmp(maskedaddr, mp, mlen); - mp1 = mp + n_mlen; + mp1 = mp + mask_cells; + + iparent = *mp1; + alen = OF_getprop(iparent, "#address-cells", + &address_cells, sizeof(address_cells)); + if (alen == -1) + address_cells = 0; + ilen = OF_getprop(iparent, "#interrupt-cells", + &interrupt_cells, sizeof(interrupt_cells)); + if (ilen == -1) + goto nomap; + + step = mask_cells + 1 + address_cells + interrupt_cells; + match = bcmp(maskedaddr, mp, mlen); if (match == 0) { - /* multiple irqs? */ - if (step == 9) { - /* pci-pci bridge */ - iparent = *mp1; - /* recurse with new 'addr' */ - return find_node_intr(iparent, &mp1[1], intr); - } else + if (OF_getprop(iparent, "interrupt-controller", + NULL, 0) == 0) { *intr = mp1[1]; - return 1; + return 1; + } + /* Recurse with new 'addr'. */ + return find_node_intr(iparent, &mp1[1], intr); } len -= step * sizeof(u_int32_t); mp += step; |