* [PATCH v1 01/17] ACPI: processor: idle: Expand _LPI package sanity checks
2026-07-09 12:29 [PATCH v1 00/17] ACPI: processor: idle/intel_idle: Add ACPI _LPI support to intel_idle Rafael J. Wysocki
@ 2026-07-09 12:30 ` Rafael J. Wysocki
2026-07-09 12:31 ` [PATCH v1 02/17] ACPI: processor: idle: Ignore _LPI states with SYSTEMIO entry method Rafael J. Wysocki
` (15 subsequent siblings)
16 siblings, 0 replies; 18+ messages in thread
From: Rafael J. Wysocki @ 2026-07-09 12:30 UTC (permalink / raw)
To: Linux PM; +Cc: Linux ACPI, Sudeep Holla
From: "Rafael J. Wysocki" <rafael.j.wysocki@intel.com>
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 <rafael.j.wysocki@intel.com>
---
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);
--
2.51.0
^ permalink raw reply related [flat|nested] 18+ messages in thread* [PATCH v1 02/17] ACPI: processor: idle: Ignore _LPI states with SYSTEMIO entry method
2026-07-09 12:29 [PATCH v1 00/17] ACPI: processor: idle/intel_idle: Add ACPI _LPI support to intel_idle Rafael J. Wysocki
2026-07-09 12:30 ` [PATCH v1 01/17] ACPI: processor: idle: Expand _LPI package sanity checks Rafael J. Wysocki
@ 2026-07-09 12:31 ` Rafael J. Wysocki
2026-07-09 12:32 ` [PATCH v1 03/17] ACPI: processor: idle: Unify debug in acpi_processor_evaluate_lpi() Rafael J. Wysocki
` (14 subsequent siblings)
16 siblings, 0 replies; 18+ messages in thread
From: Rafael J. Wysocki @ 2026-07-09 12:31 UTC (permalink / raw)
To: Linux PM; +Cc: Linux ACPI, Sudeep Holla
From: "Rafael J. Wysocki" <rafael.j.wysocki@intel.com>
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 <rafael.j.wysocki@intel.com>
---
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;
--
2.51.0
^ permalink raw reply related [flat|nested] 18+ messages in thread* [PATCH v1 03/17] ACPI: processor: idle: Unify debug in acpi_processor_evaluate_lpi()
2026-07-09 12:29 [PATCH v1 00/17] ACPI: processor: idle/intel_idle: Add ACPI _LPI support to intel_idle Rafael J. Wysocki
2026-07-09 12:30 ` [PATCH v1 01/17] ACPI: processor: idle: Expand _LPI package sanity checks Rafael J. Wysocki
2026-07-09 12:31 ` [PATCH v1 02/17] ACPI: processor: idle: Ignore _LPI states with SYSTEMIO entry method Rafael J. Wysocki
@ 2026-07-09 12:32 ` Rafael J. Wysocki
2026-07-09 12:33 ` [PATCH v1 04/17] ACPI: processor: idle: Rearrange acpi_processor_evaluate_lpi() Rafael J. Wysocki
` (13 subsequent siblings)
16 siblings, 0 replies; 18+ messages in thread
From: Rafael J. Wysocki @ 2026-07-09 12:32 UTC (permalink / raw)
To: Linux PM; +Cc: Linux ACPI, Sudeep Holla
From: "Rafael J. Wysocki" <rafael.j.wysocki@intel.com>
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 <rafael.j.wysocki@intel.com>
---
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;
}
--
2.51.0
^ permalink raw reply related [flat|nested] 18+ messages in thread* [PATCH v1 04/17] ACPI: processor: idle: Rearrange acpi_processor_evaluate_lpi()
2026-07-09 12:29 [PATCH v1 00/17] ACPI: processor: idle/intel_idle: Add ACPI _LPI support to intel_idle Rafael J. Wysocki
` (2 preceding siblings ...)
2026-07-09 12:32 ` [PATCH v1 03/17] ACPI: processor: idle: Unify debug in acpi_processor_evaluate_lpi() Rafael J. Wysocki
@ 2026-07-09 12:33 ` Rafael J. Wysocki
2026-07-09 12:34 ` [PATCH v1 05/17] ACPI: processor: idle: Split acpi_processor_evaluate_lpi() Rafael J. Wysocki
` (12 subsequent siblings)
16 siblings, 0 replies; 18+ messages in thread
From: Rafael J. Wysocki @ 2026-07-09 12:33 UTC (permalink / raw)
To: Linux PM; +Cc: Linux ACPI, Sudeep Holla
From: "Rafael J. Wysocki" <rafael.j.wysocki@intel.com>
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 <rafael.j.wysocki@intel.com>
---
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)
{
- 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;
+ 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)) {
@@ -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;
--
2.51.0
^ permalink raw reply related [flat|nested] 18+ messages in thread* [PATCH v1 05/17] ACPI: processor: idle: Split acpi_processor_evaluate_lpi()
2026-07-09 12:29 [PATCH v1 00/17] ACPI: processor: idle/intel_idle: Add ACPI _LPI support to intel_idle Rafael J. Wysocki
` (3 preceding siblings ...)
2026-07-09 12:33 ` [PATCH v1 04/17] ACPI: processor: idle: Rearrange acpi_processor_evaluate_lpi() Rafael J. Wysocki
@ 2026-07-09 12:34 ` Rafael J. Wysocki
2026-07-09 12:35 ` [PATCH v1 06/17] ACPI: processor: idle: Introduce lpi_state_debug() Rafael J. Wysocki
` (11 subsequent siblings)
16 siblings, 0 replies; 18+ messages in thread
From: Rafael J. Wysocki @ 2026-07-09 12:34 UTC (permalink / raw)
To: Linux PM; +Cc: Linux ACPI, Sudeep Holla
From: "Rafael J. Wysocki" <rafael.j.wysocki@intel.com>
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 <rafael.j.wysocki@intel.com>
---
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);
--
2.51.0
^ permalink raw reply related [flat|nested] 18+ messages in thread* [PATCH v1 06/17] ACPI: processor: idle: Introduce lpi_state_debug()
2026-07-09 12:29 [PATCH v1 00/17] ACPI: processor: idle/intel_idle: Add ACPI _LPI support to intel_idle Rafael J. Wysocki
` (4 preceding siblings ...)
2026-07-09 12:34 ` [PATCH v1 05/17] ACPI: processor: idle: Split acpi_processor_evaluate_lpi() Rafael J. Wysocki
@ 2026-07-09 12:35 ` Rafael J. Wysocki
2026-07-09 12:36 ` [PATCH v1 07/17] ACPI: processor: idle: Rearrange acpi_processor_get_lpi_info() Rafael J. Wysocki
` (10 subsequent siblings)
16 siblings, 0 replies; 18+ messages in thread
From: Rafael J. Wysocki @ 2026-07-09 12:35 UTC (permalink / raw)
To: Linux PM; +Cc: Linux ACPI, Sudeep Holla
From: "Rafael J. Wysocki" <rafael.j.wysocki@intel.com>
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 <rafael.j.wysocki@intel.com>
---
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;
}
--
2.51.0
^ permalink raw reply related [flat|nested] 18+ messages in thread* [PATCH v1 07/17] ACPI: processor: idle: Rearrange acpi_processor_get_lpi_info()
2026-07-09 12:29 [PATCH v1 00/17] ACPI: processor: idle/intel_idle: Add ACPI _LPI support to intel_idle Rafael J. Wysocki
` (5 preceding siblings ...)
2026-07-09 12:35 ` [PATCH v1 06/17] ACPI: processor: idle: Introduce lpi_state_debug() Rafael J. Wysocki
@ 2026-07-09 12:36 ` Rafael J. Wysocki
2026-07-09 12:37 ` [PATCH v1 08/17] ACPI: processor: idle: Rework first-level _LPI states processing Rafael J. Wysocki
` (9 subsequent siblings)
16 siblings, 0 replies; 18+ messages in thread
From: Rafael J. Wysocki @ 2026-07-09 12:36 UTC (permalink / raw)
To: Linux PM; +Cc: Linux ACPI, Sudeep Holla
From: "Rafael J. Wysocki" <rafael.j.wysocki@intel.com>
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 <rafael.j.wysocki@intel.com>
---
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 */
--
2.51.0
^ permalink raw reply related [flat|nested] 18+ messages in thread* [PATCH v1 08/17] ACPI: processor: idle: Rework first-level _LPI states processing
2026-07-09 12:29 [PATCH v1 00/17] ACPI: processor: idle/intel_idle: Add ACPI _LPI support to intel_idle Rafael J. Wysocki
` (6 preceding siblings ...)
2026-07-09 12:36 ` [PATCH v1 07/17] ACPI: processor: idle: Rearrange acpi_processor_get_lpi_info() Rafael J. Wysocki
@ 2026-07-09 12:37 ` Rafael J. Wysocki
2026-07-09 12:37 ` [PATCH v1 09/17] ACPI: processor: idle: Drop redundant _LPI presence checks Rafael J. Wysocki
` (8 subsequent siblings)
16 siblings, 0 replies; 18+ messages in thread
From: Rafael J. Wysocki @ 2026-07-09 12:37 UTC (permalink / raw)
To: Linux PM; +Cc: Linux ACPI, Sudeep Holla
From: "Rafael J. Wysocki" <rafael.j.wysocki@intel.com>
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 <rafael.j.wysocki@intel.com>
---
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];
--
2.51.0
^ permalink raw reply related [flat|nested] 18+ messages in thread* [PATCH v1 09/17] ACPI: processor: idle: Drop redundant _LPI presence checks
2026-07-09 12:29 [PATCH v1 00/17] ACPI: processor: idle/intel_idle: Add ACPI _LPI support to intel_idle Rafael J. Wysocki
` (7 preceding siblings ...)
2026-07-09 12:37 ` [PATCH v1 08/17] ACPI: processor: idle: Rework first-level _LPI states processing Rafael J. Wysocki
@ 2026-07-09 12:37 ` Rafael J. Wysocki
2026-07-09 12:38 ` [PATCH v1 10/17] ACPI: processor: idle: Rearrange loop in acpi_processor_get_lpi_info() Rafael J. Wysocki
` (7 subsequent siblings)
16 siblings, 0 replies; 18+ messages in thread
From: Rafael J. Wysocki @ 2026-07-09 12:37 UTC (permalink / raw)
To: Linux PM; +Cc: Linux ACPI, Sudeep Holla
From: "Rafael J. Wysocki" <rafael.j.wysocki@intel.com>
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 <rafael.j.wysocki@intel.com>
---
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;
--
2.51.0
^ permalink raw reply related [flat|nested] 18+ messages in thread* [PATCH v1 10/17] ACPI: processor: idle: Rearrange loop in acpi_processor_get_lpi_info()
2026-07-09 12:29 [PATCH v1 00/17] ACPI: processor: idle/intel_idle: Add ACPI _LPI support to intel_idle Rafael J. Wysocki
` (8 preceding siblings ...)
2026-07-09 12:37 ` [PATCH v1 09/17] ACPI: processor: idle: Drop redundant _LPI presence checks Rafael J. Wysocki
@ 2026-07-09 12:38 ` Rafael J. Wysocki
2026-07-09 12:39 ` [PATCH v1 11/17] ACPI: processor: idle: Rework flatten_lpi_states() Rafael J. Wysocki
` (6 subsequent siblings)
16 siblings, 0 replies; 18+ messages in thread
From: Rafael J. Wysocki @ 2026-07-09 12:38 UTC (permalink / raw)
To: Linux PM; +Cc: Linux ACPI, Sudeep Holla
From: "Rafael J. Wysocki" <rafael.j.wysocki@intel.com>
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 <rafael.j.wysocki@intel.com>
---
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 */
--
2.51.0
^ permalink raw reply related [flat|nested] 18+ messages in thread* [PATCH v1 11/17] ACPI: processor: idle: Rework flatten_lpi_states()
2026-07-09 12:29 [PATCH v1 00/17] ACPI: processor: idle/intel_idle: Add ACPI _LPI support to intel_idle Rafael J. Wysocki
` (9 preceding siblings ...)
2026-07-09 12:38 ` [PATCH v1 10/17] ACPI: processor: idle: Rearrange loop in acpi_processor_get_lpi_info() Rafael J. Wysocki
@ 2026-07-09 12:39 ` Rafael J. Wysocki
2026-07-09 12:39 ` [PATCH v1 12/17] ACPI: processor: idle: Introduce too_many_states() for _LPI Rafael J. Wysocki
` (5 subsequent siblings)
16 siblings, 0 replies; 18+ messages in thread
From: Rafael J. Wysocki @ 2026-07-09 12:39 UTC (permalink / raw)
To: Linux PM; +Cc: Linux ACPI, Sudeep Holla
From: "Rafael J. Wysocki" <rafael.j.wysocki@intel.com>
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 <rafael.j.wysocki@intel.com>
---
drivers/acpi/processor_idle.c | 65 +++++++++++++++++++++--------------
1 file changed, 39 insertions(+), 26 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];
-
- 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;
- }
+ flpi = &pr->power.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;
}
}
- 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;
}
--
2.51.0
^ permalink raw reply related [flat|nested] 18+ messages in thread* [PATCH v1 12/17] ACPI: processor: idle: Introduce too_many_states() for _LPI
2026-07-09 12:29 [PATCH v1 00/17] ACPI: processor: idle/intel_idle: Add ACPI _LPI support to intel_idle Rafael J. Wysocki
` (10 preceding siblings ...)
2026-07-09 12:39 ` [PATCH v1 11/17] ACPI: processor: idle: Rework flatten_lpi_states() Rafael J. Wysocki
@ 2026-07-09 12:39 ` Rafael J. Wysocki
2026-07-09 12:40 ` [PATCH v1 13/17] ACPI: processor: idle: Introduce acpi_processor_extract_lpi_info() Rafael J. Wysocki
` (4 subsequent siblings)
16 siblings, 0 replies; 18+ messages in thread
From: Rafael J. Wysocki @ 2026-07-09 12:39 UTC (permalink / raw)
To: Linux PM; +Cc: Linux ACPI, Sudeep Holla
From: "Rafael J. Wysocki" <rafael.j.wysocki@intel.com>
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 <rafael.j.wysocki@intel.com>
---
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));
--
2.51.0
^ permalink raw reply related [flat|nested] 18+ messages in thread* [PATCH v1 13/17] ACPI: processor: idle: Introduce acpi_processor_extract_lpi_info()
2026-07-09 12:29 [PATCH v1 00/17] ACPI: processor: idle/intel_idle: Add ACPI _LPI support to intel_idle Rafael J. Wysocki
` (11 preceding siblings ...)
2026-07-09 12:39 ` [PATCH v1 12/17] ACPI: processor: idle: Introduce too_many_states() for _LPI Rafael J. Wysocki
@ 2026-07-09 12:40 ` Rafael J. Wysocki
2026-07-09 12:41 ` [PATCH v1 14/17] ACPI: processor: idle: Relocate acpi_processor_extract_lpi_info() Rafael J. Wysocki
` (3 subsequent siblings)
16 siblings, 0 replies; 18+ messages in thread
From: Rafael J. Wysocki @ 2026-07-09 12:40 UTC (permalink / raw)
To: Linux PM; +Cc: Linux ACPI, Sudeep Holla
From: "Rafael J. Wysocki" <rafael.j.wysocki@intel.com>
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 <rafael.j.wysocki@intel.com>
---
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;
--
2.51.0
^ permalink raw reply related [flat|nested] 18+ messages in thread* [PATCH v1 14/17] ACPI: processor: idle: Relocate acpi_processor_extract_lpi_info()
2026-07-09 12:29 [PATCH v1 00/17] ACPI: processor: idle/intel_idle: Add ACPI _LPI support to intel_idle Rafael J. Wysocki
` (12 preceding siblings ...)
2026-07-09 12:40 ` [PATCH v1 13/17] ACPI: processor: idle: Introduce acpi_processor_extract_lpi_info() Rafael J. Wysocki
@ 2026-07-09 12:41 ` Rafael J. Wysocki
2026-07-09 12:42 ` [PATCH v1 15/17] ACPI: processor: idle: Add switch for strict _LPI processing Rafael J. Wysocki
` (2 subsequent siblings)
16 siblings, 0 replies; 18+ messages in thread
From: Rafael J. Wysocki @ 2026-07-09 12:41 UTC (permalink / raw)
To: Linux PM; +Cc: Linux ACPI, Sudeep Holla
From: "Rafael J. Wysocki" <rafael.j.wysocki@intel.com>
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 <rafael.j.wysocki@intel.com>
---
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 @@ int acpi_processor_evaluate_cst(acpi_handle handle, u32 cpu,
}
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,
--
2.51.0
^ permalink raw reply related [flat|nested] 18+ messages in thread* [PATCH v1 15/17] ACPI: processor: idle: Add switch for strict _LPI processing
2026-07-09 12:29 [PATCH v1 00/17] ACPI: processor: idle/intel_idle: Add ACPI _LPI support to intel_idle Rafael J. Wysocki
` (13 preceding siblings ...)
2026-07-09 12:41 ` [PATCH v1 14/17] ACPI: processor: idle: Relocate acpi_processor_extract_lpi_info() Rafael J. Wysocki
@ 2026-07-09 12:42 ` Rafael J. Wysocki
2026-07-09 12:43 ` [PATCH v1 16/17] intel_idle: Prepare for adding ACPI _LPI support Rafael J. Wysocki
2026-07-09 12:44 ` [PATCH v1 17/17] intel_idle: Add " Rafael J. Wysocki
16 siblings, 0 replies; 18+ messages in thread
From: Rafael J. Wysocki @ 2026-07-09 12:42 UTC (permalink / raw)
To: Linux PM; +Cc: Linux ACPI, Sudeep Holla
From: "Rafael J. Wysocki" <rafael.j.wysocki@intel.com>
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 <rafael.j.wysocki@intel.com>
---
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;
}
--
2.51.0
^ permalink raw reply related [flat|nested] 18+ messages in thread* [PATCH v1 16/17] intel_idle: Prepare for adding ACPI _LPI support
2026-07-09 12:29 [PATCH v1 00/17] ACPI: processor: idle/intel_idle: Add ACPI _LPI support to intel_idle Rafael J. Wysocki
` (14 preceding siblings ...)
2026-07-09 12:42 ` [PATCH v1 15/17] ACPI: processor: idle: Add switch for strict _LPI processing Rafael J. Wysocki
@ 2026-07-09 12:43 ` Rafael J. Wysocki
2026-07-09 12:44 ` [PATCH v1 17/17] intel_idle: Add " Rafael J. Wysocki
16 siblings, 0 replies; 18+ messages in thread
From: Rafael J. Wysocki @ 2026-07-09 12:43 UTC (permalink / raw)
To: Linux PM; +Cc: Linux ACPI, Sudeep Holla
From: "Rafael J. Wysocki" <rafael.j.wysocki@intel.com>
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 <rafael.j.wysocki@intel.com>
---
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;
--
2.51.0
^ permalink raw reply related [flat|nested] 18+ messages in thread* [PATCH v1 17/17] intel_idle: Add ACPI _LPI support
2026-07-09 12:29 [PATCH v1 00/17] ACPI: processor: idle/intel_idle: Add ACPI _LPI support to intel_idle Rafael J. Wysocki
` (15 preceding siblings ...)
2026-07-09 12:43 ` [PATCH v1 16/17] intel_idle: Prepare for adding ACPI _LPI support Rafael J. Wysocki
@ 2026-07-09 12:44 ` Rafael J. Wysocki
16 siblings, 0 replies; 18+ messages in thread
From: Rafael J. Wysocki @ 2026-07-09 12:44 UTC (permalink / raw)
To: Linux PM; +Cc: Linux ACPI, Sudeep Holla
From: "Rafael J. Wysocki" <rafael.j.wysocki@intel.com>
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>
---
drivers/idle/intel_idle.c | 160 ++++++++++++++++++++++++++++++++------
1 file 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;
}
--
2.51.0
^ permalink raw reply related [flat|nested] 18+ messages in thread