* [PATCH v2 7/7] platform/x86/intel/pmc: Add Nova Lake support to intel_pmc_core driver
From: Xi Pardee @ 2026-04-08 22:21 UTC (permalink / raw)
To: xi.pardee, irenic.rajneesh, david.e.box, ilpo.jarvinen,
platform-driver-x86, linux-kernel, linux-pm
In-Reply-To: <20260408222144.3288928-1-xi.pardee@linux.intel.com>
Add Nova Lake support in intel_pmc_core driver
Signed-off-by: Xi Pardee <xi.pardee@linux.intel.com>
---
drivers/platform/x86/intel/pmc/Makefile | 3 +-
drivers/platform/x86/intel/pmc/core.c | 2 +
drivers/platform/x86/intel/pmc/core.h | 31 +
drivers/platform/x86/intel/pmc/nvl.c | 1539 +++++++++++++++++++++++
drivers/platform/x86/intel/pmc/ptl.c | 2 +-
5 files changed, 1575 insertions(+), 2 deletions(-)
create mode 100644 drivers/platform/x86/intel/pmc/nvl.c
diff --git a/drivers/platform/x86/intel/pmc/Makefile b/drivers/platform/x86/intel/pmc/Makefile
index bb960c8721d77..23853e867c912 100644
--- a/drivers/platform/x86/intel/pmc/Makefile
+++ b/drivers/platform/x86/intel/pmc/Makefile
@@ -4,7 +4,8 @@
#
intel_pmc_core-y := core.o spt.o cnp.o icl.o \
- tgl.o adl.o mtl.o arl.o lnl.o ptl.o wcl.o
+ tgl.o adl.o mtl.o arl.o \
+ lnl.o ptl.o wcl.o nvl.o
obj-$(CONFIG_INTEL_PMC_CORE) += intel_pmc_core.o
intel_pmc_core_pltdrv-y := pltdrv.o
obj-$(CONFIG_INTEL_PMC_CORE) += intel_pmc_core_pltdrv.o
diff --git a/drivers/platform/x86/intel/pmc/core.c b/drivers/platform/x86/intel/pmc/core.c
index c84e75b19aac3..207708f4ceb94 100644
--- a/drivers/platform/x86/intel/pmc/core.c
+++ b/drivers/platform/x86/intel/pmc/core.c
@@ -1849,6 +1849,8 @@ static const struct x86_cpu_id intel_pmc_core_ids[] = {
X86_MATCH_VFM(INTEL_LUNARLAKE_M, &lnl_pmc_dev),
X86_MATCH_VFM(INTEL_PANTHERLAKE_L, &ptl_pmc_dev),
X86_MATCH_VFM(INTEL_WILDCATLAKE_L, &wcl_pmc_dev),
+ X86_MATCH_VFM(INTEL_NOVALAKE, &nvl_s_pmc_dev),
+ X86_MATCH_VFM(INTEL_NOVALAKE_L, &nvl_h_pmc_dev),
{}
};
diff --git a/drivers/platform/x86/intel/pmc/core.h b/drivers/platform/x86/intel/pmc/core.h
index a741e4698f195..f2b4a20d2ff44 100644
--- a/drivers/platform/x86/intel/pmc/core.h
+++ b/drivers/platform/x86/intel/pmc/core.h
@@ -307,6 +307,29 @@ enum ppfear_regs {
#define WCL_NUM_S0IX_BLOCKER 94
#define WCL_BLK_REQ_OFFSET 50
+/* Nova Lake */
+#define NVL_PCDH_PPFEAR_NUM_ENTRIES 13
+#define NVL_PCDH_PMC_MMIO_REG_LEN 0x363c
+#define NVL_PCDS_PMC_MMIO_REG_LEN 0x3118
+#define NVL_PCHS_PMC_MMIO_REG_LEN 0x30d8
+#define NVL_LPM_PRI_OFFSET 0x17a4
+#define NVL_LPM_EN_OFFSET 0x17a0
+#define NVL_LPM_RESIDENCY_OFFSET 0x17a8
+#define NVL_LPM_LIVE_STATUS_OFFSET 0x1760
+#define NVL_LPM_NUM_MAPS 15
+#define NVL_PCDH_NUM_S0IX_BLOCKER 107
+#define NVL_PCDS_NUM_S0IX_BLOCKER 71
+#define NVL_PCHS_NUM_S0IX_BLOCKER 54
+#define NVL_PCDS_PMC_LTR_RESERVED 0x1bac
+#define NVL_PCDH_BLK_REQ_OFFSET 53
+#define NVL_PCDS_BLK_REQ_OFFSET 18
+#define NVL_PCHS_BLK_REQ_OFFSET 46
+#define NVL_PMT_PC_GUID 0x13000101
+#define NVL_PMT_DMU_GUID 0x1a000101
+#define NVL_LTR_BLK_OFFSET 64
+#define NVL_PKGC_BLK_OFFSET 4
+#define NVL_PMT_DMU_DIE_C6_OFFSET 25
+
/* SSRAM PMC Device ID */
/* LNL */
#define PMC_DEVID_LNL_SOCM 0xa87f
@@ -329,6 +352,11 @@ enum ppfear_regs {
#define PMC_DEVID_MTL_IOEP 0x7ecf
#define PMC_DEVID_MTL_IOEM 0x7ebf
+/* NVL */
+#define PMC_DEVID_NVL_PCDH 0xd37e
+#define PMC_DEVID_NVL_PCDS 0xd47e
+#define PMC_DEVID_NVL_PCHS 0x6e27
+
extern const char *pmc_lpm_modes[];
struct pmc_bit_map {
@@ -558,6 +586,7 @@ extern const struct pmc_reg_map mtl_ioep_reg_map;
extern const struct pmc_bit_map ptl_pcdp_clocksource_status_map[];
extern const struct pmc_bit_map ptl_pcdp_vnn_req_status_3_map[];
extern const struct pmc_bit_map ptl_pcdp_signal_status_map[];
+extern const struct pmc_bit_map ptl_pcdp_ltr_show_map[];
void pmc_core_get_tgl_lpm_reqs(struct platform_device *pdev);
int pmc_core_send_ltr_ignore(struct pmc_dev *pmcdev, u32 value, int ignore);
@@ -581,6 +610,8 @@ extern struct pmc_dev_info arl_h_pmc_dev;
extern struct pmc_dev_info lnl_pmc_dev;
extern struct pmc_dev_info ptl_pmc_dev;
extern struct pmc_dev_info wcl_pmc_dev;
+extern struct pmc_dev_info nvl_s_pmc_dev;
+extern struct pmc_dev_info nvl_h_pmc_dev;
void cnl_suspend(struct pmc_dev *pmcdev);
int cnl_resume(struct pmc_dev *pmcdev);
diff --git a/drivers/platform/x86/intel/pmc/nvl.c b/drivers/platform/x86/intel/pmc/nvl.c
new file mode 100644
index 0000000000000..96f4244d602be
--- /dev/null
+++ b/drivers/platform/x86/intel/pmc/nvl.c
@@ -0,0 +1,1539 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * This file contains platform specific structure definitions
+ * and init function used by Nova Lake PCH.
+ *
+ * Copyright (c) 2026, Intel Corporation.
+ */
+
+#include <linux/pci.h>
+
+#include "core.h"
+
+/* PMC SSRAM PMT Telemetry GUIDS */
+#define PCDH_LPM_REQ_GUID 0x01093101
+#define PCHS_LPM_REQ_GUID 0x01092101
+#define PCDS_LPM_REQ_GUID 0x01091102
+
+/*
+ * Die Mapping to Product.
+ * Product PCDDie PCHDie
+ * NVL-H PCD-H None
+ * NVL-S PCD-S PCH-S
+ */
+
+static const struct pmc_bit_map nvl_pcdh_pfear_map[] = {
+ {"PMC_PGD0", BIT(0)},
+ {"FUSE_OSSE_PGD0", BIT(1)},
+ {"SPI_PGD0", BIT(2)},
+ {"XHCI_PGD0", BIT(3)},
+ {"SPA_PGD0", BIT(4)},
+ {"SPB_PGD0", BIT(5)},
+ {"MPFPW2_PGD0", BIT(6)},
+ {"GBE_PGD0", BIT(7)},
+
+ {"SBR16B20_PGD0", BIT(0)},
+ {"DBG_SBR_PGD0", BIT(1)},
+ {"SBR16B7_PGD0", BIT(2)},
+ {"STRC_PGD0", BIT(3)},
+ {"SBR16B8_PGD0", BIT(4)},
+ {"D2D_DISP_PGD1", BIT(5)},
+ {"LPSS_PGD0", BIT(6)},
+ {"LPC_PGD0", BIT(7)},
+
+ {"SMB_PGD0", BIT(0)},
+ {"ISH_PGD0", BIT(1)},
+ {"SBR16B2_PGD0", BIT(2)},
+ {"NPK_PGD0", BIT(3)},
+ {"D2D_NOC_PGD1", BIT(4)},
+ {"DBG_SBR16B_PGD0", BIT(5)},
+ {"FUSE_PGD0", BIT(6)},
+ {"SBR16B0_PGD0", BIT(7)},
+
+ {"P2SB0_PGD0", BIT(0)},
+ {"OTG_PGD0", BIT(1)},
+ {"EXI_PGD0", BIT(2)},
+ {"CSE_PGD0", BIT(3)},
+ {"CSME_KVM_PGD0", BIT(4)},
+ {"CSME_PMT_PGD0", BIT(5)},
+ {"CSME_CLINK_PGD0", BIT(6)},
+ {"SBR16B21_PGD0", BIT(7)},
+
+ {"CSME_USBR_PGD0", BIT(0)},
+ {"SBR16B22_PGD0", BIT(1)},
+ {"CSME_SMT1_PGD0", BIT(2)},
+ {"MPFPW1_PGD0", BIT(3)},
+ {"CSME_SMS2_PGD0", BIT(4)},
+ {"CSME_SMS_PGD0", BIT(5)},
+ {"CSME_RTC_PGD0", BIT(6)},
+ {"CSMEPSF_PGD0", BIT(7)},
+
+ {"D2D_NOC_PGD0", BIT(0)},
+ {"ESE_PGD0", BIT(1)},
+ {"SBR16B6_PGD0", BIT(2)},
+ {"P2SB1_PGD0", BIT(3)},
+ {"SBR16B3_PGD0", BIT(4)},
+ {"OSSE_SMT1_PGD0", BIT(5)},
+ {"D2D_DISP_PGD0", BIT(6)},
+ {"SNPS_USB2_A_PGD0", BIT(7)},
+
+ {"U3FPW1_PGD0", BIT(0)},
+ {"FIA_X_PGD0", BIT(1)},
+ {"PSF4_PGD0", BIT(2)},
+ {"CNVI_PGD0", BIT(3)},
+ {"UFSX2_PGD0", BIT(4)},
+ {"ENDBG_PGD0", BIT(5)},
+ {"DBC_PGD0", BIT(6)},
+ {"FIA_PG_PGD0", BIT(7)},
+
+ {"D2D_IPU_PGD0", BIT(0)},
+ {"NPK_PGD1", BIT(1)},
+ {"FIACPCB_X_PGD0", BIT(2)},
+ {"SBR8B4_PGD0", BIT(3)},
+ {"DBG_PSF_PGD0", BIT(4)},
+ {"PSF6_PGD0", BIT(5)},
+ {"UFSPW1_PGD0", BIT(6)},
+ {"FIA_U_PGD0", BIT(7)},
+
+ {"PSF8_PGD0", BIT(0)},
+ {"SBR16B9_PGD0", BIT(1)},
+ {"PSF0_PGD0", BIT(2)},
+ {"FIACPCB_U_PGD0", BIT(3)},
+ {"TAM_PGD0", BIT(4)},
+ {"D2D_NOC_PGD2", BIT(5)},
+ {"SBR8B2_PGD0", BIT(6)},
+ {"THC0_PGD0", BIT(7)},
+
+ {"THC1_PGD0", BIT(0)},
+ {"PMC_PGD1", BIT(1)},
+ {"DISP_PGA1_PGD0", BIT(2)},
+ {"TCSS_PGD0", BIT(3)},
+ {"DISP_PGA_PGD0", BIT(4)},
+ {"SBR16B1_PGD0", BIT(5)},
+ {"SBRG_PGD0", BIT(6)},
+ {"PSF5_PGD0", BIT(7)},
+
+ {"SBR8B3_PGD0", BIT(0)},
+ {"ACE_PGD0", BIT(1)},
+ {"ACE_PGD1", BIT(2)},
+ {"ACE_PGD2", BIT(3)},
+ {"ACE_PGD3", BIT(4)},
+ {"ACE_PGD4", BIT(5)},
+ {"ACE_PGD5", BIT(6)},
+ {"ACE_PGD6", BIT(7)},
+
+ {"ACE_PGD7", BIT(0)},
+ {"ACE_PGD8", BIT(1)},
+ {"ACE_PGD9", BIT(2)},
+ {"ACE_PGD10", BIT(3)},
+ {"FIACPCB_PG_PGD0", BIT(4)},
+ {"SNPS_USB2_B_PGD0", BIT(5)},
+ {"OSSE_PGD0", BIT(6)},
+ {"SBR8B0_PGD0", BIT(7)},
+
+ {"SBR16B4_PGD0", BIT(0)},
+ {"CSME_PTIO_PGD0", BIT(1)},
+ {}
+};
+
+static const struct pmc_bit_map *ext_nvl_pcdh_pfear_map[] = {
+ nvl_pcdh_pfear_map,
+ NULL
+};
+
+const struct pmc_bit_map nvl_pcdh_clocksource_status_map[] = {
+ {"AON2_OFF_STS", BIT(0), 1},
+ {"AON3_OFF_STS", BIT(1), 0},
+ {"AON4_OFF_STS", BIT(2), 1},
+ {"AON5_OFF_STS", BIT(3), 1},
+ {"AON1_OFF_STS", BIT(4), 0},
+ {"XTAL_LVM_OFF_STS", BIT(5), 0},
+ {"MPFPW1_0_PLL_OFF_STS", BIT(6), 1},
+ {"D2D_PLL_OFF_STS", BIT(7), 1},
+ {"USB3_PLL_OFF_STS", BIT(8), 1},
+ {"AON3_SPL_OFF_STS", BIT(9), 1},
+ {"MPFPW2_0_PLL_OFF_STS", BIT(12), 1},
+ {"XTAL_AGGR_OFF_STS", BIT(17), 1},
+ {"USB2_PLL_OFF_STS", BIT(18), 0},
+ {"DDI2_PLL_OFF_STS", BIT(19), 1},
+ {"SE_TCSS_PLL_OFF_STS", BIT(20), 1},
+ {"DDI_PLL_OFF_STS", BIT(21), 1},
+ {"FILTER_PLL_OFF_STS", BIT(22), 1},
+ {"ACE_PLL_OFF_STS", BIT(24), 0},
+ {"FABRIC_PLL_OFF_STS", BIT(25), 1},
+ {"SOC_PLL_OFF_STS", BIT(26), 1},
+ {"REF_PLL_OFF_STS", BIT(28), 1},
+ {"IMG_PLL_OFF_STS", BIT(29), 1},
+ {"GENLOCK_FILTER_PLL_OFF_STS", BIT(30), 1},
+ {"RTC_PLL_OFF_STS", BIT(31), 0},
+ {}
+};
+
+static const struct pmc_bit_map nvl_pcdh_power_gating_status_0_map[] = {
+ {"PMC_PGD0_PG_STS", BIT(0), 0},
+ {"FUSE_OSSE_PGD0_PG_STS", BIT(1), 0},
+ {"ESPISPI_PGD0_PG_STS", BIT(2), 0},
+ {"XHCI_PGD0_PG_STS", BIT(3), 1},
+ {"SPA_PGD0_PG_STS", BIT(4), 1},
+ {"SPB_PGD0_PG_STS", BIT(5), 1},
+ {"MPFPW2_PGD0_PG_STS", BIT(6), 0},
+ {"GBE_PGD0_PG_STS", BIT(7), 1},
+ {"SBR16B20_PGD0_PG_STS", BIT(8), 0},
+ {"DBG_PGD0_PG_STS", BIT(9), 0},
+ {"SBR16B7_PGD0_PG_STS", BIT(10), 0},
+ {"STRC_PGD0_PG_STS", BIT(11), 0},
+ {"SBR16B8_PGD0_PG_STS", BIT(12), 0},
+ {"D2D_DISP_PGD1_PG_STS", BIT(13), 1},
+ {"LPSS_PGD0_PG_STS", BIT(14), 1},
+ {"LPC_PGD0_PG_STS", BIT(15), 0},
+ {"SMB_PGD0_PG_STS", BIT(16), 0},
+ {"ISH_PGD0_PG_STS", BIT(17), 0},
+ {"SBR16B2_PGD0_PG_STS", BIT(18), 0},
+ {"NPK_PGD0_PG_STS", BIT(19), 0},
+ {"D2D_NOC_PGD1_PG_STS", BIT(20), 1},
+ {"DBG_SBR16B_PGD0_PG_STS", BIT(21), 0},
+ {"FUSE_PGD0_PG_STS", BIT(22), 0},
+ {"SBR16B0_PGD0_PG_STS", BIT(23), 0},
+ {"P2SB0_PGD0_PG_STS", BIT(24), 1},
+ {"XDCI_PGD0_PG_STS", BIT(25), 1},
+ {"EXI_PGD0_PG_STS", BIT(26), 0},
+ {"CSE_PGD0_PG_STS", BIT(27), 1},
+ {"KVMCC_PGD0_PG_STS", BIT(28), 1},
+ {"PMT_PGD0_PG_STS", BIT(29), 1},
+ {"CLINK_PGD0_PG_STS", BIT(30), 1},
+ {"SBR16B21_PGD0_PG_STS", BIT(31), 0},
+ {}
+};
+
+static const struct pmc_bit_map nvl_pcdh_power_gating_status_1_map[] = {
+ {"USBR0_PGD0_PG_STS", BIT(0), 1},
+ {"SBR16B22_PGD0_PG_STS", BIT(1), 0},
+ {"SMT1_PGD0_PG_STS", BIT(2), 1},
+ {"MPFPW1_PGD0_PG_STS", BIT(3), 0},
+ {"SMS2_PGD0_PG_STS", BIT(4), 1},
+ {"SMS1_PGD0_PG_STS", BIT(5), 1},
+ {"CSMERTC_PGD0_PG_STS", BIT(6), 0},
+ {"CSMEPSF_PGD0_PG_STS", BIT(7), 0},
+ {"D2D_NOC_PGD0_PG_STS", BIT(8), 0},
+ {"ESE_PGD0_PG_STS", BIT(9), 1},
+ {"SBR16B6_PGD0_PG_STS", BIT(10), 0},
+ {"P2SB1_PGD0_PG_STS", BIT(11), 1},
+ {"SBR16B3_PGD0_PG_STS", BIT(12), 0},
+ {"OSSE_SMT1_PGD0_PG_STS", BIT(13), 1},
+ {"D2D_DISP_PGD0_PG_STS", BIT(14), 1},
+ {"SNPA_USB2_A_PGD0_PG_STS", BIT(15), 0},
+ {"U3FPW1_PGD0_PG_STS", BIT(16), 0},
+ {"FIA_X_PGD0_PG_STS", BIT(17), 0},
+ {"PSF4_PGD0_PG_STS", BIT(18), 0},
+ {"CNVI_PGD0_PG_STS", BIT(19), 0},
+ {"UFSX2_PGD0_PG_STS", BIT(20), 1},
+ {"ENDBG_PGD0_PG_STS", BIT(21), 0},
+ {"DBC_PGD0_PG_STS", BIT(22), 0},
+ {"FIA_PG_PGD0_PG_STS", BIT(23), 0},
+ {"D2D_IPU_PGD0_PG_STS", BIT(24), 1},
+ {"NPK_PGD1_PG_STS", BIT(25), 0},
+ {"FIACPCB_X_PGD0_PG_STS", BIT(26), 0},
+ {"SBR8B4_PGD0_PG_STS", BIT(27), 0},
+ {"DBG_PSF_PGD0_PG_STS", BIT(28), 0},
+ {"PSF6_PGD0_PG_STS", BIT(29), 0},
+ {"UFSPW1_PGD0_PG_STS", BIT(30), 0},
+ {"FIA_U_PGD0_PG_STS", BIT(31), 0},
+ {}
+};
+
+static const struct pmc_bit_map nvl_pcdh_power_gating_status_2_map[] = {
+ {"PSF8_PGD0_PG_STS", BIT(0), 0},
+ {"SBR16B9_PGD0_PG_STS", BIT(1), 0},
+ {"PSF0_PGD0_PG_STS", BIT(2), 0},
+ {"FIACPCB_U_PGD0_PG_STS", BIT(3), 0},
+ {"TAM_PGD0_PG_STS", BIT(4), 1},
+ {"D2D_NOC_PGD2_PG_STS", BIT(5), 1},
+ {"SBR8B2_PGD0_PG_STS", BIT(6), 0},
+ {"THC0_PGD0_PG_STS", BIT(7), 1},
+ {"THC1_PGD0_PG_STS", BIT(8), 1},
+ {"PMC_PGD1_PG_STS", BIT(9), 0},
+ {"DISP_PGA1_PGD0_PG_STS", BIT(10), 0},
+ {"TCSS_PGD0_PG_STS", BIT(11), 0},
+ {"DISP_PGA_PGD0_PG_STS", BIT(12), 0},
+ {"SBR16B1_PGD0_PG_STS", BIT(13), 0},
+ {"SBRG_PGD0_PG_STS", BIT(14), 0},
+ {"PSF5_PGD0_PG_STS", BIT(15), 0},
+ {"SBR8B3_PGD0_PG_STS", BIT(16), 0},
+ {"ACE_PGD0_PG_STS", BIT(17), 0},
+ {"ACE_PGD1_PG_STS", BIT(18), 0},
+ {"ACE_PGD2_PG_STS", BIT(19), 0},
+ {"ACE_PGD3_PG_STS", BIT(20), 0},
+ {"ACE_PGD4_PG_STS", BIT(21), 0},
+ {"ACE_PGD5_PG_STS", BIT(22), 0},
+ {"ACE_PGD6_PG_STS", BIT(23), 0},
+ {"ACE_PGD7_PG_STS", BIT(24), 0},
+ {"ACE_PGD8_PG_STS", BIT(25), 0},
+ {"ACE_PGD9_PG_STS", BIT(26), 0},
+ {"ACE_PGD10_PG_STS", BIT(27), 0},
+ {"FIACPCB_PG_PGD0_PG_STS", BIT(28), 0},
+ {"SNPS_USB2_B_PGD0_PG_STS", BIT(29), 0},
+ {"OSSE_PGD0_PG_STS", BIT(30), 1},
+ {"SBR8B0_PGD0_PG_STS", BIT(31), 0},
+ {}
+};
+
+static const struct pmc_bit_map nvl_pcdh_power_gating_status_3_map[] = {
+ {"SBR16B4_PGD0_PG_STS", BIT(0), 0},
+ {"PTIO_PGD0_PG_STS", BIT(1), 1},
+ {}
+};
+
+static const struct pmc_bit_map nvl_pcdh_d3_status_0_map[] = {
+ {"LPSS_D3_STS", BIT(3), 1},
+ {"XDCI_D3_STS", BIT(4), 1},
+ {"XHCI_D3_STS", BIT(5), 1},
+ {"OSSE_D3_STS", BIT(6), 0},
+ {"SPA_D3_STS", BIT(12), 0},
+ {"SPB_D3_STS", BIT(13), 0},
+ {"ESPISPI_D3_STS", BIT(18), 0},
+ {"PSTH_D3_STS", BIT(21), 0},
+ {}
+};
+
+static const struct pmc_bit_map nvl_pcdh_d3_status_1_map[] = {
+ {"OSSE_SMT1_D3_STS", BIT(0), 0},
+ {"GBE_D3_STS", BIT(19), 0},
+ {"ITSS_D3_STS", BIT(23), 0},
+ {"CNVI_D3_STS", BIT(27), 0},
+ {"UFSX2_D3_STS", BIT(28), 0},
+ {"ESE_D3_STS", BIT(29), 0},
+ {}
+};
+
+static const struct pmc_bit_map nvl_pcdh_d3_status_2_map[] = {
+ {"CSMERTC_D3_STS", BIT(1), 0},
+ {"CSE_D3_STS", BIT(4), 0},
+ {"KVMCC_D3_STS", BIT(5), 0},
+ {"USBR0_D3_STS", BIT(6), 0},
+ {"ISH_D3_STS", BIT(7), 0},
+ {"SMT1_D3_STS", BIT(8), 0},
+ {"SMT2_D3_STS", BIT(9), 0},
+ {"SMT3_D3_STS", BIT(10), 0},
+ {"OSSE_SMT2_D3_STS", BIT(11), 0},
+ {"CLINK_D3_STS", BIT(14), 0},
+ {"PTIO_D3_STS", BIT(16), 0},
+ {"PMT_D3_STS", BIT(17), 0},
+ {"SMS1_D3_STS", BIT(18), 0},
+ {"SMS2_D3_STS", BIT(19), 0},
+ {}
+};
+
+static const struct pmc_bit_map nvl_pcdh_d3_status_3_map[] = {
+ {"THC0_D3_STS", BIT(14), 1},
+ {"THC1_D3_STS", BIT(15), 1},
+ {"OSSE_SMT3_D3_STS", BIT(16), 0},
+ {"ACE_D3_STS", BIT(23), 0},
+ {}
+};
+
+static const struct pmc_bit_map nvl_pcdh_vnn_req_status_0_map[] = {
+ {"LPSS_VNN_REQ_STS", BIT(3), 1},
+ {"OSSE_VNN_REQ_STS", BIT(6), 1},
+ {"ESPISPI_VNN_REQ_STS", BIT(18), 1},
+ {}
+};
+
+static const struct pmc_bit_map nvl_pcdh_vnn_req_status_1_map[] = {
+ {"OSSE_SMT1_VNN_REQ_STS", BIT(0), 1},
+ {"NPK_VNN_REQ_STS", BIT(4), 1},
+ {"DFXAGG_VNN_REQ_STS", BIT(8), 0},
+ {"EXI_VNN_REQ_STS", BIT(9), 1},
+ {"P2D_VNN_REQ_STS", BIT(18), 1},
+ {"GBE_VNN_REQ_STS", BIT(19), 1},
+ {"SMB_VNN_REQ_STS", BIT(25), 1},
+ {"LPC_VNN_REQ_STS", BIT(26), 0},
+ {"ESE_VNN_REQ_STS", BIT(29), 1},
+ {}
+};
+
+static const struct pmc_bit_map nvl_pcdh_vnn_req_status_2_map[] = {
+ {"CSMERTC_VNN_REQ_STS", BIT(1), 1},
+ {"CSE_VNN_REQ_STS", BIT(4), 1},
+ {"ISH_VNN_REQ_STS", BIT(7), 1},
+ {"SMT1_VNN_REQ_STS", BIT(8), 1},
+ {"CLINK_VNN_REQ_STS", BIT(14), 1},
+ {"SMS1_VNN_REQ_STS", BIT(18), 1},
+ {"SMS2_VNN_REQ_STS", BIT(19), 1},
+ {"GPIOCOM4_VNN_REQ_STS", BIT(20), 1},
+ {"GPIOCOM3_VNN_REQ_STS", BIT(21), 1},
+ {"DISP_SHIM_VNN_REQ_STS", BIT(22), 1},
+ {"GPIOCOM1_VNN_REQ_STS", BIT(23), 1},
+ {"GPIOCOM0_VNN_REQ_STS", BIT(24), 1},
+ {}
+};
+
+const struct pmc_bit_map nvl_pcdh_vnn_req_status_3_map[] = {
+ {"DTS0_VNN_REQ_STS", BIT(7), 0},
+ {"GPIOCOM5_VNN_REQ_STS", BIT(11), 1},
+ {}
+};
+
+static const struct pmc_bit_map nvl_pcdh_vnn_misc_status_map[] = {
+ {"CPU_C10_REQ_STS", BIT(0), 0},
+ {"TS_OFF_REQ_STS", BIT(1), 0},
+ {"PNDE_MET_REQ_STS", BIT(2), 1},
+ {"PG5_PMA0_REQ_STS", BIT(3), 1},
+ {"FW_THROTTLE_ALLOWED_REQ_STS", BIT(4), 0},
+ {"VNN_SOC_REQ_STS", BIT(6), 1},
+ {"ISH_VNNAON_REQ_STS", BIT(7), 0},
+ {"D2D_NOC_CFI_QACTIVE_REQ_STS", BIT(8), 1},
+ {"D2D_NOC_GPSB_QACTIVE_REQ_STS", BIT(9), 1},
+ {"D2D_IPU_QACTIVE_REQ_STS", BIT(10), 1},
+ {"PLT_GREATER_REQ_STS", BIT(11), 1},
+ {"ALL_SBR_IDLE_REQ_STS", BIT(12), 0},
+ {"PMC_IDLE_FB_OCP_REQ_STS", BIT(13), 0},
+ {"PM_SYNC_STATES_REQ_STS", BIT(14), 0},
+ {"EA_REQ_STS", BIT(15), 0},
+ {"MPHY_CORE_OFF_REQ_STS", BIT(16), 0},
+ {"BRK_EV_EN_REQ_STS", BIT(17), 0},
+ {"AUTO_DEMO_EN_REQ_STS", BIT(18), 0},
+ {"ITSS_CLK_SRC_REQ_STS", BIT(19), 1},
+ {"ARC_IDLE_REQ_STS", BIT(21), 0},
+ {"PG5_PMA1_REQ_STS", BIT(22), 1},
+ {"FIA_DEEP_PM_REQ_STS", BIT(23), 0},
+ {"XDCI_ATTACHED_REQ_STS", BIT(24), 1},
+ {"ARC_INTERRUPT_WAKE_REQ_STS", BIT(25), 0},
+ {"D2D_DISP_DDI_QACTIVE_REQ_STS", BIT(26), 1},
+ {"PRE_WAKE0_REQ_STS", BIT(27), 1},
+ {"PRE_WAKE1_REQ_STS", BIT(28), 1},
+ {"PRE_WAKE2_REQ_STS", BIT(29), 1},
+ {"PG5_PMA2_GVNN", BIT(30), 1},
+ {"D2D_DISP_EDP_QACTIVE_REQ_STS", BIT(31), 1},
+ {}
+};
+
+static const struct pmc_bit_map nvl_pcdh_rsc_status_map[] = {
+ {"CORE", 0, 1},
+ {"Memory", 0, 1},
+ {"PRIM_D2D", 0, 1},
+ {"PSF0", 0, 1},
+ {"PSF4", 0, 1},
+ {"PSF6", 0, 1},
+ {"PSF8", 0, 1},
+ {"SB", 0, 1},
+ {}
+};
+
+static const struct pmc_bit_map *nvl_pcdh_lpm_maps[] = {
+ nvl_pcdh_clocksource_status_map,
+ nvl_pcdh_power_gating_status_0_map,
+ nvl_pcdh_power_gating_status_1_map,
+ nvl_pcdh_power_gating_status_2_map,
+ nvl_pcdh_power_gating_status_3_map,
+ nvl_pcdh_d3_status_0_map,
+ nvl_pcdh_d3_status_1_map,
+ nvl_pcdh_d3_status_2_map,
+ nvl_pcdh_d3_status_3_map,
+ nvl_pcdh_vnn_req_status_0_map,
+ nvl_pcdh_vnn_req_status_1_map,
+ nvl_pcdh_vnn_req_status_2_map,
+ nvl_pcdh_vnn_req_status_3_map,
+ nvl_pcdh_vnn_misc_status_map,
+ ptl_pcdp_signal_status_map,
+ NULL
+};
+
+static const struct pmc_bit_map *nvl_pcdh_blk_maps[] = {
+ nvl_pcdh_power_gating_status_0_map,
+ nvl_pcdh_power_gating_status_1_map,
+ nvl_pcdh_power_gating_status_2_map,
+ nvl_pcdh_power_gating_status_3_map,
+ nvl_pcdh_rsc_status_map,
+ nvl_pcdh_vnn_req_status_0_map,
+ nvl_pcdh_vnn_req_status_1_map,
+ nvl_pcdh_vnn_req_status_2_map,
+ nvl_pcdh_vnn_req_status_3_map,
+ nvl_pcdh_d3_status_0_map,
+ nvl_pcdh_d3_status_1_map,
+ nvl_pcdh_d3_status_2_map,
+ nvl_pcdh_d3_status_3_map,
+ nvl_pcdh_clocksource_status_map,
+ nvl_pcdh_vnn_misc_status_map,
+ ptl_pcdp_signal_status_map,
+ NULL
+};
+
+static const struct pmc_bit_map nvl_pcds_pfear_map[] = {
+ {"PMC_PGD0", BIT(0)},
+ {"FUSE_OSSE_PGD0", BIT(1)},
+ {"SPI_PGD0", BIT(2)},
+ {"XHCI_PGD0", BIT(3)},
+ {"SPA_PGD0", BIT(4)},
+ {"SPB_PGD0", BIT(5)},
+ {"RSVD6", BIT(6)},
+ {"GBE_PGD0", BIT(7)},
+
+ {"RSVD8", BIT(0)},
+ {"RSVD9", BIT(1)},
+ {"SBR16B7_PGD0", BIT(2)},
+ {"SBR16B21_PGD0", BIT(3)},
+ {"RSVD12", BIT(4)},
+ {"D2D_DISP_PGD1", BIT(5)},
+ {"LPSS_PGD0", BIT(6)},
+ {"LPC_PGD0", BIT(7)},
+
+ {"SMB_PGD0", BIT(0)},
+ {"ISH_PGD0", BIT(1)},
+ {"SBR16B1_PGD0", BIT(2)},
+ {"NPK_PGD0", BIT(3)},
+ {"D2D_NOC_PGD1", BIT(4)},
+ {"DBG_SBR16B_PGD0", BIT(5)},
+ {"FUSE_PGD0", BIT(6)},
+ {"RSVD23", BIT(7)},
+
+ {"P2SB0_PGD0", BIT(0)},
+ {"OTG_PGD0", BIT(1)},
+ {"EXI_PGD0", BIT(2)},
+ {"CSE_PGD0", BIT(3)},
+ {"CSME_KVM_PGD0", BIT(4)},
+ {"CSME_PMT_PGD0", BIT(5)},
+ {"CSME_CLINK_PGD0", BIT(6)},
+ {"CSME_PTIO_PGD0", BIT(7)},
+
+ {"CSME_USBR_PGD0", BIT(0)},
+ {"SBR16B22_PGD0", BIT(1)},
+ {"CSME_SMT1_PGD0", BIT(2)},
+ {"P2SB1_PGD0", BIT(3)},
+ {"CSME_SMS2_PGD0", BIT(4)},
+ {"CSME_SMS_PGD0", BIT(5)},
+ {"CSME_RTC_PGD0", BIT(6)},
+ {"CSMEPSF_PGD0", BIT(7)},
+
+ {"D2D_NOC_PGD0", BIT(0)},
+ {"RSVD41", BIT(1)},
+ {"RSVD42", BIT(2)},
+ {"RSVD43", BIT(3)},
+ {"SBR16B2_PGD0", BIT(4)},
+ {"OSSE_SMT1_PGD0", BIT(5)},
+ {"D2D_DISP_PGD0", BIT(6)},
+ {"RSVD47_PGD0", BIT(7)},
+
+ {"RSVD48", BIT(0)},
+ {"DBG_PSF_PGD0", BIT(1)},
+ {"RSVD50", BIT(2)},
+ {"CNVI_PGD0", BIT(3)},
+ {"UFSX2_PGD0", BIT(4)},
+ {"ENDBG_PGD0", BIT(5)},
+ {"DBC_PGD0", BIT(6)},
+ {"SBR16B4_PGD0", BIT(7)},
+
+ {"RSVD56", BIT(0)},
+ {"NPK_PGD1", BIT(1)},
+ {"RSVD58", BIT(2)},
+ {"SBR16B20_PGD0", BIT(3)},
+ {"RSVD60", BIT(4)},
+ {"SBR8B20_PGD0", BIT(5)},
+ {"RSVD62", BIT(6)},
+ {"FIA_U_PGD0", BIT(7)},
+
+ {"PSF8_PGD0", BIT(0)},
+ {"RSVD65", BIT(1)},
+ {"RSVD66", BIT(2)},
+ {"FIACPCB_U_PGD0", BIT(3)},
+ {"TAM_PGD0", BIT(4)},
+ {"D2D_NOC_PGD2", BIT(5)},
+ {"SBR8B2_PGD0", BIT(6)},
+ {"THC0_PGD0", BIT(7)},
+
+ {"THC1_PGD0", BIT(0)},
+ {"PMC_PGD1", BIT(1)},
+ {"SBR16B3_PGD0", BIT(2)},
+ {"TCSS_PGD0", BIT(3)},
+ {"DISP_PGA_PGD0", BIT(4)},
+ {"RSVD77", BIT(5)},
+ {"RSVD78", BIT(6)},
+ {"RSVD79", BIT(7)},
+
+ {"SBRG_PGD0", BIT(0)},
+ {"RSVD81", BIT(1)},
+ {"SBR16B0_PGD0", BIT(2)},
+ {"SBR8B0_PGD0", BIT(3)},
+ {"PSF7_PGD0", BIT(4)},
+ {"RSVD85", BIT(5)},
+ {"RSVD86", BIT(6)},
+ {"RSVD87", BIT(7)},
+
+ {"SBR16B6_PGD0", BIT(0)},
+ {"PSD0_PGD0", BIT(1)},
+ {"STRC_PGD0", BIT(2)},
+ {"RSVD91", BIT(3)},
+ {"DBG_SBR_PGD0", BIT(4)},
+ {"RSVD93", BIT(5)},
+ {"OSSE_PGD0", BIT(6)},
+ {"DISP_PGA1_PGD0", BIT(7)},
+ {}
+};
+
+static const struct pmc_bit_map *ext_nvl_pcds_pfear_map[] = {
+ nvl_pcds_pfear_map,
+ NULL
+};
+
+static const struct pmc_bit_map nvl_pcds_ltr_show_map[] = {
+ {"SOUTHPORT_A", CNP_PMC_LTR_SPA},
+ {"SOUTHPORT_B", CNP_PMC_LTR_SPB},
+ {"SATA", CNP_PMC_LTR_SATA},
+ {"GIGABIT_ETHERNET", CNP_PMC_LTR_GBE},
+ {"XHCI", CNP_PMC_LTR_XHCI},
+ {"SOUTHPORT_F", ADL_PMC_LTR_SPF},
+ {"ME", CNP_PMC_LTR_ME},
+ {"SATA1", CNP_PMC_LTR_EVA},
+ {"SOUTHPORT_C", CNP_PMC_LTR_SPC},
+ {"HD_AUDIO", CNP_PMC_LTR_AZ},
+ {"CNV", CNP_PMC_LTR_CNV},
+ {"LPSS", CNP_PMC_LTR_LPSS},
+ {"SOUTHPORT_D", CNP_PMC_LTR_SPD},
+ {"SOUTHPORT_E", CNP_PMC_LTR_SPE},
+ {"SATA2", PTL_PMC_LTR_SATA2},
+ {"ESPI", CNP_PMC_LTR_ESPI},
+ {"SCC", CNP_PMC_LTR_SCC},
+ {"ISH", CNP_PMC_LTR_ISH},
+ {"UFSX2", CNP_PMC_LTR_UFSX2},
+ {"EMMC", CNP_PMC_LTR_EMMC},
+ {"WIGIG", ICL_PMC_LTR_WIGIG},
+ {"THC0", TGL_PMC_LTR_THC0},
+ {"THC1", TGL_PMC_LTR_THC1},
+ {"SOUTHPORT_G", MTL_PMC_LTR_SPG},
+ {"RSVD", NVL_PCDS_PMC_LTR_RESERVED},
+ {"IOE_PMC", MTL_PMC_LTR_IOE_PMC},
+ {"DMI3", ARL_PMC_LTR_DMI3},
+ {"OSSE", LNL_PMC_LTR_OSSE},
+
+ /* Below two cannot be used for LTR_IGNORE */
+ {"CURRENT_PLATFORM", PTL_PMC_LTR_CUR_PLT},
+ {"AGGREGATED_SYSTEM", PTL_PMC_LTR_CUR_ASLT},
+ {}
+};
+
+static const struct pmc_bit_map nvl_pcds_clocksource_status_map[] = {
+ {"AON2_OFF_STS", BIT(0), 1},
+ {"AON3_OFF_STS", BIT(1), 0},
+ {"AON4_OFF_STS", BIT(2), 1},
+ {"AON5_OFF_STS", BIT(3), 1},
+ {"AON1_OFF_STS", BIT(4), 0},
+ {"XTAL_LVM_OFF_STS", BIT(5), 0},
+ {"D2D_OFF_STS", BIT(8), 1},
+ {"AON3_SPL_OFF_STS", BIT(9), 1},
+ {"XTAL_AGGR_OFF_STS", BIT(17), 1},
+ {"BCLK_EXT_INJ_OFF_STS", BIT(18), 1},
+ {"DDI2_PLL_OFF_STS", BIT(19), 1},
+ {"SE_TCSS_PLL_OFF_STS", BIT(20), 1},
+ {"DDI_PLL_OFF_STS", BIT(21), 1},
+ {"FILTER_PLL_OFF_STS", BIT(22), 1},
+ {"PHY_OC_EXT_INJ_OFF_STS", BIT(23), 1},
+ {"ACE_PLL_OFF_STS", BIT(24), 0},
+ {"FABRIC_PLL_OFF_STS", BIT(25), 1},
+ {"SOC_PLL_OFF_STS", BIT(26), 1},
+ {"REF_PLL_OFF_STS", BIT(28), 1},
+ {"GENLOCK_FILTER_PLL_OFF_STS", BIT(30), 1},
+ {"RTC_PLL_OFF_STS", BIT(31), 0},
+ {}
+};
+
+static const struct pmc_bit_map nvl_pcds_power_gating_status_0_map[] = {
+ {"PMC_PGD0_PG_STS", BIT(0), 0},
+ {"FUSE_OSSE_PGD0_PG_STS", BIT(1), 0},
+ {"ESPISPI_PGD0_PG_STS", BIT(2), 0},
+ {"XHCI_PGD0_PG_STS", BIT(3), 0},
+ {"SPA_PGD0_PG_STS", BIT(4), 0},
+ {"SPB_PGD0_PG_STS", BIT(5), 0},
+ {"RSVD_6", BIT(6), 0},
+ {"GBE_PGD0_PG_STS", BIT(7), 0},
+ {"RSVD_8", BIT(8), 0},
+ {"RSVD_9", BIT(9), 0},
+ {"SBR16B7_PGD0_PG_STS", BIT(10), 0},
+ {"SBR16B21_PGD0_PG_STS", BIT(11), 0},
+ {"RSVD_12", BIT(12), 0},
+ {"D2D_DISP_PGD1_PG_STS", BIT(13), 1},
+ {"LPSS_PGD0_PG_STS", BIT(14), 0},
+ {"LPC_PGD0_PG_STS", BIT(15), 0},
+ {"SMB_PGD0_PG_STS", BIT(16), 0},
+ {"ISH_PGD0_PG_STS", BIT(17), 0},
+ {"SBR16B1_PGD0_PG_STS", BIT(18), 0},
+ {"NPK_PGD0_PG_STS", BIT(19), 0},
+ {"D2D_NOC_PGD1_PG_STS", BIT(20), 1},
+ {"DBG_SBR16B_PGD0_PG_STS", BIT(21), 0},
+ {"FUSE_PGD0_PG_STS", BIT(22), 0},
+ {"RSVD_23", BIT(23), 0},
+ {"P2SB0_PGD0_PG_STS", BIT(24), 1},
+ {"XDCI_PGD0_PG_STS", BIT(25), 0},
+ {"EXI_PGD0_PG_STS", BIT(26), 0},
+ {"CSE_PGD0_PG_STS", BIT(27), 1},
+ {"KVMCC_PGD0_PG_STS", BIT(28), 0},
+ {"PMT_PGD0_PG_STS", BIT(29), 0},
+ {"CLINK_PGD0_PG_STS", BIT(30), 0},
+ {"PTIO_PGD0_PG_STS", BIT(31), 0},
+ {}
+};
+
+static const struct pmc_bit_map nvl_pcds_power_gating_status_1_map[] = {
+ {"USBR0_PGD0_PG_STS", BIT(0), 0},
+ {"SBR16B22_PGD0_PG_STS", BIT(1), 0},
+ {"SMT1_PGD0_PG_STS", BIT(2), 0},
+ {"P2SB1_PGD0_PG_STS", BIT(3), 1},
+ {"SMS2_PGD0_PG_STS", BIT(4), 0},
+ {"SMS1_PGD0_PG_STS", BIT(5), 0},
+ {"CSMERTC_PGD0_PG_STS", BIT(6), 0},
+ {"CSMEPSF_PGD0_PG_STS", BIT(7), 0},
+ {"D2D_NOC_PGD0_PG_STS", BIT(8), 0},
+ {"RSVD_9", BIT(9), 0},
+ {"RSVD_10", BIT(10), 0},
+ {"RSVD_11", BIT(11), 0},
+ {"SBR16B2_PGD0_PG_STS", BIT(12), 0},
+ {"OSSE_SMT1_PGD0_PG_STS", BIT(13), 1},
+ {"D2D_DISP_PGD0_PG_STS", BIT(14), 1},
+ {"RSVD_15", BIT(15), 0},
+ {"RSVD_16", BIT(16), 0},
+ {"DBG_PSF_PGD0_PG_STS", BIT(17), 0},
+ {"RSVD_18", BIT(18), 0},
+ {"CNVI_PGD0_PG_STS", BIT(19), 0},
+ {"UFSX2_PGD0_PG_STS", BIT(20), 0},
+ {"ENDBG_PGD0_PG_STS", BIT(21), 0},
+ {"DBC_PGD0_PG_STS", BIT(22), 0},
+ {"SBR16B4_PGD0_PG_STS", BIT(23), 0},
+ {"RSVD_24", BIT(24), 0},
+ {"NPK_PGD1_PG_STS", BIT(25), 0},
+ {"RSVD_26", BIT(26), 0},
+ {"SBR16B20_PGD0_PG_STS", BIT(27), 0},
+ {"RSVD_28", BIT(28), 0},
+ {"SBR8B20_PGD0_PG_STS", BIT(29), 0},
+ {"RSVD_30", BIT(30), 0},
+ {"FIA_U_PGD0_PG_STS", BIT(31), 0},
+ {}
+};
+
+static const struct pmc_bit_map nvl_pcds_power_gating_status_2_map[] = {
+ {"PSF8_PGD0_PG_STS", BIT(0), 0},
+ {"RSVD_1", BIT(1), 0},
+ {"RSVD_2", BIT(2), 0},
+ {"FIACPCB_U_PGD0_PG_STS", BIT(3), 0},
+ {"TAM_PGD0_PG_STS", BIT(4), 1},
+ {"D2D_NOC_PGD2_PG_STS", BIT(5), 1},
+ {"SBR8B2_PGD0_PG_STS", BIT(6), 0},
+ {"THC0_PGD0_PG_STS", BIT(7), 0},
+ {"THC1_PGD0_PG_STS", BIT(8), 0},
+ {"PMC_PGD1_PG_STS", BIT(9), 0},
+ {"SBR16B3_PGD0_PG_STS", BIT(10), 0},
+ {"TCSS_PGD0_PG_STS", BIT(11), 0},
+ {"DISP_PGA_PGD0_PG_STS", BIT(12), 0},
+ {"RSVD_13", BIT(13), 0},
+ {"RSVD_14", BIT(14), 0},
+ {"RSVD_15", BIT(15), 0},
+ {"SBRG_PGD0_PG_STS", BIT(16), 0},
+ {"RSVD_17", BIT(17), 0},
+ {"SBR16B0_PGD0_PG_STS", BIT(18), 0},
+ {"SBR8B0_PGD0_PG_STS", BIT(19), 0},
+ {"PSF7_PGD0_PG_STS", BIT(20), 0},
+ {"RSVD_21", BIT(21), 0},
+ {"RSVD_22", BIT(22), 0},
+ {"RSVD_23", BIT(23), 0},
+ {"SBR16B6_PGD0_PG_STS", BIT(24), 0},
+ {"PSF0_PGD0_PG_STS", BIT(25), 0},
+ {"STRC_PGD0_PG_STS", BIT(26), 0},
+ {"RSVD_27", BIT(27), 0},
+ {"DBG_SBR_PGD0_PG_STS", BIT(28), 0},
+ {"RSVD_29", BIT(29), 0},
+ {"OSSE_PGD0_PG_STS", BIT(30), 1},
+ {"DISP_PGA1_PGD0_PG_STS", BIT(31), 0},
+ {}
+};
+
+static const struct pmc_bit_map nvl_pcds_d3_status_0_map[] = {
+ {"LPSS_D3_STS", BIT(3), 1},
+ {"XDCI_D3_STS", BIT(4), 1},
+ {"XHCI_D3_STS", BIT(5), 1},
+ {"SPA_D3_STS", BIT(12), 0},
+ {"SPB_D3_STS", BIT(13), 0},
+ {"ESPISPI_D3_STS", BIT(18), 0},
+ {"PSTH_D3_STS", BIT(21), 0},
+ {}
+};
+
+static const struct pmc_bit_map nvl_pcds_d3_status_1_map[] = {
+ {"OSSE_D3_STS", BIT(14), 0},
+ {"GBE_D3_STS", BIT(19), 0},
+ {"ITSS_D3_STS", BIT(23), 0},
+ {"CNVI_D3_STS", BIT(27), 0},
+ {"UFSX2_D3_STS", BIT(28), 0},
+ {}
+};
+
+static const struct pmc_bit_map nvl_pcds_d3_status_2_map[] = {
+ {"CSMERTC_D3_STS", BIT(1), 0},
+ {"CSE_D3_STS", BIT(4), 0},
+ {"KVMCC_D3_STS", BIT(5), 0},
+ {"USBR0_D3_STS", BIT(6), 0},
+ {"ISH_D3_STS", BIT(7), 0},
+ {"SMT1_D3_STS", BIT(8), 0},
+ {"SMT2_D3_STS", BIT(9), 0},
+ {"SMT3_D3_STS", BIT(10), 0},
+ {"OSSE_SMT1_D3_STS", BIT(12), 0},
+ {"CLINK_D3_STS", BIT(14), 0},
+ {"PTIO_D3_STS", BIT(16), 0},
+ {"PMT_D3_STS", BIT(17), 0},
+ {"SMS1_D3_STS", BIT(18), 0},
+ {"SMS2_D3_STS", BIT(19), 0},
+ {}
+};
+
+static const struct pmc_bit_map nvl_pcds_d3_status_3_map[] = {
+ {"OSSE_SMT2_D3_STS", BIT(0), 0},
+ {"THC0_D3_STS", BIT(14), 1},
+ {"THC1_D3_STS", BIT(15), 1},
+ {"OSSE_SMT3_D3_STS", BIT(19), 0},
+ {}
+};
+
+static const struct pmc_bit_map nvl_pcds_vnn_req_status_0_map[] = {
+ {"LPSS_VNN_REQ_STS", BIT(3), 0},
+ {"ESPISPI_VNN_REQ_STS", BIT(18), 1},
+ {}
+};
+
+static const struct pmc_bit_map nvl_pcds_vnn_req_status_1_map[] = {
+ {"NPK_VNN_REQ_STS", BIT(4), 1},
+ {"DFXAGG_VNN_REQ_STS", BIT(8), 0},
+ {"EXI_VNN_REQ_STS", BIT(9), 1},
+ {"OSSE_VNN_REQ_STS", BIT(14), 1},
+ {"P2D_VNN_REQ_STS", BIT(18), 1},
+ {"GBE_VNN_REQ_STS", BIT(19), 0},
+ {"SMB_VNN_REQ_STS", BIT(25), 1},
+ {"LPC_VNN_REQ_STS", BIT(26), 0},
+ {}
+};
+
+static const struct pmc_bit_map nvl_pcds_vnn_req_status_2_map[] = {
+ {"CSMERTC_VNN_REQ_STS", BIT(1), 0},
+ {"CSE_VNN_REQ_STS", BIT(4), 1},
+ {"ISH_VNN_REQ_STS", BIT(7), 0},
+ {"SMT1_VNN_REQ_STS", BIT(8), 0},
+ {"OSSE_SMT1_VNN_REQ_STS", BIT(12), 1},
+ {"CLINK_VNN_REQ_STS", BIT(14), 0},
+ {"SMS1_VNN_REQ_STS", BIT(18), 0},
+ {"SMS2_VNN_REQ_STS", BIT(19), 0},
+ {"GPIOCOM4_VNN_REQ_STS", BIT(20), 0},
+ {"GPIOCOM3_VNN_REQ_STS", BIT(21), 1},
+ {"GPIOCOM1_VNN_REQ_STS", BIT(23), 1},
+ {"GPIOCOM0_VNN_REQ_STS", BIT(24), 1},
+ {}
+};
+
+static const struct pmc_bit_map nvl_pcds_vnn_req_status_3_map[] = {
+ {"DISP_SHIM_VNN_REQ_STS", BIT(4), 1},
+ {"DTS0_VNN_REQ_STS", BIT(7), 0},
+ {"GPIOCOM5_VNN_REQ_STS", BIT(11), 0},
+ {}
+};
+
+static const struct pmc_bit_map nvl_pcds_vnn_misc_status_map[] = {
+ {"CPU_C10_REQ_STS", BIT(0), 0},
+ {"TS_OFF_REQ_STS", BIT(1), 0},
+ {"PNDE_MET_REQ_STS", BIT(2), 1},
+ {"PG5_PMA0_REQ_STS", BIT(3), 1},
+ {"FW_THROTTLE_ALLOWED_REQ_STS", BIT(4), 0},
+ {"VNN_SOC_REQ_STS", BIT(6), 1},
+ {"ISH_VNNAON_REQ_STS", BIT(7), 0},
+ {"D2D_NOC_CFI_QACTIVE_REQ_STS", BIT(8), 1},
+ {"D2D_NOC_GPSB_QACTIVE_REQ_STS", BIT(9), 1},
+ {"PLT_GREATER_REQ_STS", BIT(11), 1},
+ {"ALL_SBR_IDLE_REQ_STS", BIT(12), 0},
+ {"PMC_IDLE_FB_OCP_REQ_STS", BIT(13), 0},
+ {"PM_SYNC_STATES_REQ_STS", BIT(14), 0},
+ {"EA_REQ_STS", BIT(15), 0},
+ {"MPHY_CORE_OFF_REQ_STS", BIT(16), 0},
+ {"BRK_EV_EN_REQ_STS", BIT(17), 0},
+ {"AUTO_DEMO_EN_REQ_STS", BIT(18), 0},
+ {"ITSS_CLK_SRC_REQ_STS", BIT(19), 1},
+ {"ARC_IDLE_REQ_STS", BIT(21), 0},
+ {"PG5_PMA1_REQ_STS", BIT(22), 1},
+ {"DG5_PMA0_REQ_STS", BIT(23), 1},
+ {"ARC_INTERRUPT_WAKE_REQ_STS", BIT(25), 0},
+ {"D2D_DISP_DDI_QACTIVE_REQ_STS", BIT(26), 1},
+ {"PRE_WAKE0_REQ_STS", BIT(27), 1},
+ {"PRE_WAKE1_REQ_STS", BIT(28), 1},
+ {"PRE_WAKE2_REQ_STS", BIT(29), 1},
+ {"D2D_DISP_EDP_QACTIVE_REQ_STS", BIT(31), 1},
+ {}
+};
+
+static const struct pmc_bit_map nvl_pcds_rsc_status_map[] = {
+ {"CORE", 0, 1},
+ {"Memory", 0, 1},
+ {"PRIM_D2D", 0, 1},
+ {"PSF0", 0, 1},
+ {"SB", 0, 1},
+ {}
+};
+
+static const struct pmc_bit_map nvl_pcds_signal_status_map[] = {
+ {"LSX_Wake0_STS", BIT(0), 0},
+ {"LSX_Wake1_STS", BIT(1), 0},
+ {"LSX_Wake2_STS", BIT(2), 0},
+ {"LSX_Wake3_STS", BIT(3), 0},
+ {"LSX_Wake4_STS", BIT(4), 0},
+ {"LSX_Wake5_STS", BIT(5), 0},
+ {"LSX_Wake6_STS", BIT(6), 0},
+ {"LSX_Wake7_STS", BIT(7), 0},
+ {"LPSS_Wake0_STS", BIT(8), 1},
+ {"LPSS_Wake1_STS", BIT(9), 1},
+ {"Int_Timer_SS_Wake0_STS", BIT(10), 1},
+ {"Int_Timer_SS_Wake1_STS", BIT(11), 1},
+ {"Int_Timer_SS_Wake2_STS", BIT(12), 1},
+ {"Int_Timer_SS_Wake3_STS", BIT(13), 1},
+ {"Int_Timer_SS_Wake4_STS", BIT(14), 1},
+ {"Int_Timer_SS_Wake5_STS", BIT(15), 1},
+ {}
+};
+
+static const struct pmc_bit_map *nvl_pcds_lpm_maps[] = {
+ nvl_pcds_clocksource_status_map,
+ nvl_pcds_power_gating_status_0_map,
+ nvl_pcds_power_gating_status_1_map,
+ nvl_pcds_power_gating_status_2_map,
+ nvl_pcds_d3_status_0_map,
+ nvl_pcds_d3_status_1_map,
+ nvl_pcds_d3_status_2_map,
+ nvl_pcds_d3_status_3_map,
+ nvl_pcds_vnn_req_status_0_map,
+ nvl_pcds_vnn_req_status_1_map,
+ nvl_pcds_vnn_req_status_2_map,
+ nvl_pcds_vnn_req_status_3_map,
+ nvl_pcds_vnn_misc_status_map,
+ nvl_pcds_signal_status_map,
+ NULL
+};
+
+static const struct pmc_bit_map *nvl_pcds_blk_maps[] = {
+ nvl_pcds_power_gating_status_0_map,
+ nvl_pcds_power_gating_status_1_map,
+ nvl_pcds_power_gating_status_2_map,
+ nvl_pcds_rsc_status_map,
+ nvl_pcds_vnn_req_status_0_map,
+ nvl_pcds_vnn_req_status_1_map,
+ nvl_pcds_vnn_req_status_2_map,
+ nvl_pcds_vnn_req_status_3_map,
+ nvl_pcds_d3_status_0_map,
+ nvl_pcds_d3_status_1_map,
+ nvl_pcds_d3_status_2_map,
+ nvl_pcds_d3_status_3_map,
+ nvl_pcds_clocksource_status_map,
+ nvl_pcds_vnn_misc_status_map,
+ nvl_pcds_signal_status_map,
+ NULL
+};
+
+static const struct pmc_bit_map nvl_pchs_pfear_map[] = {
+ {"PMC_PGD0", BIT(0)},
+ {"FIA_D_PGD0", BIT(1)},
+ {"SPI_PGD0", BIT(2)},
+ {"XHCI_PGD0", BIT(3)},
+ {"SPA_PGD0", BIT(4)},
+ {"SPB_PGD0", BIT(5)},
+ {"MPFPW2_PGD0", BIT(6)},
+ {"GBE_PGD0", BIT(7)},
+
+ {"RSVD8", BIT(0)},
+ {"PSF3_PGD0", BIT(1)},
+ {"SBR5_PGD0", BIT(2)},
+ {"SBR0_PGD0", BIT(3)},
+ {"RSVD12", BIT(4)},
+ {"D2D_DISP_PGD1", BIT(5)},
+ {"LPSS_PGD0", BIT(6)},
+ {"LPC_PGD0", BIT(7)},
+
+ {"SMB_PGD0", BIT(0)},
+ {"ISH_PGD0", BIT(1)},
+ {"P2SB_PGD0", BIT(2)},
+ {"NPK_PGD0", BIT(3)},
+ {"D2D_NOC_PGD1", BIT(4)},
+ {"EAH_PGD0", BIT(5)},
+ {"FUSE_PGD0", BIT(6)},
+ {"SBR8_PGD0", BIT(7)},
+
+ {"PSF7_PGD0", BIT(0)},
+ {"OTG_PGD0", BIT(1)},
+ {"EXI_PGD0", BIT(2)},
+ {"CSE_PGD0", BIT(3)},
+ {"CSME_KVM_PGD0", BIT(4)},
+ {"CSME_PMT_PGD0", BIT(5)},
+ {"CSME_CLINK_PGD0", BIT(6)},
+ {"CSME_PTIO_PGD0", BIT(7)},
+
+ {"CSME_USBR_PGD0", BIT(0)},
+ {"SBR1_PGD0", BIT(1)},
+ {"CSME_SMT1_PGD0", BIT(2)},
+ {"MPFPW1_PGD0", BIT(3)},
+ {"CSME_SMS2_PGD0", BIT(4)},
+ {"CSME_SMS_PGD0", BIT(5)},
+ {"CSME_RTC_PGD0", BIT(6)},
+ {"CSMEPSF_PGD0", BIT(7)},
+
+ {"D2D_NOC_PGD0", BIT(0)},
+ {"ESE_PGD0", BIT(1)},
+ {"SBR2_PGD0", BIT(2)},
+ {"SBR3_PGD0", BIT(3)},
+ {"SBR4_PGD0", BIT(4)},
+ {"RSVD45", BIT(5)},
+ {"D2D_DISP_PGD0", BIT(6)},
+ {"PSF1_PGD0", BIT(7)},
+
+ {"U3FPW1_PGD0", BIT(0)},
+ {"DMI3FPW_PGD0", BIT(1)},
+ {"PSF4_PGD0", BIT(2)},
+ {"CNVI_PGD0", BIT(3)},
+ {"RSVD52", BIT(4)},
+ {"ENDBG_PGD0", BIT(5)},
+ {"DBC_PGD0", BIT(6)},
+ {"SMT4_PGD0", BIT(7)},
+
+ {"RSVD56", BIT(0)},
+ {"NPK_PGD1", BIT(1)},
+ {"RSVD58", BIT(2)},
+ {"DMI3_PGD0", BIT(3)},
+ {"RSVD60", BIT(4)},
+ {"FIACPCB_D_PGD0", BIT(5)},
+ {"RSVD62", BIT(6)},
+ {"FIA_U_PGD0", BIT(7)},
+
+ {"FIACPCB_PGS_PGD0", BIT(0)},
+ {"FIA_PGS_PGD0", BIT(1)},
+ {"RSVD66", BIT(2)},
+ {"FIACPCB_U_PGD0", BIT(3)},
+ {"TAM_PGD0", BIT(4)},
+ {"D2D_NOC_PGD2", BIT(5)},
+ {"PSF2_PGD0", BIT(6)},
+ {"THC0_PGD0", BIT(7)},
+
+ {"THC1_PGD0", BIT(0)},
+ {"PMC_PGD1", BIT(1)},
+ {"SBR9_PGD0", BIT(2)},
+ {"U3FPW2_PGD0", BIT(3)},
+ {"RSVD76", BIT(4)},
+ {"DBG_PSF_PGD0", BIT(5)},
+ {"DBG_SBR_PGD0", BIT(6)},
+ {"SBR6_PGD0", BIT(7)},
+
+ {"SPC_PGD0", BIT(0)},
+ {"ACE_PGD0", BIT(1)},
+ {"ACE_PGD1", BIT(2)},
+ {"ACE_PGD2", BIT(3)},
+ {"ACE_PGD3", BIT(4)},
+ {"ACE_PGD4", BIT(5)},
+ {"ACE_PGD5", BIT(6)},
+ {"ACE_PGD6", BIT(7)},
+
+ {"ACE_PGD7", BIT(0)},
+ {"ACE_PGD8", BIT(1)},
+ {"ACE_PGD9", BIT(2)},
+ {"ACE_PGD10", BIT(3)},
+ {"U3FPW3_PGD0", BIT(4)},
+ {"SBR7_PGD0", BIT(5)},
+ {"OSSE_PGD0", BIT(6)},
+ {"ST_PGD0", BIT(7)},
+ {}
+};
+
+static const struct pmc_bit_map *ext_nvl_pchs_pfear_map[] = {
+ nvl_pchs_pfear_map,
+ NULL
+};
+
+static const struct pmc_bit_map nvl_pchs_clocksource_status_map[] = {
+ {"AON2_OFF_STS", BIT(0), 1},
+ {"AON3_OFF_STS", BIT(1), 0},
+ {"AON4_OFF_STS", BIT(2), 0},
+ {"AON2_SPL_OFF_STS", BIT(3), 0},
+ {"AONL_OFF_STS", BIT(4), 0},
+ {"XTAL_LVM_OFF_STS", BIT(5), 0},
+ {"AON5_OFF_STS", BIT(6), 0},
+ {"USB3_PLL_OFF_STS", BIT(8), 1},
+ {"MAIN_CRO_OFF_STS", BIT(11), 0},
+ {"MAIN_DIVIDER_OFF_STS", BIT(12), 1},
+ {"REF_PLL_NON_OC_OFF_STS", BIT(13), 1},
+ {"DMI_PLL_OFF_STS", BIT(14), 1},
+ {"PHY_EXT_INJ_OFF_STS", BIT(15), 1},
+ {"AON6_MCRO_OFF_STS", BIT(16), 0},
+ {"XTAL_AGGR_OFF_STS", BIT(17), 0},
+ {"USB2_PLL_OFF_STS", BIT(18), 1},
+ {"GBE_PLL_OFF_STS", BIT(21), 1},
+ {"SATA_PLL_OFF_STS", BIT(22), 1},
+ {"PCIE0_PLL_OFF_STS", BIT(23), 1},
+ {"PCIE1_PLL_OFF_STS", BIT(24), 1},
+ {"FABRIC_PLL_OFF_STS", BIT(25), 1},
+ {"PCIE2_PLL_OFF_STS", BIT(26), 1},
+ {"REF_PLL_OFF_STS", BIT(28), 1},
+ {"REF38P4_PLL_OFF_STS", BIT(31), 1},
+ {}
+};
+
+static const struct pmc_bit_map nvl_pchs_power_gating_status_0_map[] = {
+ {"PMC_PGD0_PG_STS", BIT(0), 0},
+ {"FIA_D_PGD0_PG_STS", BIT(1), 0},
+ {"ESPISPI_PGD0_PG_STS", BIT(2), 0},
+ {"XHCI_PGD0_PG_STS", BIT(3), 0},
+ {"SPA_PGD0_PG_STS", BIT(4), 1},
+ {"SPB_PGD0_PG_STS", BIT(5), 1},
+ {"MPFPW2_PGD0_PG_STS", BIT(6), 0},
+ {"GBE_PGD0_PG_STS", BIT(7), 1},
+ {"RSVD_8", BIT(8), 0},
+ {"PSF3_PGD0_PG_STS", BIT(9), 0},
+ {"SBR5_PGD0_PG_STS", BIT(10), 0},
+ {"SBR0_PGD0_PG_STS", BIT(11), 0},
+ {"RSVD_12", BIT(12), 0},
+ {"D2D_DISP_PGD1_PG_STS", BIT(13), 0},
+ {"LPSS_PGD0_PG_STS", BIT(14), 1},
+ {"LPC_PGD0_PG_STS", BIT(15), 0},
+ {"SMB_PGD0_PG_STS", BIT(16), 0},
+ {"ISH_PGD0_PG_STS", BIT(17), 0},
+ {"P2S_PGD0_PG_STS", BIT(18), 0},
+ {"NPK_PGD0_PG_STS", BIT(19), 0},
+ {"D2D_NOC_PGD1_PG_STS", BIT(20), 0},
+ {"EAH_PGD0_PG_STS", BIT(21), 0},
+ {"FUSE_PGD0_PG_STS", BIT(22), 0},
+ {"SBR8_PGD0_PG_STS", BIT(23), 0},
+ {"PSF7_PGD0_PG_STS", BIT(24), 0},
+ {"XDCI_PGD0_PG_STS", BIT(25), 1},
+ {"EXI_PGD0_PG_STS", BIT(26), 0},
+ {"CSE_PGD0_PG_STS", BIT(27), 1},
+ {"KVMCC_PGD0_PG_STS", BIT(28), 1},
+ {"PMT_PGD0_PG_STS", BIT(29), 1},
+ {"CLINK_PGD0_PG_STS", BIT(30), 1},
+ {"PTIO_PGD0_PG_STS", BIT(31), 1},
+ {}
+};
+
+static const struct pmc_bit_map nvl_pchs_power_gating_status_1_map[] = {
+ {"USBR0_PGD0_PG_STS", BIT(0), 1},
+ {"SBR1_PGD0_PG_STS", BIT(1), 0},
+ {"SMT1_PGD0_PG_STS", BIT(2), 1},
+ {"MPFPW1_PGD0_PG_STS", BIT(3), 0},
+ {"SMS2_PGD0_PG_STS", BIT(4), 1},
+ {"SMS1_PGD0_PG_STS", BIT(5), 1},
+ {"CSMERTC_PGD0_PG_STS", BIT(6), 0},
+ {"CSMEPSF_PGD0_PG_STS", BIT(7), 0},
+ {"D2D_NOC_PGD0_PG_STS", BIT(8), 0},
+ {"ESE_PGD0_PG_STS", BIT(9), 1},
+ {"SBR2_PGD0_PG_STS", BIT(10), 0},
+ {"SBR3_PGD0_PG_STS", BIT(11), 0},
+ {"SBR4_PGD0_PG_STS", BIT(12), 0},
+ {"RSVD_13", BIT(13), 0},
+ {"D2D_DISP_PGD0_PG_STS", BIT(14), 0},
+ {"PSF1_PGD0_PG_STS", BIT(15), 0},
+ {"U3FPW1_PGD0_PG_STS", BIT(16), 0},
+ {"DMI3FPW_PGD0_PG_STS", BIT(17), 0},
+ {"PSF4_PGD0_PG_STS", BIT(18), 0},
+ {"CNVI_PGD0_PG_STS", BIT(19), 0},
+ {"RSVD_20", BIT(20), 0},
+ {"ENDBG_PGD0_PG_STS", BIT(21), 0},
+ {"DBC_PGD0_PG_STS", BIT(22), 0},
+ {"SMT4_PGD0_PG_STS", BIT(23), 1},
+ {"RSVD_24", BIT(24), 0},
+ {"NPK_PGD1_PG_STS", BIT(25), 0},
+ {"RSVD_26", BIT(26), 0},
+ {"DMI3_PGD0_PG_STS", BIT(27), 1},
+ {"RSVD_28", BIT(28), 0},
+ {"FIACPCB_D_PGD0_PG_STS", BIT(29), 0},
+ {"RSVD_30", BIT(30), 0},
+ {"FIA_U_PGD0_PG_STS", BIT(31), 0},
+ {}
+};
+
+static const struct pmc_bit_map nvl_pchs_power_gating_status_2_map[] = {
+ {"FIACPCB_PGS_PGD0_PG_STS", BIT(0), 0},
+ {"FIA_PGS_PGD0_PG_STS", BIT(1), 0},
+ {"RSVD_2", BIT(2), 0},
+ {"FIACPCB_U_PGD0_PG_STS", BIT(3), 0},
+ {"TAM_PGD0_PG_STS", BIT(4), 0},
+ {"D2D_NOC_PGD2_PG_STS", BIT(5), 0},
+ {"PSF2_PGD0_PG_STS", BIT(6), 0},
+ {"THC0_PGD0_PG_STS", BIT(7), 1},
+ {"THC1_PGD0_PG_STS", BIT(8), 1},
+ {"PMC_PGD1_PG_STS", BIT(9), 0},
+ {"SBR9_PGA0_PGD0_PG_STS", BIT(10), 0},
+ {"U3FPW2_PGD0_PG_STS", BIT(11), 0},
+ {"RSVD_12", BIT(12), 0},
+ {"DBG_PSF_PGD0_PG_STS", BIT(13), 0},
+ {"DBG_SBR_PGD0_PG_STS", BIT(14), 0},
+ {"SBR6_PGD0_PG_STS", BIT(15), 0},
+ {"SPC_PGD0_PG_STS", BIT(16), 1},
+ {"ACE_PGD0_PG_STS", BIT(17), 0},
+ {"ACE_PGD1_PG_STS", BIT(18), 0},
+ {"ACE_PGD2_PG_STS", BIT(19), 0},
+ {"ACE_PGD3_PG_STS", BIT(20), 0},
+ {"ACE_PGD4_PG_STS", BIT(21), 0},
+ {"ACE_PGD5_PG_STS", BIT(22), 0},
+ {"ACE_PGD6_PG_STS", BIT(23), 0},
+ {"ACE_PGD7_PG_STS", BIT(24), 0},
+ {"ACE_PGD8_PG_STS", BIT(25), 0},
+ {"ACE_PGD9_PG_STS", BIT(26), 0},
+ {"ACE_PGD10_PG_STS", BIT(27), 0},
+ {"U3FPW3_PGD0_PG_STS", BIT(28), 0},
+ {"SBR7_PGD0_PG_STS", BIT(29), 0},
+ {"OSSE_PGD0_PG_STS", BIT(30), 0},
+ {"SATA_PGD0_PG_STS", BIT(31), 1},
+ {}
+};
+
+static const struct pmc_bit_map nvl_pchs_d3_status_0_map[] = {
+ {"LPSS_D3_STS", BIT(3), 1},
+ {"XDCI_D3_STS", BIT(4), 1},
+ {"XHCI_D3_STS", BIT(5), 0},
+ {"SPA_D3_STS", BIT(12), 0},
+ {"SPB_D3_STS", BIT(13), 0},
+ {"SPC_D3_STS", BIT(14), 0},
+ {"ESPISPI_D3_STS", BIT(18), 0},
+ {"SATA_D3_STS", BIT(20), 1},
+ {}
+};
+
+static const struct pmc_bit_map nvl_pchs_d3_status_1_map[] = {
+ {"OSSE_D3_STS", BIT(6), 0},
+ {"GBE_D3_STS", BIT(19), 0},
+ {"ITSS_D3_STS", BIT(23), 0},
+ {"P2S_D3_STS", BIT(24), 0},
+ {"CNVI_D3_STS", BIT(27), 0},
+ {}
+};
+
+static const struct pmc_bit_map nvl_pchs_d3_status_2_map[] = {
+ {"CSMERTC_D3_STS", BIT(1), 0},
+ {"CSE_D3_STS", BIT(4), 0},
+ {"KVMCC_D3_STS", BIT(5), 0},
+ {"USBR0_D3_STS", BIT(6), 0},
+ {"ISH_D3_STS", BIT(7), 0},
+ {"SMT1_D3_STS", BIT(8), 0},
+ {"SMT2_D3_STS", BIT(9), 0},
+ {"SMT3_D3_STS", BIT(10), 0},
+ {"SMT4_D3_STS", BIT(11), 0},
+ {"SMT5_D3_STS", BIT(12), 0},
+ {"SMT6_D3_STS", BIT(13), 0},
+ {"CLINK_D3_STS", BIT(14), 0},
+ {"PTIO_D3_STS", BIT(16), 0},
+ {"PMT_D3_STS", BIT(17), 0},
+ {"SMS1_D3_STS", BIT(18), 0},
+ {"SMS2_D3_STS", BIT(19), 0},
+ {}
+};
+
+static const struct pmc_bit_map nvl_pchs_d3_status_3_map[] = {
+ {"THC0_D3_STS", BIT(14), 0},
+ {"THC1_D3_STS", BIT(15), 0},
+ {"ACE_D3_STS", BIT(23), 0},
+ {}
+};
+
+static const struct pmc_bit_map nvl_pchs_vnn_req_status_1_map[] = {
+ {"NPK_VNN_REQ_STS", BIT(4), 0},
+ {"OSSE_VNN_REQ_STS", BIT(6), 0},
+ {"DFXAGG_VNN_REQ_STS", BIT(8), 0},
+ {"EXI_VNN_REQ_STS", BIT(9), 0},
+ {"GBE_VNN_REQ_STS", BIT(19), 0},
+ {"SMB_VNN_REQ_STS", BIT(25), 0},
+ {"LPC_VNN_REQ_STS", BIT(26), 0},
+ {}
+};
+
+static const struct pmc_bit_map nvl_pchs_vnn_req_status_2_map[] = {
+ {"CSMERTC_VNN_REQ_STS", BIT(1), 0},
+ {"CSE_VNN_REQ_STS", BIT(4), 0},
+ {"ISH_VNN_REQ_STS", BIT(7), 0},
+ {"SMT1_VNN_REQ_STS", BIT(8), 0},
+ {"SMT4_VNN_REQ_STS", BIT(11), 0},
+ {"CLINK_VNN_REQ_STS", BIT(14), 0},
+ {"SMS1_VNN_REQ_STS", BIT(18), 0},
+ {"SMS2_VNN_REQ_STS", BIT(19), 0},
+ {"GPIOCOM4_VNN_REQ_STS", BIT(20), 0},
+ {"GPIOCOM3_VNN_REQ_STS", BIT(21), 0},
+ {"GPIOCOM2_VNN_REQ_STS", BIT(22), 0},
+ {"GPIOCOM1_VNN_REQ_STS", BIT(23), 0},
+ {"GPIOCOM0_VNN_REQ_STS", BIT(24), 0},
+ {}
+};
+
+static const struct pmc_bit_map nvl_pchs_vnn_misc_status_map[] = {
+ {"CPU_C10_REQ_STS", BIT(0), 0},
+ {"TS_OFF_REQ_STS", BIT(1), 0},
+ {"PNDE_MET_REQ_STS", BIT(2), 1},
+ {"PG5_PMA0_GVNN_REQ_STS", BIT(3), 1},
+ {"FW_THROTTLE_ALLOWED_REQ_STS", BIT(4), 0},
+ {"DMI_IN_L1_REQ_STS", BIT(6), 0},
+ {"ISH_VNNAON_REQ_STS", BIT(7), 0},
+ {"PLT_GREATER_REQ_STS", BIT(11), 1},
+ {"ALL_SBR_IDLE_REQ_STS", BIT(12), 0},
+ {"PMC_IDLE_FB_OCP_REQ_STS", BIT(13), 0},
+ {"PM_SYNC_STATES_REQ_STS", BIT(14), 0},
+ {"EA_REQ_STS", BIT(15), 0},
+ {"DMI_CLKREQ_B_REQ_STS", BIT(16), 0},
+ {"BRK_EV_EN_REQ_STS", BIT(17), 0},
+ {"AUTO_DEMO_EN_REQ_STS", BIT(18), 0},
+ {"ITSS_CLK_SRC_REQ_STS", BIT(19), 1},
+ {"ARC_IDLE_REQ_STS", BIT(21), 0},
+ {"PG5_PMA1_GVNN_REQ_STS", BIT(22), 1},
+ {"FIA_DEEP_PM_REQ_STS", BIT(23), 0},
+ {"XDCI_ATTACHED_REQ_STS", BIT(24), 0},
+ {"ARC_INTERRUPT_WAKE_REQ_STS", BIT(25), 0},
+ {"PRE_WAKE0_REQ_STS", BIT(27), 1},
+ {"PRE_WAKE1_REQ_STS", BIT(28), 1},
+ {"PRE_WAKE2_EN_REQ_STS", BIT(29), 0},
+ {"PG5_PMA2_GVNN_REQ_STS", BIT(30), 1},
+ {}
+};
+
+static const struct pmc_bit_map nvl_pchs_rsc_status_map[] = {
+ {"Memory", 0, 1},
+ {"Memory_NS", 0, 1},
+ {"PSF1", 0, 1},
+ {"PSF2", 0, 1},
+ {"PSF3", 0, 1},
+ {"REF_PLL", 0, 1},
+ {"SB", 0, 1},
+ {}
+};
+
+static const struct pmc_bit_map *nvl_pchs_lpm_maps[] = {
+ nvl_pchs_clocksource_status_map,
+ nvl_pchs_power_gating_status_0_map,
+ nvl_pchs_power_gating_status_1_map,
+ nvl_pchs_power_gating_status_2_map,
+ nvl_pchs_d3_status_0_map,
+ nvl_pchs_d3_status_1_map,
+ nvl_pchs_d3_status_2_map,
+ nvl_pchs_d3_status_3_map,
+ nvl_pcds_vnn_req_status_0_map,
+ nvl_pchs_vnn_req_status_1_map,
+ nvl_pchs_vnn_req_status_2_map,
+ nvl_pcdh_vnn_req_status_3_map,
+ nvl_pchs_vnn_misc_status_map,
+ ptl_pcdp_signal_status_map,
+ NULL
+};
+
+static const struct pmc_bit_map *nvl_pchs_blk_maps[] = {
+ nvl_pchs_power_gating_status_0_map,
+ nvl_pchs_power_gating_status_1_map,
+ nvl_pchs_power_gating_status_2_map,
+ nvl_pchs_rsc_status_map,
+ nvl_pchs_d3_status_0_map,
+ nvl_pchs_clocksource_status_map,
+ nvl_pchs_vnn_misc_status_map,
+ NULL
+};
+
+static const struct pmc_reg_map nvl_pcdh_reg_map = {
+ .pfear_sts = ext_nvl_pcdh_pfear_map,
+ .slp_s0_offset = CNP_PMC_SLP_S0_RES_COUNTER_OFFSET,
+ .slp_s0_res_counter_step = TGL_PMC_SLP_S0_RES_COUNTER_STEP,
+ .ltr_show_sts = ptl_pcdp_ltr_show_map,
+ .msr_sts = msr_map,
+ .ltr_ignore_offset = CNP_PMC_LTR_IGNORE_OFFSET,
+ .regmap_length = NVL_PCDH_PMC_MMIO_REG_LEN,
+ .ppfear0_offset = CNP_PMC_HOST_PPFEAR0A,
+ .ppfear_buckets = NVL_PCDH_PPFEAR_NUM_ENTRIES,
+ .pm_cfg_offset = CNP_PMC_PM_CFG_OFFSET,
+ .pm_read_disable_bit = CNP_PMC_READ_DISABLE_BIT,
+ .lpm_num_maps = NVL_LPM_NUM_MAPS,
+ .ltr_ignore_max = LNL_NUM_IP_IGN_ALLOWED,
+ .lpm_res_counter_step_x2 = TGL_PMC_LPM_RES_COUNTER_STEP_X2,
+ .etr3_offset = ETR3_OFFSET,
+ .lpm_sts_latch_en_offset = MTL_LPM_STATUS_LATCH_EN_OFFSET,
+ .lpm_priority_offset = NVL_LPM_PRI_OFFSET,
+ .lpm_en_offset = NVL_LPM_EN_OFFSET,
+ .lpm_residency_offset = NVL_LPM_RESIDENCY_OFFSET,
+ .lpm_sts = nvl_pcdh_lpm_maps,
+ .lpm_status_offset = MTL_LPM_STATUS_OFFSET,
+ .lpm_live_status_offset = NVL_LPM_LIVE_STATUS_OFFSET,
+ .s0ix_blocker_maps = nvl_pcdh_blk_maps,
+ .s0ix_blocker_offset = LNL_S0IX_BLOCKER_OFFSET,
+ .num_s0ix_blocker = NVL_PCDH_NUM_S0IX_BLOCKER,
+ .blocker_req_offset = NVL_PCDH_BLK_REQ_OFFSET,
+ .lpm_req_guid = PCDH_LPM_REQ_GUID,
+};
+
+static const struct pmc_reg_map nvl_pcds_reg_map = {
+ .pfear_sts = ext_nvl_pcds_pfear_map,
+ .slp_s0_offset = CNP_PMC_SLP_S0_RES_COUNTER_OFFSET,
+ .slp_s0_res_counter_step = TGL_PMC_SLP_S0_RES_COUNTER_STEP,
+ .ltr_show_sts = nvl_pcds_ltr_show_map,
+ .msr_sts = msr_map,
+ .ltr_ignore_offset = CNP_PMC_LTR_IGNORE_OFFSET,
+ .regmap_length = NVL_PCDS_PMC_MMIO_REG_LEN,
+ .ppfear0_offset = CNP_PMC_HOST_PPFEAR0A,
+ .ppfear_buckets = LNL_PPFEAR_NUM_ENTRIES,
+ .pm_cfg_offset = CNP_PMC_PM_CFG_OFFSET,
+ .pm_read_disable_bit = CNP_PMC_READ_DISABLE_BIT,
+ .lpm_num_maps = PTL_LPM_NUM_MAPS,
+ .ltr_ignore_max = LNL_NUM_IP_IGN_ALLOWED,
+ .lpm_res_counter_step_x2 = TGL_PMC_LPM_RES_COUNTER_STEP_X2,
+ .etr3_offset = ETR3_OFFSET,
+ .lpm_sts_latch_en_offset = MTL_LPM_STATUS_LATCH_EN_OFFSET,
+ .lpm_priority_offset = MTL_LPM_PRI_OFFSET,
+ .lpm_en_offset = MTL_LPM_EN_OFFSET,
+ .lpm_residency_offset = MTL_LPM_RESIDENCY_OFFSET,
+ .lpm_sts = nvl_pcds_lpm_maps,
+ .lpm_status_offset = MTL_LPM_STATUS_OFFSET,
+ .lpm_live_status_offset = MTL_LPM_LIVE_STATUS_OFFSET,
+ .s0ix_blocker_maps = nvl_pcds_blk_maps,
+ .s0ix_blocker_offset = LNL_S0IX_BLOCKER_OFFSET,
+ .num_s0ix_blocker = NVL_PCDS_NUM_S0IX_BLOCKER,
+ .lpm_req_guid = PCDS_LPM_REQ_GUID,
+ .blocker_req_offset = NVL_PCDS_BLK_REQ_OFFSET,
+};
+
+static const struct pmc_reg_map nvl_pchs_reg_map = {
+ .pfear_sts = ext_nvl_pchs_pfear_map,
+ .slp_s0_offset = CNP_PMC_SLP_S0_RES_COUNTER_OFFSET,
+ .slp_s0_res_counter_step = TGL_PMC_SLP_S0_RES_COUNTER_STEP,
+ .ltr_show_sts = ptl_pcdp_ltr_show_map,
+ .msr_sts = msr_map,
+ .ltr_ignore_offset = CNP_PMC_LTR_IGNORE_OFFSET,
+ .regmap_length = NVL_PCHS_PMC_MMIO_REG_LEN,
+ .ppfear0_offset = CNP_PMC_HOST_PPFEAR0A,
+ .ppfear_buckets = LNL_PPFEAR_NUM_ENTRIES,
+ .pm_cfg_offset = CNP_PMC_PM_CFG_OFFSET,
+ .pm_read_disable_bit = CNP_PMC_READ_DISABLE_BIT,
+ .lpm_num_maps = PTL_LPM_NUM_MAPS,
+ .ltr_ignore_max = LNL_NUM_IP_IGN_ALLOWED,
+ .lpm_res_counter_step_x2 = TGL_PMC_LPM_RES_COUNTER_STEP_X2,
+ .etr3_offset = ETR3_OFFSET,
+ .lpm_sts_latch_en_offset = MTL_LPM_STATUS_LATCH_EN_OFFSET,
+ .lpm_priority_offset = MTL_LPM_PRI_OFFSET,
+ .lpm_en_offset = MTL_LPM_EN_OFFSET,
+ .lpm_residency_offset = MTL_LPM_RESIDENCY_OFFSET,
+ .lpm_sts = nvl_pchs_lpm_maps,
+ .lpm_status_offset = MTL_LPM_STATUS_OFFSET,
+ .lpm_live_status_offset = MTL_LPM_LIVE_STATUS_OFFSET,
+ .s0ix_blocker_maps = nvl_pchs_blk_maps,
+ .s0ix_blocker_offset = LNL_S0IX_BLOCKER_OFFSET,
+ .num_s0ix_blocker = NVL_PCHS_NUM_S0IX_BLOCKER,
+ .blocker_req_offset = NVL_PCHS_BLK_REQ_OFFSET,
+ .lpm_req_guid = PCHS_LPM_REQ_GUID,
+};
+
+static struct pmc_info nvl_pmc_info_list[] = {
+ {
+ .devid = PMC_DEVID_NVL_PCDH,
+ .map = &nvl_pcdh_reg_map,
+ },
+ {
+ .devid = PMC_DEVID_NVL_PCDS,
+ .map = &nvl_pcds_reg_map,
+ },
+ {
+ .devid = PMC_DEVID_NVL_PCHS,
+ .map = &nvl_pchs_reg_map,
+ },
+ {}
+};
+
+const char *nvl_ltr_block_counter_arr[] = {
+ "PKGC_PREVENT_LTR_IADOMAIN",
+ "PKGC_PREVENT_LTR_GDIE",
+ "PKGC_PREVENT_LTR_PCH",
+ "PKGC_PREVENT_LTR_DISPLAY",
+ "PKGC_PREVENT_LTR_IPU",
+ NULL
+};
+
+const char *nvl_pkgc_blocker_residency[] = {
+ "PKGC_BLOCK_RESIDENCY_INVALID",
+ "PKGC_BLOCK_RESIDENCY_MISC",
+ "PKGC_BLOCK_RESIDENCY_CDIE_MISC",
+ "PKGC_BLOCK_RESIDENCY_MEDIA_MISC",
+ "PKGC_BLOCK_RESIDENCY_GT_MISC",
+ "PKGC_BLOCK_RESIDENCY_HUBATOM_MISC",
+ "PKGC_BLOCK_RESIDENCY_IPU_BUSY",
+ "PKGC_BLOCK_RESIDENCY_IPU_LTR",
+ "PKGC_BLOCK_RESIDENCY_IPU_TIMER",
+ "PKGC_BLOCK_RESIDENCY_DISP_BUSY",
+ "PKGC_BLOCK_RESIDENCY_DISP_LTR",
+ "PKGC_BLOCK_RESIDENCY_DISP_TIMER",
+ "PKGC_BLOCK_RESIDENCY_VPU_BUSY",
+ "PKGC_BLOCK_RESIDENCY_VPU_TIMER",
+ "PKGC_BLOCK_RESIDENCY_PMC_BUSY",
+ "PKGC_BLOCK_RESIDENCY_PMC_LTR",
+ "PKGC_BLOCK_RESIDENCY_PMC_TIMER",
+ "PKGC_BLOCK_RESIDENCY_HUBATOM_ARAT",
+ "PKGC_BLOCK_RESIDENCY_CDIE0_ARAT",
+ "PKGC_BLOCK_RESIDENCY_CDIE1_ARAT",
+ "PKGC_BLOCK_RESIDENCY_GT_ARAT",
+ "PKGC_BLOCK_RESIDENCY_MEDIA_ARAT",
+ "PKGC_BLOCK_RESIDENCY_DEMOTION",
+ "PKGC_BLOCK_RESIDENCY_THERMALS",
+ "PKGC_BLOCK_RESIDENCY_SNCU",
+ "PKGC_BLOCK_RESIDENCY_SVTU",
+ "PKGC_BLOCK_RESIDENCY_IAA",
+ "PKGC_BLOCK_RESIDENCY_IOC",
+ NULL,
+};
+
+static u8 nvl_pmc_list[] = {PMC_IDX_MAIN, PMC_IDX_PCH};
+static u8 nvl_h_pmc_list[] = {PMC_IDX_MAIN, PMC_IDX_PCH};
+
+#define NVL_NPU_PCI_DEV 0xd71d
+
+/*
+ * Set power state of select devices that do not have drivers to D3
+ * so that they do not block Package C entry.
+ */
+static void nvl_d3_fixup(void)
+{
+ pmc_core_set_device_d3(NVL_NPU_PCI_DEV);
+}
+
+static int nvl_resume(struct pmc_dev *pmcdev)
+{
+ nvl_d3_fixup();
+ return cnl_resume(pmcdev);
+}
+
+static int nvl_core_init(struct pmc_dev *pmcdev, struct pmc_dev_info *pmc_dev_info)
+{
+ nvl_d3_fixup();
+ return generic_core_init(pmcdev, pmc_dev_info);
+}
+
+static u32 nvl_pmt_dmu_guids[] = {NVL_PMT_DMU_GUID, 0x0};
+struct pmc_dev_info nvl_s_pmc_dev = {
+ .num_pmcs = ARRAY_SIZE(nvl_pmc_list),
+ .pmc_list = nvl_pmc_list,
+ .regmap_list = nvl_pmc_info_list,
+ .map = &nvl_pcds_reg_map,
+ .sub_req_show = &pmc_core_substate_blk_req_fops,
+ .suspend = cnl_suspend,
+ .resume = nvl_resume,
+ .init = nvl_core_init,
+ .sub_req = pmc_core_pmt_get_blk_sub_req,
+ .dmu_guids = nvl_pmt_dmu_guids,
+ .pc_guid = NVL_PMT_PC_GUID,
+ .pkgc_ltr_blocker_offset = NVL_LTR_BLK_OFFSET,
+ .pkgc_ltr_blocker_counters = nvl_ltr_block_counter_arr,
+ .pkgc_blocker_offset = NVL_PKGC_BLK_OFFSET,
+ .pkgc_blocker_counters = nvl_pkgc_blocker_residency,
+ .ssram_hidden = false,
+ .die_c6_offset = NVL_PMT_DMU_DIE_C6_OFFSET,
+};
+
+struct pmc_dev_info nvl_h_pmc_dev = {
+ .num_pmcs = ARRAY_SIZE(nvl_h_pmc_list),
+ .pmc_list = nvl_h_pmc_list,
+ .regmap_list = nvl_pmc_info_list,
+ .map = &nvl_pcdh_reg_map,
+ .sub_req_show = &pmc_core_substate_blk_req_fops,
+ .suspend = cnl_suspend,
+ .resume = nvl_resume,
+ .init = nvl_core_init,
+ .sub_req = pmc_core_pmt_get_blk_sub_req,
+ .dmu_guids = nvl_pmt_dmu_guids,
+ .pc_guid = NVL_PMT_PC_GUID,
+ .pkgc_ltr_blocker_offset = NVL_LTR_BLK_OFFSET,
+ .pkgc_ltr_blocker_counters = nvl_ltr_block_counter_arr,
+ .pkgc_blocker_offset = NVL_PKGC_BLK_OFFSET,
+ .pkgc_blocker_counters = nvl_pkgc_blocker_residency,
+ .ssram_hidden = false,
+ .die_c6_offset = NVL_PMT_DMU_DIE_C6_OFFSET,
+};
diff --git a/drivers/platform/x86/intel/pmc/ptl.c b/drivers/platform/x86/intel/pmc/ptl.c
index 7aa39db256770..3e1cf6905e111 100644
--- a/drivers/platform/x86/intel/pmc/ptl.c
+++ b/drivers/platform/x86/intel/pmc/ptl.c
@@ -137,7 +137,7 @@ static const struct pmc_bit_map *ext_ptl_pcdp_pfear_map[] = {
NULL
};
-static const struct pmc_bit_map ptl_pcdp_ltr_show_map[] = {
+const struct pmc_bit_map ptl_pcdp_ltr_show_map[] = {
{"SOUTHPORT_A", CNP_PMC_LTR_SPA},
{"SOUTHPORT_B", CNP_PMC_LTR_SPB},
{"SATA", CNP_PMC_LTR_SATA},
--
2.43.0
^ permalink raw reply related
* [PATCH v2 6/7] platform/x86/intel/pmc: Retrieve PMC info only for available PMCs
From: Xi Pardee @ 2026-04-08 22:21 UTC (permalink / raw)
To: xi.pardee, irenic.rajneesh, david.e.box, ilpo.jarvinen,
platform-driver-x86, linux-kernel, linux-pm
In-Reply-To: <20260408222144.3288928-1-xi.pardee@linux.intel.com>
Update the Intel PMC Core driver to fetch PMC information only for
available PMCs. Previously, the driver attempted to retrieve PMC info
even when the corresponding PMC was not present.
This change aligns with recent updates to the Intel SSRAM Telemetry
driver. Starting with NVL, the SSRAM Telemetry driver is probed for
each individual SSRAM device. The prior implementation could not
differentiate between an unavailable PMC and one that had not yet
completed information retrieval. To resolve this, the PMC Core driver
now skips obtaining PMC info for unavailable PMCs.
Signed-off-by: Xi Pardee <xi.pardee@linux.intel.com>
---
drivers/platform/x86/intel/pmc/arl.c | 7 +++++++
drivers/platform/x86/intel/pmc/core.c | 19 +++++++++++--------
drivers/platform/x86/intel/pmc/core.h | 4 ++++
drivers/platform/x86/intel/pmc/lnl.c | 4 ++++
drivers/platform/x86/intel/pmc/mtl.c | 4 ++++
drivers/platform/x86/intel/pmc/ptl.c | 4 ++++
drivers/platform/x86/intel/pmc/wcl.c | 4 ++++
7 files changed, 38 insertions(+), 8 deletions(-)
diff --git a/drivers/platform/x86/intel/pmc/arl.c b/drivers/platform/x86/intel/pmc/arl.c
index 4d91ee010f6d0..cd15559864317 100644
--- a/drivers/platform/x86/intel/pmc/arl.c
+++ b/drivers/platform/x86/intel/pmc/arl.c
@@ -672,6 +672,9 @@ static struct pmc_info arl_pmc_info_list[] = {
{}
};
+static u8 arl_pmc_list[] = {PMC_IDX_MAIN, PMC_IDX_IOE, PMC_IDX_PCH};
+static u8 arl_h_pmc_list[] = {PMC_IDX_MAIN, PMC_IDX_IOE};
+
#define ARL_NPU_PCI_DEV 0xad1d
#define ARL_GNA_PCI_DEV 0xae4c
#define ARL_H_NPU_PCI_DEV 0x7d1d
@@ -721,6 +724,8 @@ static int arl_h_core_init(struct pmc_dev *pmcdev, struct pmc_dev_info *pmc_dev_
static u32 ARL_PMT_DMU_GUIDS[] = {ARL_PMT_DMU_GUID, 0x0};
struct pmc_dev_info arl_pmc_dev = {
.dmu_guids = ARL_PMT_DMU_GUIDS,
+ .num_pmcs = ARRAY_SIZE(arl_pmc_list),
+ .pmc_list = arl_pmc_list,
.regmap_list = arl_pmc_info_list,
.map = &arl_socs_reg_map,
.sub_req_show = &pmc_core_substate_req_regs_fops,
@@ -735,6 +740,8 @@ struct pmc_dev_info arl_pmc_dev = {
static u32 ARL_H_PMT_DMU_GUIDS[] = {ARL_PMT_DMU_GUID, ARL_H_PMT_DMU_GUID, 0x0};
struct pmc_dev_info arl_h_pmc_dev = {
.dmu_guids = ARL_H_PMT_DMU_GUIDS,
+ .num_pmcs = ARRAY_SIZE(arl_h_pmc_list),
+ .pmc_list = arl_h_pmc_list,
.regmap_list = arl_pmc_info_list,
.map = &mtl_socm_reg_map,
.sub_req_show = &pmc_core_substate_req_regs_fops,
diff --git a/drivers/platform/x86/intel/pmc/core.c b/drivers/platform/x86/intel/pmc/core.c
index 5d2e2681b0eba..c84e75b19aac3 100644
--- a/drivers/platform/x86/intel/pmc/core.c
+++ b/drivers/platform/x86/intel/pmc/core.c
@@ -1734,16 +1734,17 @@ static int pmc_core_pmc_add(struct pmc_dev *pmcdev, unsigned int pmc_idx)
return 0;
}
-static int pmc_core_ssram_get_reg_base(struct pmc_dev *pmcdev)
+static int pmc_core_ssram_get_reg_base(struct pmc_dev *pmcdev, u8 num_pmcs, u8 *pmc_list)
{
+ unsigned int i;
int ret;
- ret = pmc_core_pmc_add(pmcdev, PMC_IDX_MAIN);
- if (ret)
- return ret;
-
- pmc_core_pmc_add(pmcdev, PMC_IDX_IOE);
- pmc_core_pmc_add(pmcdev, PMC_IDX_PCH);
+ for (i = 0; i < num_pmcs; ++i) {
+ /* Non-MAIN PMCs are allowed to fail */
+ ret = pmc_core_pmc_add(pmcdev, pmc_list[i]);
+ if (ret && (pmc_list[i] == PMC_IDX_MAIN))
+ return ret;
+ }
return 0;
}
@@ -1765,7 +1766,9 @@ int generic_core_init(struct pmc_dev *pmcdev, struct pmc_dev_info *pmc_dev_info)
ssram = pmc_dev_info->regmap_list != NULL;
if (ssram) {
pmcdev->regmap_list = pmc_dev_info->regmap_list;
- ret = pmc_core_ssram_get_reg_base(pmcdev);
+ ret = pmc_core_ssram_get_reg_base(pmcdev,
+ pmc_dev_info->num_pmcs,
+ pmc_dev_info->pmc_list);
/*
* EAGAIN error code indicates Intel PMC SSRAM Telemetry driver
* has not finished probe and PMC info is not available yet. Try
diff --git a/drivers/platform/x86/intel/pmc/core.h b/drivers/platform/x86/intel/pmc/core.h
index ef69de160ffbc..a741e4698f195 100644
--- a/drivers/platform/x86/intel/pmc/core.h
+++ b/drivers/platform/x86/intel/pmc/core.h
@@ -501,6 +501,8 @@ enum pmc_index {
* @pc_guid: GUID for telemetry region to read PKGC blocker info
* @pkgc_ltr_blocker_offset: Offset to PKGC LTR blockers in telemetry region
* @pkgc_blocker_offset:Offset to PKGC blocker in telemetry region
+ * @num_pmcs: Number of entries in @pmc_list
+ * @pmc_list: Index list of available PMC
* @regmap_list: Pointer to a list of pmc_info structure that could be
* available for the platform. When set, this field implies
* SSRAM support.
@@ -521,6 +523,8 @@ struct pmc_dev_info {
u32 pc_guid;
u32 pkgc_ltr_blocker_offset;
u32 pkgc_blocker_offset;
+ u8 num_pmcs;
+ u8 *pmc_list;
struct pmc_info *regmap_list;
const struct pmc_reg_map *map;
const struct file_operations *sub_req_show;
diff --git a/drivers/platform/x86/intel/pmc/lnl.c b/drivers/platform/x86/intel/pmc/lnl.c
index 18f303af328e3..02cbcfbb8e2a6 100644
--- a/drivers/platform/x86/intel/pmc/lnl.c
+++ b/drivers/platform/x86/intel/pmc/lnl.c
@@ -544,6 +544,8 @@ static struct pmc_info lnl_pmc_info_list[] = {
{}
};
+static u8 lnl_pmc_list[] = {PMC_IDX_MAIN};
+
#define LNL_NPU_PCI_DEV 0x643e
#define LNL_IPU_PCI_DEV 0x645d
@@ -571,6 +573,8 @@ static int lnl_core_init(struct pmc_dev *pmcdev, struct pmc_dev_info *pmc_dev_in
}
struct pmc_dev_info lnl_pmc_dev = {
+ .num_pmcs = ARRAY_SIZE(lnl_pmc_list),
+ .pmc_list = lnl_pmc_list,
.regmap_list = lnl_pmc_info_list,
.map = &lnl_socm_reg_map,
.sub_req_show = &pmc_core_substate_req_regs_fops,
diff --git a/drivers/platform/x86/intel/pmc/mtl.c b/drivers/platform/x86/intel/pmc/mtl.c
index b724dd8c34dba..e4ac09ce07a71 100644
--- a/drivers/platform/x86/intel/pmc/mtl.c
+++ b/drivers/platform/x86/intel/pmc/mtl.c
@@ -965,6 +965,8 @@ static struct pmc_info mtl_pmc_info_list[] = {
{}
};
+static u8 mtl_pmc_list[] = {PMC_IDX_MAIN, PMC_IDX_IOE};
+
#define MTL_GNA_PCI_DEV 0x7e4c
#define MTL_IPU_PCI_DEV 0x7d19
#define MTL_VPU_PCI_DEV 0x7d1d
@@ -995,6 +997,8 @@ static int mtl_core_init(struct pmc_dev *pmcdev, struct pmc_dev_info *pmc_dev_in
static u32 MTL_PMT_DMU_GUIDS[] = {MTL_PMT_DMU_GUID, 0x0};
struct pmc_dev_info mtl_pmc_dev = {
.dmu_guids = MTL_PMT_DMU_GUIDS,
+ .num_pmcs = ARRAY_SIZE(mtl_pmc_list),
+ .pmc_list = mtl_pmc_list,
.regmap_list = mtl_pmc_info_list,
.map = &mtl_socm_reg_map,
.sub_req_show = &pmc_core_substate_req_regs_fops,
diff --git a/drivers/platform/x86/intel/pmc/ptl.c b/drivers/platform/x86/intel/pmc/ptl.c
index 6c68772e738c8..7aa39db256770 100644
--- a/drivers/platform/x86/intel/pmc/ptl.c
+++ b/drivers/platform/x86/intel/pmc/ptl.c
@@ -543,6 +543,8 @@ static struct pmc_info ptl_pmc_info_list[] = {
{}
};
+static u8 ptl_pmc_list[] = {PMC_IDX_MAIN};
+
#define PTL_NPU_PCI_DEV 0xb03e
#define PTL_IPU_PCI_DEV 0xb05d
@@ -569,6 +571,8 @@ static int ptl_core_init(struct pmc_dev *pmcdev, struct pmc_dev_info *pmc_dev_in
}
struct pmc_dev_info ptl_pmc_dev = {
+ .num_pmcs = ARRAY_SIZE(ptl_pmc_list),
+ .pmc_list = ptl_pmc_list,
.regmap_list = ptl_pmc_info_list,
.map = &ptl_pcdp_reg_map,
.sub_req_show = &pmc_core_substate_blk_req_fops,
diff --git a/drivers/platform/x86/intel/pmc/wcl.c b/drivers/platform/x86/intel/pmc/wcl.c
index b55069945e9e7..4cae8501872aa 100644
--- a/drivers/platform/x86/intel/pmc/wcl.c
+++ b/drivers/platform/x86/intel/pmc/wcl.c
@@ -469,6 +469,8 @@ static struct pmc_info wcl_pmc_info_list[] = {
{}
};
+static u8 wcl_pmc_list[] = {PMC_IDX_MAIN};
+
#define WCL_NPU_PCI_DEV 0xfd3e
/*
@@ -494,6 +496,8 @@ static int wcl_core_init(struct pmc_dev *pmcdev, struct pmc_dev_info *pmc_dev_in
struct pmc_dev_info wcl_pmc_dev = {
.regmap_list = wcl_pmc_info_list,
+ .num_pmcs = ARRAY_SIZE(wcl_pmc_list),
+ .pmc_list = wcl_pmc_list,
.map = &wcl_pcdn_reg_map,
.sub_req_show = &pmc_core_substate_blk_req_fops,
.suspend = cnl_suspend,
--
2.43.0
^ permalink raw reply related
* [PATCH v2 5/7] platform/x86/intel/pmc: Add support for variable DMU offsets
From: Xi Pardee @ 2026-04-08 22:21 UTC (permalink / raw)
To: xi.pardee, irenic.rajneesh, david.e.box, ilpo.jarvinen,
platform-driver-x86, linux-kernel, linux-pm
In-Reply-To: <20260408222144.3288928-1-xi.pardee@linux.intel.com>
Add support for handling different DMU Die C6 offsets across platforms.
The previous implementation assumed a uniform DMU Die C6 offset for all
platforms, which is no longer valid.
Signed-off-by: Xi Pardee <xi.pardee@linux.intel.com>
---
drivers/platform/x86/intel/pmc/arl.c | 2 ++
drivers/platform/x86/intel/pmc/core.c | 2 +-
drivers/platform/x86/intel/pmc/core.h | 2 ++
drivers/platform/x86/intel/pmc/mtl.c | 1 +
4 files changed, 6 insertions(+), 1 deletion(-)
diff --git a/drivers/platform/x86/intel/pmc/arl.c b/drivers/platform/x86/intel/pmc/arl.c
index 95372a0807acf..4d91ee010f6d0 100644
--- a/drivers/platform/x86/intel/pmc/arl.c
+++ b/drivers/platform/x86/intel/pmc/arl.c
@@ -729,6 +729,7 @@ struct pmc_dev_info arl_pmc_dev = {
.init = arl_core_init,
.sub_req = pmc_core_pmt_get_lpm_req,
.ssram_hidden = true,
+ .die_c6_offset = MTL_PMT_DMU_DIE_C6_OFFSET,
};
static u32 ARL_H_PMT_DMU_GUIDS[] = {ARL_PMT_DMU_GUID, ARL_H_PMT_DMU_GUID, 0x0};
@@ -742,4 +743,5 @@ struct pmc_dev_info arl_h_pmc_dev = {
.init = arl_h_core_init,
.sub_req = pmc_core_pmt_get_lpm_req,
.ssram_hidden = true,
+ .die_c6_offset = MTL_PMT_DMU_DIE_C6_OFFSET,
};
diff --git a/drivers/platform/x86/intel/pmc/core.c b/drivers/platform/x86/intel/pmc/core.c
index e0ac329e6723a..5d2e2681b0eba 100644
--- a/drivers/platform/x86/intel/pmc/core.c
+++ b/drivers/platform/x86/intel/pmc/core.c
@@ -1381,7 +1381,7 @@ void pmc_core_punit_pmt_init(struct pmc_dev *pmcdev, struct pmc_dev_info *pmc_de
}
pmcdev->punit_ep = ep;
- pmcdev->die_c6_offset = MTL_PMT_DMU_DIE_C6_OFFSET;
+ pmcdev->die_c6_offset = pmc_dev_info->die_c6_offset;
}
if (pmc_dev_info->pc_guid) {
diff --git a/drivers/platform/x86/intel/pmc/core.h b/drivers/platform/x86/intel/pmc/core.h
index f385a0eccd2c2..ef69de160ffbc 100644
--- a/drivers/platform/x86/intel/pmc/core.h
+++ b/drivers/platform/x86/intel/pmc/core.h
@@ -514,6 +514,7 @@ enum pmc_index {
* @init: Function to perform platform specific init action
* @sub_req: Function to achieve low power mode substate requirements
* @ssram_hidden: Some SSRAM devices are hidden on this platform
+ * @die_c6_offset: Telemetry offset to read Die C6 residency
*/
struct pmc_dev_info {
u32 *dmu_guids;
@@ -530,6 +531,7 @@ struct pmc_dev_info {
int (*init)(struct pmc_dev *pmcdev, struct pmc_dev_info *pmc_dev_info);
int (*sub_req)(struct pmc_dev *pmcdev, struct pmc *pmc, struct telem_endpoint *ep);
bool ssram_hidden;
+ u32 die_c6_offset;
};
extern const struct pmc_bit_map msr_map[];
diff --git a/drivers/platform/x86/intel/pmc/mtl.c b/drivers/platform/x86/intel/pmc/mtl.c
index 193ebbe584023..b724dd8c34dba 100644
--- a/drivers/platform/x86/intel/pmc/mtl.c
+++ b/drivers/platform/x86/intel/pmc/mtl.c
@@ -1003,4 +1003,5 @@ struct pmc_dev_info mtl_pmc_dev = {
.init = mtl_core_init,
.sub_req = pmc_core_pmt_get_lpm_req,
.ssram_hidden = true,
+ .die_c6_offset = MTL_PMT_DMU_DIE_C6_OFFSET,
};
--
2.43.0
^ permalink raw reply related
* [PATCH v2 4/7] platform/x86/intel/pmc: Use PCI DID for PMC SSRAM device discovery
From: Xi Pardee @ 2026-04-08 22:21 UTC (permalink / raw)
To: xi.pardee, irenic.rajneesh, david.e.box, ilpo.jarvinen,
platform-driver-x86, linux-kernel, linux-pm
In-Reply-To: <20260408222144.3288928-1-xi.pardee@linux.intel.com>
Update the PMC SSRAM discovery process to identify the device using its
PCI Device ID rather than relying on a fixed PCI bus location. The
enumeration of integrated devices on the PCI bus is no longer guaranteed
to be consistent across CPUs.
On earlier platforms, the IOE and PCH SSRAM devices were hidden from the
BIOS, and the SOC SSRAM device is associated to telemetry regions from all
available SSRAM devices. Starting with Nova Lake, the IOE and PCH SSRAM
devices register their telemetry regions independently, meaning each
telemetry region is now linked to its corresponding SSRAM device. A new
ssram_hidden attribute has been added to the pmc_dev_info structure to
reflect this distinction.
Signed-off-by: David E. Box <david.e.box@linux.intel.com>
Signed-off-by: Xi Pardee <xi.pardee@linux.intel.com>
---
drivers/platform/x86/intel/pmc/arl.c | 4 ++--
drivers/platform/x86/intel/pmc/core.c | 17 ++++++++++++-----
drivers/platform/x86/intel/pmc/core.h | 6 ++++--
drivers/platform/x86/intel/pmc/lnl.c | 2 +-
drivers/platform/x86/intel/pmc/mtl.c | 2 +-
drivers/platform/x86/intel/pmc/ptl.c | 2 +-
drivers/platform/x86/intel/pmc/wcl.c | 2 +-
7 files changed, 22 insertions(+), 13 deletions(-)
diff --git a/drivers/platform/x86/intel/pmc/arl.c b/drivers/platform/x86/intel/pmc/arl.c
index eb23bc68340ab..95372a0807acf 100644
--- a/drivers/platform/x86/intel/pmc/arl.c
+++ b/drivers/platform/x86/intel/pmc/arl.c
@@ -720,7 +720,6 @@ static int arl_h_core_init(struct pmc_dev *pmcdev, struct pmc_dev_info *pmc_dev_
static u32 ARL_PMT_DMU_GUIDS[] = {ARL_PMT_DMU_GUID, 0x0};
struct pmc_dev_info arl_pmc_dev = {
- .pci_func = 0,
.dmu_guids = ARL_PMT_DMU_GUIDS,
.regmap_list = arl_pmc_info_list,
.map = &arl_socs_reg_map,
@@ -729,11 +728,11 @@ struct pmc_dev_info arl_pmc_dev = {
.resume = arl_resume,
.init = arl_core_init,
.sub_req = pmc_core_pmt_get_lpm_req,
+ .ssram_hidden = true,
};
static u32 ARL_H_PMT_DMU_GUIDS[] = {ARL_PMT_DMU_GUID, ARL_H_PMT_DMU_GUID, 0x0};
struct pmc_dev_info arl_h_pmc_dev = {
- .pci_func = 2,
.dmu_guids = ARL_H_PMT_DMU_GUIDS,
.regmap_list = arl_pmc_info_list,
.map = &mtl_socm_reg_map,
@@ -742,4 +741,5 @@ struct pmc_dev_info arl_h_pmc_dev = {
.resume = arl_h_resume,
.init = arl_h_core_init,
.sub_req = pmc_core_pmt_get_lpm_req,
+ .ssram_hidden = true,
};
diff --git a/drivers/platform/x86/intel/pmc/core.c b/drivers/platform/x86/intel/pmc/core.c
index 94ae098a155a6..e0ac329e6723a 100644
--- a/drivers/platform/x86/intel/pmc/core.c
+++ b/drivers/platform/x86/intel/pmc/core.c
@@ -1646,17 +1646,13 @@ int pmc_core_pmt_get_blk_sub_req(struct pmc_dev *pmcdev, struct pmc *pmc,
static int pmc_core_get_telem_info(struct pmc_dev *pmcdev, struct pmc_dev_info *pmc_dev_info)
{
- struct pci_dev *pcidev __free(pci_dev_put) = NULL;
struct telem_endpoint *ep;
unsigned int pmc_idx;
int ret;
- pcidev = pci_get_domain_bus_and_slot(0, 0, PCI_DEVFN(20, pmc_dev_info->pci_func));
- if (!pcidev)
- return -ENODEV;
-
for (pmc_idx = 0; pmc_idx < ARRAY_SIZE(pmcdev->pmcs); ++pmc_idx) {
struct pmc *pmc;
+ u16 devid;
pmc = pmcdev->pmcs[pmc_idx];
if (!pmc)
@@ -1665,6 +1661,16 @@ static int pmc_core_get_telem_info(struct pmc_dev *pmcdev, struct pmc_dev_info *
if (!pmc->map->lpm_req_guid)
return -ENXIO;
+ if (pmc_dev_info->ssram_hidden)
+ devid = pmcdev->pmcs[PMC_IDX_MAIN]->devid;
+ else
+ devid = pmc->devid;
+
+ struct pci_dev *pcidev __free(pci_dev_put) =
+ pci_get_device(PCI_VENDOR_ID_INTEL, devid, NULL);
+ if (!pcidev)
+ return -ENODEV;
+
ep = pmt_telem_find_and_register_endpoint(&pcidev->dev, pmc->map->lpm_req_guid, 0);
if (IS_ERR(ep)) {
dev_dbg(&pmcdev->pdev->dev, "couldn't get telem endpoint %pe", ep);
@@ -1715,6 +1721,7 @@ static int pmc_core_pmc_add(struct pmc_dev *pmcdev, unsigned int pmc_idx)
pmc->map = map;
pmc->base_addr = pmc_ssram_telemetry.base_addr;
+ pmc->devid = pmc_ssram_telemetry.devid;
pmc->regbase = ioremap(pmc->base_addr, pmc->map->regmap_length);
if (!pmc->regbase) {
diff --git a/drivers/platform/x86/intel/pmc/core.h b/drivers/platform/x86/intel/pmc/core.h
index 829b1dee3f636..f385a0eccd2c2 100644
--- a/drivers/platform/x86/intel/pmc/core.h
+++ b/drivers/platform/x86/intel/pmc/core.h
@@ -425,6 +425,7 @@ struct pmc_info {
* @ltr_ign: Holds LTR ignore data while suspended
* @num_lpm_modes: Count of enabled modes
* @lpm_en_modes: Array of enabled modes from lowest to highest priority
+ * @devid: Device ID of the SSRAM device
*
* pmc contains info about one power management controller device.
*/
@@ -436,6 +437,7 @@ struct pmc {
u32 ltr_ign;
u8 num_lpm_modes;
u8 lpm_en_modes[LPM_MAX_NUM_MODES];
+ u16 devid;
};
/**
@@ -495,7 +497,6 @@ enum pmc_index {
/**
* struct pmc_dev_info - Structure to keep PMC device info
- * @pci_func: Function number of the primary PMC
* @dmu_guids: List of Die Management Unit GUID
* @pc_guid: GUID for telemetry region to read PKGC blocker info
* @pkgc_ltr_blocker_offset: Offset to PKGC LTR blockers in telemetry region
@@ -512,9 +513,9 @@ enum pmc_index {
* @resume: Function to perform platform specific resume
* @init: Function to perform platform specific init action
* @sub_req: Function to achieve low power mode substate requirements
+ * @ssram_hidden: Some SSRAM devices are hidden on this platform
*/
struct pmc_dev_info {
- u8 pci_func;
u32 *dmu_guids;
u32 pc_guid;
u32 pkgc_ltr_blocker_offset;
@@ -528,6 +529,7 @@ struct pmc_dev_info {
int (*resume)(struct pmc_dev *pmcdev);
int (*init)(struct pmc_dev *pmcdev, struct pmc_dev_info *pmc_dev_info);
int (*sub_req)(struct pmc_dev *pmcdev, struct pmc *pmc, struct telem_endpoint *ep);
+ bool ssram_hidden;
};
extern const struct pmc_bit_map msr_map[];
diff --git a/drivers/platform/x86/intel/pmc/lnl.c b/drivers/platform/x86/intel/pmc/lnl.c
index 1cd81ee54dcf8..18f303af328e3 100644
--- a/drivers/platform/x86/intel/pmc/lnl.c
+++ b/drivers/platform/x86/intel/pmc/lnl.c
@@ -571,7 +571,6 @@ static int lnl_core_init(struct pmc_dev *pmcdev, struct pmc_dev_info *pmc_dev_in
}
struct pmc_dev_info lnl_pmc_dev = {
- .pci_func = 2,
.regmap_list = lnl_pmc_info_list,
.map = &lnl_socm_reg_map,
.sub_req_show = &pmc_core_substate_req_regs_fops,
@@ -579,4 +578,5 @@ struct pmc_dev_info lnl_pmc_dev = {
.resume = lnl_resume,
.init = lnl_core_init,
.sub_req = pmc_core_pmt_get_lpm_req,
+ .ssram_hidden = true,
};
diff --git a/drivers/platform/x86/intel/pmc/mtl.c b/drivers/platform/x86/intel/pmc/mtl.c
index 57508cbf9cd42..193ebbe584023 100644
--- a/drivers/platform/x86/intel/pmc/mtl.c
+++ b/drivers/platform/x86/intel/pmc/mtl.c
@@ -994,7 +994,6 @@ static int mtl_core_init(struct pmc_dev *pmcdev, struct pmc_dev_info *pmc_dev_in
static u32 MTL_PMT_DMU_GUIDS[] = {MTL_PMT_DMU_GUID, 0x0};
struct pmc_dev_info mtl_pmc_dev = {
- .pci_func = 2,
.dmu_guids = MTL_PMT_DMU_GUIDS,
.regmap_list = mtl_pmc_info_list,
.map = &mtl_socm_reg_map,
@@ -1003,4 +1002,5 @@ struct pmc_dev_info mtl_pmc_dev = {
.resume = mtl_resume,
.init = mtl_core_init,
.sub_req = pmc_core_pmt_get_lpm_req,
+ .ssram_hidden = true,
};
diff --git a/drivers/platform/x86/intel/pmc/ptl.c b/drivers/platform/x86/intel/pmc/ptl.c
index 1f48e2bbc699f..6c68772e738c8 100644
--- a/drivers/platform/x86/intel/pmc/ptl.c
+++ b/drivers/platform/x86/intel/pmc/ptl.c
@@ -569,7 +569,6 @@ static int ptl_core_init(struct pmc_dev *pmcdev, struct pmc_dev_info *pmc_dev_in
}
struct pmc_dev_info ptl_pmc_dev = {
- .pci_func = 2,
.regmap_list = ptl_pmc_info_list,
.map = &ptl_pcdp_reg_map,
.sub_req_show = &pmc_core_substate_blk_req_fops,
@@ -577,4 +576,5 @@ struct pmc_dev_info ptl_pmc_dev = {
.resume = ptl_resume,
.init = ptl_core_init,
.sub_req = pmc_core_pmt_get_blk_sub_req,
+ .ssram_hidden = true,
};
diff --git a/drivers/platform/x86/intel/pmc/wcl.c b/drivers/platform/x86/intel/pmc/wcl.c
index a45707e6364f2..b55069945e9e7 100644
--- a/drivers/platform/x86/intel/pmc/wcl.c
+++ b/drivers/platform/x86/intel/pmc/wcl.c
@@ -493,7 +493,6 @@ static int wcl_core_init(struct pmc_dev *pmcdev, struct pmc_dev_info *pmc_dev_in
}
struct pmc_dev_info wcl_pmc_dev = {
- .pci_func = 2,
.regmap_list = wcl_pmc_info_list,
.map = &wcl_pcdn_reg_map,
.sub_req_show = &pmc_core_substate_blk_req_fops,
@@ -501,4 +500,5 @@ struct pmc_dev_info wcl_pmc_dev = {
.resume = wcl_resume,
.init = wcl_core_init,
.sub_req = pmc_core_pmt_get_blk_sub_req,
+ .ssram_hidden = true,
};
--
2.43.0
^ permalink raw reply related
* [PATCH v2 3/7] platform/x86/intel/pmc: Enable Pkgc blocking residency counter
From: Xi Pardee @ 2026-04-08 22:21 UTC (permalink / raw)
To: xi.pardee, irenic.rajneesh, david.e.box, ilpo.jarvinen,
platform-driver-x86, linux-kernel, linux-pm
In-Reply-To: <20260408222144.3288928-1-xi.pardee@linux.intel.com>
Enable the Package C-state blocking counter in the PMT telemetry
region. This counter reports the number of 10 µs intervals during
which a Package C-state 10.2/3 entry was blocked for the specified
reasons.
Create a common helper for pmc_core_pkgc_ltr_blocker_show() and
pmc_core_pkgc_blocker_residency_show() as these two functions
share similar logic.
Signed-off-by: Xi Pardee <xi.pardee@linux.intel.com>
---
drivers/platform/x86/intel/pmc/core.c | 40 ++++++++++++++++++++-------
drivers/platform/x86/intel/pmc/core.h | 8 ++++++
2 files changed, 38 insertions(+), 10 deletions(-)
diff --git a/drivers/platform/x86/intel/pmc/core.c b/drivers/platform/x86/intel/pmc/core.c
index 5c519942ec58c..94ae098a155a6 100644
--- a/drivers/platform/x86/intel/pmc/core.c
+++ b/drivers/platform/x86/intel/pmc/core.c
@@ -1071,29 +1071,44 @@ static int pmc_core_die_c6_us_show(struct seq_file *s, void *unused)
}
DEFINE_SHOW_ATTRIBUTE(pmc_core_die_c6_us);
-static int pmc_core_pkgc_ltr_blocker_show(struct seq_file *s, void *unused)
+static int pmc_core_pkgc_counters_show(struct seq_file *s,
+ struct telem_endpoint *ep,
+ u32 offset, const char **counters)
{
- struct pmc_dev *pmcdev = s->private;
- const char **pkgc_ltr_blocker_counters;
unsigned int i;
u32 counter;
int ret;
- pkgc_ltr_blocker_counters = pmcdev->pkgc_ltr_blocker_counters;
- for (i = 0; pkgc_ltr_blocker_counters[i]; i++) {
- ret = pmt_telem_read32(pmcdev->pc_ep,
- pmcdev->pkgc_ltr_blocker_offset + i,
- &counter, 1);
-
+ for (i = 0; counters[i]; i++) {
+ ret = pmt_telem_read32(ep, offset + i, &counter, 1);
if (ret)
return ret;
- seq_printf(s, "%-30s %-30u\n", pkgc_ltr_blocker_counters[i], counter);
+ seq_printf(s, "%-30s %-30u\n", counters[i], counter);
}
return 0;
}
+
+static int pmc_core_pkgc_ltr_blocker_show(struct seq_file *s, void *unused)
+{
+ struct pmc_dev *pmcdev = s->private;
+
+ return pmc_core_pkgc_counters_show(s, pmcdev->pc_ep,
+ pmcdev->pkgc_ltr_blocker_offset,
+ pmcdev->pkgc_ltr_blocker_counters);
+}
DEFINE_SHOW_ATTRIBUTE(pmc_core_pkgc_ltr_blocker);
+static int pmc_core_pkgc_blocker_residency_show(struct seq_file *s, void *unused)
+{
+ struct pmc_dev *pmcdev = s->private;
+
+ return pmc_core_pkgc_counters_show(s, pmcdev->pc_ep,
+ pmcdev->pkgc_blocker_offset,
+ pmcdev->pkgc_blocker_counters);
+}
+DEFINE_SHOW_ATTRIBUTE(pmc_core_pkgc_blocker_residency);
+
static int pmc_core_lpm_latch_mode_show(struct seq_file *s, void *unused)
{
struct pmc_dev *pmcdev = s->private;
@@ -1381,6 +1396,8 @@ void pmc_core_punit_pmt_init(struct pmc_dev *pmcdev, struct pmc_dev_info *pmc_de
pmcdev->pc_ep = ep;
pmcdev->pkgc_ltr_blocker_counters = pmc_dev_info->pkgc_ltr_blocker_counters;
pmcdev->pkgc_ltr_blocker_offset = pmc_dev_info->pkgc_ltr_blocker_offset;
+ pmcdev->pkgc_blocker_counters = pmc_dev_info->pkgc_blocker_counters;
+ pmcdev->pkgc_blocker_offset = pmc_dev_info->pkgc_blocker_offset;
}
}
@@ -1510,6 +1527,9 @@ static void pmc_core_dbgfs_register(struct pmc_dev *pmcdev, struct pmc_dev_info
debugfs_create_file("pkgc_ltr_blocker_show", 0444,
pmcdev->dbgfs_dir, pmcdev,
&pmc_core_pkgc_ltr_blocker_fops);
+ debugfs_create_file("pkgc_blocker_residency_show", 0444,
+ pmcdev->dbgfs_dir, pmcdev,
+ &pmc_core_pkgc_blocker_residency_fops);
}
}
diff --git a/drivers/platform/x86/intel/pmc/core.h b/drivers/platform/x86/intel/pmc/core.h
index a20aab73c1409..829b1dee3f636 100644
--- a/drivers/platform/x86/intel/pmc/core.h
+++ b/drivers/platform/x86/intel/pmc/core.h
@@ -455,6 +455,8 @@ struct pmc {
*
* @pkgc_ltr_blocker_counters: Array of PKGC LTR blocker counters
* @pkgc_ltr_blocker_offset: Offset to PKGC LTR blockers in telemetry region
+ * @pkgc_blocker_counters: Array of PKGC blocker counters
+ * @pkgc_blocker_offset: Offset to PKGC blocker in telemetry region
*
* pmc_dev contains info about power management controller device.
*/
@@ -480,6 +482,8 @@ struct pmc_dev {
const char **pkgc_ltr_blocker_counters;
u32 pkgc_ltr_blocker_offset;
+ const char **pkgc_blocker_counters;
+ u32 pkgc_blocker_offset;
};
enum pmc_index {
@@ -495,6 +499,7 @@ enum pmc_index {
* @dmu_guids: List of Die Management Unit GUID
* @pc_guid: GUID for telemetry region to read PKGC blocker info
* @pkgc_ltr_blocker_offset: Offset to PKGC LTR blockers in telemetry region
+ * @pkgc_blocker_offset:Offset to PKGC blocker in telemetry region
* @regmap_list: Pointer to a list of pmc_info structure that could be
* available for the platform. When set, this field implies
* SSRAM support.
@@ -502,6 +507,7 @@ enum pmc_index {
* specific attributes of the primary PMC
* @sub_req_show: File operations to show substate requirements
* @pkgc_ltr_blocker_counters: Array of PKGC LTR blocker counters
+ * @pkgc_blocker_counters: Array of PKGC blocker counters
* @suspend: Function to perform platform specific suspend
* @resume: Function to perform platform specific resume
* @init: Function to perform platform specific init action
@@ -512,10 +518,12 @@ struct pmc_dev_info {
u32 *dmu_guids;
u32 pc_guid;
u32 pkgc_ltr_blocker_offset;
+ u32 pkgc_blocker_offset;
struct pmc_info *regmap_list;
const struct pmc_reg_map *map;
const struct file_operations *sub_req_show;
const char **pkgc_ltr_blocker_counters;
+ const char **pkgc_blocker_counters;
void (*suspend)(struct pmc_dev *pmcdev);
int (*resume)(struct pmc_dev *pmcdev);
int (*init)(struct pmc_dev *pmcdev, struct pmc_dev_info *pmc_dev_info);
--
2.43.0
^ permalink raw reply related
* [PATCH v2 2/7] platform/x86/intel/pmc: Enable PkgC LTR blocking counter
From: Xi Pardee @ 2026-04-08 22:21 UTC (permalink / raw)
To: xi.pardee, irenic.rajneesh, david.e.box, ilpo.jarvinen,
platform-driver-x86, linux-kernel, linux-pm
In-Reply-To: <20260408222144.3288928-1-xi.pardee@linux.intel.com>
Enable the Package C-state LTR blocking counter in the PMT telemetry
region. This counter records how many times any Package C-state entry
is blocked for the specified reasons.
Signed-off-by: Xi Pardee <xi.pardee@linux.intel.com>
---
drivers/platform/x86/intel/pmc/core.c | 74 +++++++++++++++++++++++----
drivers/platform/x86/intel/pmc/core.h | 15 +++++-
2 files changed, 77 insertions(+), 12 deletions(-)
diff --git a/drivers/platform/x86/intel/pmc/core.c b/drivers/platform/x86/intel/pmc/core.c
index c8a92d6235203..5c519942ec58c 100644
--- a/drivers/platform/x86/intel/pmc/core.c
+++ b/drivers/platform/x86/intel/pmc/core.c
@@ -1071,6 +1071,29 @@ static int pmc_core_die_c6_us_show(struct seq_file *s, void *unused)
}
DEFINE_SHOW_ATTRIBUTE(pmc_core_die_c6_us);
+static int pmc_core_pkgc_ltr_blocker_show(struct seq_file *s, void *unused)
+{
+ struct pmc_dev *pmcdev = s->private;
+ const char **pkgc_ltr_blocker_counters;
+ unsigned int i;
+ u32 counter;
+ int ret;
+
+ pkgc_ltr_blocker_counters = pmcdev->pkgc_ltr_blocker_counters;
+ for (i = 0; pkgc_ltr_blocker_counters[i]; i++) {
+ ret = pmt_telem_read32(pmcdev->pc_ep,
+ pmcdev->pkgc_ltr_blocker_offset + i,
+ &counter, 1);
+
+ if (ret)
+ return ret;
+ seq_printf(s, "%-30s %-30u\n", pkgc_ltr_blocker_counters[i], counter);
+ }
+
+ return 0;
+}
+DEFINE_SHOW_ATTRIBUTE(pmc_core_pkgc_ltr_blocker);
+
static int pmc_core_lpm_latch_mode_show(struct seq_file *s, void *unused)
{
struct pmc_dev *pmcdev = s->private;
@@ -1322,7 +1345,7 @@ static struct telem_endpoint *pmc_core_register_endpoint(struct pci_dev *pcidev,
return ERR_PTR(-ENODEV);
}
-void pmc_core_punit_pmt_init(struct pmc_dev *pmcdev, u32 *guids)
+void pmc_core_punit_pmt_init(struct pmc_dev *pmcdev, struct pmc_dev_info *pmc_dev_info)
{
struct telem_endpoint *ep;
@@ -1333,16 +1356,32 @@ void pmc_core_punit_pmt_init(struct pmc_dev *pmcdev, u32 *guids)
return;
}
- ep = pmc_core_register_endpoint(pcidev, guids);
- if (IS_ERR(ep)) {
- dev_err(&pmcdev->pdev->dev,
- "pmc_core: couldn't get DMU telem endpoint %ld",
- PTR_ERR(ep));
- return;
+ if (pmc_dev_info->dmu_guids) {
+ ep = pmc_core_register_endpoint(pcidev, pmc_dev_info->dmu_guids);
+ if (IS_ERR(ep)) {
+ dev_err(&pmcdev->pdev->dev,
+ "pmc_core: couldn't get DMU telem endpoint %ld",
+ PTR_ERR(ep));
+ return;
+ }
+
+ pmcdev->punit_ep = ep;
+ pmcdev->die_c6_offset = MTL_PMT_DMU_DIE_C6_OFFSET;
}
- pmcdev->punit_ep = ep;
- pmcdev->die_c6_offset = MTL_PMT_DMU_DIE_C6_OFFSET;
+ if (pmc_dev_info->pc_guid) {
+ ep = pmt_telem_find_and_register_endpoint(&pcidev->dev, pmc_dev_info->pc_guid, 0);
+ if (IS_ERR(ep)) {
+ dev_err(&pmcdev->pdev->dev,
+ "pmc_core: couldn't get Package C-state telem endpoint %ld",
+ PTR_ERR(ep));
+ return;
+ }
+
+ pmcdev->pc_ep = ep;
+ pmcdev->pkgc_ltr_blocker_counters = pmc_dev_info->pkgc_ltr_blocker_counters;
+ pmcdev->pkgc_ltr_blocker_offset = pmc_dev_info->pkgc_ltr_blocker_offset;
+ }
}
void pmc_core_set_device_d3(unsigned int device)
@@ -1466,6 +1505,13 @@ static void pmc_core_dbgfs_register(struct pmc_dev *pmcdev, struct pmc_dev_info
pmcdev->dbgfs_dir, pmcdev,
&pmc_core_die_c6_us_fops);
}
+
+ if (pmcdev->pc_ep) {
+ debugfs_create_file("pkgc_ltr_blocker_show", 0444,
+ pmcdev->dbgfs_dir, pmcdev,
+ &pmc_core_pkgc_ltr_blocker_fops);
+ }
+
}
/*
@@ -1716,8 +1762,8 @@ int generic_core_init(struct pmc_dev *pmcdev, struct pmc_dev_info *pmc_dev_info)
}
pmc_core_get_low_power_modes(pmcdev);
- if (pmc_dev_info->dmu_guids)
- pmc_core_punit_pmt_init(pmcdev, pmc_dev_info->dmu_guids);
+ if (pmc_dev_info->dmu_guids || pmc_dev_info->pc_guid)
+ pmc_core_punit_pmt_init(pmcdev, pmc_dev_info);
if (ssram) {
ret = pmc_core_get_telem_info(pmcdev, pmc_dev_info);
@@ -1738,6 +1784,9 @@ int generic_core_init(struct pmc_dev *pmcdev, struct pmc_dev_info *pmc_dev_info)
if (pmcdev->punit_ep)
pmt_telem_unregister_endpoint(pmcdev->punit_ep);
+ if (pmcdev->pc_ep)
+ pmt_telem_unregister_endpoint(pmcdev->pc_ep);
+
return ret;
}
@@ -1834,6 +1883,9 @@ static void pmc_core_clean_structure(struct platform_device *pdev)
if (pmcdev->punit_ep)
pmt_telem_unregister_endpoint(pmcdev->punit_ep);
+ if (pmcdev->pc_ep)
+ pmt_telem_unregister_endpoint(pmcdev->pc_ep);
+
platform_set_drvdata(pdev, NULL);
}
diff --git a/drivers/platform/x86/intel/pmc/core.h b/drivers/platform/x86/intel/pmc/core.h
index 118c8740ad3aa..a20aab73c1409 100644
--- a/drivers/platform/x86/intel/pmc/core.h
+++ b/drivers/platform/x86/intel/pmc/core.h
@@ -453,6 +453,9 @@ struct pmc {
* @suspend: Function to perform platform specific suspend
* @resume: Function to perform platform specific resume
*
+ * @pkgc_ltr_blocker_counters: Array of PKGC LTR blocker counters
+ * @pkgc_ltr_blocker_offset: Offset to PKGC LTR blockers in telemetry region
+ *
* pmc_dev contains info about power management controller device.
*/
struct pmc_dev {
@@ -471,8 +474,12 @@ struct pmc_dev {
u8 num_of_pkgc;
u32 die_c6_offset;
+ struct telem_endpoint *pc_ep;
struct telem_endpoint *punit_ep;
struct pmc_info *regmap_list;
+
+ const char **pkgc_ltr_blocker_counters;
+ u32 pkgc_ltr_blocker_offset;
};
enum pmc_index {
@@ -486,12 +493,15 @@ enum pmc_index {
* struct pmc_dev_info - Structure to keep PMC device info
* @pci_func: Function number of the primary PMC
* @dmu_guids: List of Die Management Unit GUID
+ * @pc_guid: GUID for telemetry region to read PKGC blocker info
+ * @pkgc_ltr_blocker_offset: Offset to PKGC LTR blockers in telemetry region
* @regmap_list: Pointer to a list of pmc_info structure that could be
* available for the platform. When set, this field implies
* SSRAM support.
* @map: Pointer to a pmc_reg_map struct that contains platform
* specific attributes of the primary PMC
* @sub_req_show: File operations to show substate requirements
+ * @pkgc_ltr_blocker_counters: Array of PKGC LTR blocker counters
* @suspend: Function to perform platform specific suspend
* @resume: Function to perform platform specific resume
* @init: Function to perform platform specific init action
@@ -500,9 +510,12 @@ enum pmc_index {
struct pmc_dev_info {
u8 pci_func;
u32 *dmu_guids;
+ u32 pc_guid;
+ u32 pkgc_ltr_blocker_offset;
struct pmc_info *regmap_list;
const struct pmc_reg_map *map;
const struct file_operations *sub_req_show;
+ const char **pkgc_ltr_blocker_counters;
void (*suspend)(struct pmc_dev *pmcdev);
int (*resume)(struct pmc_dev *pmcdev);
int (*init)(struct pmc_dev *pmcdev, struct pmc_dev_info *pmc_dev_info);
@@ -535,7 +548,7 @@ int pmc_core_send_ltr_ignore(struct pmc_dev *pmcdev, u32 value, int ignore);
int pmc_core_resume_common(struct pmc_dev *pmcdev);
int get_primary_reg_base(struct pmc *pmc);
-void pmc_core_punit_pmt_init(struct pmc_dev *pmcdev, u32 *guids);
+void pmc_core_punit_pmt_init(struct pmc_dev *pmcdev, struct pmc_dev_info *pmc_dev_info);
void pmc_core_set_device_d3(unsigned int device);
int generic_core_init(struct pmc_dev *pmcdev, struct pmc_dev_info *pmc_dev_info);
--
2.43.0
^ permalink raw reply related
* [PATCH v2 1/7] platform/x86/intel/pmc: Use __free() in pmc_core_punit_pmt_init()
From: Xi Pardee @ 2026-04-08 22:21 UTC (permalink / raw)
To: xi.pardee, irenic.rajneesh, david.e.box, ilpo.jarvinen,
platform-driver-x86, linux-kernel, linux-pm
In-Reply-To: <20260408222144.3288928-1-xi.pardee@linux.intel.com>
Use scope-based cleanup in pmc_core_punit_pmt_init() instead of manually
freeing. This simplifies the code flow by removing the explicit put call,
making it less error-prone.
Signed-off-by: Xi Pardee <xi.pardee@linux.intel.com>
---
drivers/platform/x86/intel/pmc/core.c | 5 ++---
1 file changed, 2 insertions(+), 3 deletions(-)
diff --git a/drivers/platform/x86/intel/pmc/core.c b/drivers/platform/x86/intel/pmc/core.c
index d91e1ab842d65..c8a92d6235203 100644
--- a/drivers/platform/x86/intel/pmc/core.c
+++ b/drivers/platform/x86/intel/pmc/core.c
@@ -1325,16 +1325,15 @@ static struct telem_endpoint *pmc_core_register_endpoint(struct pci_dev *pcidev,
void pmc_core_punit_pmt_init(struct pmc_dev *pmcdev, u32 *guids)
{
struct telem_endpoint *ep;
- struct pci_dev *pcidev;
- pcidev = pci_get_domain_bus_and_slot(0, 0, PCI_DEVFN(10, 0));
+ struct pci_dev *pcidev __free(pci_dev_put) = pci_get_domain_bus_and_slot(0, 0,
+ PCI_DEVFN(10, 0));
if (!pcidev) {
dev_err(&pmcdev->pdev->dev, "PUNIT PMT device not found.");
return;
}
ep = pmc_core_register_endpoint(pcidev, guids);
- pci_dev_put(pcidev);
if (IS_ERR(ep)) {
dev_err(&pmcdev->pdev->dev,
"pmc_core: couldn't get DMU telem endpoint %ld",
--
2.43.0
^ permalink raw reply related
* [PATCH v2 0/7] Enable NVL support in intel_pmc_core
From: Xi Pardee @ 2026-04-08 22:21 UTC (permalink / raw)
To: xi.pardee, irenic.rajneesh, david.e.box, ilpo.jarvinen,
platform-driver-x86, linux-kernel, linux-pm
This patch series introduces two new features, enhances existing
functionalities, and adds NVL support to the intel_pmc_core driver.
The first three patches add new attributes to improve Package C-state
debugging. The fourth and fifth patches refine current functionality
for better support. The sixth patch enables the intel_pmc_core driver
retrieves PMC information only for available PMCs. Finally, the last
patch adds support for Nova Lake platforms.
v2->v1:
- Add a patch to use __free(pci_dev_put) in pmc_core_punit_pmt_init().
- When using scoped base cleanup method, move variable declaration and
assignment in one place.
- Simplifies logic and remove unneeded offset variables.
- Create common helper function to used by pmc_core_pkgc_ltr_blocker_show()
and pmc_core_pkgc_blocker_residency_show()
- Add num_pmcs field in pmc_dev_info struct to store the number of PMCs
available in the platform.
- Use lowercase letter for variable in nvl.c().
- Fix typo.
Xi Pardee (7):
platform/x86/intel/pmc: Use __free() in pmc_core_punit_pmt_init()
platform/x86/intel/pmc: Enable PkgC LTR blocking counter
platform/x86/intel/pmc: Enable Pkgc blocking residency counter
platform/x86/intel/pmc: Use PCI DID for PMC SSRAM device discovery
platform/x86/intel/pmc: Add support for variable DMU offsets
platform/x86/intel/pmc: Retrieve PMC info only for available PMCs
platform/x86/intel/pmc: Add Nova Lake support to intel_pmc_core driver
drivers/platform/x86/intel/pmc/Makefile | 3 +-
drivers/platform/x86/intel/pmc/arl.c | 13 +-
drivers/platform/x86/intel/pmc/core.c | 137 +-
drivers/platform/x86/intel/pmc/core.h | 66 +-
drivers/platform/x86/intel/pmc/lnl.c | 6 +-
drivers/platform/x86/intel/pmc/mtl.c | 7 +-
drivers/platform/x86/intel/pmc/nvl.c | 1539 +++++++++++++++++++++++
drivers/platform/x86/intel/pmc/ptl.c | 8 +-
drivers/platform/x86/intel/pmc/wcl.c | 6 +-
9 files changed, 1747 insertions(+), 38 deletions(-)
create mode 100644 drivers/platform/x86/intel/pmc/nvl.c
--
2.43.0
^ permalink raw reply
* Re: [patch V2 02/11] hrtimer: Use hrtimer_start_expires_user() for hrtimer sleepers
From: Frederic Weisbecker @ 2026-04-08 20:41 UTC (permalink / raw)
To: Thomas Gleixner
Cc: LKML, Peter Zijlstra (Intel), Anna-Maria Behnsen, Calvin Owens,
John Stultz, Stephen Boyd, Alexander Viro, Christian Brauner,
Jan Kara, linux-fsdevel, Sebastian Reichel, linux-pm,
Pablo Neira Ayuso, Florian Westphal, Phil Sutter, netfilter-devel,
coreteam
In-Reply-To: <20260408114952.062400833@kernel.org>
Le Wed, Apr 08, 2026 at 01:53:52PM +0200, Thomas Gleixner a écrit :
> Most hrtimer sleepers are user controlled and user space can hand arbitrary
> expiry values in as long as they are valid timespecs. If the expiry value
> is in the past then this requires a full loop through reprogramming the
> clock event device, taking the hrtimer interrupt, waking the task and
> reprogram again.
>
> Use hrtimer_start_expires_user() which avoids the full round trip by
> checking the timer for expiry on enqueue.
>
> Signed-off-by: Thomas Gleixner <tglx@kernel.org>
> Acked-by: Peter Zijlstra (Intel) <peterz@infradead.org>
> Cc: Anna-Maria Behnsen <anna-maria@linutronix.de>
> Cc: Frederic Weisbecker <frederic@kernel.org>
Reviewed-by: Frederic Weisbecker <frederic@kernel.org>
--
Frederic Weisbecker
SUSE Labs
^ permalink raw reply
* Re: [patch V2 01/11] hrtimer: Provide hrtimer_start_range_ns_user()
From: Frederic Weisbecker @ 2026-04-08 16:53 UTC (permalink / raw)
To: Thomas Gleixner
Cc: LKML, Calvin Owens, Anna-Maria Behnsen, Peter Zijlstra (Intel),
John Stultz, Stephen Boyd, Alexander Viro, Christian Brauner,
Jan Kara, linux-fsdevel, Sebastian Reichel, linux-pm,
Pablo Neira Ayuso, Florian Westphal, Phil Sutter, netfilter-devel,
coreteam
In-Reply-To: <20260408114951.995031895@kernel.org>
Le Wed, Apr 08, 2026 at 01:53:46PM +0200, Thomas Gleixner a écrit :
> Calvin reported an odd NMI watchdog lockup which claims that the CPU locked
> up in user space. He provided a reproducer, which set's up a timerfd based
> timer and then rearms it in a loop with an absolute expiry time of 1ns.
>
> As the expiry time is in the past, the timer ends up as the first expiring
> timer in the per CPU hrtimer base and the clockevent device is programmed
> with the minimum delta value. If the machine is fast enough, this ends up
> in a endless loop of programming the delta value to the minimum value
> defined by the clock event device, before the timer interrupt can fire,
> which starves the interrupt and consequently triggers the lockup detector
> because the hrtimer callback of the lockup mechanism is never invoked.
>
> The clockevents code already has a last resort mechanism to prevent that,
> but it's sensible to catch such issues before trying to reprogram the clock
> event device.
>
> Provide a variant of hrtimer_start_range_ns(), which sanity checks the
> timer after queueing it. It does not so before because the timer might be
> armed and therefore needs to be dequeued. also we optimize for the latest
> possible point to check, so that the clock event prevention is avoided as
> much as possible.
>
> If the timer is already expired _before_ the clock event is reprogrammed,
> remove the timer from the queue and signal to the caller that the operation
> failed by returning false.
>
> That allows the caller to take immediate action without going through the
> loops and hoops of the hrtimer interrupt.
>
> The queueing code can't invoke the timer callback as the caller might hold
> a lock which is taken in the callback.
>
> Add a tracepoint which allows to analyze the expired at start situation.
>
> Reported-by: Calvin Owens <calvin@wbinvd.org>
> Signed-off-by: Thomas Gleixner <tglx@kernel.org>
> Cc: Anna-Maria Behnsen <anna-maria@linutronix.de>
> Cc: Frederic Weisbecker <frederic@kernel.org>
Reviewed-by: Frederic Weisbecker <frederic@kernel.org>
--
Frederic Weisbecker
SUSE Labs
^ permalink raw reply
* Re: [PATCH RESEND v1] thermal: core: fix blocking in unregistering zone
From: Rafael J. Wysocki @ 2026-04-08 15:57 UTC (permalink / raw)
To: Guenter Roeck
Cc: Rafael J. Wysocki, Jiajia Liu, Daniel Lezcano, Zhang Rui,
Lukasz Luba, linux-pm, linux-kernel, Armin Wolf, linux-hwmon
In-Reply-To: <e2e9c203-608b-4c96-b034-ba95eec61ac5@roeck-us.net>
On Wed, Apr 8, 2026 at 5:32 PM Guenter Roeck <linux@roeck-us.net> wrote:
>
> On 4/8/26 08:05, Rafael J. Wysocki wrote:
> > On Sun, Apr 5, 2026 at 5:34 AM Guenter Roeck <linux@roeck-us.net> wrote:
> >>
> >> On 4/4/26 10:38, Rafael J. Wysocki wrote:
> >>> On Sat, Apr 4, 2026 at 4:02 PM Guenter Roeck <linux@roeck-us.net> wrote:
> >>>>
> >>>> On 4/4/26 05:58, Rafael J. Wysocki wrote:
> >>>>> On Fri, Apr 3, 2026 at 4:20 PM Guenter Roeck <linux@roeck-us.net> wrote:
> >>>>>>
> >>>>>> On 4/3/26 05:52, Rafael J. Wysocki wrote:
> >>>>>> .[ ... ]
> >>>>>>> It appears to work for me, but I'm not sure if having multiple hwmon class
> >>>>>>> devices with the same value in the name attribute is fine.
> >>>>>>
> >>>>>> Like this ?
> >>>>>>
> >>>>>> $ cd /sys/class/hwmon
> >>>>>> $ grep . */name
> >>>>>> hwmon0/name:r8169_0_c00:00
> >>>>>> hwmon1/name:nvme
> >>>>>> hwmon2/name:nvme
> >>>>>> hwmon3/name:nct6687
> >>>>>> hwmon4/name:k10temp
> >>>>>> hwmon5/name:spd5118
> >>>>>> hwmon6/name:spd5118
> >>>>>> hwmon7/name:spd5118
> >>>>>> hwmon8/name:spd5118
> >>>>>> hwmon9/name:mt7921_phy0
> >>>>>
> >>>>> Yes.
> >>>>>
> >>>>>> Names such as "r8169_0_c00:00" and "mt7921_phy0" are actually overkill
> >>>>>> since the "sensors" command makes it
> >>>>>>
> >>>>>> r8169_0_c00:00-mdio-0
> >>>>>> Adapter: MDIO adapter
> >>>>>> temp1: +36.0°C (high = +120.0°C)
> >>>>>>
> >>>>>> mt7921_phy0-pci-0d00
> >>>>>> Adapter: PCI adapter
> >>>>>> temp1: +30.0°C
> >>>>>>
> >>>>>> essentially duplicating the device index.
> >>>>>
> >>>>> Well, with the patch posted by me, the output of sensors from a test
> >>>>> system looks like this:
> >>>>>
> >>>>> acpitz-acpi-0
> >>>>> Adapter: ACPI interface
> >>>>> temp1: +16.8°C
> >>>>>
> >>>>> pch_cannonlake-virtual-0
> >>>>> Adapter: Virtual device
> >>>>> temp1: +33.0°C
> >>>>>
> >>>>> acpitz-acpi-0
> >>>>> Adapter: ACPI interface
> >>>>> temp1: +27.8°C
> >>>>>
> >>>>> (some further data excluded), which is kind of confusing (note the
> >>>>> duplicate acpitz-acpi-0 entries with different values of temp1).
> >>>>>
> >>>>
> >>>> Yes, agreed, that is confusing. I would have expected the second one
> >>>> to be identified as "acpitz-acpi-1". Do they both have the same parent ?
> >>>
> >>> No, they don't.
> >>>
> >>> The parent of each of them is a thermal zone device and both parents
> >>> have the same "type" value.
> >>>
> >>>>> That could be disambiguated by concatenating the thermal zone ID
> >>>>> (possibly after a '_') to the name. Or the "temp*" things for thermal
> >>>>> zones of the same type could carry different numbers.
> >>>>>
> >>>>> A less attractive alternative would be to register a special virtual
> >>>>> device serving as a parent for all hwmon interfaces registered
> >>>>> automatically for thermal zones.
> >>>>
> >>>> If they all have the same parent, technically it should be a single
> >>>> hwmon device with multiple sensors, as in:
> >>>>
> >>>> acpitz-acpi-0
> >>>> Adapter: ACPI interface
> >>>> temp1: +16.8°C
> >>>> temp2: +27.8°C
> >>>
> >>> So somebody tried to make it look like that by registering hwmon
> >>> interfaces for all of the thermal zones of the same type under one of
> >>> them, but that (quite obviously) doesn't work.
> >>
> >> Not sure I understand why that doesn't work or why that is obvious,
> >> but I'll take you by your word (I would agree that the current
> >> _implementation_ looks problematic).
> >
> > For example, say that there are two ACPI thermal zones on a system
> >
> > /sys/devices/virtual/thermal/thermal_zone0/
> > /sys/devices/virtual/thermal/thermal_zone1/
> >
> > The current mainline code registers a hwmon class device for thermal_zone0 only:
> >
> > /sys/devices/virtual/thermal/thermal_zone0/hwmon0/
> >
> > because the type is "acpitz" for both of them, but it adds a sysfs
> > attribute that belongs to thermal_zone1 under it:
> >
> > /sys/devices/virtual/thermal/thermal_zone0/hwmon0/temp2_input
> >
> > There is also
> >
> > /sys/devices/virtual/thermal/thermal_zone0/hwmon0/temp1_input
> >
> > but it belongs to thermal_zone0.
> >
> > Interesting things happen when thermal_zone0 is removed, for example
> > because the ACPI thermal driver is unbound from the underlying
> > platform device. Namely, the removal code skips the removal of hwmon0
> > because of the temp2_input attribute belonging to thermal_zone1 which
> > effectively prevents thermal_zone0 removal from making progress.
> >
> > AFAICS, nothing particularly smart can be done to address this issue
> > while retaining the current design of the code. Reparenting hwmon0 to
> > thermal_zone1 may confuse user space as well as removing hwmon0 along
> > with temp2_input. That's why I think that this is a design issue.
> >
>
> The ACPI power meter driver has pretty much the same problem. A clear
> solution would require making hwmon sysfs attributes dynamic in nature
> (i.e., by adding the ability to change the visibility of attributes in
> runtime). I have started working on that, but did not have time to
> complete the work. The ACPI power meter driver uses a kludge around that:
> It unregisters the hwmon device whenever it gets a METER_NOTIFY_CONFIG
> event and re-registers it.
>
> Anyway, registering separate hwmon devices, one per thermal zone,
> is perfectly fine with me.
OK, I'll do that and see how it goes.
Thanks for the feedback!
^ permalink raw reply
* Re: [PATCH RESEND v1] thermal: core: fix blocking in unregistering zone
From: Guenter Roeck @ 2026-04-08 15:32 UTC (permalink / raw)
To: Rafael J. Wysocki
Cc: Jiajia Liu, Daniel Lezcano, Zhang Rui, Lukasz Luba, linux-pm,
linux-kernel, Armin Wolf, linux-hwmon
In-Reply-To: <CAJZ5v0jfi_gXPVq9E2eJe_0MG4vVojyDo6=ABv4fNFK=Q_qpug@mail.gmail.com>
On 4/8/26 08:05, Rafael J. Wysocki wrote:
> On Sun, Apr 5, 2026 at 5:34 AM Guenter Roeck <linux@roeck-us.net> wrote:
>>
>> On 4/4/26 10:38, Rafael J. Wysocki wrote:
>>> On Sat, Apr 4, 2026 at 4:02 PM Guenter Roeck <linux@roeck-us.net> wrote:
>>>>
>>>> On 4/4/26 05:58, Rafael J. Wysocki wrote:
>>>>> On Fri, Apr 3, 2026 at 4:20 PM Guenter Roeck <linux@roeck-us.net> wrote:
>>>>>>
>>>>>> On 4/3/26 05:52, Rafael J. Wysocki wrote:
>>>>>> .[ ... ]
>>>>>>> It appears to work for me, but I'm not sure if having multiple hwmon class
>>>>>>> devices with the same value in the name attribute is fine.
>>>>>>
>>>>>> Like this ?
>>>>>>
>>>>>> $ cd /sys/class/hwmon
>>>>>> $ grep . */name
>>>>>> hwmon0/name:r8169_0_c00:00
>>>>>> hwmon1/name:nvme
>>>>>> hwmon2/name:nvme
>>>>>> hwmon3/name:nct6687
>>>>>> hwmon4/name:k10temp
>>>>>> hwmon5/name:spd5118
>>>>>> hwmon6/name:spd5118
>>>>>> hwmon7/name:spd5118
>>>>>> hwmon8/name:spd5118
>>>>>> hwmon9/name:mt7921_phy0
>>>>>
>>>>> Yes.
>>>>>
>>>>>> Names such as "r8169_0_c00:00" and "mt7921_phy0" are actually overkill
>>>>>> since the "sensors" command makes it
>>>>>>
>>>>>> r8169_0_c00:00-mdio-0
>>>>>> Adapter: MDIO adapter
>>>>>> temp1: +36.0°C (high = +120.0°C)
>>>>>>
>>>>>> mt7921_phy0-pci-0d00
>>>>>> Adapter: PCI adapter
>>>>>> temp1: +30.0°C
>>>>>>
>>>>>> essentially duplicating the device index.
>>>>>
>>>>> Well, with the patch posted by me, the output of sensors from a test
>>>>> system looks like this:
>>>>>
>>>>> acpitz-acpi-0
>>>>> Adapter: ACPI interface
>>>>> temp1: +16.8°C
>>>>>
>>>>> pch_cannonlake-virtual-0
>>>>> Adapter: Virtual device
>>>>> temp1: +33.0°C
>>>>>
>>>>> acpitz-acpi-0
>>>>> Adapter: ACPI interface
>>>>> temp1: +27.8°C
>>>>>
>>>>> (some further data excluded), which is kind of confusing (note the
>>>>> duplicate acpitz-acpi-0 entries with different values of temp1).
>>>>>
>>>>
>>>> Yes, agreed, that is confusing. I would have expected the second one
>>>> to be identified as "acpitz-acpi-1". Do they both have the same parent ?
>>>
>>> No, they don't.
>>>
>>> The parent of each of them is a thermal zone device and both parents
>>> have the same "type" value.
>>>
>>>>> That could be disambiguated by concatenating the thermal zone ID
>>>>> (possibly after a '_') to the name. Or the "temp*" things for thermal
>>>>> zones of the same type could carry different numbers.
>>>>>
>>>>> A less attractive alternative would be to register a special virtual
>>>>> device serving as a parent for all hwmon interfaces registered
>>>>> automatically for thermal zones.
>>>>
>>>> If they all have the same parent, technically it should be a single
>>>> hwmon device with multiple sensors, as in:
>>>>
>>>> acpitz-acpi-0
>>>> Adapter: ACPI interface
>>>> temp1: +16.8°C
>>>> temp2: +27.8°C
>>>
>>> So somebody tried to make it look like that by registering hwmon
>>> interfaces for all of the thermal zones of the same type under one of
>>> them, but that (quite obviously) doesn't work.
>>
>> Not sure I understand why that doesn't work or why that is obvious,
>> but I'll take you by your word (I would agree that the current
>> _implementation_ looks problematic).
>
> For example, say that there are two ACPI thermal zones on a system
>
> /sys/devices/virtual/thermal/thermal_zone0/
> /sys/devices/virtual/thermal/thermal_zone1/
>
> The current mainline code registers a hwmon class device for thermal_zone0 only:
>
> /sys/devices/virtual/thermal/thermal_zone0/hwmon0/
>
> because the type is "acpitz" for both of them, but it adds a sysfs
> attribute that belongs to thermal_zone1 under it:
>
> /sys/devices/virtual/thermal/thermal_zone0/hwmon0/temp2_input
>
> There is also
>
> /sys/devices/virtual/thermal/thermal_zone0/hwmon0/temp1_input
>
> but it belongs to thermal_zone0.
>
> Interesting things happen when thermal_zone0 is removed, for example
> because the ACPI thermal driver is unbound from the underlying
> platform device. Namely, the removal code skips the removal of hwmon0
> because of the temp2_input attribute belonging to thermal_zone1 which
> effectively prevents thermal_zone0 removal from making progress.
>
> AFAICS, nothing particularly smart can be done to address this issue
> while retaining the current design of the code. Reparenting hwmon0 to
> thermal_zone1 may confuse user space as well as removing hwmon0 along
> with temp2_input. That's why I think that this is a design issue.
>
The ACPI power meter driver has pretty much the same problem. A clear
solution would require making hwmon sysfs attributes dynamic in nature
(i.e., by adding the ability to change the visibility of attributes in
runtime). I have started working on that, but did not have time to
complete the work. The ACPI power meter driver uses a kludge around that:
It unregisters the hwmon device whenever it gets a METER_NOTIFY_CONFIG
event and re-registers it.
Anyway, registering separate hwmon devices, one per thermal zone,
is perfectly fine with me.
Guenter
^ permalink raw reply
* [PATCH v2] interconnect: imx: fix use-after-free in imx_icc_node_init_qos()
From: Wentao Liang @ 2026-04-08 15:30 UTC (permalink / raw)
To: Georgi Djakov, Shawn Guo, Sascha Hauer
Cc: Pengutronix Kernel Team, Fabio Estevam, Wentao Liang, linux-pm,
imx, linux-arm-kernel, linux-kernel, stable
The function imx_icc_node_init_qos() manually manages the reference count
of struct device_node *dn using of_node_put(). However, some error paths
use dn after the put, leading to use-after-free. Convert to automatic
cleanup using __free(device_node) to ensure the reference is always
released when dn goes out of scope.
Fixes: f0d8048525d7 ("interconnect: Add imx core driver")
Cc: stable@vger.kernel.org
Signed-off-by: Wentao Liang <vulab@iscas.ac.cn>
---
Changes in v2:
- Use auto cheanup to fix the problem.
---
drivers/interconnect/imx/imx.c | 6 ++----
1 file changed, 2 insertions(+), 4 deletions(-)
diff --git a/drivers/interconnect/imx/imx.c b/drivers/interconnect/imx/imx.c
index 9511f80cf041..e5fcdcb88cfb 100644
--- a/drivers/interconnect/imx/imx.c
+++ b/drivers/interconnect/imx/imx.c
@@ -120,7 +120,8 @@ static int imx_icc_node_init_qos(struct icc_provider *provider,
struct imx_icc_node *node_data = node->data;
const struct imx_icc_node_adj_desc *adj = node_data->desc->adj;
struct device *dev = provider->dev;
- struct device_node *dn = NULL;
+ struct device_node *__free(device_nod) dn = of_parse_phandle(dev->of_node,
+ adj->phandle_name, 0);
struct platform_device *pdev;
if (adj->main_noc) {
@@ -128,7 +129,6 @@ static int imx_icc_node_init_qos(struct icc_provider *provider,
dev_dbg(dev, "icc node %s[%d] is main noc itself\n",
node->name, node->id);
} else {
- dn = of_parse_phandle(dev->of_node, adj->phandle_name, 0);
if (!dn) {
dev_warn(dev, "Failed to parse %s\n",
adj->phandle_name);
@@ -138,12 +138,10 @@ static int imx_icc_node_init_qos(struct icc_provider *provider,
if (!of_device_is_available(dn)) {
dev_warn(dev, "Missing property %s, skip scaling %s\n",
adj->phandle_name, node->name);
- of_node_put(dn);
return 0;
}
pdev = of_find_device_by_node(dn);
- of_node_put(dn);
if (!pdev) {
dev_warn(dev, "node %s[%d] missing device for %pOF\n",
node->name, node->id, dn);
--
2.34.1
^ permalink raw reply related
* Status of thermal support for i.MX93
From: Stefan Wahren @ 2026-04-08 15:28 UTC (permalink / raw)
To: Jacky Bai, Alice Guo, Frank Li
Cc: Fabio Estevam, imx@lists.linux.dev, Linux ARM,
open list:GENERIC PM DOMAINS, Daniel Lezcano, Sascha Hauer
Hi,
AFAIK the thermal support for i.MX93 hasn't been mainlined yet. The last
version I can find is here [1].
Are there any plans to finish this work?
Thanks
[1] -
https://lore.kernel.org/linux-arm-kernel/d9392dbc-806a-41df-8992-28c3d6132309@linaro.org/#t
^ permalink raw reply
* Re: [patch 01/12] clockevents: Prevent timer interrupt starvation
From: Thomas Gleixner @ 2026-04-08 15:18 UTC (permalink / raw)
To: Thomas Weißschuh
Cc: LKML, Calvin Owens, Peter Zijlstra, Anna-Maria Behnsen,
Frederic Weisbecker, Ingo Molnar, John Stultz, Stephen Boyd,
Alexander Viro, Christian Brauner, Jan Kara, linux-fsdevel,
Sebastian Reichel, linux-pm, Pablo Neira Ayuso, Florian Westphal,
Phil Sutter, netfilter-devel, coreteam
In-Reply-To: <20260408155353-42aeefa4-db66-48aa-ab07-0538a8cfdbf0@linutronix.de>
On Wed, Apr 08 2026 at 15:55, Thomas Weißschuh wrote:
> On Wed, Apr 08, 2026 at 02:41:20PM +0200, Thomas Weißschuh wrote:
> --- a/kernel/time/clockevents.c
> +++ b/kernel/time/clockevents.c
> @@ -369,7 +369,7 @@ int clockevents_program_event(struct clock_event_device *dev, ktime_t expires, b
> if (dev->next_event_forced)
> return 0;
>
> - if (dev->set_next_event(dev->min_delta_ticks, dev)) {
> + if (dev->set_next_event(dev->min_delta_ns, dev)) {
That's wrong as the callback expects cycles (ticks) not nanoseconds.
I've just pushed out an updated version to tip timers/urgent which
addresses a potentially related issue. Delta patch below.
Thanks,
tglx
---
--- a/kernel/time/clockevents.c
+++ b/kernel/time/clockevents.c
@@ -324,6 +324,8 @@ int clockevents_program_event(struct clo
return dev->set_next_ktime(expires, dev);
delta = ktime_to_ns(ktime_sub(expires, ktime_get()));
+ if (delta <= 0 && !force)
+ return -ETIME;
if (delta > (int64_t)dev->min_delta_ns) {
delta = min(delta, (int64_t) dev->max_delta_ns);
^ permalink raw reply
* Re: [PATCH RESEND v1] thermal: core: fix blocking in unregistering zone
From: Rafael J. Wysocki @ 2026-04-08 15:05 UTC (permalink / raw)
To: Guenter Roeck
Cc: Rafael J. Wysocki, Jiajia Liu, Daniel Lezcano, Zhang Rui,
Lukasz Luba, linux-pm, linux-kernel, Armin Wolf, linux-hwmon
In-Reply-To: <e5638cf8-88ee-4b61-b032-6cf324b7c642@roeck-us.net>
On Sun, Apr 5, 2026 at 5:34 AM Guenter Roeck <linux@roeck-us.net> wrote:
>
> On 4/4/26 10:38, Rafael J. Wysocki wrote:
> > On Sat, Apr 4, 2026 at 4:02 PM Guenter Roeck <linux@roeck-us.net> wrote:
> >>
> >> On 4/4/26 05:58, Rafael J. Wysocki wrote:
> >>> On Fri, Apr 3, 2026 at 4:20 PM Guenter Roeck <linux@roeck-us.net> wrote:
> >>>>
> >>>> On 4/3/26 05:52, Rafael J. Wysocki wrote:
> >>>> .[ ... ]
> >>>>> It appears to work for me, but I'm not sure if having multiple hwmon class
> >>>>> devices with the same value in the name attribute is fine.
> >>>>
> >>>> Like this ?
> >>>>
> >>>> $ cd /sys/class/hwmon
> >>>> $ grep . */name
> >>>> hwmon0/name:r8169_0_c00:00
> >>>> hwmon1/name:nvme
> >>>> hwmon2/name:nvme
> >>>> hwmon3/name:nct6687
> >>>> hwmon4/name:k10temp
> >>>> hwmon5/name:spd5118
> >>>> hwmon6/name:spd5118
> >>>> hwmon7/name:spd5118
> >>>> hwmon8/name:spd5118
> >>>> hwmon9/name:mt7921_phy0
> >>>
> >>> Yes.
> >>>
> >>>> Names such as "r8169_0_c00:00" and "mt7921_phy0" are actually overkill
> >>>> since the "sensors" command makes it
> >>>>
> >>>> r8169_0_c00:00-mdio-0
> >>>> Adapter: MDIO adapter
> >>>> temp1: +36.0°C (high = +120.0°C)
> >>>>
> >>>> mt7921_phy0-pci-0d00
> >>>> Adapter: PCI adapter
> >>>> temp1: +30.0°C
> >>>>
> >>>> essentially duplicating the device index.
> >>>
> >>> Well, with the patch posted by me, the output of sensors from a test
> >>> system looks like this:
> >>>
> >>> acpitz-acpi-0
> >>> Adapter: ACPI interface
> >>> temp1: +16.8°C
> >>>
> >>> pch_cannonlake-virtual-0
> >>> Adapter: Virtual device
> >>> temp1: +33.0°C
> >>>
> >>> acpitz-acpi-0
> >>> Adapter: ACPI interface
> >>> temp1: +27.8°C
> >>>
> >>> (some further data excluded), which is kind of confusing (note the
> >>> duplicate acpitz-acpi-0 entries with different values of temp1).
> >>>
> >>
> >> Yes, agreed, that is confusing. I would have expected the second one
> >> to be identified as "acpitz-acpi-1". Do they both have the same parent ?
> >
> > No, they don't.
> >
> > The parent of each of them is a thermal zone device and both parents
> > have the same "type" value.
> >
> >>> That could be disambiguated by concatenating the thermal zone ID
> >>> (possibly after a '_') to the name. Or the "temp*" things for thermal
> >>> zones of the same type could carry different numbers.
> >>>
> >>> A less attractive alternative would be to register a special virtual
> >>> device serving as a parent for all hwmon interfaces registered
> >>> automatically for thermal zones.
> >>
> >> If they all have the same parent, technically it should be a single
> >> hwmon device with multiple sensors, as in:
> >>
> >> acpitz-acpi-0
> >> Adapter: ACPI interface
> >> temp1: +16.8°C
> >> temp2: +27.8°C
> >
> > So somebody tried to make it look like that by registering hwmon
> > interfaces for all of the thermal zones of the same type under one of
> > them, but that (quite obviously) doesn't work.
>
> Not sure I understand why that doesn't work or why that is obvious,
> but I'll take you by your word (I would agree that the current
> _implementation_ looks problematic).
For example, say that there are two ACPI thermal zones on a system
/sys/devices/virtual/thermal/thermal_zone0/
/sys/devices/virtual/thermal/thermal_zone1/
The current mainline code registers a hwmon class device for thermal_zone0 only:
/sys/devices/virtual/thermal/thermal_zone0/hwmon0/
because the type is "acpitz" for both of them, but it adds a sysfs
attribute that belongs to thermal_zone1 under it:
/sys/devices/virtual/thermal/thermal_zone0/hwmon0/temp2_input
There is also
/sys/devices/virtual/thermal/thermal_zone0/hwmon0/temp1_input
but it belongs to thermal_zone0.
Interesting things happen when thermal_zone0 is removed, for example
because the ACPI thermal driver is unbound from the underlying
platform device. Namely, the removal code skips the removal of hwmon0
because of the temp2_input attribute belonging to thermal_zone1 which
effectively prevents thermal_zone0 removal from making progress.
AFAICS, nothing particularly smart can be done to address this issue
while retaining the current design of the code. Reparenting hwmon0 to
thermal_zone1 may confuse user space as well as removing hwmon0 along
with temp2_input. That's why I think that this is a design issue.
> I looked into the source code of the "sensors" command. It indeed does
> not index ACPI devices (nor virtual devices, for that matter) but
> assumes that such devices are unique. My apologies for not realizing
> this earlier.
>
> So your only option is indeed to index the chip name if you want/need
> more than one hwmon device with the same base name (here: acpitz)
> instantiated from the thermal subsystem.
>
> One comment to one of your earlier e-mails:
>
> "However, it is more of a design issue IMV because putting temperature
> attributes for all of the (possibly unrelated) thermal zones of the
> same type under one hwmon interface is not particularly useful"
>
> A single hardware monitoring device, by design, serves multiple
> thermal zones. Anything else would not make sense for multi-channel
> hardware monitoring chips. The hardware monitoring subsystem groups
> sensors by chip, not by thermal zones.
>
> Having said this: This discussion is not new. Certain subsystems
> advocate for having one hardware monitoring device per sensor,
> not per chip. One submitter went as far as telling me that I am
> clueless. We don't need to repeat the exercise. I have my opinion,
> you have yours, and all we can do is to agree to disagree.
I'm not sure if this has anything to do with hardware monitoring chips
because hwmon_device_register_for_thermal() sets the chip argument of
__hwmon_device_register() to NULL, so the chip information is missing
in this particular case. The underlying hardware may or may not be a
multi-channel hardware monitoring chip, that is hard to tell in
general.
In the particular case of ACPI thermal zones, they each correspond to
a different platform device and regarding those as different channels
of the same hardware monitoring chip is kind of a stretch IMV (they
may even be located at different places in the device hierarchy).
Regardless, it should be possible to remove each of them cleanly
because they are handled by the driver independently.
^ permalink raw reply
* Re: [PATCH v2 0/7] thermal: samsung: Add support for Google GS101 TMU
From: Alexey Klimov @ 2026-04-08 14:49 UTC (permalink / raw)
To: Tudor Ambarus
Cc: Rafael J. Wysocki, Daniel Lezcano, Zhang Rui, Lukasz Luba,
Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Krzysztof Kozlowski, Alim Akhtar, Bartlomiej Zolnierkiewicz,
Kees Cook, Gustavo A. R. Silva, Peter Griffin, André Draszik,
willmcvicker, jyescas, shin.son, linux-samsung-soc, linux-kernel,
linux-pm, devicetree, linux-arm-kernel, linux-hardening
In-Reply-To: <20260119-acpm-tmu-v2-0-e02a834f04c6@linaro.org>
On Mon Jan 19, 2026 at 12:08 PM GMT, Tudor Ambarus wrote:
> Add support for the Thermal Management Unit (TMU) on the Google GS101
> SoC.
>
> The GS101 TMU implementation utilizes a hybrid architecture where
> management is shared between the kernel and the Alive Clock and
> Power Manager (ACPM) firmware.
Do you plan to update or work on this series? If, by some reason,
this series is postphoned I can rebase it and re-send, for example.
IIRC it needs a clean rebase as a minimial change.
I am constructing some code on top of it, so it will be nice to have
newer version that can be (re-)tested for Exynos850.
Thanks,
Alexey
[...]
^ permalink raw reply
* [PATCH v2] cpufreq: Fix hotplug-suspend race during reboot
From: Tianxiang Chen @ 2026-04-08 14:19 UTC (permalink / raw)
To: rafael; +Cc: viresh.kumar, lingyue, linux-pm, linux-kernel, Tianxiang Chen
In-Reply-To: <CAJZ5v0ie54h2aK05qNZTWNw5bu7GZDgsxM55KSsuF=ReLMkm-w@mail.gmail.com>
During system reboot, cpufreq_suspend() is called via the
kernel_restart() -> device_shutdown() -> pm_notifier_call_chain()
path. Unlike the normal system suspend path, the reboot path does not
call freeze_processes(), so userspace processes and kernel threads
remain active.
This allows CPU hotplug operations to run concurrently with
cpufreq_suspend(). The original code has no synchronization with CPU
hotplug, leading to a race condition where governor_data can be freed
by the hotplug path while cpufreq_suspend() is still accessing it,
resulting in a null pointer dereference:
Unable to handle kernel NULL pointer dereference
Call Trace:
do_kernel_fault+0x28/0x3c
cpufreq_suspend+0xdc/0x160
device_shutdown+0x18/0x200
kernel_restart+0x40/0x80
arm64_sys_reboot+0x1b0/0x200
Fix this by adding cpus_read_lock()/cpus_read_unlock() to
cpufreq_suspend() to block CPU hotplug operations while suspend is in
progress.
Signed-off-by: Tianxiang Chen <nanmu@xiaomi.com>
---
v2:
- Update changelog to explicitly mention reboot scenario
- Add observed crash trace
---
drivers/cpufreq/cpufreq.c | 2 ++
1 file changed, 2 insertions(+)
diff --git a/drivers/cpufreq/cpufreq.c b/drivers/cpufreq/cpufreq.c
index 1f794524a1d9..6f1d264c378b 100644
--- a/drivers/cpufreq/cpufreq.c
+++ b/drivers/cpufreq/cpufreq.c
@@ -1979,6 +1979,7 @@ void cpufreq_suspend(void)
if (!cpufreq_driver)
return;
+ cpus_read_lock();
if (!has_target() && !cpufreq_driver->suspend)
goto suspend;
@@ -1998,6 +1999,7 @@ void cpufreq_suspend(void)
suspend:
cpufreq_suspended = true;
+ cpus_read_unlock();
}
/**
--
2.34.1
^ permalink raw reply related
* Re: [patch 01/12] clockevents: Prevent timer interrupt starvation
From: Frederic Weisbecker @ 2026-04-08 14:15 UTC (permalink / raw)
To: Thomas Gleixner
Cc: LKML, Calvin Owens, Peter Zijlstra, Anna-Maria Behnsen,
Ingo Molnar, John Stultz, Stephen Boyd, Alexander Viro,
Christian Brauner, Jan Kara, linux-fsdevel, Sebastian Reichel,
linux-pm, Pablo Neira Ayuso, Florian Westphal, Phil Sutter,
netfilter-devel, coreteam
In-Reply-To: <20260407083247.562657657@kernel.org>
Le Tue, Apr 07, 2026 at 10:54:17AM +0200, Thomas Gleixner a écrit :
> From: Thomas Gleixner <tglx@kernel.org>
>
> Calvin reported an odd NMI watchdog lockup which claims that the CPU locked
> up in user space. He provided a reproducer, which sets up a timerfd based
> timer and then rearms it in a loop with an absolute expiry time of 1ns.
>
> As the expiry time is in the past, the timer ends up as the first expiring
> timer in the per CPU hrtimer base and the clockevent device is programmed
> with the minimum delta value. If the machine is fast enough, this ends up
> in a endless loop of programming the delta value to the minimum value
> defined by the clock event device, before the timer interrupt can fire,
> which starves the interrupt and consequently triggers the lockup detector
> because the hrtimer callback of the lockup mechanism is never invoked.
>
> As a first step to prevent this, avoid reprogramming the clock event device
> when:
> - a forced minimum delta event is pending
> - the new expiry delta is less then or equal to the minimum delta
>
> Thanks to Calvin for providing the reproducer and to Borislav for testing
> and providing data from his Zen5 machine.
>
> The problem is not limited to Zen5, but depending on the underlying
> clock event device (e.g. TSC deadline timer on Intel) and the CPU speed
> not necessarily observable.
>
> This change serves only as the last resort and further changes will be made
> to prevent this scenario earlier in the call chain as far as possible.
>
> Fixes: d316c57ff6bf ("[PATCH] clockevents: add core functionality")
> Reported-by: Calvin Owens <calvin@wbinvd.org>
> Signed-off-by: Thomas Gleixner <tglx@kernel.org>
> Cc: Peter Zijlstra <peterz@infradead.org>
> Cc: Anna-Maria Behnsen <anna-maria@linutronix.de>
> Cc: Frederic Weisbecker <frederic@kernel.org>
> Cc: Ingo Molnar <mingo@kernel.org>
> Link: https://lore.kernel.org/lkml/acMe-QZUel-bBYUh@mozart.vkv.me/
Reviewed-by: Frederic Weisbecker <frederic@kernel.org>
--
Frederic Weisbecker
SUSE Labs
^ permalink raw reply
* [PATCH] pmdomain: mediatek: fix use-after-free in scpsys_get_bus_protection_legacy()
From: Wentao Liang @ 2026-04-08 14:11 UTC (permalink / raw)
To: Ulf Hansson, Matthias Brugger, AngeloGioacchino Del Regno
Cc: nfraprado, Macpaul Lin, Adam Ford, Chen-Yu Tsai, linux-pm,
linux-kernel, linux-arm-kernel, linux-mediatek, Wentao Liang,
stable
In scpsys_get_bus_protection_legacy(), of_find_node_with_property()
returns a device node with its reference count incremented. The function
then calls of_node_put(node) before checking whether
syscon_regmap_lookup_by_phandle() returns an error. If an error occurs,
dev_err_probe() dereferences the node pointer to print diagnostic
information, but the node memory may have already been freed due to the
earlier of_node_put(), leading to a use-after-free vulnerability.
Fix this by moving the of_node_put() call after the error check, ensuring
the node is still valid when accessed in the error path.
Fixes: c29345fa5f66 ("pmdomain: mediatek: Refactor bus protection regmaps retrieval")
Cc: stable@vger.kernel.org
Signed-off-by: Wentao Liang <vulab@iscas.ac.cn>
---
drivers/pmdomain/mediatek/mtk-pm-domains.c | 10 +++++++---
1 file changed, 7 insertions(+), 3 deletions(-)
diff --git a/drivers/pmdomain/mediatek/mtk-pm-domains.c b/drivers/pmdomain/mediatek/mtk-pm-domains.c
index e2800aa1bc59..d3b36f32417c 100644
--- a/drivers/pmdomain/mediatek/mtk-pm-domains.c
+++ b/drivers/pmdomain/mediatek/mtk-pm-domains.c
@@ -993,6 +993,7 @@ static int scpsys_get_bus_protection_legacy(struct device *dev, struct scpsys *s
struct device_node *node, *smi_np;
int num_regmaps = 0, i, j;
struct regmap *regmap[3];
+ int ret = 0;
/*
* Legacy code retrieves a maximum of three bus protection handles:
@@ -1043,11 +1044,14 @@ static int scpsys_get_bus_protection_legacy(struct device *dev, struct scpsys *s
if (node) {
regmap[2] = syscon_regmap_lookup_by_phandle(node, "mediatek,infracfg-nao");
num_regmaps++;
- of_node_put(node);
- if (IS_ERR(regmap[2]))
- return dev_err_probe(dev, PTR_ERR(regmap[2]),
+ if (IS_ERR(regmap[2])) {
+ ret = dev_err_probe(dev, PTR_ERR(regmap[2]),
"%pOF: failed to get infracfg regmap\n",
node);
+ of_node_put(node);
+ return ret;
+ }
+ of_node_put(node);
} else {
regmap[2] = NULL;
}
--
2.34.1
^ permalink raw reply related
* Re: [patch 01/12] clockevents: Prevent timer interrupt starvation
From: Thomas Weißschuh @ 2026-04-08 13:55 UTC (permalink / raw)
To: Thomas Gleixner
Cc: LKML, Calvin Owens, Peter Zijlstra, Anna-Maria Behnsen,
Frederic Weisbecker, Ingo Molnar, John Stultz, Stephen Boyd,
Alexander Viro, Christian Brauner, Jan Kara, linux-fsdevel,
Sebastian Reichel, linux-pm, Pablo Neira Ayuso, Florian Westphal,
Phil Sutter, netfilter-devel, coreteam
In-Reply-To: <20260408143313-ac6c3b82-70e6-4ce3-b33a-20f5e6ba160b@linutronix.de>
On Wed, Apr 08, 2026 at 02:41:20PM +0200, Thomas Weißschuh wrote:
> Hi Thomas,
>
> On Tue, Apr 07, 2026 at 10:54:17AM +0200, Thomas Gleixner wrote:
> > From: Thomas Gleixner <tglx@kernel.org>
> >
> > Calvin reported an odd NMI watchdog lockup which claims that the CPU locked
> > up in user space. He provided a reproducer, which sets up a timerfd based
> > timer and then rearms it in a loop with an absolute expiry time of 1ns.
> >
> > As the expiry time is in the past, the timer ends up as the first expiring
> > timer in the per CPU hrtimer base and the clockevent device is programmed
> > with the minimum delta value. If the machine is fast enough, this ends up
> > in a endless loop of programming the delta value to the minimum value
> > defined by the clock event device, before the timer interrupt can fire,
> > which starves the interrupt and consequently triggers the lockup detector
> > because the hrtimer callback of the lockup mechanism is never invoked.
> >
> > As a first step to prevent this, avoid reprogramming the clock event device
> > when:
> > - a forced minimum delta event is pending
> > - the new expiry delta is less then or equal to the minimum delta
>
> with this patch now in the tip tree my QEMU/virtme-ng based machine
> fails to boot. The startup seems to freeze in:
> start_kernel() -> rest_init() -> schedule_preempt_disabled() -> schedule()
>
> CONFIG_GENERIC_CLOCKEVENTS=y
> CONFIG_GENERIC_CLOCKEVENTS_BROADCAST=y
> CONFIG_GENERIC_CLOCKEVENTS_BROADCAST_IDLE=y
> CONFIG_GENERIC_CLOCKEVENTS_MIN_ADJUST=y
> CONFIG_HZ=1000
>
> CPU: i5-1135G7
> clock event device: lapic-deadline
>
> The clockevent device is still reprogrammed each millisecond,
> presumably for the tick.
>
> (...)
This fixes the issue for me:
--- a/kernel/time/clockevents.c
+++ b/kernel/time/clockevents.c
@@ -369,7 +369,7 @@ int clockevents_program_event(struct clock_event_device *dev, ktime_t expires, b
if (dev->next_event_forced)
return 0;
- if (dev->set_next_event(dev->min_delta_ticks, dev)) {
+ if (dev->set_next_event(dev->min_delta_ns, dev)) {
if (!force || clockevents_program_min_delta(dev))
return -ETIME;
}
Thomas
^ permalink raw reply
* Re: [patch 01/12] clockevents: Prevent timer interrupt starvation
From: Thomas Weißschuh @ 2026-04-08 12:41 UTC (permalink / raw)
To: Thomas Gleixner
Cc: LKML, Calvin Owens, Peter Zijlstra, Anna-Maria Behnsen,
Frederic Weisbecker, Ingo Molnar, John Stultz, Stephen Boyd,
Alexander Viro, Christian Brauner, Jan Kara, linux-fsdevel,
Sebastian Reichel, linux-pm, Pablo Neira Ayuso, Florian Westphal,
Phil Sutter, netfilter-devel, coreteam
In-Reply-To: <20260407083247.562657657@kernel.org>
Hi Thomas,
On Tue, Apr 07, 2026 at 10:54:17AM +0200, Thomas Gleixner wrote:
> From: Thomas Gleixner <tglx@kernel.org>
>
> Calvin reported an odd NMI watchdog lockup which claims that the CPU locked
> up in user space. He provided a reproducer, which sets up a timerfd based
> timer and then rearms it in a loop with an absolute expiry time of 1ns.
>
> As the expiry time is in the past, the timer ends up as the first expiring
> timer in the per CPU hrtimer base and the clockevent device is programmed
> with the minimum delta value. If the machine is fast enough, this ends up
> in a endless loop of programming the delta value to the minimum value
> defined by the clock event device, before the timer interrupt can fire,
> which starves the interrupt and consequently triggers the lockup detector
> because the hrtimer callback of the lockup mechanism is never invoked.
>
> As a first step to prevent this, avoid reprogramming the clock event device
> when:
> - a forced minimum delta event is pending
> - the new expiry delta is less then or equal to the minimum delta
with this patch now in the tip tree my QEMU/virtme-ng based machine
fails to boot. The startup seems to freeze in:
start_kernel() -> rest_init() -> schedule_preempt_disabled() -> schedule()
CONFIG_GENERIC_CLOCKEVENTS=y
CONFIG_GENERIC_CLOCKEVENTS_BROADCAST=y
CONFIG_GENERIC_CLOCKEVENTS_BROADCAST_IDLE=y
CONFIG_GENERIC_CLOCKEVENTS_MIN_ADJUST=y
CONFIG_HZ=1000
CPU: i5-1135G7
clock event device: lapic-deadline
The clockevent device is still reprogrammed each millisecond,
presumably for the tick.
(...)
Thomas
^ permalink raw reply
* [PATCH v11 11/14] sched: add need-resched timed wait interface
From: Ankur Arora @ 2026-04-08 12:25 UTC (permalink / raw)
To: linux-kernel, linux-arch, linux-arm-kernel, linux-pm, bpf
Cc: arnd, catalin.marinas, will, peterz, akpm, mark.rutland, harisokn,
cl, ast, rafael, daniel.lezcano, memxor, zhenglifeng1, xueshuai,
rdunlap, david.laight.linux, joao.m.martins, boris.ostrovsky,
konrad.wilk, ashok.bhat, Ankur Arora, Ingo Molnar
In-Reply-To: <20260408122538.3610871-1-ankur.a.arora@oracle.com>
Add tif_bitset_relaxed_wait() (and tif_need_resched_relaxed_wait()
which wraps it) which takes the thread_info bit and timeout duration
as parameters and waits until the bit is set or for the expiration
of the timeout.
The wait is implemented via smp_cond_load_relaxed_timeout().
smp_cond_load_relaxed_timeout() essentially provides the pattern used
in poll_idle() where we spin in a loop waiting for the flag to change
until a timeout occurs.
tif_need_resched_relaxed_wait() allows us to abstract out the internals
of waiting, scheduler specific details etc.
Placed in linux/sched/idle.h instead of linux/thread_info.h to work
around recursive include hell.
Cc: Ingo Molnar <mingo@kernel.org>
Cc: Peter Zijlstra <peterz@infradead.org>
Cc: Rafael J. Wysocki <rafael@kernel.org>
Cc: Daniel Lezcano <daniel.lezcano@linaro.org>
Cc: linux-pm@vger.kernel.org
Signed-off-by: Ankur Arora <ankur.a.arora@oracle.com>
---
include/linux/sched/idle.h | 29 +++++++++++++++++++++++++++++
1 file changed, 29 insertions(+)
diff --git a/include/linux/sched/idle.h b/include/linux/sched/idle.h
index 8465ff1f20d1..ddee9b019895 100644
--- a/include/linux/sched/idle.h
+++ b/include/linux/sched/idle.h
@@ -3,6 +3,7 @@
#define _LINUX_SCHED_IDLE_H
#include <linux/sched.h>
+#include <linux/sched/clock.h>
enum cpu_idle_type {
__CPU_NOT_IDLE = 0,
@@ -113,4 +114,32 @@ static __always_inline void current_clr_polling(void)
}
#endif
+/*
+ * Caller needs to make sure that the thread context cannot be preempted
+ * or migrated, so current_thread_info() cannot change from under us.
+ *
+ * This also allows us to safely stay in the local_clock domain.
+ */
+static __always_inline bool tif_bitset_relaxed_wait(int tif, u64 timeout_ns)
+{
+ unsigned long flags;
+
+ flags = smp_cond_load_relaxed_timeout(¤t_thread_info()->flags,
+ (VAL & BIT(tif)),
+ local_clock_noinstr(),
+ timeout_ns);
+ return flags & BIT(tif);
+}
+
+/**
+ * tif_need_resched_relaxed_wait() - Wait for need-resched being set
+ * with no ordering guarantees until a timeout expires.
+ *
+ * @timeout_ns: timeout value.
+ */
+static __always_inline bool tif_need_resched_relaxed_wait(u64 timeout_ns)
+{
+ return tif_bitset_relaxed_wait(TIF_NEED_RESCHED, timeout_ns);
+}
+
#endif /* _LINUX_SCHED_IDLE_H */
--
2.31.1
^ permalink raw reply related
* [PATCH v11 09/14] bpf/rqspinlock: switch check_timeout() to a clock interface
From: Ankur Arora @ 2026-04-08 12:25 UTC (permalink / raw)
To: linux-kernel, linux-arch, linux-arm-kernel, linux-pm, bpf
Cc: arnd, catalin.marinas, will, peterz, akpm, mark.rutland, harisokn,
cl, ast, rafael, daniel.lezcano, memxor, zhenglifeng1, xueshuai,
rdunlap, david.laight.linux, joao.m.martins, boris.ostrovsky,
konrad.wilk, ashok.bhat, Ankur Arora
In-Reply-To: <20260408122538.3610871-1-ankur.a.arora@oracle.com>
check_timeout() gets the current time value and depending on how
much time has passed, checks for deadlock or times out, returning 0
or -errno on deadlock or timeout.
Switch this out to a clock style interface, where it functions as a
clock in the "lock-domain", returning the current time until a
deadlock or timeout occurs. Once a deadlock or timeout has occurred,
it stops functioning as a clock and returns error.
Also adjust the RES_CHECK_TIMEOUT macro to discard the clock value
when updating the explicit return status.
Cc: bpf@vger.kernel.org
Cc: Kumar Kartikeya Dwivedi <memxor@gmail.com>
Cc: Alexei Starovoitov <ast@kernel.org>
Acked-by: Kumar Kartikeya Dwivedi <memxor@gmail.com>
Signed-off-by: Ankur Arora <ankur.a.arora@oracle.com>
---
kernel/bpf/rqspinlock.c | 45 +++++++++++++++++++++++++++--------------
1 file changed, 30 insertions(+), 15 deletions(-)
diff --git a/kernel/bpf/rqspinlock.c b/kernel/bpf/rqspinlock.c
index e4e338cdb437..0ec17ebb67c1 100644
--- a/kernel/bpf/rqspinlock.c
+++ b/kernel/bpf/rqspinlock.c
@@ -196,8 +196,12 @@ static noinline int check_deadlock_ABBA(rqspinlock_t *lock, u32 mask)
return 0;
}
-static noinline int check_timeout(rqspinlock_t *lock, u32 mask,
- struct rqspinlock_timeout *ts)
+/*
+ * Returns current monotonic time in ns on success or, negative errno
+ * value on failure due to timeout expiration or detection of deadlock.
+ */
+static noinline s64 clock_deadlock(rqspinlock_t *lock, u32 mask,
+ struct rqspinlock_timeout *ts)
{
u64 prev = ts->cur;
u64 time;
@@ -207,7 +211,7 @@ static noinline int check_timeout(rqspinlock_t *lock, u32 mask,
return -EDEADLK;
ts->cur = ktime_get_mono_fast_ns();
ts->timeout_end = ts->cur + ts->duration;
- return 0;
+ return (s64)ts->cur;
}
time = ktime_get_mono_fast_ns();
@@ -219,11 +223,15 @@ static noinline int check_timeout(rqspinlock_t *lock, u32 mask,
* checks.
*/
if (prev + NSEC_PER_MSEC < time) {
+ int ret;
ts->cur = time;
- return check_deadlock_ABBA(lock, mask);
+ ret = check_deadlock_ABBA(lock, mask);
+ if (ret)
+ return ret;
+
}
- return 0;
+ return (s64)time;
}
/*
@@ -231,15 +239,22 @@ static noinline int check_timeout(rqspinlock_t *lock, u32 mask,
* as the macro does internal amortization for us.
*/
#ifndef res_smp_cond_load_acquire
-#define RES_CHECK_TIMEOUT(ts, ret, mask) \
- ({ \
- if (!(ts).spin++) \
- (ret) = check_timeout((lock), (mask), &(ts)); \
- (ret); \
+#define RES_CHECK_TIMEOUT(ts, ret, mask) \
+ ({ \
+ s64 __timeval_err = 0; \
+ if (!(ts).spin++) \
+ __timeval_err = clock_deadlock((lock), (mask), &(ts)); \
+ (ret) = __timeval_err < 0 ? __timeval_err : 0; \
+ __timeval_err; \
})
#else
-#define RES_CHECK_TIMEOUT(ts, ret, mask) \
- ({ (ret) = check_timeout((lock), (mask), &(ts)); })
+#define RES_CHECK_TIMEOUT(ts, ret, mask) \
+ ({ \
+ s64 __timeval_err; \
+ __timeval_err = clock_deadlock((lock), (mask), &(ts)); \
+ (ret) = __timeval_err < 0 ? __timeval_err : 0; \
+ __timeval_err; \
+ })
#endif
/*
@@ -281,7 +296,7 @@ int __lockfunc resilient_tas_spin_lock(rqspinlock_t *lock)
val = atomic_read(&lock->val);
if (val || !atomic_try_cmpxchg(&lock->val, &val, 1)) {
- if (RES_CHECK_TIMEOUT(ts, ret, ~0u))
+ if (RES_CHECK_TIMEOUT(ts, ret, ~0u) < 0)
goto out;
cpu_relax();
goto retry;
@@ -406,7 +421,7 @@ int __lockfunc resilient_queued_spin_lock_slowpath(rqspinlock_t *lock, u32 val)
*/
if (val & _Q_LOCKED_MASK) {
RES_RESET_TIMEOUT(ts, RES_DEF_TIMEOUT);
- res_smp_cond_load_acquire(&lock->locked, !VAL || RES_CHECK_TIMEOUT(ts, ret, _Q_LOCKED_MASK));
+ res_smp_cond_load_acquire(&lock->locked, !VAL || RES_CHECK_TIMEOUT(ts, ret, _Q_LOCKED_MASK) < 0);
}
if (ret) {
@@ -568,7 +583,7 @@ int __lockfunc resilient_queued_spin_lock_slowpath(rqspinlock_t *lock, u32 val)
*/
RES_RESET_TIMEOUT(ts, RES_DEF_TIMEOUT * 2);
val = res_atomic_cond_read_acquire(&lock->val, !(VAL & _Q_LOCKED_PENDING_MASK) ||
- RES_CHECK_TIMEOUT(ts, ret, _Q_LOCKED_PENDING_MASK));
+ RES_CHECK_TIMEOUT(ts, ret, _Q_LOCKED_PENDING_MASK) < 0);
/* Disable queue destruction when we detect deadlocks. */
if (ret == -EDEADLK) {
--
2.31.1
^ permalink raw reply related
* [PATCH v11 02/14] arm64: barrier: Support smp_cond_load_relaxed_timeout()
From: Ankur Arora @ 2026-04-08 12:25 UTC (permalink / raw)
To: linux-kernel, linux-arch, linux-arm-kernel, linux-pm, bpf
Cc: arnd, catalin.marinas, will, peterz, akpm, mark.rutland, harisokn,
cl, ast, rafael, daniel.lezcano, memxor, zhenglifeng1, xueshuai,
rdunlap, david.laight.linux, joao.m.martins, boris.ostrovsky,
konrad.wilk, ashok.bhat, Ankur Arora
In-Reply-To: <20260408122538.3610871-1-ankur.a.arora@oracle.com>
Support waiting in smp_cond_load_relaxed_timeout() via
__cmpwait_relaxed(). To ensure that we wake from waiting in
WFE periodically and don't block forever if there are no stores
to ptr, this path is only used when the event-stream is enabled.
Note that when using __cmpwait_relaxed() we ignore the timeout
value, allowing an overshoot by up to the event-stream period.
And, in the unlikely event that the event-stream is unavailable,
fallback to spin-waiting.
Also set SMP_TIMEOUT_POLL_COUNT to 1 so we do the time-check in
each iteration of smp_cond_load_relaxed_timeout().
And finally define ARCH_HAS_CPU_RELAX to indicate that we have
an optimized implementation of cpu_poll_relax().
Cc: Arnd Bergmann <arnd@arndb.de>
Cc: Will Deacon <will@kernel.org>
Cc: Catalin Marinas <catalin.marinas@arm.com>
Cc: linux-arm-kernel@lists.infradead.org
Suggested-by: Will Deacon <will@kernel.org>
Acked-by: Will Deacon <will@kernel.org>
Reviewed-by: Catalin Marinas <catalin.marinas@arm.com>
Signed-off-by: Ankur Arora <ankur.a.arora@oracle.com>
---
arch/arm64/Kconfig | 3 +++
arch/arm64/include/asm/barrier.h | 21 +++++++++++++++++++++
2 files changed, 24 insertions(+)
diff --git a/arch/arm64/Kconfig b/arch/arm64/Kconfig
index 9ea19b74b6c3..e3ce08276e9b 100644
--- a/arch/arm64/Kconfig
+++ b/arch/arm64/Kconfig
@@ -1628,6 +1628,9 @@ config ARCH_SUPPORTS_CRASH_DUMP
config ARCH_DEFAULT_CRASH_DUMP
def_bool y
+config ARCH_HAS_CPU_RELAX
+ def_bool y
+
config ARCH_HAS_GENERIC_CRASHKERNEL_RESERVATION
def_bool CRASH_RESERVE
diff --git a/arch/arm64/include/asm/barrier.h b/arch/arm64/include/asm/barrier.h
index 9495c4441a46..6190e178db51 100644
--- a/arch/arm64/include/asm/barrier.h
+++ b/arch/arm64/include/asm/barrier.h
@@ -12,6 +12,7 @@
#include <linux/kasan-checks.h>
#include <asm/alternative-macros.h>
+#include <asm/vdso/processor.h>
#define __nops(n) ".rept " #n "\nnop\n.endr\n"
#define nops(n) asm volatile(__nops(n))
@@ -219,6 +220,26 @@ do { \
(typeof(*ptr))VAL; \
})
+/* Re-declared here to avoid include dependency. */
+extern bool arch_timer_evtstrm_available(void);
+
+/*
+ * In the common case, cpu_poll_relax() sits waiting in __cmpwait_relaxed()
+ * for the ptr value to change.
+ *
+ * Since this period is reasonably long, choose SMP_TIMEOUT_POLL_COUNT
+ * to be 1, so smp_cond_load_{relaxed,acquire}_timeout() does a
+ * time-check in each iteration.
+ */
+#define SMP_TIMEOUT_POLL_COUNT 1
+
+#define cpu_poll_relax(ptr, val, timeout_ns) do { \
+ if (arch_timer_evtstrm_available()) \
+ __cmpwait_relaxed(ptr, val); \
+ else \
+ cpu_relax(); \
+} while (0)
+
#include <asm-generic/barrier.h>
#endif /* __ASSEMBLER__ */
--
2.31.1
^ permalink raw reply related
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox