Skip site navigation (1)Skip section navigation (2)
Date:      Sat, 14 Nov 2015 01:06:45 +0000 (UTC)
From:      John Baldwin <jhb@FreeBSD.org>
To:        src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-stable@freebsd.org, svn-src-stable-10@freebsd.org
Subject:   svn commit: r290809 - in stable: 10/share/man/man9 10/sys/dev/pci 9/share/man/man9 9/sys/dev/pci
Message-ID:  <201511140106.tAE16j5Q097967@repo.freebsd.org>

next in thread | raw e-mail | index | archive | help
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/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)

Changes in other areas also in this revision:
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)

Modified: stable/10/share/man/man9/Makefile
==============================================================================
--- stable/10/share/man/man9/Makefile	Sat Nov 14 00:04:42 2015	(r290808)
+++ stable/10/share/man/man9/Makefile	Sat Nov 14 01:06:45 2015	(r290809)
@@ -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

Modified: stable/10/share/man/man9/pci.9
==============================================================================
--- stable/10/share/man/man9/pci.9	Sat Nov 14 00:04:42 2015	(r290808)
+++ stable/10/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
@@ -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 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/10/sys/dev/pci/pci.c
==============================================================================
--- stable/10/sys/dev/pci/pci.c	Sat Nov 14 00:04:42 2015	(r290808)
+++ stable/10/sys/dev/pci/pci.c	Sat Nov 14 01:06:45 2015	(r290809)
@@ -1895,6 +1895,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.
  */
@@ -5139,3 +5196,44 @@ pci_get_rid_method(device_t dev, device_
 
 	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;
+	}
+}

Modified: stable/10/sys/dev/pci/pcivar.h
==============================================================================
--- stable/10/sys/dev/pci/pcivar.h	Sat Nov 14 00:04:42 2015	(r290808)
+++ stable/10/sys/dev/pci/pcivar.h	Sat Nov 14 01:06:45 2015	(r290809)
@@ -501,10 +501,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);
 
 
 #ifdef BUS_SPACE_MAXADDR



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?201511140106.tAE16j5Q097967>