diff options
author | tmm <tmm@FreeBSD.org> | 2002-06-12 19:20:57 +0000 |
---|---|---|
committer | tmm <tmm@FreeBSD.org> | 2002-06-12 19:20:57 +0000 |
commit | 0d05c0dd9cf4ae0953ba607bcf41e2abafebb77d (patch) | |
tree | 98a711dedb0135a584c0316111043e98d41cfdd7 /sys/sparc64/pci | |
parent | c580ba61b3d5ce3f74c50c463c50afc556dd8d7a (diff) | |
download | FreeBSD-src-0d05c0dd9cf4ae0953ba607bcf41e2abafebb77d.zip FreeBSD-src-0d05c0dd9cf4ae0953ba607bcf41e2abafebb77d.tar.gz |
Add PCI bus enumeration and latency timer setup to the sparc64 MD PCI
code. Both tasks are not always performed completely by the firmware.
The former is required to get some e450 models to boot; the latter fixes
the repeated fifo underruns with hme(4)s and gem(4)s observed on some
machines (and probably performance problems with other peripherals as
well).
Diffstat (limited to 'sys/sparc64/pci')
-rw-r--r-- | sys/sparc64/pci/ofw_pci.c | 206 | ||||
-rw-r--r-- | sys/sparc64/pci/ofw_pci.h | 17 | ||||
-rw-r--r-- | sys/sparc64/pci/psycho.c | 60 | ||||
-rw-r--r-- | sys/sparc64/pci/psychoreg.h | 8 |
4 files changed, 203 insertions, 88 deletions
diff --git a/sys/sparc64/pci/ofw_pci.c b/sys/sparc64/pci/ofw_pci.c index 636c9f9..d36e730 100644 --- a/sys/sparc64/pci/ofw_pci.c +++ b/sys/sparc64/pci/ofw_pci.c @@ -50,6 +50,12 @@ #include "pcib_if.h" +u_int8_t pci_bus_cnt; +phandle_t *pci_bus_map; +int pci_bus_map_sz; + +#define PCI_BUS_MAP_INC 10 + u_int32_t ofw_pci_route_intr(phandle_t node) { @@ -61,24 +67,72 @@ ofw_pci_route_intr(phandle_t node) return (rv); } +u_int8_t +ofw_pci_alloc_busno(phandle_t node) +{ + phandle_t *om; + int osz; + u_int8_t n; + + n = pci_bus_cnt++; + /* Establish a mapping between bus numbers and device nodes. */ + if (n >= pci_bus_map_sz) { + osz = pci_bus_map_sz; + om = pci_bus_map; + pci_bus_map_sz = n + PCI_BUS_MAP_INC; + pci_bus_map = malloc(sizeof(*pci_bus_map) * pci_bus_map_sz, + M_DEVBUF, M_WAITOK | M_ZERO); + if (om != NULL) { + bcopy(om, pci_bus_map, sizeof(*om) * osz); + free(om, M_DEVBUF); + } + } + pci_bus_map[n] = node; + return (n); +} + +/* + * Initialize bridge bus numbers for bridges that implement the primary, + * secondary and subordinate bus number registers. + */ +void +ofw_pci_binit(device_t busdev, struct ofw_pci_bdesc *obd) +{ + +#ifdef OFW_PCI_DEBUG + printf("PCI-PCI bridge at %u/%u/%u: setting bus #s to %u/%u/%u\n", + obd->obd_bus, obd->obd_slot, obd->obd_func, obd->obd_bus, + obd->obd_secbus, obd->obd_subbus); +#endif /* OFW_PCI_DEBUG */ + PCIB_WRITE_CONFIG(busdev, obd->obd_bus, obd->obd_slot, obd->obd_func, + PCIR_PRIBUS_1, obd->obd_bus, 1); + PCIB_WRITE_CONFIG(busdev, obd->obd_bus, obd->obd_slot, obd->obd_func, + PCIR_SECBUS_1, obd->obd_secbus, 1); + PCIB_WRITE_CONFIG(busdev, obd->obd_bus, obd->obd_slot, obd->obd_func, + 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 configuration registers - * of attached devices using firmware information. + * through bridges, and initialize the interrupt line and latency timer + * configuration registers of attached devices using firmware information, + * as well as the the bus numbers and ranges of the bridges. */ void -ofw_pci_init_intr(device_t dev, phandle_t bus) +ofw_pci_init(device_t dev, phandle_t bushdl, struct ofw_pci_bdesc *obd) { struct ofw_pci_register pcir; + struct ofw_pci_bdesc subobd, *tobd; phandle_t node; char type[32]; - int intr; - int freemap; + int intr, freemap; + u_int slot, busno, func, sub, lat; - if ((node = OF_child(bus)) == 0) + if ((node = OF_child(bushdl)) == 0) return; freemap = 0; + busno = obd->obd_secbus; do { if (node == -1) panic("ofw_pci_init_intr: OF_child failed"); @@ -86,60 +140,88 @@ ofw_pci_init_intr(device_t dev, phandle_t bus) type[0] = '\0'; else type[sizeof(type) - 1] = '\0'; + if (OF_getprop(node, "reg", &pcir, sizeof(pcir)) == -1) + panic("ofw_pci_route_intr: 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) { /* - * This is a pci-pci bridge, recurse to initialize the - * child bus. The hierarchy is usually at most 2 levels - * deep, so recursion is feasible. + * This is a pci-pci bridge, initalize the bus number and + * recurse to initialize the child bus. The hierarchy is + * usually at most 2 levels deep, so recursion is + * feasible. + */ + subobd.obd_bus = busno; + subobd.obd_slot = slot; + subobd.obd_func = func; + sub = ofw_pci_alloc_busno(node); + subobd.obd_secbus = subobd.obd_subbus = sub; + /* Assume this bridge is mostly standard conforming. */ + subobd.obd_init = ofw_pci_binit; + subobd.obd_super = obd; + /* + * Need to change all subordinate bus registers of the + * bridges above this one now so that configuration + * transactions will get through. */ + for (tobd = obd; tobd != NULL; tobd = tobd->obd_super) { + tobd->obd_subbus = sub; + tobd->obd_init(dev, tobd); + } + subobd.obd_init(dev, &subobd); #ifdef OFW_PCI_DEBUG device_printf(dev, "%s: descending to " "subordinate PCI bus\n", __func__); -#endif - ofw_pci_init_intr(dev, node); +#endif /* OFW_PCI_DEBUG */ + ofw_pci_init(dev, node, &subobd); } else { - if (OF_getprop(node, "reg", &pcir, sizeof(pcir)) == -1) - panic("ofw_pci_route_intr: OF_getprop failed"); + /* + * Initialize the latency timer register for + * busmaster devices to work properly. This is another + * task which the firmware does not always perform. + * The Min_Gnt register can be used to compute it's + * recommended value: it contains the desired latency + * in units of 1/4 us. To calculate the correct latency + * timer value, a bus clock of 33 and no wait states + * should be assumed. + */ + lat = PCIB_READ_CONFIG(dev, busno, slot, func, + PCIR_MINGNT, 1) * 33 / 4; + if (lat != 0) { +#ifdef OFW_PCI_DEBUG + printf("device %d/%d/%d: latency timer %d -> " + "%d\n", busno, slot, func, + PCIB_READ_CONFIG(dev, busno, slot, func, + PCIR_LATTIMER, 1), lat); +#endif /* OFW_PCI_DEBUG */ + PCIB_WRITE_CONFIG(dev, busno, slot, func, + PCIR_LATTIMER, imin(lat, 255), 1); + } + /* Initialize the intline registers. */ if ((intr = ofw_pci_route_intr(node)) != 255) { #ifdef OFW_PCI_DEBUG 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), - intr, - (int)PCIB_READ_CONFIG(dev, - OFW_PCI_PHYS_HI_BUS(pcir.phys_hi), - OFW_PCI_PHYS_HI_DEVICE(pcir.phys_hi), - OFW_PCI_PHYS_HI_FUNCTION(pcir.phys_hi), - PCIR_INTLINE, 1)); + __func__, busno, slot, func, intr, + (int)PCIB_READ_CONFIG(dev, busno, slot, + func, PCIR_INTLINE, 1)); #endif /* OFW_PCI_DEBUG */ - PCIB_WRITE_CONFIG(dev, - OFW_PCI_PHYS_HI_BUS(pcir.phys_hi), - OFW_PCI_PHYS_HI_DEVICE(pcir.phys_hi), - OFW_PCI_PHYS_HI_FUNCTION(pcir.phys_hi), + PCIB_WRITE_CONFIG(dev, busno, slot, func, PCIR_INTLINE, intr, 1); } else { #ifdef OFW_PCI_DEBUG 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), - (int)PCIB_READ_CONFIG(dev, - OFW_PCI_PHYS_HI_BUS(pcir.phys_hi), - OFW_PCI_PHYS_HI_DEVICE(pcir.phys_hi), - OFW_PCI_PHYS_HI_FUNCTION(pcir.phys_hi), - PCIR_INTLINE, 1)); + __func__, busno, slot, func, + (int)PCIB_READ_CONFIG(dev, busno, slot, + func, PCIR_INTLINE, 1)); #endif /* OFW_PCI_DEBUG */ - /* The firmware initializes to 0 instead 255 */ - PCIB_WRITE_CONFIG(dev, - OFW_PCI_PHYS_HI_BUS(pcir.phys_hi), - OFW_PCI_PHYS_HI_DEVICE(pcir.phys_hi), - OFW_PCI_PHYS_HI_FUNCTION(pcir.phys_hi), + /* + * The firmware initializes to 0 instead of + * 255. + */ + PCIB_WRITE_CONFIG(dev, busno, slot, func, PCIR_INTLINE, 255, 1); } } @@ -149,34 +231,17 @@ ofw_pci_init_intr(device_t dev, phandle_t bus) phandle_t ofw_pci_find_node(int bus, int slot, int func) { - phandle_t node, bnode, parent; + phandle_t node, bnode; struct ofw_pci_register pcir; - int br[2]; - char name[16]; - - /* 1. Try to find the bus in question. */ - bnode = 0; - name[sizeof(name) - 1] = '\0'; - parent = OF_peer(0); - node = OF_child(parent); - while (node != 0 && node != -1) { - if (OF_getprop(node, "name", name, sizeof(name) - 1) != -1 && - strcmp(name, "pci") == 0 && - OF_getprop(node, "bus-range", br, sizeof(br)) != -1) { - /* Found the bus? */ - if (bus == br[0]) { - bnode = node; - break; - } - /* Need to descend? */ - if (bus > br[0] && bus <= br[1]) { - parent = node; - node = OF_child(node); - continue; - } - } - node = OF_peer(node); - } + + /* + * Retrieve the bus node from the mapping that was created on + * initialization. The bus numbers the firmware uses cannot be trusted, + * so they might have needed to be changed and this is necessary. + */ + if (bus >= pci_bus_map_sz) + return (0); + bnode = pci_bus_map[bus]; if (bnode == 0) return (0); for (node = OF_child(bnode); node != 0 && node != -1; @@ -184,11 +249,8 @@ ofw_pci_find_node(int bus, int slot, int func) if (OF_getprop(node, "reg", &pcir, sizeof(pcir)) == -1) continue; if (OFW_PCI_PHYS_HI_DEVICE(pcir.phys_hi) == slot && - OFW_PCI_PHYS_HI_FUNCTION(pcir.phys_hi) == func) { - if (OFW_PCI_PHYS_HI_BUS(pcir.phys_hi) != bus) - panic("ofw_pci_find_node: bus number mismatch"); + OFW_PCI_PHYS_HI_FUNCTION(pcir.phys_hi) == func) return (node); - } } return (0); } diff --git a/sys/sparc64/pci/ofw_pci.h b/sys/sparc64/pci/ofw_pci.h index 862df92..68cf1ec 100644 --- a/sys/sparc64/pci/ofw_pci.h +++ b/sys/sparc64/pci/ofw_pci.h @@ -56,8 +56,23 @@ struct ofw_pci_imap_msk { u_int32_t intr; }; +struct ofw_pci_bdesc; +typedef void ofw_pci_binit_t(device_t, struct ofw_pci_bdesc *); + +struct ofw_pci_bdesc { + u_int obd_bus; + u_int obd_slot; + u_int obd_func; + u_int obd_secbus; + u_int obd_subbus; + ofw_pci_binit_t *obd_init; + struct ofw_pci_bdesc *obd_super; +}; + u_int32_t ofw_pci_route_intr(phandle_t); -void ofw_pci_init_intr(device_t, phandle_t); +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 *); 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 f62eceb..901639e 100644 --- a/sys/sparc64/pci/psycho.c +++ b/sys/sparc64/pci/psycho.c @@ -94,6 +94,7 @@ static void psycho_wakeup(void *); /* IOMMU support */ static void psycho_iommu_init(struct psycho_softc *, int); +static ofw_pci_binit_t psycho_binit; /* * bus space and bus dma support for UltraSPARC `psycho'. note that most @@ -288,6 +289,7 @@ psycho_attach(device_t dev) struct psycho_softc *osc = NULL; struct psycho_softc *asc; struct upa_regs *reg; + struct ofw_pci_bdesc obd; char compat[32]; char *model; phandle_t node; @@ -416,21 +418,9 @@ psycho_attach(device_t dev) PCICTL_RTRYWAIT); PCICTL_WRITE8(sc, PCR_CS, csr); - /* grab the psycho ranges */ + /* Grab the psycho ranges */ psycho_get_ranges(sc->sc_node, &sc->sc_range, &sc->sc_nrange); - /* get the bus-range for the psycho */ - n = OF_getprop(node, "bus-range", (void *)psycho_br, sizeof(psycho_br)); - if (n == -1) - panic("could not get psycho bus-range"); - if (n != sizeof(psycho_br)) - panic("broken psycho bus-range (%d)", n); - - printf("bus range %u to %u; PCI bus %d\n", psycho_br[0], psycho_br[1], - psycho_br[0]); - - sc->sc_busno = psycho_br[0]; - /* Initialize memory and i/o rmans */ sc->sc_io_rman.rm_type = RMAN_ARRAY; sc->sc_io_rman.rm_descr = "Psycho PCI I/O Ports"; @@ -606,14 +596,36 @@ psycho_attach(device_t dev) #endif /* + * Get the bus range from the firmware; it is used solely for obtaining + * the inital bus number, and cannot be trusted on all machines. + */ + n = OF_getprop(node, "bus-range", (void *)psycho_br, sizeof(psycho_br)); + if (n == -1) + panic("could not get psycho bus-range"); + if (n != sizeof(psycho_br)) + panic("broken psycho bus-range (%d)", n); + + sc->sc_busno = ofw_pci_alloc_busno(sc->sc_node); + obd.obd_bus = psycho_br[0]; + obd.obd_secbus = obd.obd_subbus = sc->sc_busno; + obd.obd_slot = PCS_DEVICE; + obd.obd_func = PCS_FUNC; + obd.obd_init = psycho_binit; + obd.obd_super = NULL; + /* Initial setup. */ + psycho_binit(dev, &obd); + /* Update the bus number to what was just programmed. */ + obd.obd_bus = obd.obd_secbus; + /* * Initialize the interrupt registers of all devices hanging from * the host bridge directly or indirectly via PCI-PCI bridges. * The MI code (and the PCI spec) assume that this is done during * system initialization, however the firmware does not do this * at least on some models, and we probably shouldn't trust that * the firmware uses the same model as this driver if it does. + * Additionally, set up the bus numbers and ranges. */ - ofw_pci_init_intr(dev, sc->sc_node); + ofw_pci_init(dev, sc->sc_node, &obd); device_add_child(dev, "pci", device_get_unit(dev)); return (bus_generic_attach(dev)); @@ -837,6 +849,25 @@ psycho_iommu_init(struct psycho_softc *sc, int tsbsize) iommu_init(name, is, tsbsize, iobase); } +static void +psycho_binit(device_t busdev, struct ofw_pci_bdesc *obd) +{ + +#ifdef PSYCHO_DEBUG + printf("psycho at %u/%u/%u: setting bus #s to %u/%u/%u\n", + obd->obd_bus, obd->obd_slot, obd->obd_func, obd->obd_bus, + obd->obd_secbus, obd->obd_subbus); +#endif /* PSYCHO_DEBUG */ + /* + * NOTE: this must be kept in this order, since the last write will + * change the config space address of the psycho. + */ + PCIB_WRITE_CONFIG(busdev, obd->obd_bus, obd->obd_slot, obd->obd_func, + PCSR_SUBBUS, obd->obd_subbus, 1); + PCIB_WRITE_CONFIG(busdev, obd->obd_bus, obd->obd_slot, obd->obd_func, + PCSR_SECBUS, obd->obd_secbus, 1); +} + static int psycho_maxslots(device_t dev) { @@ -961,7 +992,6 @@ 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; /* * XXX: ugly loathsome hack: diff --git a/sys/sparc64/pci/psychoreg.h b/sys/sparc64/pci/psychoreg.h index 7de9c62..412a217 100644 --- a/sys/sparc64/pci/psychoreg.h +++ b/sys/sparc64/pci/psychoreg.h @@ -240,6 +240,14 @@ #define UEAFSR_P_DWR (1UL << 61) /* pri. error during write */ #define UEAFSR_P_DRD (1UL << 62) /* pri. error during read */ +/* Definitions for the psycho configuration space */ +#define PCS_DEVICE 0 /* Device number of psycho CS entry */ +#define PCS_FUNC 0 /* Function number of psycho CS entry */ + +/* Non-Standard registers in the configration space */ +#define PCSR_SECBUS 0x40 /* Secondary bus number register */ +#define PCSR_SUBBUS 0x41 /* Subordinate bus number register */ + /* * these are the PROM structures we grovel */ |