Add an API function to allow endpoint device drivers to retrieve steering tags for a specific cpu_uid. This is achieved by invoking ACPI _DSM on device's root port. 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 | 162 ++++++++++++++++++++++++++++++++++++++++ include/linux/pci-tph.h | 12 +++ 2 files changed, 174 insertions(+) diff --git a/drivers/pci/pcie/tph.c b/drivers/pci/pcie/tph.c index 7183370b0977..c805c8b1a7d2 100644 --- a/drivers/pci/pcie/tph.c +++ b/drivers/pci/pcie/tph.c @@ -7,12 +7,133 @@ * Wei Huang <wei.huang2@xxxxxxx> */ +#define pr_fmt(fmt) "TPH: " fmt +#define dev_fmt pr_fmt + #include <linux/pci.h> +#include <linux/pci-acpi.h> #include <linux/bitfield.h> #include <linux/pci-tph.h> #include "../pci.h" +/* + * The st_info struct defines the steering tag returned by the firmware _DSM + * method defined in PCI Firmware Spec r3.3, sect 4.6.15 "_DSM to Query Cache + * Locality TPH Features" + * + * @vm_st_valid: 8 bit tag for volatile memory is valid + * @vm_xst_valid: 16 bit tag for volatile memory is valid + * @vm_ignore: 1 => was and will be ignored, 0 => ph should be supplied + * @vm_st: 8 bit steering tag for volatile mem + * @vm_xst: 16 bit steering tag for volatile mem + * @pm_st_valid: 8 bit tag for persistent memory is valid + * @pm_xst_valid: 16 bit tag for persistent memory is valid + * @pm_ph_ignore: 1 => was and will be ignore, 0 => ph should be supplied + * @pm_st: 8 bit steering tag for persistent mem + * @pm_xst: 16 bit steering tag for persistent mem + */ +union st_info { + struct { + u64 vm_st_valid : 1; + u64 vm_xst_valid : 1; + u64 vm_ph_ignore : 1; + u64 rsvd1 : 5; + u64 vm_st : 8; + u64 vm_xst : 16; + u64 pm_st_valid : 1; + u64 pm_xst_valid : 1; + u64 pm_ph_ignore : 1; + u64 rsvd2 : 5; + u64 pm_st : 8; + u64 pm_xst : 16; + }; + u64 value; +}; + +static u16 tph_extract_tag(enum tph_mem_type mem_type, u8 req_type, + union st_info *st_tag) +{ + switch (req_type) { + case PCI_TPH_REQ_TPH_ONLY: /* 8 bit tags */ + switch (mem_type) { + case TPH_MEM_TYPE_VM: + if (st_tag->vm_st_valid) + return st_tag->vm_st; + break; + case TPH_MEM_TYPE_PM: + if (st_tag->pm_st_valid) + return st_tag->pm_st; + break; + } + break; + case PCI_TPH_REQ_EXT_TPH: /* 16 bit tags */ + switch (mem_type) { + case TPH_MEM_TYPE_VM: + if (st_tag->vm_xst_valid) + return st_tag->vm_xst; + break; + case TPH_MEM_TYPE_PM: + if (st_tag->pm_xst_valid) + return st_tag->pm_xst; + break; + } + break; + default: + pr_err("invalid steering tag in ACPI _DSM\n"); + return 0; + } + + return 0; +} + +#define TPH_ST_DSM_FUNC_INDEX 0xF +static acpi_status tph_invoke_dsm(acpi_handle handle, u32 cpu_uid, u8 ph, + u8 target_type, bool cache_ref_valid, + u64 cache_ref, union st_info *st_out) +{ + union acpi_object arg3[3], in_obj, *out_obj; + + if (!acpi_check_dsm(handle, &pci_acpi_dsm_guid, 7, BIT(TPH_ST_DSM_FUNC_INDEX))) + return AE_ERROR; + + /* DWORD: feature ID (0 for processor cache ST query) */ + arg3[0].integer.type = ACPI_TYPE_INTEGER; + arg3[0].integer.value = 0; + + /* DWORD: target UID */ + arg3[1].integer.type = ACPI_TYPE_INTEGER; + arg3[1].integer.value = cpu_uid; + + /* QWORD: properties */ + arg3[2].integer.type = ACPI_TYPE_INTEGER; + arg3[2].integer.value = ph & 3; + arg3[2].integer.value |= (target_type & 1) << 2; + arg3[2].integer.value |= (cache_ref_valid & 1) << 3; + arg3[2].integer.value |= (cache_ref << 32); + + in_obj.type = ACPI_TYPE_PACKAGE; + in_obj.package.count = ARRAY_SIZE(arg3); + in_obj.package.elements = arg3; + + out_obj = acpi_evaluate_dsm(handle, &pci_acpi_dsm_guid, 7, + TPH_ST_DSM_FUNC_INDEX, &in_obj); + + if (!out_obj) + return AE_ERROR; + + if (out_obj->type != ACPI_TYPE_BUFFER) { + ACPI_FREE(out_obj); + return AE_ERROR; + } + + st_out->value = *((u64 *)(out_obj->buffer.pointer)); + + ACPI_FREE(out_obj); + + return AE_OK; +} + /* Update the ST Mode Select field of TPH Control Register */ static void set_ctrl_reg_mode_sel(struct pci_dev *pdev, u8 st_mode) { @@ -89,3 +210,44 @@ bool pcie_tph_intr_vec_supported(struct pci_dev *pdev) return true; } EXPORT_SYMBOL(pcie_tph_intr_vec_supported); + +/** + * pcie_tph_get_st_from_acpi() - Retrieve steering tag for a specific CPU + * using platform ACPI _DSM + * @pdev: pci device + * @cpu_acpi_uid: the acpi cpu_uid. + * @mem_type: memory type (vram, nvram) + * @req_type: request type (disable, tph, extended tph) + * @tag: steering tag return value + * + * Return: 0 if success, otherwise errno + */ +int pcie_tph_get_st_from_acpi(struct pci_dev *pdev, unsigned int cpu_acpi_uid, + enum tph_mem_type mem_type, u8 req_type, + u16 *tag) +{ + struct pci_dev *rp; + acpi_handle rp_acpi_handle; + union st_info info; + + if (!pdev->tph_cap) + return -ENODEV; + + /* find ACPI handler for device's root port */ + rp = pcie_find_root_port(pdev); + if (!rp || !rp->bus || !rp->bus->bridge) + return -ENODEV; + rp_acpi_handle = ACPI_HANDLE(rp->bus->bridge); + + /* invoke _DSM to extract tag value */ + if (tph_invoke_dsm(rp_acpi_handle, cpu_acpi_uid, 0, 0, false, 0, &info) != AE_OK) { + *tag = 0; + return -EINVAL; + } + + *tag = tph_extract_tag(mem_type, req_type, &info); + pci_dbg(pdev, "%s: cpu=%d tag=%d\n", __func__, cpu_acpi_uid, *tag); + + return 0; +} +EXPORT_SYMBOL(pcie_tph_get_st_from_acpi); diff --git a/include/linux/pci-tph.h b/include/linux/pci-tph.h index 854677651d81..b12a592f3d49 100644 --- a/include/linux/pci-tph.h +++ b/include/linux/pci-tph.h @@ -9,15 +9,27 @@ #ifndef LINUX_PCI_TPH_H #define LINUX_PCI_TPH_H +enum tph_mem_type { + TPH_MEM_TYPE_VM, /* volatile memory type */ + TPH_MEM_TYPE_PM /* persistent memory type */ +}; + #ifdef CONFIG_PCIE_TPH void pcie_tph_disable(struct pci_dev *dev); void pcie_tph_set_nostmode(struct pci_dev *dev); bool pcie_tph_intr_vec_supported(struct pci_dev *dev); +int pcie_tph_get_st_from_acpi(struct pci_dev *dev, unsigned int cpu_acpi_uid, + enum tph_mem_type tag_type, u8 req_enable, + u16 *tag); #else static inline void pcie_tph_disable(struct pci_dev *dev) {} static inline void pcie_tph_set_nostmode(struct pci_dev *dev) {} static inline bool pcie_tph_intr_vec_supported(struct pci_dev *dev) { return false; } +static inline int pcie_tph_get_st_from_acpi(struct pci_dev *dev, unsigned int cpu_acpi_uid, + enum tph_mem_type tag_type, u8 req_enable, + u16 *tag) +{ return false; } #endif #endif /* LINUX_PCI_TPH_H */ -- 2.45.1