Example: HP EliteBook 8560w

Note

This configuration was not written by me, and I do not fully understand the STMM method. The following documentation was included for completeness and reference purposes.

Analyzing the DSDT DSL

By looking at the DSL file, we find the following methods:

        Method (GFRM, 0, Serialized)
        {
            Local0 = 0x00
            If (\_SB.PCI0.LPCB.EC0.ECRG)
            {
                Acquire (\_SB.PCI0.LPCB.EC0.ECMX, 0xFFFF)
                Local0 = \_SB.PCI0.LPCB.EC0.FRDC
                Release (\_SB.PCI0.LPCB.EC0.ECMX)
                If (Local0)
                {
                    Local1 = (Local0 >> 0x01)
                    Local2 = (0x0003C000 + Local1)
                    Divide (Local2, Local0, Local1, Local0)
                }
            }

            Return (Local0)
        }

        [...]

        Method (STMM, 1, NotSerialized)
        {
            Debug = "SetThermalStatus"
            CreateByteField (Arg0, 0x00, IDTA)
            If (((IDTA >= 0x10) && (IDTA <= 0x15)))
            {
                \_SB.SSMI (0xEA75, 0x02, 0x28, 0x574D4953, 0x00)
                Return (WFDA ())
            }

            Local0 = Package (0x02)
                {
                    0x00, 
                    0x00
                }
            If (((IDTA >= 0x20) && (IDTA <= 0x24)))
            {
                Local7 = (IDTA - 0x20)
                Local1 = DerefOf (Arg0 [0x01])
                Local2 = DerefOf (Arg0 [0x02])
                If ((Local1 != DerefOf (THCT [Local7])))
                {
                    Return (Package (0x02)
                    {
                        0x06, 
                        0x00
                    })
                }

                If ((Local1 == 0x00))
                {
                    Return (Local0)
                }

                If (\_SB.PCI0.LPCB.EC0.ECRG)
                {
                    Acquire (\_SB.PCI0.LPCB.EC0.ECMX, 0xFFFF)
                    If (((Local7 < 0x03) || (Local7 == 0x04)))
                    {
                        \_SB.PCI0.LPCB.EC0.CRZN = (0x01 + Local7)
                        If ((Local2 == 0xFF))
                        {
                            Local2 = 0x00
                        }

                        \_SB.PCI0.LPCB.EC0.TEMP = Local2
                        If ((Local7 == 0x02))
                        {
                            Local2 = DerefOf (Arg0 [0x03])
                            \_SB.PCI0.LPCB.EC0.CRZN = 0x04
                            If ((Local2 == 0xFF))
                            {
                                Local2 = 0x00
                            }

                            \_SB.PCI0.LPCB.EC0.TEMP = Local2
                        }
                    }
                    Else
                    {
                        If ((Local2 != 0xFF))
                        {
                            Local2 = \_TZ.CTCT (Local2)
                        }

                        \_SB.PCI0.LPCB.EC0.FTGC = Local2
                    }

                    Release (\_SB.PCI0.LPCB.EC0.ECMX)
                }

                Local6 = 0x00
                While ((Local6 < Local1))
                {
                    DerefOf (TSTV [Local7]) [Local6] = DerefOf (Arg0 [
                        (Local6 + 0x02)])
                    Local6++
                }

                TSTM = 0x01
                Return (Local0)
            }

            If ((IDTA == 0xAA))
            {
                Local1 = 0x00
                While ((Local1 < SizeOf (TSTV)))
                {
                    Local2 = 0x00
                    Local3 = DerefOf (THCT [Local1])
                    While ((Local2 < Local3))
                    {
                        DerefOf (TSTV [Local1]) [Local2] = 0xFF
                        Local2++
                    }

                    Local1++
                }

                If (\_SB.PCI0.LPCB.EC0.ECRG)
                {
                    Acquire (\_SB.PCI0.LPCB.EC0.ECMX, 0xFFFF)
                    Local1 = 0x01
                    While ((Local1 <= 0x05))
                    {
                        \_SB.PCI0.LPCB.EC0.CRZN = Local1
                        \_SB.PCI0.LPCB.EC0.TEMP = 0x00
                        Local1++
                    }

                    \_SB.PCI0.LPCB.EC0.FTGC = 0xFF
                    Release (\_SB.PCI0.LPCB.EC0.ECMX)
                }

                TSTM = 0x00
                Return (Local0)
            }

            Return (Package (0x02)
            {
                0x06, 
                0x00
            })
        }

Calculating Register Addresses

We again have a look at the DSL file:

        OperationRegion (ECRM, EmbeddedControl, 0x00, 0xFF)
        Field (ECRM, ByteAcc, NoLock, Preserve)
        {
            [...]
            Offset (0x22), 
            CRZN,   8, 
            THTA,   8, 
            HYST,   8, 
            CRIT,   8, 
            TEMP,   8, 
            TENA,   8, 
            Offset (0x29), 
            TOAD,   8, 
            PHTP,   8, 
            THEM,   8, 
            TMPO,   8, 
            Offset (0x2E), 
            FRDC,   8, 
            FTGC,   8, 
            [...]
        }

We calculate the register addresses as follows:

Finding the ReadRegister Values

We ensure that the fan is turned off (or at least at the minimum speed).

sudo ec_probe read 46

Output: 100

We run stress -c 8 to stress the system and spin up the fan.

sudo ec_probe read 46

Output: 50

Result: 100 is the MinSpeedValue and 50 is the MaxSpeedValue

Finding the WriteRegister Values

We assume that the values used by ReadRegister correspond to those used by WriteRegister:

sudo ec_probe write 47 50
sudo ec_probe write 47 100
sudo ec_probe write 47 255

We see that:

Writing the Configuration File

The configuration for RegisterWriteConfigurations is as follows (I did not fully understand how these values were derived).

See the configuration file on GitHub