summaryrefslogtreecommitdiffstats
path: root/sys/dev/pci
diff options
context:
space:
mode:
authorjhb <jhb@FreeBSD.org>2015-11-14 01:06:45 +0000
committerjhb <jhb@FreeBSD.org>2015-11-14 01:06:45 +0000
commit5fa66f56fec675533c2f53126a7e7cba785279a5 (patch)
treeace90b5139646c3f0b5f34125b0d427d5ca609cb /sys/dev/pci
parenteef9ad01d678b34f81605b33793b0b2f0fdd12af (diff)
downloadFreeBSD-src-5fa66f56fec675533c2f53126a7e7cba785279a5.zip
FreeBSD-src-5fa66f56fec675533c2f53126a7e7cba785279a5.tar.gz
MFC 290414,290415:
Additional PCI helper functions. 290414: Add helper routines for PCI device drivers to read, write, and modify PCI-Express capability registers (that is, PCI config registers in the standard PCI config space belonging to the PCI-Express capability register set). Note that all of the current PCI-e registers are either 16 or 32-bits, so only widths of 2 or 4 bytes are supported. 290415: Add a new helper function for PCI devices to locate the upstream PCI-express root port of a given PCI device.
Diffstat (limited to 'sys/dev/pci')
-rw-r--r--sys/dev/pci/pci.c98
-rw-r--r--sys/dev/pci/pcivar.h5
2 files changed, 103 insertions, 0 deletions
diff --git a/sys/dev/pci/pci.c b/sys/dev/pci/pci.c
index e5db8e2..fb9a202 100644
--- a/sys/dev/pci/pci.c
+++ b/sys/dev/pci/pci.c
@@ -1895,6 +1895,63 @@ pci_set_max_read_req(device_t dev, int size)
return (size);
}
+uint32_t
+pcie_read_config(device_t dev, int reg, int width)
+{
+ struct pci_devinfo *dinfo = device_get_ivars(dev);
+ int cap;
+
+ cap = dinfo->cfg.pcie.pcie_location;
+ if (cap == 0) {
+ if (width == 2)
+ return (0xffff);
+ return (0xffffffff);
+ }
+
+ return (pci_read_config(dev, cap + reg, width));
+}
+
+void
+pcie_write_config(device_t dev, int reg, uint32_t value, int width)
+{
+ struct pci_devinfo *dinfo = device_get_ivars(dev);
+ int cap;
+
+ cap = dinfo->cfg.pcie.pcie_location;
+ if (cap == 0)
+ return;
+ pci_write_config(dev, cap + reg, value, width);
+}
+
+/*
+ * Adjusts a PCI-e capability register by clearing the bits in mask
+ * and setting the bits in (value & mask). Bits not set in mask are
+ * not adjusted.
+ *
+ * Returns the old value on success or all ones on failure.
+ */
+uint32_t
+pcie_adjust_config(device_t dev, int reg, uint32_t mask, uint32_t value,
+ int width)
+{
+ struct pci_devinfo *dinfo = device_get_ivars(dev);
+ uint32_t old, new;
+ int cap;
+
+ cap = dinfo->cfg.pcie.pcie_location;
+ if (cap == 0) {
+ if (width == 2)
+ return (0xffff);
+ return (0xffffffff);
+ }
+
+ old = pci_read_config(dev, cap + reg, width);
+ new = old & ~mask;
+ new |= (value & mask);
+ pci_write_config(dev, cap + reg, new, width);
+ return (old);
+}
+
/*
* Support for MSI message signalled interrupts.
*/
@@ -5139,3 +5196,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 0157ee7..4b570d0 100644
--- a/sys/dev/pci/pcivar.h
+++ b/sys/dev/pci/pcivar.h
@@ -501,10 +501,15 @@ 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);
int pci_set_max_read_req(device_t dev, int size);
+uint32_t pcie_read_config(device_t dev, int reg, int width);
+void pcie_write_config(device_t dev, int reg, uint32_t value, int width);
+uint32_t pcie_adjust_config(device_t dev, int reg, uint32_t mask,
+ uint32_t value, int width);
#ifdef BUS_SPACE_MAXADDR
OpenPOWER on IntegriCloud