diff options
author | jhb <jhb@FreeBSD.org> | 2015-11-14 01:06:45 +0000 |
---|---|---|
committer | jhb <jhb@FreeBSD.org> | 2015-11-14 01:06:45 +0000 |
commit | 5fa66f56fec675533c2f53126a7e7cba785279a5 (patch) | |
tree | ace90b5139646c3f0b5f34125b0d427d5ca609cb /sys/dev/pci | |
parent | eef9ad01d678b34f81605b33793b0b2f0fdd12af (diff) | |
download | FreeBSD-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.c | 98 | ||||
-rw-r--r-- | sys/dev/pci/pcivar.h | 5 |
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 |