diff --git a/sys/sparc64/ebus/ebus.c b/sys/sparc64/ebus/ebus.c index 42d78c858c1e..6c71e029b26e 100644 --- a/sys/sparc64/ebus/ebus.c +++ b/sys/sparc64/ebus/ebus.c @@ -112,7 +112,7 @@ static struct resource *ebus_alloc_resource(device_t, device_t, int, int *, u_long, u_long, u_long, u_int); static struct resource_list *ebus_get_resource_list(device_t, device_t); -static struct ebus_devinfo *ebus_setup_dinfo(struct ebus_softc *, +static struct ebus_devinfo *ebus_setup_dinfo(device_t, struct ebus_softc *, phandle_t, char *); static void ebus_destroy_dinfo(struct ebus_devinfo *); static int ebus_print_res(struct ebus_devinfo *); @@ -200,7 +200,7 @@ ebus_probe(device_t dev) if ((OF_getprop_alloc(node, "name", 1, (void **)&cname)) == -1) continue; - if ((edi = ebus_setup_dinfo(sc, node, cname)) == NULL) { + if ((edi = ebus_setup_dinfo(dev, sc, node, cname)) == NULL) { device_printf(dev, "<%s>: incomplete\n", cname); free(cname, M_OFWPROP); continue; @@ -363,7 +363,8 @@ ebus_get_resource_list(device_t dev, device_t child) } static struct ebus_devinfo * -ebus_setup_dinfo(struct ebus_softc *sc, phandle_t node, char *name) +ebus_setup_dinfo(device_t dev, struct ebus_softc *sc, phandle_t node, + char *name) { struct ebus_devinfo *edi; struct isa_regs *reg; @@ -398,7 +399,8 @@ 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], ofw_pci_orb_callback); + intr = ofw_bus_route_intr(node, intrs[i], ofw_pci_orb_callback, + dev); 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 2f67b249de0c..9f071835dbdd 100644 --- a/sys/sparc64/include/ofw_bus.h +++ b/sys/sparc64/include/ofw_bus.h @@ -32,8 +32,8 @@ #define ORIR_NOTFOUND 0xffffffff typedef int obr_callback_t(phandle_t, u_int8_t *, int, u_int8_t *, int, - u_int8_t **, int *); + u_int8_t **, int *, void *); -u_int32_t ofw_bus_route_intr(phandle_t, int, obr_callback_t *); +u_int32_t ofw_bus_route_intr(phandle_t, int, obr_callback_t *, void *); #endif /* !_MACHINE_OFW_BUS_H_ */ diff --git a/sys/sparc64/isa/isa.c b/sys/sparc64/isa/isa.c index ef746c3223bd..09c95c18f4d4 100644 --- a/sys/sparc64/isa/isa.c +++ b/sys/sparc64/isa/isa.c @@ -134,7 +134,7 @@ isa_init(device_t dev) if (ino > 7) panic("isa_init: XXX: ino too large"); isa_ino[ino] = ofw_bus_route_intr(node, ino, - ofw_pci_orb_callback); + ofw_pci_orb_callback, dev); } for (nbr -= 1; nbr >= 0; nbr--) { diff --git a/sys/sparc64/pci/ofw_pci.c b/sys/sparc64/pci/ofw_pci.c index 829ad6039c6d..ca54bf130d70 100644 --- a/sys/sparc64/pci/ofw_pci.c +++ b/sys/sparc64/pci/ofw_pci.c @@ -46,44 +46,90 @@ #include +#include #include #include #include #include #include "pcib_if.h" +#include "sparcbus_if.h" u_int8_t pci_bus_cnt; phandle_t *pci_bus_map; int pci_bus_map_sz; -#define OPQ_NO_SWIZZLE 1 +/* Do not swizzle on a PCI bus node with no interrupt-map propery. */ +#define OPQ_NO_SWIZZLE 1 +/* + * INOs < 255 are really intpin numbers; use a driver method to figure out + * the real INO. + */ +#define OPQ_INO_CALLBACK 2 +/* + * Do not map EBus interrupts at PCI buses, but assume that they are fully + * specified already. + */ +#define OPQ_EBUS_NOMAP 4 + static struct ofw_pci_quirk { char *opq_model; int opq_quirks; } ofw_pci_quirks[] = { - { "SUNW,Ultra-4", OPQ_NO_SWIZZLE }, - { "SUNW,Ultra-1-Engine", OPQ_NO_SWIZZLE }, + { "SUNW,Ultra-4", OPQ_INO_CALLBACK | OPQ_EBUS_NOMAP }, + { "SUNW,Ultra-1-Engine", OPQ_NO_SWIZZLE }, }; #define OPQ_NENT (sizeof(ofw_pci_quirks) / sizeof(ofw_pci_quirks[0])) static int pci_quirks; #define OFW_PCI_PCIBUS "pci" +#define OFW_PCI_EBUS "ebus" #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) + u_int8_t *pregptr, int pregsz, u_int8_t **rintr, int *terminate, + void *cookie) { + device_t dev = cookie; struct ofw_pci_register preg; u_int32_t pintr, intr; + u_int slot; char type[32]; + int found = 0; - if (pintsz != sizeof(u_int32_t)) + if ((pci_quirks & OPQ_EBUS_NOMAP) != 0 && + OF_getprop(node, "name", type, sizeof(type)) != -1 && + strcmp(type, OFW_PCI_EBUS) == 0) { + *terminate = 1; + return (-1); + } + if (pintsz != sizeof(u_int32_t) || pregsz < sizeof(preg)) return (-1); bcopy(pintptr, &pintr, sizeof(pintr)); - if ((pci_quirks & OPQ_NO_SWIZZLE) == 0 && pregsz >= sizeof(preg) && + bcopy(pregptr, &preg, sizeof(preg)); + slot = OFW_PCI_PHYS_HI_DEVICE(preg.phys_hi); + + if ((pci_quirks & OPQ_INO_CALLBACK) != 0 && pintr <= 255) { + /* + * The e450 has no interrupt maps at all, and it usually has + * full interrupt numbers, including IGN, in the interrupt + * properties. There is one exception, however: the property + * values for external PCI devices seem to always be below 255 + * and describe the interrupt pin to be used on the slot, while + * we have to figure out the base INO by looking at the slot + * number (which we do using a sparcbus method). + * + * Of course, there is an exception to that nice rule: + * in the ebus case, the interrupt property has the correct + * INO (but without IGN). This is dealt with above. + */ + intr = SPARCBUS_GUESS_INO(dev, node, slot, pintr); + found = intr != 255; + *terminate = found; + } + if (!found && (pci_quirks & OPQ_NO_SWIZZLE) == 0 && OF_getprop(node, "device_type", type, sizeof(type)) != -1 && strcmp(type, OFW_PCI_PCIBUS) == 0 && pintr >= 1 && pintr <= 4) { /* @@ -91,29 +137,31 @@ ofw_pci_orb_callback(phandle_t node, u_int8_t *pintptr, int pintsz, * PCI bridges without interrupt maps, where we apparently must * do the PCI swizzle and continue to map on at the parent. */ - bcopy(pregptr, &preg, sizeof(preg)); - intr = (OFW_PCI_PHYS_HI_DEVICE(preg.phys_hi) + pintr + 3) % - 4 + 1; + intr = (slot + pintr + 3) % 4 + 1; + *terminate = 0; + found = 1; + } + + if (found) { *rintr = malloc(sizeof(intr), M_OFWPROP, M_WAITOK); bcopy(&intr, *rintr, sizeof(intr)); - *terminate = 0; return (sizeof(intr)); - } - return (-1); + } else + return (-1); } -u_int32_t -ofw_pci_route_intr(phandle_t node, u_int32_t ign) +static u_int32_t +ofw_pci_route_intr(device_t dev, phandle_t node, u_int32_t ign) { u_int32_t rv; - rv = ofw_bus_route_intr(node, ORIP_NOINT, ofw_pci_orb_callback); + rv = ofw_bus_route_intr(node, ORIP_NOINT, ofw_pci_orb_callback, dev); 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. + * Some machines (notably the SPARCengine Ultra AX and the e450) 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; @@ -273,7 +321,7 @@ ofw_pci_init(device_t dev, phandle_t bushdl, u_int32_t ign, } /* Initialize the intline registers. */ - if ((intr = ofw_pci_route_intr(node, ign)) != 255) { + if ((intr = ofw_pci_route_intr(dev, 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 45bbb430cd24..1c2122bf8c95 100644 --- a/sys/sparc64/pci/ofw_pci.h +++ b/sys/sparc64/pci/ofw_pci.h @@ -71,7 +71,6 @@ struct ofw_pci_bdesc { struct ofw_pci_bdesc *obd_super; }; -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; diff --git a/sys/sparc64/pci/ofw_pci_if.m b/sys/sparc64/pci/ofw_pci_if.m index 2583878b2fb1..fe7826e6cb2d 100644 --- a/sys/sparc64/pci/ofw_pci_if.m +++ b/sys/sparc64/pci/ofw_pci_if.m @@ -26,6 +26,8 @@ #include #include +#include + INTERFACE sparcbus; HEADER { @@ -37,10 +39,6 @@ HEADER { }; CODE { - static int sparcbus_default_intr_pending(device_t, int); - static bus_space_handle_t sparcbus_default_get_bus_handle(device_t, - enum sbbt_id, bus_space_handle_t childhdl, bus_space_tag_t *tag); - static int sparcbus_default_intr_pending(device_t dev, int intr) { @@ -48,6 +46,15 @@ CODE { return (SPARCBUS_INTR_PENDING(device_get_parent(dev), intr)); } + static u_int32_t + sparcbus_default_guess_ino(device_t dev, phandle_t node, u_int slot, + u_int pin) + { + + return (SPARCBUS_GUESS_INO(device_get_parent(dev), node, slot, + pin)); + } + static bus_space_handle_t sparcbus_default_get_bus_handle(device_t dev, enum sbbt_id id, bus_space_handle_t childhdl, bus_space_tag_t *tag) @@ -64,6 +71,17 @@ METHOD int intr_pending { int intr; } DEFAULT sparcbus_default_intr_pending; +# Let the bus driver guess the INO of the device at the given slot and intpin +# on the bus described by the node if it could not be determined from the +# firmware properties. Returns 255 if no INO could be found (mapping will +# continue at the parent), or the desired INO. +METHOD u_int32_t guess_ino { + device_t dev; + phandle_t node; + u_int slot; + u_int pin; +} DEFAULT sparcbus_default_guess_ino; + # Get the bustag for the root bus. This is needed for ISA old-stlye # in[bwl]()/out[bwl]() support, where no tag retrieved from a resource is # passed. The returned tag is used to construct a tag for the whole ISA bus. diff --git a/sys/sparc64/pci/psycho.c b/sys/sparc64/pci/psycho.c index 4cd7fef94bb6..fc99bf0df63f 100644 --- a/sys/sparc64/pci/psycho.c +++ b/sys/sparc64/pci/psycho.c @@ -141,6 +141,7 @@ static void psycho_write_config(device_t, u_int, u_int, u_int, u_int, u_int32_t, int); static int psycho_route_interrupt(device_t, device_t, int); static int psycho_intr_pending(device_t, int); +static u_int32_t psycho_guess_ino(device_t, phandle_t, u_int, u_int); static bus_space_handle_t psycho_get_bus_handle(device_t dev, enum sbbt_id id, bus_space_handle_t childhdl, bus_space_tag_t *tag); @@ -167,6 +168,7 @@ static device_method_t psycho_methods[] = { /* sparcbus interface */ DEVMETHOD(sparcbus_intr_pending, psycho_intr_pending), + DEVMETHOD(sparcbus_guess_ino, psycho_guess_ino), DEVMETHOD(sparcbus_get_bus_handle, psycho_get_bus_handle), { 0, 0 } @@ -336,7 +338,6 @@ psycho_attach(device_t dev) struct upa_regs *reg; struct ofw_pci_bdesc obd; struct psycho_desc *desc; - vm_paddr_t pcictl_offs; phandle_t node; u_int64_t csr; u_long mlen; @@ -374,13 +375,25 @@ psycho_attach(device_t dev) panic("psycho_attach: %d not enough registers", nreg); sc->sc_basepaddr = (vm_paddr_t)UPA_REG_PHYS(®[2]); mlen = UPA_REG_SIZE(®[2]); - pcictl_offs = UPA_REG_PHYS(®[0]); + sc->sc_pcictl = UPA_REG_PHYS(®[0]) - sc->sc_basepaddr; + switch (sc->sc_pcictl) { + case PSR_PCICTL0: + sc->sc_half = 0; + break; + case PSR_PCICTL1: + sc->sc_half = 1; + break; + default: + panic("psycho_attach: bogus pci control register " + "location"); + } } else { if (nreg <= 0) panic("psycho_attach: %d not enough registers", nreg); sc->sc_basepaddr = (vm_paddr_t)UPA_REG_PHYS(®[0]); mlen = UPA_REG_SIZE(reg); - pcictl_offs = sc->sc_basepaddr + PSR_PCICTL0; + sc->sc_pcictl = PSR_PCICTL0; + sc->sc_half = 0; } /* @@ -415,17 +428,14 @@ psycho_attach(device_t dev) sc->sc_bustag = osc->sc_bustag; sc->sc_bushandle = osc->sc_bushandle; } - if (pcictl_offs < sc->sc_basepaddr) - panic("psycho_attach: bogus pci control register location"); - sc->sc_pcictl = pcictl_offs - sc->sc_basepaddr; csr = PSYCHO_READ8(sc, PSR_CS); sc->sc_ign = 0x7c0; /* APB IGN is always 0x7c */ if (sc->sc_mode == PSYCHO_MODE_PSYCHO) sc->sc_ign = PSYCHO_GCSR_IGN(csr) << 6; - device_printf(dev, "%s, impl %d, version %d, ign %#x\n", + device_printf(dev, "%s, impl %d, version %d, ign %#x, bus %c\n", desc->pd_name, (int)PSYCHO_GCSR_IMPL(csr), - (int)PSYCHO_GCSR_VERS(csr), sc->sc_ign); + (int)PSYCHO_GCSR_VERS(csr), sc->sc_ign, 'A' + sc->sc_half); /* * Setup the PCI control register @@ -1272,6 +1282,31 @@ psycho_intr_pending(device_t dev, int intr) return (diag != 0); } +static u_int32_t +psycho_guess_ino(device_t dev, phandle_t node, u_int slot, u_int pin) +{ + struct psycho_softc *sc = (struct psycho_softc *)device_get_softc(dev); + bus_addr_t intrmap; + + /* + * If this is not for one of our direct children (i.e. we are mapping + * at our node), tell the interrupt mapper to go on - we need the + * slot number of the device or it's topmost parent bridge to guess + * the INO. + */ + if (node != sc->sc_node) + return (255); + /* + * Actually guess the INO. We always assume that this is a non-OBIO + * device, and use from the slot number to determine it. + * We only need to do this on e450s, it seems; here, the slot numbers + * for bus A are one-based, while those for bus B seemingly have an + * offset of 2 (hence the factor of 3 below). + */ + intrmap = PSR_PCIA0_INT_MAP + 8 * (slot - 1 + 3 * sc->sc_half); + return (INTINO(PSYCHO_READ8(sc, intrmap)) + pin - 1); +} + static bus_space_handle_t psycho_get_bus_handle(device_t dev, enum sbbt_id id, bus_space_handle_t childhdl, bus_space_tag_t *tag) diff --git a/sys/sparc64/pci/psychovar.h b/sys/sparc64/pci/psychovar.h index 3b8fa73ad7cd..2a77b152bbae 100644 --- a/sys/sparc64/pci/psychovar.h +++ b/sys/sparc64/pci/psychovar.h @@ -61,6 +61,9 @@ struct psycho_softc { #define PSYCHO_MODE_SABRE 1 #define PSYCHO_MODE_PSYCHO 2 + /* Bus A or B of a psycho pair? */ + int sc_half; + struct iommu_state *sc_is; u_int32_t sc_dvmabase; diff --git a/sys/sparc64/sparc64/ofw_bus.c b/sys/sparc64/sparc64/ofw_bus.c index 605a347d7940..6d8e242b91fa 100644 --- a/sys/sparc64/sparc64/ofw_bus.c +++ b/sys/sparc64/sparc64/ofw_bus.c @@ -160,7 +160,7 @@ ofw_bus_find_intr(u_int8_t *intr, int intrsz, u_int8_t *regs, int physsz, * This should work for all bus systems. */ u_int32_t -ofw_bus_route_intr(phandle_t node, int intrp, obr_callback_t *cb) +ofw_bus_route_intr(phandle_t node, int intrp, obr_callback_t *cb, void *cookie) { u_int8_t *reg, *intr, *tintr, *imap, *imapmsk; phandle_t parent; @@ -201,7 +201,7 @@ ofw_bus_route_intr(phandle_t node, int intrp, obr_callback_t *cb) */ if (cb != NULL) { tisz = cb(parent, intr, isz, reg, regsz, &tintr, - &found); + &found, cookie); if (tisz != -1) { isz = tisz; free(intr, M_OFWPROP); diff --git a/sys/sparc64/sparc64/sparcbus_if.m b/sys/sparc64/sparc64/sparcbus_if.m index 2583878b2fb1..fe7826e6cb2d 100644 --- a/sys/sparc64/sparc64/sparcbus_if.m +++ b/sys/sparc64/sparc64/sparcbus_if.m @@ -26,6 +26,8 @@ #include #include +#include + INTERFACE sparcbus; HEADER { @@ -37,10 +39,6 @@ HEADER { }; CODE { - static int sparcbus_default_intr_pending(device_t, int); - static bus_space_handle_t sparcbus_default_get_bus_handle(device_t, - enum sbbt_id, bus_space_handle_t childhdl, bus_space_tag_t *tag); - static int sparcbus_default_intr_pending(device_t dev, int intr) { @@ -48,6 +46,15 @@ CODE { return (SPARCBUS_INTR_PENDING(device_get_parent(dev), intr)); } + static u_int32_t + sparcbus_default_guess_ino(device_t dev, phandle_t node, u_int slot, + u_int pin) + { + + return (SPARCBUS_GUESS_INO(device_get_parent(dev), node, slot, + pin)); + } + static bus_space_handle_t sparcbus_default_get_bus_handle(device_t dev, enum sbbt_id id, bus_space_handle_t childhdl, bus_space_tag_t *tag) @@ -64,6 +71,17 @@ METHOD int intr_pending { int intr; } DEFAULT sparcbus_default_intr_pending; +# Let the bus driver guess the INO of the device at the given slot and intpin +# on the bus described by the node if it could not be determined from the +# firmware properties. Returns 255 if no INO could be found (mapping will +# continue at the parent), or the desired INO. +METHOD u_int32_t guess_ino { + device_t dev; + phandle_t node; + u_int slot; + u_int pin; +} DEFAULT sparcbus_default_guess_ino; + # Get the bustag for the root bus. This is needed for ISA old-stlye # in[bwl]()/out[bwl]() support, where no tag retrieved from a resource is # passed. The returned tag is used to construct a tag for the whole ISA bus.