From mboxrd@z Thu Jan 1 00:00:00 1970 Received: from mgamail.intel.com (mgamail.intel.com [192.198.163.5]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 57FEA40F75D; Wed, 23 Sep 2026 17:37:37 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=192.198.163.5 ARC-Seal:i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1790185065; cv=none; b=G1kNZnTgifJ0hr0I2Rk8rtnjK9BJg7i2pGWKEJZhULKiSjwAsZOEOs2DMkmJtr26k0SVNkilW+Ps+9Zn8qERo1OK1xEKjiXU3kwA0T4punSqUOBlM1cDV4XJd4FuGC6MLNVDLwBHPJpFaWcDO0V+H+SnnGhbhaPaEuW0iyav/+8= ARC-Message-Signature:i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1790185065; c=relaxed/simple; bh=uk1riq0CcS0i6QUl6g36+WEkrLDSo3ynvZqQ0EFMFkc=; h=Message-ID:Date:MIME-Version:Subject:To:Cc:References:From: In-Reply-To:Content-Type; b=YPlL4UpdXpQD0vPN+19Xam5YzwAqkxEBzjjsfayIG56cTyHc8eN02OmcQHmEA9GTlb9TBxrck2JneTV3QftYx0PVQ98IP2WFEwXzEaLUt+HJBAxjZ1NJzUswbJQGwgo9qwEnMIT9+TujD/bywkJNJJ2DVQEj3hEWYNE4OMJh/zc= ARC-Authentication-Results:i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=linux.intel.com; spf=pass smtp.mailfrom=linux.intel.com; dkim=pass (2048-bit key) header.d=intel.com header.i=@intel.com header.b=oFVjWnao; arc=none smtp.client-ip=192.198.163.5 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=linux.intel.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=linux.intel.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=intel.com header.i=@intel.com header.b="oFVjWnao" DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=intel.com; i=@intel.com; q=dns/txt; s=Intel; t=1790185058; x=1821721058; h=message-id:date:mime-version:subject:to:cc:references: from:in-reply-to:content-transfer-encoding; bh=uk1riq0CcS0i6QUl6g36+WEkrLDSo3ynvZqQ0EFMFkc=; b=oFVjWnaoE+D9H30pcboZC+N17LxORyBGT58iwFZs+jJsT+59sJYEB5cE vSlW7H/AzwBycGeetvM0P3DfFP3iD1fUvlu+G1e43kP+M9h5eOgTTl+IM kG1VoZr1J9RX49s3n6T+QfmNOqJ4vvLfNyZLq0j78i9G2V6XzpQPjLPMW vjQn/DGSmEOad2jc733DdJH7pppmRLflCY830cWOn3midPMzhUG7AjbF2 Nb2W6+37T2iOihnPNaCOEn8Da43Af1iBO3GzkFp+0hcP9hPD2vtmhYbTr LM7BCdBzsQVFSkoJ0oZWUP4cORkKU6KB/LzZxS3s9Bzd5UaFGVtJOb+pv A==; X-CSE-ConnectionGUID: 7sbGJ5hBT8aswBfamWgV0Q== X-CSE-MsgGUID: 6Y7SeTf8Q2u+xe7NCcjsfQ== X-IronPort-AV: E=McAfee;i="6800,10657,11914"; a="1412636" X-IronPort-AV: E=Sophos;i="6.27,119,1787036400"; d="scan'208";a="1412636" Received: from fmviesa009.fm.intel.com ([10.60.135.149]) by fmvoesa115.fm.intel.com with ESMTP/TLS/ECDHE-RSA-AES256-GCM-SHA384; 23 Sep 2026 10:37:36 -0700 X-CSE-ConnectionGUID: 7zWq+JHiQw6dbfQpoM7mIQ== X-CSE-MsgGUID: 3McLmK6AR/K6RZLTBcZXoA== X-ExtLoop1: 1 X-IronPort-AV: E=Sophos;i="6.27,119,1787036400"; d="scan'208";a="270208830" Received: from soc-pf446t5c.clients.intel.com (HELO [10.24.80.90]) ([10.24.80.90]) by fmviesa009-auth.fm.intel.com with ESMTP/TLS/ECDHE-RSA-AES256-GCM-SHA384; 23 Sep 2026 10:37:35 -0700 Message-ID: <1bf1c3bd-2273-4f6e-a49c-de9f3dbf4e65@linux.intel.com> Date: Wed, 23 Sep 2026 10:37:35 -0700 Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 User-Agent: Mozilla Thunderbird Subject: Re: [PATCH v1 10/10] platform/x86/intel/tpmi: Split off the PCI VSEC enumeration To: =?UTF-8?Q?Ilpo_J=C3=A4rvinen?= Cc: Srinivas Pandruvada , Hans de Goede , David E Box , Andy Shevchenko , platform-driver-x86@vger.kernel.org, LKML References: <20260922183311.3783010-1-sathyanarayanan.kuppuswamy@linux.intel.com> <20260922183311.3783010-11-sathyanarayanan.kuppuswamy@linux.intel.com> Content-Language: en-US From: Kuppuswamy Sathyanarayanan In-Reply-To: Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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 >> Signed-off-by: Srinivas Pandruvada >> Signed-off-by: Kuppuswamy Sathyanarayanan >> Reviewed-by: Andy Shevchenko >> --- >> 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 >> 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 >> +#include >> +#include >> +#include >> +#include >> +#include > > Alphabetic order. > >> +#include >> +#include >> +#include >> +#include >> +#include >> +#include >> +#include >> +#include >> +#include >> +#include >> +#include > > 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 >> + >> +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 >> -#include >> #include >> -#include >> -#include >> -#include >> -#include >> -#include >> #include >> -#include >> -#include >> #include >> -#include >> -#include >> -#include >> -#include >> -#include >> - >> -/** >> - * 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