[PATCH 5/7] platform/x86/intel/tpmi: Process CPU package mapping

From: Srinivas Pandruvada
Date: Wed Feb 01 2023 - 20:09:22 EST


There is one Intel Out-of-Band (OOB) PCI device per CPU package. Since
TPMI feature is exposed via OOB PCI device, there will be multiple
TPMI device instances on a multi CPU package system.

There are several PM features, which needs to associate APIC based CPU
package ID information to a TPMI instance. For example if Intel Speed
Select feature requires control of a CPU package, it needs to identify
right TPMI device instance.

There is one special TPMI ID (ID = 0x81) in the PFS. The MMIO
region of this TPMI ID points to a mapping table:
- PCI Bus ID
- PCI Device ID
- APIC based Package ID

This mapping information can be used by any PM feature driver which
requires mapping from a CPU package to a TPMI device instance.

Unlike other TPMI features, device node is not created for this feature
ID (0x81). Instead store the mapping information as platform data, which
is part of the per PCI device TPMI instance (struct intel_tpmi_info).
Later the TPMI feature drivers can get the mapping information using an
interface "tpmi_get_platform_data()"

Signed-off-by: Srinivas Pandruvada <srinivas.pandruvada@xxxxxxxxxxxxxxx>
Reviewed-by: Pierre-Louis Bossart <pierre-louis.bossart@xxxxxxxxxxxxxxx>
Acked-by: Rafael J. Wysocki <rafael.j.wysocki@xxxxxxxxx>
---
drivers/platform/x86/intel/tpmi.c | 72 +++++++++++++++++++++++++++++++
include/linux/intel_tpmi.h | 28 ++++++++++++
2 files changed, 100 insertions(+)
create mode 100644 include/linux/intel_tpmi.h

diff --git a/drivers/platform/x86/intel/tpmi.c b/drivers/platform/x86/intel/tpmi.c
index 1b87b46ba8a9..e612da579b29 100644
--- a/drivers/platform/x86/intel/tpmi.c
+++ b/drivers/platform/x86/intel/tpmi.c
@@ -47,6 +47,7 @@
*/

#include <linux/auxiliary_bus.h>
+#include <linux/intel_tpmi.h>
#include <linux/io.h>
#include <linux/module.h>
#include <linux/pci.h>
@@ -96,6 +97,7 @@ struct intel_tpmi_pm_feature {
* @vsec_dev: Pointer to intel_vsec_device structure for this TPMI device
* @feature_count: Number of TPMI of TPMI instances pointed by tpmi_features
* @pfs_start: Start of PFS offset for the TPMI instances in this device
+ * @plat_info: Stores platform info which can be used by the client drivers
*
* Stores the information for all TPMI devices enumerated from a single PCI device.
*/
@@ -104,8 +106,30 @@ struct intel_tpmi_info {
struct intel_vsec_device *vsec_dev;
int feature_count;
u64 pfs_start;
+ struct intel_tpmi_plat_info plat_info;
};

+/**
+ * struct tpmi_info_header - CPU package ID to PCI device mapping information
+ * @fn: PCI function number
+ * @dev: PCI device number
+ * @bus: PCI bus number
+ * @pkg: CPU Package id
+ * @reserved: Reserved for future use
+ * @lock: When set to 1 the register is locked and becomes read-only
+ * until next reset. Not for use by the OS driver.
+ *
+ * The structure to read hardware provided mapping information.
+ */
+struct tpmi_info_header {
+ u64 fn:3;
+ u64 dev:5;
+ u64 bus:8;
+ u64 pkg:8;
+ u64 reserved:39;
+ u64 lock:1;
+} __packed;
+
/*
* List of supported TMPI IDs.
* Some TMPI IDs are not used by Linux, so the numbers are not consecutive.
@@ -115,11 +139,20 @@ enum intel_tpmi_id {
TPMI_ID_PEM = 1, /* Power and Perf excursion Monitor */
TPMI_ID_UNCORE = 2, /* Uncore Frequency Scaling */
TPMI_ID_SST = 5, /* Speed Select Technology */
+ TPMI_INFO_ID = 0x81, /* Special ID for PCI BDF and Package ID information */
};

/* Used during auxbus device creation */
static DEFINE_IDA(intel_vsec_tpmi_ida);

+struct intel_tpmi_plat_info *tpmi_get_platform_data(struct auxiliary_device *auxdev)
+{
+ struct intel_vsec_device *vsec_dev = auxdev_to_ivdev(auxdev);
+
+ return vsec_dev->priv_data;
+}
+EXPORT_SYMBOL_NS_GPL(tpmi_get_platform_data, INTEL_TPMI);
+
static const char *intel_tpmi_name(enum intel_tpmi_id id)
{
switch (id) {
@@ -177,6 +210,8 @@ static int tpmi_create_device(struct intel_tpmi_info *tpmi_info,
feature_vsec_dev->pcidev = vsec_dev->pcidev;
feature_vsec_dev->resource = res;
feature_vsec_dev->num_resources = pfs->pfs_header.num_entries;
+ feature_vsec_dev->priv_data = &tpmi_info->plat_info;
+ feature_vsec_dev->priv_data_size = sizeof(tpmi_info->plat_info);
feature_vsec_dev->ida = &intel_vsec_tpmi_ida;

/*
@@ -220,6 +255,31 @@ static int tpmi_create_devices(struct intel_tpmi_info *tpmi_info)
return 0;
}

+#define TPMI_INFO_BUS_INFO_OFFSET 0x08
+
+static int tpmi_process_info(struct intel_tpmi_info *tpmi_info,
+ struct intel_tpmi_pm_feature *pfs)
+{
+ struct tpmi_info_header header;
+ void __iomem *info_mem;
+
+ info_mem = ioremap(pfs->vsec_offset + TPMI_INFO_BUS_INFO_OFFSET,
+ pfs->pfs_header.entry_size * 4 - TPMI_INFO_BUS_INFO_OFFSET);
+ if (!info_mem)
+ return -ENOMEM;
+
+ memcpy_fromio(&header, info_mem, sizeof(header));
+
+ tpmi_info->plat_info.package_id = header.pkg;
+ tpmi_info->plat_info.bus_number = header.bus;
+ tpmi_info->plat_info.device_number = header.dev;
+ tpmi_info->plat_info.function_number = header.fn;
+
+ iounmap(info_mem);
+
+ return 0;
+}
+
static int tpmi_fetch_pfs_header(struct intel_tpmi_pm_feature *pfs, u64 start, int size)
{
void __iomem *pfs_mem;
@@ -238,6 +298,7 @@ static int tpmi_fetch_pfs_header(struct intel_tpmi_pm_feature *pfs, u64 start, i
static int intel_vsec_tpmi_init(struct auxiliary_device *auxdev)
{
struct intel_vsec_device *vsec_dev = auxdev_to_ivdev(auxdev);
+ struct pci_dev *pci_dev = vsec_dev->pcidev;
struct intel_tpmi_info *tpmi_info;
u64 pfs_start = 0;
int i;
@@ -248,6 +309,7 @@ static int intel_vsec_tpmi_init(struct auxiliary_device *auxdev)

tpmi_info->vsec_dev = vsec_dev;
tpmi_info->feature_count = vsec_dev->num_resources;
+ tpmi_info->plat_info.bus_number = pci_dev->bus->number;

tpmi_info->tpmi_features = devm_kcalloc(&auxdev->dev, vsec_dev->num_resources,
sizeof(*tpmi_info->tpmi_features),
@@ -282,6 +344,16 @@ static int intel_vsec_tpmi_init(struct auxiliary_device *auxdev)
pfs->pfs_header.cap_offset *= 1024;

pfs->vsec_offset = pfs_start + pfs->pfs_header.cap_offset;
+
+ /*
+ * Process TPMI_INFO to get PCI device to CPU package ID.
+ * Device nodes for TPMI features are not created in this
+ * for loop. So, the mapping information will be available
+ * when actual device nodes created outside this
+ * loop via tpmi_create_devices().
+ */
+ if (pfs->pfs_header.tpmi_id == TPMI_INFO_ID)
+ tpmi_process_info(tpmi_info, pfs);
}

tpmi_info->pfs_start = pfs_start;
diff --git a/include/linux/intel_tpmi.h b/include/linux/intel_tpmi.h
new file mode 100644
index 000000000000..5b665320ecb4
--- /dev/null
+++ b/include/linux/intel_tpmi.h
@@ -0,0 +1,28 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * intel_tpmi.h: Intel TPMI core external interface
+ */
+
+#ifndef _INTEL_TPMI_H_
+#define _INTEL_TPMI_H_
+
+/**
+ * struct intel_tpmi_plat_info - Platform information for a TPMI device instance
+ * @package_id: CPU Package id
+ * @bus_number: PCI bus number
+ * @device_number: PCI device number
+ * @function_number: PCI function number
+ *
+ * Structure to store platform data for a TPMI device instance. This
+ * struct is used to return data via tpmi_get_platform_data().
+ */
+struct intel_tpmi_plat_info {
+ u8 package_id;
+ u8 bus_number;
+ u8 device_number;
+ u8 function_number;
+};
+
+struct intel_tpmi_plat_info *tpmi_get_platform_data(struct auxiliary_device *auxdev);
+
+#endif
--
2.31.1