[PATCH V4 04/12] PCI/TPH: Add pcie_enable_tph() to enable TPH

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Allow drivers to enable TPH support using a specific ST mode. It checks
whether the mode is actually supported by the device before enabling.
Additionally determines what types of requests, TPH (8-bit) or Extended
TPH (16-bit), can be issued by the device based on the device's TPH
Requester capability and its Root Port's Completer capability.

Co-developed-by: Eric Van Tassell <Eric.VanTassell@xxxxxxx>
Signed-off-by: Eric Van Tassell <Eric.VanTassell@xxxxxxx>
Signed-off-by: Wei Huang <wei.huang2@xxxxxxx>
Reviewed-by: Ajit Khaparde <ajit.khaparde@xxxxxxxxxxxx>
Reviewed-by: Somnath Kotur <somnath.kotur@xxxxxxxxxxxx>
Reviewed-by: Andy Gospodarek <andrew.gospodarek@xxxxxxxxxxxx>
---
 drivers/pci/pcie/tph.c  | 92 +++++++++++++++++++++++++++++++++++++++++
 include/linux/pci-tph.h |  3 ++
 include/linux/pci.h     |  3 ++
 3 files changed, 98 insertions(+)

diff --git a/drivers/pci/pcie/tph.c b/drivers/pci/pcie/tph.c
index a28dced3097d..14ad8c5e895c 100644
--- a/drivers/pci/pcie/tph.c
+++ b/drivers/pci/pcie/tph.c
@@ -7,6 +7,7 @@
  *     Wei Huang <wei.huang2@xxxxxxx>
  */
 #include <linux/pci.h>
+#include <linux/bitfield.h>
 #include <linux/pci-tph.h>
 
 #include "../pci.h"
@@ -21,6 +22,97 @@ static u8 get_st_modes(struct pci_dev *pdev)
 	return reg;
 }
 
+/* Return device's Root Port completer capability */
+static u8 get_rp_completer_type(struct pci_dev *pdev)
+{
+	struct pci_dev *rp;
+	u32 reg;
+	int ret;
+
+	rp = pcie_find_root_port(pdev);
+	if (!rp)
+		return 0;
+
+	ret = pcie_capability_read_dword(rp, PCI_EXP_DEVCAP2, &reg);
+	if (ret)
+		return 0;
+
+	return FIELD_GET(PCI_EXP_DEVCAP2_TPH_COMP_MASK, reg);
+}
+
+/**
+ * pcie_enable_tph - Enable TPH support for device using a specific ST mode
+ * @pdev: PCI device
+ * @mode: ST mode to enable, as returned by pcie_tph_modes()
+ *
+ * Checks whether the mode is actually supported by the device before enabling
+ * and returns an error if not. Additionally determines what types of requests,
+ * TPH or extended TPH, can be issued by the device based on its TPH requester
+ * capability and the Root Port's completer capability.
+ *
+ * Return: 0 on success, otherwise negative value (-errno)
+ */
+int pcie_enable_tph(struct pci_dev *pdev, int mode)
+{
+	u32 reg;
+	u8 dev_modes;
+	u8 rp_req_type;
+
+	if (!pdev->tph_cap)
+		return -EINVAL;
+
+	if (pdev->tph_enabled)
+		return -EBUSY;
+
+	/* Check ST mode comptability */
+	dev_modes = get_st_modes(pdev);
+	if (!(mode & dev_modes))
+		return -EINVAL;
+
+	/* Select a supported mode */
+	switch (mode) {
+	case PCI_TPH_CAP_INT_VEC:
+		pdev->tph_mode = PCI_TPH_INT_VEC_MODE;
+		break;
+	case PCI_TPH_CAP_DEV_SPEC:
+		pdev->tph_mode = PCI_TPH_DEV_SPEC_MODE;
+		break;
+	case PCI_TPH_CAP_NO_ST:
+		pdev->tph_mode = PCI_TPH_NO_ST_MODE;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	/* Get req_type supported by device and its Root Port */
+	reg = pci_read_config_dword(pdev, pdev->tph_cap + PCI_TPH_CAP, &reg);
+	if (FIELD_GET(PCI_TPH_CAP_EXT_TPH, reg))
+		pdev->tph_req_type = PCI_TPH_REQ_EXT_TPH;
+	else
+		pdev->tph_req_type = PCI_TPH_REQ_TPH_ONLY;
+
+	rp_req_type = get_rp_completer_type(pdev);
+
+	/* Final req_type is the smallest value of two */
+	pdev->tph_req_type = min(pdev->tph_req_type, rp_req_type);
+
+	/* Write them into TPH control register */
+	pci_read_config_dword(pdev, pdev->tph_cap + PCI_TPH_CTRL, &reg);
+
+	reg &= ~PCI_TPH_CTRL_MODE_SEL_MASK;
+	reg |= FIELD_PREP(PCI_TPH_CTRL_MODE_SEL_MASK, pdev->tph_mode);
+
+	reg &= ~PCI_TPH_CTRL_REQ_EN_MASK;
+	reg |= FIELD_PREP(PCI_TPH_CTRL_REQ_EN_MASK, pdev->tph_req_type);
+
+	pci_write_config_dword(pdev, pdev->tph_cap + PCI_TPH_CTRL, reg);
+
+	pdev->tph_enabled = 1;
+
+	return 0;
+}
+EXPORT_SYMBOL(pcie_enable_tph);
+
 /**
  * pcie_tph_modes - Get the ST modes supported by device
  * @pdev: PCI device
diff --git a/include/linux/pci-tph.h b/include/linux/pci-tph.h
index fa378afe9c7e..cdf561076484 100644
--- a/include/linux/pci-tph.h
+++ b/include/linux/pci-tph.h
@@ -10,8 +10,11 @@
 #define LINUX_PCI_TPH_H
 
 #ifdef CONFIG_PCIE_TPH
+int pcie_enable_tph(struct pci_dev *pdev, int mode);
 int pcie_tph_modes(struct pci_dev *pdev);
 #else
+static inline int pcie_enable_tph(struct pci_dev *pdev, int mode)
+{ return -EINVAL; }
 static inline int pcie_tph_modes(struct pci_dev *pdev) { return 0; }
 #endif
 
diff --git a/include/linux/pci.h b/include/linux/pci.h
index c59e7ecab491..6f05deb6a0bf 100644
--- a/include/linux/pci.h
+++ b/include/linux/pci.h
@@ -433,6 +433,7 @@ struct pci_dev {
 	unsigned int	ats_enabled:1;		/* Address Translation Svc */
 	unsigned int	pasid_enabled:1;	/* Process Address Space ID */
 	unsigned int	pri_enabled:1;		/* Page Request Interface */
+	unsigned int	tph_enabled:1;		/* TLP Processing Hints */
 	unsigned int	is_managed:1;		/* Managed via devres */
 	unsigned int	is_msi_managed:1;	/* MSI release via devres installed */
 	unsigned int	needs_freset:1;		/* Requires fundamental reset */
@@ -533,6 +534,8 @@ struct pci_dev {
 
 #ifdef CONFIG_PCIE_TPH
 	u16		tph_cap;	/* TPH capability offset */
+	u8		tph_mode;	/* TPH mode */
+	u8		tph_req_type;	/* TPH requester type */
 #endif
 };
 
-- 
2.45.1





[Index of Archives]     [Kernel Newbies]     [Security]     [Netfilter]     [Bugtraq]     [Linux FS]     [Yosemite Forum]     [MIPS Linux]     [ARM Linux]     [Linux Security]     [Linux RAID]     [Samba]     [Video 4 Linux]     [Device Mapper]     [Linux Resources]

  Powered by Linux