From 04a34538f4532f90084e0d8654d32eafaa9d68ee Mon Sep 17 00:00:00 2001 From: Alejandro Colomar Date: Tue, 3 Oct 2023 14:59:53 +0300 Subject: [PATCH 1/9] kernel.h: Move ARRAY_SIZE() to a separate header ANBZ: #26486 commit 3cd39bc3b11b8d34b7d7c961a35fdfd18b0ebf75 upstream. Touching files so used for the kernel, forces 'make' to recompile most of the kernel. Having those definitions in more granular files helps avoid recompiling so much of the kernel. Intel-SIG: commit 3cd39bc3b11b kernel.h: Move ARRAY_SIZE() to a separate header Backport PLR (Power Limit Reasons) TPMI driver Signed-off-by: Alejandro Colomar Reviewed-by: Giovanni Cabiddu Reviewed-by: Andy Shevchenko Link: https://lore.kernel.org/r/20230817143352.132583-2-lucas.segarra.fernandez@intel.com [andy: reduced to cover only string.h for now] Signed-off-by: Andy Shevchenko [ Zhang Rui: amend commit log ] Signed-off-by: Zhang Rui --- include/linux/array_size.h | 13 +++++++++++++ include/linux/kernel.h | 7 +------ include/linux/string.h | 1 + 3 files changed, 15 insertions(+), 6 deletions(-) create mode 100644 include/linux/array_size.h diff --git a/include/linux/array_size.h b/include/linux/array_size.h new file mode 100644 index 000000000000..06d7d83196ca --- /dev/null +++ b/include/linux/array_size.h @@ -0,0 +1,13 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef _LINUX_ARRAY_SIZE_H +#define _LINUX_ARRAY_SIZE_H + +#include + +/** + * ARRAY_SIZE - get the number of elements in array @arr + * @arr: array to be sized + */ +#define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0]) + __must_be_array(arr)) + +#endif /* _LINUX_ARRAY_SIZE_H */ diff --git a/include/linux/kernel.h b/include/linux/kernel.h index cee8fe87e9f4..d9ad21058eed 100644 --- a/include/linux/kernel.h +++ b/include/linux/kernel.h @@ -13,6 +13,7 @@ #include #include +#include #include #include #include @@ -50,12 +51,6 @@ #define READ 0 #define WRITE 1 -/** - * ARRAY_SIZE - get the number of elements in array @arr - * @arr: array to be sized - */ -#define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0]) + __must_be_array(arr)) - #define PTR_IF(cond, ptr) ((cond) ? (ptr) : NULL) #define u64_to_user_ptr(x) ( \ diff --git a/include/linux/string.h b/include/linux/string.h index 361294697f8c..433c207a01da 100644 --- a/include/linux/string.h +++ b/include/linux/string.h @@ -2,6 +2,7 @@ #ifndef _LINUX_STRING_H_ #define _LINUX_STRING_H_ +#include #include /* for inline */ #include /* for size_t */ #include /* for NULL */ -- Gitee From 0cecbaa2f4004448f037940777eb1bbf072cfe70 Mon Sep 17 00:00:00 2001 From: Tero Kristo Date: Mon, 27 May 2024 16:29:33 +0300 Subject: [PATCH 2/9] platform/x86/intel/tpmi: Add support for performance limit reasons MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ANBZ: #26486 commit b44d79d6bad16c30978c2cee3421133d3f181494 upstream. Add TPMI ID 0x0C (Perf Limit Reasons) to the list of supported TPMI IDs. Intel-SIG: commit b44d79d6bad1 platform/x86/intel/tpmi: Add support for performance limit reasons Backport PLR (Power Limit Reasons) TPMI driver Reviewed-by: Andy Shevchenko Reviewed-by: Ilpo Järvinen Signed-off-by: Tero Kristo Link: https://lore.kernel.org/r/20240527133400.483634-2-tero.kristo@linux.intel.com Signed-off-by: Ilpo Järvinen [ Zhang Rui: amend commit log ] Signed-off-by: Zhang Rui --- drivers/platform/x86/intel/tpmi.c | 2 ++ include/linux/intel_tpmi.h | 1 + 2 files changed, 3 insertions(+) diff --git a/drivers/platform/x86/intel/tpmi.c b/drivers/platform/x86/intel/tpmi.c index 060f9b86bc03..437c1a06d77f 100644 --- a/drivers/platform/x86/intel/tpmi.c +++ b/drivers/platform/x86/intel/tpmi.c @@ -576,6 +576,8 @@ static const char *intel_tpmi_name(enum intel_tpmi_id id) return "uncore"; case TPMI_ID_SST: return "sst"; + case TPMI_ID_PLR: + return "plr"; default: return NULL; } diff --git a/include/linux/intel_tpmi.h b/include/linux/intel_tpmi.h index 1e880cb0f454..a88ac937d2c2 100644 --- a/include/linux/intel_tpmi.h +++ b/include/linux/intel_tpmi.h @@ -21,6 +21,7 @@ enum intel_tpmi_id { TPMI_ID_PEM = 1, /* Power and Perf excursion Monitor */ TPMI_ID_UNCORE = 2, /* Uncore Frequency Scaling */ TPMI_ID_SST = 5, /* Speed Select Technology */ + TPMI_ID_PLR = 0xc, /* Performance Limit Reasons */ TPMI_CONTROL_ID = 0x80, /* Special ID for getting feature status */ TPMI_INFO_ID = 0x81, /* Special ID for PCI BDF and Package ID information */ }; -- Gitee From ad7b691ff313ce04d720fd994f2766d69b4093fb Mon Sep 17 00:00:00 2001 From: Tero Kristo Date: Mon, 27 May 2024 16:29:34 +0300 Subject: [PATCH 3/9] platform/x86/intel/tpmi: Add API to get debugfs root MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ANBZ: #26486 commit d36842bacf8e3491f555059f27de57b3436cc3ff upstream. Add new API to get the debugfs root directory for TPMI. This allows any TPMI devices to add their own debugfs items under the same directory structure. Intel-SIG: commit d36842bacf8e platform/x86/intel/tpmi: Add API to get debugfs root Backport PLR (Power Limit Reasons) TPMI driver Reviewed-by: Andy Shevchenko Reviewed-by: Ilpo Järvinen Signed-off-by: Tero Kristo Link: https://lore.kernel.org/r/20240527133400.483634-3-tero.kristo@linux.intel.com Signed-off-by: Ilpo Järvinen [ Zhang Rui: amend commit log ] Signed-off-by: Zhang Rui --- drivers/platform/x86/intel/tpmi.c | 9 +++++++++ include/linux/intel_tpmi.h | 1 + 2 files changed, 10 insertions(+) diff --git a/drivers/platform/x86/intel/tpmi.c b/drivers/platform/x86/intel/tpmi.c index 437c1a06d77f..a3430abeb986 100644 --- a/drivers/platform/x86/intel/tpmi.c +++ b/drivers/platform/x86/intel/tpmi.c @@ -357,6 +357,15 @@ int tpmi_get_feature_status(struct auxiliary_device *auxdev, } EXPORT_SYMBOL_NS_GPL(tpmi_get_feature_status, INTEL_TPMI); +struct dentry *tpmi_get_debugfs_dir(struct auxiliary_device *auxdev) +{ + struct intel_vsec_device *intel_vsec_dev = dev_to_ivdev(auxdev->dev.parent); + struct intel_tpmi_info *tpmi_info = auxiliary_get_drvdata(&intel_vsec_dev->auxdev); + + 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; diff --git a/include/linux/intel_tpmi.h b/include/linux/intel_tpmi.h index a88ac937d2c2..ff480b47ae64 100644 --- a/include/linux/intel_tpmi.h +++ b/include/linux/intel_tpmi.h @@ -54,4 +54,5 @@ struct resource *tpmi_get_resource_at_index(struct auxiliary_device *auxdev, int int tpmi_get_resource_count(struct auxiliary_device *auxdev); int tpmi_get_feature_status(struct auxiliary_device *auxdev, int feature_id, bool *read_blocked, bool *write_blocked); +struct dentry *tpmi_get_debugfs_dir(struct auxiliary_device *auxdev); #endif -- Gitee From dec3ce064ce2f1a6d71599cd9adfa8f1b80335ee Mon Sep 17 00:00:00 2001 From: Srinivas Pandruvada Date: Tue, 28 May 2024 10:34:57 +0300 Subject: [PATCH 4/9] platform/x86/intel: TPMI domain id and CPU mapping MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ANBZ: #26486 commit 17ca2780458cdb0afc623e3432b1977847da3d15 upstream. Each TPMI power domain includes a group of CPUs. Several power management settings in this case applicable to a group of CPUs. There can be several power domains in a CPU package. So, provide interfaces for: - Get power domain id for a Linux CPU - Get mask of Linux CPUs in a power domain Hardware Punit uses different CPU numbering, which is not based on APIC (Advanced Programmable Interrupt Controller) CPU numbering. The Linux CPU numbering is based on APIC CPU numbering. Some PM features like Intel Speed Select, the CPU core mask provided by the hardware is based on the Punit CPU numbering. To use the core mask, this mask needs to be converted to a Linux CPUs mask. So, provide interfaces for: - Convert to a Linux CPU number from a Punit CPU number - Convert to a Punit CPU number from a Linux CPU number On each CPU online, MSR 0x54 is used to read the mapping and stores in a per cpu array. Create a hash for faster searching of a Linux CPU number from a Punit CPU number. Intel-SIG: commit 17ca2780458c platform/x86/intel: TPMI domain id and CPU mapping Backport PLR (Power Limit Reasons) TPMI driver Signed-off-by: Srinivas Pandruvada [tero.kristo: minor updates] Reviewed-by: Andy Shevchenko Reviewed-by: Ilpo Järvinen Signed-off-by: Tero Kristo Link: https://lore.kernel.org/r/20240528073457.497816-1-tero.kristo@linux.intel.com Signed-off-by: Ilpo Järvinen [ Zhang Rui: amend commit log ] Signed-off-by: Zhang Rui --- drivers/platform/x86/intel/Kconfig | 4 + drivers/platform/x86/intel/Makefile | 3 + .../platform/x86/intel/tpmi_power_domains.c | 235 ++++++++++++++++++ .../platform/x86/intel/tpmi_power_domains.h | 18 ++ 4 files changed, 260 insertions(+) create mode 100644 drivers/platform/x86/intel/tpmi_power_domains.c create mode 100644 drivers/platform/x86/intel/tpmi_power_domains.h diff --git a/drivers/platform/x86/intel/Kconfig b/drivers/platform/x86/intel/Kconfig index e9dc0c021029..e97a97355d5a 100644 --- a/drivers/platform/x86/intel/Kconfig +++ b/drivers/platform/x86/intel/Kconfig @@ -192,10 +192,14 @@ config INTEL_SMARTCONNECT This driver checks to determine whether the device has Intel Smart Connect enabled, and if so disables it. +config INTEL_TPMI_POWER_DOMAINS + 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 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 c1d5fe05e3f3..10437e56027d 100644 --- a/drivers/platform/x86/intel/Makefile +++ b/drivers/platform/x86/intel/Makefile @@ -53,6 +53,9 @@ obj-$(CONFIG_INTEL_PUNIT_IPC) += intel_punit_ipc.o intel_vsec_tpmi-y := tpmi.o obj-$(CONFIG_INTEL_TPMI) += intel_vsec_tpmi.o +intel_tpmi_power_domains-y := tpmi_power_domains.o +obj-$(CONFIG_INTEL_TPMI_POWER_DOMAINS) += intel_tpmi_power_domains.o + # Intel Uncore drivers intel-rst-y := rst.o obj-$(CONFIG_INTEL_RST) += intel-rst.o diff --git a/drivers/platform/x86/intel/tpmi_power_domains.c b/drivers/platform/x86/intel/tpmi_power_domains.c new file mode 100644 index 000000000000..4eb02553957c --- /dev/null +++ b/drivers/platform/x86/intel/tpmi_power_domains.c @@ -0,0 +1,235 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Mapping of TPMI power domains CPU mapping + * + * Copyright (c) 2024, Intel Corporation. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "tpmi_power_domains.h" + +#define MSR_PM_LOGICAL_ID 0x54 + +/* + * Struct of MSR 0x54 + * [15:11] PM_DOMAIN_ID + * [10:3] MODULE_ID (aka IDI_AGENT_ID) + * [2:0] LP_ID + * For Atom: + * [2] Always 0 + * [1:0] core ID within module + * For Core + * [2:1] Always 0 + * [0] thread ID + */ + +#define LP_ID_MASK GENMASK_ULL(2, 0) +#define MODULE_ID_MASK GENMASK_ULL(10, 3) +#define PM_DOMAIN_ID_MASK GENMASK_ULL(15, 11) + +/** + * struct tpmi_cpu_info - Mapping information for a CPU + * @hnode: Used to add mapping information to hash list + * @linux_cpu: Linux CPU number + * @pkg_id: Package ID of this CPU + * @punit_thread_id: Punit thread id of this CPU + * @punit_core_id: Punit core id + * @punit_domain_id: Power domain id from Punit + * + * Structure to store mapping information for a Linux CPU + * to a Punit core, thread and power domain. + */ +struct tpmi_cpu_info { + struct hlist_node hnode; + int linux_cpu; + u8 pkg_id; + u8 punit_thread_id; + u8 punit_core_id; + u8 punit_domain_id; +}; + +static DEFINE_PER_CPU(struct tpmi_cpu_info, tpmi_cpu_info); + +/* The dynamically assigned cpu hotplug state to free later */ +static enum cpuhp_state tpmi_hp_state __read_mostly; + +#define MAX_POWER_DOMAINS 8 + +static cpumask_t *tpmi_power_domain_mask; + +/* Lock to protect tpmi_power_domain_mask and tpmi_cpu_hash */ +static DEFINE_MUTEX(tpmi_lock); + +static const struct x86_cpu_id tpmi_cpu_ids[] = { + X86_MATCH_VFM(INTEL_GRANITERAPIDS_X, NULL), + X86_MATCH_VFM(INTEL_ATOM_CRESTMONT_X, NULL), + X86_MATCH_VFM(INTEL_ATOM_CRESTMONT, NULL), + X86_MATCH_VFM(INTEL_GRANITERAPIDS_D, NULL), + {} +}; +MODULE_DEVICE_TABLE(x86cpu, tpmi_cpu_ids); + +static DECLARE_HASHTABLE(tpmi_cpu_hash, 8); + +static bool tpmi_domain_is_valid(struct tpmi_cpu_info *info) +{ + return info->pkg_id < topology_max_packages() && + info->punit_domain_id < MAX_POWER_DOMAINS; +} + +int tpmi_get_linux_cpu_number(int package_id, int domain_id, int punit_core_id) +{ + struct tpmi_cpu_info *info; + int ret = -EINVAL; + + guard(mutex)(&tpmi_lock); + hash_for_each_possible(tpmi_cpu_hash, info, hnode, punit_core_id) { + if (info->punit_domain_id == domain_id && info->pkg_id == package_id) { + ret = info->linux_cpu; + break; + } + } + + return ret; +} +EXPORT_SYMBOL_NS_GPL(tpmi_get_linux_cpu_number, INTEL_TPMI_POWER_DOMAIN); + +int tpmi_get_punit_core_number(int cpu_no) +{ + if (cpu_no >= num_possible_cpus()) + return -EINVAL; + + return per_cpu(tpmi_cpu_info, cpu_no).punit_core_id; +} +EXPORT_SYMBOL_NS_GPL(tpmi_get_punit_core_number, INTEL_TPMI_POWER_DOMAIN); + +int tpmi_get_power_domain_id(int cpu_no) +{ + if (cpu_no >= num_possible_cpus()) + return -EINVAL; + + return per_cpu(tpmi_cpu_info, cpu_no).punit_domain_id; +} +EXPORT_SYMBOL_NS_GPL(tpmi_get_power_domain_id, INTEL_TPMI_POWER_DOMAIN); + +cpumask_t *tpmi_get_power_domain_mask(int cpu_no) +{ + struct tpmi_cpu_info *info; + cpumask_t *mask; + int index; + + if (cpu_no >= num_possible_cpus()) + return NULL; + + info = &per_cpu(tpmi_cpu_info, cpu_no); + if (!tpmi_domain_is_valid(info)) + return NULL; + + index = info->pkg_id * MAX_POWER_DOMAINS + info->punit_domain_id; + guard(mutex)(&tpmi_lock); + mask = &tpmi_power_domain_mask[index]; + + return mask; +} +EXPORT_SYMBOL_NS_GPL(tpmi_get_power_domain_mask, INTEL_TPMI_POWER_DOMAIN); + +static int tpmi_get_logical_id(unsigned int cpu, struct tpmi_cpu_info *info) +{ + u64 data; + int ret; + + ret = rdmsrl_safe(MSR_PM_LOGICAL_ID, &data); + if (ret) + return ret; + + info->punit_domain_id = FIELD_GET(PM_DOMAIN_ID_MASK, data); + if (info->punit_domain_id >= MAX_POWER_DOMAINS) + return -EINVAL; + + info->punit_thread_id = FIELD_GET(LP_ID_MASK, data); + info->punit_core_id = FIELD_GET(MODULE_ID_MASK, data); + info->pkg_id = topology_physical_package_id(cpu); + info->linux_cpu = cpu; + + return 0; +} + +static int tpmi_cpu_online(unsigned int cpu) +{ + struct tpmi_cpu_info *info = &per_cpu(tpmi_cpu_info, cpu); + int ret, index; + + /* Don't fail CPU online for some bad mapping of CPUs */ + ret = tpmi_get_logical_id(cpu, info); + if (ret) + return 0; + + index = info->pkg_id * MAX_POWER_DOMAINS + info->punit_domain_id; + + guard(mutex)(&tpmi_lock); + cpumask_set_cpu(cpu, &tpmi_power_domain_mask[index]); + hash_add(tpmi_cpu_hash, &info->hnode, info->punit_core_id); + + return 0; +} + +static int __init tpmi_init(void) +{ + const struct x86_cpu_id *id; + u64 data; + int ret; + + id = x86_match_cpu(tpmi_cpu_ids); + if (!id) + return -ENODEV; + + /* Check for MSR 0x54 presence */ + ret = rdmsrl_safe(MSR_PM_LOGICAL_ID, &data); + if (ret) + return ret; + + tpmi_power_domain_mask = kcalloc(size_mul(topology_max_packages(), MAX_POWER_DOMAINS), + sizeof(*tpmi_power_domain_mask), GFP_KERNEL); + if (!tpmi_power_domain_mask) + return -ENOMEM; + + ret = cpuhp_setup_state(CPUHP_AP_ONLINE_DYN, + "platform/x86/tpmi_power_domains:online", + tpmi_cpu_online, NULL); + if (ret < 0) { + kfree(tpmi_power_domain_mask); + return ret; + } + + tpmi_hp_state = ret; + + return 0; +} +module_init(tpmi_init) + +static void __exit tpmi_exit(void) +{ + cpuhp_remove_state(tpmi_hp_state); + kfree(tpmi_power_domain_mask); +} +module_exit(tpmi_exit) + +MODULE_DESCRIPTION("TPMI Power Domains Mapping"); +MODULE_LICENSE("GPL"); diff --git a/drivers/platform/x86/intel/tpmi_power_domains.h b/drivers/platform/x86/intel/tpmi_power_domains.h new file mode 100644 index 000000000000..e35750dd9273 --- /dev/null +++ b/drivers/platform/x86/intel/tpmi_power_domains.h @@ -0,0 +1,18 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Mapping of TPMI power domain and CPUs + * + * Copyright (c) 2024, Intel Corporation. + */ + +#ifndef _TPMI_POWER_DOMAINS_H_ +#define _TPMI_POWER_DOMAINS_H_ + +#include + +int tpmi_get_linux_cpu_number(int package_id, int die_id, int punit_core_id); +int tpmi_get_punit_core_number(int cpu_no); +int tpmi_get_power_domain_id(int cpu_no); +cpumask_t *tpmi_get_power_domain_mask(int cpu_no); + +#endif -- Gitee From 06eddcbf6c3db8c8c837ee39098ce455225280de Mon Sep 17 00:00:00 2001 From: Tero Kristo Date: Mon, 27 May 2024 16:29:36 +0300 Subject: [PATCH 5/9] platform/x86/intel/tpmi: Add new auxiliary driver for performance limits MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ANBZ: #26486 commit 811f67c51636d43791995bf483c5c1904900b531 upstream. Add new auxiliary driver that exposes the SoC performance limit reasons via debugfs interface. Intel-SIG: commit 811f67c51636 platform/x86/intel/tpmi: Add new auxiliary driver for performance limits Backport PLR (Power Limit Reasons) TPMI driver Reviewed-by: Andy Shevchenko Reviewed-by: Ilpo Järvinen Signed-off-by: Tero Kristo Link: https://lore.kernel.org/r/20240527133400.483634-5-tero.kristo@linux.intel.com Signed-off-by: Ilpo Järvinen [ Zhang Rui: amend commit log ] Signed-off-by: Zhang Rui --- drivers/platform/x86/intel/Kconfig | 7 + drivers/platform/x86/intel/Makefile | 1 + drivers/platform/x86/intel/intel_plr_tpmi.c | 204 ++++++++++++++++++++ 3 files changed, 212 insertions(+) create mode 100644 drivers/platform/x86/intel/intel_plr_tpmi.c diff --git a/drivers/platform/x86/intel/Kconfig b/drivers/platform/x86/intel/Kconfig index e97a97355d5a..ad50bbabec61 100644 --- a/drivers/platform/x86/intel/Kconfig +++ b/drivers/platform/x86/intel/Kconfig @@ -209,6 +209,13 @@ config INTEL_TPMI To compile this driver as a module, choose M here: the module will be called intel_vsec_tpmi. +config INTEL_PLR_TPMI + tristate "Intel SoC TPMI Power Limit Reasons driver" + depends on INTEL_TPMI + help + This driver provides the TPMI power limit reasons status information + via debugfs files. + config INTEL_TURBO_MAX_3 bool "Intel Turbo Boost Max Technology 3.0 enumeration driver" depends on X86_64 && SCHED_MC_PRIO diff --git a/drivers/platform/x86/intel/Makefile b/drivers/platform/x86/intel/Makefile index 10437e56027d..74db065c82d6 100644 --- a/drivers/platform/x86/intel/Makefile +++ b/drivers/platform/x86/intel/Makefile @@ -52,6 +52,7 @@ obj-$(CONFIG_INTEL_PUNIT_IPC) += intel_punit_ipc.o # TPMI drivers intel_vsec_tpmi-y := tpmi.o obj-$(CONFIG_INTEL_TPMI) += intel_vsec_tpmi.o +obj-$(CONFIG_INTEL_PLR_TPMI) += intel_plr_tpmi.o intel_tpmi_power_domains-y := tpmi_power_domains.o obj-$(CONFIG_INTEL_TPMI_POWER_DOMAINS) += intel_tpmi_power_domains.o diff --git a/drivers/platform/x86/intel/intel_plr_tpmi.c b/drivers/platform/x86/intel/intel_plr_tpmi.c new file mode 100644 index 000000000000..3c51e50bd244 --- /dev/null +++ b/drivers/platform/x86/intel/intel_plr_tpmi.c @@ -0,0 +1,204 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Performance Limit Reasons via TPMI + * + * Copyright (c) 2024, Intel Corporation. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define PLR_HEADER 0x00 +#define PLR_DIE_LEVEL 0x18 + +#define PLR_INVALID GENMASK_ULL(63, 0) + +struct tpmi_plr_die { + void __iomem *base; +}; + +struct tpmi_plr { + struct dentry *dbgfs_dir; + struct tpmi_plr_die *die_info; + int num_dies; +}; + +static const char * const plr_coarse_reasons[] = { + "FREQUENCY", + "CURRENT", + "POWER", + "THERMAL", + "PLATFORM", + "MCP", + "RAS", + "MISC", + "QOS", + "DFC", +}; + +static u64 plr_read(struct tpmi_plr_die *plr_die, int offset) +{ + return readq(plr_die->base + offset); +} + +static void plr_write(u64 val, struct tpmi_plr_die *plr_die, int offset) +{ + writeq(val, plr_die->base + offset); +} + +static void plr_print_bits(struct seq_file *s, u64 val, int bits) +{ + const unsigned long mask[] = { BITMAP_FROM_U64(val) }; + int bit; + + for_each_set_bit(bit, mask, bits) { + if (bit >= ARRAY_SIZE(plr_coarse_reasons)) + seq_printf(s, " UNKNOWN(%d)", bit); + else + seq_printf(s, " %s", plr_coarse_reasons[bit]); + } + + if (!val) + seq_puts(s, " none"); + + seq_putc(s, '\n'); +} + +static int plr_status_show(struct seq_file *s, void *unused) +{ + struct tpmi_plr_die *plr_die = s->private; + u64 val; + + val = plr_read(plr_die, PLR_DIE_LEVEL); + seq_puts(s, "cpus"); + plr_print_bits(s, val, 32); + + return 0; +} + +static ssize_t plr_status_write(struct file *filp, const char __user *ubuf, + size_t count, loff_t *ppos) +{ + struct seq_file *s = filp->private_data; + struct tpmi_plr_die *plr_die = s->private; + bool val; + int ret; + + ret = kstrtobool_from_user(ubuf, count, &val); + if (ret) + return ret; + + if (val != 0) + return -EINVAL; + + plr_write(0, plr_die, PLR_DIE_LEVEL); + + return count; +} +DEFINE_SHOW_STORE_ATTRIBUTE(plr_status); + +static int intel_plr_probe(struct auxiliary_device *auxdev, const struct auxiliary_device_id *id) +{ + struct intel_tpmi_plat_info *plat_info; + struct dentry *dentry; + int i, num_resources; + struct resource *res; + struct tpmi_plr *plr; + void __iomem *base; + char name[16]; + int err; + + plat_info = tpmi_get_platform_data(auxdev); + if (!plat_info) + return dev_err_probe(&auxdev->dev, -EINVAL, "No platform info\n"); + + dentry = tpmi_get_debugfs_dir(auxdev); + if (!dentry) + return dev_err_probe(&auxdev->dev, -ENODEV, "No TPMI debugfs directory.\n"); + + num_resources = tpmi_get_resource_count(auxdev); + if (!num_resources) + return -EINVAL; + + plr = devm_kzalloc(&auxdev->dev, sizeof(*plr), GFP_KERNEL); + if (!plr) + return -ENOMEM; + + plr->die_info = devm_kcalloc(&auxdev->dev, num_resources, sizeof(*plr->die_info), + GFP_KERNEL); + if (!plr->die_info) + return -ENOMEM; + + plr->num_dies = num_resources; + plr->dbgfs_dir = debugfs_create_dir("plr", dentry); + + for (i = 0; i < num_resources; i++) { + res = tpmi_get_resource_at_index(auxdev, i); + if (!res) { + err = dev_err_probe(&auxdev->dev, -EINVAL, "No resource\n"); + goto err; + } + + base = devm_ioremap_resource(&auxdev->dev, res); + if (IS_ERR(base)) { + err = PTR_ERR(base); + goto err; + } + + plr->die_info[i].base = base; + + if (plr_read(&plr->die_info[i], PLR_HEADER) == PLR_INVALID) + continue; + + snprintf(name, sizeof(name), "domain%d", i); + + dentry = debugfs_create_dir(name, plr->dbgfs_dir); + debugfs_create_file("status", 0444, dentry, &plr->die_info[i], + &plr_status_fops); + } + + auxiliary_set_drvdata(auxdev, plr); + + return 0; + +err: + debugfs_remove_recursive(plr->dbgfs_dir); + return err; +} + +static void intel_plr_remove(struct auxiliary_device *auxdev) +{ + struct tpmi_plr *plr = auxiliary_get_drvdata(auxdev); + + debugfs_remove_recursive(plr->dbgfs_dir); +} + +static const struct auxiliary_device_id intel_plr_id_table[] = { + { .name = "intel_vsec.tpmi-plr" }, + {} +}; +MODULE_DEVICE_TABLE(auxiliary, intel_plr_id_table); + +static struct auxiliary_driver intel_plr_aux_driver = { + .id_table = intel_plr_id_table, + .remove = intel_plr_remove, + .probe = intel_plr_probe, +}; +module_auxiliary_driver(intel_plr_aux_driver); + +MODULE_IMPORT_NS(INTEL_TPMI); +MODULE_DESCRIPTION("Intel TPMI PLR Driver"); +MODULE_LICENSE("GPL"); -- Gitee From a9ef0f7168c0d5a39e6732d524aa02bea001d623 Mon Sep 17 00:00:00 2001 From: Tero Kristo Date: Mon, 27 May 2024 16:29:37 +0300 Subject: [PATCH 6/9] platform/x86/intel/tpmi/plr: Add support for the plr mailbox MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ANBZ: #26486 commit 9e9397a41b7b1db10603edcb13913034bd41cce0 upstream. Add support for reading fine grained power limit reasons via the PLR mailbox. Intel-SIG: commit 9e9397a41b7b platform/x86/intel/tpmi/plr: Add support for the plr mailbox Backport PLR (Power Limit Reasons) TPMI driver Reviewed-by: Andy Shevchenko Reviewed-by: Ilpo Järvinen Signed-off-by: Tero Kristo Link: https://lore.kernel.org/r/20240527133400.483634-6-tero.kristo@linux.intel.com Signed-off-by: Ilpo Järvinen [ Zhang Rui: amend commit log ] Signed-off-by: Zhang Rui --- drivers/platform/x86/intel/intel_plr_tpmi.c | 157 +++++++++++++++++++- 1 file changed, 153 insertions(+), 4 deletions(-) diff --git a/drivers/platform/x86/intel/intel_plr_tpmi.c b/drivers/platform/x86/intel/intel_plr_tpmi.c index 3c51e50bd244..c1aa52c23d25 100644 --- a/drivers/platform/x86/intel/intel_plr_tpmi.c +++ b/drivers/platform/x86/intel/intel_plr_tpmi.c @@ -7,6 +7,7 @@ #include #include +#include #include #include #include @@ -14,26 +15,50 @@ #include #include #include +#include #include +#include #include #include +#include #include #include #include +#include "tpmi_power_domains.h" + #define PLR_HEADER 0x00 +#define PLR_MAILBOX_INTERFACE 0x08 +#define PLR_MAILBOX_DATA 0x10 #define PLR_DIE_LEVEL 0x18 +#define PLR_MODULE_ID_MASK GENMASK_ULL(19, 12) +#define PLR_RUN_BUSY BIT_ULL(63) + +#define PLR_COMMAND_WRITE 1 + #define PLR_INVALID GENMASK_ULL(63, 0) +#define PLR_TIMEOUT_US 5 +#define PLR_TIMEOUT_MAX_US 1000 + +#define PLR_COARSE_REASON_BITS 32 + +struct tpmi_plr; + struct tpmi_plr_die { void __iomem *base; + struct mutex lock; /* Protect access to PLR mailbox */ + int package_id; + int die_id; + struct tpmi_plr *plr; }; struct tpmi_plr { struct dentry *dbgfs_dir; struct tpmi_plr_die *die_info; int num_dies; + struct auxiliary_device *auxdev; }; static const char * const plr_coarse_reasons[] = { @@ -49,6 +74,39 @@ static const char * const plr_coarse_reasons[] = { "DFC", }; +static const char * const plr_fine_reasons[] = { + "FREQUENCY_CDYN0", + "FREQUENCY_CDYN1", + "FREQUENCY_CDYN2", + "FREQUENCY_CDYN3", + "FREQUENCY_CDYN4", + "FREQUENCY_CDYN5", + "FREQUENCY_FCT", + "FREQUENCY_PCS_TRL", + "CURRENT_MTPMAX", + "POWER_FAST_RAPL", + "POWER_PKG_PL1_MSR_TPMI", + "POWER_PKG_PL1_MMIO", + "POWER_PKG_PL1_PCS", + "POWER_PKG_PL2_MSR_TPMI", + "POWER_PKG_PL2_MMIO", + "POWER_PKG_PL2_PCS", + "POWER_PLATFORM_PL1_MSR_TPMI", + "POWER_PLATFORM_PL1_MMIO", + "POWER_PLATFORM_PL1_PCS", + "POWER_PLATFORM_PL2_MSR_TPMI", + "POWER_PLATFORM_PL2_MMIO", + "POWER_PLATFORM_PL2_PCS", + "UNKNOWN(22)", + "THERMAL_PER_CORE", + "DFC_UFS", + "PLATFORM_PROCHOT", + "PLATFORM_HOT_VR", + "UNKNOWN(27)", + "UNKNOWN(28)", + "MISC_PCS_PSTATE", +}; + static u64 plr_read(struct tpmi_plr_die *plr_die, int offset) { return readq(plr_die->base + offset); @@ -59,16 +117,68 @@ static void plr_write(u64 val, struct tpmi_plr_die *plr_die, int offset) writeq(val, plr_die->base + offset); } +static int plr_read_cpu_status(struct tpmi_plr_die *plr_die, int cpu, + u64 *status) +{ + u64 regval; + int ret; + + lockdep_assert_held(&plr_die->lock); + + regval = FIELD_PREP(PLR_MODULE_ID_MASK, tpmi_get_punit_core_number(cpu)); + regval |= PLR_RUN_BUSY; + + plr_write(regval, plr_die, PLR_MAILBOX_INTERFACE); + + ret = readq_poll_timeout(plr_die->base + PLR_MAILBOX_INTERFACE, regval, + !(regval & PLR_RUN_BUSY), PLR_TIMEOUT_US, + PLR_TIMEOUT_MAX_US); + if (ret) + return ret; + + *status = plr_read(plr_die, PLR_MAILBOX_DATA); + + return 0; +} + +static int plr_clear_cpu_status(struct tpmi_plr_die *plr_die, int cpu) +{ + u64 regval; + + lockdep_assert_held(&plr_die->lock); + + regval = FIELD_PREP(PLR_MODULE_ID_MASK, tpmi_get_punit_core_number(cpu)); + regval |= PLR_RUN_BUSY | PLR_COMMAND_WRITE; + + plr_write(0, plr_die, PLR_MAILBOX_DATA); + + plr_write(regval, plr_die, PLR_MAILBOX_INTERFACE); + + return readq_poll_timeout(plr_die->base + PLR_MAILBOX_INTERFACE, regval, + !(regval & PLR_RUN_BUSY), PLR_TIMEOUT_US, + PLR_TIMEOUT_MAX_US); +} + static void plr_print_bits(struct seq_file *s, u64 val, int bits) { const unsigned long mask[] = { BITMAP_FROM_U64(val) }; - int bit; + const char *str; + int bit, index; for_each_set_bit(bit, mask, bits) { - if (bit >= ARRAY_SIZE(plr_coarse_reasons)) - seq_printf(s, " UNKNOWN(%d)", bit); + if (bit < PLR_COARSE_REASON_BITS) { + if (bit < ARRAY_SIZE(plr_coarse_reasons)) + str = plr_coarse_reasons[bit]; + } else { + index = bit - PLR_COARSE_REASON_BITS; + if (index < ARRAY_SIZE(plr_fine_reasons)) + str = plr_fine_reasons[index]; + } + + if (str) + seq_printf(s, " %s", str); else - seq_printf(s, " %s", plr_coarse_reasons[bit]); + seq_printf(s, " UNKNOWN(%d)", bit); } if (!val) @@ -80,12 +190,33 @@ static void plr_print_bits(struct seq_file *s, u64 val, int bits) static int plr_status_show(struct seq_file *s, void *unused) { struct tpmi_plr_die *plr_die = s->private; + int ret; u64 val; val = plr_read(plr_die, PLR_DIE_LEVEL); seq_puts(s, "cpus"); plr_print_bits(s, val, 32); + guard(mutex)(&plr_die->lock); + + for (int cpu = 0; cpu < nr_cpu_ids; cpu++) { + if (plr_die->die_id != tpmi_get_power_domain_id(cpu)) + continue; + + if (plr_die->package_id != topology_physical_package_id(cpu)) + continue; + + seq_printf(s, "cpu%d", cpu); + ret = plr_read_cpu_status(plr_die, cpu, &val); + if (ret) { + dev_err(&plr_die->plr->auxdev->dev, "Failed to read PLR for cpu %d, ret=%d\n", + cpu, ret); + return ret; + } + + plr_print_bits(s, val, 64); + } + return 0; } @@ -106,6 +237,18 @@ static ssize_t plr_status_write(struct file *filp, const char __user *ubuf, plr_write(0, plr_die, PLR_DIE_LEVEL); + guard(mutex)(&plr_die->lock); + + for (int cpu = 0; cpu < nr_cpu_ids; cpu++) { + if (plr_die->die_id != tpmi_get_power_domain_id(cpu)) + continue; + + if (plr_die->package_id != topology_physical_package_id(cpu)) + continue; + + plr_clear_cpu_status(plr_die, cpu); + } + return count; } DEFINE_SHOW_STORE_ATTRIBUTE(plr_status); @@ -144,6 +287,7 @@ static int intel_plr_probe(struct auxiliary_device *auxdev, const struct auxilia plr->num_dies = num_resources; plr->dbgfs_dir = debugfs_create_dir("plr", dentry); + plr->auxdev = auxdev; for (i = 0; i < num_resources; i++) { res = tpmi_get_resource_at_index(auxdev, i); @@ -159,6 +303,10 @@ static int intel_plr_probe(struct auxiliary_device *auxdev, const struct auxilia } plr->die_info[i].base = base; + plr->die_info[i].package_id = plat_info->package_id; + plr->die_info[i].die_id = i; + plr->die_info[i].plr = plr; + mutex_init(&plr->die_info[i].lock); if (plr_read(&plr->die_info[i], PLR_HEADER) == PLR_INVALID) continue; @@ -200,5 +348,6 @@ static struct auxiliary_driver intel_plr_aux_driver = { module_auxiliary_driver(intel_plr_aux_driver); MODULE_IMPORT_NS(INTEL_TPMI); +MODULE_IMPORT_NS(INTEL_TPMI_POWER_DOMAIN); MODULE_DESCRIPTION("Intel TPMI PLR Driver"); MODULE_LICENSE("GPL"); -- Gitee From 19c445239a0b751899f56c9e60a2ada0e13230a9 Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Mon, 15 Jul 2024 15:22:55 -0500 Subject: [PATCH 7/9] platform/x86/intel/tpmi/plr: Fix output in plr_print_bits() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ANBZ: #26486 commit 182c6941c55347c735e70ffca77741cd000cbec7 upstream. Smatch complains that 'str' can be used without being initialized: drivers/platform/x86/intel/intel_plr_tpmi.c:178 plr_print_bits() error: uninitialized symbol 'str'. In this loop, we iterate over all the set bits and print the name of the bit. The intention is that if there is a bit which is between 0-31 we look for the name in the first array plr_coarse_reasons[] which has 10 elements. If the bit is in the 32-63 range we look for it in the plr_fine_reasons[] array which has 30 elements. If the bit is in the invalid ranges, 10-31 or 62-63, then we should print "UNKNOWN(%d)". The problem is that 'str' needs to be initialized at the start of each iteration, otherwise if we can't find the string then instead of printing "UNKNOWN(%d)", we will re-print whatever the previous bit was. Intel-SIG: commit 182c6941c553 platform/x86/intel/tpmi/plr: Fix output in plr_print_bits() Backport PLR (Power Limit Reasons) TPMI driver Fixes: 9e9397a41b7b ("platform/x86/intel/tpmi/plr: Add support for the plr mailbox") Signed-off-by: Dan Carpenter Link: https://lore.kernel.org/r/b0084e70-4144-445a-9b89-fb19f6b8336a@stanley.mountain Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen [ Zhang Rui: amend commit log ] Signed-off-by: Zhang Rui --- drivers/platform/x86/intel/intel_plr_tpmi.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/platform/x86/intel/intel_plr_tpmi.c b/drivers/platform/x86/intel/intel_plr_tpmi.c index c1aa52c23d25..69ace6a629bc 100644 --- a/drivers/platform/x86/intel/intel_plr_tpmi.c +++ b/drivers/platform/x86/intel/intel_plr_tpmi.c @@ -162,10 +162,11 @@ static int plr_clear_cpu_status(struct tpmi_plr_die *plr_die, int cpu) static void plr_print_bits(struct seq_file *s, u64 val, int bits) { const unsigned long mask[] = { BITMAP_FROM_U64(val) }; - const char *str; int bit, index; for_each_set_bit(bit, mask, bits) { + const char *str = NULL; + if (bit < PLR_COARSE_REASON_BITS) { if (bit < ARRAY_SIZE(plr_coarse_reasons)) str = plr_coarse_reasons[bit]; -- Gitee From 51185cafe7a645a7b4a1d6d553d675e729cf00b4 Mon Sep 17 00:00:00 2001 From: Srinivas Pandruvada Date: Fri, 3 Jan 2025 07:52:53 -0800 Subject: [PATCH 8/9] platform/x86/intel: power-domains: Add Clearwater Forest support MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ANBZ: #26486 commit bee9a0838fd223823e5a6d85c055ab1691dc738e upstream. Add Clearwater Forest support (INTEL_ATOM_DARKMONT_X) to tpmi_cpu_ids to support domaid id mappings. Intel-SIG: commit bee9a0838fd2 platform/x86/intel: power-domains: Add Clearwater Forest support Backport PLR (Power Limit Reasons) TPMI driver Signed-off-by: Srinivas Pandruvada Link: https://lore.kernel.org/r/20250103155255.1488139-1-srinivas.pandruvada@linux.intel.com Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen [ Zhang Rui: amend commit log ] Signed-off-by: Zhang Rui --- drivers/platform/x86/intel/tpmi_power_domains.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/platform/x86/intel/tpmi_power_domains.c b/drivers/platform/x86/intel/tpmi_power_domains.c index 4eb02553957c..6b7933fab0f1 100644 --- a/drivers/platform/x86/intel/tpmi_power_domains.c +++ b/drivers/platform/x86/intel/tpmi_power_domains.c @@ -81,6 +81,7 @@ static const struct x86_cpu_id tpmi_cpu_ids[] = { X86_MATCH_VFM(INTEL_GRANITERAPIDS_X, NULL), X86_MATCH_VFM(INTEL_ATOM_CRESTMONT_X, NULL), X86_MATCH_VFM(INTEL_ATOM_CRESTMONT, NULL), + X86_MATCH_VFM(INTEL_ATOM_DARKMONT_X, NULL), X86_MATCH_VFM(INTEL_GRANITERAPIDS_D, NULL), {} }; -- Gitee From e77810343d95dd833d1c64fbbca223e3095e3ea5 Mon Sep 17 00:00:00 2001 From: Zhang Rui Date: Tue, 11 Nov 2025 10:54:22 +0800 Subject: [PATCH 9/9] anolis: configs: enable config for PLR ANBZ: #26486 Enable config for PLR (Perf Limit Reasons). Signed-off-by: Zhang Rui --- anolis/configs/L1-RECOMMEND/x86/CONFIG_INTEL_PLR_TPMI | 1 + 1 file changed, 1 insertion(+) create mode 100644 anolis/configs/L1-RECOMMEND/x86/CONFIG_INTEL_PLR_TPMI diff --git a/anolis/configs/L1-RECOMMEND/x86/CONFIG_INTEL_PLR_TPMI b/anolis/configs/L1-RECOMMEND/x86/CONFIG_INTEL_PLR_TPMI new file mode 100644 index 000000000000..b35666300dd3 --- /dev/null +++ b/anolis/configs/L1-RECOMMEND/x86/CONFIG_INTEL_PLR_TPMI @@ -0,0 +1 @@ +CONFIG_INTEL_PLR_TPMI=m -- Gitee