svn commit: r290809 - in stable: 10/share/man/man9 10/sys/dev/pci 9/share/man/man9 9/sys/dev/pci
John Baldwin
jhb at FreeBSD.org
Sat Nov 14 01:06:47 UTC 2015
Author: jhb
Date: Sat Nov 14 01:06:45 2015
New Revision: 290809
URL: https://svnweb.freebsd.org/changeset/base/290809
Log:
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.
Modified:
stable/9/share/man/man9/Makefile
stable/9/share/man/man9/pci.9
stable/9/sys/dev/pci/pci.c
stable/9/sys/dev/pci/pcivar.h
Directory Properties:
stable/9/share/man/man9/ (props changed)
stable/9/sys/ (props changed)
stable/9/sys/dev/ (props changed)
Changes in other areas also in this revision:
Modified:
stable/10/share/man/man9/Makefile
stable/10/share/man/man9/pci.9
stable/10/sys/dev/pci/pci.c
stable/10/sys/dev/pci/pcivar.h
Directory Properties:
stable/10/ (props changed)
Modified: stable/9/share/man/man9/Makefile
==============================================================================
--- stable/9/share/man/man9/Makefile Sat Nov 14 00:04:42 2015 (r290808)
+++ stable/9/share/man/man9/Makefile Sat Nov 14 01:06:45 2015 (r290809)
@@ -978,6 +978,7 @@ MLINKS+=pci.9 pci_alloc_msi.9 \
pci.9 pci_find_cap.9 \
pci.9 pci_find_dbsf.9 \
pci.9 pci_find_device.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 \
@@ -992,7 +993,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
Modified: stable/9/share/man/man9/pci.9
==============================================================================
--- stable/9/share/man/man9/pci.9 Sat Nov 14 00:04:42 2015 (r290808)
+++ stable/9/share/man/man9/pci.9 Sat Nov 14 01:06:45 2015 (r290809)
@@ -25,7 +25,7 @@
.\"
.\" $FreeBSD$
.\"
-.Dd March 5, 2012
+.Dd November 5, 2015
.Dt PCI 9
.Os
.Sh NAME
@@ -40,6 +40,7 @@
.Nm pci_find_cap ,
.Nm pci_find_dbsf ,
.Nm pci_find_device ,
+.Nm pci_find_pcie_root_port ,
.Nm pci_get_max_read_req ,
.Nm pci_get_powerstate ,
.Nm pci_get_vpd_ident ,
@@ -54,7 +55,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
@@ -80,6 +84,8 @@
.Fn pci_find_dbsf "uint32_t domain" "uint8_t bus" "uint8_t slot" "uint8_t func"
.Ft device_t
.Fn pci_find_device "uint16_t vendor" "uint16_t device"
+.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
@@ -110,6 +116,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
@@ -146,6 +164,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
@@ -225,6 +285,16 @@ If the capability is not found or the de
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
Modified: stable/9/sys/dev/pci/pci.c
==============================================================================
--- stable/9/sys/dev/pci/pci.c Sat Nov 14 00:04:42 2015 (r290808)
+++ stable/9/sys/dev/pci/pci.c Sat Nov 14 01:06:45 2015 (r290809)
@@ -1814,6 +1814,63 @@ pci_set_max_read_req(device_t dev, int s
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.
*/
@@ -4686,3 +4743,44 @@ pci_restore_state(device_t dev)
dinfo = device_get_ivars(dev);
pci_cfg_restore(dev, dinfo);
}
+
+/* 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;
+ }
+}
Modified: stable/9/sys/dev/pci/pcivar.h
==============================================================================
--- stable/9/sys/dev/pci/pcivar.h Sat Nov 14 00:04:42 2015 (r290808)
+++ stable/9/sys/dev/pci/pcivar.h Sat Nov 14 01:06:45 2015 (r290809)
@@ -475,10 +475,15 @@ int pci_msix_device_blacklisted(device_t
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);
#endif /* _SYS_BUS_H_ */
More information about the svn-src-stable-9
mailing list