mirror of https://lore.kernel.org/lkml/
 help / color / mirror / Atom feed
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


      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®