The diff below teaches ppb(4) how to configure PCI bridges that are
left unconfigured by firmware. This happens for example on Apple
hardware, where devices connected through Thunderbolt are left
unconfigured. And since Thunderbolt is basically a bunch of PCI
bridges (or more correctly PCIe switches), this includes a couple of
ppb(4) devices.
With this diff, an Apple Thunderbolt Gigabit Ethernet adapter that is
plugged in when my MacBookPro12,1 is booted starts working:
bge0 at pci9 dev 0 function 0 "Broadcom BCM57762" rev 0x00, unknown BCM57766
(0x57766000): msi, address 98:5a:eb:c7:30:e4
brgphy0 at bge0 phy 1: BCM57765 10/100/1000baseT PHY, rev. 0
Since this diff changes the code that deals with partly configured PCI
bridges as well, it would be good to test this on a wide variety of
hardware, especially if you have one of those 4-port or 6-port em(4)
cards.
If you find any regressions, please send me:
* dmesg (with and without this diff)
* pcidump -vxx (with and without this diff)
Thanks,
Mark
Index: ppb.c
===================================================================
RCS file: /cvs/src/sys/dev/pci/ppb.c,v
retrieving revision 1.64
diff -u -p -r1.64 ppb.c
--- ppb.c 19 Oct 2015 19:24:54 -0000 1.64
+++ ppb.c 28 Nov 2015 12:44:06 -0000
@@ -66,6 +66,7 @@ struct ppb_softc {
pcitag_t sc_tag; /* ...and tag. */
pci_intr_handle_t sc_ih[4];
void *sc_intrhand;
+ struct extent *sc_busex;
struct extent *sc_ioex;
struct extent *sc_memex;
struct extent *sc_pmemex;
@@ -106,6 +107,8 @@ struct cfdriver ppb_cd = {
NULL, "ppb", DV_DULL
};
+void ppb_alloc_busrange(struct ppb_softc *, struct pci_attach_args *,
+ pcireg_t *);
void ppb_alloc_resources(struct ppb_softc *, struct pci_attach_args *);
int ppb_intr(void *);
void ppb_hotplug_insert(void *);
@@ -151,6 +154,7 @@ ppbattach(struct device *parent, struct
pci_intr_handle_t ih;
pcireg_t busdata, reg, blr;
char *name;
+ int sec, sub;
int pin;
sc->sc_pc = pc;
@@ -158,6 +162,16 @@ ppbattach(struct device *parent, struct
busdata = pci_conf_read(pc, pa->pa_tag, PPB_REG_BUSINFO);
+ /*
+ * When the bus number isn't configured, try to allocate one
+ * ourselves.
+ */
+ if (busdata == 0 && pa->pa_busex)
+ ppb_alloc_busrange(sc, pa, &busdata);
+
+ /*
+ * When the bus number still isn't set correctly, give up.
+ */
if (PPB_BUSINFO_SECONDARY(busdata) == 0) {
printf(": not configured by system firmware\n");
return;
@@ -175,6 +189,19 @@ ppbattach(struct device *parent, struct
pa->pa_bus, PPB_BUSINFO_PRIMARY(busdata));
#endif
+ sec = PPB_BUSINFO_SECONDARY(busdata);
+ sub = PPB_BUSINFO_SUBORDINATE(busdata);
+ if (sub > sec) {
+ name = malloc(PPB_EXNAMLEN, M_DEVBUF, M_NOWAIT);
+ if (name) {
+ snprintf(name, PPB_EXNAMLEN, "%s pcibus",
sc->sc_dev.dv_xname);
+ sc->sc_busex = extent_create(name, 0, 0xff,
+ M_DEVBUF, NULL, 0, EX_NOWAIT | EX_FILLED);
+ extent_free(sc->sc_busex, sec + 1,
+ sub - sec, EX_NOWAIT);
+ }
+ }
+
/* Check for PCI Express capabilities and setup hotplug support. */
if (pci_get_capability(pc, pa->pa_tag, PCI_CAP_PCIEXPRESS,
&sc->sc_cap_off, ®) && (reg & PCI_PCIE_XCAP_SI)) {
@@ -313,6 +340,7 @@ ppbattach(struct device *parent, struct
pba.pba_dmat = pa->pa_dmat;
pba.pba_pc = pc;
pba.pba_flags = pa->pa_flags & ~PCI_FLAGS_MRM_OKAY;
+ pba.pba_busex = sc->sc_busex;
pba.pba_ioex = sc->sc_ioex;
pba.pba_memex = sc->sc_memex;
pba.pba_pmemex = sc->sc_pmemex;
@@ -338,6 +366,12 @@ ppbdetach(struct device *self, int flags
rv = config_detach_children(self, flags);
+ if (sc->sc_busex) {
+ name = sc->sc_busex->ex_name;
+ extent_destroy(sc->sc_busex);
+ free(name, M_DEVBUF, PPB_EXNAMLEN);
+ }
+
if (sc->sc_ioex) {
name = sc->sc_ioex->ex_name;
extent_destroy(sc->sc_ioex);
@@ -485,6 +519,25 @@ ppbactivate(struct device *self, int act
}
void
+ppb_alloc_busrange(struct ppb_softc *sc, struct pci_attach_args *pa,
+ pcireg_t *busdata)
+{
+ pci_chipset_tag_t pc = sc->sc_pc;
+ u_long busnum, busrange;
+
+ for (busrange = 8; busrange > 0; busrange >>= 1) {
+ if (extent_alloc(pa->pa_busex, busrange, 1, 0, 0,
+ EX_NOWAIT, &busnum))
+ continue;
+ *busdata |= pa->pa_bus;
+ *busdata |= (busnum << 8);
+ *busdata |= ((busnum + busrange - 1) << 16);
+ pci_conf_write(pc, pa->pa_tag, PPB_REG_BUSINFO, *busdata);
+ return;
+ }
+}
+
+void
ppb_alloc_resources(struct ppb_softc *sc, struct pci_attach_args *pa)
{
pci_chipset_tag_t pc = sc->sc_pc;
@@ -530,11 +583,15 @@ ppb_alloc_resources(struct ppb_softc *sc
reg_start = PCI_MAPREG_START;
reg_end = PCI_MAPREG_PPB_END;
reg_rom = 0; /* 0x38 */
+ io_count++;
+ mem_count++;
break;
case 2: /* PCI-Cardbus bridge */
reg_start = PCI_MAPREG_START;
reg_end = PCI_MAPREG_PCB_END;
reg_rom = 0;
+ io_count++;
+ mem_count++;
break;
default:
return;
@@ -632,6 +689,9 @@ ppb_alloc_resources(struct ppb_softc *sc
}
}
}
+
+ /* Enable bus master. */
+ csr |= PCI_COMMAND_MASTER_ENABLE;
pci_conf_write(pc, sc->sc_tag, PCI_COMMAND_STATUS_REG, csr);
}