Analyzing the DSDT DSL
By looking at the DSL file, we find the methods we are interested in:
Method (SFNV, 2, Serialized)
{
If ((Arg0 == Zero))
{
If ((DECF & One))
{
Local0 = RRAM (0x0521)
Local0 |= 0x80
WRAM (0x0521, Local0)
}
If ((DECF & 0x02))
{
Local0 = RRAM (0x0522)
Local0 |= 0x80
WRAM (0x0522, Local0)
}
DECF = Zero
Return (Zero)
}
If ((Arg0 == One))
{
Local0 = RRAM (0x0521)
Local0 &= 0x7F
WRAM (0x0521, Local0)
DECF |= One
ST84 (Zero, Arg1)
Return (Zero)
}
If ((Arg0 == 0x02))
{
Local0 = RRAM (0x0522)
Local0 &= 0x7F
WRAM (0x0522, Local0)
DECF |= 0x02
ST84 (One, Arg1)
Return (Zero)
}
Return (Zero)
}
[...]
Method (RRAM, 1, Serialized)
{
If (ECAV ())
{
Acquire (MU4T, 0xFFFF)
If ((WEOE () != Zero))
{
Release (MU4T)
Return (Ones)
}
If ((WEIE () != Zero))
{
Release (MU4T)
Return (Ones)
}
EC4C = 0xFF
If ((WEIE () != Zero))
{
Release (MU4T)
Return (Ones)
}
Local0 = Arg0
Local1 = (Local0 & 0xFF)
Local0 >>= 0x08
Local0 &= 0xFF
EC4C = 0x80
If ((WEIE () != Zero))
{
Release (MU4T)
Return (Ones)
}
EC4D = Local0
If ((WEIE () != Zero))
{
Release (MU4T)
Return (Ones)
}
EC4D = Local1
If ((WEIE () != Zero))
{
Release (MU4T)
Return (Ones)
}
If ((WEOF () != Zero))
{
Release (MU4T)
Return (Ones)
}
Local0 = EC4D /* \_SB_.PCI0.LPCB.EC0_.EC4D */
Release (MU4T)
Return (Local0)
}
Return (Ones)
}
- SFNV ("Set Fan Voltage") is used to set the fan speed
- RRAM ("Read RAM") is used to read from the EC RAM
Finding the Arguments for the Write Method
Let's have a look at the SFNV method from above:
- It takes two arguments
- The first argument is tested against the values 0x0, 0x1 and 0x2
- The second argument is only used if the first argument is 0x1 or 0x2
Let's try calling the SFNV method:
sudo ec_probe acpi_call '\_SB.PCI0.LPCB.EC0.SFNV' 1 0
sudo ec_probe acpi_call '\_SB.PCI0.LPCB.EC0.SFNV' 1 255
sudo ec_probe acpi_call '\_SB.PCI0.LPCB.EC0.SFNV' 0 0
The first command stops the fan, the second sets the fan to full speed, and the third resets the fan.
Result:
- \_SB.PCI0.LPCB.EC0.SFNV 0 0 is used as ResetAcpiMethod
- \_SB.PCI0.LPCB.EC0.SFNV 1 $ is used as WriteAcpiMethod
Finding the Read Register
We couldn't find any register or method that reports the fan speed.
However, it must be set somewhere in the EC RAM.
Luckily, we have the RRAM method to read values out of the EC RAM.
Let's make a dump of all EC RAM registers when the fan is stopped:
sudo ec_probe acpi_call '\_SB.PCI0.LPCB.EC0.SFNV' 1 0
for i in $(seq 0 65535); do
printf "$i: "; sudo ec_probe acpi_call '\_SB.PCI0.LPCB.EC0.RRAM' $i
done > dump_low
Then we turn the fan up and create another dump:
sudo ec_probe acpi_call '\_SB.PCI0.LPCB.EC0.SFNV' 1 255
for i in $(seq 0 65535); do
printf "$i: "; sudo ec_probe acpi_call '\_SB.PCI0.LPCB.EC0.RRAM' $i
done > dump_high
Now we can compare the two dumps to identify changes, e.g. by using:
diff dump_low dump_high
We see that register 6151 contains 0 in dump_low and 255 in dump_high.
Result:
- \_SB.PCI0.LPCB.EC0.RRAM 6151 is used as ReadAcpiMethod
Writing the Configuration File
- \_SB.PCI0.LPCB.EC0.RRAM 6151 is used as ReadAcpiMethod
- \_SB.PCI0.LPCB.EC0.SFNV 1 $ is used as WriteAcpiMethod
- \_SB.PCI0.LPCB.EC0.SFNV 0 0 is used as ResetAcpiMethod
- 0 is the MinSpeedValue
- 255 is the MaxSpeedValue
- IndependentReadMinMaxValues is false, since the values of both methods (ReadAcpiMethod and WriteAcpiMethod) share the same range
See the configuration file on GitHub
Note
Maybe there is another way to control the fan by using the QMOD method
and the TAH0, TAH1, TH1R, TH1L, TH0R and TH0L registers.
However, I was not able to figure it out.