diff options
author | jhb <jhb@FreeBSD.org> | 2015-11-05 21:26:06 +0000 |
---|---|---|
committer | jhb <jhb@FreeBSD.org> | 2015-11-05 21:26:06 +0000 |
commit | 0c12e4753ba6951c7105eee5418f69cfda826f9c (patch) | |
tree | 8c409df6aaf8e0cc9b8b82131928b46509d8b572 | |
parent | 42d9b66898a9a9cbc2e8113e8c44b5acebc7109b (diff) | |
download | FreeBSD-src-0c12e4753ba6951c7105eee5418f69cfda826f9c.zip FreeBSD-src-0c12e4753ba6951c7105eee5418f69cfda826f9c.tar.gz |
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.
Reviewed by: imp
MFC after: 1 week
Sponsored by: Chelsio
Differential Revision: https://reviews.freebsd.org/D4088
-rw-r--r-- | share/man/man9/Makefile | 5 | ||||
-rw-r--r-- | share/man/man9/pci.9 | 61 | ||||
-rw-r--r-- | sys/dev/pci/pci.c | 57 | ||||
-rw-r--r-- | sys/dev/pci/pcivar.h | 4 |
4 files changed, 124 insertions, 3 deletions
diff --git a/share/man/man9/Makefile b/share/man/man9/Makefile index 516d6c3..5ae857e 100644 --- a/share/man/man9/Makefile +++ b/share/man/man9/Makefile @@ -1290,7 +1290,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+=pci_iov_schema.9 pci_iov_schema_alloc_node.9 \ pci_iov_schema.9 pci_iov_schema_add_bool.9 \ pci_iov_schema.9 pci_iov_schema_add_string.9 \ diff --git a/share/man/man9/pci.9 b/share/man/man9/pci.9 index 5d98e9c..2321638 100644 --- a/share/man/man9/pci.9 +++ b/share/man/man9/pci.9 @@ -25,7 +25,7 @@ .\" .\" $FreeBSD$ .\" -.Dd July 8, 2015 +.Dd November 5, 2015 .Dt PCI 9 .Os .Sh NAME @@ -58,7 +58,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 @@ -118,6 +121,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" .In dev/pci/pci_iov.h .Ft int .Fn pci_iov_attach "device_t dev" "nvlist_t *pf_schema" "nvlist_t *vf_schema" @@ -159,6 +174,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 diff --git a/sys/dev/pci/pci.c b/sys/dev/pci/pci.c index 39d311f..5116a4f 100644 --- a/sys/dev/pci/pci.c +++ b/sys/dev/pci/pci.c @@ -1917,6 +1917,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. */ diff --git a/sys/dev/pci/pcivar.h b/sys/dev/pci/pcivar.h index 7f2e958..242201b 100644 --- a/sys/dev/pci/pcivar.h +++ b/sys/dev/pci/pcivar.h @@ -551,6 +551,10 @@ 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 |