Index: head/share/man/man9/Makefile =================================================================== --- head/share/man/man9/Makefile +++ head/share/man/man9/Makefile @@ -1290,7 +1290,10 @@ 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 \ Index: head/share/man/man9/pci.9 =================================================================== --- head/share/man/man9/pci.9 +++ head/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 @@ .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 Index: head/sys/dev/pci/pci.c =================================================================== --- head/sys/dev/pci/pci.c +++ head/sys/dev/pci/pci.c @@ -1917,6 +1917,63 @@ 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. */ Index: head/sys/dev/pci/pcivar.h =================================================================== --- head/sys/dev/pci/pcivar.h +++ head/sys/dev/pci/pcivar.h @@ -551,6 +551,10 @@ 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