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 | |
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.
-rw-r--r-- | share/man/man9/Makefile | 6 | ||||
-rw-r--r-- | share/man/man9/pci.9 | 74 | ||||
-rw-r--r-- | sys/dev/pci/pci.c | 98 | ||||
-rw-r--r-- | sys/dev/pci/pcivar.h | 5 |
4 files changed, 180 insertions, 3 deletions
diff --git a/share/man/man9/Makefile b/share/man/man9/Makefile index 69bf839..d9156c0 100644 --- a/share/man/man9/Makefile +++ b/share/man/man9/Makefile @@ -1014,6 +1014,7 @@ MLINKS+=pci.9 pci_alloc_msi.9 \ pci.9 pci_find_device.9 \ pci.9 pci_find_extcap.9 \ pci.9 pci_find_htcap.9 \ + pci.9 pci_find_pcie_root_port.9 \ pci.9 pci_get_max_read_req.9 \ pci.9 pci_get_powerstate.9 \ pci.9 pci_get_vpd_ident.9 \ @@ -1028,7 +1029,10 @@ MLINKS+=pci.9 pci_alloc_msi.9 \ pci.9 pci_save_state.9 \ pci.9 pci_set_powerstate.9 \ pci.9 pci_set_max_read_req.9 \ - pci.9 pci_write_config.9 + pci.9 pci_write_config.9 \ + pci.9 pcie_adjust_config.9 \ + pci.9 pcie_read_config.9 \ + pci.9 pcie_write_config.9 \ MLINKS+=pfil.9 pfil_add_hook.9 \ pfil.9 pfil_hook_get.9 \ pfil.9 pfil_remove_hook.9 diff --git a/share/man/man9/pci.9 b/share/man/man9/pci.9 index 28bae85..6599e28 100644 --- a/share/man/man9/pci.9 +++ b/share/man/man9/pci.9 @@ -25,7 +25,7 @@ .\" .\" $FreeBSD$ .\" -.Dd March 5, 2012 +.Dd November 5, 2015 .Dt PCI 9 .Os .Sh NAME @@ -42,6 +42,7 @@ .Nm pci_find_device , .Nm pci_find_extcap , .Nm pci_find_htcap , +.Nm pci_find_pcie_root_port , .Nm pci_get_max_read_req , .Nm pci_get_powerstate , .Nm pci_get_vpd_ident , @@ -56,7 +57,10 @@ .Nm pci_save_state , .Nm pci_set_max_read_req , .Nm pci_set_powerstate , -.Nm pci_write_config +.Nm pci_write_config , +.Nm pcie_adjust_config , +.Nm pcie_read_config , +.Nm pcie_write_config .Nd PCI bus interface .Sh SYNOPSIS .In sys/bus.h @@ -86,6 +90,8 @@ .Fn pci_find_extcap "device_t dev" "int capability" "int *capreg" .Ft int .Fn pci_find_htcap "device_t dev" "int capability" "int *capreg" +.Ft device_t +.Fn pci_find_pcie_root_port "device_t dev" .Ft int .Fn pci_get_max_read_req "device_t dev" .Ft int @@ -116,6 +122,18 @@ .Fn pci_set_powerstate "device_t dev" "int state" .Ft void .Fn pci_write_config "device_t dev" "int reg" "uint32_t val" "int width" +.Ft uint32_t +.Fo pcie_adjust_config +.Fa "device_t dev" +.Fa "int reg" +.Fa "uint32_t mask" +.Fa "uint32_t val" +.Fa "int width" +.Fc +.Ft uint32_t +.Fn pcie_read_config "device_t dev" "int reg" "int width" +.Ft void +.Fn pcie_write_config "device_t dev" "int reg" "uint32_t val" "int width" .Sh DESCRIPTION The .Nm @@ -152,6 +170,48 @@ with .Fa width specifying the size of the access. .Pp +The +.Fn pcie_adjust_config +function is used to modify the value of a register in the PCI-express +capability register set of device +.Fa dev . +The offset +.Fa reg +specifies a relative offset in the register set with +.Fa width +specifying the size of the access. +The new value of the register is computed by modifying bits set in +.Fa mask +to the value in +.Fa val . +Any bits not specified in +.Fa mask +are preserved. +The previous value of the register is returned. +.Pp +The +.Fn pcie_read_config +function is used to read the value of a register in the PCI-express +capability register set of device +.Fa dev . +The offset +.Fa reg +specifies a relative offset in the register set with +.Fa width +specifying the size of the access. +.Pp +The +.Fn pcie_write_config +function is used to write the value +.Fa val +to a register in the PCI-express capability register set of device +.Fa dev . +The offset +.Fa reg +specifies a relative offset in the register set with +.Fa width +specifying the size of the access. +.Pp .Em NOTE : Device drivers should only use these functions for functionality that is not available via another @@ -274,6 +334,16 @@ If the capability is not found or the device is not a HyperTransport device, returns an error. .Pp The +.Fn pci_find_pcie_root_port +function walks up the PCI device hierarchy to locate the PCI-express root +port upstream of +.Fa dev . +If a root port is not found, +.Fn pci_find_pcie_root_port +returns +.Dv NULL . +.Pp +The .Fn pci_get_vpd_ident function is used to fetch a device's Vital Product Data .Pq VPD 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 |