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).
This commit is contained in:
Thomas Moestl 2002-06-12 19:20:57 +00:00
parent 61ffc0b1a6
commit 5fb49f9fd6
4 changed files with 202 additions and 87 deletions

View File

@ -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);
}

View File

@ -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);

View File

@ -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";
@ -605,6 +595,27 @@ 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.
@ -612,8 +623,9 @@ psycho_attach(device_t dev)
* 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:

View File

@ -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
*/