diff options
| author | Rafael J. Wysocki <rafael.j.wysocki@intel.com> | 2026-07-09 14:44:23 +0200 |
|---|---|---|
| committer | Rafael J. Wysocki <rafael.j.wysocki@intel.com> | 2026-07-17 11:10:10 +0200 |
| commit | abc5c103c66d9b8102605e63ad0ebb4cadc58995 (patch) | |
| tree | 7e24d912be16e5c02feb6b44fafe90e2ffd78c4b | |
| parent | 4d7821961f788615ef6fe0ad6d8a034579800e61 (diff) | |
| download | linux-next-abc5c103c66d9b8102605e63ad0ebb4cadc58995.tar.gz linux-next-abc5c103c66d9b8102605e63ad0ebb4cadc58995.zip | |
intel_idle: Add ACPI _LPI support
Allow intel_idle to use idle states information coming from ACPI _LPI
objects by making it call acpi_processor_extract_lpi_info() and, if
that is successful, using the list of idle states produced by that
function instead of the one coming from acpi_processor_evaluate_cst().
Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
Link: https://patch.msgid.link/2280567.Icojqenx9y@rafael.j.wysocki
| -rw-r--r-- | drivers/idle/intel_idle.c | 160 |
1 files changed, 138 insertions, 22 deletions
diff --git a/drivers/idle/intel_idle.c b/drivers/idle/intel_idle.c index fedaa8142121..bcc2725769c5 100644 --- a/drivers/idle/intel_idle.c +++ b/drivers/idle/intel_idle.c @@ -1779,6 +1779,7 @@ module_param_named(no_native, no_native, bool, 0444); MODULE_PARM_DESC(no_native, "Ignore cpu specific (native) idle states in lieu of ACPI idle states"); static struct acpi_processor_power acpi_state_table __initdata; +static bool acpi_lpi_available __initdata; /** * intel_idle_cst_usable - Check if the _CST information can be used. @@ -1803,18 +1804,37 @@ static bool __init intel_idle_cst_usable(void) return true; } -static bool __init intel_idle_acpi_cst_extract(void) +static bool __init intel_idle_acpi_extract_lpi_cstates(void) { unsigned int cpu; - if (no_acpi) { - pr_debug("Not allowed to use ACPI _CST\n"); - return false; + for_each_possible_cpu(cpu) { + struct acpi_processor *pr; + + pr = per_cpu(processors, cpu); + if (!pr) + continue; + + if (acpi_processor_extract_lpi_info(pr->handle, + &acpi_state_table, true)) + continue; + + acpi_lpi_available = true; + return true; } + pr_debug("No ACPI _LPI idle states\n"); + return false; +} + +static bool __init intel_idle_acpi_extract_cst_cstates(void) +{ + unsigned int cpu; + for_each_possible_cpu(cpu) { - struct acpi_processor *pr = per_cpu(processors, cpu); + struct acpi_processor *pr; + pr = per_cpu(processors, cpu); if (!pr) continue; @@ -1826,14 +1846,36 @@ static bool __init intel_idle_acpi_cst_extract(void) if (!intel_idle_cst_usable()) continue; - if (!acpi_processor_claim_cst_control()) - break; + return true; + } + + pr_debug("ACPI _CST not found or not usable\n"); + return false; +} +static bool __init intel_idle_acpi_extract_cstates(void) +{ + if (intel_idle_acpi_extract_lpi_cstates()) + return true; + + if (intel_idle_acpi_extract_cst_cstates()) return true; + + return false; +} + +static bool __init intel_idle_acpi_probe(void) +{ + if (no_acpi) { + pr_debug("Not allowed to use ACPI for C-states extraction\n"); + return false; } + if (intel_idle_acpi_extract_cstates() && + acpi_processor_claim_cst_control()) + return true; + acpi_state_table.count = 0; - pr_debug("ACPI _CST not found or not usable\n"); return false; } @@ -1847,7 +1889,53 @@ static void __init intel_idle_complete_state_init(struct cpuidle_state *state) state->enter_s2idle = intel_idle_s2idle; } -static void __init intel_idle_init_cstates_acpi(struct cpuidle_driver *drv) +static void __init intel_idle_init_cstates_acpi_lpi(struct cpuidle_driver *drv) +{ + int index; + + for (index = 0; index < acpi_state_table.count; index++) { + struct acpi_lpi_state *lpi_state; + struct cpuidle_state *state; + + if (intel_idle_max_cstate_reached(index)) + break; + + lpi_state = &acpi_state_table.lpi_states[index]; + + state = &drv->states[drv->state_count++]; + + scnprintf(state->name, CPUIDLE_NAME_LEN, "C%d_LPI", index + 1); + strscpy(state->desc, lpi_state->desc, CPUIDLE_DESC_LEN); + state->exit_latency = lpi_state->wake_latency; + state->target_residency = lpi_state->min_residency; + state->flags = MWAIT2flg(lpi_state->address); + /* + * Assume that entering any of the idle states extracted from + * _LPI except for the first two will cause the TLB to be + * flushed and let the core call leave_mm() for them upfront + * to avoid unnecessary wakeups due to TLB shootdowns. + */ + if (index > 1) + state->flags |= CPUIDLE_FLAG_TLB_FLUSHED; + + if (disabled_states_mask & BIT(index + 1)) + state->flags |= CPUIDLE_FLAG_OFF; + + intel_idle_complete_state_init(state); + + pr_info("%s: MWAIT hint 0x%x\n", state->name, flg2MWAIT(state->flags)); + } + + /* + * Assume the first idle state in the table to be C1 and if any deeper + * idle states are exposed while X86_FEATURE_NONSTOP_TSC is unset, mark + * the TSC as unstable. + */ + if (index > 1 && !boot_cpu_has(X86_FEATURE_NONSTOP_TSC)) + mark_tsc_unstable("TSC halts in idle"); +} + +static void __init intel_idle_init_cstates_acpi_cst(struct cpuidle_driver *drv) { int cstate, limit = min_t(int, CPUIDLE_STATE_MAX, acpi_state_table.count); @@ -1896,6 +1984,14 @@ static void __init intel_idle_init_cstates_acpi(struct cpuidle_driver *drv) } } +static void __init intel_idle_init_cstates_acpi(struct cpuidle_driver *drv) +{ + if (acpi_lpi_available) + intel_idle_init_cstates_acpi_lpi(drv); + else + intel_idle_init_cstates_acpi_cst(drv); +} + static bool __init intel_idle_acpi_hint_match(unsigned int flags, u32 acpi_hint, u32 table_hint) { @@ -1906,18 +2002,23 @@ static bool __init intel_idle_acpi_hint_match(unsigned int flags, u32 acpi_hint, return acpi_hint == table_hint; } -static bool __init intel_idle_off_by_default(unsigned int flags, u32 mwait_hint) +static bool __init intel_idle_off_by_default_lpi(unsigned int flags, u32 mwait_hint) { - int cstate, limit; + int index; - /* - * If there are no _CST C-states, do not disable any C-states by - * default. - */ - if (!acpi_state_table.count) - return false; + for (index = 0; index < acpi_state_table.count; index++) { + u32 acpi_hint = acpi_state_table.lpi_states[index].address; + + if (intel_idle_acpi_hint_match(flags, acpi_hint, mwait_hint)) + return false; + } + return true; +} + +static bool __init intel_idle_off_by_default_cst(unsigned int flags, u32 mwait_hint) +{ + int cstate, limit = min_t(int, CPUIDLE_STATE_MAX, acpi_state_table.count); - limit = min_t(int, CPUIDLE_STATE_MAX, acpi_state_table.count); /* * If limit > 0, intel_idle_cst_usable() has returned 'true', so all of * the interesting states are ACPI_CSTATE_FFH. @@ -1931,6 +2032,21 @@ static bool __init intel_idle_off_by_default(unsigned int flags, u32 mwait_hint) return true; } +static bool __init intel_idle_off_by_default(unsigned int flags, u32 mwait_hint) +{ + /* + * If there is no C-states information in the ACPI tables, do not + * disable any C-states by default. + */ + if (!acpi_state_table.count) + return false; + + if (acpi_lpi_available) + return intel_idle_off_by_default_lpi(flags, mwait_hint); + + return intel_idle_off_by_default_cst(flags, mwait_hint); +} + static inline bool ignore_native(void) { return no_native && !no_acpi; @@ -1938,7 +2054,7 @@ static inline bool ignore_native(void) #else /* !CONFIG_ACPI_PROCESSOR_CSTATE */ #define force_use_acpi (false) -static inline bool intel_idle_acpi_cst_extract(void) { return false; } +static inline bool intel_idle_acpi_probe(void) { return false; } static inline void intel_idle_init_cstates_acpi(struct cpuidle_driver *drv) { } static inline bool intel_idle_off_by_default(unsigned int flags, u32 mwait_hint) { @@ -2750,7 +2866,7 @@ static int __init intel_idle_init(void) if (icpu) { if (icpu->state_table) cpuidle_state_table = icpu->state_table; - else if (!intel_idle_acpi_cst_extract()) + else if (!intel_idle_acpi_probe()) return -ENODEV; auto_demotion_disable_flags = icpu->auto_demotion_disable_flags; @@ -2759,8 +2875,8 @@ static int __init intel_idle_init(void) if (icpu->c1_demotion_supported) c1_demotion_supported = true; if (icpu->use_acpi || force_use_acpi) - intel_idle_acpi_cst_extract(); - } else if (!intel_idle_acpi_cst_extract()) { + intel_idle_acpi_probe(); + } else if (!intel_idle_acpi_probe()) { return -ENODEV; } |
