* [PATCH 1/3] intel_idle: Add constants for MSR_PKG_CST_CONFIG_CONTROL
2026-04-25 7:25 [PATCH 0/3] intel_idle: Drop C-states redundant when PC6 is disabled Artem Bityutskiy
@ 2026-04-25 7:25 ` Artem Bityutskiy
2026-04-25 7:25 ` [PATCH] intel_idle: Add Panther Lake C-states table Artem Bityutskiy
` (2 subsequent siblings)
3 siblings, 0 replies; 6+ messages in thread
From: Artem Bityutskiy @ 2026-04-25 7:25 UTC (permalink / raw)
To: Rafael J. Wysocki; +Cc: Linux PM Mailing List
From: Artem Bityutskiy <artem.bityutskiy@linux.intel.com>
Add two constants for the package C-state limit fields in
MSR_PKG_CST_CONFIG_CONTROL.
The SKX_ prefix stands for "Skylake Xeon" and makes it explicit that
the mask is CPU model-specific. The same values have applied to all
Xeon platforms starting from SKX.
Reviewed-by: Andy Shevchenko <andriy.shevchenko@intel.com>
Signed-off-by: Artem Bityutskiy <artem.bityutskiy@linux.intel.com>
---
drivers/idle/intel_idle.c | 9 +++++++--
1 file changed, 7 insertions(+), 2 deletions(-)
diff --git a/drivers/idle/intel_idle.c b/drivers/idle/intel_idle.c
index f49c939d636f4..49894c6c7f20b 100644
--- a/drivers/idle/intel_idle.c
+++ b/drivers/idle/intel_idle.c
@@ -81,6 +81,11 @@ static bool ibrs_off __read_mostly;
/* Maximum allowed C-state target residency */
#define MAX_CMDLINE_RESIDENCY_US (100 * USEC_PER_MSEC)
+/* The Package C-State Limit bits in MSR_PKG_CST_CONFIG_CONTROL */
+#define SKX_PKG_CST_LIMIT_MASK GENMASK(2, 0)
+/* PC6 is enabled when Package C-State Limit >= this value */
+#define SKX_PKG_CST_LIMIT_PC6 2
+
static char cmdline_table_str[MAX_CMDLINE_TABLE_LEN] __read_mostly;
static struct cpuidle_device __percpu *intel_idle_cpuidle_devices;
@@ -2048,7 +2053,7 @@ static void __init skx_idle_state_table_update(void)
* 011b: C6 (retention)
* 111b: No Package C state limits.
*/
- if ((msr & 0x7) < 2) {
+ if ((msr & SKX_PKG_CST_LIMIT_MASK) < SKX_PKG_CST_LIMIT_PC6) {
/*
* Uses the CC6 + PC0 latency and 3 times of
* latency for target_residency if the PC6
@@ -2076,7 +2081,7 @@ static void __init spr_idle_state_table_update(void)
rdmsrq(MSR_PKG_CST_CONFIG_CONTROL, msr);
/* Limit value 2 and above allow for PC6. */
- if ((msr & 0x7) < 2) {
+ if ((msr & SKX_PKG_CST_LIMIT_MASK) < SKX_PKG_CST_LIMIT_PC6) {
spr_cstates[2].exit_latency = 190;
spr_cstates[2].target_residency = 600;
}
--
2.53.0
^ permalink raw reply related [flat|nested] 6+ messages in thread* [PATCH] intel_idle: Add Panther Lake C-states table
2026-04-25 7:25 [PATCH 0/3] intel_idle: Drop C-states redundant when PC6 is disabled Artem Bityutskiy
2026-04-25 7:25 ` [PATCH 1/3] intel_idle: Add constants for MSR_PKG_CST_CONFIG_CONTROL Artem Bityutskiy
@ 2026-04-25 7:25 ` Artem Bityutskiy
2026-04-25 7:27 ` Artem Bityutskiy
2026-04-25 7:25 ` [PATCH 2/3] intel_idle: Introduce a helper for checking PC6 Artem Bityutskiy
2026-04-25 7:25 ` [PATCH 3/3] intel_idle: Drop C-states redundant when PC6 is disabled Artem Bityutskiy
3 siblings, 1 reply; 6+ messages in thread
From: Artem Bityutskiy @ 2026-04-25 7:25 UTC (permalink / raw)
To: Rafael J. Wysocki; +Cc: Linux PM Mailing List
From: Artem Bityutskiy <artem.bityutskiy@linux.intel.com>
Panther Lake supports the following requestable C-states: C1, C1E, C6S, C10.
Add a custom table for them.
Signed-off-by: Artem Bityutskiy <artem.bityutskiy@linux.intel.com>
---
drivers/idle/intel_idle.c | 42 +++++++++++++++++++++++++++++++++++++++
1 file changed, 42 insertions(+)
Based on latency analysis using the 'wult' tool, and power break even
analysis using the 'pbe' tool.
diff --git a/drivers/idle/intel_idle.c b/drivers/idle/intel_idle.c
index 167eb72105baf..36f5766381172 100644
--- a/drivers/idle/intel_idle.c
+++ b/drivers/idle/intel_idle.c
@@ -994,6 +994,43 @@ static struct cpuidle_state mtl_l_cstates[] __initdata = {
.enter = NULL }
};
+static struct cpuidle_state ptl_cstates[] __initdata = {
+ {
+ .name = "C1",
+ .desc = "MWAIT 0x00",
+ .flags = MWAIT2flg(0x00),
+ .exit_latency = 1,
+ .target_residency = 1,
+ .enter = &intel_idle,
+ .enter_s2idle = intel_idle_s2idle, },
+ {
+ .name = "C1E",
+ .desc = "MWAIT 0x01",
+ .flags = MWAIT2flg(0x01) | CPUIDLE_FLAG_ALWAYS_ENABLE,
+ .exit_latency = 10,
+ .target_residency = 10,
+ .enter = &intel_idle,
+ .enter_s2idle = intel_idle_s2idle, },
+ {
+ .name = "C6S",
+ .desc = "MWAIT 0x21",
+ .flags = MWAIT2flg(0x21) | CPUIDLE_FLAG_TLB_FLUSHED,
+ .exit_latency = 300,
+ .target_residency = 300,
+ .enter = &intel_idle,
+ .enter_s2idle = intel_idle_s2idle, },
+ {
+ .name = "C10",
+ .desc = "MWAIT 0x60",
+ .flags = MWAIT2flg(0x60) | CPUIDLE_FLAG_TLB_FLUSHED,
+ .exit_latency = 370,
+ .target_residency = 2500,
+ .enter = &intel_idle,
+ .enter_s2idle = intel_idle_s2idle, },
+ {
+ .enter = NULL }
+};
+
static struct cpuidle_state gmt_cstates[] __initdata = {
{
.name = "C1",
@@ -1623,6 +1660,10 @@ static const struct idle_cpu idle_cpu_mtl_l __initconst = {
.state_table = mtl_l_cstates,
};
+static const struct idle_cpu idle_cpu_ptl __initconst = {
+ .state_table = ptl_cstates,
+};
+
static const struct idle_cpu idle_cpu_gmt __initconst = {
.state_table = gmt_cstates,
};
@@ -1737,6 +1778,7 @@ static const struct x86_cpu_id intel_idle_ids[] __initconst = {
X86_MATCH_VFM(INTEL_ALDERLAKE, &idle_cpu_adl),
X86_MATCH_VFM(INTEL_ALDERLAKE_L, &idle_cpu_adl_l),
X86_MATCH_VFM(INTEL_METEORLAKE_L, &idle_cpu_mtl_l),
+ X86_MATCH_VFM(INTEL_PANTHERLAKE_L, &idle_cpu_ptl),
X86_MATCH_VFM(INTEL_ATOM_GRACEMONT, &idle_cpu_gmt),
X86_MATCH_VFM(INTEL_SAPPHIRERAPIDS_X, &idle_cpu_spr),
X86_MATCH_VFM(INTEL_EMERALDRAPIDS_X, &idle_cpu_spr),
--
2.53.0
^ permalink raw reply related [flat|nested] 6+ messages in thread* Re: [PATCH] intel_idle: Add Panther Lake C-states table
2026-04-25 7:25 ` [PATCH] intel_idle: Add Panther Lake C-states table Artem Bityutskiy
@ 2026-04-25 7:27 ` Artem Bityutskiy
0 siblings, 0 replies; 6+ messages in thread
From: Artem Bityutskiy @ 2026-04-25 7:27 UTC (permalink / raw)
To: Rafael J. Wysocki; +Cc: Linux PM Mailing List
On Sat, 2026-04-25 at 10:25 +0300, Artem Bityutskiy wrote:
> From: Artem Bityutskiy <artem.bityutskiy@linux.intel.com>
>
> Panther Lake supports the following requestable C-states: C1, C1E, C6S, C10.
> Add a custom table for them.
>
> Signed-off-by: Artem Bityutskiy <artem.bityutskiy@linux.intel.com>
> ---
> drivers/idle/intel_idle.c | 42 +++++++++++++++++++++++++++++++++++++++
> 1 file changed, 42 insertions(+)
Oh I apologize for sending this one, it does not belong to the series,
I did not noticed that I copied to the same directory. Please, ignore
this patch.
Artem.
^ permalink raw reply [flat|nested] 6+ messages in thread
* [PATCH 2/3] intel_idle: Introduce a helper for checking PC6
2026-04-25 7:25 [PATCH 0/3] intel_idle: Drop C-states redundant when PC6 is disabled Artem Bityutskiy
2026-04-25 7:25 ` [PATCH 1/3] intel_idle: Add constants for MSR_PKG_CST_CONFIG_CONTROL Artem Bityutskiy
2026-04-25 7:25 ` [PATCH] intel_idle: Add Panther Lake C-states table Artem Bityutskiy
@ 2026-04-25 7:25 ` Artem Bityutskiy
2026-04-25 7:25 ` [PATCH 3/3] intel_idle: Drop C-states redundant when PC6 is disabled Artem Bityutskiy
3 siblings, 0 replies; 6+ messages in thread
From: Artem Bityutskiy @ 2026-04-25 7:25 UTC (permalink / raw)
To: Rafael J. Wysocki; +Cc: Linux PM Mailing List
From: Artem Bityutskiy <artem.bityutskiy@linux.intel.com>
Introduce the skx_is_pc6_disabled() for checking if PC6 is disabled and
switch the following functions to use it:
- skx_idle_state_table_update()
- spr_idle_state_table_update()
At the same time, clean them up improving the commentary and moving it to
the function kernel-doc.
Purely a clean up, no functional changes intended.
Reviewed-by: Andy Shevchenko <andriy.shevchenko@intel.com>
Signed-off-by: Artem Bityutskiy <artem.bityutskiy@linux.intel.com>
---
drivers/idle/intel_idle.c | 48 +++++++++++++++++++--------------------
1 file changed, 24 insertions(+), 24 deletions(-)
diff --git a/drivers/idle/intel_idle.c b/drivers/idle/intel_idle.c
index 49894c6c7f20b..562f8e27256e7 100644
--- a/drivers/idle/intel_idle.c
+++ b/drivers/idle/intel_idle.c
@@ -2037,12 +2037,13 @@ static void __init sklh_idle_state_table_update(void)
}
/**
- * skx_idle_state_table_update - Adjust the Sky Lake/Cascade Lake
- * idle states table.
+ * skx_is_pc6_disabled() - Check if PC6 is disabled in BIOS.
+ *
+ * Return: %true if PC6 is disabled, %false otherwise.
*/
-static void __init skx_idle_state_table_update(void)
+static bool __init skx_is_pc6_disabled(void)
{
- unsigned long long msr;
+ u64 msr;
rdmsrq(MSR_PKG_CST_CONFIG_CONTROL, msr);
@@ -2053,35 +2054,34 @@ static void __init skx_idle_state_table_update(void)
* 011b: C6 (retention)
* 111b: No Package C state limits.
*/
- if ((msr & SKX_PKG_CST_LIMIT_MASK) < SKX_PKG_CST_LIMIT_PC6) {
- /*
- * Uses the CC6 + PC0 latency and 3 times of
- * latency for target_residency if the PC6
- * is disabled in BIOS. This is consistent
- * with how intel_idle driver uses _CST
- * to set the target_residency.
- */
+ return (msr & SKX_PKG_CST_LIMIT_MASK) < SKX_PKG_CST_LIMIT_PC6;
+}
+
+/**
+ * skx_idle_state_table_update - Adjust the SKX/CLX idle states table.
+ *
+ * Adjust Sky Lake or Cascade Lake Xeon idle states if PC6 is disabled in BIOS.
+ * Use the CC6 + PC0 latency and 3 times of that latency for target_residency.
+ * This is consistent with how the intel_idle driver uses _CST to set the
+ * target_residency.
+ */
+static void __init skx_idle_state_table_update(void)
+{
+ if (skx_is_pc6_disabled()) {
skx_cstates[2].exit_latency = 92;
skx_cstates[2].target_residency = 276;
}
}
/**
- * spr_idle_state_table_update - Adjust Sapphire Rapids idle states table.
+ * spr_idle_state_table_update - Adjust Sapphire Rapids Xeon idle states table.
+ *
+ * By default, the C6 state assumes the worst-case scenario of package C6.
+ * However, if PC6 is disabled in BIOS, update the numbers to match core C6.
*/
static void __init spr_idle_state_table_update(void)
{
- unsigned long long msr;
-
- /*
- * By default, the C6 state assumes the worst-case scenario of package
- * C6. However, if PC6 is disabled, we update the numbers to match
- * core C6.
- */
- rdmsrq(MSR_PKG_CST_CONFIG_CONTROL, msr);
-
- /* Limit value 2 and above allow for PC6. */
- if ((msr & SKX_PKG_CST_LIMIT_MASK) < SKX_PKG_CST_LIMIT_PC6) {
+ if (skx_is_pc6_disabled()) {
spr_cstates[2].exit_latency = 190;
spr_cstates[2].target_residency = 600;
}
--
2.53.0
^ permalink raw reply related [flat|nested] 6+ messages in thread* [PATCH 3/3] intel_idle: Drop C-states redundant when PC6 is disabled
2026-04-25 7:25 [PATCH 0/3] intel_idle: Drop C-states redundant when PC6 is disabled Artem Bityutskiy
` (2 preceding siblings ...)
2026-04-25 7:25 ` [PATCH 2/3] intel_idle: Introduce a helper for checking PC6 Artem Bityutskiy
@ 2026-04-25 7:25 ` Artem Bityutskiy
3 siblings, 0 replies; 6+ messages in thread
From: Artem Bityutskiy @ 2026-04-25 7:25 UTC (permalink / raw)
To: Rafael J. Wysocki; +Cc: Linux PM Mailing List
From: Artem Bityutskiy <artem.bityutskiy@linux.intel.com>
On modern Xeon platforms, such as Granite Rapids, Sierra Forest, and
Clearwater Forest, there are two flavors of requestable C6 states: C6
and C6P. C6 allows only core C6 (CC6), while C6P allows both CC6 and
package C6 (PC6). PC6 saves more power but also has a higher exit
latency, so many users disable it in BIOS.
When PC6 is disabled, C6P becomes identical to C6 — the CPU treats C6P
requests as C6 requests. Exposing both C6 and C6P to user space in this
situation is confusing: two states with the same name look different but
behave the same. It also adds unnecessary overhead to the cpuidle
subsystem, which is a fast path: the governor evaluates every registered
state on idle entry.
Drop C-states that are redundant when PC6 is disabled by marking them
with CPUIDLE_FLAG_UNUSABLE, which causes cpuidle to exclude them when
registering idle states.
Signed-off-by: Artem Bityutskiy <artem.bityutskiy@linux.intel.com>
---
drivers/idle/intel_idle.c | 53 +++++++++++++++++++++++++++++++++++++++
1 file changed, 53 insertions(+)
diff --git a/drivers/idle/intel_idle.c b/drivers/idle/intel_idle.c
index 562f8e27256e7..f052564eb308b 100644
--- a/drivers/idle/intel_idle.c
+++ b/drivers/idle/intel_idle.c
@@ -2087,6 +2087,53 @@ static void __init spr_idle_state_table_update(void)
}
}
+/**
+ * drop_pc6_redundant_cstates() - Drop C-states redundant when PC6 is disabled.
+ * @states: Idle states table to modify.
+ *
+ * When PC6 is disabled in BIOS, C-states that exist solely to enable PC6
+ * entry (such as C6P or C6SP) become identical to shallower C-states like
+ * C6, and are therefore redundant. Should be called only on systems with
+ * multiple C6 flavors.
+ */
+static void __init drop_pc6_redundant_cstates(struct cpuidle_state *states)
+{
+ int count;
+
+ if (!skx_is_pc6_disabled())
+ /* PC6 is not disabled, nothing to do */
+ return;
+
+ for (count = 0; states[count].enter; count++)
+ continue;
+
+ if (count < 2) {
+ pr_debug("Too few idle states to drop PC6-redundant states\n");
+ return;
+ }
+
+ /*
+ * Sanity check: At this point all platforms with multiple C6 flavors
+ * use the CPUIDLE_FLAG_PARTIAL_HINT_MATCH flag. And the last state in
+ * the table is the one that becomes redundant when PC6 is disabled.
+ */
+ if (!(states[count - 1].flags & CPUIDLE_FLAG_PARTIAL_HINT_MATCH)) {
+ pr_debug("Can't drop PC6-redundant states: unexpected flags\n");
+ return;
+ }
+
+ /*
+ * On all current platforms with multiple C6 flavors, there is only one
+ * C-state that becomes redundant when PC6 is disabled. This state is
+ * the last one in the table. Drop it by marking it with
+ * CPUIDLE_FLAG_UNUSABLE so that cpuidle excludes it when registering
+ * idle states.
+ */
+ pr_info("Dropping idle state %s because PC6 is disabled\n",
+ states[count - 1].name);
+ states[count - 1].flags |= CPUIDLE_FLAG_UNUSABLE;
+}
+
/**
* byt_cht_auto_demotion_disable - Disable Bay/Cherry Trail auto-demotion.
*/
@@ -2176,6 +2223,12 @@ static void __init intel_idle_init_cstates_icpu(struct cpuidle_driver *drv)
case INTEL_ATOM_AIRMONT:
byt_cht_auto_demotion_disable();
break;
+ case INTEL_GRANITERAPIDS_D:
+ case INTEL_GRANITERAPIDS_X:
+ case INTEL_ATOM_CRESTMONT_X:
+ case INTEL_ATOM_DARKMONT_X:
+ drop_pc6_redundant_cstates(cpuidle_state_table);
+ break;
}
for (cstate = 0; cstate < CPUIDLE_STATE_MAX; ++cstate) {
--
2.53.0
^ permalink raw reply related [flat|nested] 6+ messages in thread