diff --git a/sys/dev/cardbus/cardbus.c b/sys/dev/cardbus/cardbus.c index a6037e15c1ea..765c1700dd28 100644 --- a/sys/dev/cardbus/cardbus.c +++ b/sys/dev/cardbus/cardbus.c @@ -346,6 +346,7 @@ static device_method_t cardbus_methods[] = { DEVMETHOD(bus_get_dma_tag, bus_generic_get_dma_tag), DEVMETHOD(bus_read_ivar, cardbus_read_ivar), DEVMETHOD(bus_driver_added, cardbus_driver_added), + DEVMETHOD(bus_rescan, kobj_error_method), /* Card Interface */ DEVMETHOD(card_attach_card, cardbus_attach_card), diff --git a/sys/dev/pci/pci.c b/sys/dev/pci/pci.c index 532351ea31ed..bdb3c5e4fb7b 100644 --- a/sys/dev/pci/pci.c +++ b/sys/dev/pci/pci.c @@ -168,6 +168,7 @@ static device_method_t pci_methods[] = { DEVMETHOD(bus_remap_intr, pci_remap_intr_method), DEVMETHOD(bus_suspend_child, pci_suspend_child), DEVMETHOD(bus_resume_child, pci_resume_child), + DEVMETHOD(bus_rescan, pci_rescan_method), /* PCI interface */ DEVMETHOD(pci_read_config, pci_read_config_method), @@ -3917,6 +3918,103 @@ pci_add_children(device_t dev, int domain, int busno) #undef REG } +int +pci_rescan_method(device_t dev) +{ +#define REG(n, w) PCIB_READ_CONFIG(pcib, busno, s, f, n, w) + device_t pcib = device_get_parent(dev); + struct pci_softc *sc; + device_t child, *devlist, *unchanged; + int devcount, error, i, j, maxslots, oldcount; + int busno, domain, s, f, pcifunchigh; + uint8_t hdrtype; + + /* No need to check for ARI on a rescan. */ + error = device_get_children(dev, &devlist, &devcount); + if (error) + return (error); + if (devcount != 0) { + unchanged = malloc(devcount * sizeof(device_t), M_TEMP, + M_NOWAIT | M_ZERO); + if (unchanged == NULL) { + free(devlist, M_TEMP); + return (ENOMEM); + } + } else + unchanged = NULL; + + sc = device_get_softc(dev); + domain = pcib_get_domain(dev); + busno = pcib_get_bus(dev); + maxslots = PCIB_MAXSLOTS(pcib); + for (s = 0; s <= maxslots; s++) { + /* If function 0 is not present, skip to the next slot. */ + f = 0; + if (REG(PCIR_VENDOR, 2) == 0xffff) + continue; + pcifunchigh = 0; + hdrtype = REG(PCIR_HDRTYPE, 1); + if ((hdrtype & PCIM_HDRTYPE) > PCI_MAXHDRTYPE) + continue; + if (hdrtype & PCIM_MFDEV) + pcifunchigh = PCIB_MAXFUNCS(pcib); + for (f = 0; f <= pcifunchigh; f++) { + if (REG(PCIR_VENDOR, 2) == 0xfff) + continue; + + /* + * Found a valid function. Check if a + * device_t for this device already exists. + */ + for (i = 0; i < devcount; i++) { + child = devlist[i]; + if (child == NULL) + continue; + if (pci_get_slot(child) == s && + pci_get_function(child) == f) { + unchanged[i] = child; + goto next_func; + } + } + + pci_identify_function(pcib, dev, domain, busno, s, f); + next_func:; + } + } + + /* Remove devices that are no longer present. */ + for (i = 0; i < devcount; i++) { + if (unchanged[i] != NULL) + continue; + device_delete_child(dev, devlist[i]); + } + + free(devlist, M_TEMP); + oldcount = devcount; + + /* Try to attach the devices just added. */ + error = device_get_children(dev, &devlist, &devcount); + if (error) { + free(unchanged, M_TEMP); + return (error); + } + + for (i = 0; i < devcount; i++) { + for (j = 0; j < oldcount; j++) { + if (devlist[i] == unchanged[j]) + goto next_device; + } + + device_probe_and_attach(devlist[i]); + next_device:; + } + + free(unchanged, M_TEMP); + free(devlist, M_TEMP); + return (0); +#undef REG +} + #ifdef PCI_IOV device_t pci_add_iov_child(device_t bus, device_t pf, uint16_t rid, uint16_t vid, diff --git a/sys/dev/pci/pci_private.h b/sys/dev/pci/pci_private.h index 222ffb89063a..a9958ca627db 100644 --- a/sys/dev/pci/pci_private.h +++ b/sys/dev/pci/pci_private.h @@ -57,6 +57,7 @@ void pci_add_resources(device_t bus, device_t dev, int force, void pci_add_resources_ea(device_t bus, device_t dev, int alloc_iov); struct pci_devinfo *pci_alloc_devinfo_method(device_t dev); int pci_attach_common(device_t dev); +int pci_rescan_method(device_t dev); void pci_driver_added(device_t dev, driver_t *driver); int pci_ea_is_enabled(device_t dev, int rid); int pci_print_child(device_t dev, device_t child); diff --git a/sys/mips/nlm/xlp_pci.c b/sys/mips/nlm/xlp_pci.c index 9e911469d086..43b1574ae83d 100644 --- a/sys/mips/nlm/xlp_pci.c +++ b/sys/mips/nlm/xlp_pci.c @@ -154,6 +154,7 @@ static device_method_t xlp_pci_methods[] = { /* Device interface */ DEVMETHOD(device_probe, xlp_pci_probe), DEVMETHOD(device_attach, xlp_pci_attach), + DEVMETHOD(bus_rescan, kobj_error_method), DEVMETHOD_END }; diff --git a/sys/powerpc/ofw/ofw_pcibus.c b/sys/powerpc/ofw/ofw_pcibus.c index 01e2343ebca6..05aa06c3961a 100644 --- a/sys/powerpc/ofw/ofw_pcibus.c +++ b/sys/powerpc/ofw/ofw_pcibus.c @@ -77,6 +77,7 @@ static device_method_t ofw_pcibus_methods[] = { /* Bus interface */ DEVMETHOD(bus_child_deleted, ofw_pcibus_child_deleted), DEVMETHOD(bus_child_pnpinfo_str, ofw_pcibus_child_pnpinfo_str_method), + DEVMETHOD(bus_rescan, kobj_error_method), /* PCI interface */ DEVMETHOD(pci_alloc_devinfo, ofw_pcibus_alloc_devinfo), diff --git a/sys/sparc64/pci/ofw_pcibus.c b/sys/sparc64/pci/ofw_pcibus.c index 11f19cf93e5a..2fa289fa6b91 100644 --- a/sys/sparc64/pci/ofw_pcibus.c +++ b/sys/sparc64/pci/ofw_pcibus.c @@ -81,6 +81,7 @@ static device_method_t ofw_pcibus_methods[] = { /* Bus interface */ DEVMETHOD(bus_child_deleted, ofw_pcibus_child_deleted), DEVMETHOD(bus_child_pnpinfo_str, ofw_pcibus_pnpinfo_str), + DEVMETHOD(bus_rescan, kobj_error_method), /* PCI interface */ DEVMETHOD(pci_alloc_devinfo, ofw_pcibus_alloc_devinfo),