diff --git a/sys/sparc64/ebus/ebus.c b/sys/sparc64/ebus/ebus.c index 69a12da40ffb..42d78c858c1e 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 2b0670e341d5..2f67b249de0c 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 f570a9febd78..7c73488af12d 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 0536db7b206f..ef746c3223bd 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 d36e7307ace6..6d7754b6ac28 100644 --- a/sys/sparc64/pci/ofw_pci.c +++ b/sys/sparc64/pci/ofw_pci.c @@ -47,6 +47,7 @@ #include #include +#include #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 68cf1ec8947d..45bbb430cd24 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 + /* 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 edf9f11f569d..51fb78f7666f 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 a387704ec94c..4b2edfc34cd2 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 d17fb06d7421..605a347d7940 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;