zhen-zen / YogaSMC

ACPI driver for OEM hardware.
GNU General Public License v2.0
232 stars 25 forks source link

EC not responding after sleep on Thinkpad Edge series #112

Closed Someone52 closed 2 years ago

Someone52 commented 3 years ago

So as I was on a hunt to get my thinkpad E590 fully working, I've encountered a strange issue. All of the FN keys, even volume ones that were otherwise natively handled by the OS without any other kexts/acpi mods would stop working. Then I've realized it wasn't just the fn keys but also other misc things such as the lid open/close too!

Quick search revealed that this issue is known for thinkpads and so far no one managed to figure it out: https://github.com/dandepeched/Hackintosh-ThinkPad-E580 "Lid close and FN keys stop working after sleep also battery status is stuck. Fixed after reboot." https://github.com/SukkaW/ThinkPad-E480-Hackintosh "Fn shortcut might stop working after wake from sleep. It is a common bug happened among ThinkPad E470, E480 E490, E570, E580, E590, etc." & etc

I've started my own investigation, pulling my unpatched acpi dsdt which can be found here: https://pastebin.ubuntu.com/p/CZG6sM9VQY/

At first, I've verified that my _PTS and _WAK as well as OPTS and OWAK were being called correctly with S3 state, in right order & etc. All good so far

Then I've started checking all the fields/methods & etc that should enable the keyboard, such as MHKE(1). After further analysis, it seemed like the methods seen in the ACPI instead are separate hotkey system that needs to be polled.

Method (MHKE, 1, Serialized)
                {
                    DHKB = Arg0
                    Acquire (XDHK, 0xFFFF)
                    DHKH = Zero
                    DHKW = Zero
                    DHKS = Zero
                    DHKD = Zero
                    DHKT = Zero
                    DHWW = Zero
                    Release (XDHK)
                }

                Method (MHKQ, 1, Serialized)
                {
                    If (DHKB)
                    {
                        If (DHKC)
                        {
                            Acquire (XDHK, 0xFFFF)
                            If ((Arg0 < 0x1000)){}
                            ElseIf ((Arg0 < 0x2000))
                            {
                                DHKH = Arg0
                            }
                            ElseIf ((Arg0 < 0x3000))
                            {
                                DHKW = Arg0
                            }
                            ElseIf ((Arg0 < 0x4000))
                            {
                                DHKS = Arg0
                            }
                            ElseIf ((Arg0 < 0x5000))
                            {
                                DHKD = Arg0
                            }
                            ElseIf ((Arg0 < 0x6000))
                            {
                                DHKH = Arg0
                            }
                            ElseIf ((Arg0 < 0x7000))
                            {
                                DHKT = Arg0
                            }
                            ElseIf ((Arg0 < 0x8000))
                            {
                                DHWW = Arg0
                            }
                            Else
                            {
                            }

                            Release (XDHK)
                            Notify (HKEY, 0x80) // Status Change
                        }
                        ElseIf ((Arg0 == 0x1004))
                        {
                            Notify (SLPB, 0x80) // Status Change
                        }
                    }
                }

               Method (MHKP, 0, NotSerialized)
                {
                    Acquire (XDHK, 0xFFFF)
                    If (DHWW)
                    {
                        Local1 = DHWW /* \_SB_.PCI0.LPCB.EC__.HKEY.DHWW */
                        DHWW = Zero
                    }
                    ElseIf (DHKW)
                    {
                        Local1 = DHKW /* \_SB_.PCI0.LPCB.EC__.HKEY.DHKW */
                        DHKW = Zero
                    }
                    ElseIf (DHKD)
                    {
                        Local1 = DHKD /* \_SB_.PCI0.LPCB.EC__.HKEY.DHKD */
                        DHKD = Zero
                    }
                    ElseIf (DHKS)
                    {
                        Local1 = DHKS /* \_SB_.PCI0.LPCB.EC__.HKEY.DHKS */
                        DHKS = Zero
                    }
                    ElseIf (DHKT)
                    {
                        Local1 = DHKT /* \_SB_.PCI0.LPCB.EC__.HKEY.DHKT */
                        DHKT = Zero
                    }
                    Else
                    {
                        Local1 = DHKH /* \_SB_.PCI0.LPCB.EC__.HKEY.DHKH */
                        DHKH = Zero
                    }

                    Release (XDHK)
                    Return (Local1)
                }

with MHKP being the poll method, futher confimed by looking at some drivers found in the net:

https://github.com/torvalds/linux/blob/master/drivers/platform/x86/thinkpad_acpi.c http://ftp.jp.freebsd.org/pub/NetBSD/misc/joerg/GENERIC/src/src/sys/dev/acpi/thinkpad_acpi.c.html

It seems like this is exactly how things like hotkeys & lid open/close events retrieval was intended on thinkpads.

zhen-zen commented 3 years ago

Actually the MHKE function should work as intended on its own and it will only decide whether ACPI keys will be reported to the system. iirc if you add some tracing logs to those _Qxx methods, all of them won't called at all. As you mentioned, even the volume keys (mode controlled by EC) won't report though PS2. So it's a firmware issue indeed. However, the firmware concept here is too broad and we need to narrow it down.

  1. First I wonder if those those SMI methods went through. Commands might be trapped there or not processed at all. You can verify it by setting keyboard backlight manually in prefpane.

  2. I have also suspected those _PSW methods are not called at all. According to ACPI spec,

The _PSW method was deprecated in ACPI 3.0. OSPM must use _DSW if it is present. Otherwise, it may use _PSW.

Since other ACPI devices has the _DSW method, maybe macOS just won't call it.

Edit: AppleACPIPlatform don't support _DSW method evaluation.

There are some registers like HWFN and HWLO. You can also dump and compare the EC region before and after the S3 sleep.

  1. This is the most complicated one. Given that this issue doesn't exist under linux, I suppose there's something wrong when macOS tries to re-establish the connection to embedded controller after wakeup. Maybe something in _INI or OINI might do the job and reset the controller. Maybe you can replace OINI / OPTS with ones of more tracing logs. Or you can load the acpi_call module in Linux and execute the commands one by one and reverse.
zhen-zen commented 3 years ago

Might be related: https://patchwork.kernel.org/project/linux-pm/patch/2279758.LZ0rCGQtcH@aspire.rjw.lan/

zhen-zen commented 3 years ago

Can you try the latest build of https://github.com/zhen-zen/ThermalSolution ?

zhen-zen commented 2 years ago

Closed due to inactivity.