summaryrefslogtreecommitdiffstats
path: root/sys/dev
diff options
context:
space:
mode:
authorjhb <jhb@FreeBSD.org>2015-11-05 21:27:25 +0000
committerjhb <jhb@FreeBSD.org>2015-11-05 21:27:25 +0000
commitfa7f239637c899b490a1463c23b0516030c79bc4 (patch)
tree5c0c3dd2265de647727b90524bf7ccdbbf094e85 /sys/dev
parent0c12e4753ba6951c7105eee5418f69cfda826f9c (diff)
downloadFreeBSD-src-fa7f239637c899b490a1463c23b0516030c79bc4.zip
FreeBSD-src-fa7f239637c899b490a1463c23b0516030c79bc4.tar.gz
Add a new helper function for PCI devices to locate the upstream
PCI-express root port of a given PCI device. Reviewed by: kib, imp MFC after: 1 week Sponsored by: Chelsio Differential Revision: https://reviews.freebsd.org/D4089
Diffstat (limited to 'sys/dev')
-rw-r--r--sys/dev/pci/pci.c41
-rw-r--r--sys/dev/pci/pcivar.h1
2 files changed, 42 insertions, 0 deletions
diff --git a/sys/dev/pci/pci.c b/sys/dev/pci/pci.c
index 5116a4f..f5e8470 100644
--- a/sys/dev/pci/pci.c
+++ b/sys/dev/pci/pci.c
@@ -5431,3 +5431,44 @@ pci_get_rid_method(device_t dev, device_t child)
return (PCIB_GET_RID(device_get_parent(dev), child));
}
+
+/* Find the upstream port of a given PCI device in a root complex. */
+device_t
+pci_find_pcie_root_port(device_t dev)
+{
+ struct pci_devinfo *dinfo;
+ devclass_t pci_class;
+ device_t pcib, bus;
+
+ pci_class = devclass_find("pci");
+ KASSERT(device_get_devclass(device_get_parent(dev)) == pci_class,
+ ("%s: non-pci device %s", __func__, device_get_nameunit(dev)));
+
+ /*
+ * Walk the bridge hierarchy until we find a PCI-e root
+ * port or a non-PCI device.
+ */
+ for (;;) {
+ bus = device_get_parent(dev);
+ KASSERT(bus != NULL, ("%s: null parent of %s", __func__,
+ device_get_nameunit(dev)));
+
+ pcib = device_get_parent(bus);
+ KASSERT(pcib != NULL, ("%s: null bridge of %s", __func__,
+ device_get_nameunit(bus)));
+
+ /*
+ * pcib's parent must be a PCI bus for this to be a
+ * PCI-PCI bridge.
+ */
+ if (device_get_devclass(device_get_parent(pcib)) != pci_class)
+ return (NULL);
+
+ dinfo = device_get_ivars(pcib);
+ if (dinfo->cfg.pcie.pcie_location != 0 &&
+ dinfo->cfg.pcie.pcie_type == PCIEM_TYPE_ROOT_PORT)
+ return (pcib);
+
+ dev = pcib;
+ }
+}
diff --git a/sys/dev/pci/pcivar.h b/sys/dev/pci/pcivar.h
index 242201b..b8f64c2 100644
--- a/sys/dev/pci/pcivar.h
+++ b/sys/dev/pci/pcivar.h
@@ -547,6 +547,7 @@ int pci_msix_device_blacklisted(device_t dev);
void pci_ht_map_msi(device_t dev, uint64_t addr);
+device_t pci_find_pcie_root_port(device_t dev);
int pci_get_max_read_req(device_t dev);
void pci_restore_state(device_t dev);
void pci_save_state(device_t dev);
OpenPOWER on IntegriCloud