From d5c13047a132162d2649be876906ead691d12948 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Thu, 9 Jul 2026 14:30:35 +0200 Subject: [PATCH 01/18] ACPI: processor: idle: Expand _LPI package sanity checks The _LPI package sanity checks in acpi_processor_evaluate_lpi() miss a couple of things, so expand them by adding a buffer size check before retrieving a struct acpi_power_register from it (and skip the given state if the buffer is not large enough to hold a register structure) and making the function avoid copying the state description from the ACPI table if there are too few elements in the package supposed to hold it. While at it, relocate and rephrase a comment about skipping _LPI state package elements [7-8]. Fixes: a36a7fecfe60 ("ACPI / processor_idle: Add support for Low Power Idle(LPI) states") Signed-off-by: Rafael J. Wysocki Reviewed-by: Sudeep Holla Acked-by: Huisong Li Link: https://patch.msgid.link/5084143.GXAFRqVoOG@rafael.j.wysocki --- drivers/acpi/processor_idle.c | 28 +++++++++++++++++++++------- 1 file changed, 21 insertions(+), 7 deletions(-) diff --git a/drivers/acpi/processor_idle.c b/drivers/acpi/processor_idle.c index 4482cf28f56a..d573f201295a 100644 --- a/drivers/acpi/processor_idle.c +++ b/drivers/acpi/processor_idle.c @@ -927,6 +927,13 @@ static int acpi_processor_evaluate_lpi(acpi_handle handle, if (obj->type == ACPI_TYPE_BUFFER) { struct acpi_power_register *reg; + if (obj->buffer.length < sizeof(*reg)) { + acpi_handle_debug(handle, + "Invalid register data for _LPI state %d\n", + state_idx); + continue; + } + reg = (struct acpi_power_register *)obj->buffer.pointer; if (reg->space_id != ACPI_ADR_SPACE_SYSTEM_IO && reg->space_id != ACPI_ADR_SPACE_FIXED_HARDWARE) @@ -945,13 +952,6 @@ static int acpi_processor_evaluate_lpi(acpi_handle handle, continue; } - /* elements[7,8] skipped for now i.e. Residency/Usage counter*/ - - obj = pkg_elem + 9; - if (obj->type == ACPI_TYPE_STRING) - strscpy(lpi_state->desc, obj->string.pointer, - ACPI_CX_DESC_LEN); - lpi_state->index = state_idx; if (obj_get_integer(pkg_elem + 0, &lpi_state->min_residency)) { pr_debug("No min. residency found, assuming 10 us\n"); @@ -974,6 +974,20 @@ static int acpi_processor_evaluate_lpi(acpi_handle handle, if (obj_get_integer(pkg_elem + 5, &lpi_state->enable_parent_state)) lpi_state->enable_parent_state = 0; + + /* Skip elements [7-8] i.e. Residency/Usage counters. */ + + /* + * Avoid out-of-bounds access if the size of the package is less + * than expected. + */ + if (element->package.count < 10) + continue; + + obj = pkg_elem + 9; + if (obj->type == ACPI_TYPE_STRING) + strscpy(lpi_state->desc, obj->string.pointer, + ACPI_CX_DESC_LEN); } acpi_handle_debug(handle, "Found %d power states\n", state_idx); From 291c2047e73cc60e5da9b0b3190e48d55d2b104b Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Thu, 9 Jul 2026 14:31:28 +0200 Subject: [PATCH 02/18] ACPI: processor: idle: Ignore _LPI states with SYSTEMIO entry method The only entry method for _LPI states supported by acpi_idle_lpi_enter() is FFH and it is better to ignore _LPI states with the SYSTEMIO entry method upfront than return an error from acpi_idle_lpi_enter() on attempts to use them. Update acpi_processor_evaluate_lpi() accordingly. Signed-off-by: Rafael J. Wysocki Reviewed-by: Sudeep Holla Acked-by: Huisong Li Link: https://patch.msgid.link/2268989.irdbgypaU6@rafael.j.wysocki --- drivers/acpi/processor_idle.c | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/drivers/acpi/processor_idle.c b/drivers/acpi/processor_idle.c index d573f201295a..aa6d5d8edc79 100644 --- a/drivers/acpi/processor_idle.c +++ b/drivers/acpi/processor_idle.c @@ -935,14 +935,15 @@ static int acpi_processor_evaluate_lpi(acpi_handle handle, } reg = (struct acpi_power_register *)obj->buffer.pointer; - if (reg->space_id != ACPI_ADR_SPACE_SYSTEM_IO && - reg->space_id != ACPI_ADR_SPACE_FIXED_HARDWARE) + if (reg->space_id != ACPI_ADR_SPACE_FIXED_HARDWARE) { + acpi_handle_debug(handle, + "Unsupported entry method for _LPI state %d\n", + state_idx); continue; + } + lpi_state->entry_method = ACPI_CSTATE_FFH; lpi_state->address = reg->address; - lpi_state->entry_method = - reg->space_id == ACPI_ADR_SPACE_FIXED_HARDWARE ? - ACPI_CSTATE_FFH : ACPI_CSTATE_SYSTEMIO; } else if (obj->type == ACPI_TYPE_INTEGER) { lpi_state->entry_method = ACPI_CSTATE_INTEGER; lpi_state->address = obj->integer.value; From df0702bfb56b4b7e31b82ac16a4332ad9b311e13 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Thu, 9 Jul 2026 14:32:15 +0200 Subject: [PATCH 03/18] ACPI: processor: idle: Unify debug in acpi_processor_evaluate_lpi() Use acpi_handle_debug() consistently for printing debug messages in acpi_processor_evaluate_lpi() because that makes it somewhat easier to identify the source of the problem in the ACPI tables. No intentional functional impact beyond debug output. Signed-off-by: Rafael J. Wysocki Reviewed-by: Sudeep Holla Acked-by: Huisong Li Link: https://patch.msgid.link/3051550.e9J7NaK4W3@rafael.j.wysocki --- drivers/acpi/processor_idle.c | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/drivers/acpi/processor_idle.c b/drivers/acpi/processor_idle.c index aa6d5d8edc79..e8682f779249 100644 --- a/drivers/acpi/processor_idle.c +++ b/drivers/acpi/processor_idle.c @@ -890,7 +890,7 @@ static int acpi_processor_evaluate_lpi(acpi_handle handle, /* There must be at least 4 elements = 3 elements + 1 package */ if (!lpi_data || lpi_data->type != ACPI_TYPE_PACKAGE || lpi_data->package.count < 4) { - pr_debug("not enough elements in _LPI\n"); + acpi_handle_debug(handle, "Not enough elements in _LPI\n"); ret = -ENODATA; goto end; } @@ -899,7 +899,7 @@ static int acpi_processor_evaluate_lpi(acpi_handle handle, /* Validate number of power states. */ if (pkg_count < 1 || pkg_count != lpi_data->package.count - 3) { - pr_debug("count given by _LPI is not valid\n"); + acpi_handle_debug(handle, "Invalid _LPI state count\n"); ret = -ENODATA; goto end; } @@ -948,19 +948,24 @@ static int acpi_processor_evaluate_lpi(acpi_handle handle, lpi_state->entry_method = ACPI_CSTATE_INTEGER; lpi_state->address = obj->integer.value; } else { - pr_debug("Entry method of state-%d is invalid, disable it.\n", - state_idx); + acpi_handle_debug(handle, + "Invalid entry method for _LPI state %d\n", + state_idx); continue; } lpi_state->index = state_idx; if (obj_get_integer(pkg_elem + 0, &lpi_state->min_residency)) { - pr_debug("No min. residency found, assuming 10 us\n"); + acpi_handle_debug(handle, + "Assuming 10 us min. residency for _LPI state %d\n", + state_idx); lpi_state->min_residency = 10; } if (obj_get_integer(pkg_elem + 1, &lpi_state->wake_latency)) { - pr_debug("No wakeup residency found, assuming 10 us\n"); + acpi_handle_debug(handle, + "Assuming 10 us wake latency for _LPI state %d\n", + state_idx); lpi_state->wake_latency = 10; } From d06c12bebf9825c747e5f9dc7969c06c209bf8e2 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Thu, 9 Jul 2026 14:33:41 +0200 Subject: [PATCH 04/18] ACPI: processor: idle: Rearrange acpi_processor_evaluate_lpi() Rearrange acpi_processor_evaluate_lpi() to make it somewhat easier to follow and diagnose (if need be). In particular: * Rename some local variables and reorder their definitions. * Change the type of local variables used for storing firmware-provided values to unsigned int (they cannot be negative). * Eliminate local variable "loop" that is redundant. * Avoid explicit pointer arithmetic. * Print the correct number of _LPI state packages in the final debug message. No intentional functional impact beyond debug output. Signed-off-by: Rafael J. Wysocki Reviewed-by: Sudeep Holla Link: https://patch.msgid.link/3426078.44csPzL39Z@rafael.j.wysocki --- drivers/acpi/processor_idle.c | 64 +++++++++++++++++++---------------- 1 file changed, 34 insertions(+), 30 deletions(-) diff --git a/drivers/acpi/processor_idle.c b/drivers/acpi/processor_idle.c index e8682f779249..cd506e9e5a84 100644 --- a/drivers/acpi/processor_idle.c +++ b/drivers/acpi/processor_idle.c @@ -872,12 +872,12 @@ static int obj_get_integer(union acpi_object *obj, u32 *value) static int acpi_processor_evaluate_lpi(acpi_handle handle, struct acpi_lpi_states_array *info) { + struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL }; + union acpi_object *lpi_data, *lpi_pkg; + unsigned int lpi_pkg_count, state_idx; + struct acpi_lpi_state *lpi_state; acpi_status status; int ret = 0; - int pkg_count, state_idx = 1, loop; - struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL }; - union acpi_object *lpi_data; - struct acpi_lpi_state *lpi_state; status = acpi_evaluate_object(handle, "_LPI", NULL, &buffer); if (ACPI_FAILURE(status)) { @@ -895,41 +895,46 @@ static int acpi_processor_evaluate_lpi(acpi_handle handle, goto end; } - pkg_count = lpi_data->package.elements[2].integer.value; + lpi_pkg_count = lpi_data->package.elements[2].integer.value; /* Validate number of power states. */ - if (pkg_count < 1 || pkg_count != lpi_data->package.count - 3) { + if (!lpi_pkg_count || lpi_pkg_count != lpi_data->package.count - 3) { acpi_handle_debug(handle, "Invalid _LPI state count\n"); ret = -ENODATA; goto end; } - lpi_state = kzalloc_objs(*lpi_state, pkg_count); + lpi_state = kzalloc_objs(*lpi_state, lpi_pkg_count); if (!lpi_state) { ret = -ENOMEM; goto end; } - info->size = pkg_count; + info->size = lpi_pkg_count; info->entries = lpi_state; - /* LPI States start at index 3 */ - for (loop = 3; state_idx <= pkg_count; loop++, state_idx++, lpi_state++) { - union acpi_object *element, *pkg_elem, *obj; + /* _LPI State packages start at index 3. */ + lpi_pkg = &lpi_data->package.elements[3]; - element = &lpi_data->package.elements[loop]; - if (element->type != ACPI_TYPE_PACKAGE || element->package.count < 7) + for (state_idx = 1; state_idx <= lpi_pkg_count; + state_idx++, lpi_state++, lpi_pkg++) { + union acpi_object *lpi_pkg_elem, *obj; + + lpi_state->index = state_idx; + + if (lpi_pkg->type != ACPI_TYPE_PACKAGE || lpi_pkg->package.count < 7) continue; - pkg_elem = element->package.elements; + lpi_pkg_elem = lpi_pkg->package.elements; - obj = pkg_elem + 6; + /* Get the entry method first and skip the state if that fails. */ + obj = &lpi_pkg_elem[6]; if (obj->type == ACPI_TYPE_BUFFER) { struct acpi_power_register *reg; if (obj->buffer.length < sizeof(*reg)) { acpi_handle_debug(handle, - "Invalid register data for _LPI state %d\n", + "Invalid register data for _LPI state %u\n", state_idx); continue; } @@ -937,7 +942,7 @@ static int acpi_processor_evaluate_lpi(acpi_handle handle, reg = (struct acpi_power_register *)obj->buffer.pointer; if (reg->space_id != ACPI_ADR_SPACE_FIXED_HARDWARE) { acpi_handle_debug(handle, - "Unsupported entry method for _LPI state %d\n", + "Unsupported entry method for _LPI state %u\n", state_idx); continue; } @@ -949,36 +954,35 @@ static int acpi_processor_evaluate_lpi(acpi_handle handle, lpi_state->address = obj->integer.value; } else { acpi_handle_debug(handle, - "Invalid entry method for _LPI state %d\n", + "Invalid entry method for _LPI state %u\n", state_idx); continue; } - lpi_state->index = state_idx; - if (obj_get_integer(pkg_elem + 0, &lpi_state->min_residency)) { + if (obj_get_integer(&lpi_pkg_elem[0], &lpi_state->min_residency)) { acpi_handle_debug(handle, - "Assuming 10 us min. residency for _LPI state %d\n", + "Assuming 10 us min. residency for _LPI state %u\n", state_idx); lpi_state->min_residency = 10; } - if (obj_get_integer(pkg_elem + 1, &lpi_state->wake_latency)) { + if (obj_get_integer(&lpi_pkg_elem[1], &lpi_state->wake_latency)) { acpi_handle_debug(handle, - "Assuming 10 us wake latency for _LPI state %d\n", + "Assuming 10 us wake latency for _LPI state %u\n", state_idx); lpi_state->wake_latency = 10; } - if (obj_get_integer(pkg_elem + 2, &lpi_state->flags)) + if (obj_get_integer(&lpi_pkg_elem[2], &lpi_state->flags)) lpi_state->flags = 0; - if (obj_get_integer(pkg_elem + 3, &lpi_state->arch_flags)) + if (obj_get_integer(&lpi_pkg_elem[3], &lpi_state->arch_flags)) lpi_state->arch_flags = 0; - if (obj_get_integer(pkg_elem + 4, &lpi_state->res_cnt_freq)) + if (obj_get_integer(&lpi_pkg_elem[4], &lpi_state->res_cnt_freq)) lpi_state->res_cnt_freq = 1; - if (obj_get_integer(pkg_elem + 5, &lpi_state->enable_parent_state)) + if (obj_get_integer(&lpi_pkg_elem[5], &lpi_state->enable_parent_state)) lpi_state->enable_parent_state = 0; /* Skip elements [7-8] i.e. Residency/Usage counters. */ @@ -987,16 +991,16 @@ static int acpi_processor_evaluate_lpi(acpi_handle handle, * Avoid out-of-bounds access if the size of the package is less * than expected. */ - if (element->package.count < 10) + if (lpi_pkg->package.count < 10) continue; - obj = pkg_elem + 9; + obj = &lpi_pkg_elem[9]; if (obj->type == ACPI_TYPE_STRING) strscpy(lpi_state->desc, obj->string.pointer, ACPI_CX_DESC_LEN); } - acpi_handle_debug(handle, "Found %d power states\n", state_idx); + acpi_handle_debug(handle, "Found %u power states\n", lpi_pkg_count); end: kfree(buffer.pointer); return ret; From d48c5722248cbbe2e742c2d7e7b8a0417c93bd6b Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Thu, 9 Jul 2026 14:34:36 +0200 Subject: [PATCH 05/18] ACPI: processor: idle: Split acpi_processor_evaluate_lpi() Move individual _LPI state package processing from acpi_processor_evaluate_lpi() to a separate new function called process_lpi_state_package(). No intentional functional impact. Signed-off-by: Rafael J. Wysocki Reviewed-by: Sudeep Holla Acked-by: Huisong Li Link: https://patch.msgid.link/10878273.nUPlyArG6x@rafael.j.wysocki --- drivers/acpi/processor_idle.c | 167 +++++++++++++++++----------------- 1 file changed, 86 insertions(+), 81 deletions(-) diff --git a/drivers/acpi/processor_idle.c b/drivers/acpi/processor_idle.c index cd506e9e5a84..771a7bd6fb73 100644 --- a/drivers/acpi/processor_idle.c +++ b/drivers/acpi/processor_idle.c @@ -869,6 +869,90 @@ static int obj_get_integer(union acpi_object *obj, u32 *value) return 0; } +static void process_lpi_state_package(union acpi_object *lpi_pkg, + struct acpi_lpi_state *lpi_state, + acpi_handle handle, + unsigned int state_idx) +{ + union acpi_object *lpi_pkg_elem, *obj; + + if (lpi_pkg->type != ACPI_TYPE_PACKAGE || lpi_pkg->package.count < 7) + return; + + lpi_pkg_elem = lpi_pkg->package.elements; + + /* Get the entry method first and skip the state if that fails. */ + obj = &lpi_pkg_elem[6]; + if (obj->type == ACPI_TYPE_BUFFER) { + struct acpi_power_register *reg; + + if (obj->buffer.length < sizeof(*reg)) { + acpi_handle_debug(handle, + "Invalid register data for _LPI state %u\n", + state_idx); + return; + } + + reg = (struct acpi_power_register *)obj->buffer.pointer; + if (reg->space_id != ACPI_ADR_SPACE_FIXED_HARDWARE) { + acpi_handle_debug(handle, + "Unsupported entry method for _LPI state %u\n", + state_idx); + return; + } + + lpi_state->entry_method = ACPI_CSTATE_FFH; + lpi_state->address = reg->address; + } else if (obj->type == ACPI_TYPE_INTEGER) { + lpi_state->entry_method = ACPI_CSTATE_INTEGER; + lpi_state->address = obj->integer.value; + } else { + acpi_handle_debug(handle, + "Invalid entry method for _LPI state %u\n", + state_idx); + return; + } + + if (obj_get_integer(&lpi_pkg_elem[0], &lpi_state->min_residency)) { + acpi_handle_debug(handle, + "Assuming 10 us min. residency for _LPI state %u\n", + state_idx); + lpi_state->min_residency = 10; + } + + if (obj_get_integer(&lpi_pkg_elem[1], &lpi_state->wake_latency)) { + acpi_handle_debug(handle, + "Assuming 10 us wake latency for _LPI state %u\n", + state_idx); + lpi_state->wake_latency = 10; + } + + if (obj_get_integer(&lpi_pkg_elem[2], &lpi_state->flags)) + lpi_state->flags = 0; + + if (obj_get_integer(&lpi_pkg_elem[3], &lpi_state->arch_flags)) + lpi_state->arch_flags = 0; + + if (obj_get_integer(&lpi_pkg_elem[4], &lpi_state->res_cnt_freq)) + lpi_state->res_cnt_freq = 1; + + if (obj_get_integer(&lpi_pkg_elem[5], &lpi_state->enable_parent_state)) + lpi_state->enable_parent_state = 0; + + /* Skip elements [7-8] i.e. Residency/Usage counters. */ + + /* + * Avoid out-of-bounds access if the size of the package is less than + * expected. + */ + if (lpi_pkg->package.count < 10) + return; + + obj = &lpi_pkg_elem[9]; + if (obj->type == ACPI_TYPE_STRING) + strscpy(lpi_state->desc, obj->string.pointer, ACPI_CX_DESC_LEN); +} + static int acpi_processor_evaluate_lpi(acpi_handle handle, struct acpi_lpi_states_array *info) { @@ -916,88 +1000,9 @@ static int acpi_processor_evaluate_lpi(acpi_handle handle, /* _LPI State packages start at index 3. */ lpi_pkg = &lpi_data->package.elements[3]; - for (state_idx = 1; state_idx <= lpi_pkg_count; - state_idx++, lpi_state++, lpi_pkg++) { - union acpi_object *lpi_pkg_elem, *obj; - + for (state_idx = 1; state_idx <= lpi_pkg_count; state_idx++) { lpi_state->index = state_idx; - - if (lpi_pkg->type != ACPI_TYPE_PACKAGE || lpi_pkg->package.count < 7) - continue; - - lpi_pkg_elem = lpi_pkg->package.elements; - - /* Get the entry method first and skip the state if that fails. */ - obj = &lpi_pkg_elem[6]; - if (obj->type == ACPI_TYPE_BUFFER) { - struct acpi_power_register *reg; - - if (obj->buffer.length < sizeof(*reg)) { - acpi_handle_debug(handle, - "Invalid register data for _LPI state %u\n", - state_idx); - continue; - } - - reg = (struct acpi_power_register *)obj->buffer.pointer; - if (reg->space_id != ACPI_ADR_SPACE_FIXED_HARDWARE) { - acpi_handle_debug(handle, - "Unsupported entry method for _LPI state %u\n", - state_idx); - continue; - } - - lpi_state->entry_method = ACPI_CSTATE_FFH; - lpi_state->address = reg->address; - } else if (obj->type == ACPI_TYPE_INTEGER) { - lpi_state->entry_method = ACPI_CSTATE_INTEGER; - lpi_state->address = obj->integer.value; - } else { - acpi_handle_debug(handle, - "Invalid entry method for _LPI state %u\n", - state_idx); - continue; - } - - if (obj_get_integer(&lpi_pkg_elem[0], &lpi_state->min_residency)) { - acpi_handle_debug(handle, - "Assuming 10 us min. residency for _LPI state %u\n", - state_idx); - lpi_state->min_residency = 10; - } - - if (obj_get_integer(&lpi_pkg_elem[1], &lpi_state->wake_latency)) { - acpi_handle_debug(handle, - "Assuming 10 us wake latency for _LPI state %u\n", - state_idx); - lpi_state->wake_latency = 10; - } - - if (obj_get_integer(&lpi_pkg_elem[2], &lpi_state->flags)) - lpi_state->flags = 0; - - if (obj_get_integer(&lpi_pkg_elem[3], &lpi_state->arch_flags)) - lpi_state->arch_flags = 0; - - if (obj_get_integer(&lpi_pkg_elem[4], &lpi_state->res_cnt_freq)) - lpi_state->res_cnt_freq = 1; - - if (obj_get_integer(&lpi_pkg_elem[5], &lpi_state->enable_parent_state)) - lpi_state->enable_parent_state = 0; - - /* Skip elements [7-8] i.e. Residency/Usage counters. */ - - /* - * Avoid out-of-bounds access if the size of the package is less - * than expected. - */ - if (lpi_pkg->package.count < 10) - continue; - - obj = &lpi_pkg_elem[9]; - if (obj->type == ACPI_TYPE_STRING) - strscpy(lpi_state->desc, obj->string.pointer, - ACPI_CX_DESC_LEN); + process_lpi_state_package(lpi_pkg++, lpi_state++, handle, state_idx); } acpi_handle_debug(handle, "Found %u power states\n", lpi_pkg_count); From 7358da875332d20e59a49cea3b35bab8c5519d53 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Thu, 9 Jul 2026 14:35:14 +0200 Subject: [PATCH 06/18] ACPI: processor: idle: Introduce lpi_state_debug() Add a helper macro called lpi_state_debug() for printing debug messages regarding _LPI states and use it in acpi_processor_evaluate_lpi(). No intentional functional impact. Signed-off-by: Rafael J. Wysocki Link: https://patch.msgid.link/2043393.PYKUYFuaPT@rafael.j.wysocki --- drivers/acpi/processor_idle.c | 23 ++++++++--------------- 1 file changed, 8 insertions(+), 15 deletions(-) diff --git a/drivers/acpi/processor_idle.c b/drivers/acpi/processor_idle.c index 771a7bd6fb73..f2ebd99a2895 100644 --- a/drivers/acpi/processor_idle.c +++ b/drivers/acpi/processor_idle.c @@ -869,6 +869,9 @@ static int obj_get_integer(union acpi_object *obj, u32 *value) return 0; } +#define lpi_state_debug(handle, message, state_idx) \ + acpi_handle_debug(handle, message " for _LPI state %u\n", state_idx) + static void process_lpi_state_package(union acpi_object *lpi_pkg, struct acpi_lpi_state *lpi_state, acpi_handle handle, @@ -887,17 +890,13 @@ static void process_lpi_state_package(union acpi_object *lpi_pkg, struct acpi_power_register *reg; if (obj->buffer.length < sizeof(*reg)) { - acpi_handle_debug(handle, - "Invalid register data for _LPI state %u\n", - state_idx); + lpi_state_debug(handle, "Invalid register data", state_idx); return; } reg = (struct acpi_power_register *)obj->buffer.pointer; if (reg->space_id != ACPI_ADR_SPACE_FIXED_HARDWARE) { - acpi_handle_debug(handle, - "Unsupported entry method for _LPI state %u\n", - state_idx); + lpi_state_debug(handle, "Unsupported entry method", state_idx); return; } @@ -907,23 +906,17 @@ static void process_lpi_state_package(union acpi_object *lpi_pkg, lpi_state->entry_method = ACPI_CSTATE_INTEGER; lpi_state->address = obj->integer.value; } else { - acpi_handle_debug(handle, - "Invalid entry method for _LPI state %u\n", - state_idx); + lpi_state_debug(handle, "Invalid entry method", state_idx); return; } if (obj_get_integer(&lpi_pkg_elem[0], &lpi_state->min_residency)) { - acpi_handle_debug(handle, - "Assuming 10 us min. residency for _LPI state %u\n", - state_idx); + lpi_state_debug(handle, "Assuming 10 us min. residency", state_idx); lpi_state->min_residency = 10; } if (obj_get_integer(&lpi_pkg_elem[1], &lpi_state->wake_latency)) { - acpi_handle_debug(handle, - "Assuming 10 us wake latency for _LPI state %u\n", - state_idx); + lpi_state_debug(handle, "Assuming 10 us wake latency", state_idx); lpi_state->wake_latency = 10; } From fcdc7587db7701f5c451b558b989822131b1b8c8 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Thu, 9 Jul 2026 14:36:28 +0200 Subject: [PATCH 07/18] ACPI: processor: idle: Rearrange acpi_processor_get_lpi_info() Reorder the definitions of local variables in acpi_processor_get_lpi_info() and drop local variable pr_ahandle that is not really necessary from it. Additionally, move two definitions of local variables to the loop in which they are used and rearrange the code slightly to prepare it for subsequent changes. No intentional functional impact. Signed-off-by: Rafael J. Wysocki Reviewed-by: Sudeep Holla Acked-by: Huisong Li Link: https://patch.msgid.link/7965163.EvYhyI6sBW@rafael.j.wysocki --- drivers/acpi/processor_idle.c | 33 ++++++++++++++++++--------------- 1 file changed, 18 insertions(+), 15 deletions(-) diff --git a/drivers/acpi/processor_idle.c b/drivers/acpi/processor_idle.c index f2ebd99a2895..e990a43514e6 100644 --- a/drivers/acpi/processor_idle.c +++ b/drivers/acpi/processor_idle.c @@ -1099,12 +1099,11 @@ int __weak acpi_processor_ffh_lpi_probe(unsigned int cpu) static int acpi_processor_get_lpi_info(struct acpi_processor *pr) { - int ret, i; - acpi_status status; - acpi_handle handle = pr->handle, pr_ahandle; - struct acpi_device *d = NULL; - struct acpi_lpi_states_array info[2], *tmp, *prev, *curr; + struct acpi_lpi_states_array info[2], *prev, *curr; + acpi_handle handle = pr->handle; unsigned int state_count; + acpi_status status; + int ret, i; /* make sure our architecture has support */ ret = acpi_processor_ffh_lpi_probe(pr->id); @@ -1117,22 +1116,26 @@ static int acpi_processor_get_lpi_info(struct acpi_processor *pr) if (!acpi_has_method(handle, "_LPI")) return -EINVAL; - prev = &info[0]; - curr = &info[1]; - handle = pr->handle; - ret = acpi_processor_evaluate_lpi(handle, prev); + curr = &info[0]; + + ret = acpi_processor_evaluate_lpi(handle, curr); if (ret) return ret; - state_count = flatten_lpi_states(pr, 0, prev, NULL); - status = acpi_get_parent(handle, &pr_ahandle); + state_count = flatten_lpi_states(pr, 0, curr, NULL); + + prev = curr; + curr = &info[1]; + + status = acpi_get_parent(handle, &handle); while (ACPI_SUCCESS(status)) { - d = acpi_fetch_acpi_dev(pr_ahandle); + struct acpi_lpi_states_array *tmp; + struct acpi_device *d; + + d = acpi_fetch_acpi_dev(handle); if (!d) break; - handle = pr_ahandle; - if (strcmp(acpi_device_hid(d), ACPI_PROCESSOR_CONTAINER_HID)) break; @@ -1149,7 +1152,7 @@ static int acpi_processor_get_lpi_info(struct acpi_processor *pr) tmp = prev, prev = curr, curr = tmp; - status = acpi_get_parent(handle, &pr_ahandle); + status = acpi_get_parent(handle, &handle); } /* reset the index after flattening */ From ab96ea910d06d60758adbc330456528cb41cf2eb Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Thu, 9 Jul 2026 14:37:09 +0200 Subject: [PATCH 08/18] ACPI: processor: idle: Rework first-level _LPI states processing The first-level _LPI states need not be combined with the previous level and the entry method for them cannot be ACPI_CSTATE_INTEGER, so process them directly in acpi_processor_get_lpi_info() instead of doing a special case for them in flatten_lpi_states(). Also bail out if there are no _LPI states at the first level because that means that there are no _LPI states at all. Signed-off-by: Rafael J. Wysocki Reviewed-by: Sudeep Holla Link: https://patch.msgid.link/3703077.iIbC2pHGDl@rafael.j.wysocki --- drivers/acpi/processor_idle.c | 47 +++++++++++++++++++++++++++-------- 1 file changed, 37 insertions(+), 10 deletions(-) diff --git a/drivers/acpi/processor_idle.c b/drivers/acpi/processor_idle.c index e990a43514e6..efd3e76377fa 100644 --- a/drivers/acpi/processor_idle.c +++ b/drivers/acpi/processor_idle.c @@ -1068,13 +1068,6 @@ static unsigned int flatten_lpi_states(struct acpi_processor *pr, flpi = &pr->power.lpi_states[flat_state_cnt]; - if (!prev_level) { /* leaf/processor node */ - memcpy(flpi, t, sizeof(*t)); - stash_composite_state(curr_level, flpi); - flat_state_cnt++; - continue; - } - for (i = 0; i < prev_level->composite_states_size; i++) { p = prev_level->composite_states[i]; if (t->index <= p->enable_parent_state && @@ -1101,9 +1094,10 @@ static int acpi_processor_get_lpi_info(struct acpi_processor *pr) { struct acpi_lpi_states_array info[2], *prev, *curr; acpi_handle handle = pr->handle; - unsigned int state_count; + unsigned int state_count = 0; acpi_status status; - int ret, i; + unsigned int i; + int ret; /* make sure our architecture has support */ ret = acpi_processor_ffh_lpi_probe(pr->id); @@ -1117,12 +1111,45 @@ static int acpi_processor_get_lpi_info(struct acpi_processor *pr) return -EINVAL; curr = &info[0]; + curr->composite_states_size = 0; ret = acpi_processor_evaluate_lpi(handle, curr); if (ret) return ret; - state_count = flatten_lpi_states(pr, 0, curr, NULL); + /* Copy all of the usable first-level states to power.lpi_states[]. */ + for (i = 0; i < curr->size; i++) { + struct acpi_lpi_state *lpi = &curr->entries[i]; + struct acpi_lpi_state *flpi; + + /* + * Skip states that are not enabled or have an inadequate entry + * method for this level. + */ + if (!(lpi->flags & ACPI_LPI_STATE_FLAGS_ENABLED) || + lpi->entry_method == ACPI_CSTATE_INTEGER) + continue; + + if (state_count >= ACPI_PROCESSOR_MAX_POWER) { + acpi_handle_info(handle, + "No space for more _LPI states than %d\n", + ACPI_PROCESSOR_MAX_POWER); + break; + } + + flpi = &pr->power.lpi_states[state_count++]; + memcpy(flpi, lpi, sizeof(*lpi)); + stash_composite_state(curr, flpi); + } + + kfree(curr->entries); + + /* + * If there are no _LPI states at the first level, there are no _LPI + * states at all. + */ + if (!state_count) + return -ENODATA; prev = curr; curr = &info[1]; From 863572d8711c7f02344f5e37163063c43c4b105d Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Thu, 9 Jul 2026 14:37:49 +0200 Subject: [PATCH 09/18] ACPI: processor: idle: Drop redundant _LPI presence checks The acpi_has_method() checks for _LPI in acpi_processor_get_lpi_info() are redundant because acpi_processor_evaluate_lpi() returns an error when _LPI is not present, so drop them. No intentional functional impact. Signed-off-by: Rafael J. Wysocki Reviewed-by: Sudeep Holla Acked-by: Huisong Li Link: https://patch.msgid.link/3349003.5fSG56mABF@rafael.j.wysocki --- drivers/acpi/processor_idle.c | 7 ------- 1 file changed, 7 deletions(-) diff --git a/drivers/acpi/processor_idle.c b/drivers/acpi/processor_idle.c index efd3e76377fa..274abe3da7a4 100644 --- a/drivers/acpi/processor_idle.c +++ b/drivers/acpi/processor_idle.c @@ -1107,9 +1107,6 @@ static int acpi_processor_get_lpi_info(struct acpi_processor *pr) if (!osc_pc_lpi_support_confirmed) return -EOPNOTSUPP; - if (!acpi_has_method(handle, "_LPI")) - return -EINVAL; - curr = &info[0]; curr->composite_states_size = 0; @@ -1166,10 +1163,6 @@ static int acpi_processor_get_lpi_info(struct acpi_processor *pr) if (strcmp(acpi_device_hid(d), ACPI_PROCESSOR_CONTAINER_HID)) break; - /* can be optional ? */ - if (!acpi_has_method(handle, "_LPI")) - break; - ret = acpi_processor_evaluate_lpi(handle, curr); if (ret) break; From 75739f2c708f7be888fd5353811f169bbbc44313 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Thu, 9 Jul 2026 14:38:29 +0200 Subject: [PATCH 10/18] ACPI: processor: idle: Rearrange loop in acpi_processor_get_lpi_info() Eliminate local variable status (that is redundant) from acpi_processor_get_lpi_info() and make that function call acpi_get_parent() in one place. No intentional functional impact. Signed-off-by: Rafael J. Wysocki Reviewed-by: Sudeep Holla Acked-by: Huisong Li Link: https://patch.msgid.link/1862515.VLH7GnMWUR@rafael.j.wysocki --- drivers/acpi/processor_idle.c | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/drivers/acpi/processor_idle.c b/drivers/acpi/processor_idle.c index 274abe3da7a4..57a0a8aaa42f 100644 --- a/drivers/acpi/processor_idle.c +++ b/drivers/acpi/processor_idle.c @@ -1095,7 +1095,6 @@ static int acpi_processor_get_lpi_info(struct acpi_processor *pr) struct acpi_lpi_states_array info[2], *prev, *curr; acpi_handle handle = pr->handle; unsigned int state_count = 0; - acpi_status status; unsigned int i; int ret; @@ -1151,11 +1150,13 @@ static int acpi_processor_get_lpi_info(struct acpi_processor *pr) prev = curr; curr = &info[1]; - status = acpi_get_parent(handle, &handle); - while (ACPI_SUCCESS(status)) { + for (;;) { struct acpi_lpi_states_array *tmp; struct acpi_device *d; + if (ACPI_FAILURE(acpi_get_parent(handle, &handle))) + break; + d = acpi_fetch_acpi_dev(handle); if (!d) break; @@ -1171,8 +1172,6 @@ static int acpi_processor_get_lpi_info(struct acpi_processor *pr) state_count = flatten_lpi_states(pr, state_count, curr, prev); tmp = prev, prev = curr, curr = tmp; - - status = acpi_get_parent(handle, &handle); } /* reset the index after flattening */ From 592343c4d2a215f07b5d5c657370a4fa2ac731c6 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Thu, 9 Jul 2026 14:39:06 +0200 Subject: [PATCH 11/18] ACPI: processor: idle: Rework flatten_lpi_states() Rewrite flatten_lpi_states() to make it easier to follow: * Rename its flat_state_cnt, curr_level and prev_level parameters to state_count, curr, and prev, respectively, so their names match the names of analogous variables in acpi_processor_get_lpi_info(). * Eliminate a redundant local variable state_count. * Move definitions of local variables to the code blocks in which they are used. * Reduce the indentation level in the inner loop. * Use more meaningful names for local variables. * Move two statements that belong in acpi_processor_get_lpi_info() from flatten_lpi_states() to that function. * Use acpi_handle_info() for printing a message when the count of flattened states gets too large and drop the message requesting ACPI_PROCESSOR_MAX_POWER to be adjusted which is pointless. * Add a comment explaining what happens in that function. No intentional functional impact beyond the message printed when the count of flattened states is too large. Signed-off-by: Rafael J. Wysocki Reviewed-by: Sudeep Holla Acked-by: Huisong Li Link: https://patch.msgid.link/2064627.usQuhbGJ8B@rafael.j.wysocki --- drivers/acpi/processor_idle.c | 63 +++++++++++++++++++++-------------- 1 file changed, 38 insertions(+), 25 deletions(-) diff --git a/drivers/acpi/processor_idle.c b/drivers/acpi/processor_idle.c index 57a0a8aaa42f..edc9b5d88b79 100644 --- a/drivers/acpi/processor_idle.c +++ b/drivers/acpi/processor_idle.c @@ -1045,44 +1045,53 @@ static void stash_composite_state(struct acpi_lpi_states_array *curr_level, } static unsigned int flatten_lpi_states(struct acpi_processor *pr, - unsigned int flat_state_cnt, - struct acpi_lpi_states_array *curr_level, - struct acpi_lpi_states_array *prev_level) + unsigned int state_count, + struct acpi_lpi_states_array *curr, + struct acpi_lpi_states_array *prev) { - int i, j, state_count = curr_level->size; - struct acpi_lpi_state *p, *t = curr_level->entries; + struct acpi_lpi_state *parent_lpi = curr->entries; + unsigned int j; - curr_level->composite_states_size = 0; - for (j = 0; j < state_count; j++, t++) { + /* + * Combine each of the "raw" _LPI states from the current (processor + * container) level with all of the composite _LPI states from the + * previous (processor or processor container) level. + */ + for (j = 0; j < curr->size; j++, parent_lpi++) { struct acpi_lpi_state *flpi; + int i; - if (!(t->flags & ACPI_LPI_STATE_FLAGS_ENABLED)) + if (!(parent_lpi->flags & ACPI_LPI_STATE_FLAGS_ENABLED)) continue; - if (flat_state_cnt >= ACPI_PROCESSOR_MAX_POWER) { - pr_warn("Limiting number of LPI states to max (%d)\n", - ACPI_PROCESSOR_MAX_POWER); - pr_warn("Please increase ACPI_PROCESSOR_MAX_POWER if needed.\n"); + if (state_count >= ACPI_PROCESSOR_MAX_POWER) { + acpi_handle_info(pr->handle, + "No space for more _LPI states than %d\n", + ACPI_PROCESSOR_MAX_POWER); break; } - flpi = &pr->power.lpi_states[flat_state_cnt]; + flpi = &pr->power.lpi_states[state_count]; - for (i = 0; i < prev_level->composite_states_size; i++) { - p = prev_level->composite_states[i]; - if (t->index <= p->enable_parent_state && - combine_lpi_states(p, t, flpi)) { - stash_composite_state(curr_level, flpi); - flat_state_cnt++; - flpi++; - if (flat_state_cnt >= ACPI_PROCESSOR_MAX_POWER) - break; - } + for (i = 0; i < prev->composite_states_size; i++) { + struct acpi_lpi_state *local_lpi = prev->composite_states[i]; + + if (parent_lpi->index > local_lpi->enable_parent_state) + continue; + + if (!combine_lpi_states(local_lpi, parent_lpi, flpi)) + continue; + + stash_composite_state(curr, flpi); + state_count++; + flpi++; + + if (state_count >= ACPI_PROCESSOR_MAX_POWER) + break; } } - kfree(curr_level->entries); - return flat_state_cnt; + return state_count; } int __weak acpi_processor_ffh_lpi_probe(unsigned int cpu) @@ -1164,6 +1173,8 @@ static int acpi_processor_get_lpi_info(struct acpi_processor *pr) if (strcmp(acpi_device_hid(d), ACPI_PROCESSOR_CONTAINER_HID)) break; + curr->composite_states_size = 0; + ret = acpi_processor_evaluate_lpi(handle, curr); if (ret) break; @@ -1171,6 +1182,8 @@ static int acpi_processor_get_lpi_info(struct acpi_processor *pr) /* flatten all the LPI states in this level of hierarchy */ state_count = flatten_lpi_states(pr, state_count, curr, prev); + kfree(curr->entries); + tmp = prev, prev = curr, curr = tmp; } From db4c69b33bc5f3e41b80b10b2e00bed4a690c648 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Thu, 9 Jul 2026 14:39:49 +0200 Subject: [PATCH 12/18] ACPI: processor: idle: Introduce too_many_states() for _LPI To reduce code duplication, introduce a function called too_many_states() that will check if the total number of _LPI states for a given CPU is too large and print a message in that case. Use that function in flatten_lpi_states() and acpi_processor_get_lpi_info(). No functional impact beyond reducing dynamic debug flexibility. Signed-off-by: Rafael J. Wysocki Reviewed-by: Sudeep Holla Acked-by: Huisong Li Link: https://patch.msgid.link/1964818.CQOukoFCf9@rafael.j.wysocki --- drivers/acpi/processor_idle.c | 22 ++++++++++++---------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/drivers/acpi/processor_idle.c b/drivers/acpi/processor_idle.c index edc9b5d88b79..c7f206c094fb 100644 --- a/drivers/acpi/processor_idle.c +++ b/drivers/acpi/processor_idle.c @@ -1044,6 +1044,16 @@ static void stash_composite_state(struct acpi_lpi_states_array *curr_level, curr_level->composite_states[curr_level->composite_states_size++] = t; } +static bool too_many_states(acpi_handle handle, unsigned int state_count) +{ + if (state_count < ACPI_PROCESSOR_MAX_POWER) + return false; + + acpi_handle_info(handle, "No space for more _LPI states than %d\n", + ACPI_PROCESSOR_MAX_POWER); + return true; +} + static unsigned int flatten_lpi_states(struct acpi_processor *pr, unsigned int state_count, struct acpi_lpi_states_array *curr, @@ -1064,12 +1074,8 @@ static unsigned int flatten_lpi_states(struct acpi_processor *pr, if (!(parent_lpi->flags & ACPI_LPI_STATE_FLAGS_ENABLED)) continue; - if (state_count >= ACPI_PROCESSOR_MAX_POWER) { - acpi_handle_info(pr->handle, - "No space for more _LPI states than %d\n", - ACPI_PROCESSOR_MAX_POWER); + if (too_many_states(pr->handle, state_count)) break; - } flpi = &pr->power.lpi_states[state_count]; @@ -1135,12 +1141,8 @@ static int acpi_processor_get_lpi_info(struct acpi_processor *pr) lpi->entry_method == ACPI_CSTATE_INTEGER) continue; - if (state_count >= ACPI_PROCESSOR_MAX_POWER) { - acpi_handle_info(handle, - "No space for more _LPI states than %d\n", - ACPI_PROCESSOR_MAX_POWER); + if (too_many_states(pr->handle, state_count)) break; - } flpi = &pr->power.lpi_states[state_count++]; memcpy(flpi, lpi, sizeof(*lpi)); From 99ba75bbc17bb9f6e1e46e809651f5f5e39f8f69 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Thu, 9 Jul 2026 14:40:35 +0200 Subject: [PATCH 13/18] ACPI: processor: idle: Introduce acpi_processor_extract_lpi_info() In preparation for adding ACPI _LPI support to the intel_idle driver, move the majority of the acpi_processor_get_lpi_info() function body to a new function called acpi_processor_extract_lpi_info() that will be exported to external code subsequently. No intentional functional impact. Signed-off-by: Rafael J. Wysocki Reviewed-by: Sudeep Holla Acked-by: Huisong Li Link: https://patch.msgid.link/2709187.Lt9SDvczpP@rafael.j.wysocki --- drivers/acpi/processor_idle.c | 44 +++++++++++++++++++++++------------ 1 file changed, 29 insertions(+), 15 deletions(-) diff --git a/drivers/acpi/processor_idle.c b/drivers/acpi/processor_idle.c index c7f206c094fb..2c6c9a7365fb 100644 --- a/drivers/acpi/processor_idle.c +++ b/drivers/acpi/processor_idle.c @@ -1054,7 +1054,8 @@ static bool too_many_states(acpi_handle handle, unsigned int state_count) return true; } -static unsigned int flatten_lpi_states(struct acpi_processor *pr, +static unsigned int flatten_lpi_states(acpi_handle handle, + struct acpi_lpi_state *lpi_states, unsigned int state_count, struct acpi_lpi_states_array *curr, struct acpi_lpi_states_array *prev) @@ -1074,10 +1075,10 @@ static unsigned int flatten_lpi_states(struct acpi_processor *pr, if (!(parent_lpi->flags & ACPI_LPI_STATE_FLAGS_ENABLED)) continue; - if (too_many_states(pr->handle, state_count)) + if (too_many_states(handle, state_count)) break; - flpi = &pr->power.lpi_states[state_count]; + flpi = &lpi_states[state_count]; for (i = 0; i < prev->composite_states_size; i++) { struct acpi_lpi_state *local_lpi = prev->composite_states[i]; @@ -1105,19 +1106,15 @@ int __weak acpi_processor_ffh_lpi_probe(unsigned int cpu) return -EOPNOTSUPP; } -static int acpi_processor_get_lpi_info(struct acpi_processor *pr) +static int acpi_processor_extract_lpi_info(acpi_handle pr_handle, + struct acpi_processor_power *pr_power) { struct acpi_lpi_states_array info[2], *prev, *curr; - acpi_handle handle = pr->handle; + acpi_handle handle = pr_handle; unsigned int state_count = 0; unsigned int i; int ret; - /* make sure our architecture has support */ - ret = acpi_processor_ffh_lpi_probe(pr->id); - if (ret == -EOPNOTSUPP) - return ret; - if (!osc_pc_lpi_support_confirmed) return -EOPNOTSUPP; @@ -1141,10 +1138,10 @@ static int acpi_processor_get_lpi_info(struct acpi_processor *pr) lpi->entry_method == ACPI_CSTATE_INTEGER) continue; - if (too_many_states(pr->handle, state_count)) + if (too_many_states(pr_handle, state_count)) break; - flpi = &pr->power.lpi_states[state_count++]; + flpi = &pr_power->lpi_states[state_count++]; memcpy(flpi, lpi, sizeof(*lpi)); stash_composite_state(curr, flpi); } @@ -1182,7 +1179,8 @@ static int acpi_processor_get_lpi_info(struct acpi_processor *pr) break; /* flatten all the LPI states in this level of hierarchy */ - state_count = flatten_lpi_states(pr, state_count, curr, prev); + state_count = flatten_lpi_states(pr_handle, pr_power->lpi_states, + state_count, curr, prev); kfree(curr->entries); @@ -1191,9 +1189,25 @@ static int acpi_processor_get_lpi_info(struct acpi_processor *pr) /* reset the index after flattening */ for (i = 0; i < state_count; i++) - pr->power.lpi_states[i].index = i; + pr_power->lpi_states[i].index = i; - pr->power.count = state_count; + pr_power->count = state_count; + + return 0; +} + +static int acpi_processor_get_lpi_info(struct acpi_processor *pr) +{ + int ret; + + /* make sure our architecture has support */ + ret = acpi_processor_ffh_lpi_probe(pr->id); + if (ret == -EOPNOTSUPP) + return ret; + + ret = acpi_processor_extract_lpi_info(pr->handle, &pr->power); + if (ret) + return ret; /* Tell driver that _LPI is supported. */ pr->flags.has_lpi = 1; From c60e851f9c179c265dd71a8ecc49abee977bff14 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Thu, 9 Jul 2026 14:41:36 +0200 Subject: [PATCH 14/18] ACPI: processor: idle: Relocate acpi_processor_extract_lpi_info() In preparation for adding ACPI _LPI support to the intel_idle driver, move acpi_processor_extract_lpi_info() along with some static functions used by it to the acpi_processor.c file containing the non-modular part of the ACPI processor driver, so it can be called by external non-modular code like intel_idle. However, export it to modules in the ACPI_PROCESSOR_IDLE import namespace so that the modular part of the ACPI processor driver can still invoke it. No intentional functional impact. Signed-off-by: Rafael J. Wysocki Reviewed-by: Sudeep Holla Acked-by: Huisong Li Link: https://patch.msgid.link/2277662.Mh6RI2rZIc@rafael.j.wysocki --- drivers/acpi/acpi_processor.c | 341 ++++++++++++++++++++++++++++++++++ drivers/acpi/processor_idle.c | 338 --------------------------------- include/linux/acpi.h | 11 ++ 3 files changed, 352 insertions(+), 338 deletions(-) diff --git a/drivers/acpi/acpi_processor.c b/drivers/acpi/acpi_processor.c index 00775b91bd41..cdba1aee84e9 100644 --- a/drivers/acpi/acpi_processor.c +++ b/drivers/acpi/acpi_processor.c @@ -994,3 +994,344 @@ end: } EXPORT_SYMBOL_NS_GPL(acpi_processor_evaluate_cst, "ACPI_PROCESSOR_IDLE"); #endif /* CONFIG_ACPI_PROCESSOR_CSTATE */ + +#ifdef CONFIG_ACPI_PROCESSOR_IDLE +struct acpi_lpi_states_array { + unsigned int size; + unsigned int composite_states_size; + struct acpi_lpi_state *entries; + struct acpi_lpi_state *composite_states[ACPI_PROCESSOR_MAX_POWER]; +}; + +static int obj_get_integer(union acpi_object *obj, u32 *value) +{ + if (obj->type != ACPI_TYPE_INTEGER) + return -EINVAL; + + *value = obj->integer.value; + return 0; +} + +#define lpi_state_debug(handle, message, state_idx) \ + acpi_handle_debug(handle, message " for _LPI state %u\n", state_idx) + +static void process_lpi_state_package(union acpi_object *lpi_pkg, + struct acpi_lpi_state *lpi_state, + acpi_handle handle, + unsigned int state_idx) +{ + union acpi_object *lpi_pkg_elem, *obj; + + if (lpi_pkg->type != ACPI_TYPE_PACKAGE || lpi_pkg->package.count < 7) + return; + + lpi_pkg_elem = lpi_pkg->package.elements; + + /* Get the entry method first and skip the state if that fails. */ + obj = &lpi_pkg_elem[6]; + if (obj->type == ACPI_TYPE_BUFFER) { + struct acpi_power_register *reg; + + if (obj->buffer.length < sizeof(*reg)) { + lpi_state_debug(handle, "Invalid register data", state_idx); + return; + } + + reg = (struct acpi_power_register *)obj->buffer.pointer; + if (reg->space_id != ACPI_ADR_SPACE_FIXED_HARDWARE) { + lpi_state_debug(handle, "Unsupported entry method", state_idx); + return; + } + + lpi_state->entry_method = ACPI_CSTATE_FFH; + lpi_state->address = reg->address; + } else if (obj->type == ACPI_TYPE_INTEGER) { + lpi_state->entry_method = ACPI_CSTATE_INTEGER; + lpi_state->address = obj->integer.value; + } else { + lpi_state_debug(handle, "Invalid entry method", state_idx); + return; + } + + if (obj_get_integer(&lpi_pkg_elem[0], &lpi_state->min_residency)) { + lpi_state_debug(handle, "Assuming 10 us min. residency", state_idx); + lpi_state->min_residency = 10; + } + + if (obj_get_integer(&lpi_pkg_elem[1], &lpi_state->wake_latency)) { + lpi_state_debug(handle, "Assuming 10 us wake latency", state_idx); + lpi_state->wake_latency = 10; + } + + if (obj_get_integer(&lpi_pkg_elem[2], &lpi_state->flags)) + lpi_state->flags = 0; + + if (obj_get_integer(&lpi_pkg_elem[3], &lpi_state->arch_flags)) + lpi_state->arch_flags = 0; + + if (obj_get_integer(&lpi_pkg_elem[4], &lpi_state->res_cnt_freq)) + lpi_state->res_cnt_freq = 1; + + if (obj_get_integer(&lpi_pkg_elem[5], &lpi_state->enable_parent_state)) + lpi_state->enable_parent_state = 0; + + /* Skip elements [7-8] i.e. Residency/Usage counters. */ + + /* + * Avoid out-of-bounds access if the size of the package is less than + * expected. + */ + if (lpi_pkg->package.count < 10) + return; + + obj = &lpi_pkg_elem[9]; + if (obj->type == ACPI_TYPE_STRING) + strscpy(lpi_state->desc, obj->string.pointer, ACPI_CX_DESC_LEN); +} + +static int acpi_processor_evaluate_lpi(acpi_handle handle, + struct acpi_lpi_states_array *info) +{ + struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL }; + union acpi_object *lpi_data, *lpi_pkg; + unsigned int lpi_pkg_count, state_idx; + struct acpi_lpi_state *lpi_state; + acpi_status status; + int ret = 0; + + status = acpi_evaluate_object(handle, "_LPI", NULL, &buffer); + if (ACPI_FAILURE(status)) { + acpi_handle_debug(handle, "No _LPI, giving up\n"); + return -ENODEV; + } + + lpi_data = buffer.pointer; + + /* There must be at least 4 elements = 3 elements + 1 package */ + if (!lpi_data || lpi_data->type != ACPI_TYPE_PACKAGE || + lpi_data->package.count < 4) { + acpi_handle_debug(handle, "Not enough elements in _LPI\n"); + ret = -ENODATA; + goto end; + } + + lpi_pkg_count = lpi_data->package.elements[2].integer.value; + + /* Validate number of power states. */ + if (!lpi_pkg_count || lpi_pkg_count != lpi_data->package.count - 3) { + acpi_handle_debug(handle, "Invalid _LPI state count\n"); + ret = -ENODATA; + goto end; + } + + lpi_state = kzalloc_objs(*lpi_state, lpi_pkg_count); + if (!lpi_state) { + ret = -ENOMEM; + goto end; + } + + info->size = lpi_pkg_count; + info->entries = lpi_state; + + /* _LPI State packages start at index 3. */ + lpi_pkg = &lpi_data->package.elements[3]; + + for (state_idx = 1; state_idx <= lpi_pkg_count; state_idx++) { + lpi_state->index = state_idx; + process_lpi_state_package(lpi_pkg++, lpi_state++, handle, state_idx); + } + + acpi_handle_debug(handle, "Found %u power states\n", lpi_pkg_count); +end: + kfree(buffer.pointer); + return ret; +} + +/** + * combine_lpi_states - combine local and parent LPI states to form a composite LPI state + * + * @local: local LPI state + * @parent: parent LPI state + * @result: composite LPI state + */ +static bool combine_lpi_states(struct acpi_lpi_state *local, + struct acpi_lpi_state *parent, + struct acpi_lpi_state *result) +{ + if (parent->entry_method == ACPI_CSTATE_INTEGER) { + if (!parent->address) /* 0 means autopromotable */ + return false; + result->address = local->address + parent->address; + } else { + result->address = parent->address; + } + + result->min_residency = max(local->min_residency, parent->min_residency); + result->wake_latency = local->wake_latency + parent->wake_latency; + result->enable_parent_state = parent->enable_parent_state; + result->entry_method = local->entry_method; + + result->flags = parent->flags; + result->arch_flags = parent->arch_flags; + result->index = parent->index; + + scnprintf(result->desc, ACPI_CX_DESC_LEN, "%s+%s", local->desc, parent->desc); + return true; +} + +#define ACPI_LPI_STATE_FLAGS_ENABLED BIT(0) + +static void stash_composite_state(struct acpi_lpi_states_array *curr_level, + struct acpi_lpi_state *t) +{ + curr_level->composite_states[curr_level->composite_states_size++] = t; +} + +static bool too_many_states(acpi_handle handle, unsigned int state_count) +{ + if (state_count < ACPI_PROCESSOR_MAX_POWER) + return false; + + acpi_handle_info(handle, "No space for more _LPI states than %d\n", + ACPI_PROCESSOR_MAX_POWER); + return true; +} + +static unsigned int flatten_lpi_states(acpi_handle handle, + struct acpi_lpi_state *lpi_states, + unsigned int state_count, + struct acpi_lpi_states_array *curr, + struct acpi_lpi_states_array *prev) +{ + struct acpi_lpi_state *parent_lpi = curr->entries; + unsigned int j; + + /* + * Combine each of the "raw" _LPI states from the current (processor + * container) level with all of the composite _LPI states from the + * previous (processor or processor container) level. + */ + for (j = 0; j < curr->size; j++, parent_lpi++) { + struct acpi_lpi_state *flpi; + int i; + + if (!(parent_lpi->flags & ACPI_LPI_STATE_FLAGS_ENABLED)) + continue; + + if (too_many_states(handle, state_count)) + break; + + flpi = &lpi_states[state_count]; + + for (i = 0; i < prev->composite_states_size; i++) { + struct acpi_lpi_state *local_lpi = prev->composite_states[i]; + + if (parent_lpi->index > local_lpi->enable_parent_state) + continue; + + if (!combine_lpi_states(local_lpi, parent_lpi, flpi)) + continue; + + stash_composite_state(curr, flpi); + state_count++; + flpi++; + + if (state_count >= ACPI_PROCESSOR_MAX_POWER) + break; + } + } + + return state_count; +} + +int acpi_processor_extract_lpi_info(acpi_handle pr_handle, + struct acpi_processor_power *pr_power) +{ + struct acpi_lpi_states_array info[2], *prev, *curr; + acpi_handle handle = pr_handle; + unsigned int state_count = 0; + unsigned int i; + int ret; + + if (!osc_pc_lpi_support_confirmed) + return -EOPNOTSUPP; + + curr = &info[0]; + curr->composite_states_size = 0; + + ret = acpi_processor_evaluate_lpi(handle, curr); + if (ret) + return ret; + + /* Copy all of the usable first-level states to power.lpi_states[]. */ + for (i = 0; i < curr->size; i++) { + struct acpi_lpi_state *lpi = &curr->entries[i]; + struct acpi_lpi_state *flpi; + + /* + * Skip states that are not enabled or have an inadequate entry + * method for this level. + */ + if (!(lpi->flags & ACPI_LPI_STATE_FLAGS_ENABLED) || + lpi->entry_method == ACPI_CSTATE_INTEGER) + continue; + + if (too_many_states(pr_handle, state_count)) + break; + + flpi = &pr_power->lpi_states[state_count++]; + memcpy(flpi, lpi, sizeof(*lpi)); + stash_composite_state(curr, flpi); + } + + kfree(curr->entries); + + /* + * If there are no _LPI states at the first level, there are no _LPI + * states at all. + */ + if (!state_count) + return -ENODATA; + + prev = curr; + curr = &info[1]; + + for (;;) { + struct acpi_lpi_states_array *tmp; + struct acpi_device *d; + + if (ACPI_FAILURE(acpi_get_parent(handle, &handle))) + break; + + d = acpi_fetch_acpi_dev(handle); + if (!d) + break; + + if (strcmp(acpi_device_hid(d), ACPI_PROCESSOR_CONTAINER_HID)) + break; + + curr->composite_states_size = 0; + + ret = acpi_processor_evaluate_lpi(handle, curr); + if (ret) + break; + + /* flatten all the LPI states in this level of hierarchy */ + state_count = flatten_lpi_states(pr_handle, pr_power->lpi_states, + state_count, curr, prev); + + kfree(curr->entries); + + tmp = prev, prev = curr, curr = tmp; + } + + /* reset the index after flattening */ + for (i = 0; i < state_count; i++) + pr_power->lpi_states[i].index = i; + + pr_power->count = state_count; + + return 0; +} +EXPORT_SYMBOL_NS_GPL(acpi_processor_extract_lpi_info, "ACPI_PROCESSOR_IDLE"); +#endif /* CONFIG_ACPI_PROCESSOR_IDLE */ diff --git a/drivers/acpi/processor_idle.c b/drivers/acpi/processor_idle.c index 2c6c9a7365fb..44a52148bc25 100644 --- a/drivers/acpi/processor_idle.c +++ b/drivers/acpi/processor_idle.c @@ -853,349 +853,11 @@ static int acpi_processor_setup_cstates(struct acpi_processor *pr) #endif /* CONFIG_ACPI_PROCESSOR_CSTATE */ -struct acpi_lpi_states_array { - unsigned int size; - unsigned int composite_states_size; - struct acpi_lpi_state *entries; - struct acpi_lpi_state *composite_states[ACPI_PROCESSOR_MAX_POWER]; -}; - -static int obj_get_integer(union acpi_object *obj, u32 *value) -{ - if (obj->type != ACPI_TYPE_INTEGER) - return -EINVAL; - - *value = obj->integer.value; - return 0; -} - -#define lpi_state_debug(handle, message, state_idx) \ - acpi_handle_debug(handle, message " for _LPI state %u\n", state_idx) - -static void process_lpi_state_package(union acpi_object *lpi_pkg, - struct acpi_lpi_state *lpi_state, - acpi_handle handle, - unsigned int state_idx) -{ - union acpi_object *lpi_pkg_elem, *obj; - - if (lpi_pkg->type != ACPI_TYPE_PACKAGE || lpi_pkg->package.count < 7) - return; - - lpi_pkg_elem = lpi_pkg->package.elements; - - /* Get the entry method first and skip the state if that fails. */ - obj = &lpi_pkg_elem[6]; - if (obj->type == ACPI_TYPE_BUFFER) { - struct acpi_power_register *reg; - - if (obj->buffer.length < sizeof(*reg)) { - lpi_state_debug(handle, "Invalid register data", state_idx); - return; - } - - reg = (struct acpi_power_register *)obj->buffer.pointer; - if (reg->space_id != ACPI_ADR_SPACE_FIXED_HARDWARE) { - lpi_state_debug(handle, "Unsupported entry method", state_idx); - return; - } - - lpi_state->entry_method = ACPI_CSTATE_FFH; - lpi_state->address = reg->address; - } else if (obj->type == ACPI_TYPE_INTEGER) { - lpi_state->entry_method = ACPI_CSTATE_INTEGER; - lpi_state->address = obj->integer.value; - } else { - lpi_state_debug(handle, "Invalid entry method", state_idx); - return; - } - - if (obj_get_integer(&lpi_pkg_elem[0], &lpi_state->min_residency)) { - lpi_state_debug(handle, "Assuming 10 us min. residency", state_idx); - lpi_state->min_residency = 10; - } - - if (obj_get_integer(&lpi_pkg_elem[1], &lpi_state->wake_latency)) { - lpi_state_debug(handle, "Assuming 10 us wake latency", state_idx); - lpi_state->wake_latency = 10; - } - - if (obj_get_integer(&lpi_pkg_elem[2], &lpi_state->flags)) - lpi_state->flags = 0; - - if (obj_get_integer(&lpi_pkg_elem[3], &lpi_state->arch_flags)) - lpi_state->arch_flags = 0; - - if (obj_get_integer(&lpi_pkg_elem[4], &lpi_state->res_cnt_freq)) - lpi_state->res_cnt_freq = 1; - - if (obj_get_integer(&lpi_pkg_elem[5], &lpi_state->enable_parent_state)) - lpi_state->enable_parent_state = 0; - - /* Skip elements [7-8] i.e. Residency/Usage counters. */ - - /* - * Avoid out-of-bounds access if the size of the package is less than - * expected. - */ - if (lpi_pkg->package.count < 10) - return; - - obj = &lpi_pkg_elem[9]; - if (obj->type == ACPI_TYPE_STRING) - strscpy(lpi_state->desc, obj->string.pointer, ACPI_CX_DESC_LEN); -} - -static int acpi_processor_evaluate_lpi(acpi_handle handle, - struct acpi_lpi_states_array *info) -{ - struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL }; - union acpi_object *lpi_data, *lpi_pkg; - unsigned int lpi_pkg_count, state_idx; - struct acpi_lpi_state *lpi_state; - acpi_status status; - int ret = 0; - - status = acpi_evaluate_object(handle, "_LPI", NULL, &buffer); - if (ACPI_FAILURE(status)) { - acpi_handle_debug(handle, "No _LPI, giving up\n"); - return -ENODEV; - } - - lpi_data = buffer.pointer; - - /* There must be at least 4 elements = 3 elements + 1 package */ - if (!lpi_data || lpi_data->type != ACPI_TYPE_PACKAGE || - lpi_data->package.count < 4) { - acpi_handle_debug(handle, "Not enough elements in _LPI\n"); - ret = -ENODATA; - goto end; - } - - lpi_pkg_count = lpi_data->package.elements[2].integer.value; - - /* Validate number of power states. */ - if (!lpi_pkg_count || lpi_pkg_count != lpi_data->package.count - 3) { - acpi_handle_debug(handle, "Invalid _LPI state count\n"); - ret = -ENODATA; - goto end; - } - - lpi_state = kzalloc_objs(*lpi_state, lpi_pkg_count); - if (!lpi_state) { - ret = -ENOMEM; - goto end; - } - - info->size = lpi_pkg_count; - info->entries = lpi_state; - - /* _LPI State packages start at index 3. */ - lpi_pkg = &lpi_data->package.elements[3]; - - for (state_idx = 1; state_idx <= lpi_pkg_count; state_idx++) { - lpi_state->index = state_idx; - process_lpi_state_package(lpi_pkg++, lpi_state++, handle, state_idx); - } - - acpi_handle_debug(handle, "Found %u power states\n", lpi_pkg_count); -end: - kfree(buffer.pointer); - return ret; -} - -/** - * combine_lpi_states - combine local and parent LPI states to form a composite LPI state - * - * @local: local LPI state - * @parent: parent LPI state - * @result: composite LPI state - */ -static bool combine_lpi_states(struct acpi_lpi_state *local, - struct acpi_lpi_state *parent, - struct acpi_lpi_state *result) -{ - if (parent->entry_method == ACPI_CSTATE_INTEGER) { - if (!parent->address) /* 0 means autopromotable */ - return false; - result->address = local->address + parent->address; - } else { - result->address = parent->address; - } - - result->min_residency = max(local->min_residency, parent->min_residency); - result->wake_latency = local->wake_latency + parent->wake_latency; - result->enable_parent_state = parent->enable_parent_state; - result->entry_method = local->entry_method; - - result->flags = parent->flags; - result->arch_flags = parent->arch_flags; - result->index = parent->index; - - scnprintf(result->desc, ACPI_CX_DESC_LEN, "%s+%s", local->desc, parent->desc); - return true; -} - -#define ACPI_LPI_STATE_FLAGS_ENABLED BIT(0) - -static void stash_composite_state(struct acpi_lpi_states_array *curr_level, - struct acpi_lpi_state *t) -{ - curr_level->composite_states[curr_level->composite_states_size++] = t; -} - -static bool too_many_states(acpi_handle handle, unsigned int state_count) -{ - if (state_count < ACPI_PROCESSOR_MAX_POWER) - return false; - - acpi_handle_info(handle, "No space for more _LPI states than %d\n", - ACPI_PROCESSOR_MAX_POWER); - return true; -} - -static unsigned int flatten_lpi_states(acpi_handle handle, - struct acpi_lpi_state *lpi_states, - unsigned int state_count, - struct acpi_lpi_states_array *curr, - struct acpi_lpi_states_array *prev) -{ - struct acpi_lpi_state *parent_lpi = curr->entries; - unsigned int j; - - /* - * Combine each of the "raw" _LPI states from the current (processor - * container) level with all of the composite _LPI states from the - * previous (processor or processor container) level. - */ - for (j = 0; j < curr->size; j++, parent_lpi++) { - struct acpi_lpi_state *flpi; - int i; - - if (!(parent_lpi->flags & ACPI_LPI_STATE_FLAGS_ENABLED)) - continue; - - if (too_many_states(handle, state_count)) - break; - - flpi = &lpi_states[state_count]; - - for (i = 0; i < prev->composite_states_size; i++) { - struct acpi_lpi_state *local_lpi = prev->composite_states[i]; - - if (parent_lpi->index > local_lpi->enable_parent_state) - continue; - - if (!combine_lpi_states(local_lpi, parent_lpi, flpi)) - continue; - - stash_composite_state(curr, flpi); - state_count++; - flpi++; - - if (state_count >= ACPI_PROCESSOR_MAX_POWER) - break; - } - } - - return state_count; -} - int __weak acpi_processor_ffh_lpi_probe(unsigned int cpu) { return -EOPNOTSUPP; } -static int acpi_processor_extract_lpi_info(acpi_handle pr_handle, - struct acpi_processor_power *pr_power) -{ - struct acpi_lpi_states_array info[2], *prev, *curr; - acpi_handle handle = pr_handle; - unsigned int state_count = 0; - unsigned int i; - int ret; - - if (!osc_pc_lpi_support_confirmed) - return -EOPNOTSUPP; - - curr = &info[0]; - curr->composite_states_size = 0; - - ret = acpi_processor_evaluate_lpi(handle, curr); - if (ret) - return ret; - - /* Copy all of the usable first-level states to power.lpi_states[]. */ - for (i = 0; i < curr->size; i++) { - struct acpi_lpi_state *lpi = &curr->entries[i]; - struct acpi_lpi_state *flpi; - - /* - * Skip states that are not enabled or have an inadequate entry - * method for this level. - */ - if (!(lpi->flags & ACPI_LPI_STATE_FLAGS_ENABLED) || - lpi->entry_method == ACPI_CSTATE_INTEGER) - continue; - - if (too_many_states(pr_handle, state_count)) - break; - - flpi = &pr_power->lpi_states[state_count++]; - memcpy(flpi, lpi, sizeof(*lpi)); - stash_composite_state(curr, flpi); - } - - kfree(curr->entries); - - /* - * If there are no _LPI states at the first level, there are no _LPI - * states at all. - */ - if (!state_count) - return -ENODATA; - - prev = curr; - curr = &info[1]; - - for (;;) { - struct acpi_lpi_states_array *tmp; - struct acpi_device *d; - - if (ACPI_FAILURE(acpi_get_parent(handle, &handle))) - break; - - d = acpi_fetch_acpi_dev(handle); - if (!d) - break; - - if (strcmp(acpi_device_hid(d), ACPI_PROCESSOR_CONTAINER_HID)) - break; - - curr->composite_states_size = 0; - - ret = acpi_processor_evaluate_lpi(handle, curr); - if (ret) - break; - - /* flatten all the LPI states in this level of hierarchy */ - state_count = flatten_lpi_states(pr_handle, pr_power->lpi_states, - state_count, curr, prev); - - kfree(curr->entries); - - tmp = prev, prev = curr, curr = tmp; - } - - /* reset the index after flattening */ - for (i = 0; i < state_count; i++) - pr_power->lpi_states[i].index = i; - - pr_power->count = state_count; - - return 0; -} - static int acpi_processor_get_lpi_info(struct acpi_processor *pr) { int ret; diff --git a/include/linux/acpi.h b/include/linux/acpi.h index 60ab50cb8930..1d3ea92a2344 100644 --- a/include/linux/acpi.h +++ b/include/linux/acpi.h @@ -315,6 +315,17 @@ static inline int acpi_processor_evaluate_cst(acpi_handle handle, u32 cpu, } #endif +#ifdef CONFIG_ACPI_PROCESSOR_IDLE +int acpi_processor_extract_lpi_info(acpi_handle pr_handle, + struct acpi_processor_power *pr_power); +#else +static inline int acpi_processor_extract_lpi_info(acpi_handle pr_handle, + struct acpi_processor_power *pr_power) +{ + return -ENODEV; +} +#endif + #ifdef CONFIG_ACPI_HOTPLUG_CPU /* Arch dependent functions for cpu hotplug support */ int acpi_map_cpu(acpi_handle handle, phys_cpuid_t physid, u32 acpi_id, From 67fcf679c80835a6110190dfd3287a93a7df1dfb Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Thu, 9 Jul 2026 14:42:33 +0200 Subject: [PATCH 15/18] ACPI: processor: idle: Add switch for strict _LPI processing Add a "strict" argument to acpi_processor_extract_lpi_info() that, when set, will cause it to ignore _LPI states without minimum residency or wake latency instead of assuming 10 us values for these parameters. No intentional functional impact. Signed-off-by: Rafael J. Wysocki Reviewed-by: Sudeep Holla Acked-by: Huisong Li Link: https://patch.msgid.link/3896986.MHq7AAxBmi@rafael.j.wysocki --- drivers/acpi/acpi_processor.c | 25 +++++++++++++++++++------ drivers/acpi/processor_idle.c | 2 +- include/linux/acpi.h | 6 ++++-- 3 files changed, 24 insertions(+), 9 deletions(-) diff --git a/drivers/acpi/acpi_processor.c b/drivers/acpi/acpi_processor.c index cdba1aee84e9..5fbab54171b7 100644 --- a/drivers/acpi/acpi_processor.c +++ b/drivers/acpi/acpi_processor.c @@ -1018,7 +1018,7 @@ static int obj_get_integer(union acpi_object *obj, u32 *value) static void process_lpi_state_package(union acpi_object *lpi_pkg, struct acpi_lpi_state *lpi_state, acpi_handle handle, - unsigned int state_idx) + unsigned int state_idx, bool strict) { union acpi_object *lpi_pkg_elem, *obj; @@ -1054,11 +1054,21 @@ static void process_lpi_state_package(union acpi_object *lpi_pkg, } if (obj_get_integer(&lpi_pkg_elem[0], &lpi_state->min_residency)) { + if (strict) { + lpi_state_debug(handle, "No min. residency", state_idx); + return; + } + lpi_state_debug(handle, "Assuming 10 us min. residency", state_idx); lpi_state->min_residency = 10; } if (obj_get_integer(&lpi_pkg_elem[1], &lpi_state->wake_latency)) { + if (strict) { + lpi_state_debug(handle, "No wake latency", state_idx); + return; + } + lpi_state_debug(handle, "Assuming 10 us wake latency", state_idx); lpi_state->wake_latency = 10; } @@ -1090,7 +1100,8 @@ static void process_lpi_state_package(union acpi_object *lpi_pkg, } static int acpi_processor_evaluate_lpi(acpi_handle handle, - struct acpi_lpi_states_array *info) + struct acpi_lpi_states_array *info, + bool strict) { struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL }; union acpi_object *lpi_data, *lpi_pkg; @@ -1138,7 +1149,8 @@ static int acpi_processor_evaluate_lpi(acpi_handle handle, for (state_idx = 1; state_idx <= lpi_pkg_count; state_idx++) { lpi_state->index = state_idx; - process_lpi_state_package(lpi_pkg++, lpi_state++, handle, state_idx); + process_lpi_state_package(lpi_pkg++, lpi_state++, handle, + state_idx, strict); } acpi_handle_debug(handle, "Found %u power states\n", lpi_pkg_count); @@ -1245,7 +1257,8 @@ static unsigned int flatten_lpi_states(acpi_handle handle, } int acpi_processor_extract_lpi_info(acpi_handle pr_handle, - struct acpi_processor_power *pr_power) + struct acpi_processor_power *pr_power, + bool strict) { struct acpi_lpi_states_array info[2], *prev, *curr; acpi_handle handle = pr_handle; @@ -1259,7 +1272,7 @@ int acpi_processor_extract_lpi_info(acpi_handle pr_handle, curr = &info[0]; curr->composite_states_size = 0; - ret = acpi_processor_evaluate_lpi(handle, curr); + ret = acpi_processor_evaluate_lpi(handle, curr, strict); if (ret) return ret; @@ -1312,7 +1325,7 @@ int acpi_processor_extract_lpi_info(acpi_handle pr_handle, curr->composite_states_size = 0; - ret = acpi_processor_evaluate_lpi(handle, curr); + ret = acpi_processor_evaluate_lpi(handle, curr, strict); if (ret) break; diff --git a/drivers/acpi/processor_idle.c b/drivers/acpi/processor_idle.c index 44a52148bc25..e113bcbbb882 100644 --- a/drivers/acpi/processor_idle.c +++ b/drivers/acpi/processor_idle.c @@ -867,7 +867,7 @@ static int acpi_processor_get_lpi_info(struct acpi_processor *pr) if (ret == -EOPNOTSUPP) return ret; - ret = acpi_processor_extract_lpi_info(pr->handle, &pr->power); + ret = acpi_processor_extract_lpi_info(pr->handle, &pr->power, false); if (ret) return ret; diff --git a/include/linux/acpi.h b/include/linux/acpi.h index 1d3ea92a2344..9e418b23373c 100644 --- a/include/linux/acpi.h +++ b/include/linux/acpi.h @@ -317,10 +317,12 @@ static inline int acpi_processor_evaluate_cst(acpi_handle handle, u32 cpu, #ifdef CONFIG_ACPI_PROCESSOR_IDLE int acpi_processor_extract_lpi_info(acpi_handle pr_handle, - struct acpi_processor_power *pr_power); + struct acpi_processor_power *pr_power, + bool strict); #else static inline int acpi_processor_extract_lpi_info(acpi_handle pr_handle, - struct acpi_processor_power *pr_power) + struct acpi_processor_power *pr_power, + bool strict) { return -ENODEV; } From 4d7821961f788615ef6fe0ad6d8a034579800e61 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Thu, 9 Jul 2026 14:43:43 +0200 Subject: [PATCH 16/18] intel_idle: Prepare for adding ACPI _LPI support In preparation for adding ACPI _LPI support to intel_idle, move some code used for processing ACPI idle states information coming from _CST objects to separate functions because that code will be also used for processing idle states information coming from _LPI. No intentional functional impact. Signed-off-by: Rafael J. Wysocki Link: https://patch.msgid.link/1932965.atdPhlSkOF@rafael.j.wysocki --- drivers/idle/intel_idle.c | 35 ++++++++++++++++++++++------------- 1 file changed, 22 insertions(+), 13 deletions(-) diff --git a/drivers/idle/intel_idle.c b/drivers/idle/intel_idle.c index d74b478db280..fedaa8142121 100644 --- a/drivers/idle/intel_idle.c +++ b/drivers/idle/intel_idle.c @@ -1837,6 +1837,16 @@ static bool __init intel_idle_acpi_cst_extract(void) return false; } +static void __init intel_idle_complete_state_init(struct cpuidle_state *state) +{ + if (intel_idle_state_needs_timer_stop(state)) + state->flags |= CPUIDLE_FLAG_TIMER_STOP; + + state->enter = intel_idle; + state->enter_dead = intel_idle_enter_dead; + state->enter_s2idle = intel_idle_s2idle; +} + static void __init intel_idle_init_cstates_acpi(struct cpuidle_driver *drv) { int cstate, limit = min_t(int, CPUIDLE_STATE_MAX, acpi_state_table.count); @@ -1879,18 +1889,23 @@ static void __init intel_idle_init_cstates_acpi(struct cpuidle_driver *drv) if (disabled_states_mask & BIT(cstate)) state->flags |= CPUIDLE_FLAG_OFF; - if (intel_idle_state_needs_timer_stop(state)) - state->flags |= CPUIDLE_FLAG_TIMER_STOP; - if (cx->type > ACPI_STATE_C1 && !boot_cpu_has(X86_FEATURE_NONSTOP_TSC)) mark_tsc_unstable("TSC halts in idle"); - state->enter = intel_idle; - state->enter_dead = intel_idle_enter_dead; - state->enter_s2idle = intel_idle_s2idle; + intel_idle_complete_state_init(state); } } +static bool __init intel_idle_acpi_hint_match(unsigned int flags, u32 acpi_hint, + u32 table_hint) +{ + if (flags & CPUIDLE_FLAG_PARTIAL_HINT_MATCH) { + acpi_hint &= ~MWAIT_SUBSTATE_MASK; + table_hint &= ~MWAIT_SUBSTATE_MASK; + } + return acpi_hint == table_hint; +} + static bool __init intel_idle_off_by_default(unsigned int flags, u32 mwait_hint) { int cstate, limit; @@ -1909,14 +1924,8 @@ static bool __init intel_idle_off_by_default(unsigned int flags, u32 mwait_hint) */ for (cstate = 1; cstate < limit; cstate++) { u32 acpi_hint = acpi_state_table.states[cstate].address; - u32 table_hint = mwait_hint; - if (flags & CPUIDLE_FLAG_PARTIAL_HINT_MATCH) { - acpi_hint &= ~MWAIT_SUBSTATE_MASK; - table_hint &= ~MWAIT_SUBSTATE_MASK; - } - - if (acpi_hint == table_hint) + if (intel_idle_acpi_hint_match(flags, acpi_hint, mwait_hint)) return false; } return true; From abc5c103c66d9b8102605e63ad0ebb4cadc58995 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Thu, 9 Jul 2026 14:44:23 +0200 Subject: [PATCH 17/18] 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 Link: https://patch.msgid.link/2280567.Icojqenx9y@rafael.j.wysocki --- drivers/idle/intel_idle.c | 164 ++++++++++++++++++++++++++++++++------ 1 file changed, 140 insertions(+), 24 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; } - 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; } From c0139e2b2c047d054271f484a1765d5edccd816a Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Wed, 22 Jul 2026 14:53:31 +0200 Subject: [PATCH 18/18] intel_idle: Update documentation after adding ACPI _LPI support After adding ACPI _LPI support to intel_idle, update its admin-guide documentation to cover the changed behavior. Signed-off-by: Rafael J. Wysocki Reviewed-by: Sudeep Holla Link: https://patch.msgid.link/12962673.O9o76ZdvQC@rafael.j.wysocki --- Documentation/admin-guide/pm/intel_idle.rst | 54 ++++++++++++--------- 1 file changed, 31 insertions(+), 23 deletions(-) diff --git a/Documentation/admin-guide/pm/intel_idle.rst b/Documentation/admin-guide/pm/intel_idle.rst index 188d52cd26e8..996e5bd83c42 100644 --- a/Documentation/admin-guide/pm/intel_idle.rst +++ b/Documentation/admin-guide/pm/intel_idle.rst @@ -87,17 +87,22 @@ tables with any processor model recognized by it; see `below `_.] If the ACPI tables are going to be used for building the list of available idle -states, ``intel_idle`` first looks for a ``_CST`` object under one of the ACPI -objects corresponding to the CPUs in the system (refer to the ACPI specification -[2]_ for the description of ``_CST`` and its output package). Because the -``CPUIdle`` subsystem expects that the list of idle states supplied by the -driver will be suitable for all of the CPUs handled by it and ``intel_idle`` is -registered as the ``CPUIdle`` driver for all of the CPUs in the system, the -driver looks for the first ``_CST`` object returning at least one valid idle -state description and such that all of the idle states included in its return -package are of the FFH (Functional Fixed Hardware) type, which means that the -``MWAIT`` instruction is expected to be used to tell the processor that it can -enter one of them. The return package of that ``_CST`` is then assumed to be +states, ``intel_idle`` will be looking for ``_LPI`` or ``_CST`` objects in them +(refer to the ACPI specification [2]_ for the definitions of the ``_LPI`` and +``_CST`` objects). If ``_LPI`` is present under at least one of the ACPI +objects representing the CPUs in the system and ``_LPI`` processing produces a +non-empty list of valid idle states, it will be used. Otherwise, ``_CST`` will +be used so long as it is present under at least one of the ACPI objects +representing the CPUs in the system and it returns a non-empty list of valid +idle states. In either case, since the ``CPUIdle`` subsystem expects that the +list of idle states supplied by the driver will be suitable for all of the CPUs +handled by it and ``intel_idle`` is registered as the ``CPUIdle`` driver for all +of the CPUs in the system, ``intel_idle`` looks for the first CPU where the +ACPI-supplied list of idle states (coming from either ``_LPI`` or ``_CST``) +is not empty. Moreover, all of the states in that list need to be of the FFH +(Functional Fixed Hardware) type, which means that the ``MWAIT`` instruction is +expected to be used to tell the processor that the given idle state may be +entered. If that expectation is met, the list of idle states is assumed to be applicable to all of the other CPUs in the system and the idle state descriptions extracted from it are stored in a preliminary list of idle states coming from the ACPI tables. [This step is skipped if ``intel_idle`` is @@ -129,18 +134,21 @@ If the given processor model is not recognized by ``intel_idle``, but it supports ``MWAIT``, the preliminary list of idle states coming from the ACPI tables is used for building the final list that will be supplied to the ``CPUIdle`` core during driver registration. For each idle state in that list, -the description, ``MWAIT`` hint and exit latency are copied to the corresponding -entry in the final list of idle states. The name of the idle state represented -by it (to be returned by the ``name`` idle state attribute in ``sysfs``) is -"CX_ACPI", where X is the index of that idle state in the final list (note that -the minimum value of X is 1, because 0 is reserved for the "polling" state), and -its target residency is based on the exit latency value. Specifically, for -C1-type idle states the exit latency value is also used as the target residency -(for compatibility with the majority of the "internal" tables of idle states for -various processor models recognized by ``intel_idle``) and for the other idle -state types (C2 and C3) the target residency value is 3 times the exit latency -(again, that is because it reflects the target residency to exit latency ratio -in the majority of cases for the processor models recognized by ``intel_idle``). +the description, ``MWAIT`` hint and exit (wake) latency are copied to the +corresponding entry in the final list of idle states. If the preliminary list +of idle states has been obtained through ``_LPI`` processing, the minimum +residency parameter of the given idle state is taken as its target residency. +Otherwise, for C1-type idle states, the exit latency value is also used as the +target residency (for compatibility with the majority of the "internal" tables +of idle states for various processor models recognized by ``intel_idle``), and +for the other idle state types (C2 and C3) the target residency value is 3 times +the exit latency (again, that is because it reflects the target residency to +exit latency ratio in the majority of cases for the processor models recognized +by ``intel_idle``). The name of the idle state (to be returned by the ``name`` +idle state attribute in ``sysfs``) is either "Cx_LPI" (if it comes from ``_LPI`` +processing) or "Cx_ACPI", where x is the index of that idle state in the final +list (note that the minimum value of x is 1, because 0 is reserved for the +"polling" state), and its target residency is based on the exit latency value. All of the idle states in the final list are enabled by default in this case.