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:
- GFRM (GetFanRPM?) which accesses FRDC
- STMM (SetThermalStatus) which modifies FTGC, CRZN and TEMP
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:
- FRDC = Offset(0x2E) = 46
- FTGC = Offset(0x2E) + (8 / 8) = 0x2F = 47
- CRZN = Offset(0x22) = 34
- TEMP = Offset(0x22) + ((8 + 8 + 8 + 8) / 8) = 38
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:
- 50 sets the fan to maximum speed
- 100 sets the fan to minimum speed
- 255 resets the fan
Writing the Configuration File
- 46 (FRDC) is the ReadRegister
- 47 (FTGC) is the WriteRegister
- 100 is the MinSpeedValue
- 50 is the MaxSpeedValue
- 255 is the FanSpeedResetValue
- IndependentReadMinMaxValues is false, since FRDC and FTGC share the same range
The configuration for RegisterWriteConfigurations is as follows (I did not fully understand how these values were derived).
- Register 34 (CRZN) must be set to 1
- Register 38 (TEMP) must be set to 28
See the configuration file on GitHub