Re: [PATCH] platform/x86: huawei-wmi: add ACPI fallback for Fn-lock on newer models

From: Ilpo Järvinen

Date: Tue May 26 2026 - 05:08:09 EST


On Tue, 26 May 2026, Superstes wrote:

> >> Thank you for working on this. Could you open an issue and/or send a PR
> to https://github.com/aymanbagabas/Huawei-WMI ;?
> Yes, I can send PR in the next two-three days.
> >> This is very specific to the FLMH-XX model. Can you confirm this is the case for
> other newer models?No, it is the latest model in the Matebook 14 series, I've just
> bought it recently and faced multiple issues, which I try to resolve. I can't found
> any information about that issues from another users, but i think that it can be
> related with other recent laptop based on Intel Core Ultra processors, it will be
> good to have someone with following models:
> MateBook Fold (2025)
> MateBook X Pro (2024)
> MateBook GT 14 (2024)
> MateBook D 14 (2024)
> >> Wouldn't it be better to put this behavior behind a quirk instead of enforcing it
> on the whole module?
> I haven't found a better place to make such changes. But you can suggest something.
> But I think that my workaround is safe because it calls very specific ACPI objects
> and uses default fallbacks.
>
> By the way, maybe You, or Ilpo knows a good place to implement board specific i2c
> acpi fixes? It Looks like there is a race condition situation which leads to
> arbitration loss when kernel tries to initialize multiple devices on the i2c bus. To
> fix this I've disabled light sensor and made systemd service to remove and enable
> the i2c_hid_acpi module, but I haven't found a place where to implement a quirk that
> fixes that issue.

Do you mean you'd want to serialize .probe()s of those devices?

--
i.

> пн, 25 мая 2026 г. в 23:38, Ayman Bagabas <ayman.bagabas@xxxxxxxxx>:
> >
> > Newer Huawei laptops (e.g. FLMH-XX / MateBook 14 2024) no longer
> support
> > the legacy WMI interface for Fn-lock control. Instead, they expose
> direct
> > ACPI methods \GFRS and \SFRS (Get/Set Fn key Reversal Status) which
> > communicate with the EC via registers 0x6B (read) and 0x6C (write).
> >
> > Add huawei_acpi_fn_lock_get() and huawei_acpi_fn_lock_set() helpers
> that
> > use acpi_evaluate_object() to call these methods. Both
> > huawei_wmi_fn_lock_get() and huawei_wmi_fn_lock_set() now probe for
> > \GFRS/\SFRS via acpi_has_method() first and fall back to the legacy WMI
> > path if not present.
>
> Thank you for working on this. Could you open an issue and/or send a PR
> to https://github.com/aymanbagabas/Huawei-WMI ?
>
> >
> > Tested on: HUAWEI FLMH-XX (MateBook 14 2024),
> > CachyOS (kernel 7.0.9-1-cachyos).
> >
> > Signed-off-by: Shaposhnikov Daniil <2minesweeper2@xxxxxxxxx>
> > ---
> >  drivers/platform/x86/huawei-wmi.c | 95 +++++++++++++++++++++++++++++++
> >  1 file changed, 95 insertions(+)
> >
> > diff --git a/drivers/platform/x86/huawei-wmi.c
> b/drivers/platform/x86/huawei-wmi.c
> > index 93cca17fdf58..19cd8f1a8e33 100644
> > --- a/drivers/platform/x86/huawei-wmi.c
> > +++ b/drivers/platform/x86/huawei-wmi.c
> > @@ -527,11 +527,101 @@ static void huawei_wmi_battery_exit(struct
> device *dev)
> >
> >  /* Fn lock */
> >
> > +/*
> > + * Newer Huawei models (e.g. HUAWEI FLMH-XX / MateBook 14 2024) use
> direct
> > + * ACPI methods \GFRS / \SFRS (Get/Set Fn key Reversal Status) to
> control
> > + * Fn-lock via EC registers 0x6B (read) and 0x6C (write).
> > + *
> > + * GFRS response buffer layout:
> > + *   byte[0] = STAT (0 = success)
> > + *   byte[1] = 0x01 (fn-lock off) or 0x02 (fn-lock on)
> > + *
> > + * SFRS argument layout (CreateByteField(Arg0, 0x02, FRSR)):
> > + *   Value is read from byte[2] of the integer argument, so it must be
> > + *   passed as (value << 16):
> > + *   (1 << 16) = fn-lock off (writes 0x55 to EC 0x6C)
> > + *   (2 << 16) = fn-lock on  (writes 0x5A to EC 0x6C)
> > + */
>
> This is very specific to the FLMH-XX model. Can you confirm this is the
> case for other newer models?
>
> > +
> > +static int huawei_acpi_fn_lock_get(int *on)
> > +{
> > +       union acpi_object acpi_arg, *obj;
> > +       struct acpi_object_list arg_list = { .count = 1, .pointer =
> &acpi_arg };
> > +       struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, NULL };
> > +       acpi_status status;
> > +       u8 val;
> > +
> > +       acpi_arg.type = ACPI_TYPE_INTEGER;
> > +       acpi_arg.integer.value = 0;
> > +
> > +       status = acpi_evaluate_object(NULL, "\\GFRS", &arg_list,
> &output);
> > +       if (ACPI_FAILURE(status))
> > +               return -EIO;
> > +
> > +       obj = output.pointer;
> > +       if (!obj || obj->type != ACPI_TYPE_BUFFER || obj->buffer.length
> < 2) {
> > +               kfree(obj);
> > +               return -ENODATA;
> > +       }
> > +
> > +       /* byte[0] = STAT (0 = success), byte[1] = 1 (off) or 2 (on) */
> > +       if (obj->buffer.pointer[0] != 0) {
> > +               kfree(obj);
> > +               return -EIO;
> > +       }
> > +
> > +       val = obj->buffer.pointer[1];
> > +       if (val != 1 && val != 2) {
> > +               kfree(obj);
> > +               return -ENODATA;
> > +       }
> > +
> > +       if (on)
> > +               *on = val - 1; /* 1→0 (off), 2→1 (on) */
> > +
> > +       kfree(obj);
> > +       return 0;
> > +}
> > +
> > +static int huawei_acpi_fn_lock_set(int on)
> > +{
> > +       union acpi_object acpi_arg, *obj;
> > +       struct acpi_object_list arg_list = { .count = 1, .pointer =
> &acpi_arg };
> > +       struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, NULL };
> > +       acpi_status status;
> > +       int ret = 0;
> > +
> > +       /*
> > +        * SFRS reads byte[2] of its argument via CreateByteField(Arg0,
> 0x02).
> > +        * on=0 → FRSR=1 → EC gets 0x55 (fn-lock off)
> > +        * on=1 → FRSR=2 → EC gets 0x5A (fn-lock on)
> > +        */
> > +       acpi_arg.type = ACPI_TYPE_INTEGER;
> > +       acpi_arg.integer.value = (u64)(on + 1) << 16;
> > +
> > +       status = acpi_evaluate_object(NULL, "\\SFRS", &arg_list,
> &output);
> > +       if (ACPI_FAILURE(status))
> > +               return -EIO;
> > +
> > +       obj = output.pointer;
> > +       if (obj && obj->type == ACPI_TYPE_BUFFER &&
> > +           obj->buffer.length >= 1 && obj->buffer.pointer[0] != 0)
> > +               ret = -EIO;
> > +
> > +       kfree(obj);
> > +       return ret;
> > +}
> > +
> >  static int huawei_wmi_fn_lock_get(int *on)
> >  {
> >         u8 ret[0x100] = { 0 };
> >         int err, i;
> >
> > +       /* Newer models: use direct ACPI \GFRS method */
> > +       if (acpi_has_method(NULL, "\\GFRS"))
> > +               return huawei_acpi_fn_lock_get(on);
>
> Wouldn't it be better to put this behavior behind a quirk instead
> of enforcing it on the whole module? Keep in mind that this is
> also used by Honor devices that have the same WMI method.
>
> > +
> > +       /* Legacy WMI fallback */
> >         err = huawei_wmi_cmd(FN_LOCK_GET, ret, 0x100);
> >         if (err)
> >                 return err;
> > @@ -550,6 +640,11 @@ static int huawei_wmi_fn_lock_set(int on)
> >  {
> >         union hwmi_arg arg;
> >
> > +       /* Newer models: use direct ACPI \SFRS method */
> > +       if (acpi_has_method(NULL, "\\SFRS"))
> > +               return huawei_acpi_fn_lock_set(on);
> > +
> > +       /* Legacy WMI fallback */
> >         arg.cmd = FN_LOCK_SET;
> >         arg.args[2] = on + 1; // 0 undefined, 1 off, 2 on.
> >
> > --
> > 2.54.0
> >
>
>
>