diff mbox series

[v1,08/24] pci: pci-uclass: Add support for Single-Root I/O Virtualization

Message ID 20200724100856.1482324-9-sr@denx.de
State Superseded
Delegated to: Stefan Roese
Headers show
Series arm: Introduce Marvell/Cavium OcteonTX/TX2 | expand

Commit Message

Stefan Roese July 24, 2020, 10:08 a.m. UTC
From: Suneel Garapati <sgarapati@marvell.com>

SR-IOV - Single Root I/O Virtualization
PF - Physical Function VF - Virtual Function

If SR-IOV capability is present, use it to initialize Virtual Function
PCI device instances. pci_sriov_init function will read SR-IOV
registers to create VF devices under the PF PCI device and also bind
driver if available. This function needs to be invoked from Physical
function device driver which expects VF device support, creating
minimal impact on existing framework.

Signed-off-by: Suneel Garapati <sgarapati@marvell.com>
Cc: Simon Glass <sjg@chromium.org>
Cc: Bin Meng <bmeng.cn@gmail.com>

Signed-off-by: Stefan Roese <sr@denx.de>
---

Changes in v1:
- Change patch subject
- Enhance commit text: expanded the abbreviations
- Changed some printf() to debug()
- Changed 0 to '\0' in memset()
- Comments for variables in struct pci_child_platdata added
- Comments for newly introduced functions added in prototype / header

 drivers/pci/Kconfig      |  10 ++++
 drivers/pci/pci-uclass.c | 114 +++++++++++++++++++++++++++++++++++++++
 include/pci.h            |  38 +++++++++++++
 3 files changed, 162 insertions(+)

Comments

Simon Glass July 28, 2020, 7:01 p.m. UTC | #1
Hi Stefan,

On Fri, 24 Jul 2020 at 04:09, Stefan Roese <sr@denx.de> wrote:
>
> From: Suneel Garapati <sgarapati@marvell.com>
>
> SR-IOV - Single Root I/O Virtualization
> PF - Physical Function VF - Virtual Function
>
> If SR-IOV capability is present, use it to initialize Virtual Function
> PCI device instances. pci_sriov_init function will read SR-IOV
> registers to create VF devices under the PF PCI device and also bind
> driver if available. This function needs to be invoked from Physical
> function device driver which expects VF device support, creating
> minimal impact on existing framework.
>
> Signed-off-by: Suneel Garapati <sgarapati@marvell.com>
> Cc: Simon Glass <sjg@chromium.org>
> Cc: Bin Meng <bmeng.cn@gmail.com>
>
> Signed-off-by: Stefan Roese <sr@denx.de>
> ---
>
> Changes in v1:
> - Change patch subject
> - Enhance commit text: expanded the abbreviations
> - Changed some printf() to debug()
> - Changed 0 to '\0' in memset()
> - Comments for variables in struct pci_child_platdata added
> - Comments for newly introduced functions added in prototype / header
>
>  drivers/pci/Kconfig      |  10 ++++
>  drivers/pci/pci-uclass.c | 114 +++++++++++++++++++++++++++++++++++++++
>  include/pci.h            |  38 +++++++++++++
>  3 files changed, 162 insertions(+)

This looks OK but all of these patches need updates to the sandbox
tests to cover the new functionality.

Regards,
SImon
diff mbox series

Patch

diff --git a/drivers/pci/Kconfig b/drivers/pci/Kconfig
index 14c96dd108..3676fa85db 100644
--- a/drivers/pci/Kconfig
+++ b/drivers/pci/Kconfig
@@ -53,6 +53,16 @@  config PCI_REGION_MULTI_ENTRY
 	  region type. This helps to add support for SoC's like OcteonTX/TX2
 	  where every peripheral is on the PCI bus.
 
+config PCI_SRIOV
+	bool "Enable Single Root I/O Virtualization support for PCI"
+	depends on PCI || DM_PCI
+	default n
+	help
+	  Say Y here if you want to enable PCI Single Root I/O Virtualization
+	  capability support. This helps to enumerate Virtual Function devices
+	  if available on a PCI Physical Function device and probe for
+	  applicable drivers.
+
 config PCIE_ECAM_GENERIC
 	bool "Generic ECAM-based PCI host controller support"
 	default n
diff --git a/drivers/pci/pci-uclass.c b/drivers/pci/pci-uclass.c
index 9985f3dda1..b0f2b5b77e 100644
--- a/drivers/pci/pci-uclass.c
+++ b/drivers/pci/pci-uclass.c
@@ -1545,6 +1545,120 @@  int dm_pci_flr(struct udevice *dev)
 	return 0;
 }
 
+#if defined(CONFIG_PCI_SRIOV)
+int pci_sriov_init(struct udevice *pdev, int vf_en)
+{
+	u16 vendor, device;
+	struct udevice *bus;
+	struct udevice *dev;
+	pci_dev_t bdf;
+	u16 ctrl;
+	u16 num_vfs;
+	u16 total_vf;
+	u16 vf_offset;
+	u16 vf_stride;
+	int vf, ret;
+	int pos;
+
+	pos = dm_pci_find_ext_capability(pdev, PCI_EXT_CAP_ID_SRIOV);
+	if (!pos) {
+		debug("Error: SRIOV capability not found\n");
+		return -ENOENT;
+	}
+
+	dm_pci_read_config16(pdev, pos + PCI_SRIOV_CTRL, &ctrl);
+
+	dm_pci_read_config16(pdev, pos + PCI_SRIOV_TOTAL_VF, &total_vf);
+	if (vf_en > total_vf)
+		vf_en = total_vf;
+	dm_pci_write_config16(pdev, pos + PCI_SRIOV_NUM_VF, vf_en);
+
+	ctrl |= PCI_SRIOV_CTRL_VFE | PCI_SRIOV_CTRL_MSE;
+	dm_pci_write_config16(pdev, pos + PCI_SRIOV_CTRL, ctrl);
+
+	dm_pci_read_config16(pdev, pos + PCI_SRIOV_NUM_VF, &num_vfs);
+	if (num_vfs > vf_en)
+		num_vfs = vf_en;
+
+	dm_pci_read_config16(pdev, pos + PCI_SRIOV_VF_OFFSET, &vf_offset);
+	dm_pci_read_config16(pdev, pos + PCI_SRIOV_VF_STRIDE, &vf_stride);
+
+	dm_pci_read_config16(pdev, PCI_VENDOR_ID, &vendor);
+	dm_pci_read_config16(pdev, pos + PCI_SRIOV_VF_DID, &device);
+
+	bdf = dm_pci_get_bdf(pdev);
+
+	pci_get_bus(PCI_BUS(bdf), &bus);
+
+	if (!bus)
+		return -ENODEV;
+
+	bdf += PCI_BDF(0, 0, vf_offset);
+
+	for (vf = 0; vf < num_vfs; vf++) {
+		struct pci_child_platdata *pplat;
+		ulong class;
+
+		pci_bus_read_config(bus, bdf, PCI_CLASS_DEVICE,
+				    &class, PCI_SIZE_16);
+
+		debug("%s: bus %d/%s: found VF %x:%x\n", __func__,
+		      bus->seq, bus->name, PCI_DEV(bdf), PCI_FUNC(bdf));
+
+		/* Find this device in the device tree */
+		ret = pci_bus_find_devfn(bus, PCI_MASK_BUS(bdf), &dev);
+
+		if (ret == -ENODEV) {
+			struct pci_device_id find_id;
+
+			memset(&find_id, '\0', sizeof(find_id));
+			find_id.vendor = vendor;
+			find_id.device = device;
+			find_id.class = class;
+
+			ret = pci_find_and_bind_driver(bus, &find_id,
+						       bdf, &dev);
+
+			if (ret)
+				return ret;
+		}
+
+		/* Update the platform data */
+		pplat = dev_get_parent_platdata(dev);
+		pplat->devfn = PCI_MASK_BUS(bdf);
+		pplat->vendor = vendor;
+		pplat->device = device;
+		pplat->class = class;
+		pplat->is_virtfn = true;
+		pplat->pfdev = pdev;
+		pplat->virtid = vf * vf_stride + vf_offset;
+
+		debug("%s: bus %d/%s: found VF %x:%x %x:%x class %lx id %x\n",
+		      __func__, dev->seq, dev->name, PCI_DEV(bdf),
+		      PCI_FUNC(bdf), vendor, device, class, pplat->virtid);
+		bdf += PCI_BDF(0, 0, vf_stride);
+	}
+
+	return 0;
+}
+
+int pci_sriov_get_totalvfs(struct udevice *pdev)
+{
+	u16 total_vf;
+	int pos;
+
+	pos = dm_pci_find_ext_capability(pdev, PCI_EXT_CAP_ID_SRIOV);
+	if (!pos) {
+		debug("Error: SRIOV capability not found\n");
+		return -ENOENT;
+	}
+
+	dm_pci_read_config16(pdev, pos + PCI_SRIOV_TOTAL_VF, &total_vf);
+
+	return total_vf;
+}
+#endif /* SRIOV */
+
 UCLASS_DRIVER(pci) = {
 	.id		= UCLASS_PCI,
 	.name		= "pci",
diff --git a/include/pci.h b/include/pci.h
index 53f1386fd4..4f4b11df4a 100644
--- a/include/pci.h
+++ b/include/pci.h
@@ -493,6 +493,17 @@ 
 #define PCI_EXP_SLTCAP		20	/* Slot Capabilities */
 #define  PCI_EXP_SLTCAP_PSN	0xfff80000 /* Physical Slot Number */
 #define PCI_EXP_LNKCTL2		48	/* Link Control 2 */
+/* Single Root I/O Virtualization Registers */
+#define PCI_SRIOV_CAP		0x04	/* SR-IOV Capabilities */
+#define PCI_SRIOV_CTRL		0x08	/* SR-IOV Control */
+#define  PCI_SRIOV_CTRL_VFE	0x01	/* VF Enable */
+#define  PCI_SRIOV_CTRL_MSE	0x08	/* VF Memory Space Enable */
+#define PCI_SRIOV_INITIAL_VF	0x0c	/* Initial VFs */
+#define PCI_SRIOV_TOTAL_VF	0x0e	/* Total VFs */
+#define PCI_SRIOV_NUM_VF	0x10	/* Number of VFs */
+#define PCI_SRIOV_VF_OFFSET	0x14	/* First VF Offset */
+#define PCI_SRIOV_VF_STRIDE	0x16	/* Following VF Stride */
+#define PCI_SRIOV_VF_DID	0x1a	/* VF Device ID */
 
 /* Include the ID list */
 
@@ -890,12 +901,20 @@  struct udevice;
  * @vendor:	PCI vendor ID (see pci_ids.h)
  * @device:	PCI device ID (see pci_ids.h)
  * @class:	PCI class, 3 bytes: (base, sub, prog-if)
+ * @is_virtfn:	True for Virtual Function device
+ * @pfdev:	Handle to Physical Function device
+ * @virtid:	Virtual Function Index
  */
 struct pci_child_platdata {
 	int devfn;
 	unsigned short vendor;
 	unsigned short device;
 	unsigned int class;
+
+	/* Variables for CONFIG_PCI_SRIOV */
+	bool is_virtfn;
+	struct udevice *pfdev;
+	int virtid;
 };
 
 /* PCI bus operations */
@@ -1208,6 +1227,25 @@  int pci_generic_mmap_read_config(
 	ulong *valuep,
 	enum pci_size_t size);
 
+#if defined(CONFIG_PCI_SRIOV)
+/**
+ * pci_sriov_init() - Scan Virtual Function devices
+ *
+ * @pdev:	Physical Function udevice handle
+ * @vf_en:	Number of Virtual Function devices to enable
+ * @return 0 on success, -ve on error
+ */
+int pci_sriov_init(struct udevice *pdev, int vf_en);
+
+/**
+ * pci_sriov_get_totalvfs() - Get total available Virtual Function devices
+ *
+ * @pdev:	Physical Function udevice handle
+ * @return count on success, -ve on error
+ */
+int pci_sriov_get_totalvfs(struct udevice *pdev);
+#endif
+
 #ifdef CONFIG_DM_PCI_COMPAT
 /* Compatibility with old naming */
 static inline int pci_write_config_dword(pci_dev_t pcidev, int offset,