| From ae5be72159a5ce260ec2694c2f2188ba8ac4002a Mon Sep 17 00:00:00 2001 |
| From: Sasha Levin <sashal@kernel.org> |
| Date: Wed, 12 May 2021 17:15:14 -0500 |
| Subject: ACPI: processor idle: Fix up C-state latency if not ordered |
| |
| From: Mario Limonciello <mario.limonciello@amd.com> |
| |
| [ Upstream commit 65ea8f2c6e230bdf71fed0137cf9e9d1b307db32 ] |
| |
| 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 <Prike.Liang@amd.com> |
| Suggested-by: Alex Deucher <alexander.deucher@amd.com> |
| Signed-off-by: Mario Limonciello <mario.limonciello@amd.com> |
| [ rjw: Subject and changelog edits ] |
| Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com> |
| Signed-off-by: Sasha Levin <sashal@kernel.org> |
| --- |
| drivers/acpi/processor_idle.c | 40 +++++++++++++++++++++++++++++++++++ |
| 1 file changed, 40 insertions(+) |
| |
| diff --git a/drivers/acpi/processor_idle.c b/drivers/acpi/processor_idle.c |
| index fb161a21d0ae..8377c3ed10ff 100644 |
| --- a/drivers/acpi/processor_idle.c |
| +++ b/drivers/acpi/processor_idle.c |
| @@ -16,6 +16,7 @@ |
| #include <linux/acpi.h> |
| #include <linux/dmi.h> |
| #include <linux/sched.h> /* need_resched() */ |
| +#include <linux/sort.h> |
| #include <linux/tick.h> |
| #include <linux/cpuidle.h> |
| #include <linux/cpu.h> |
| @@ -389,10 +390,37 @@ static void acpi_processor_power_verify_c3(struct acpi_processor *pr, |
| return; |
| } |
| |
| +static int acpi_cst_latency_cmp(const void *a, const void *b) |
| +{ |
| + const struct acpi_processor_cx *x = a, *y = b; |
| + |
| + if (!(x->valid && y->valid)) |
| + return 0; |
| + if (x->latency > y->latency) |
| + return 1; |
| + if (x->latency < y->latency) |
| + return -1; |
| + return 0; |
| +} |
| +static void acpi_cst_latency_swap(void *a, void *b, int n) |
| +{ |
| + struct acpi_processor_cx *x = a, *y = b; |
| + u32 tmp; |
| + |
| + if (!(x->valid && y->valid)) |
| + return; |
| + tmp = x->latency; |
| + x->latency = y->latency; |
| + y->latency = tmp; |
| +} |
| + |
| static int acpi_processor_power_verify(struct acpi_processor *pr) |
| { |
| unsigned int i; |
| unsigned int working = 0; |
| + unsigned int last_latency = 0; |
| + unsigned int last_type = 0; |
| + bool buggy_latency = false; |
| |
| pr->power.timer_broadcast_on_state = INT_MAX; |
| |
| @@ -416,12 +444,24 @@ static int acpi_processor_power_verify(struct acpi_processor *pr) |
| } |
| if (!cx->valid) |
| continue; |
| + if (cx->type >= last_type && cx->latency < last_latency) |
| + buggy_latency = true; |
| + last_latency = cx->latency; |
| + last_type = cx->type; |
| |
| lapic_timer_check_state(i, pr, cx); |
| tsc_check_state(cx->type); |
| working++; |
| } |
| |
| + if (buggy_latency) { |
| + pr_notice("FW issue: working around C-state latencies out of order\n"); |
| + sort(&pr->power.states[1], max_cstate, |
| + sizeof(struct acpi_processor_cx), |
| + acpi_cst_latency_cmp, |
| + acpi_cst_latency_swap); |
| + } |
| + |
| lapic_timer_propagate_broadcast(pr); |
| |
| return (working); |
| -- |
| 2.30.2 |
| |