summaryrefslogtreecommitdiffstats
path: root/sys/sparc64
diff options
context:
space:
mode:
authortmm <tmm@FreeBSD.org>2002-03-24 02:11:06 +0000
committertmm <tmm@FreeBSD.org>2002-03-24 02:11:06 +0000
commit521e80e7004d21d31b68b84f1038865ad8513c5b (patch)
tree38a7ef1596d0873fd81c61d83dd216b08b60752f /sys/sparc64
parent587f263c4d8db119551ac4d9518abee9498ebd04 (diff)
downloadFreeBSD-src-521e80e7004d21d31b68b84f1038865ad8513c5b.zip
FreeBSD-src-521e80e7004d21d31b68b84f1038865ad8513c5b.tar.gz
Make the OpenFirmware interrupt mapping code more generic, to reduce
the bus-dependent code and to be able to support more systems. The core of the new code is mostly obtained from NetBSD. Kluge the interrupt routing methods of the psycho and apb drivers so that an intline of 0 can be handled for now; real routing is still not possible (all intline registers are preinitialized instead); this will require a sparc64-specific adaption of the driver for generic PCI-PCI bridges with a custom routing method to work right.
Diffstat (limited to 'sys/sparc64')
-rw-r--r--sys/sparc64/ebus/ebus.c80
-rw-r--r--sys/sparc64/ebus/ebusvar.h11
-rw-r--r--sys/sparc64/include/ofw_bus.h6
-rw-r--r--sys/sparc64/isa/isa.c41
-rw-r--r--sys/sparc64/isa/ofw_isa.c13
-rw-r--r--sys/sparc64/pci/apb.c26
-rw-r--r--sys/sparc64/pci/ofw_pci.c116
-rw-r--r--sys/sparc64/pci/ofw_pci.h15
-rw-r--r--sys/sparc64/pci/psycho.c33
-rw-r--r--sys/sparc64/pci/psychovar.h3
-rw-r--r--sys/sparc64/sparc64/ofw_bus.c226
11 files changed, 288 insertions, 282 deletions
diff --git a/sys/sparc64/ebus/ebus.c b/sys/sparc64/ebus/ebus.c
index f28343a..69a12da 100644
--- a/sys/sparc64/ebus/ebus.c
+++ b/sys/sparc64/ebus/ebus.c
@@ -54,13 +54,13 @@
#include <sys/kernel.h>
#include <sys/malloc.h>
+#include <ofw/openfirm.h>
+#include <ofw/ofw_pci.h>
+
#include <machine/bus.h>
#include <machine/ofw_bus.h>
#include <machine/resource.h>
-#include <ofw/openfirm.h>
-#include <ofw/ofw_pci.h>
-
#include <pci/pcireg.h>
#include <pci/pcivar.h>
@@ -99,18 +99,7 @@ struct ebus_softc {
struct isa_ranges *sc_range;
- struct ofw_pci_register *sc_reg;
-
- int sc_imap_type;
-
- struct isa_imap *sc_ebus_imap;
- struct isa_imap_msk sc_ebus_imapmsk;
-
- struct ofw_pci_imap *sc_pci_imap;
- struct ofw_pci_imap_msk sc_pci_imapmsk;
-
int sc_nrange;
- int sc_nreg;
int sc_nimap;
};
@@ -127,7 +116,6 @@ static struct ebus_devinfo *ebus_setup_dinfo(struct ebus_softc *,
phandle_t, char *);
static void ebus_destroy_dinfo(struct ebus_devinfo *);
static int ebus_print_res(struct ebus_devinfo *);
-static int ebus_map_intr(struct ebus_softc *, int, struct isa_regs *, int);
static device_method_t ebus_methods[] = {
/* Device interface */
@@ -199,42 +187,10 @@ ebus_probe(device_t dev)
sc = device_get_softc(dev);
sc->sc_node = node;
- /*
- * Fill in our softc with information from the prom.
- * There are two possible cases how interrupt mapping needs to be
- * handled:
- * - if the ebus node has an interrupt-map properties, the interrut
- * numbers in child nodes can be mapped using lookups in this map,
- * using the registers of the child node in question to find the
- * map entry
- * - if it does not have such a properties, the interrupts are mapped
- * in the next higher interrupt map (PCI in our case), using the
- * interrupt number of the child, but the registers of the ebus
- * node, to find the mapping.
- */
- sc->sc_imap_type = EBUS_IT_EBUS;
- sc->sc_nimap = OF_getprop_alloc(node, "interrupt-map",
- sizeof(*sc->sc_ebus_imap), (void **)&sc->sc_ebus_imap);
- if (sc->sc_nimap == -1) {
- sc->sc_nimap = ofw_pci_find_imap(node, &sc->sc_pci_imap,
- &sc->sc_pci_imapmsk);
- if (sc->sc_nimap == -1)
- panic("ebus_probe: no interrupt map found");
- sc->sc_imap_type = EBUS_IT_PCI;
- } else {
- if (OF_getprop(node, "interrupt-map-mask",
- &sc->sc_ebus_imapmsk, sizeof(sc->sc_ebus_imapmsk)) == -1) {
- panic("ebus_probe: could not get ebus "
- "interrupt-map-mask");
- }
- }
-
sc->sc_nrange = OF_getprop_alloc(node, "ranges",
sizeof(*sc->sc_range), (void **)&sc->sc_range);
- sc->sc_nreg = OF_getprop_alloc(node, "reg",
- sizeof(*sc->sc_reg), (void **)&sc->sc_reg);
- if (sc->sc_nrange == -1 || sc->sc_nreg == -1)
- panic("ebus_attach: could not get ranges/reg property");
+ if (sc->sc_nrange == -1)
+ panic("ebus_attach: could not get ranges property");
/*
* now attach all our children
@@ -411,9 +367,9 @@ ebus_setup_dinfo(struct ebus_softc *sc, phandle_t node, char *name)
{
struct ebus_devinfo *edi;
struct isa_regs *reg;
- u_int32_t *intrs;
+ u_int32_t *intrs, intr;
u_int64_t start;
- int nreg, nintr, i, intr;
+ int nreg, nintr, i;
edi = malloc(sizeof(*edi), M_DEVBUF, M_ZERO | M_WAITOK);
if (edi == NULL)
@@ -442,10 +398,11 @@ ebus_setup_dinfo(struct ebus_softc *sc, phandle_t node, char *name)
nintr = OF_getprop_alloc(node, "interrupts", sizeof(*intrs),
(void **)&intrs);
for (i = 0; i < nintr; i++) {
- intr = ebus_map_intr(sc, intrs[i], reg, nreg);
- if (intr == -1)
+ intr = ofw_bus_route_intr(node, intrs[i]);
+ if (intr == ORIR_NOTFOUND) {
panic("ebus_setup_dinfo: could not map ebus "
"interrupt %d", intrs[i]);
+ }
resource_list_add(&edi->edi_rl, SYS_RES_IRQ, i,
intr, intr, 1);
}
@@ -479,20 +436,3 @@ ebus_print_res(struct ebus_devinfo *edi)
"%ld");
return (retval);
}
-
-static int
-ebus_map_intr(struct ebus_softc *sc, int intr, struct isa_regs *regs,
- int nregs)
-{
- int rv;
-
- if (sc->sc_imap_type == EBUS_IT_PCI) {
- rv = ofw_pci_route_intr2(intr, sc->sc_reg, sc->sc_pci_imap,
- sc->sc_nimap, &sc->sc_pci_imapmsk);
- if (rv == 255)
- return (-1);
- return (rv);
- }
- return (ofw_isa_map_intr(sc->sc_ebus_imap, sc->sc_nimap,
- &sc->sc_ebus_imapmsk, intr, regs, nregs));
-}
diff --git a/sys/sparc64/ebus/ebusvar.h b/sys/sparc64/ebus/ebusvar.h
index 69cecc7..c8fd7c6 100644
--- a/sys/sparc64/ebus/ebusvar.h
+++ b/sys/sparc64/ebus/ebusvar.h
@@ -35,17 +35,6 @@
#ifndef _SPARC64_EBUS_EBUSVAR_H_
#define _SPARC64_EBUS_EBUSVAR_H_
-/*
- * Interrupt map type (for the sc_imap_type softc member):
- * If the ebus node has an interrupt map, this is set to EBUS_IT_EBUS (and the
- * relevant sotfc members are initialized with the EBUS types), otherwise,
- * it is EBUS_IT_PCI, in which case the standard PCI functions are used to
- * map the interrupt (this is needed because the the maps and masks are
- * different).
- */
-#define EBUS_IT_EBUS 1
-#define EBUS_IT_PCI 2
-
enum ebus_device_ivars {
EBUS_IVAR_COMPAT,
EBUS_IVAR_NAME,
diff --git a/sys/sparc64/include/ofw_bus.h b/sys/sparc64/include/ofw_bus.h
index 55f15f9..2b0670e 100644
--- a/sys/sparc64/include/ofw_bus.h
+++ b/sys/sparc64/include/ofw_bus.h
@@ -28,7 +28,9 @@
#ifndef _MACHINE_OFW_BUS_H_
#define _MACHINE_OFW_BUS_H_
-u_int32_t ofw_bus_route_intr(int intr, void *regs, int regsz, int physz,
- int nregs, void *imap, int nimap, void *imapmsk, char *regm);
+#define ORIP_NOINT -1
+#define ORIR_NOTFOUND 0xffffffff
+
+u_int32_t ofw_bus_route_intr(phandle_t, int);
#endif /* !_MACHINE_OFW_BUS_H_ */
diff --git a/sys/sparc64/isa/isa.c b/sys/sparc64/isa/isa.c
index dd915cf..37fa3d4 100644
--- a/sys/sparc64/isa/isa.c
+++ b/sys/sparc64/isa/isa.c
@@ -51,6 +51,7 @@
#include <ofw/openfirm.h>
#include <machine/intr_machdep.h>
+#include <machine/ofw_bus.h>
#include <machine/resource.h>
#include <sparc64/pci/ofw_pci.h>
@@ -71,11 +72,8 @@ u_int64_t isa_mem_limit;
device_t isa_bus_device;
-static struct ofw_pci_register isab_reg;
-static struct ofw_pci_imap *isab_imap;
-static int isab_nimap;
-static struct ofw_pci_imap_msk isab_imap_msk;
static phandle_t isab_node;
+static u_int32_t isa_ino[8];
/*
* XXX: This is really partly partly PCI-specific, but unfortunately is
@@ -92,8 +90,6 @@ static phandle_t isab_node;
static int isa_route_intr_res(device_t, u_long, u_long);
-static int isa_ino[8];
-
intrmask_t
isa_irq_pending(void)
{
@@ -103,7 +99,7 @@ isa_irq_pending(void)
/* XXX: Is this correct? */
for (i = 7, pending = 0; i >= 0; i--) {
pending <<= 1;
- if (isa_ino[i] != 255) {
+ if (isa_ino[i] != ORIR_NOTFOUND) {
pending |= (SPARCBUS_INTR_PENDING(isa_bus_device,
isa_ino[i]) == 0) ? 0 : 1;
}
@@ -116,28 +112,30 @@ isa_init(device_t dev)
{
device_t bridge;
phandle_t node;
+ u_int32_t ino;
struct isa_ranges *br;
int nbr, i;
/* The parent of the bus must be a PCI-ISA bridge. */
bridge = device_get_parent(dev);
- isab_node = ofw_pci_find_node(pci_get_bus(bridge), pci_get_slot(bridge),
- pci_get_function(bridge));
- if (OF_getprop(isab_node, "reg", &isab_reg, sizeof(isab_reg)) < 0)
- panic("isa_init: cannot get bridge reg property");
+ isab_node = ofw_pci_node(bridge);
nbr = OF_getprop_alloc(isab_node, "ranges", sizeof(*br), (void **)&br);
if (nbr <= 0)
panic("isa_init: cannot get bridge range property");
- node = isab_node;
- isab_nimap = ofw_pci_find_imap(node, &isab_imap, &isab_imap_msk);
- if (isab_nimap == -1)
- panic("isa_init: could not find interrupt-map");
- for (i = 0; i < 8; i++) {
- isa_ino[i] = ofw_pci_route_intr2(i, &isab_reg, isab_imap,
- isab_nimap, &isab_imap_msk);
+ /*
+ * This is really a bad kluge; however, it is needed to provide
+ * isa_irq_pending().
+ */
+ for (i = 0; i < 8; i++)
+ isa_ino[i] = ORIR_NOTFOUND;
+ for (node = OF_child(isab_node); node != 0; node = OF_peer(node)) {
+ if (OF_getprop(node, "interrupts", &ino, sizeof(ino)) == -1)
+ continue;
+ if (ino > 7)
+ panic("isa_init: XXX: ino too large");
+ isa_ino[ino] = ofw_bus_route_intr(node, ino);
}
-
for (nbr -= 1; nbr >= 0; nbr--) {
switch(ISAB_RANGE_SPACE(br + nbr)) {
case ISAR_SPACE_IO:
@@ -168,8 +166,9 @@ isa_route_intr_res(device_t bus, u_long start, u_long end)
panic("isa_route_intr_res: allocation of interrupt range not "
"supported (0x%lx - 0x%lx)", start, end);
}
- res = ofw_pci_route_intr2(start, &isab_reg, isab_imap, isab_nimap,
- &isab_imap_msk);
+ if (start > 7)
+ panic("isa_route_intr_res: start out of isa range");
+ res = isa_ino[start];
if (res == 255)
device_printf(bus, "could not map interrupt %d\n", res);
return (res);
diff --git a/sys/sparc64/isa/ofw_isa.c b/sys/sparc64/isa/ofw_isa.c
index 7cf41c4..a87665e 100644
--- a/sys/sparc64/isa/ofw_isa.c
+++ b/sys/sparc64/isa/ofw_isa.c
@@ -48,19 +48,6 @@
#include <sparc64/isa/ofw_isa.h>
#include <sparc64/pci/ofw_pci.h>
-/*
- * This applies only for an ISA/EBus with an own interrupt-map property.
- */
-int
-ofw_isa_map_intr(struct isa_imap *imap, int nimap, struct isa_imap_msk *imapmsk,
- int intr, struct isa_regs *regs, int nregs)
-{
- char regm[8];
-
- return (ofw_bus_route_intr(intr, regs, sizeof(*regs), 8, nregs,
- imap, nimap, imapmsk, regm));
-}
-
/* XXX: this only supports PCI as parent bus right now. */
int
ofw_isa_map_iorange(struct isa_ranges *range, int nrange, u_long *start,
diff --git a/sys/sparc64/pci/apb.c b/sys/sparc64/pci/apb.c
index 52a5270..4f01630 100644
--- a/sys/sparc64/pci/apb.c
+++ b/sys/sparc64/pci/apb.c
@@ -46,8 +46,13 @@
#include <sys/malloc.h>
#include <sys/bus.h>
+#include <dev/ofw/openfirm.h>
+#include <dev/ofw/ofw_pci.h>
+
#include <machine/resource.h>
+#include <sparc64/pci/ofw_pci.h>
+
#include <pci/pcivar.h>
#include <pci/pcireg.h>
@@ -343,12 +348,27 @@ apb_write_config(device_t dev, int b, int s, int f, int reg, u_int32_t val,
}
/*
- * Route an interrupt across a PCI bridge - the APB does not route interrupts,
- * and routing of interrupts that are not preinitialized is not supported yet.
+ * Route an interrupt across a PCI bridge - we need to rely on the firmware
+ * here.
*/
static int
apb_route_interrupt(device_t pcib, device_t dev, int pin)
{
- panic("apb_route_interrupt");
+ /*
+ * XXX: ugly loathsome hack:
+ * We can't use ofw_pci_route_intr() here; the device passed may be
+ * the one of a bridge, so the original device can't be recovered.
+ *
+ * We need to use the firmware to route interrupts, however it has
+ * no interface which could be used to interpret intpins; instead,
+ * all assignments are done by device.
+ *
+ * The MI pci code will try to reroute interrupts of 0, although they
+ * are correct; all other interrupts are preinitialized, so if we
+ * get here, the intline is either 0 (so return 0), or we hit a
+ * device which was not preinitialized (e.g. hotplugged stuff), in
+ * which case we are lost.
+ */
+ return (0);
}
diff --git a/sys/sparc64/pci/ofw_pci.c b/sys/sparc64/pci/ofw_pci.c
index f56efaf..636c9f9 100644
--- a/sys/sparc64/pci/ofw_pci.c
+++ b/sys/sparc64/pci/ofw_pci.c
@@ -50,66 +50,15 @@
#include "pcib_if.h"
-/*
- * Find the interrupt-map properties for a node. This might not be a property
- * of the parent, because there may be bridges in between, so go up through the
- * tree to find it.
- * This seems to be only needed for PCI systems, so it has not been moved to
- * ofw_bus.c
- */
-int
-ofw_pci_find_imap(phandle_t node, struct ofw_pci_imap **imap,
- struct ofw_pci_imap_msk *imapmsk)
-{
- int nimap;
-
- nimap = -1;
- while ((node = OF_parent(node)) != 0) {
- if ((nimap = OF_getprop_alloc(node, "interrupt-map",
- sizeof(**imap), (void **)imap)) == -1 ||
- OF_getprop(node, "interrupt-map-mask",
- imapmsk, sizeof(*imapmsk)) == -1) {
- if (*imap != NULL) {
- free(*imap, M_OFWPROP);
- *imap = NULL;
- }
- nimap = -1;
- } else
- break;
- }
- return (nimap);
-}
-
-/*
- * Route an interrupt using the firmware nodes. Returns 255 for interrupts
- * that cannot be routed (suitable for the PCI code).
- */
-int
-ofw_pci_route_intr2(int intr, struct ofw_pci_register *pcir,
- struct ofw_pci_imap *imap, int nimap, struct ofw_pci_imap_msk *imapmsk)
-{
- char regm[12];
- int cintr;
-
- cintr = ofw_bus_route_intr(intr, pcir, sizeof(*pcir), 12, 1, imap,
- nimap, imapmsk, regm);
- if (cintr == -1)
- return (255);
- else
- return (cintr);
-}
-
-int
-ofw_pci_route_intr(phandle_t node, struct ofw_pci_register *pcir,
- struct ofw_pci_imap *intrmap, int nintrmap,
- struct ofw_pci_imap_msk *intrmapmsk)
+u_int32_t
+ofw_pci_route_intr(phandle_t node)
{
- int intr;
+ u_int32_t rv;
- if (OF_getprop(node, "interrupts", &intr, sizeof(intr)) == -1)
+ rv = ofw_bus_route_intr(node, ORIP_NOINT);
+ if (rv == ORIR_NOTFOUND)
return (255);
-
- return (ofw_pci_route_intr2(intr, pcir, intrmap, nintrmap, intrmapmsk));
+ return (rv);
}
#define OFW_PCI_PCIBUS "pci"
@@ -119,10 +68,8 @@ ofw_pci_route_intr(phandle_t node, struct ofw_pci_register *pcir,
* of attached devices using firmware information.
*/
void
-ofw_pci_init_intr(device_t dev, phandle_t bus, struct ofw_pci_imap *intrmap,
- int nintrmap, struct ofw_pci_imap_msk *intrmapmsk)
+ofw_pci_init_intr(device_t dev, phandle_t bus)
{
- struct ofw_pci_imap_msk lintrmapmsk;
struct ofw_pci_register pcir;
phandle_t node;
char type[32];
@@ -146,40 +93,19 @@ ofw_pci_init_intr(device_t dev, phandle_t bus, struct ofw_pci_imap *intrmap,
* deep, so recursion is feasible.
*/
#ifdef OFW_PCI_DEBUG
- device_printf(dev, __func__": descending to "
- "subordinate PCI bus\n");
+ device_printf(dev, "%s: descending to "
+ "subordinate PCI bus\n", __func__);
#endif
- ofw_pci_init_intr(dev, node, NULL, 0, NULL);
+ ofw_pci_init_intr(dev, node);
} else {
if (OF_getprop(node, "reg", &pcir, sizeof(pcir)) == -1)
panic("ofw_pci_route_intr: OF_getprop failed");
- /*
- * If we didn't get interrupt map properties passed,
- * try to find them now. On some systems, buses that
- * have no non-bridge children have no such properties,
- * so only try to find them at need.
- */
- if (intrmap == NULL) {
- nintrmap = OF_getprop_alloc(bus,
- "interrupt-map", sizeof(*intrmap),
- (void **)&intrmap);
- if (nintrmap == -1 ||
- OF_getprop(bus, "interrupt-map-mask",
- &lintrmapmsk, sizeof(lintrmapmsk)) == -1) {
- printf("ofw_pci_init_intr: could not get "
- "interrupt map properties\n");
- if (nintrmap != -1)
- free(intrmap, M_OFWPROP);
- return;
- }
- intrmapmsk = &lintrmapmsk;
- freemap = 1;
- }
- if ((intr = ofw_pci_route_intr(node, &pcir, intrmap,
- nintrmap, intrmapmsk)) != 255) {
+
+ if ((intr = ofw_pci_route_intr(node)) != 255) {
#ifdef OFW_PCI_DEBUG
- device_printf(dev, __func__": mapping intr for "
+ device_printf(dev, "%s: mapping intr for "
"%d/%d/%d to %d (preset was %d)\n",
+ __func__,
OFW_PCI_PHYS_HI_BUS(pcir.phys_hi),
OFW_PCI_PHYS_HI_DEVICE(pcir.phys_hi),
OFW_PCI_PHYS_HI_FUNCTION(pcir.phys_hi),
@@ -189,7 +115,6 @@ ofw_pci_init_intr(device_t dev, phandle_t bus, struct ofw_pci_imap *intrmap,
OFW_PCI_PHYS_HI_DEVICE(pcir.phys_hi),
OFW_PCI_PHYS_HI_FUNCTION(pcir.phys_hi),
PCIR_INTLINE, 1));
-
#endif /* OFW_PCI_DEBUG */
PCIB_WRITE_CONFIG(dev,
OFW_PCI_PHYS_HI_BUS(pcir.phys_hi),
@@ -198,8 +123,9 @@ ofw_pci_init_intr(device_t dev, phandle_t bus, struct ofw_pci_imap *intrmap,
PCIR_INTLINE, intr, 1);
} else {
#ifdef OFW_PCI_DEBUG
- device_printf(dev, __func__": no interrupt "
+ device_printf(dev, "%s: no interrupt "
"mapping found for %d/%d/%d (preset %d)\n",
+ __func__,
OFW_PCI_PHYS_HI_BUS(pcir.phys_hi),
OFW_PCI_PHYS_HI_DEVICE(pcir.phys_hi),
OFW_PCI_PHYS_HI_FUNCTION(pcir.phys_hi),
@@ -218,8 +144,6 @@ ofw_pci_init_intr(device_t dev, phandle_t bus, struct ofw_pci_imap *intrmap,
}
}
} while ((node = OF_peer(node)) != 0);
- if (freemap)
- free(intrmap, M_OFWPROP);
}
phandle_t
@@ -268,3 +192,11 @@ ofw_pci_find_node(int bus, int slot, int func)
}
return (0);
}
+
+phandle_t
+ofw_pci_node(device_t dev)
+{
+
+ return (ofw_pci_find_node(pci_get_bus(dev), pci_get_slot(dev),
+ pci_get_function(dev)));
+}
diff --git a/sys/sparc64/pci/ofw_pci.h b/sys/sparc64/pci/ofw_pci.h
index dc453be..862df92 100644
--- a/sys/sparc64/pci/ofw_pci.h
+++ b/sys/sparc64/pci/ofw_pci.h
@@ -56,18 +56,9 @@ struct ofw_pci_imap_msk {
u_int32_t intr;
};
-int ofw_pci_find_imap(phandle_t, struct ofw_pci_imap **,
- struct ofw_pci_imap_msk *);
-int ofw_pci_route_intr2(int, struct ofw_pci_register *,
- struct ofw_pci_imap *, int, struct ofw_pci_imap_msk *);
-int ofw_pci_route_intr(phandle_t, struct ofw_pci_register *,
- struct ofw_pci_imap *, int, struct ofw_pci_imap_msk *);
-void ofw_pci_init_intr(device_t, phandle_t, struct ofw_pci_imap *, int,
- struct ofw_pci_imap_msk *);
+u_int32_t ofw_pci_route_intr(phandle_t);
+void ofw_pci_init_intr(device_t, phandle_t);
phandle_t ofw_pci_find_node(int, int, int);
-int ofw_pci_dev_iterate_node(device_t, phandle_t, uintptr_t *, int, int *,
- int *, uintptr_t *, uintptr_t *);
-int ofw_pci_dev_iterate(device_t, uintptr_t *, int, int *, int *, uintptr_t *,
- uintptr_t *);
+phandle_t ofw_pci_node(device_t);
#endif /* ! _SPARC64_PCI_OFW_PCI_H_ */
diff --git a/sys/sparc64/pci/psycho.c b/sys/sparc64/pci/psycho.c
index 54edab5..bd26e84 100644
--- a/sys/sparc64/pci/psycho.c
+++ b/sys/sparc64/pci/psycho.c
@@ -478,16 +478,6 @@ psycho_attach(device_t dev)
/* XXX: register as root dma tag (kluge). */
sparc64_root_dma_tag = sc->sc_dmat;
- if ((sc->sc_nintrmap = OF_getprop_alloc(sc->sc_node, "interrupt-map",
- sizeof(*sc->sc_intrmap), (void **)&sc->sc_intrmap)) == -1 ||
- OF_getprop(sc->sc_node, "interrupt-map-mask", &sc->sc_intrmapmsk,
- sizeof(sc->sc_intrmapmsk)) == -1) {
- if (sc->sc_intrmap != NULL) {
- free(sc->sc_intrmap, M_OFWPROP);
- sc->sc_intrmap = NULL;
- }
- }
-
/* Register the softc, this is needed for paired psychos. */
if (psycho_ndevs < sizeof(psycho_softcs) / sizeof(psycho_softcs[0]))
psycho_softcs[psycho_ndevs] = sc;
@@ -617,8 +607,7 @@ psycho_attach(device_t dev)
* at least on some models, and we probably shouldn't trust that
* the firmware uses the same model as this driver if it does.
*/
- ofw_pci_init_intr(dev, sc->sc_node, sc->sc_intrmap, sc->sc_nintrmap,
- &sc->sc_intrmapmsk);
+ ofw_pci_init_intr(dev, sc->sc_node);
device_add_child(dev, "pci", device_get_unit(dev));
return (bus_generic_attach(dev));
@@ -966,14 +955,24 @@ psycho_write_config(device_t dev, u_int bus, u_int slot, u_int func,
static int
psycho_route_interrupt(device_t bus, device_t dev, int pin)
{
+ int intline;
/*
- * Since we preinitialize all interrupt line registers, this should not
- * happen for any built-in device.
- * Devices on bridges that route interrupts cannot work now - the
- * interrupt pin mappings are not known from the firmware...
+ * XXX: ugly loathsome hack:
+ * We can't use ofw_pci_route_intr() here; the device passed may be
+ * the one of a bridge, so the original device can't be recovered.
+ *
+ * We need to use the firmware to route interrupts, however it has
+ * no interface which could be used to interpret intpins; instead,
+ * all assignments are done by device.
+ *
+ * The MI pci code will try to reroute interrupts of 0, although they
+ * are correct; all other interrupts are preinitialized, so if we
+ * get here, the intline is either 0 (so return 0), or we hit a
+ * device which was not preinitialized (e.g. hotplugged stuff), in
+ * which case we are lost.
*/
- panic("psycho_route_interrupt");
+ return (0);
}
static int
diff --git a/sys/sparc64/pci/psychovar.h b/sys/sparc64/pci/psychovar.h
index 25ca9c1..cee7c77 100644
--- a/sys/sparc64/pci/psychovar.h
+++ b/sys/sparc64/pci/psychovar.h
@@ -74,9 +74,6 @@ struct psycho_softc {
*/
struct upa_ranges *sc_range;
int sc_nrange;
- struct ofw_pci_imap *sc_intrmap;
- int sc_nintrmap;
- struct ofw_pci_imap_msk sc_intrmapmsk;
/* our tags */
bus_space_tag_t sc_cfgt;
diff --git a/sys/sparc64/sparc64/ofw_bus.c b/sys/sparc64/sparc64/ofw_bus.c
index 22fb7da..d17fb06 100644
--- a/sys/sparc64/sparc64/ofw_bus.c
+++ b/sys/sparc64/sparc64/ofw_bus.c
@@ -1,3 +1,33 @@
+/*
+ * Copyright (C) 1996 Wolfgang Solfrank.
+ * Copyright (C) 1996 TooLs GmbH.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by TooLs GmbH.
+ * 4. The name of TooLs GmbH may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY TOOLS GMBH ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL TOOLS GMBH BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
/*-
* Copyright (c) 2001 by Thomas Moestl <tmm@FreeBSD.org>.
* All rights reserved.
@@ -22,6 +52,8 @@
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
* USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
+ * from: $NetBSD: ofw_machdep.c,v 1.16 2001/07/20 00:07:14 eeh Exp $
+ *
* $FreeBSD$
*/
@@ -40,51 +72,169 @@
#include <machine/ofw_bus.h>
+/*
+ * Other than in OpenFirmware calls, the size of a bus cell seems to be always
+ * the same.
+ */
+typedef u_int32_t pcell_t;
+
+static int
+ofw_bus_searchprop(phandle_t node, char *propname, void *buf, int buflen)
+{
+ int rv;
+
+ for (; node != 0; node = OF_parent(node)) {
+ if ((rv = OF_getprop(node, propname, buf, buflen)) != -1)
+ return (rv);
+ }
+ return (-1);
+}
+
/*
- * Route an interrupt using the firmware. This takes an interrupt map and mask,
- * as retrieved from the firmware (this must be done by the caller, since it
- * is not bus-independent).
- * regs points to a "reg" property as returned by the firmware. regsz ist the
- * the size of one reg element, physz is the size of the physical address member
- * at the start of each reg (this is matched against the interrupt map).
- * The interrupt map has entries of the size (physsz + 12), the 12 being the
- * size of two u_int32_t that hold the interrupt number to compare against, the
- * node the map belongs to and the interrupt that the child interrupt is mapped
- * to (if the map entry matches).
- * The first nregs registers are checked against the map; in some cases (e.g.
- * PCI), only the first must be checked.
- * The mask consists of a mask wich must be and-ed to the checked physical
- * address part of the ofw reg and to the interrupt number before checking
- * against the map.
- * regm should point to a buffer of physsz size (this is not malloc'ed because
- * malloc cannot be called in all situations).
+ * Map an interrupt using the firmware reg, interrupt-map and
+ * interrupt-map-mask properties.
+ * The interrupt is returned in *result, which is malloc()'ed. The size of
+ * the interrupt specifiaction is returned.
*/
-u_int32_t
-ofw_bus_route_intr(int intr, void *regs, int regsz, int physsz, int nregs,
- void *imap, int nimap, void *imapmsk, char *regm)
+static int
+ofw_bus_find_intr(u_int8_t *intr, int intrsz, u_int8_t *regs, int physsz,
+ u_int8_t *imap, int imapsz, u_int8_t *imapmsk, u_int8_t **result)
{
+ phandle_t parent;
+ char *ref;
u_int8_t *mptr;
- u_int32_t mintr, cintr;
- int r, i;
+ pcell_t pintrsz;
+ int i, rsz, tsz;
- cintr = -1;
- bcopy((u_int8_t *)imapmsk + physsz, &mintr, sizeof(mintr));
- mintr &= intr;
- for (r = 0; r < nregs; r++) {
- for (i = 0; i < physsz; i++) {
- regm[i] = ((u_int8_t *)regs)[r * regsz + i] &
- ((u_int8_t *)imapmsk)[i];
+ rsz = -1;
+ ref = malloc(physsz + intrsz, M_TEMP, M_WAITOK);
+ if (imapmsk != NULL) {
+ for (i = 0; i < physsz; i++)
+ ref[i] = regs[i] & imapmsk[i];
+ for (i = 0; i < intrsz; i++)
+ ref[physsz + i] = intr[i] & imapmsk[physsz + i];
+ } else {
+ bcopy(regs, ref, physsz);
+ bcopy(intr, ref + physsz, intrsz);
+ }
+ mptr = imap;
+ i = imapsz;
+ while (i > 0) {
+ KASSERT(i >= physsz + sizeof(parent),
+ ("ofw_bus_find_intr: truncated map"));
+ bcopy(mptr + physsz + intrsz, &parent, sizeof(parent));
+ if (ofw_bus_searchprop(parent, "#interrupt-cells",
+ &pintrsz, sizeof(pintrsz)) == -1)
+ pintrsz = 1; /* default */
+ pintrsz *= sizeof(pcell_t);
+ KASSERT(i >= physsz + intrsz + sizeof(parent) +
+ pintrsz, ("ofw_bus_find_intr: truncated map"));
+ if (bcmp(ref, mptr, physsz + intrsz) == 0) {
+ *result = malloc(pintrsz, M_OFWPROP, M_WAITOK);
+ bcopy(mptr + physsz + intrsz + sizeof(parent),
+ *result, pintrsz);
+ rsz = pintrsz;
+ break;
}
- for (i = 0; i < nimap; i++) {
- mptr = (u_int8_t *)imap + i * (physsz + 12);
- if (bcmp(regm, mptr, physsz) == 0 &&
- bcmp(&mintr, mptr + physsz, sizeof(mintr)) == 0) {
- bcopy(mptr + physsz + 8, &cintr,
- sizeof(cintr));
- break;
- }
+ tsz = physsz + intrsz + sizeof(phandle_t) + pintrsz;
+ mptr += tsz;
+ i -= tsz;
+ }
+ free(ref, M_TEMP);
+ return (rsz);
+}
+
+/*
+ * Apply the OpenFirmware algorithm for mapping an interrupt. First, the
+ * 'interrupts' and 'reg' properties are retrieved; those are matched against
+ * the interrupt map of the next higher node. If there is no match or no such
+ * propery, we go to the next higher node, using the 'reg' property of the node
+ * that was just processed unusccessfully.
+ * When a match occurs, we continue to search, using the new interrupt
+ * specification that was just found.
+ * When the root node is reached with at least one successful mapping performed,
+ * and the format is right, the interrupt number is returned.
+ *
+ * This should work for all bus systems.
+ */
+u_int32_t
+ofw_bus_route_intr(phandle_t node, int intrp)
+{
+ u_int8_t *reg, *intr, *tintr, *imap, *imapmsk;
+ phandle_t parent;
+ pcell_t addrc, ic;
+ u_int32_t rv;
+ int regsz, tisz, isz, imapsz, found;
+
+ found = 0;
+ reg = imap = imapmsk = NULL;
+ if (intrp == ORIP_NOINT) {
+ isz = OF_getprop_alloc(node, "interrupts", 1, (void **)&intr);
+ if (isz < 0)
+ return (ORIR_NOTFOUND);
+ } else {
+ ic = intrp;
+ isz = sizeof(ic);
+ intr = malloc(isz, M_OFWPROP, M_WAITOK);
+ bcopy(&ic, intr, isz);
+ }
+ /*
+ * Note that apparently, remapping at multiple levels is allowed;
+ * however, this causes problems with EBus at least, and seems to never
+ * be needed, so we disable it for now (*sigh*).
+ */
+ for (parent = OF_parent(node); parent != 0 && !found;
+ parent = OF_parent(node = parent)) {
+ if (reg != NULL)
+ free(reg, M_OFWPROP);
+ regsz = OF_getprop_alloc(node, "reg", 1, (void **)&reg);
+ if (regsz < 0)
+ panic("ofw_bus_route_intr: could not get reg property");
+ imapsz = OF_getprop_alloc(parent, "interrupt-map", 1,
+ (void **)&imap);
+ if (imapsz == -1)
+ continue;
+ if (OF_getprop(parent, "#address-cells", &addrc,
+ sizeof(addrc)) == -1)
+ addrc = 2;
+ addrc *= sizeof(pcell_t);
+ /*
+ * Failures to get the mask are ignored; a full mask is assumed
+ * in this case.
+ */
+ OF_getprop_alloc(parent, "interrupt-map-mask", 1,
+ (void **)&imapmsk);
+ tisz = ofw_bus_find_intr(intr, isz, reg, addrc, imap, imapsz,
+ imapmsk, &tintr);
+ if (tisz != -1) {
+ found = 1;
+ isz = tisz;
+ free(intr, M_OFWPROP);
+ intr = tintr;
}
+ free(imap, M_OFWPROP);
+ if (imapmsk != NULL)
+ free(imapmsk, M_OFWPROP);
+ }
+ if (reg != NULL)
+ free(reg, M_OFWPROP);
+#if 0
+ /*
+ * Obviously there are some boxes that don't require mapping at all,
+ * for example the U30, which has no interrupt maps for children of
+ * the root PCI bus.
+ */
+ if (!found) {
+ if (intrp != ORIP_NOINT)
+ return (ORIR_NOTFOUND);
+ panic("ofw_bus_route_intr: 'interrupts' property, but no "
+ "mapping found");
}
- return (cintr);
+#endif
+ KASSERT(isz == sizeof(u_int32_t),
+ ("ofw_bus_route_intr: bad interrupt spec size %d", isz));
+ bcopy(intr, &rv, sizeof(rv));
+ free(intr, M_OFWPROP);
+ return (rv);
}
OpenPOWER on IntegriCloud