On Tue, 28 Jul 2026, 孙 誉铭 wrote: > The Xiaomi Book Pro 14 (2026, Panther Lake) ships with broken > firmware: ECON, referenced by \_SB.PC00.LPCB.Q_EC._STA, is not defined > in any of the 34 SSDTs the firmware loads (boot log: "Could not > resolve symbol [\_SB.PC00.LPCB.Q_EC._STA.ECON], AE_NOT_FOUND"). The > ACPI EC driver therefore never binds and none of the EC query handlers > execute - including _Q0C/_Q0D which notify the PNP0C0D lid device. As > a result closing the lid generates no SW_LID event: the screen stays > on and keys depressed by the closed lid keep typing into the visible > lock screen, locking the user account after repeated failed logins. > > The EC itself is alive and its shared-memory window at 0xFE0B0300 > (DSDT region "ERAM") tracks the lid state bit LSTE (offset 0x14, bit > 0). Poll it from a delayed-work item and, on a change, evaluate the > very AML methods the EC query would have run (_Q0C/_Q0D), which update > the GFX CLID field and notify LID0 so the button driver emits SW_LID. > > Gate the quirk on both the MIFS v2 firmware variant and DMI matching > so no other machine gets polled. Verified on the affected machine: lid > close/open now produce proper switch events handled by systemd-logind > (Lid closed -> suspend -> Lid opened -> resume). So does this mean it polls it while the machine is supposed to be suspended? > Signed-off-by: Yuming Sun > --- > drivers/platform/x86/bitland-mifs-wmi.c | 94 ++++++++++++++++++++++++- > 1 file changed, 93 insertions(+), 1 deletion(-) > > diff --git a/drivers/platform/x86/bitland-mifs-wmi.c b/drivers/platform/x86/bitland-mifs-wmi.c > index 5f7c85a8..550ee981 100644 > --- a/drivers/platform/x86/bitland-mifs-wmi.c > +++ b/drivers/platform/x86/bitland-mifs-wmi.c > @@ -14,6 +14,7 @@ > #include > #include > #include > +#include > #include > #include > #include > @@ -34,6 +35,7 @@ > #include > #include > #include > +#include > > #define DRV_NAME "bitland-mifs-wmi" > #define BITLAND_MIFS_GUID "B60BFB48-3E5B-49E4-A0E9-8CFFE1B3434B" > @@ -161,6 +163,11 @@ struct bitland_mifs_output { > #define MIFS_V2_EC_MEM_SIZE 0x100 > #define MIFS_V2_EC_REG_FAN1 0x69 > #define MIFS_V2_EC_REG_FAN2 0x6B > +#define MIFS_V2_EC_REG_LID 0x14 /* bit 0: LSTE, 1 = lid open */ > +#define MIFS_V2_EC_LID_OPEN BIT(0) > + > +/* Interval for the lid-state poll; see bitland_mifs_lid_poll() */ > +#define MIFS_V2_LID_POLL_MS 250 > > static u16 mifs_status(const struct bitland_mifs_output *out) > { > @@ -200,6 +207,9 @@ struct bitland_mifs_wmi_data { > bool profile_valid; > bool is_v2; /* MIFS v2 firmware: QFAN perf-mode codes */ > u8 __iomem *ec_mem; /* v2 EC shared-memory window */ > + struct delayed_work lid_work; > + u8 last_lid; > + bool lid_quirk; > }; > > static int bitland_mifs_wmi_call(struct bitland_mifs_wmi_data *data, > @@ -390,6 +400,9 @@ static int bitland_mifs_wmi_suspend(struct device *dev) > if (!data->pp_dev) > return 0; > > + if (data->lid_quirk) > + cancel_delayed_work_sync(&data->lid_work); > + > /* > * Never abort suspend: some firmware revisions answer the perf-mode > * query with values this driver cannot map, or fail the call > @@ -410,6 +423,11 @@ static int bitland_mifs_wmi_resume(struct device *dev) > if (!data->pp_dev) > return 0; > > + if (data->lid_quirk) { > + data->last_lid = 0xff; Magic literal. > + schedule_delayed_work(&data->lid_work, 0); > + } > + > if (!data->profile_valid) > return 0; > > @@ -549,6 +567,57 @@ static const struct hwmon_chip_info laptop_chip_info = { > .info = laptop_hwmon_info, > }; > > +/* > + * Lid switch quirk (Xiaomi Book Pro 14 2026): > + * > + * The firmware of this machine never defines ECON, which is referenced > + * by \_SB.PC00.LPCB.Q_EC._STA, so the ACPI EC driver never binds and > + * none of the EC query handlers run. This kills the lid events > + * (_Q0C/_Q0D) as well as every other EC-driven event. Poll the lid > + * state bit (LSTE) in the EC shared-memory window and, on a change, > + * evaluate the very AML methods the EC query would have run: they > + * update the GFX CLID field and notify the PNP0C0D lid device, which > + * makes the button driver emit the SW_LID event. > + */ > +#define MIFS_LID_CLOSE_METHOD "\\_SB.PC00.LPCB.Q_EC._Q0C" > +#define MIFS_LID_OPEN_METHOD "\\_SB.PC00.LPCB.Q_EC._Q0D" > + > +static void bitland_mifs_lid_poll(struct work_struct *work) > +{ > + struct bitland_mifs_wmi_data *data = > + container_of(to_delayed_work(work), struct bitland_mifs_wmi_data, > + lid_work); > + u8 lid; > + > + lid = readb(data->ec_mem + MIFS_V2_EC_REG_LID) & MIFS_V2_EC_LID_OPEN; > + if (lid != data->last_lid && data->last_lid != 0xff) { Please name the literal properly, you're clearly meaning something with it. > + acpi_status status; > + acpi_string method = (acpi_string)(lid ? MIFS_LID_OPEN_METHOD : > + MIFS_LID_CLOSE_METHOD); > + > + status = acpi_evaluate_object(NULL, method, NULL, NULL); > + if (ACPI_FAILURE(status)) > + dev_warn(&data->wdev->dev, > + "lid %s AML failed: %s\n", > + lid ? "open" : "close", > + acpi_format_exception(status)); So if acpi failed, you still write ->last_lid? > + } > + data->last_lid = lid; > + > + schedule_delayed_work(&data->lid_work, > + msecs_to_jiffies(MIFS_V2_LID_POLL_MS)); > +} > + > +static const struct dmi_system_id bitland_mifs_lid_quirk_table[] = { > + { > + .matches = { > + DMI_MATCH(DMI_SYS_VENDOR, "XIAOMI"), > + DMI_MATCH(DMI_PRODUCT_NAME, "Xiaomi Book Pro 14"), > + }, > + }, > + { } > +}; > + > static int laptop_kbd_led_set(struct led_classdev *led_cdev, > enum led_brightness value) > { > @@ -843,6 +912,9 @@ static int bitland_mifs_wmi_probe(struct wmi_device *wdev, const void *context) > if (!drv_data->ec_mem) > dev_warn(&wdev->dev, > "cannot map EC window, fan tach unavailable\n"); > + > + drv_data->lid_quirk = drv_data->ec_mem && > + dmi_check_system(bitland_mifs_lid_quirk_table); > } > } > > @@ -897,9 +969,20 @@ static int bitland_mifs_wmi_probe(struct wmi_device *wdev, const void *context) > if (ret) > return ret; > > - return devm_add_action_or_reset(&wdev->dev, > + ret = devm_add_action_or_reset(&wdev->dev, > bitland_notifier_unregister, > &drv_data->notifier); > + if (ret) > + return ret; > + > + if (drv_data->lid_quirk) { > + INIT_DELAYED_WORK(&drv_data->lid_work, bitland_mifs_lid_poll); devm_delayed_work_autocancel() ? > + drv_data->last_lid = 0xff; The literal here as well. > + schedule_delayed_work(&drv_data->lid_work, > + msecs_to_jiffies(MIFS_V2_LID_POLL_MS)); Add include for msecs_to_jiffies(). > + } > + > + return 0; > } > > static void bitland_mifs_wmi_notify(struct wmi_device *wdev, > @@ -982,6 +1065,14 @@ static void bitland_mifs_wmi_notify(struct wmi_device *wdev, > } > } > > +static void bitland_mifs_wmi_remove(struct wmi_device *wdev) > +{ > + struct bitland_mifs_wmi_data *data = dev_get_drvdata(&wdev->dev); > + > + if (data->lid_quirk) > + cancel_delayed_work_sync(&data->lid_work); > +} > + > static const struct wmi_device_id bitland_mifs_wmi_id_table[] = { > { BITLAND_MIFS_GUID, (void *)BITLAND_WMI_CONTROL }, > { BITLAND_EVENT_GUID, (void *)BITLAND_WMI_EVENT }, > @@ -999,6 +1090,7 @@ static struct wmi_driver bitland_mifs_wmi_driver = { > .id_table = bitland_mifs_wmi_id_table, > .min_event_size = sizeof(struct bitland_mifs_event), > .probe = bitland_mifs_wmi_probe, > + .remove = bitland_mifs_wmi_remove, > .notify_new = bitland_mifs_wmi_notify, > }; > > -- i.