diff mbox series

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

Message ID 20240822204120.3634-5-wei.huang2@amd.com (mailing list archive)
State Superseded
Headers show
Series PCIe TPH and cache direct injection support | expand

Commit Message

Wei Huang Aug. 22, 2024, 8:41 p.m. UTC
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@amd.com>
Signed-off-by: Eric Van Tassell <Eric.VanTassell@amd.com>
Signed-off-by: Wei Huang <wei.huang2@amd.com>
Reviewed-by: Ajit Khaparde <ajit.khaparde@broadcom.com>
Reviewed-by: Somnath Kotur <somnath.kotur@broadcom.com>
Reviewed-by: Andy Gospodarek <andrew.gospodarek@broadcom.com>
---
 drivers/pci/pcie/tph.c  | 92 +++++++++++++++++++++++++++++++++++++++++
 include/linux/pci-tph.h |  3 ++
 include/linux/pci.h     |  3 ++
 3 files changed, 98 insertions(+)

Comments

Alejandro Lucero Palau Sept. 13, 2024, 11:35 a.m. UTC | #1
On 8/22/24 21:41, Wei Huang wrote:
> 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@amd.com>
> Signed-off-by: Eric Van Tassell <Eric.VanTassell@amd.com>
> Signed-off-by: Wei Huang <wei.huang2@amd.com>
> Reviewed-by: Ajit Khaparde <ajit.khaparde@broadcom.com>
> Reviewed-by: Somnath Kotur <somnath.kotur@broadcom.com>
> Reviewed-by: Andy Gospodarek <andrew.gospodarek@broadcom.com>
> ---
>   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@amd.com>
>    */
>   #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);


No error checked. Same with below pci_write_config_dword.


> +
> +	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
>   };
>
diff mbox series

Patch

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@amd.com>
  */
 #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
 };