> -----Original Message----- > From: Limonciello, Mario <Mario.Limonciello@xxxxxxx> > Sent: Wednesday, May 12, 2021 11:24 > To: Rafael J . Wysocki <rjw@xxxxxxxxxxxxx>; Len Brown <lenb@xxxxxxxxxx>; > linux-acpi@xxxxxxxxxxxxxxx > Cc: Limonciello, Mario <Mario.Limonciello@xxxxxxx>; Liang, Prike > <Prike.Liang@xxxxxxx>; Deucher, Alexander <Alexander.Deucher@xxxxxxx> > Subject: [PATCH v2] ACPI / idle: override c-state latency when not in > conformance with s0ix > > 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 happening 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@xxxxxxx> > Suggested-by: Alex Deucher <alexander.deucher@xxxxxxx> > Signed-off-by: Mario Limonciello <mario.limonciello@xxxxxxx> > --- > 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 4e2d76b8b697..c1f52c5ceef2 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> > @@ -388,10 +389,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; > > @@ -415,12 +443,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; My apologies, I noticed while testing this on a different machine /without/ the problem that notice was also emitted and realized I made a logic error. It should be: cx->latency < last_latency. I'll resubmit it. > + 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.25.1