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
This commit is contained in:
jhb 2015-11-05 21:26:06 +00:00
parent 42d9b66898
commit 0c12e4753b
4 changed files with 124 additions and 3 deletions

View File

@ -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 \

View File

@ -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

View File

@ -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.
*/

View File

@ -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