]> www.infradead.org Git - users/dwmw2/linux.git/commitdiff
PCI: Add TLP Processing Hints (TPH) support
authorWei Huang <wei.huang2@amd.com>
Wed, 2 Oct 2024 16:59:50 +0000 (11:59 -0500)
committerBjorn Helgaas <bhelgaas@google.com>
Wed, 2 Oct 2024 21:20:01 +0000 (16:20 -0500)
Add support for PCIe TLP Processing Hints (TPH) support (see PCIe r6.2,
sec 6.17).

Add TPH register definitions in pci_regs.h, including the TPH Requester
capability register, TPH Requester control register, TPH Completer
capability, and the ST fields of MSI-X entry.

Introduce pcie_enable_tph() and pcie_disable_tph(), enabling drivers to
toggle TPH support and configure specific ST mode as needed. Also add a new
kernel parameter, "pci=notph", allowing users to disable TPH support across
the entire system.

Link: https://lore.kernel.org/r/20241002165954.128085-2-wei.huang2@amd.com
Co-developed-by: Jing Liu <jing2.liu@intel.com>
Co-developed-by: Paul Luse <paul.e.luse@linux.intel.com>
Co-developed-by: Eric Van Tassell <Eric.VanTassell@amd.com>
Signed-off-by: Jing Liu <jing2.liu@intel.com>
Signed-off-by: Paul Luse <paul.e.luse@linux.intel.com>
Signed-off-by: Eric Van Tassell <Eric.VanTassell@amd.com>
Signed-off-by: Wei Huang <wei.huang2@amd.com>
Signed-off-by: Bjorn Helgaas <bhelgaas@google.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>
Reviewed-by: Jonathan Cameron <Jonathan.Cameron@huawei.com>
Reviewed-by: Lukas Wunner <lukas@wunner.de>
Documentation/admin-guide/kernel-parameters.txt
drivers/pci/Kconfig
drivers/pci/Makefile
drivers/pci/pci.c
drivers/pci/pci.h
drivers/pci/probe.c
drivers/pci/tph.c [new file with mode: 0644]
include/linux/pci-tph.h [new file with mode: 0644]
include/linux/pci.h
include/uapi/linux/pci_regs.h

index 1518343bbe2237f1d577df5656339d6224b769be..178995b07451f5b688da137f856b3764a2d9f0cb 100644 (file)
                nomio           [S390] Do not use MIO instructions.
                norid           [S390] ignore the RID field and force use of
                                one PCI domain per PCI function
+               notph           [PCIE] If the PCIE_TPH kernel config parameter
+                               is enabled, this kernel boot option can be used
+                               to disable PCIe TLP Processing Hints support
+                               system-wide.
 
        pcie_aspm=      [PCIE] Forcibly enable or ignore PCIe Active State Power
                        Management.
index 0d94e4a967d81d50b9a3f3aebda56d37b36fc271..2f270e4414b317f1c2eb364bd2fc34b41d992558 100644 (file)
@@ -173,6 +173,15 @@ config PCI_PASID
 
          If unsure, say N.
 
+config PCIE_TPH
+       bool "TLP Processing Hints"
+       help
+         This option adds support for PCIe TLP Processing Hints (TPH).
+         TPH allows endpoint devices to provide optimization hints, such as
+         desired caching behavior, for requests that target memory space.
+         These hints, called Steering Tags, can empower the system hardware
+         to optimize the utilization of platform resources.
+
 config PCI_P2PDMA
        bool "PCI peer-to-peer transfer support"
        depends on ZONE_DEVICE
index 374c5c06d92f9ddc4c8dfc2663b1cc8c979f5b41..b2a100f2e24a3748f8ab223e8da48888b659f013 100644 (file)
@@ -36,6 +36,7 @@ obj-$(CONFIG_VGA_ARB)         += vgaarb.o
 obj-$(CONFIG_PCI_DOE)          += doe.o
 obj-$(CONFIG_PCI_DYNAMIC_OF_NODES) += of_property.o
 obj-$(CONFIG_PCI_NPEM)         += npem.o
+obj-$(CONFIG_PCIE_TPH)         += tph.o
 
 # Endpoint library must be initialized before its users
 obj-$(CONFIG_PCI_ENDPOINT)     += endpoint/
index 7d85c04fbba2ae937f15f2a5b481e002ce81f334..89dafecc869bd3992c4ced53dccef614dcea67c1 100644 (file)
@@ -1828,6 +1828,7 @@ int pci_save_state(struct pci_dev *dev)
        pci_save_dpc_state(dev);
        pci_save_aer_state(dev);
        pci_save_ptm_state(dev);
+       pci_save_tph_state(dev);
        return pci_save_vc_state(dev);
 }
 EXPORT_SYMBOL(pci_save_state);
@@ -1933,6 +1934,7 @@ void pci_restore_state(struct pci_dev *dev)
        pci_restore_rebar_state(dev);
        pci_restore_dpc_state(dev);
        pci_restore_ptm_state(dev);
+       pci_restore_tph_state(dev);
 
        pci_aer_clear_status(dev);
        pci_restore_aer_state(dev);
@@ -6896,6 +6898,8 @@ static int __init pci_setup(char *str)
                                pci_no_domains();
                        } else if (!strncmp(str, "noari", 5)) {
                                pcie_ari_disabled = true;
+                       } else if (!strncmp(str, "notph", 5)) {
+                               pci_no_tph();
                        } else if (!strncmp(str, "cbiosize=", 9)) {
                                pci_cardbus_io_size = memparse(str + 9, &str);
                        } else if (!strncmp(str, "cbmemsize=", 10)) {
index 14d00ce45bfa954daea1a297c00c2c08d98636d3..d89fdbf04f36338f7f363ee5e9bd0b160d5c3b28 100644 (file)
@@ -597,6 +597,18 @@ static inline int pci_iov_bus_range(struct pci_bus *bus)
 
 #endif /* CONFIG_PCI_IOV */
 
+#ifdef CONFIG_PCIE_TPH
+void pci_restore_tph_state(struct pci_dev *dev);
+void pci_save_tph_state(struct pci_dev *dev);
+void pci_no_tph(void);
+void pci_tph_init(struct pci_dev *dev);
+#else
+static inline void pci_restore_tph_state(struct pci_dev *dev) { }
+static inline void pci_save_tph_state(struct pci_dev *dev) { }
+static inline void pci_no_tph(void) { }
+static inline void pci_tph_init(struct pci_dev *dev) { }
+#endif
+
 #ifdef CONFIG_PCIE_PTM
 void pci_ptm_init(struct pci_dev *dev);
 void pci_save_ptm_state(struct pci_dev *dev);
index 4f68414c308609ed99e675a943e73a8f6ae0dfe3..b086d53a90485581263699f985ad2ff1a45a6851 100644 (file)
@@ -2495,6 +2495,7 @@ static void pci_init_capabilities(struct pci_dev *dev)
        pci_dpc_init(dev);              /* Downstream Port Containment */
        pci_rcec_init(dev);             /* Root Complex Event Collector */
        pci_doe_init(dev);              /* Data Object Exchange */
+       pci_tph_init(dev);              /* TLP Processing Hints */
 
        pcie_report_downtraining(dev);
        pci_init_reset_methods(dev);
diff --git a/drivers/pci/tph.c b/drivers/pci/tph.c
new file mode 100644 (file)
index 0000000..4d6317c
--- /dev/null
@@ -0,0 +1,197 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * TPH (TLP Processing Hints) support
+ *
+ * Copyright (C) 2024 Advanced Micro Devices, Inc.
+ *     Eric Van Tassell <Eric.VanTassell@amd.com>
+ *     Wei Huang <wei.huang2@amd.com>
+ */
+#include <linux/pci.h>
+#include <linux/bitfield.h>
+#include <linux/pci-tph.h>
+
+#include "pci.h"
+
+/* System-wide TPH disabled */
+static bool pci_tph_disabled;
+
+static u8 get_st_modes(struct pci_dev *pdev)
+{
+       u32 reg;
+
+       pci_read_config_dword(pdev, pdev->tph_cap + PCI_TPH_CAP, &reg);
+       reg &= PCI_TPH_CAP_ST_NS | PCI_TPH_CAP_ST_IV | PCI_TPH_CAP_ST_DS;
+
+       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_disable_tph - Turn off TPH support for device
+ * @pdev: PCI device
+ *
+ * Return: none
+ */
+void pcie_disable_tph(struct pci_dev *pdev)
+{
+       if (!pdev->tph_cap)
+               return;
+
+       if (!pdev->tph_enabled)
+               return;
+
+       pci_write_config_dword(pdev, pdev->tph_cap + PCI_TPH_CTRL, 0);
+
+       pdev->tph_mode = 0;
+       pdev->tph_req_type = 0;
+       pdev->tph_enabled = 0;
+}
+EXPORT_SYMBOL(pcie_disable_tph);
+
+/**
+ * pcie_enable_tph - Enable TPH support for device using a specific ST mode
+ * @pdev: PCI device
+ * @mode: ST mode to enable. Current supported modes include:
+ *
+ *   - PCI_TPH_ST_NS_MODE: NO ST Mode
+ *   - PCI_TPH_ST_IV_MODE: Interrupt Vector Mode
+ *   - PCI_TPH_ST_DS_MODE: Device Specific Mode
+ *
+ * Check whether the mode is actually supported by the device before enabling
+ * and return an error if not. Additionally determine 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;
+
+       /* Honor "notph" kernel parameter */
+       if (pci_tph_disabled)
+               return -EINVAL;
+
+       if (!pdev->tph_cap)
+               return -EINVAL;
+
+       if (pdev->tph_enabled)
+               return -EBUSY;
+
+       /* Sanitize and check ST mode compatibility */
+       mode &= PCI_TPH_CTRL_MODE_SEL_MASK;
+       dev_modes = get_st_modes(pdev);
+       if (!((1 << mode) & dev_modes))
+               return -EINVAL;
+
+       pdev->tph_mode = mode;
+
+       /* Get req_type supported by device and its Root Port */
+       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);
+
+       if (pdev->tph_req_type == PCI_TPH_REQ_DISABLE)
+               return -EINVAL;
+
+       /* 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);
+
+void pci_restore_tph_state(struct pci_dev *pdev)
+{
+       struct pci_cap_saved_state *save_state;
+       u32 *cap;
+
+       if (!pdev->tph_cap)
+               return;
+
+       if (!pdev->tph_enabled)
+               return;
+
+       save_state = pci_find_saved_ext_cap(pdev, PCI_EXT_CAP_ID_TPH);
+       if (!save_state)
+               return;
+
+       /* Restore control register and all ST entries */
+       cap = &save_state->cap.data[0];
+       pci_write_config_dword(pdev, pdev->tph_cap + PCI_TPH_CTRL, *cap++);
+}
+
+void pci_save_tph_state(struct pci_dev *pdev)
+{
+       struct pci_cap_saved_state *save_state;
+       u32 *cap;
+
+       if (!pdev->tph_cap)
+               return;
+
+       if (!pdev->tph_enabled)
+               return;
+
+       save_state = pci_find_saved_ext_cap(pdev, PCI_EXT_CAP_ID_TPH);
+       if (!save_state)
+               return;
+
+       /* Save control register */
+       cap = &save_state->cap.data[0];
+       pci_read_config_dword(pdev, pdev->tph_cap + PCI_TPH_CTRL, cap++);
+}
+
+void pci_no_tph(void)
+{
+       pci_tph_disabled = true;
+
+       pr_info("PCIe TPH is disabled\n");
+}
+
+void pci_tph_init(struct pci_dev *pdev)
+{
+       u32 save_size;
+
+       pdev->tph_cap = pci_find_ext_capability(pdev, PCI_EXT_CAP_ID_TPH);
+       if (!pdev->tph_cap)
+               return;
+
+       save_size = sizeof(u32);
+       pci_add_ext_cap_save_buffer(pdev, PCI_EXT_CAP_ID_TPH, save_size);
+}
diff --git a/include/linux/pci-tph.h b/include/linux/pci-tph.h
new file mode 100644 (file)
index 0000000..58654a3
--- /dev/null
@@ -0,0 +1,21 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * TPH (TLP Processing Hints)
+ *
+ * Copyright (C) 2024 Advanced Micro Devices, Inc.
+ *     Eric Van Tassell <Eric.VanTassell@amd.com>
+ *     Wei Huang <wei.huang2@amd.com>
+ */
+#ifndef LINUX_PCI_TPH_H
+#define LINUX_PCI_TPH_H
+
+#ifdef CONFIG_PCIE_TPH
+void pcie_disable_tph(struct pci_dev *pdev);
+int pcie_enable_tph(struct pci_dev *pdev, int mode);
+#else
+static inline void pcie_disable_tph(struct pci_dev *pdev) { }
+static inline int pcie_enable_tph(struct pci_dev *pdev, int mode)
+{ return -EINVAL; }
+#endif
+
+#endif /* LINUX_PCI_TPH_H */
index 573b4c4c2be61f86338e753d97bc618a4dd25ccd..8351d76b6e126697eec27337962b04a62652f773 100644 (file)
@@ -434,6 +434,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 */
@@ -534,6 +535,12 @@ struct pci_dev {
 
        /* These methods index pci_reset_fn_methods[] */
        u8 reset_methods[PCI_NUM_RESET_METHODS]; /* In priority order */
+
+#ifdef CONFIG_PCIE_TPH
+       u16             tph_cap;        /* TPH capability offset */
+       u8              tph_mode;       /* TPH mode */
+       u8              tph_req_type;   /* TPH requester type */
+#endif
 };
 
 static inline struct pci_dev *pci_physfn(struct pci_dev *dev)
index 12323b3334a9c16beda9e13a039cd38ae2cacfad..155dea741615c61affe1f81050d3a961fecc6043 100644 (file)
 #define PCI_MSIX_ENTRY_UPPER_ADDR      0x4  /* Message Upper Address */
 #define PCI_MSIX_ENTRY_DATA            0x8  /* Message Data */
 #define PCI_MSIX_ENTRY_VECTOR_CTRL     0xc  /* Vector Control */
-#define  PCI_MSIX_ENTRY_CTRL_MASKBIT   0x00000001
+#define  PCI_MSIX_ENTRY_CTRL_MASKBIT   0x00000001  /* Mask Bit */
+#define  PCI_MSIX_ENTRY_CTRL_ST                0xffff0000  /* Steering Tag */
 
 /* CompactPCI Hotswap Register */
 
 #define  PCI_EXP_DEVCAP2_ATOMIC_COMP64 0x00000100 /* 64b AtomicOp completion */
 #define  PCI_EXP_DEVCAP2_ATOMIC_COMP128        0x00000200 /* 128b AtomicOp completion */
 #define  PCI_EXP_DEVCAP2_LTR           0x00000800 /* Latency tolerance reporting */
+#define  PCI_EXP_DEVCAP2_TPH_COMP_MASK 0x00003000 /* TPH completer support */
 #define  PCI_EXP_DEVCAP2_OBFF_MASK     0x000c0000 /* OBFF support mechanism */
 #define  PCI_EXP_DEVCAP2_OBFF_MSG      0x00040000 /* New message signaling */
 #define  PCI_EXP_DEVCAP2_OBFF_WAKE     0x00080000 /* Re-use WAKE# for OBFF */
 #define  PCI_DPA_CAP_SUBSTATE_MASK     0x1F    /* # substates - 1 */
 #define PCI_DPA_BASE_SIZEOF    16      /* size with 0 substates */
 
+/* TPH Completer Support */
+#define PCI_EXP_DEVCAP2_TPH_COMP_NONE          0x0 /* None */
+#define PCI_EXP_DEVCAP2_TPH_COMP_TPH_ONLY      0x1 /* TPH only */
+#define PCI_EXP_DEVCAP2_TPH_COMP_EXT_TPH       0x3 /* TPH and Extended TPH */
+
 /* TPH Requester */
 #define PCI_TPH_CAP            4       /* capability register */
-#define  PCI_TPH_CAP_LOC_MASK  0x600   /* location mask */
-#define   PCI_TPH_LOC_NONE     0x000   /* no location */
-#define   PCI_TPH_LOC_CAP      0x200   /* in capability */
-#define   PCI_TPH_LOC_MSIX     0x400   /* in MSI-X */
-#define PCI_TPH_CAP_ST_MASK    0x07FF0000      /* ST table mask */
-#define PCI_TPH_CAP_ST_SHIFT   16      /* ST table shift */
-#define PCI_TPH_BASE_SIZEOF    0xc     /* size with no ST table */
+#define  PCI_TPH_CAP_ST_NS     0x00000001 /* No ST Mode Supported */
+#define  PCI_TPH_CAP_ST_IV     0x00000002 /* Interrupt Vector Mode Supported */
+#define  PCI_TPH_CAP_ST_DS     0x00000004 /* Device Specific Mode Supported */
+#define  PCI_TPH_CAP_EXT_TPH   0x00000100 /* Ext TPH Requester Supported */
+#define  PCI_TPH_CAP_LOC_MASK  0x00000600 /* ST Table Location */
+#define   PCI_TPH_LOC_NONE     0x00000000 /* Not present */
+#define   PCI_TPH_LOC_CAP      0x00000200 /* In capability */
+#define   PCI_TPH_LOC_MSIX     0x00000400 /* In MSI-X */
+#define  PCI_TPH_CAP_ST_MASK   0x07FF0000 /* ST Table Size */
+#define  PCI_TPH_CAP_ST_SHIFT  16      /* ST Table Size shift */
+#define PCI_TPH_BASE_SIZEOF    0xc     /* Size with no ST table */
+
+#define PCI_TPH_CTRL           8       /* control register */
+#define  PCI_TPH_CTRL_MODE_SEL_MASK    0x00000007 /* ST Mode Select */
+#define   PCI_TPH_ST_NS_MODE           0x0 /* No ST Mode */
+#define   PCI_TPH_ST_IV_MODE           0x1 /* Interrupt Vector Mode */
+#define   PCI_TPH_ST_DS_MODE           0x2 /* Device Specific Mode */
+#define  PCI_TPH_CTRL_REQ_EN_MASK      0x00000300 /* TPH Requester Enable */
+#define   PCI_TPH_REQ_DISABLE          0x0 /* No TPH requests allowed */
+#define   PCI_TPH_REQ_TPH_ONLY         0x1 /* TPH only requests allowed */
+#define   PCI_TPH_REQ_EXT_TPH          0x3 /* Extended TPH requests allowed */
 
 /* Downstream Port Containment */
 #define PCI_EXP_DPC_CAP                        0x04    /* DPC Capability */