summaryrefslogtreecommitdiffstats
path: root/sys
diff options
context:
space:
mode:
authormarcel <marcel@FreeBSD.org>2008-03-05 16:46:38 +0000
committermarcel <marcel@FreeBSD.org>2008-03-05 16:46:38 +0000
commita43ebd515c88be9d501daa673f803853d1a715e1 (patch)
treea1f08a6d7f5cd245c6f181d74328cf8aa39710ef /sys
parente378ea99349b36daa38977c06a93dade92ddd254 (diff)
downloadFreeBSD-src-a43ebd515c88be9d501daa673f803853d1a715e1.zip
FreeBSD-src-a43ebd515c88be9d501daa673f803853d1a715e1.tar.gz
o Various fixes related to PCI Express:
- Even for the PCI Express host controller we need to use bus 0 for configuration space accesses to devices directly on the host controller's bus. - Pass the maximum number of slots to pci_ocp_init() because the caller knows how many slots the bus has. Previously a PCI or PCI-X bus underneath a PCI Express host controller would not be enumerated properly. o Pull the interrupt routing logic out of pci_ocp_init() and into its own function. The logic is not quite right and is expected to be a bit more complex. o Fix/add support for PCI domains. The PCI domain is the unit number as per other PCI host controller drivers. As such, we can use logical bus numbers again and don't have to guarantee globally unique bus numbers. Remove pci_ocp_busnr. Return the highest bus number ito the caller of pci_ocp_init() now that we don't have a global variable anymore. o BAR programming fixes: - Non-type0 headers have at most 1 BAR, not 0. - First write ~0 to the BAR in question and then read back its size. Obtained from: Juniper Networks (mostly)
Diffstat (limited to 'sys')
-rw-r--r--sys/powerpc/mpc85xx/pci_ocp.c102
1 files changed, 65 insertions, 37 deletions
diff --git a/sys/powerpc/mpc85xx/pci_ocp.c b/sys/powerpc/mpc85xx/pci_ocp.c
index 66c2d65..e315454 100644
--- a/sys/powerpc/mpc85xx/pci_ocp.c
+++ b/sys/powerpc/mpc85xx/pci_ocp.c
@@ -1,5 +1,6 @@
/*-
- * Copyright 2006 by Juniper Networks. All rights reserved.
+ * Copyright 2006-2007 by Juniper Networks.
+ * All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@@ -93,8 +94,6 @@ struct pci_ocp_softc {
int sc_pcie:1;
};
-static int pci_ocp_busnr = 0;
-
static int pci_ocp_attach(device_t);
static int pci_ocp_probe(device_t);
@@ -154,7 +153,7 @@ pci_ocp_cfgread(struct pci_ocp_softc *sc, u_int bus, u_int slot, u_int func,
{
uint32_t addr, data;
- if (bus == sc->sc_busnr && !sc->sc_pcie)
+ if (bus == sc->sc_busnr)
bus = 0;
addr = CONFIG_ACCESS_ENABLE;
@@ -192,7 +191,7 @@ pci_ocp_cfgwrite(struct pci_ocp_softc *sc, u_int bus, u_int slot, u_int func,
{
uint32_t addr;
- if (bus == sc->sc_busnr && !sc->sc_pcie)
+ if (bus == sc->sc_busnr)
bus = 0;
addr = CONFIG_ACCESS_ENABLE;
@@ -359,8 +358,8 @@ pci_ocp_probe(device_t dev)
cfgreg = (cfgreg >> 8) & 0xff;
}
- bus_get_resource(dev, SYS_RES_MEMORY, 1, &start, &size);
- if (start == 0 || size == 0)
+ error = bus_get_resource(dev, SYS_RES_MEMORY, 1, &start, &size);
+ if (error || start == 0 || size == 0)
goto out;
snprintf(buf, sizeof(buf),
@@ -378,19 +377,17 @@ pci_ocp_init_bar(struct pci_ocp_softc *sc, int bus, int slot, int func,
int barno)
{
bus_addr_t *allocp;
- uint32_t addr, bar, mask, size;
+ uint32_t addr, mask, size;
int reg, width;
reg = PCIR_BAR(barno);
- bar = pci_ocp_read_config(sc->sc_dev, bus, slot, func, reg, 4);
- if (bar == 0)
- return (1);
- width = ((bar & 7) == 4) ? 2 : 1;
-
pci_ocp_write_config(sc->sc_dev, bus, slot, func, reg, ~0, 4);
size = pci_ocp_read_config(sc->sc_dev, bus, slot, func, reg, 4);
+ if (size == 0)
+ return (1);
+ width = ((size & 7) == 4) ? 2 : 1;
- if (bar & 1) { /* I/O port */
+ if (size & 1) { /* I/O port */
allocp = &sc->sc_ioport_alloc;
size &= ~3;
if ((size & 0xffff0000) == 0)
@@ -409,8 +406,9 @@ pci_ocp_init_bar(struct pci_ocp_softc *sc, int bus, int slot, int func,
*allocp = addr + size;
if (bootverbose)
- printf("PCI %u:%u:%u: reg %x: size=%08x: addr=%08x\n",
- bus, slot, func, reg, size, addr);
+ printf("PCI %u:%u:%u:%u: reg %x: size=%08x: addr=%08x\n",
+ device_get_unit(sc->sc_dev), bus, slot, func, reg,
+ size, addr);
pci_ocp_write_config(sc->sc_dev, bus, slot, func, reg, addr, 4);
if (width == 2)
@@ -419,17 +417,41 @@ pci_ocp_init_bar(struct pci_ocp_softc *sc, int bus, int slot, int func,
return (width);
}
-static void
-pci_ocp_init(struct pci_ocp_softc *sc, int bus)
+static u_int
+pci_ocp_route_int(struct pci_ocp_softc *sc, u_int bus, u_int slot, u_int func,
+ u_int intpin)
+{
+ u_int intline;
+
+ /*
+ * Default interrupt routing.
+ */
+ if (intpin != 0) {
+ intline = intpin - 1;
+ intline += (bus != sc->sc_busnr) ? slot : 0;
+ intline = PIC_IRQ_EXT(intline & 3);
+ } else
+ intline = 0xff;
+
+ if (bootverbose)
+ printf("PCI %u:%u:%u:%u: intpin %u: intline=%u\n",
+ device_get_unit(sc->sc_dev), bus, slot, func,
+ intpin, intline);
+
+ return (intline);
+}
+
+static int
+pci_ocp_init(struct pci_ocp_softc *sc, int bus, int maxslot)
{
- int slot, maxslot;
+ int secbus, slot;
int func, maxfunc;
int bar, maxbar;
uint16_t vendor, device;
uint8_t cr8, command, hdrtype, class, subclass;
uint8_t intline, intpin;
- maxslot = (sc->sc_pcie) ? 1 : 31;
+ secbus = bus;
for (slot = 0; slot < maxslot; slot++) {
maxfunc = 0;
for (func = 0; func <= maxfunc; func++) {
@@ -482,7 +504,7 @@ pci_ocp_init(struct pci_ocp_softc *sc, int bus)
}
/* Program the base address registers. */
- maxbar = (hdrtype & PCIM_HDRTYPE) ? 0 : 6;
+ maxbar = (hdrtype & PCIM_HDRTYPE) ? 1 : 6;
bar = 0;
while (bar < maxbar)
bar += pci_ocp_init_bar(sc, bus, slot, func,
@@ -491,12 +513,8 @@ pci_ocp_init(struct pci_ocp_softc *sc, int bus)
/* Perform interrupt routing. */
intpin = pci_ocp_read_config(sc->sc_dev, bus, slot,
func, PCIR_INTPIN, 1);
- if (intpin != 0) {
- intline = intpin - 1;
- intline += (bus != sc->sc_busnr) ? slot : 0;
- intline = PIC_IRQ_EXT(intline & 3);
- } else
- intline = 0xff;
+ intline = pci_ocp_route_int(sc, bus, slot, func,
+ intpin);
pci_ocp_write_config(sc->sc_dev, bus, slot, func,
PCIR_INTLINE, intline, 1);
@@ -517,6 +535,8 @@ pci_ocp_init(struct pci_ocp_softc *sc, int bus)
if (subclass != PCIS_BRIDGE_PCI)
continue;
+ secbus++;
+
/* Program I/O decoder. */
pci_ocp_write_config(sc->sc_dev, bus, slot, func,
PCIR_IOBASEL_1, sc->sc_ioport.rm_start >> 8, 1);
@@ -543,20 +563,22 @@ pci_ocp_init(struct pci_ocp_softc *sc, int bus)
pci_ocp_write_config(sc->sc_dev, bus, slot, func,
PCIR_PMLIMITH_1, 0x00000000, 4);
- pci_ocp_busnr++;
pci_ocp_write_config(sc->sc_dev, bus, slot, func,
PCIR_PRIBUS_1, bus, 1);
pci_ocp_write_config(sc->sc_dev, bus, slot, func,
- PCIR_SECBUS_1, pci_ocp_busnr, 1);
+ PCIR_SECBUS_1, secbus, 1);
pci_ocp_write_config(sc->sc_dev, bus, slot, func,
PCIR_SUBBUS_1, 0xff, 1);
- pci_ocp_init(sc, pci_ocp_busnr);
+ secbus = pci_ocp_init(sc, secbus,
+ (subclass == PCIS_BRIDGE_PCI) ? 31 : 1);
pci_ocp_write_config(sc->sc_dev, bus, slot, func,
- PCIR_SUBBUS_1, pci_ocp_busnr, 1);
+ PCIR_SUBBUS_1, secbus, 1);
}
}
+
+ return (secbus);
}
static void
@@ -619,7 +641,10 @@ pci_ocp_iorange(struct pci_ocp_softc *sc, int type, int wnd)
bus_addr_t *vap, *allocp;
int error;
- bus_get_resource(sc->sc_dev, type, 1, &start, &size);
+ error = bus_get_resource(sc->sc_dev, type, 1, &start, &size);
+ if (error)
+ return (error);
+
end = start + size - 1;
switch (type) {
@@ -658,7 +683,8 @@ pci_ocp_iorange(struct pci_ocp_softc *sc, int type, int wnd)
*allocp = pci_start + alloc;
*vap = (uintptr_t)pmap_mapdev(start, size);
- pci_ocp_outbound(sc, wnd, type, start, size, pci_start);
+ if (wnd != -1)
+ pci_ocp_outbound(sc, wnd, type, start, size, pci_start);
return (0);
}
@@ -667,7 +693,7 @@ pci_ocp_attach(device_t dev)
{
struct pci_ocp_softc *sc;
uint32_t cfgreg;
- int error;
+ int error, maxslot;
sc = device_get_softc(dev);
sc->sc_dev = dev;
@@ -697,9 +723,8 @@ pci_ocp_attach(device_t dev)
pci_ocp_inbound(sc, 2, -1, 0, 0, 0);
pci_ocp_inbound(sc, 3, OCP85XX_TGTIF_RAM, 0, 2U*1024U*1024U*1024U, 0);
- sc->sc_busnr = pci_ocp_busnr;
- pci_ocp_init(sc, sc->sc_busnr);
- pci_ocp_busnr++;
+ maxslot = (sc->sc_pcie) ? 1 : 31;
+ pci_ocp_init(sc, sc->sc_busnr, maxslot);
device_add_child(dev, "pci", -1);
return (bus_generic_attach(dev));
@@ -763,6 +788,9 @@ pci_ocp_read_ivar(device_t dev, device_t child, int which, uintptr_t *result)
case PCIB_IVAR_BUS:
*result = sc->sc_busnr;
return (0);
+ case PCIB_IVAR_DOMAIN:
+ *result = device_get_unit(dev);
+ return (0);
}
return (ENOENT);
}
OpenPOWER on IntegriCloud