PCI: Add TLP Processing Hints (TPH) support

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>
This commit is contained in:
Wei Huang 2024-10-02 11:59:50 -05:00 committed by Bjorn Helgaas
parent 9852d85ec9
commit f69767a1ad
10 changed files with 285 additions and 8 deletions

View File

@ -4678,6 +4678,10 @@
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.

View 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

View 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/

View 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)) {

View 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);

View 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);

197
drivers/pci/tph.c Normal file
View File

@ -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);
}

21
include/linux/pci-tph.h Normal file
View File

@ -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 */

View 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)

View File

@ -340,7 +340,8 @@
#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 */
@ -659,6 +660,7 @@
#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 */
@ -1023,15 +1025,34 @@
#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 */