summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--sys/sparc64/ebus/ebus.c2
-rw-r--r--sys/sparc64/include/ofw_bus.h5
-rw-r--r--sys/sparc64/include/ver.h1
-rw-r--r--sys/sparc64/isa/isa.c3
-rw-r--r--sys/sparc64/pci/ofw_pci.c72
-rw-r--r--sys/sparc64/pci/ofw_pci.h7
-rw-r--r--sys/sparc64/pci/psycho.c2
-rw-r--r--sys/sparc64/sparc64/machdep.c5
-rw-r--r--sys/sparc64/sparc64/ofw_bus.c23
9 files changed, 102 insertions, 18 deletions
diff --git a/sys/sparc64/ebus/ebus.c b/sys/sparc64/ebus/ebus.c
index 69a12da..42d78c8 100644
--- a/sys/sparc64/ebus/ebus.c
+++ b/sys/sparc64/ebus/ebus.c
@@ -398,7 +398,7 @@ 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 = ofw_bus_route_intr(node, intrs[i]);
+ intr = ofw_bus_route_intr(node, intrs[i], ofw_pci_orb_callback);
if (intr == ORIR_NOTFOUND) {
panic("ebus_setup_dinfo: could not map ebus "
"interrupt %d", intrs[i]);
diff --git a/sys/sparc64/include/ofw_bus.h b/sys/sparc64/include/ofw_bus.h
index 2b0670e..2f67b24 100644
--- a/sys/sparc64/include/ofw_bus.h
+++ b/sys/sparc64/include/ofw_bus.h
@@ -31,6 +31,9 @@
#define ORIP_NOINT -1
#define ORIR_NOTFOUND 0xffffffff
-u_int32_t ofw_bus_route_intr(phandle_t, int);
+typedef int obr_callback_t(phandle_t, u_int8_t *, int, u_int8_t *, int,
+ u_int8_t **, int *);
+
+u_int32_t ofw_bus_route_intr(phandle_t, int, obr_callback_t *);
#endif /* !_MACHINE_OFW_BUS_H_ */
diff --git a/sys/sparc64/include/ver.h b/sys/sparc64/include/ver.h
index f570a9f..7c73488 100644
--- a/sys/sparc64/include/ver.h
+++ b/sys/sparc64/include/ver.h
@@ -59,6 +59,7 @@
(((ver) & VER_MAXWIN_MASK) >> VER_MAXWIN_SHIFT)
extern int cpu_impl;
+extern char sparc64_model[];
/* Known implementations. */
#define CPU_IMPL_SPARC64 0x01
diff --git a/sys/sparc64/isa/isa.c b/sys/sparc64/isa/isa.c
index 0536db7..ef746c3 100644
--- a/sys/sparc64/isa/isa.c
+++ b/sys/sparc64/isa/isa.c
@@ -133,7 +133,8 @@ isa_init(device_t dev)
continue;
if (ino > 7)
panic("isa_init: XXX: ino too large");
- isa_ino[ino] = ofw_bus_route_intr(node, ino);
+ isa_ino[ino] = ofw_bus_route_intr(node, ino,
+ ofw_pci_orb_callback);
}
for (nbr -= 1; nbr >= 0; nbr--) {
diff --git a/sys/sparc64/pci/ofw_pci.c b/sys/sparc64/pci/ofw_pci.c
index d36e730..6d7754b 100644
--- a/sys/sparc64/pci/ofw_pci.c
+++ b/sys/sparc64/pci/ofw_pci.c
@@ -47,6 +47,7 @@
#include <sparc64/pci/ofw_pci.h>
#include <machine/ofw_bus.h>
+#include <machine/ver.h>
#include "pcib_if.h"
@@ -54,16 +55,63 @@ u_int8_t pci_bus_cnt;
phandle_t *pci_bus_map;
int pci_bus_map_sz;
+#define OPQ_NEED_SWIZZLE 1
+static struct ofw_pci_quirk {
+ char *opq_model;
+ int opq_quirks;
+} ofw_pci_quirks[] = {
+ { "SUNW,UltraSPARC-IIi-cEngine", OPQ_NEED_SWIZZLE }
+};
+#define OPQ_NENT (sizeof(ofw_pci_quirks) / sizeof(ofw_pci_quirks[0]))
+
+static int pci_quirks;
+
+#define OFW_PCI_PCIBUS "pci"
#define PCI_BUS_MAP_INC 10
+int
+ofw_pci_orb_callback(phandle_t node, u_int8_t *pintptr, int pintsz,
+ u_int8_t *pregptr, int pregsz, u_int8_t **rintr, int *terminate)
+{
+ struct ofw_pci_register preg;
+ u_int32_t pintr, intr;
+ char type[32];
+
+ if ((pci_quirks & OPQ_NEED_SWIZZLE) != 0 &&
+ pintsz == sizeof(u_int32_t) && pregsz >= sizeof(preg) &&
+ OF_getprop(node, "device_type", type, sizeof(type)) != -1 &&
+ strcmp(type, OFW_PCI_PCIBUS) == 0) {
+ /*
+ * Handle a quirk found on some Netra t1 models: there exist
+ * PCI bridges without interrupt maps, where we apparently must
+ * do the PCI swizzle and continue to map on at the parent.
+ */
+ bcopy(pintptr, &pintr, sizeof(pintr));
+ bcopy(pregptr, &preg, sizeof(preg));
+ intr = (OFW_PCI_PHYS_HI_DEVICE(preg.phys_hi) + pintr) % 4;
+ *rintr = malloc(sizeof(intr), M_OFWPROP, M_WAITOK);
+ bcopy(&intr, *rintr, sizeof(intr));
+ *terminate = 0;
+ return (sizeof(intr));
+ }
+ return (-1);
+}
+
u_int32_t
-ofw_pci_route_intr(phandle_t node)
+ofw_pci_route_intr(phandle_t node, u_int32_t ign)
{
u_int32_t rv;
- rv = ofw_bus_route_intr(node, ORIP_NOINT);
+ rv = ofw_bus_route_intr(node, ORIP_NOINT, ofw_pci_orb_callback);
if (rv == ORIR_NOTFOUND)
return (255);
+ /*
+ * Some machines (notably the SPARCengine Ultra AX) have no mappings
+ * at all, but use complete interrupt vector number including the IGN.
+ * Catch this case and remove the IGN.
+ */
+ if (rv > ign)
+ rv -= ign;
return (rv);
}
@@ -112,7 +160,6 @@ ofw_pci_binit(device_t busdev, struct ofw_pci_bdesc *obd)
PCIR_SUBBUS_1, obd->obd_subbus, 1);
}
-#define OFW_PCI_PCIBUS "pci"
/*
* Walk the PCI bus hierarchy, starting with the root PCI bus and descending
* through bridges, and initialize the interrupt line and latency timer
@@ -120,15 +167,24 @@ ofw_pci_binit(device_t busdev, struct ofw_pci_bdesc *obd)
* as well as the the bus numbers and ranges of the bridges.
*/
void
-ofw_pci_init(device_t dev, phandle_t bushdl, struct ofw_pci_bdesc *obd)
+ofw_pci_init(device_t dev, phandle_t bushdl, u_int32_t ign,
+ struct ofw_pci_bdesc *obd)
{
struct ofw_pci_register pcir;
struct ofw_pci_bdesc subobd, *tobd;
phandle_t node;
char type[32];
- int intr, freemap;
+ int i, intr, freemap;
u_int slot, busno, func, sub, lat;
+ /* Initialize the quirk list. */
+ for (i = 0; i < OPQ_NENT; i++) {
+ if (strcmp(sparc64_model, ofw_pci_quirks[i].opq_model) == 0) {
+ pci_quirks = ofw_pci_quirks[i].opq_quirks;
+ break;
+ }
+ }
+
if ((node = OF_child(bushdl)) == 0)
return;
freemap = 0;
@@ -141,7 +197,7 @@ ofw_pci_init(device_t dev, phandle_t bushdl, struct ofw_pci_bdesc *obd)
else
type[sizeof(type) - 1] = '\0';
if (OF_getprop(node, "reg", &pcir, sizeof(pcir)) == -1)
- panic("ofw_pci_route_intr: OF_getprop failed");
+ panic("ofw_pci_init: OF_getprop failed");
slot = OFW_PCI_PHYS_HI_DEVICE(pcir.phys_hi);
func = OFW_PCI_PHYS_HI_FUNCTION(pcir.phys_hi);
if (strcmp(type, OFW_PCI_PCIBUS) == 0) {
@@ -173,7 +229,7 @@ ofw_pci_init(device_t dev, phandle_t bushdl, struct ofw_pci_bdesc *obd)
device_printf(dev, "%s: descending to "
"subordinate PCI bus\n", __func__);
#endif /* OFW_PCI_DEBUG */
- ofw_pci_init(dev, node, &subobd);
+ ofw_pci_init(dev, node, ign, &subobd);
} else {
/*
* Initialize the latency timer register for
@@ -199,7 +255,7 @@ ofw_pci_init(device_t dev, phandle_t bushdl, struct ofw_pci_bdesc *obd)
}
/* Initialize the intline registers. */
- if ((intr = ofw_pci_route_intr(node)) != 255) {
+ if ((intr = ofw_pci_route_intr(node, ign)) != 255) {
#ifdef OFW_PCI_DEBUG
device_printf(dev, "%s: mapping intr for "
"%d/%d/%d to %d (preset was %d)\n",
diff --git a/sys/sparc64/pci/ofw_pci.h b/sys/sparc64/pci/ofw_pci.h
index 68cf1ec..45bbb43 100644
--- a/sys/sparc64/pci/ofw_pci.h
+++ b/sys/sparc64/pci/ofw_pci.h
@@ -34,6 +34,8 @@
#ifndef _SPARC64_PCI_OFW_PCI_H_
#define _SPARC64_PCI_OFW_PCI_H_
+#include <machine/ofw_bus.h>
+
/* PCI range child spaces. XXX: are these MI? */
#define PCI_CS_CONFIG 0x00
#define PCI_CS_IO 0x01
@@ -69,10 +71,11 @@ struct ofw_pci_bdesc {
struct ofw_pci_bdesc *obd_super;
};
-u_int32_t ofw_pci_route_intr(phandle_t);
+u_int32_t ofw_pci_route_intr(phandle_t, u_int32_t);
+obr_callback_t ofw_pci_orb_callback;
u_int8_t ofw_pci_alloc_busno(phandle_t);
ofw_pci_binit_t ofw_pci_binit;
-void ofw_pci_init(device_t, phandle_t, struct ofw_pci_bdesc *);
+void ofw_pci_init(device_t, phandle_t, u_int32_t, struct ofw_pci_bdesc *);
phandle_t ofw_pci_find_node(int, int, int);
phandle_t ofw_pci_node(device_t);
diff --git a/sys/sparc64/pci/psycho.c b/sys/sparc64/pci/psycho.c
index edf9f11..51fb78f 100644
--- a/sys/sparc64/pci/psycho.c
+++ b/sys/sparc64/pci/psycho.c
@@ -630,7 +630,7 @@ psycho_attach(device_t dev)
* the firmware uses the same model as this driver if it does.
* Additionally, set up the bus numbers and ranges.
*/
- ofw_pci_init(dev, sc->sc_node, &obd);
+ ofw_pci_init(dev, sc->sc_node, sc->sc_ign, &obd);
device_add_child(dev, "pci", device_get_unit(dev));
return (bus_generic_attach(dev));
diff --git a/sys/sparc64/sparc64/machdep.c b/sys/sparc64/sparc64/machdep.c
index a387704..4b2edfc 100644
--- a/sys/sparc64/sparc64/machdep.c
+++ b/sys/sparc64/sparc64/machdep.c
@@ -130,6 +130,8 @@ u_long ofw_tba;
static struct timecounter tick_tc;
+char sparc64_model[32];
+
static timecounter_get_t tick_get_timecount;
void sparc64_init(caddr_t mdp, u_long o1, u_long o2, u_long o3,
ofw_vec_t *vec);
@@ -159,6 +161,7 @@ cpu_startup(void *arg)
tc_init(&tick_tc);
cpu_identify(rdpr(ver), tick_freq, PCPU_GET(cpuid));
+ printf("Model: %s\n", sparc64_model);
vm_ksubmap_init(&kmi);
@@ -348,6 +351,8 @@ sparc64_init(caddr_t mdp, u_long o1, u_long o2, u_long o3, ofw_vec_t *vec)
OF_getprop(PCPU_GET(node), "clock-frequency", &clock, sizeof(clock));
tick_init(clock);
+
+ OF_getprop(root, "name", sparc64_model, sizeof(sparc64_model) - 1);
}
void
diff --git a/sys/sparc64/sparc64/ofw_bus.c b/sys/sparc64/sparc64/ofw_bus.c
index d17fb06..605a347 100644
--- a/sys/sparc64/sparc64/ofw_bus.c
+++ b/sys/sparc64/sparc64/ofw_bus.c
@@ -151,15 +151,16 @@ ofw_bus_find_intr(u_int8_t *intr, int intrsz, u_int8_t *regs, int physsz,
* 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 a match occurs, we should continue to search, using the new interrupt
+ * specification that was just found; this is currently not performed
+ * (see below).
* 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)
+ofw_bus_route_intr(phandle_t node, int intrp, obr_callback_t *cb)
{
u_int8_t *reg, *intr, *tintr, *imap, *imapmsk;
phandle_t parent;
@@ -193,8 +194,22 @@ ofw_bus_route_intr(phandle_t node, int intrp)
panic("ofw_bus_route_intr: could not get reg property");
imapsz = OF_getprop_alloc(parent, "interrupt-map", 1,
(void **)&imap);
- if (imapsz == -1)
+ if (imapsz == -1) {
+ /*
+ * Use the callback to allow caller-specific workarounds
+ * for firmware bugs (missing properties).
+ */
+ if (cb != NULL) {
+ tisz = cb(parent, intr, isz, reg, regsz, &tintr,
+ &found);
+ if (tisz != -1) {
+ isz = tisz;
+ free(intr, M_OFWPROP);
+ intr = tintr;
+ }
+ }
continue;
+ }
if (OF_getprop(parent, "#address-cells", &addrc,
sizeof(addrc)) == -1)
addrc = 2;
OpenPOWER on IntegriCloud