Contribute
Register

Fan speed control on High Sierra

Status
Not open for further replies.
Hi, @black.dragon74
I’m currently trying to do something with my laptop’s fan. I'm looking at your SSDT-OVERRIDES, and I have some questions.

How do you build fan tables, FTA1 and FTA2? Is FTA1 just temperature range in integer, from 32° to 52°? Then why it contains values up to 52, while comment says "if temp is above 46C, let bios take control of things"?

I'm not up to speed on ACPI. What is the reason for DerefOf here?:
Code:
Local2 = FIDX
Local1 -= DerefOf(FHST[Local2])
FHST[Local2] = Local0
Is this just the way to access list elements in ACPI?

What is \_SB.PCI0.LPCB.EC0.BRAH used for? It's in TCPU in your SSDT-FAN from post #9, but missing from SSDT-OVERRIDES.

BTW, I'm going to try using native speed control, as @Ig0r described in #34, perhaps with some temperature hysteresis instead of timeouts. Will report if I succeed.

Also, min and max speeds I get are ~4080 to ~6000 RPM — are those even real values for laptop fan? Speed calculations are the same as in original DSDT, though (and Windows tools report them too), but they do seem huge.
 
I'm not up to speed on ACPI. What is the reason for DerefOf here?:
Code:
Local2 = FIDX
Local1 -= DerefOf(FHST[Local2])
FHST[Local2] = Local0
Is this just the way to access list elements in ACPI?

The subscript operator (actually is Index opcode) evaluates to a reference of the array item.
To extract the value, you need DerefOf.

eg.
FHST[Local2]
is really
Index(FHST,Local2)

To get the value that is stored there, needs to be:
DerefOf(Index(FHST,Local2))

You might want to read the ACPI specification...
 
I changed the values but then forgot to update the comment.

More info : https://www.tonymacx86.com/threads/fan-speed-control-on-high-sierra.238454/page-4#post-1639347
Okay. Temperature ranges look good for me, too. Forgot I don't have NVidia furnace. Probably don't even need all that speed range, too.

Another question — do you reset/return fan control to system on sleep or reboot? Like, adding code to _PTS?

FHST[Local2]
is really
Index(FHST,Local2)

To get the value that is stored there, needs to be:
DerefOf(Index(FHST,Local2))
Ah, yes, I didn't look at Index. Thanks.
 
Another question — do you reset/return fan control to system on sleep or reboot? Like, adding code to _PTS?
Nope. The implementation I use doesn’t need to do _PTS stuff manually. System takes care of that.

I am very busy ATM. Will write a guide and will explain everything that I can there.
 
Ok, I have some indeterminate problem. ACPIPoller.kext doesn't load. I.e. it isn't in kextstat output. It is in /L/E, kextcache is updated, all as usual. @RehabMan, what might be the problem? Maybe there's incompatibility with other kexts? Report attached.
 

Attachments

  • debug_19638.zip
    2 MB · Views: 86
Ok, I have some indeterminate problem. ACPIPoller.kext doesn't load. I.e. it isn't in kextstat output. It is in /L/E, kextcache is updated, all as usual. @RehabMan, what might be the problem? Maybe there's incompatibility with other kexts? Report attached.
Can’t take a look at your debug report now but have you made sure that the Method name in Info.plist of ACPIPoller matches to the method in Fan control SSDT?
 
Hello everyone! Here is the raw code of SSDT-FAN with custom fan control implemented. This should work for all ASUS laptops (Haswell or above, not tested below that). Read the comments to understand how it works. Method to calculate average is based on @RehabMan's work! (Thank You!)

I have also attached a compiled AML, you can put it in CLOVER/ACPI/Patched (If using sorted order don't forget to add "SSDT-FAN.aml" to it)

Code:
// 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
        }  
                       
    }
}

I won't be able to provide any support until last week of march 2018 as I have my exams. After that I will possibly create a new thread for custom FAN control.

Note: Use the attached ACPIPoller.kext in conjunction with this SSDT. Install ACPIPoller.kext to /L/E (On 10.11+) or /S/L/E

Regards
 

Attachments

  • SSDT-FAN.aml.zip
    773 bytes · Views: 165
  • ACPIPoller.zip
    10.1 KB · Views: 141
Last edited:
Ok, I have some indeterminate problem. ACPIPoller.kext doesn't load. I.e. it isn't in kextstat output. It is in /L/E, kextcache is updated, all as usual. @RehabMan, what might be the problem? Maybe there's incompatibility with other kexts? Report attached.

ACPIPoller.kext matches against FAN00000. You coded FAN0000 in SSDT-FAN.aml.
No expectation that ACPIPoller will load with an incorrect _HID.
 
To everyone, please note that SSDT-FAN expects the method "FCPU" to be called every second, for that you need to have ACPIPoller.kext installed (see a few posts above).

Some general notes regarding ACPIPoller's Info.plist
  • Make sure the IONameMatch field matches to FAN's _HID in SSDT-FAN (FAN0000 in my SSDT-FAN)
  • Make sure the method array's object matches to the name of fan control method in our SSDT (FCPU in my SSDT-FAN)
I am attaching an image to help you with the same:

Screen Shot 2018-02-17 at 7.34.01 PM.png


If you miss these, fan control will not work (you will only get RPM and TEMP readings)

Regards
 
Status
Not open for further replies.
Back
Top