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.