Skip to content

Commit 65ea8f2

Browse files
superm1rafaeljw
authored andcommitted
ACPI: processor idle: Fix up C-state latency if not ordered
Generally, the C-state latency is provided by the _CST method or FADT, but some OEM platforms using AMD Picasso, Renoir, Van Gogh, and Cezanne set the C2 latency greater than C3's which causes the C2 state to be skipped. That will block the core entering PC6, which prevents S0ix working properly on Linux systems. In other operating systems, the latency values are not validated and this does not cause problems by skipping states. To avoid this issue on Linux, detect when latencies are not an arithmetic progression and sort them. Link: https://gitlab.freedesktop.org/agd5f/linux/-/commit/026d186e4592c1ee9c1cb44295912d0294508725 Link: https://gitlab.freedesktop.org/drm/amd/-/issues/1230#note_712174 Suggested-by: Prike Liang <[email protected]> Suggested-by: Alex Deucher <[email protected]> Signed-off-by: Mario Limonciello <[email protected]> [ rjw: Subject and changelog edits ] Signed-off-by: Rafael J. Wysocki <[email protected]>
1 parent d07f6ca commit 65ea8f2

File tree

1 file changed

+40
-0
lines changed

1 file changed

+40
-0
lines changed

drivers/acpi/processor_idle.c

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
#include <linux/acpi.h>
1717
#include <linux/dmi.h>
1818
#include <linux/sched.h> /* need_resched() */
19+
#include <linux/sort.h>
1920
#include <linux/tick.h>
2021
#include <linux/cpuidle.h>
2122
#include <linux/cpu.h>
@@ -384,10 +385,37 @@ static void acpi_processor_power_verify_c3(struct acpi_processor *pr,
384385
return;
385386
}
386387

388+
static int acpi_cst_latency_cmp(const void *a, const void *b)
389+
{
390+
const struct acpi_processor_cx *x = a, *y = b;
391+
392+
if (!(x->valid && y->valid))
393+
return 0;
394+
if (x->latency > y->latency)
395+
return 1;
396+
if (x->latency < y->latency)
397+
return -1;
398+
return 0;
399+
}
400+
static void acpi_cst_latency_swap(void *a, void *b, int n)
401+
{
402+
struct acpi_processor_cx *x = a, *y = b;
403+
u32 tmp;
404+
405+
if (!(x->valid && y->valid))
406+
return;
407+
tmp = x->latency;
408+
x->latency = y->latency;
409+
y->latency = tmp;
410+
}
411+
387412
static int acpi_processor_power_verify(struct acpi_processor *pr)
388413
{
389414
unsigned int i;
390415
unsigned int working = 0;
416+
unsigned int last_latency = 0;
417+
unsigned int last_type = 0;
418+
bool buggy_latency = false;
391419

392420
pr->power.timer_broadcast_on_state = INT_MAX;
393421

@@ -411,12 +439,24 @@ static int acpi_processor_power_verify(struct acpi_processor *pr)
411439
}
412440
if (!cx->valid)
413441
continue;
442+
if (cx->type >= last_type && cx->latency < last_latency)
443+
buggy_latency = true;
444+
last_latency = cx->latency;
445+
last_type = cx->type;
414446

415447
lapic_timer_check_state(i, pr, cx);
416448
tsc_check_state(cx->type);
417449
working++;
418450
}
419451

452+
if (buggy_latency) {
453+
pr_notice("FW issue: working around C-state latencies out of order\n");
454+
sort(&pr->power.states[1], max_cstate,
455+
sizeof(struct acpi_processor_cx),
456+
acpi_cst_latency_cmp,
457+
acpi_cst_latency_swap);
458+
}
459+
420460
lapic_timer_propagate_broadcast(pr);
421461

422462
return (working);

0 commit comments

Comments
 (0)