// SSDT for temp reading and custom fan control for ASUS laptops (Haswell or above)
// Created by black.dragon74
DefinitionBlock("SSDT-FAN", "SSDT", 2, "Nick", "AsFanC", 0)
{
// Declare externals
External (NOPT.ICFC, IntObj) // Reads config from my SSDT-Config and enables/disables custom fan control (CPU TEMP and FAN RPM will be always shown if using this SSDT)
External (\_SB.PCI0.LPCB.EC0.ECAV, MethodObj) // Check if EC (Embedded controller) is ready
External (\_SB.PCI0.LPCB.EC0.ST83, MethodObj) // FAN values in bytes are stored here
External (\_SB.PCI0.LPCB.EC0.TACH, MethodObj) // Returns FAN speed in RPM unit
External (\_SB.PCI0.LPCB.EC0.ECPU, FieldUnitObj) // Current CPU Temperature
External (\_SB.PCI0.LPCB.EC0.ST98, MethodObj) // Method that can acquire mutex and set FAN QMOD || QMOD = Max Allowed RPM of FAN
// Add methods to read FAN RPM in compliance to FakeSMC_ACPI_Sensors
Device (SMCD)
{
Name (_HID, "FAN0000")
Name (_STA, 1) // Just so that we could turn device on and off as required
Name (TACH, Package()
{
"System Fan", "FAN0"
})
// Add package wrt ACPISensors
Name (TEMP, Package()
{
"CPU Heatsink", "TCPU"
})
Method (FAN0, 0)
{
// Check is EC is ready
If (\_SB.PCI0.LPCB.EC0.ECAV())
{
// Continue
Local0 = \_SB.PCI0.LPCB.EC0.ST83(0)
If (Local0 == 255)
{
// Store it in debug log
Debug = "FAN0 is at maximum speed now"
Return (Local0)
}
// Get RPM and store it in a var
Local0 = \_SB.PCI0.LPCB.EC0.TACH(0) // Passing 0 as an arg to method TACH return speed of FAN1.
// If you have 2 fans you would use 2 args (Zero and One)
}
Else
{
// Terminate, return Zero
Local0 = 0
}
// Return 255, 0 or Fan RPM based on conditionals above
Return (Local0)
}
Method (TCPU, 0)
{
// Add method to handle CPU temperature
// Check if EC is ready
If (\_SB.PCI0.LPCB.EC0.ECAV())
{
Local0 = \_SB.PCI0.LPCB.EC0.ECPU
Local1 = 60
If (Local0 < 128)
{
Local1 = Local0
}
}
Else
{
// Terminate, return Zero
Local1 = 0
}
// Return final CPU temp. ACPISensors take care of the conversion.
Return (Local1)
}
// Custom FAN table by black.dragon74 for ASUS machines
// Quietest fan operation yet coolest CPU.
// Scaling from values as low as 255 RPM to values as high as 5026 RPM (That's great!)
// Scaling that ASUS provided was from 2200 RPM to 2900 RPM (Duh!)
// If temperature is less than 32C, the FAN is turned off
// Please note: I can bear with the noise if I get a cooler CPU and better performance
// This SSDT doesn't let CPU temp cross 65C (in my case) but, the fan noise is there
// You may tweak the tables below to suit your requirements (Tables namely: FTA1, FTA2)
// Also, if you get FAN speed as 255 RPM that means your FAN is at it's maximum speed
// Temperatures. 0xFF means if temp is above 52C, let bios take control of things.
Name(FTA1, Package()
{
// CPU temp in celcius
32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 0xFF,
})
// Fan speeds. 255(0xFF) is max, 0(0x00) is for fan off (Not auto)
Name(FTA2, Package()
{
0, 10, 20, 30, 40, 50, 60, 70, 80, 90, 100, 110, 120, 130, 140, 160, 185, 205, 225, 245, 250, 255
})
// Time out values
Name (FCTU, 2) // RPM Up
Name (FCTD, 5) // RPM Down
// Table to keep track of past temperatures (to track average)
Name (FHST, Buffer() { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }) // Size should match the count of above FTA1 and FTA2 package
Name (FIDX, 0) // current index in buffer above
Name (FNUM, 0) // number of entries in above buffer to count in avg
Name (FSUM, 0) // current sum of entries in buffer
// Keeps track of last fan speed set, and counter to set new one
Name (FLST, 0xFF) // last index for fan control
Name (FCNT, 0) // count of times it has been "wrong", 0 means no counter
// Method to control FAN wrt TEMP
// Name in ACPIPoller.kext's INFO.PLIST should be FCPU with HID FAN0000
Method (FCPU, 0)
{
// If ICFC is set to 0, terminate and hence disable custom fan control
If (CondRefOf(\NOPT.ICFC))
{
If (\NOPT.ICFC == 0)
{
Debug = "Custom FAN Control disabled via CONFIG"
Return (0)
}
}
// If EC is not ready, terminate
If (!\_SB.PCI0.LPCB.EC0.ECAV())
{
Return (0)
}
Local5 = \_SB.PCI0.LPCB.EC0.ECPU // Current temperature of the CPU Heatsink
If (Local5 < 128)
{
Local0 = Local5 // Store temperature in Local0
}
Else
{
Local0 = 60 // As per BIOS
}
// calculate average temperature
Local1 = Local0 + FSUM
Local2 = FIDX
Local1 -= DerefOf(FHST[Local2])
FHST[Local2] = Local0
FSUM = Local1 // Local1 is new sum
// adjust current index into temperature history table
Local2++
if (Local2 >= SizeOf(FHST)) { Local2 = 0 }
FIDX = Local2
// adjust total items collected in temp table
Local2 = FNUM
if (Local2 != SizeOf(FHST))
{
Local2++
FNUM = Local2
}
// Local1 is new sum, Local2 is number of entries in sum
Local0 = Local1 / Local2 // Local0 is now average temp
// table based search (use avg temperature to search)
if (Local0 > 255) { Local0 = 255 }
Local2 = Match(FTA1, MGE, Local0, MTR, 0, 0)
// calculate difference between current and found index
if (Local2 > FLST)
{
Local1 = Local2 - FLST
Local4 = FCTU
}
else
{
Local1 = FLST - Local2
Local4 = FCTD
}
// set new fan speed, if necessary
if (!Local1)
{
// no difference, so leave current fan speed and reset count
FCNT = 0
}
else
{
// there is a difference, start/continue process of changing fan
Local3 = FCNT
FCNT++
// how long to wait depends on how big the difference
// 20 secs if diff is 2, 5 secs if diff is 4, etc.
Local1 = Local4 / Local1
if (Local3 >= Local1)
{
// timeout expired, so set new fan speed
FLST = Local2
\_SB.PCI0.LPCB.EC0.ST98 (DerefOf(FTA2[Local2]))
FCNT = 0
}
}
Return (1) // Return something as this is a requirement of a ACPI Method
}
}
}