From: Kuppuswamy Sathyanarayanan <sathyanarayanan.kuppuswamy@linux.intel.com>
To: "Ilpo Järvinen" <ilpo.jarvinen@linux.intel.com>
Cc: Srinivas Pandruvada <srinivas.pandruvada@linux.intel.com>,
Hans de Goede <hansg@kernel.org>,
David E Box <david.e.box@linux.intel.com>,
Andy Shevchenko <andriy.shevchenko@intel.com>,
platform-driver-x86@vger.kernel.org,
LKML <linux-kernel@vger.kernel.org>
Subject: Re: [PATCH v1 10/10] platform/x86/intel/tpmi: Split off the PCI VSEC enumeration
Date: Wed, 23 Sep 2026 10:37:35 -0700 [thread overview]
Message-ID: <1bf1c3bd-2273-4f6e-a49c-de9f3dbf4e65@linux.intel.com> (raw)
In-Reply-To: <a32a80d9-7d09-1302-9a0c-35bf99c9b5b7@linux.intel.com>
On 9/22/2026 1:19 PM, Ilpo Järvinen wrote:
> On Tue, 22 Sep 2026, Kuppuswamy Sathyanarayanan wrote:
>
>> Everything the TPMI driver does once the PFS table has been located is
>> independent of how the TPMI MMIO region was discovered, from walking the
>> PFS to creating the per feature devices. Only the entry point is VSEC
>> specific and it is now reduced to filling in the arguments of
>> intel_tpmi_init().
>>
>> Move the enumeration independent code to a new intel-tpmi_common module
>> and leave the auxiliary driver binding to the "intel_vsec.tpmi" device
>> created by the Intel VSEC driver in vsec_tpmi.c. The code is moved as
>> is, apart from a stray blank line and a typo in a comment that are fixed
>> in passing.
>>
>> tpmi_common.h exposes what an enumeration method needs, which is
>> intel_tpmi_init(), intel_tpmi_deinit() and struct intel_tpmi_info. The
>> structure is a complete type because the enumeration method allocates it
>> and is expected to extend it with its own data. The PFS, TPMI_INFO and
>> feature state layouts stay private to tpmi_common.c, only the hardware
>> parsing touches them.
>>
>> CONFIG_INTEL_TPMI_COMMON is a hidden symbol selected by the enumeration
>> method, in the same way as CONFIG_INTEL_TPMI_POWER_DOMAINS. The TPMI
>> client API is now exported by intel-tpmi_common, so the client drivers
>> depend on that module instead of intel-vsec_tpmi. That is what allows a
>> second, non PCI enumeration method to be added later without the client
>> drivers knowing which one is loaded.
>>
>> No functional change.
>>
>> Co-developed-by: Srinivas Pandruvada <srinivas.pandruvada@linux.intel.com>
>> Signed-off-by: Srinivas Pandruvada <srinivas.pandruvada@linux.intel.com>
>> Signed-off-by: Kuppuswamy Sathyanarayanan <sathyanarayanan.kuppuswamy@linux.intel.com>
>> Reviewed-by: Andy Shevchenko <andriy.shevchenko@intel.com>
>> ---
>> MAINTAINERS | 1 +
>> drivers/platform/x86/intel/Kconfig | 4 +
>> drivers/platform/x86/intel/Makefile | 1 +
>> drivers/platform/x86/intel/tpmi_common.c | 829 ++++++++++++++++++++++
>> drivers/platform/x86/intel/tpmi_common.h | 52 ++
>> drivers/platform/x86/intel/vsec_tpmi.c | 836 +----------------------
>> 6 files changed, 895 insertions(+), 828 deletions(-)
>> create mode 100644 drivers/platform/x86/intel/tpmi_common.c
>> create mode 100644 drivers/platform/x86/intel/tpmi_common.h
>>
>> diff --git a/MAINTAINERS b/MAINTAINERS
>> index 3a19da74d00c..40ea7921ed8b 100644
>> --- a/MAINTAINERS
>> +++ b/MAINTAINERS
>> @@ -13522,6 +13522,7 @@ M: Srinivas Pandruvada <srinivas.pandruvada@linux.intel.com>
>> L: platform-driver-x86@vger.kernel.org
>> S: Maintained
>> F: Documentation/ABI/testing/debugfs-tpmi
>> +F: drivers/platform/x86/intel/tpmi_common.[ch]
>> F: drivers/platform/x86/intel/vsec_tpmi.c
>> F: include/linux/intel_tpmi.h
>>
>> diff --git a/drivers/platform/x86/intel/Kconfig b/drivers/platform/x86/intel/Kconfig
>> index 2900407d6095..ea6dc7b50d90 100644
>> --- a/drivers/platform/x86/intel/Kconfig
>> +++ b/drivers/platform/x86/intel/Kconfig
>> @@ -209,11 +209,15 @@ config INTEL_SMARTCONNECT
>> config INTEL_TPMI_POWER_DOMAINS
>> tristate
>>
>> +config INTEL_TPMI_COMMON
>> + tristate
>> +
>> config INTEL_TPMI
>> tristate "Intel Topology Aware Register and PM Capsule Interface (TPMI)"
>> depends on INTEL_VSEC
>> depends on X86_64
>> select INTEL_TPMI_POWER_DOMAINS
>> + select INTEL_TPMI_COMMON
>> help
>> The Intel Topology Aware Register and PM Capsule Interface (TPMI),
>> provides enumerable MMIO interface for power management features.
>> diff --git a/drivers/platform/x86/intel/Makefile b/drivers/platform/x86/intel/Makefile
>> index 138b13756158..d2c63a136e51 100644
>> --- a/drivers/platform/x86/intel/Makefile
>> +++ b/drivers/platform/x86/intel/Makefile
>> @@ -40,6 +40,7 @@ intel-target-$(CONFIG_INTEL_PUNIT_IPC) += punit_ipc.o
>> # TPMI drivers
>> intel-target-$(CONFIG_INTEL_PLR_TPMI) += plr_tpmi.o
>> intel-target-$(CONFIG_INTEL_TPMI_POWER_DOMAINS) += tpmi_power_domains.o
>> +intel-target-$(CONFIG_INTEL_TPMI_COMMON) += tpmi_common.o
>> intel-target-$(CONFIG_INTEL_TPMI) += vsec_tpmi.o
>>
>> # Intel Uncore drivers
>> diff --git a/drivers/platform/x86/intel/tpmi_common.c b/drivers/platform/x86/intel/tpmi_common.c
>> new file mode 100644
>> index 000000000000..0db766309529
>> --- /dev/null
>> +++ b/drivers/platform/x86/intel/tpmi_common.c
>> @@ -0,0 +1,829 @@
>> +// SPDX-License-Identifier: GPL-2.0-only
>> +/*
>> + * Enumeration independent part of the Intel TPMI driver
>> + *
>> + * Copyright (c) 2026, Intel Corporation.
>> + * All Rights Reserved.
>> + *
>> + * The TPMI (Topology Aware Register and PM Capsule Interface) provides a
>> + * flexible, extendable and PCIe enumerable MMIO interface for PM features.
>> + *
>> + * For example Intel RAPL (Running Average Power Limit) provides a MMIO
>> + * interface using TPMI. This has advantage over traditional MSR
>> + * (Model Specific Register) interface, where a thread needs to be scheduled
>> + * on the target CPU to read or write. Also the RAPL features vary between
>> + * CPU models, and hence lot of model specific code. Here TPMI provides an
>> + * architectural interface by providing hierarchical tables and fields,
>> + * which will not need any model specific implementation.
>> + *
>> + * The TPMI specification defines a PFS (PM Feature Structure) table.
>> + * This table is present in the TPMI MMIO region. Each TPMI PM feature
>> + * has one entry in the PFS with a unique TPMI ID and its access details.
>> + *
>> + * The names of the devices created for the PM features start with the
>> + * "intel_vsec.tpmi-" prefix which is followed by a specific name of the
>> + * given PM feature (for example, "intel_vsec.tpmi-rapl.0").
>> + *
>> + * The device nodes are create by using interface "intel_vsec_add_aux()"
>> + * provided by the Intel VSEC driver.
>> + */
>> +
>> +#include <linux/align.h>
>> +#include <linux/array_size.h>
>> +#include <linux/auxiliary_bus.h>
>> +#include <linux/bitfield.h>
>> +#include <linux/debugfs.h>
>> +#include <linux/cleanup.h>
>
> Alphabetic order.
>
>> +#include <linux/delay.h>
>> +#include <linux/intel_tpmi.h>
>> +#include <linux/intel_vsec.h>
>> +#include <linux/io.h>
>> +#include <linux/iopoll.h>
>> +#include <linux/module.h>
>> +#include <linux/notifier.h>
>> +#include <linux/pci.h>
>> +#include <linux/security.h>
>> +#include <linux/sizes.h>
>> +#include <linux/string_helpers.h>
>
> Please check you've the includes for the stuff you're using, I'll mention
> some below but might miss one or two.
>
> Some/most of the code comments below may be better done in own patches on
> top of this patch as this seems to be mostly moving existing code around.
> It would keep this easier to review (patches only moving code around can
> usually be checked using diff-of-diffs to find unwanted code changes, I
> do use it frequently to review move changes).
>
Sure. I will fix them in v2.
>> +
>> +#include "tpmi_common.h"
>> +
>> +/**
>> + * struct intel_tpmi_pfs_entry - TPMI PM Feature Structure (PFS) entry
>> + * @tpmi_id: TPMI feature identifier (what the feature is and its data format).
>> + * @num_entries: Number of feature interface instances present in the PFS.
>> + * This represents the maximum number of Power domains in the SoC.
>> + * @entry_size: Interface instance entry size in 32-bit words.
>> + * @cap_offset: Offset from the PM_Features base address to the base of the PM VSEC
>> + * register bank in KB.
>> + * @attribute: Feature attribute: 0=BIOS. 1=OS. 2-3=Reserved.
>> + * @reserved: Bits for use in the future.
>> + *
>> + * Represents one TPMI feature entry data in the PFS retrieved as is
>> + * from the hardware.
>> + */
>> +struct intel_tpmi_pfs_entry {
>> + u64 tpmi_id:8;
>> + u64 num_entries:8;
>> + u64 entry_size:16;
>> + u64 cap_offset:16;
>> + u64 attribute:2;
>> + u64 reserved:14;
>> +} __packed;
>
> Add include for __packed.
>
>> +
>> +/**
>> + * struct intel_tpmi_pm_feature - TPMI PM Feature information for a TPMI ID
>> + * @pfs_header: PFS header retireved from the hardware.
>> + * @vsec_offset: Starting MMIO address for this feature in bytes. Essentially
>> + * this offset = "Address" from VSEC header + PFS Capability
>> + * offset for this feature entry.
>> + *
>> + * Represents TPMI instance information for one TPMI ID.
>> + */
>> +struct intel_tpmi_pm_feature {
>> + struct intel_tpmi_pfs_entry pfs_header;
>> + u64 vsec_offset;
>> +};
>> +
>> +/**
>> + * 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
>> + * @segment: PCI segment id
>> + * @partition: Package Partition id
>> + * @cdie_mask: Bitmap of compute dies in the current partition
>> + * @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 segment:8;
>> + u64 partition:2;
>> + u64 cdie_mask:16;
>> + u64 reserved:13;
>> + u64 lock:1;
>> +} __packed;
>> +
>> +/**
>> + * struct tpmi_feature_state - Structure to read hardware state of a feature
>> + * @enabled: Enable state of a feature, 1: enabled, 0: disabled
>> + * @reserved_1: Reserved for future use
>> + * @write_blocked: Writes are blocked means all write operations are ignored
>> + * @read_blocked: Reads are blocked means will read 0xFFs
>> + * @pcs_select: Interface used by out of band software, not used in OS
>> + * @reserved_2: Reserved for future use
>> + * @id: TPMI ID of the feature
>> + * @reserved_3: Reserved for future use
>> + * @locked: When set to 1, OS can't change this register.
>> + *
>> + * The structure is used to read hardware state of a TPMI feature. This
>> + * information is used for debug and restricting operations for this feature.
>> + */
>> +struct tpmi_feature_state {
>> + u32 enabled:1;
>> + u32 reserved_1:3;
>> + u32 write_blocked:1;
>> + u32 read_blocked:1;
>> + u32 pcs_select:1;
>> + u32 reserved_2:1;
>> + u32 id:8;
>> + u32 reserved_3:15;
>> + u32 locked:1;
>> +} __packed;
>> +
>> +/*
>> + * The size from hardware is in u32 units. This size is from a trusted hardware,
>> + * but better to verify for pre silicon platforms. Set size to 0, when invalid.
>> + */
>> +#define TPMI_GET_SINGLE_ENTRY_SIZE(pfs) \
>> +({ \
>> + pfs->pfs_header.entry_size > SZ_1K ? 0 : pfs->pfs_header.entry_size << 2; \
>
> * sizeof(u32) instead of shift?
>
>> +})
>> +
>> +/* Used during auxbus device creation */
>> +static DEFINE_IDA(intel_vsec_tpmi_ida);
>
> Include.
>
>> +
>> +static BLOCKING_NOTIFIER_HEAD(tpmi_notify_list);
>> +
>> +int tpmi_register_notifier(struct notifier_block *nb)
>> +{
>> + return blocking_notifier_chain_register(&tpmi_notify_list, nb);
>> +}
>> +EXPORT_SYMBOL_NS_GPL(tpmi_register_notifier, "INTEL_TPMI");
>
> Include.
>
>> +
>> +int tpmi_unregister_notifier(struct notifier_block *nb)
>> +{
>> + return blocking_notifier_chain_unregister(&tpmi_notify_list, nb);
>> +}
>> +EXPORT_SYMBOL_NS_GPL(tpmi_unregister_notifier, "INTEL_TPMI");
>> +
>> +struct oobmsm_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");
>> +
>> +int tpmi_get_resource_count(struct auxiliary_device *auxdev)
>> +{
>> + struct intel_vsec_device *vsec_dev = auxdev_to_ivdev(auxdev);
>> +
>> + if (vsec_dev)
>> + return vsec_dev->num_resources;
>> +
>> + return 0;
>> +}
>> +EXPORT_SYMBOL_NS_GPL(tpmi_get_resource_count, "INTEL_TPMI");
>> +
>> +struct resource *tpmi_get_resource_at_index(struct auxiliary_device *auxdev, int index)
>> +{
>> + struct intel_vsec_device *vsec_dev = auxdev_to_ivdev(auxdev);
>> +
>> + if (vsec_dev && index < vsec_dev->num_resources)
>> + return &vsec_dev->resource[index];
>> +
>> + return NULL;
>> +}
>> +EXPORT_SYMBOL_NS_GPL(tpmi_get_resource_at_index, "INTEL_TPMI");
>> +
>> +/* TPMI Control Interface */
>> +
>> +#define TPMI_CONTROL_STATUS_OFFSET 0x00
>> +#define TPMI_COMMAND_OFFSET 0x08
>> +#define TMPI_CONTROL_DATA_VAL_OFFSET 0x0c
>> +
>> +/*
>> + * Spec is calling for max 1 seconds to get ownership at the worst
>> + * case. Read at 10 ms timeouts and repeat up to 1 second.
>> + */
>> +#define TPMI_CONTROL_TIMEOUT_US (10 * USEC_PER_MSEC)
>> +#define TPMI_CONTROL_TIMEOUT_MAX_US (1 * USEC_PER_SEC)
>
> Include for *_PER_*
>
>> +
>> +#define TPMI_RB_TIMEOUT_US (10 * USEC_PER_MSEC)
>> +#define TPMI_RB_TIMEOUT_MAX_US USEC_PER_SEC
>> +
>> +/* TPMI Control status register defines */
>> +
>> +#define TPMI_CONTROL_STATUS_RB BIT_ULL(0)
>> +
>> +#define TPMI_CONTROL_STATUS_OWNER GENMASK_ULL(5, 4)
>
> Add include.
>
>> +#define TPMI_OWNER_NONE 0
>> +#define TPMI_OWNER_IN_BAND 1
>> +
>> +#define TPMI_CONTROL_STATUS_CPL BIT_ULL(6)
>> +#define TPMI_CONTROL_STATUS_RESULT GENMASK_ULL(15, 8)
>> +#define TPMI_CONTROL_STATUS_LEN GENMASK_ULL(31, 16)
>> +
>> +#define TPMI_CMD_PKT_LEN 2
>> +#define TPMI_CMD_STATUS_SUCCESS 0x40
>> +
>> +/* TPMI command data registers */
>> +#define TMPI_CONTROL_DATA_CMD GENMASK_ULL(7, 0)
>> +#define TPMI_CONTROL_DATA_VAL_FEATURE GENMASK_ULL(48, 40)
>> +
>> +/* Command to send via control interface */
>> +#define TPMI_CONTROL_GET_STATE_CMD 0x10
>> +
>> +#define TPMI_CONTROL_CMD_MASK GENMASK_ULL(48, 40)
>> +
>> +#define TPMI_CMD_LEN_MASK GENMASK_ULL(18, 16)
>> +
>> +/* Mutex to complete get feature status without interruption */
>> +static DEFINE_MUTEX(tpmi_dev_lock);
>> +
>> +static int tpmi_wait_for_owner(struct intel_tpmi_info *tpmi_info, u8 owner)
>> +{
>> + u64 control;
>> +
>> + return readq_poll_timeout(tpmi_info->tpmi_control_mem + TPMI_CONTROL_STATUS_OFFSET,
>> + control, owner == FIELD_GET(TPMI_CONTROL_STATUS_OWNER, control),
>> + TPMI_CONTROL_TIMEOUT_US, TPMI_CONTROL_TIMEOUT_MAX_US);
>> +}
>> +
>> +static int tpmi_read_feature_status(struct intel_tpmi_info *tpmi_info, int feature_id,
>> + struct tpmi_feature_state *feature_state)
>> +{
>> + u64 control, data;
>> + int ret;
>> +
>> + if (!tpmi_info->tpmi_control_mem)
>> + return -EFAULT;
>> +
>> + mutex_lock(&tpmi_dev_lock);
>
> Please use guard to avoid unlock related gotos.
>
>> +
>> + /* Wait for owner bit set to 0 (none) */
>> + ret = tpmi_wait_for_owner(tpmi_info, TPMI_OWNER_NONE);
>> + if (ret)
>> + goto err_unlock;
>> +
>> + /* set command id to 0x10 for TPMI_GET_STATE */
>> + data = FIELD_PREP(TMPI_CONTROL_DATA_CMD, TPMI_CONTROL_GET_STATE_CMD);
>> +
>> + /* 32 bits for DATA offset and +8 for feature_id field */
>> + data |= FIELD_PREP(TPMI_CONTROL_DATA_VAL_FEATURE, feature_id);
>> +
>> + /* Write at command offset for qword access */
>> + writeq(data, tpmi_info->tpmi_control_mem + TPMI_COMMAND_OFFSET);
>> +
>> + /* Wait for owner bit set to in-band */
>> + ret = tpmi_wait_for_owner(tpmi_info, TPMI_OWNER_IN_BAND);
>> + if (ret)
>> + goto err_unlock;
>> +
>> + /* Set Run Busy and packet length of 2 dwords */
>> + control = TPMI_CONTROL_STATUS_RB;
>> + control |= FIELD_PREP(TPMI_CONTROL_STATUS_LEN, TPMI_CMD_PKT_LEN);
>> +
>> + /* Write at status offset for qword access */
>> + writeq(control, tpmi_info->tpmi_control_mem + TPMI_CONTROL_STATUS_OFFSET);
>> +
>> + /* Wait for Run Busy clear */
>> + ret = readq_poll_timeout(tpmi_info->tpmi_control_mem + TPMI_CONTROL_STATUS_OFFSET,
>> + control, !(control & TPMI_CONTROL_STATUS_RB),
>> + TPMI_RB_TIMEOUT_US, TPMI_RB_TIMEOUT_MAX_US);
>> + if (ret)
>> + goto done_proc;
>> +
>> + control = FIELD_GET(TPMI_CONTROL_STATUS_RESULT, control);
>> + if (control != TPMI_CMD_STATUS_SUCCESS) {
>> + ret = -EBUSY;
>> + goto done_proc;
>> + }
>> +
>> + /* Response is ready */
>> + memcpy_fromio(feature_state, tpmi_info->tpmi_control_mem + TMPI_CONTROL_DATA_VAL_OFFSET,
>> + sizeof(*feature_state));
>> +
>> + ret = 0;
>> +
>> +done_proc:
>> + /* Set CPL "completion" bit */
>> + writeq(TPMI_CONTROL_STATUS_CPL, tpmi_info->tpmi_control_mem + TPMI_CONTROL_STATUS_OFFSET);
>> +
>> +err_unlock:
>> + mutex_unlock(&tpmi_dev_lock);
>> +
>> + return ret;
>> +}
>> +
>> +int tpmi_get_feature_status(struct auxiliary_device *auxdev,
>> + int feature_id, bool *read_blocked, bool *write_blocked)
>> +{
>> + struct intel_tpmi_info *tpmi_info = dev_get_drvdata(auxdev->dev.parent);
>> + struct tpmi_feature_state feature_state;
>> + int ret;
>> +
>> + ret = tpmi_read_feature_status(tpmi_info, feature_id, &feature_state);
>> + if (ret)
>> + return ret;
>> +
>> + *read_blocked = feature_state.read_blocked;
>> + *write_blocked = feature_state.write_blocked;
>> +
>> + return 0;
>> +}
>> +EXPORT_SYMBOL_NS_GPL(tpmi_get_feature_status, "INTEL_TPMI");
>> +
>> +struct dentry *tpmi_get_debugfs_dir(struct auxiliary_device *auxdev)
>> +{
>> + struct intel_tpmi_info *tpmi_info = dev_get_drvdata(auxdev->dev.parent);
>> +
>> + return tpmi_info->dbgfs_dir;
>> +}
>> +EXPORT_SYMBOL_NS_GPL(tpmi_get_debugfs_dir, "INTEL_TPMI");
>> +
>> +static int tpmi_pfs_dbg_show(struct seq_file *s, void *unused)
>> +{
>> + struct intel_tpmi_info *tpmi_info = s->private;
>> + int locked, disabled, read_blocked, write_blocked;
>> + struct tpmi_feature_state feature_state;
>> + struct intel_tpmi_pm_feature *pfs;
>> + int ret, i;
>> +
>> + seq_printf(s, "tpmi PFS start offset 0x:%llx\n", tpmi_info->pfs_start);
>> + seq_puts(s, "tpmi_id\t\tentries\t\tsize\t\tcap_offset\tattribute\tvsec_offset\tlocked\tdisabled\tread_blocked\twrite_blocked\n");
>
> Add include for these calls.
>
>> + for (i = 0; i < tpmi_info->feature_count; ++i) {
>> + pfs = &tpmi_info->tpmi_features[i];
>> + ret = tpmi_read_feature_status(tpmi_info, pfs->pfs_header.tpmi_id, &feature_state);
>> + if (ret) {
>> + locked = 'U';
>> + disabled = 'U';
>> + read_blocked = 'U';
>> + write_blocked = 'U';
>> + } else {
>> + disabled = feature_state.enabled ? 'N' : 'Y';
>> + locked = feature_state.locked ? 'Y' : 'N';
>> + read_blocked = feature_state.read_blocked ? 'Y' : 'N';
>> + write_blocked = feature_state.write_blocked ? 'Y' : 'N';
>> + }
>> + seq_printf(s, "0x%02x\t\t0x%02x\t\t0x%04x\t\t0x%04x\t\t0x%02x\t\t0x%016llx\t%c\t%c\t\t%c\t\t%c\n",
>> + pfs->pfs_header.tpmi_id, pfs->pfs_header.num_entries,
>> + pfs->pfs_header.entry_size, pfs->pfs_header.cap_offset,
>> + pfs->pfs_header.attribute, pfs->vsec_offset, locked, disabled,
>> + read_blocked, write_blocked);
>> + }
>> +
>> + return 0;
>> +}
>> +DEFINE_SHOW_ATTRIBUTE(tpmi_pfs_dbg);
>> +
>> +#define MEM_DUMP_COLUMN_COUNT 8
>> +
>> +static int tpmi_mem_dump_show(struct seq_file *s, void *unused)
>> +{
>> + size_t row_size = MEM_DUMP_COLUMN_COUNT * sizeof(u32);
>> + struct intel_tpmi_pm_feature *pfs = s->private;
>> + int count, ret = 0;
>> + void __iomem *mem;
>> + u32 size;
>> + u64 off;
>> + u8 *buffer;
>> +
>> + size = TPMI_GET_SINGLE_ENTRY_SIZE(pfs);
>> + if (!size)
>> + return -EIO;
>> +
>> + buffer = kmalloc(size, GFP_KERNEL);
>
> Please use __free() and move var declaration here.
>
>> + if (!buffer)
>> + return -ENOMEM;
>> +
>> + off = pfs->vsec_offset;
>> +
>> + mutex_lock(&tpmi_dev_lock);
>
> guard()
>
>> +
>> + for (count = 0; count < pfs->pfs_header.num_entries; ++count) {
>> + seq_printf(s, "TPMI Instance:%d offset:0x%llx\n", count, off);
>> +
>> + mem = ioremap(off, size);
>> + if (!mem) {
>> + ret = -ENOMEM;
>> + break;
>
> With the cleanup.h in use, this can return directly.
>
>> + }
>> +
>> + memcpy_fromio(buffer, mem, size);
>> +
>> + seq_hex_dump(s, " ", DUMP_PREFIX_OFFSET, row_size, sizeof(u32), buffer, size,
>> + false);
>> +
>> + iounmap(mem);
>> +
>> + off += size;
>> + }
>> +
>> + mutex_unlock(&tpmi_dev_lock);
>> +
>> + kfree(buffer);
>> +
>> + return ret;
>> +}
>> +DEFINE_SHOW_ATTRIBUTE(tpmi_mem_dump);
>> +
>> +static ssize_t mem_write(struct file *file, const char __user *userbuf, size_t len, loff_t *ppos)
>> +{
>> + struct seq_file *m = file->private_data;
>> + struct intel_tpmi_pm_feature *pfs = m->private;
>> + u32 addr, value, punit, size;
>> + u32 num_elems;
>> + void __iomem *mem;
>> + int ret;
>> +
>> + size = TPMI_GET_SINGLE_ENTRY_SIZE(pfs);
>> + if (!size)
>> + return -EIO;
>> +
>> + u32 *array __free(kfree) = NULL;
>> + ret = parse_int_array_user(userbuf, len, (int **)&array);
>> + if (ret < 0)
>> + return ret;
>> +
>> + num_elems = *array;
>> + if (num_elems != 3)
>> + return -EINVAL;
>> +
>> + punit = array[1];
>> + addr = array[2];
>> + value = array[3];
>> +
>> + if (!IS_ALIGNED(addr, sizeof(u32)))
>> + return -EINVAL;
>> +
>> + if (punit >= pfs->pfs_header.num_entries)
>> + return -EINVAL;
>> +
>> + if (addr >= size)
>> + return -EINVAL;
>> +
>> + guard(mutex)(&tpmi_dev_lock);
>> +
>> + mem = ioremap(pfs->vsec_offset + punit * size, size);
>> + if (!mem)
>> + return -ENOMEM;
>> +
>> + writel(value, mem + addr);
>> +
>> + iounmap(mem);
>> +
>> + return len;
>> +}
>> +
>> +static int mem_write_show(struct seq_file *s, void *unused)
>> +{
>> + return 0;
>> +}
>> +
>> +static int mem_write_open(struct inode *inode, struct file *file)
>> +{
>> + return single_open(file, mem_write_show, inode->i_private);
>> +}
>> +
>> +static const struct file_operations mem_write_ops = {
>> + .open = mem_write_open,
>> + .read = seq_read,
>> + .write = mem_write,
>> + .llseek = seq_lseek,
>> + .release = single_release,
>> +};
>> +
>> +static void tpmi_dbgfs_register(struct intel_tpmi_info *tpmi_info)
>> +{
>> + char name[64];
>> + int i;
>> +
>> + snprintf(name, sizeof(name), "tpmi-%s", dev_name(tpmi_info->parent));
>
> Prefer scnprintf() over snprintf() so that snprintf() could eventually be
> removed. The difference doesn't matter since you're not using the return
> value.
>
>> + tpmi_info->dbgfs_dir = debugfs_create_dir(name, NULL);
>> +
>> + debugfs_create_file("pfs_dump", 0444, tpmi_info->dbgfs_dir, tpmi_info, &tpmi_pfs_dbg_fops);
>> +
>> + for (i = 0; i < tpmi_info->feature_count; ++i) {
>> + struct intel_tpmi_pm_feature *pfs;
>> + struct dentry *dir;
>> +
>> + pfs = &tpmi_info->tpmi_features[i];
>> + snprintf(name, sizeof(name), "tpmi-id-%02x", pfs->pfs_header.tpmi_id);
>> + dir = debugfs_create_dir(name, tpmi_info->dbgfs_dir);
>> +
>> + debugfs_create_file("mem_dump", 0444, dir, pfs, &tpmi_mem_dump_fops);
>> + debugfs_create_file("mem_write", 0644, dir, pfs, &mem_write_ops);
>> + }
>> +}
>> +
>> +static void tpmi_set_control_base(struct intel_tpmi_info *tpmi_info,
>> + struct intel_tpmi_pm_feature *pfs)
>> +{
>> + void __iomem *mem;
>> + u32 size;
>> +
>> + size = TPMI_GET_SINGLE_ENTRY_SIZE(pfs);
>> + if (!size)
>> + return;
>> +
>> + mem = devm_ioremap(tpmi_info->tpmi_dev, pfs->vsec_offset, size);
>> + if (!mem)
>> + return;
>> +
>> + /* mem is pointing to TPMI CONTROL base */
>> + tpmi_info->tpmi_control_mem = mem;
>> +}
>> +
>> +/*
>> + * The TPMI IDs are sparse, so the unused entries are left NULL and are
>> + * rejected by the caller like any other unsupported feature.
>> + */
>> +static const char * const intel_tpmi_names[] = {
>> + [TPMI_ID_RAPL] = "tpmi-rapl",
>> + [TPMI_ID_PEM] = "tpmi-pem",
>> + [TPMI_ID_UNCORE] = "tpmi-uncore",
>> + [TPMI_ID_SST] = "tpmi-sst",
>> + [TPMI_ID_PLR] = "tpmi-plr",
>> +};
>> +
>> +static const char *intel_tpmi_name(enum intel_tpmi_id id)
>> +{
>> + if (id >= ARRAY_SIZE(intel_tpmi_names))
>> + return NULL;
>> +
>> + return intel_tpmi_names[id];
>> +}
>> +
>> +static int tpmi_create_device(struct intel_tpmi_info *tpmi_info,
>> + struct intel_tpmi_pm_feature *pfs,
>> + u64 pfs_start)
>> +{
>> + struct intel_vsec_device *feature_vsec_dev;
>> + struct tpmi_feature_state feature_state;
>> + struct resource *res, *tmp;
>> + const char *name;
>> + int i, ret;
>> +
>> + ret = tpmi_read_feature_status(tpmi_info, pfs->pfs_header.tpmi_id, &feature_state);
>> + if (ret)
>> + return ret;
>> +
>> + /*
>> + * If not enabled, continue to look at other features in the PFS, so return -EOPNOTSUPP.
>> + * This will not cause failure of loading of this driver.
>> + */
>> + if (!feature_state.enabled)
>> + return -EOPNOTSUPP;
>> +
>> + name = intel_tpmi_name(pfs->pfs_header.tpmi_id);
>> + if (!name)
>> + return -EOPNOTSUPP;
>> +
>> + feature_vsec_dev = kzalloc_flex(*feature_vsec_dev, resource, pfs->pfs_header.num_entries);
>
> Include.
>
>> + if (!feature_vsec_dev)
>> + return -ENOMEM;
>> +
>> + feature_vsec_dev->num_resources = pfs->pfs_header.num_entries;
>> + res = feature_vsec_dev->resource;
>> +
>> + for (i = 0, tmp = res; i < pfs->pfs_header.num_entries; i++, tmp++) {
>> + u64 entry_size_bytes = pfs->pfs_header.entry_size * sizeof(u32);
>> +
>> + tmp->start = pfs->vsec_offset + entry_size_bytes * i;
>> + tmp->end = tmp->start + entry_size_bytes - 1;
>> + tmp->flags = IORESOURCE_MEM;
>
> Include.
>
>> + }
>> +
>> + feature_vsec_dev->dev = tpmi_info->parent;
>> + 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;
>> +
>> + /*
>> + * intel_vsec_add_aux() is resource managed, no explicit
>> + * delete is required on error or on module unload.
>> + * feature_vsec_dev and res memory are also freed as part of
>> + * device deletion.
>> + *
>> + * "name" must outlive the auxiliary device, as the auxiliary bus
>> + * stores the pointer rather than a copy of the string.
>> + */
>> + return intel_vsec_add_aux(tpmi_info->tpmi_dev, feature_vsec_dev, name);
>> +}
>> +
>> +static int tpmi_create_devices(struct intel_tpmi_info *tpmi_info)
>> +{
>> + int ret, i;
>> +
>> + for (i = 0; i < tpmi_info->feature_count; i++) {
>> + ret = tpmi_create_device(tpmi_info, &tpmi_info->tpmi_features[i],
>> + tpmi_info->pfs_start);
>> + /*
>> + * Fail, if the supported features fails to create device,
>> + * otherwise, continue. Even if one device failed to create,
>> + * fail the loading of driver. Since intel_vsec_add_aux()
>> + * is resource managed, no clean up is required for the
>> + * successfully created devices.
>> + */
>
> IMO, this could become a function comment instead (maybe with a minor
> wording tweaks).
>
>> + if (ret && ret != -EOPNOTSUPP)
>> + return ret;
>> + }
>> +
>> + return 0;
>> +}
>> +
>> +#define TPMI_INFO_BUS_INFO_OFFSET 0x08
>> +#define TPMI_INFO_MAJOR_VERSION 0x00
>> +#define TPMI_INFO_MINOR_VERSION 0x02
>> +
>> +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;
>> + u64 feature_header;
>> + int ret = 0;
>> +
>> + info_mem = ioremap(pfs->vsec_offset, pfs->pfs_header.entry_size * sizeof(u32));
>> + if (!info_mem)
>> + return -ENOMEM;
>> +
>> + feature_header = readq(info_mem);
>> + if (TPMI_MAJOR_VERSION(feature_header) != TPMI_INFO_MAJOR_VERSION) {
>> + ret = -ENODEV;
>> + goto error_info_header;
>> + }
>> +
>> + memcpy_fromio(&header, info_mem + TPMI_INFO_BUS_INFO_OFFSET, 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;
>> +
>> + if (TPMI_MINOR_VERSION(feature_header) >= TPMI_INFO_MINOR_VERSION) {
>> + tpmi_info->plat_info.cdie_mask = header.cdie_mask;
>> + tpmi_info->plat_info.partition = header.partition;
>> + tpmi_info->plat_info.segment = header.segment;
>> + }
>> +
>> +error_info_header:
>> + iounmap(info_mem);
>> +
>> + return ret;
>> +}
>> +
>> +static int tpmi_fetch_pfs_header(struct intel_tpmi_pm_feature *pfs, u64 start, int size)
>> +{
>> + void __iomem *pfs_mem;
>> +
>> + pfs_mem = ioremap(start, size);
>> + if (!pfs_mem)
>> + return -ENOMEM;
>> +
>> + memcpy_fromio(&pfs->pfs_header, pfs_mem, sizeof(pfs->pfs_header));
>> +
>> + iounmap(pfs_mem);
>> +
>> + return 0;
>> +}
>> +
>> +#define TPMI_CAP_OFFSET_UNIT 1024
>> +
>> +/**
>> + * intel_tpmi_init() - Bring up a TPMI instance
>> + * @tpmi_info: Caller allocated and zeroed state of the TPMI instance
>> + * @tpmi_dev: Device representing the TPMI instance. Owns the devm
>> + * allocations and parents the TPMI feature devices
>> + * @parent: Device the TPMI instance was enumerated from
>> + * @resource: Array of @feature_count MMIO resources, one per PFS entry
>> + * @feature_count: Number of entries in @resource
>> + *
>> + * Walk the PFS of the TPMI instance described by @resource, register the
>> + * debugfs interface and create a device for each supported PM feature.
>> + *
>> + * Return: 0 on success, negative errno otherwise.
>> + */
>> +int intel_tpmi_init(struct intel_tpmi_info *tpmi_info, struct device *tpmi_dev,
>> + struct device *parent, struct resource *resource, int feature_count)
>> +{
>> + struct pci_dev *pci_dev = dev_is_pci(parent) ? to_pci_dev(parent) : NULL;
>> + u64 pfs_start = 0;
>> + int ret, i;
>> +
>> + tpmi_info->tpmi_dev = tpmi_dev;
>> + tpmi_info->parent = parent;
>> + tpmi_info->feature_count = feature_count;
>> + tpmi_info->resource = resource;
>> +
>> + /*
>> + * Seed the bus number from the enumerating device. It is only a
>> + * fallback, tpmi_process_info() replaces it with the value the
>> + * TPMI_INFO feature reports.
>> + */
>> + if (pci_dev)
>> + tpmi_info->plat_info.bus_number = pci_dev->bus->number;
>> +
>> + tpmi_info->tpmi_features = devm_kcalloc(tpmi_info->tpmi_dev, tpmi_info->feature_count,
>> + sizeof(*tpmi_info->tpmi_features),
>> + GFP_KERNEL);
>> + if (!tpmi_info->tpmi_features)
>> + return -ENOMEM;
>> +
>> + for (i = 0; i < tpmi_info->feature_count; i++) {
>> + struct intel_tpmi_pm_feature *pfs;
>> + struct resource *res;
>> + u64 res_start;
>> + int size, ret;
>> +
>> + pfs = &tpmi_info->tpmi_features[i];
>> +
>> + res = &tpmi_info->resource[i];
>> + if (!res)
>> + continue;
>> +
>> + res_start = res->start;
>> + size = resource_size(res);
>> + if (size < 0)
>> + continue;
>> +
>> + ret = tpmi_fetch_pfs_header(pfs, res_start, size);
>> + if (ret)
>> + continue;
>> +
>> + if (!pfs_start)
>> + pfs_start = res_start;
>> +
>> + pfs->vsec_offset = pfs_start + pfs->pfs_header.cap_offset * TPMI_CAP_OFFSET_UNIT;
>> +
>> + /*
>> + * 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) {
>> + ret = tpmi_process_info(tpmi_info, pfs);
>> + if (ret)
>> + return ret;
>> +
>> + /*
>> + * The mapping is looked up by pci_dev, so it is only
>> + * meaningful for PCI enumerated TPMI instances.
>> + */
>> + if (pci_dev) {
>> + ret = intel_vsec_set_mapping(&tpmi_info->plat_info,
>> + &pci_dev->dev);
>> + if (ret)
>> + return ret;
>> + }
>> + }
>> +
>> + if (pfs->pfs_header.tpmi_id == TPMI_CONTROL_ID)
>> + tpmi_set_control_base(tpmi_info, pfs);
>> + }
>> +
>> + tpmi_info->pfs_start = pfs_start;
>> +
>> + dev_set_drvdata(tpmi_info->tpmi_dev, tpmi_info);
>> +
>> + /*
>> + * Allow debugfs when security policy allows. Everything this debugfs
>> + * interface provides, can also be done via /dev/mem access. If
>> + * /dev/mem interface is locked, don't allow debugfs to present any
>> + * information. Also check for CAP_SYS_RAWIO as /dev/mem interface.
>> + */
>> + if (!security_locked_down(LOCKDOWN_DEV_MEM) && capable(CAP_SYS_RAWIO))
>> + tpmi_dbgfs_register(tpmi_info);
>> +
>> + ret = tpmi_create_devices(tpmi_info);
>> + if (ret) {
>> + debugfs_remove_recursive(tpmi_info->dbgfs_dir);
>> + return ret;
>> + }
>> +
>> + blocking_notifier_call_chain(&tpmi_notify_list, TPMI_CORE_INIT, tpmi_info->tpmi_dev);
>> +
>> + return 0;
>> +}
>> +EXPORT_SYMBOL_NS_GPL(intel_tpmi_init, "INTEL_TPMI");
>> +
>> +/**
>> + * intel_tpmi_deinit() - Tear a TPMI instance down
>> + * @tpmi_info: State of the TPMI instance, as passed to intel_tpmi_init()
>> + *
>> + * The TPMI feature devices and the devm allocations are released by the
>> + * device core when the TPMI device goes away.
>> + */
>> +void intel_tpmi_deinit(struct intel_tpmi_info *tpmi_info)
>> +{
>> + blocking_notifier_call_chain(&tpmi_notify_list, TPMI_CORE_EXIT, tpmi_info->tpmi_dev);
>> +
>> + debugfs_remove_recursive(tpmi_info->dbgfs_dir);
>> +}
>> +EXPORT_SYMBOL_NS_GPL(intel_tpmi_deinit, "INTEL_TPMI");
>> +
>> +MODULE_IMPORT_NS("INTEL_VSEC");
>> +MODULE_DESCRIPTION("Intel TPMI core support");
>> +MODULE_LICENSE("GPL");
>> diff --git a/drivers/platform/x86/intel/tpmi_common.h b/drivers/platform/x86/intel/tpmi_common.h
>> new file mode 100644
>> index 000000000000..dd4bbe517e1c
>> --- /dev/null
>> +++ b/drivers/platform/x86/intel/tpmi_common.h
>> @@ -0,0 +1,52 @@
>> +/* SPDX-License-Identifier: GPL-2.0-only */
>> +/*
>> + * Intel TPMI core interface for TPMI enumeration methods
>> + *
>> + * Copyright (c) 2026, Intel Corporation.
>> + * All Rights Reserved.
>> + */
>> +
>> +#ifndef _TPMI_COMMON_H_
>> +#define _TPMI_COMMON_H_
>> +
>> +#include <linux/intel_vsec.h>
>> +
>> +struct intel_tpmi_pm_feature;
>> +
>> +/**
>> + * struct intel_tpmi_info - TPMI information for all IDs in an instance
>> + * @tpmi_features: Pointer to a list of TPMI feature instances
>> + * @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
>> + * @tpmi_control_mem: Memory mapped IO for getting control information
>> + * @dbgfs_dir: debugfs entry pointer
>> + * @resource: Array of feature_count MMIO resources, one per PFS entry
>> + * @tpmi_dev: Device this TPMI instance is bound to. It backs the devm
>> + * allocations, holds this structure as its driver data and
>> + * the TPMI feature devices are created under it.
>> + * @parent: Device this TPMI instance was enumerated from. It names
>> + * the debugfs directory and becomes intel_vsec_device::dev
>> + * of every TPMI feature device, so that the enumerating bus
>> + * can find them again, for example during PCI error
>> + * recovery.
>> + *
>> + * Stores the information for all TPMI devices of one TPMI instance.
>> + */
>> +struct intel_tpmi_info {
>> + struct intel_tpmi_pm_feature *tpmi_features;
>> + int feature_count;
>> + u64 pfs_start;
>> + struct oobmsm_plat_info plat_info;
>> + void __iomem *tpmi_control_mem;
>> + struct dentry *dbgfs_dir;
>> + struct resource *resource;
>> + struct device *tpmi_dev;
>> + struct device *parent;
>> +};
>> +
>> +int intel_tpmi_init(struct intel_tpmi_info *tpmi_info, struct device *tpmi_dev,
>> + struct device *parent, struct resource *resource, int feature_count);
>> +void intel_tpmi_deinit(struct intel_tpmi_info *tpmi_info);
>> +
>> +#endif /* _TPMI_COMMON_H_ */
>> diff --git a/drivers/platform/x86/intel/vsec_tpmi.c b/drivers/platform/x86/intel/vsec_tpmi.c
>> index 3274a24152ed..053fe0f58641 100644
>> --- a/drivers/platform/x86/intel/vsec_tpmi.c
>> +++ b/drivers/platform/x86/intel/vsec_tpmi.c
>> @@ -1,21 +1,10 @@
>> // SPDX-License-Identifier: GPL-2.0-only
>> /*
>> - * Driver to enumerate TPMI features and create devices
>> + * Enumerate TPMI features from a PCI VSEC structure
>> *
>> * Copyright (c) 2023, Intel Corporation.
>> * All Rights Reserved.
>> *
>> - * The TPMI (Topology Aware Register and PM Capsule Interface) provides a
>> - * flexible, extendable and PCIe enumerable MMIO interface for PM features.
>> - *
>> - * For example Intel RAPL (Running Average Power Limit) provides a MMIO
>> - * interface using TPMI. This has advantage over traditional MSR
>> - * (Model Specific Register) interface, where a thread needs to be scheduled
>> - * on the target CPU to read or write. Also the RAPL features vary between
>> - * CPU models, and hence lot of model specific code. Here TPMI provides an
>> - * architectural interface by providing hierarchical tables and fields,
>> - * which will not need any model specific implementation.
>> - *
>> * The TPMI interface uses a PCI VSEC structure to expose the location of
>> * MMIO region.
>> *
>> @@ -29,827 +18,18 @@
>> * This TPMI driver will bind to the TPMI auxiliary device object created
>> * by the Intel VSEC driver.
>> *
>> - * The TPMI specification defines a PFS (PM Feature Structure) table.
>> - * This table is present in the TPMI MMIO region. The starting address
>> - * of PFS is derived from the tBIR (Bar Indicator Register) and "Address"
>> - * field from the VSEC header.
>> - *
>> - * Each TPMI PM feature has one entry in the PFS with a unique TPMI
>> - * ID and its access details. The TPMI driver creates device nodes
>> - * for the supported PM features.
>> - *
>> - * The names of the devices created by the TPMI driver start with the
>> - * "intel_vsec.tpmi-" prefix which is followed by a specific name of the
>> - * given PM feature (for example, "intel_vsec.tpmi-rapl.0").
>> - *
>> - * The device nodes are create by using interface "intel_vsec_add_aux()"
>> - * provided by the Intel VSEC driver.
>> + * The starting address of the PFS table in the TPMI MMIO region is derived
>> + * from the tBIR (Bar Indicator Register) and "Address" field from the VSEC
>> + * header by the Intel VSEC driver, which passes the result on as the
>> + * resources of the auxiliary device. Everything that happens from there on
>> + * is enumeration independent and lives in tpmi_common.c.
>> */
>>
>> -#include <linux/align.h>
>> -#include <linux/array_size.h>
>> #include <linux/auxiliary_bus.h>
>> -#include <linux/bitfield.h>
>> -#include <linux/debugfs.h>
>> -#include <linux/cleanup.h>
>> -#include <linux/delay.h>
>> -#include <linux/intel_tpmi.h>
>> #include <linux/intel_vsec.h>
>> -#include <linux/io.h>
>> -#include <linux/iopoll.h>
>> #include <linux/module.h>
>> -#include <linux/notifier.h>
>> -#include <linux/pci.h>
>> -#include <linux/security.h>
>> -#include <linux/sizes.h>
>> -#include <linux/string_helpers.h>
>> -
>> -/**
>> - * struct intel_tpmi_pfs_entry - TPMI PM Feature Structure (PFS) entry
>> - * @tpmi_id: TPMI feature identifier (what the feature is and its data format).
>> - * @num_entries: Number of feature interface instances present in the PFS.
>> - * This represents the maximum number of Power domains in the SoC.
>> - * @entry_size: Interface instance entry size in 32-bit words.
>> - * @cap_offset: Offset from the PM_Features base address to the base of the PM VSEC
>> - * register bank in KB.
>> - * @attribute: Feature attribute: 0=BIOS. 1=OS. 2-3=Reserved.
>> - * @reserved: Bits for use in the future.
>> - *
>> - * Represents one TPMI feature entry data in the PFS retrieved as is
>> - * from the hardware.
>> - */
>> -struct intel_tpmi_pfs_entry {
>> - u64 tpmi_id:8;
>> - u64 num_entries:8;
>> - u64 entry_size:16;
>> - u64 cap_offset:16;
>> - u64 attribute:2;
>> - u64 reserved:14;
>> -} __packed;
>> -
>> -/**
>> - * struct intel_tpmi_pm_feature - TPMI PM Feature information for a TPMI ID
>> - * @pfs_header: PFS header retireved from the hardware.
>> - * @vsec_offset: Starting MMIO address for this feature in bytes. Essentially
>> - * this offset = "Address" from VSEC header + PFS Capability
>> - * offset for this feature entry.
>> - *
>> - * Represents TPMI instance information for one TPMI ID.
>> - */
>> -struct intel_tpmi_pm_feature {
>> - struct intel_tpmi_pfs_entry pfs_header;
>> - u64 vsec_offset;
>> -};
>> -
>> -/**
>> - * struct intel_tpmi_info - TPMI information for all IDs in an instance
>> - * @tpmi_features: Pointer to a list of TPMI feature instances
>> - * @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
>> - * @tpmi_control_mem: Memory mapped IO for getting control information
>> - * @dbgfs_dir: debugfs entry pointer
>> - * @resource: Array of feature_count MMIO resources, one per PFS entry
>> - * @tpmi_dev: Device this TPMI instance is bound to. It backs the devm
>> - * allocations, holds this structure as its driver data and
>> - * the TPMI feature devices are created under it.
>> - * @parent: Device this TPMI instance was enumerated from. It names
>> - * the debugfs directory and becomes intel_vsec_device::dev
>> - * of every TPMI feature device, so that the enumerating bus
>> - * can find them again, for example during PCI error
>> - * recovery.
>> - *
>> - * Stores the information for all TPMI devices of one TPMI instance.
>> - */
>> -struct intel_tpmi_info {
>> - struct intel_tpmi_pm_feature *tpmi_features;
>> - int feature_count;
>> - u64 pfs_start;
>> - struct oobmsm_plat_info plat_info;
>> - void __iomem *tpmi_control_mem;
>> - struct dentry *dbgfs_dir;
>> - struct resource *resource;
>> - struct device *tpmi_dev;
>> - struct device *parent;
>> -};
>> -
>> -/**
>> - * 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
>> - * @segment: PCI segment id
>> - * @partition: Package Partition id
>> - * @cdie_mask: Bitmap of compute dies in the current partition
>> - * @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 segment:8;
>> - u64 partition:2;
>> - u64 cdie_mask:16;
>> - u64 reserved:13;
>> - u64 lock:1;
>> -} __packed;
>> -
>> -/**
>> - * struct tpmi_feature_state - Structure to read hardware state of a feature
>> - * @enabled: Enable state of a feature, 1: enabled, 0: disabled
>> - * @reserved_1: Reserved for future use
>> - * @write_blocked: Writes are blocked means all write operations are ignored
>> - * @read_blocked: Reads are blocked means will read 0xFFs
>> - * @pcs_select: Interface used by out of band software, not used in OS
>> - * @reserved_2: Reserved for future use
>> - * @id: TPMI ID of the feature
>> - * @reserved_3: Reserved for future use
>> - * @locked: When set to 1, OS can't change this register.
>> - *
>> - * The structure is used to read hardware state of a TPMI feature. This
>> - * information is used for debug and restricting operations for this feature.
>> - */
>> -struct tpmi_feature_state {
>> - u32 enabled:1;
>> - u32 reserved_1:3;
>> - u32 write_blocked:1;
>> - u32 read_blocked:1;
>> - u32 pcs_select:1;
>> - u32 reserved_2:1;
>> - u32 id:8;
>> - u32 reserved_3:15;
>> - u32 locked:1;
>> -} __packed;
>> -
>> -/*
>> - * The size from hardware is in u32 units. This size is from a trusted hardware,
>> - * but better to verify for pre silicon platforms. Set size to 0, when invalid.
>> - */
>> -#define TPMI_GET_SINGLE_ENTRY_SIZE(pfs) \
>> -({ \
>> - pfs->pfs_header.entry_size > SZ_1K ? 0 : pfs->pfs_header.entry_size << 2; \
>> -})
>> -
>> -/* Used during auxbus device creation */
>> -static DEFINE_IDA(intel_vsec_tpmi_ida);
>> -
>> -static BLOCKING_NOTIFIER_HEAD(tpmi_notify_list);
>> -
>> -int tpmi_register_notifier(struct notifier_block *nb)
>> -{
>> - return blocking_notifier_chain_register(&tpmi_notify_list, nb);
>> -}
>> -EXPORT_SYMBOL_NS_GPL(tpmi_register_notifier, "INTEL_TPMI");
>> -
>> -int tpmi_unregister_notifier(struct notifier_block *nb)
>> -{
>> - return blocking_notifier_chain_unregister(&tpmi_notify_list, nb);
>> -}
>> -EXPORT_SYMBOL_NS_GPL(tpmi_unregister_notifier, "INTEL_TPMI");
>> -
>> -struct oobmsm_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");
>> -
>> -int tpmi_get_resource_count(struct auxiliary_device *auxdev)
>> -{
>> - struct intel_vsec_device *vsec_dev = auxdev_to_ivdev(auxdev);
>> -
>> - if (vsec_dev)
>> - return vsec_dev->num_resources;
>> -
>> - return 0;
>> -}
>> -EXPORT_SYMBOL_NS_GPL(tpmi_get_resource_count, "INTEL_TPMI");
>> -
>> -struct resource *tpmi_get_resource_at_index(struct auxiliary_device *auxdev, int index)
>> -{
>> - struct intel_vsec_device *vsec_dev = auxdev_to_ivdev(auxdev);
>> -
>> - if (vsec_dev && index < vsec_dev->num_resources)
>> - return &vsec_dev->resource[index];
>> -
>> - return NULL;
>> -}
>> -EXPORT_SYMBOL_NS_GPL(tpmi_get_resource_at_index, "INTEL_TPMI");
>> -
>> -/* TPMI Control Interface */
>> -
>> -#define TPMI_CONTROL_STATUS_OFFSET 0x00
>> -#define TPMI_COMMAND_OFFSET 0x08
>> -#define TMPI_CONTROL_DATA_VAL_OFFSET 0x0c
>> -
>> -/*
>> - * Spec is calling for max 1 seconds to get ownership at the worst
>> - * case. Read at 10 ms timeouts and repeat up to 1 second.
>> - */
>> -#define TPMI_CONTROL_TIMEOUT_US (10 * USEC_PER_MSEC)
>> -#define TPMI_CONTROL_TIMEOUT_MAX_US (1 * USEC_PER_SEC)
>> -
>> -#define TPMI_RB_TIMEOUT_US (10 * USEC_PER_MSEC)
>> -#define TPMI_RB_TIMEOUT_MAX_US USEC_PER_SEC
>> -
>> -/* TPMI Control status register defines */
>> -
>> -#define TPMI_CONTROL_STATUS_RB BIT_ULL(0)
>> -
>> -#define TPMI_CONTROL_STATUS_OWNER GENMASK_ULL(5, 4)
>> -#define TPMI_OWNER_NONE 0
>> -#define TPMI_OWNER_IN_BAND 1
>> -
>> -#define TPMI_CONTROL_STATUS_CPL BIT_ULL(6)
>> -#define TPMI_CONTROL_STATUS_RESULT GENMASK_ULL(15, 8)
>> -#define TPMI_CONTROL_STATUS_LEN GENMASK_ULL(31, 16)
>> -
>> -#define TPMI_CMD_PKT_LEN 2
>> -#define TPMI_CMD_STATUS_SUCCESS 0x40
>> -
>> -/* TPMI command data registers */
>> -#define TMPI_CONTROL_DATA_CMD GENMASK_ULL(7, 0)
>> -#define TPMI_CONTROL_DATA_VAL_FEATURE GENMASK_ULL(48, 40)
>> -
>> -/* Command to send via control interface */
>> -#define TPMI_CONTROL_GET_STATE_CMD 0x10
>> -
>> -#define TPMI_CONTROL_CMD_MASK GENMASK_ULL(48, 40)
>> -
>> -#define TPMI_CMD_LEN_MASK GENMASK_ULL(18, 16)
>> -
>> -/* Mutex to complete get feature status without interruption */
>> -static DEFINE_MUTEX(tpmi_dev_lock);
>> -
>> -static int tpmi_wait_for_owner(struct intel_tpmi_info *tpmi_info, u8 owner)
>> -{
>> - u64 control;
>> -
>> - return readq_poll_timeout(tpmi_info->tpmi_control_mem + TPMI_CONTROL_STATUS_OFFSET,
>> - control, owner == FIELD_GET(TPMI_CONTROL_STATUS_OWNER, control),
>> - TPMI_CONTROL_TIMEOUT_US, TPMI_CONTROL_TIMEOUT_MAX_US);
>> -}
>> -
>> -static int tpmi_read_feature_status(struct intel_tpmi_info *tpmi_info, int feature_id,
>> - struct tpmi_feature_state *feature_state)
>> -{
>> - u64 control, data;
>> - int ret;
>> -
>> - if (!tpmi_info->tpmi_control_mem)
>> - return -EFAULT;
>> -
>> - mutex_lock(&tpmi_dev_lock);
>> -
>> - /* Wait for owner bit set to 0 (none) */
>> - ret = tpmi_wait_for_owner(tpmi_info, TPMI_OWNER_NONE);
>> - if (ret)
>> - goto err_unlock;
>> -
>> - /* set command id to 0x10 for TPMI_GET_STATE */
>> - data = FIELD_PREP(TMPI_CONTROL_DATA_CMD, TPMI_CONTROL_GET_STATE_CMD);
>> -
>> - /* 32 bits for DATA offset and +8 for feature_id field */
>> - data |= FIELD_PREP(TPMI_CONTROL_DATA_VAL_FEATURE, feature_id);
>> -
>> - /* Write at command offset for qword access */
>> - writeq(data, tpmi_info->tpmi_control_mem + TPMI_COMMAND_OFFSET);
>> -
>> - /* Wait for owner bit set to in-band */
>> - ret = tpmi_wait_for_owner(tpmi_info, TPMI_OWNER_IN_BAND);
>> - if (ret)
>> - goto err_unlock;
>> -
>> - /* Set Run Busy and packet length of 2 dwords */
>> - control = TPMI_CONTROL_STATUS_RB;
>> - control |= FIELD_PREP(TPMI_CONTROL_STATUS_LEN, TPMI_CMD_PKT_LEN);
>> -
>> - /* Write at status offset for qword access */
>> - writeq(control, tpmi_info->tpmi_control_mem + TPMI_CONTROL_STATUS_OFFSET);
>> -
>> - /* Wait for Run Busy clear */
>> - ret = readq_poll_timeout(tpmi_info->tpmi_control_mem + TPMI_CONTROL_STATUS_OFFSET,
>> - control, !(control & TPMI_CONTROL_STATUS_RB),
>> - TPMI_RB_TIMEOUT_US, TPMI_RB_TIMEOUT_MAX_US);
>> - if (ret)
>> - goto done_proc;
>> -
>> - control = FIELD_GET(TPMI_CONTROL_STATUS_RESULT, control);
>> - if (control != TPMI_CMD_STATUS_SUCCESS) {
>> - ret = -EBUSY;
>> - goto done_proc;
>> - }
>> -
>> - /* Response is ready */
>> - memcpy_fromio(feature_state, tpmi_info->tpmi_control_mem + TMPI_CONTROL_DATA_VAL_OFFSET,
>> - sizeof(*feature_state));
>> -
>> - ret = 0;
>> -
>> -done_proc:
>> - /* Set CPL "completion" bit */
>> - writeq(TPMI_CONTROL_STATUS_CPL, tpmi_info->tpmi_control_mem + TPMI_CONTROL_STATUS_OFFSET);
>> -
>> -err_unlock:
>> - mutex_unlock(&tpmi_dev_lock);
>> -
>> - return ret;
>> -}
>> -
>> -int tpmi_get_feature_status(struct auxiliary_device *auxdev,
>> - int feature_id, bool *read_blocked, bool *write_blocked)
>> -{
>> - struct intel_tpmi_info *tpmi_info = dev_get_drvdata(auxdev->dev.parent);
>> - struct tpmi_feature_state feature_state;
>> - int ret;
>> -
>> - ret = tpmi_read_feature_status(tpmi_info, feature_id, &feature_state);
>> - if (ret)
>> - return ret;
>> -
>> - *read_blocked = feature_state.read_blocked;
>> - *write_blocked = feature_state.write_blocked;
>> -
>> - return 0;
>> -}
>> -EXPORT_SYMBOL_NS_GPL(tpmi_get_feature_status, "INTEL_TPMI");
>> -
>> -struct dentry *tpmi_get_debugfs_dir(struct auxiliary_device *auxdev)
>> -{
>> - struct intel_tpmi_info *tpmi_info = dev_get_drvdata(auxdev->dev.parent);
>> -
>> - return tpmi_info->dbgfs_dir;
>> -}
>> -EXPORT_SYMBOL_NS_GPL(tpmi_get_debugfs_dir, "INTEL_TPMI");
>> -
>> -static int tpmi_pfs_dbg_show(struct seq_file *s, void *unused)
>> -{
>> - struct intel_tpmi_info *tpmi_info = s->private;
>> - int locked, disabled, read_blocked, write_blocked;
>> - struct tpmi_feature_state feature_state;
>> - struct intel_tpmi_pm_feature *pfs;
>> - int ret, i;
>> -
>> -
>> - seq_printf(s, "tpmi PFS start offset 0x:%llx\n", tpmi_info->pfs_start);
>> - seq_puts(s, "tpmi_id\t\tentries\t\tsize\t\tcap_offset\tattribute\tvsec_offset\tlocked\tdisabled\tread_blocked\twrite_blocked\n");
>> - for (i = 0; i < tpmi_info->feature_count; ++i) {
>> - pfs = &tpmi_info->tpmi_features[i];
>> - ret = tpmi_read_feature_status(tpmi_info, pfs->pfs_header.tpmi_id, &feature_state);
>> - if (ret) {
>> - locked = 'U';
>> - disabled = 'U';
>> - read_blocked = 'U';
>> - write_blocked = 'U';
>> - } else {
>> - disabled = feature_state.enabled ? 'N' : 'Y';
>> - locked = feature_state.locked ? 'Y' : 'N';
>> - read_blocked = feature_state.read_blocked ? 'Y' : 'N';
>> - write_blocked = feature_state.write_blocked ? 'Y' : 'N';
>> - }
>> - seq_printf(s, "0x%02x\t\t0x%02x\t\t0x%04x\t\t0x%04x\t\t0x%02x\t\t0x%016llx\t%c\t%c\t\t%c\t\t%c\n",
>> - pfs->pfs_header.tpmi_id, pfs->pfs_header.num_entries,
>> - pfs->pfs_header.entry_size, pfs->pfs_header.cap_offset,
>> - pfs->pfs_header.attribute, pfs->vsec_offset, locked, disabled,
>> - read_blocked, write_blocked);
>> - }
>> -
>> - return 0;
>> -}
>> -DEFINE_SHOW_ATTRIBUTE(tpmi_pfs_dbg);
>> -
>> -#define MEM_DUMP_COLUMN_COUNT 8
>> -
>> -static int tpmi_mem_dump_show(struct seq_file *s, void *unused)
>> -{
>> - size_t row_size = MEM_DUMP_COLUMN_COUNT * sizeof(u32);
>> - struct intel_tpmi_pm_feature *pfs = s->private;
>> - int count, ret = 0;
>> - void __iomem *mem;
>> - u32 size;
>> - u64 off;
>> - u8 *buffer;
>> -
>> - size = TPMI_GET_SINGLE_ENTRY_SIZE(pfs);
>> - if (!size)
>> - return -EIO;
>> -
>> - buffer = kmalloc(size, GFP_KERNEL);
>> - if (!buffer)
>> - return -ENOMEM;
>> -
>> - off = pfs->vsec_offset;
>> -
>> - mutex_lock(&tpmi_dev_lock);
>> -
>> - for (count = 0; count < pfs->pfs_header.num_entries; ++count) {
>> - seq_printf(s, "TPMI Instance:%d offset:0x%llx\n", count, off);
>> -
>> - mem = ioremap(off, size);
>> - if (!mem) {
>> - ret = -ENOMEM;
>> - break;
>> - }
>> -
>> - memcpy_fromio(buffer, mem, size);
>> -
>> - seq_hex_dump(s, " ", DUMP_PREFIX_OFFSET, row_size, sizeof(u32), buffer, size,
>> - false);
>>
>> - iounmap(mem);
>> -
>> - off += size;
>> - }
>> -
>> - mutex_unlock(&tpmi_dev_lock);
>> -
>> - kfree(buffer);
>> -
>> - return ret;
>> -}
>> -DEFINE_SHOW_ATTRIBUTE(tpmi_mem_dump);
>> -
>> -static ssize_t mem_write(struct file *file, const char __user *userbuf, size_t len, loff_t *ppos)
>> -{
>> - struct seq_file *m = file->private_data;
>> - struct intel_tpmi_pm_feature *pfs = m->private;
>> - u32 addr, value, punit, size;
>> - u32 num_elems;
>> - void __iomem *mem;
>> - int ret;
>> -
>> - size = TPMI_GET_SINGLE_ENTRY_SIZE(pfs);
>> - if (!size)
>> - return -EIO;
>> -
>> - u32 *array __free(kfree) = NULL;
>> - ret = parse_int_array_user(userbuf, len, (int **)&array);
>> - if (ret < 0)
>> - return ret;
>> -
>> - num_elems = *array;
>> - if (num_elems != 3)
>> - return -EINVAL;
>> -
>> - punit = array[1];
>> - addr = array[2];
>> - value = array[3];
>> -
>> - if (!IS_ALIGNED(addr, sizeof(u32)))
>> - return -EINVAL;
>> -
>> - if (punit >= pfs->pfs_header.num_entries)
>> - return -EINVAL;
>> -
>> - if (addr >= size)
>> - return -EINVAL;
>> -
>> - guard(mutex)(&tpmi_dev_lock);
>> -
>> - mem = ioremap(pfs->vsec_offset + punit * size, size);
>> - if (!mem)
>> - return -ENOMEM;
>> -
>> - writel(value, mem + addr);
>> -
>> - iounmap(mem);
>> -
>> - return len;
>> -}
>> -
>> -static int mem_write_show(struct seq_file *s, void *unused)
>> -{
>> - return 0;
>> -}
>> -
>> -static int mem_write_open(struct inode *inode, struct file *file)
>> -{
>> - return single_open(file, mem_write_show, inode->i_private);
>> -}
>> -
>> -static const struct file_operations mem_write_ops = {
>> - .open = mem_write_open,
>> - .read = seq_read,
>> - .write = mem_write,
>> - .llseek = seq_lseek,
>> - .release = single_release,
>> -};
>> -
>> -static void tpmi_dbgfs_register(struct intel_tpmi_info *tpmi_info)
>> -{
>> - char name[64];
>> - int i;
>> -
>> - snprintf(name, sizeof(name), "tpmi-%s", dev_name(tpmi_info->parent));
>> - tpmi_info->dbgfs_dir = debugfs_create_dir(name, NULL);
>> -
>> - debugfs_create_file("pfs_dump", 0444, tpmi_info->dbgfs_dir, tpmi_info, &tpmi_pfs_dbg_fops);
>> -
>> - for (i = 0; i < tpmi_info->feature_count; ++i) {
>> - struct intel_tpmi_pm_feature *pfs;
>> - struct dentry *dir;
>> -
>> - pfs = &tpmi_info->tpmi_features[i];
>> - snprintf(name, sizeof(name), "tpmi-id-%02x", pfs->pfs_header.tpmi_id);
>> - dir = debugfs_create_dir(name, tpmi_info->dbgfs_dir);
>> -
>> - debugfs_create_file("mem_dump", 0444, dir, pfs, &tpmi_mem_dump_fops);
>> - debugfs_create_file("mem_write", 0644, dir, pfs, &mem_write_ops);
>> - }
>> -}
>> -
>> -static void tpmi_set_control_base(struct intel_tpmi_info *tpmi_info,
>> - struct intel_tpmi_pm_feature *pfs)
>> -{
>> - void __iomem *mem;
>> - u32 size;
>> -
>> - size = TPMI_GET_SINGLE_ENTRY_SIZE(pfs);
>> - if (!size)
>> - return;
>> -
>> - mem = devm_ioremap(tpmi_info->tpmi_dev, pfs->vsec_offset, size);
>> - if (!mem)
>> - return;
>> -
>> - /* mem is pointing to TPMI CONTROL base */
>> - tpmi_info->tpmi_control_mem = mem;
>> -}
>> -
>> -/*
>> - * The TPMI IDs are sparse, so the unused entries are left NULL and are
>> - * rejected by the caller like any other unsupported feature.
>> - */
>> -static const char * const intel_tpmi_names[] = {
>> - [TPMI_ID_RAPL] = "tpmi-rapl",
>> - [TPMI_ID_PEM] = "tpmi-pem",
>> - [TPMI_ID_UNCORE] = "tpmi-uncore",
>> - [TPMI_ID_SST] = "tpmi-sst",
>> - [TPMI_ID_PLR] = "tpmi-plr",
>> -};
>> -
>> -static const char *intel_tpmi_name(enum intel_tpmi_id id)
>> -{
>> - if (id >= ARRAY_SIZE(intel_tpmi_names))
>> - return NULL;
>> -
>> - return intel_tpmi_names[id];
>> -}
>> -
>> -static int tpmi_create_device(struct intel_tpmi_info *tpmi_info,
>> - struct intel_tpmi_pm_feature *pfs,
>> - u64 pfs_start)
>> -{
>> - struct intel_vsec_device *feature_vsec_dev;
>> - struct tpmi_feature_state feature_state;
>> - struct resource *res, *tmp;
>> - const char *name;
>> - int i, ret;
>> -
>> - ret = tpmi_read_feature_status(tpmi_info, pfs->pfs_header.tpmi_id, &feature_state);
>> - if (ret)
>> - return ret;
>> -
>> - /*
>> - * If not enabled, continue to look at other features in the PFS, so return -EOPNOTSUPP.
>> - * This will not cause failure of loading of this driver.
>> - */
>> - if (!feature_state.enabled)
>> - return -EOPNOTSUPP;
>> -
>> - name = intel_tpmi_name(pfs->pfs_header.tpmi_id);
>> - if (!name)
>> - return -EOPNOTSUPP;
>> -
>> - feature_vsec_dev = kzalloc_flex(*feature_vsec_dev, resource, pfs->pfs_header.num_entries);
>> - if (!feature_vsec_dev)
>> - return -ENOMEM;
>> -
>> - feature_vsec_dev->num_resources = pfs->pfs_header.num_entries;
>> - res = feature_vsec_dev->resource;
>> -
>> - for (i = 0, tmp = res; i < pfs->pfs_header.num_entries; i++, tmp++) {
>> - u64 entry_size_bytes = pfs->pfs_header.entry_size * sizeof(u32);
>> -
>> - tmp->start = pfs->vsec_offset + entry_size_bytes * i;
>> - tmp->end = tmp->start + entry_size_bytes - 1;
>> - tmp->flags = IORESOURCE_MEM;
>> - }
>> -
>> - feature_vsec_dev->dev = tpmi_info->parent;
>> - 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;
>> -
>> - /*
>> - * intel_vsec_add_aux() is resource managed, no explicit
>> - * delete is required on error or on module unload.
>> - * feature_vsec_dev and res memory are also freed as part of
>> - * device deletion.
>> - *
>> - * "name" must outlive the auxiliary device, as the auxiliary bus
>> - * stores the pointer rather than a copy of the string.
>> - */
>> - return intel_vsec_add_aux(tpmi_info->tpmi_dev, feature_vsec_dev, name);
>> -}
>> -
>> -static int tpmi_create_devices(struct intel_tpmi_info *tpmi_info)
>> -{
>> - int ret, i;
>> -
>> - for (i = 0; i < tpmi_info->feature_count; i++) {
>> - ret = tpmi_create_device(tpmi_info, &tpmi_info->tpmi_features[i],
>> - tpmi_info->pfs_start);
>> - /*
>> - * Fail, if the supported features fails to create device,
>> - * otherwise, continue. Even if one device failed to create,
>> - * fail the loading of driver. Since intel_vsec_add_aux()
>> - * is resource managed, no clean up is required for the
>> - * successfully created devices.
>> - */
>> - if (ret && ret != -EOPNOTSUPP)
>> - return ret;
>> - }
>> -
>> - return 0;
>> -}
>> -
>> -#define TPMI_INFO_BUS_INFO_OFFSET 0x08
>> -#define TPMI_INFO_MAJOR_VERSION 0x00
>> -#define TPMI_INFO_MINOR_VERSION 0x02
>> -
>> -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;
>> - u64 feature_header;
>> - int ret = 0;
>> -
>> - info_mem = ioremap(pfs->vsec_offset, pfs->pfs_header.entry_size * sizeof(u32));
>> - if (!info_mem)
>> - return -ENOMEM;
>> -
>> - feature_header = readq(info_mem);
>> - if (TPMI_MAJOR_VERSION(feature_header) != TPMI_INFO_MAJOR_VERSION) {
>> - ret = -ENODEV;
>> - goto error_info_header;
>> - }
>> -
>> - memcpy_fromio(&header, info_mem + TPMI_INFO_BUS_INFO_OFFSET, 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;
>> -
>> - if (TPMI_MINOR_VERSION(feature_header) >= TPMI_INFO_MINOR_VERSION) {
>> - tpmi_info->plat_info.cdie_mask = header.cdie_mask;
>> - tpmi_info->plat_info.partition = header.partition;
>> - tpmi_info->plat_info.segment = header.segment;
>> - }
>> -
>> -error_info_header:
>> - iounmap(info_mem);
>> -
>> - return ret;
>> -}
>> -
>> -static int tpmi_fetch_pfs_header(struct intel_tpmi_pm_feature *pfs, u64 start, int size)
>> -{
>> - void __iomem *pfs_mem;
>> -
>> - pfs_mem = ioremap(start, size);
>> - if (!pfs_mem)
>> - return -ENOMEM;
>> -
>> - memcpy_fromio(&pfs->pfs_header, pfs_mem, sizeof(pfs->pfs_header));
>> -
>> - iounmap(pfs_mem);
>> -
>> - return 0;
>> -}
>> -
>> -#define TPMI_CAP_OFFSET_UNIT 1024
>> -
>> -static int intel_tpmi_init(struct intel_tpmi_info *tpmi_info, struct device *tpmi_dev,
>> - struct device *parent, struct resource *resource,
>> - int feature_count)
>> -{
>> - struct pci_dev *pci_dev = dev_is_pci(parent) ? to_pci_dev(parent) : NULL;
>> - u64 pfs_start = 0;
>> - int ret, i;
>> -
>> - tpmi_info->tpmi_dev = tpmi_dev;
>> - tpmi_info->parent = parent;
>> - tpmi_info->feature_count = feature_count;
>> - tpmi_info->resource = resource;
>> -
>> - /*
>> - * Seed the bus number from the enumerating device. It is only a
>> - * fallback, tpmi_process_info() replaces it with the value the
>> - * TPMI_INFO feature reports.
>> - */
>> - if (pci_dev)
>> - tpmi_info->plat_info.bus_number = pci_dev->bus->number;
>> -
>> - tpmi_info->tpmi_features = devm_kcalloc(tpmi_info->tpmi_dev, tpmi_info->feature_count,
>> - sizeof(*tpmi_info->tpmi_features),
>> - GFP_KERNEL);
>> - if (!tpmi_info->tpmi_features)
>> - return -ENOMEM;
>> -
>> - for (i = 0; i < tpmi_info->feature_count; i++) {
>> - struct intel_tpmi_pm_feature *pfs;
>> - struct resource *res;
>> - u64 res_start;
>> - int size, ret;
>> -
>> - pfs = &tpmi_info->tpmi_features[i];
>> -
>> - res = &tpmi_info->resource[i];
>> - if (!res)
>> - continue;
>> -
>> - res_start = res->start;
>> - size = resource_size(res);
>> - if (size < 0)
>> - continue;
>> -
>> - ret = tpmi_fetch_pfs_header(pfs, res_start, size);
>> - if (ret)
>> - continue;
>> -
>> - if (!pfs_start)
>> - pfs_start = res_start;
>> -
>> - pfs->vsec_offset = pfs_start + pfs->pfs_header.cap_offset * TPMI_CAP_OFFSET_UNIT;
>> -
>> - /*
>> - * 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) {
>> - ret = tpmi_process_info(tpmi_info, pfs);
>> - if (ret)
>> - return ret;
>> -
>> - /*
>> - * The mapping is looked up by pci_dev, so it is only
>> - * meaningful for PCI enumerated TPMI instances.
>> - */
>> - if (pci_dev) {
>> - ret = intel_vsec_set_mapping(&tpmi_info->plat_info,
>> - &pci_dev->dev);
>> - if (ret)
>> - return ret;
>> - }
>> - }
>> -
>> - if (pfs->pfs_header.tpmi_id == TPMI_CONTROL_ID)
>> - tpmi_set_control_base(tpmi_info, pfs);
>> - }
>> -
>> - tpmi_info->pfs_start = pfs_start;
>> -
>> - dev_set_drvdata(tpmi_info->tpmi_dev, tpmi_info);
>> -
>> - /*
>> - * Allow debugfs when security policy allows. Everything this debugfs
>> - * interface provides, can also be done via /dev/mem access. If
>> - * /dev/mem interface is locked, don't allow debugfs to present any
>> - * information. Also check for CAP_SYS_RAWIO as /dev/mem interface.
>> - */
>> - if (!security_locked_down(LOCKDOWN_DEV_MEM) && capable(CAP_SYS_RAWIO))
>> - tpmi_dbgfs_register(tpmi_info);
>> -
>> - ret = tpmi_create_devices(tpmi_info);
>> - if (ret) {
>> - debugfs_remove_recursive(tpmi_info->dbgfs_dir);
>> - return ret;
>> - }
>> -
>> - blocking_notifier_call_chain(&tpmi_notify_list, TPMI_CORE_INIT, tpmi_info->tpmi_dev);
>> -
>> - return 0;
>> -}
>> -
>> -static void intel_tpmi_deinit(struct intel_tpmi_info *tpmi_info)
>> -{
>> - blocking_notifier_call_chain(&tpmi_notify_list, TPMI_CORE_EXIT, tpmi_info->tpmi_dev);
>> -
>> - debugfs_remove_recursive(tpmi_info->dbgfs_dir);
>> -}
>> +#include "tpmi_common.h"
>>
>> static int tpmi_probe(struct auxiliary_device *auxdev,
>> const struct auxiliary_device_id *id)
>> @@ -886,6 +66,6 @@ static struct auxiliary_driver tpmi_aux_driver = {
>>
>> module_auxiliary_driver(tpmi_aux_driver);
>>
>> -MODULE_IMPORT_NS("INTEL_VSEC");
>> +MODULE_IMPORT_NS("INTEL_TPMI");
>> MODULE_DESCRIPTION("Intel TPMI enumeration module");
>> MODULE_LICENSE("GPL");
>>
--
Sathyanarayanan Kuppuswamy
Linux Kernel Developer
prev parent reply other threads:[~2026-09-23 17:37 UTC|newest]
Thread overview: 13+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-09-22 18:33 [PATCH v1 00/10] Decouple PCI and auxbus details from Intel TPMI driver Kuppuswamy Sathyanarayanan
2026-09-22 18:33 ` [PATCH v1 01/10] platform/x86/intel/tpmi: Use static strings for the feature device names Kuppuswamy Sathyanarayanan
2026-09-22 18:33 ` [PATCH v1 02/10] platform/x86/intel/vsec: Pass a struct device to intel_vsec_set_mapping() Kuppuswamy Sathyanarayanan
2026-09-22 18:33 ` [PATCH v1 03/10] platform/x86/intel/tpmi: Remove unused vsec_dev from intel_tpmi_pm_feature Kuppuswamy Sathyanarayanan
2026-09-22 18:33 ` [PATCH v1 04/10] platform/x86/intel/tpmi: Get tpmi_info directly from the parent device Kuppuswamy Sathyanarayanan
2026-09-22 18:33 ` [PATCH v1 05/10] platform/x86/intel/tpmi: Keep the feature resources in intel_tpmi_info Kuppuswamy Sathyanarayanan
2026-09-22 18:33 ` [PATCH v1 06/10] platform/x86/intel/tpmi: Describe a TPMI instance by its two devices Kuppuswamy Sathyanarayanan
2026-09-22 18:33 ` [PATCH v1 07/10] platform/x86/intel/tpmi: Drop unused arg from tpmi_set_control_base() Kuppuswamy Sathyanarayanan
2026-09-22 18:33 ` [PATCH v1 08/10] platform/x86/intel/tpmi: Do not assume TPMI is enumerated from PCI Kuppuswamy Sathyanarayanan
2026-09-22 18:33 ` [PATCH v1 09/10] platform/x86/intel/tpmi: Split out enumeration independent init and exit Kuppuswamy Sathyanarayanan
2026-09-22 18:33 ` [PATCH v1 10/10] platform/x86/intel/tpmi: Split off the PCI VSEC enumeration Kuppuswamy Sathyanarayanan
2026-09-22 20:19 ` Ilpo Järvinen
2026-09-23 17:37 ` Kuppuswamy Sathyanarayanan [this message]
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=1bf1c3bd-2273-4f6e-a49c-de9f3dbf4e65@linux.intel.com \
--to=sathyanarayanan.kuppuswamy@linux.intel.com \
--cc=andriy.shevchenko@intel.com \
--cc=david.e.box@linux.intel.com \
--cc=hansg@kernel.org \
--cc=ilpo.jarvinen@linux.intel.com \
--cc=linux-kernel@vger.kernel.org \
--cc=platform-driver-x86@vger.kernel.org \
--cc=srinivas.pandruvada@linux.intel.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox
all inboxes | Powered by JetHome®