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
This commit is contained in:
Rafael J. Wysocki 2026-07-09 14:44:23 +02:00
parent 4d7821961f
commit abc5c103c6

View File

@ -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;
}
for_each_possible_cpu(cpu) {
struct acpi_processor *pr = per_cpu(processors, cpu);
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;
pr = per_cpu(processors, cpu);
if (!pr)
continue;
@ -1826,17 +1846,39 @@ static bool __init intel_idle_acpi_cst_extract(void)
if (!intel_idle_cst_usable())
continue;
if (!acpi_processor_claim_cst_control())
break;
return true;
}
acpi_state_table.count = 0;
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;
return false;
}
static void __init intel_idle_complete_state_init(struct cpuidle_state *state)
{
if (intel_idle_state_needs_timer_stop(state))
@ -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;
}