aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--sys/dev/cardbus/cardbus.c1
-rw-r--r--sys/dev/pci/pci.c98
-rw-r--r--sys/dev/pci/pci_private.h1
-rw-r--r--sys/mips/nlm/xlp_pci.c1
-rw-r--r--sys/powerpc/ofw/ofw_pcibus.c1
-rw-r--r--sys/sparc64/pci/ofw_pcibus.c1
6 files changed, 103 insertions, 0 deletions
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),