AMD GPU advanced but very simple fanspeed controller script

I have hesitated a long time before I decided to write this.
The fact that it may help others made me decide to do it anyway.

I have ran this little gadget for years now and it is probably the most robust and unobtrusive piece of software in my system.

AMD GPU cooling

Although I really love my XFX RX 570 discrete GPU and it renders everything I throw at it with remarkable speed and without a hitch (yes, even FarCry 6) it has two drawbacks:

  1. The fans are absolutely great but also very loud when running fullspeed (4000 rpm).
  2. The card gets extremely hot under Linux (on MS-Windows it's ok)

The reason it runs hot under Linux is that there is no fancontrol so the fans are at a fixed speed of around 1453 rpm which is nice and silent but in my opinion inadequate when running very heavy loads.

For MS-Windows there is the AMD driver software suite that takes care of it but for Linux there was nothing, at least not when I wrote this script.

The familiy of AMD RX chips can handle a maximum temperature of a stunning 94°C ( 200°F ) before they either throttle or shut down but running the GPU close to that temperature is in my opinion way too hot to be healthy, possibly shortening the lifespan of the GPU considerably.

Goals:

  1. I want the maximum temperature set according to my wishes.
  2. The fan should be at full speed at maximum temperature regardless any other settings: Survival of the card is of utmost importance. It should never exceed the maximum temperature that I configure.
  3. The fan should stop (preventing unneeded wear and tear) when the temperature is below a certain level
  4. The script must allow for the possibility to balance temperature with fan noise because every build and every person is different.
  5. The script must be extremely simple. The reasons are
    a. less chance on bugs, easier to spot them
    b. easier to reason about how it works
    c. much more robust (which is very important because GPUs are pretty expensive).
  6. It must be an invisible "set it and forget it" script.

What it turned out to be:

All of the above, You can change the following parameters in the script:

  1. temperature for maximum fanspeed
  2. temperature where the fan kicks in
  3. temperature where the fan shuts off
  4. fansetting for minimum rpm (also prevents stalling)

At the end of the script there is a sleep statement that defines the time interval between measurements. It is set at 10 seconds which is frequent enough but you may set it to 5 or even less although I wouldn't go much lower than that because it will only consume more CPU time without resulting in any advantage in return.

What to check before saving the script

The GPU userspace-interface usually resides here : '/sys/class/drm/card0/device/hwmon/hwmon1'

The script will exit immediately if it doesn't so you might want to make sure that it really is there.

This is the code:

#!/bin/bash

## This is where the GPU-unit resides
cd /sys/class/drm/card0/device/hwmon/hwmon1 || exit

## give control back to the GPU-unit whenever we exit:
trap "echo 2 >pwm1_enable ; exit" INT TERM EXIT

## BEWARE!! temperature is expressed in °C * 1000 !!
t_max=80000             ## temperature for maximum fanspeed
t_fan_on=50000 	        ## temperature where the fan kicks in
t_fan_off=45000	        ## temperature where the fan shuts off
f_min=100				## fansetting for minimum rpm (also prevents stalling)

## don't touch the values below
f_max=255 ; f_off=0   
while :
do
    ## get current temperature
    t_current=$(<temp1_input)

    ## low temperature -> shut fan off
    (( t_current < t_fan_off )) && (( f_set = f_off ))

    ## when in operational range ...
    if  (( t_current > t_fan_on ))
    then
        ## ... calculate fansetting ...
        (( f_set = f_max * ( t_current - t_fan_on ) / ( t_max - t_fan_on ) ))

        ## ... and prevent fan from dropping below minimum rpm
        (( f_set < f_min )) && (( f_set = f_min ))
    fi

    echo 1 >pwm1_enable     ## always take control of the GPU first
    echo $f_set >pwm1       ## write preferred fanspeed setting to GPU
    /usr/bin/sleep 10       ## polling interval in seconds (default: 10)
done

How does it work

I have to reveal that I am artistically challanged so forgive my doodling:

Things to keep in mind

  1. Adjusting t_fan_on to a higher temperature (that is: to the right) will result in marginally less fan noise and a higher constant temperature

  2. Adjusting t_fan_on to a higher temperature will also result in a steeper curve because the f_max/t_max point stays where it is.
    Reason: fan must always be full speed at maximum temperature point.

  3. Setting the t_fan_on value too high will result in significant and reasonably fast rpm changes whenever it reaches the diagonal part of the curve which might get on your nerves.

  4. the distance between t_fan_on and t_fan_off is to prevent the controller from oscillating around one switchpoint, also known as hysteresis.

  5. Setting t_fan_off at a value lower than case temperature or minimum GPUtemperature wil, ofcourse, result in an always running fan.

  6. Setting f_min equal to f_max will result in maximum fanspeed whenever the fan is on, always.

  7. Keep f_on at least at 80 or 90 to prevent stalling of the fan

  8. Keep t_fan_on at least 10°C lower than t_max

  9. Keep t_fan_off at least a few °C lower than than t_fan_on:

testing

Save the script as '/usr/local/bin/fanctl' and make it executable and run it with

sudo /usr/local/bin/fanctl

starting it automatically at boot time
paste codeblock below in a terminal and press enter

cat<<\FANCTL | sudo tee /lib/systemd/system/fanctl.service
[Unit]
Description=AMDGPU Fancontrol Daemon
ConditionPathExists=/usr/local/bin/fanctl
ConditionFileIsExecutable=/usr/local/bin/fanctl
After=multi-user.target

[Service]
ExecStart=/usr/local/bin/fanctl

[Install]
WantedBy=multi-user.target
FANCTL

sudo ln -s /lib/systemd/system/fanctl.service /etc/systemd/system/multi-user.target.wants/fanctl.service

Extra

I also wrote a monitoring tool to keep an eye on temperature and fan behaviour.
It has the following features:

  1. showing fan rpm
  2. showing GPU temperature
  3. showing if the card is controlled by a fancontroller.
  4. showing if my fancontroller is doing that job
  5. starting and stopping of the fancontroller
  6. editing the fancontroller

I will post the code of this monitoring tool in a followup if any of you is interested.

Below you see a picture with this monitoring tool in action ( monitoring temperature and fanspeed ) while both Unigine Heaven and Furmark are heavily torturing (a.k.a. stresstesting) my GPU at the same time, this has been running for 10 minutes before I took the screenshot and it is keeping the GPU at a nice constant 72°C . Settings of the fancontroller are the same as in the code above.

Conclusion: project succeeded :slight_smile:

If you have any questions or discover an error ( despite me having checking everything carefully in this post ). Please let me know in this thread.

EDIT 2024-08-09:
Today I have found some excellent documentation on the sysfs interface of AMDGPU.
You'll recognize one or two things that I've used in the script.
https://www.kernel.org/doc/html/v6.8/gpu/amdgpu/thermal.html

7 Likes

Just after posting this, I discovered that somebody else created a fancontroller too and it has a lot of possibilities.

features:

  • Viewing information about the GPU
  • Power/thermals monitoring
  • Fan curve control
  • Overclocking (GPU/VRAM clockspeed, voltage)
  • Power states configuration

It has a few (very) minor drawbacks though:

  • The inteface is Gnome4x based on libadwaita :face_vomiting:
  • it is not as lightweight, not even in the same ballpark
  • it has a graphic fan-tuner instead of a parametric one.

At least, check it out. You might like it better than mine. :+1:

6 Likes

Fan and Temperature monitor

Here is the code for the 'monitoring tool' that you can see in the last picture of the first post (above) and that I promised earlier.
It can also start and stop the fanctrl daemon on demand.

#!/bin/bash

trap "tput sgr0 ; tput cnorm ; exit" INT TERM EXIT

# Get colors #
f="$(tput blink)"
b="$(tput bold)"
n="$(tput sgr0)"
y="$(tput setaf 3)"
c="$(tput setaf 6)"
r="$(tput setaf 1)"
R="$r$b"
C="$c$b"
F="$c$b$f"
y="$n$y"
tput civis # hide the cursor

declare -r fanmode=( "${R}error${y}" "${C}fanctl${y}" "${c}autonomous${y}" )
declare -r closing="${F}closing${y}"
declare -r fanctl="/usr/local/bin/fanctl"
declare -u choice

# This is where the GPU-unit most often resides
cd /sys/class/drm/card0/device/hwmon/hwmon1

while :
do
	realtemp="$(<temp1_input)"; pwm="$(<pwm1)"; actualrpm="$(<fan1_input)"

	# fan physics
	if pgrep fanctl >/dev/null
	then status="${C}running${y}" && [ "$pwm" -lt "10" ] && actualrpm="0"
	else status="${c}off${y}"
	fi

	# fan driver on/off
	if [ "$stop" ]
	then [ "$status" = "${C}running${y}" ] && status="$closing" || unset stop
	fi

	# display menu
	clear ; cat<<-MENU
	${y}GPU temperature	= ${C}${realtemp:0:-3}${y}°C
	Fanspeed 	= ${C}${actualrpm}${y} rpm
	Fanregulation	= ${fanmode[$(<pwm1_enable)]}
	Fanctl-daemon	= ${status}
	________________________________${n}
	[${b}L${n}]aunch	[${b}F${n}]anctl edit
	[${b}S${n}]top		[${b}Q${n}]uit
MENU
	read -s -n1 -t 1 choice
	case "$choice" in
	@) pluma "$0" &>/dev/null; exec "$0"	;;
	F) sudo pluma "$fanctl" &>/dev/null		;;
	L) sudo systemd-run fanctl &>/dev/null	;;
	S) sudo killall -TERM fanctl; stop=true	;;
	Q) exit									;;
	esac
done
5 Likes

Thank you, @tkn. That looks very interesting and promising!

Unfortunately for me, I see no file/directory that hints at being a substitute for hwmon. Here is my directory listing

root@OasisMega1:/Oasis/bin# ls -lL /sys/class/drm/card0/device
total 0
-r--r--r-- 1 root root      4096 Aug  7 17:23 ari_enabled
-r--r--r-- 1 root root      4096 Aug  7 17:23 boot_vga
-rw-r--r-- 1 root root      4096 Aug  7 17:23 broken_parity_status
-r--r--r-- 1 root root      4096 Aug  7 17:23 class
-rw-r--r-- 1 root root       256 Aug  7 17:23 config
-r--r--r-- 1 root root      4096 Aug  7 17:23 consistent_dma_mask_bits
drwxr-xr-x 2 root root         0 Aug  7 17:23 consumer:pci:0000:01:05.1
-rw-r--r-- 1 root root      4096 Aug  7 17:23 d3cold_allowed
-r--r--r-- 1 root root      4096 Aug  7 16:24 device
-r--r--r-- 1 root root      4096 Aug  7 17:23 dma_mask_bits
drwxr-xr-x 2 root root         0 Aug  7 17:23 driver
-rw-r--r-- 1 root root      4096 Aug  7 17:23 driver_override
drwxr-xr-x 4 root root         0 Aug  7 16:24 drm
-rw-r--r-- 1 root root      4096 Aug  7 17:23 enable
drwxr-xr-x 4 root root         0 Aug  7 16:24 firmware_node
drwxr-xr-x 3 root root         0 Aug  7 17:23 graphics
drwxr-xr-x 4 root root         0 Aug  7 16:24 i2c-0
drwxr-xr-x 4 root root         0 Aug  7 16:24 i2c-1
drwxr-xr-x 4 root root         0 Aug  7 16:24 i2c-2
drwxr-xr-x 4 root root         0 Aug  7 16:24 i2c-3
drwxr-xr-x 4 root root         0 Aug  7 16:24 i2c-4
-r--r--r-- 1 root root      4096 Aug  7 17:23 irq
drwxr-xr-x 2 root root         0 Aug  7 17:23 link
-r--r--r-- 1 root root      4096 Aug  7 17:23 local_cpulist
-r--r--r-- 1 root root      4096 Aug  7 17:23 local_cpus
-r--r--r-- 1 root root      4096 Aug  7 17:23 modalias
-rw-r--r-- 1 root root      4096 Aug  7 17:23 msi_bus
-rw-r--r-- 1 root root      4096 Aug  7 17:23 numa_node
drwxr-xr-x 2 root root         0 Aug  7 16:24 power
-rw-r--r-- 1 root root      4096 Aug  7 17:23 power_method
-rw-r--r-- 1 root root      4096 Aug  7 17:23 power_profile
-r--r--r-- 1 root root      4096 Aug  7 17:23 power_state
--w--w---- 1 root root      4096 Aug  7 17:23 remove
--w------- 1 root root      4096 Aug  7 17:23 rescan
--w------- 1 root root      4096 Aug  7 17:23 reset
-rw-r--r-- 1 root root      4096 Aug  7 17:23 reset_method
-r--r--r-- 1 root root      4096 Aug  7 17:23 resource
-rw------- 1 root root 268435456 Aug  7 17:23 resource0
-rw------- 1 root root 268435456 Aug  7 17:23 resource0_wc
-rw------- 1 root root       256 Aug  7 17:23 resource1
-rw------- 1 root root     65536 Aug  7 17:23 resource2
-rw------- 1 root root   1048576 Aug  7 17:23 resource5
-r--r--r-- 1 root root      4096 Aug  7 17:23 revision
-rw------- 1 root root    131072 Aug  7 17:23 rom
drwxr-xr-x 5 root root         0 Aug  7 15:04 subsystem
-r--r--r-- 1 root root      4096 Aug  7 17:23 subsystem_device
-r--r--r-- 1 root root      4096 Aug  7 15:05 subsystem_vendor
-rw-r--r-- 1 root root      4096 Aug  7 16:24 uevent
-r--r--r-- 1 root root      4096 Aug  7 16:24 vendor
root@OasisMega1:/Oasis/bin# 

This is my report from "inxi":

 COMMAND: 'inxi -y 255 -c 0 -M -xxx'
Machine:   Type: Desktop Mobo: ASUSTeK model: M4A78-E v: Rev 1.xx serial: 101048580000313 BIOS: American Megatrends v: 2603 date: 04/13/2011 

----------------------------------------------------------------------
  COMMAND: 'inxi -y 255 -c 0 -G -xxx'
Graphics:  Device-1: Advanced Micro Devices [AMD/ATI] RS780D [Radeon HD 3300] vendor: ASUSTeK driver: radeon v: kernel bus ID: 01:05.0 chip ID: 1002:9614 
           Display: server: X.Org 1.20.13 driver: ati,radeon unloaded: fbdev,modesetting,vesa compositor: compton v: 0.1~beta2+20150922 resolution: 1440x900~60Hz 
           OpenGL: renderer: AMD RS780 (DRM 2.50.0 / 5.4.0-186-generic LLVM 12.0.0) v: 3.3 Mesa 21.2.6 compat-v: 3.0 direct render: Yes 

----------------------------------------------------------------------
 COMMAND: 'inxi -y 255 -c 0 -s -xxx'
Sensors:   System Temperatures: cpu: 55.0 C mobo: 51.0 C
           Fan Speeds (RPM): cpu: 4753 case-1: 0 case-2: 0
           Power: 12v: 12.36 5v: N/A 3.3v: 3.47 vbat: N/A

However, I have sensors active and showing in my Panel Applet:

I have fans as follows:

  • fan 1 blowing cooling air away from top CPU,
  • fan 2 rear interface panel, blowing air into chassis,
  • fan 3 on side of chassis, blowing air into chassis, (my retrofit) and
  • (uncontrolled) fan 4 on PSU blowing chassis air out.

Would you have advice on where, or what, to look for, to make your tool work for me?

Hi Eric,


EDIT:
I checked your motherboard model, and as far as I can see it has an RS790GX which is a RS780 integrated in the northbridge on bord and thus it has no fan whatsoever.
In this case a GPU fancontroller would, ofcourse, be impossible without fan.
However: If you also have a discrete and actively cooled RS780 GPU card, and disabled the on-board GPU, you might read on.


This might be a bit difficult: My script, as is, only works on "AMDGPU"

The RS780 is quite old and is not supported by the "AMDGPU" driver but by the "Radeon" driver which is quite a different beast.

I am a bit familiar with the old ATI "RS780". I used to have a laptop and a motherboard with a RS780 onboard (long long ago) and assisted a little bit in the creation of a kernelpatch for the, in some cases, somewhat unstable PLL behaviour of that chip.

This is a chipset of the "R600" family,
Details can be found here

If you have installed lm-sensors it should list the fan if the parameters are available. ( see also this post )

If the fan is not listed, the driver does not offer the possibility to influence the fanspeed. Also, if there is no sensor listed you can not get the temperature.

To list what your system offers with respect to these parameters you can use this command:

find /sys/class/hwmon/*/fan* |while read line
do
    echo -ne "$line\t"
    cat "${line%/*}/name"
done 2>/dev/null

It will list all fans detected with the name of the chip/driver it is connected to.

On my system, the relevant part of the output gives me this:

/sys/class/hwmon/hwmon1/fan1_enable	amdgpu
/sys/class/hwmon/hwmon1/fan1_input	amdgpu
/sys/class/hwmon/hwmon1/fan1_max	amdgpu
/sys/class/hwmon/hwmon1/fan1_min	amdgpu
/sys/class/hwmon/hwmon1/fan1_target	amdgpu

If your chip/driver is not listed, then there is no way to get it working with my script.
However, if it is listed, there is a possibility that the script might be adapted to the parameters c.q. driver/kernel variables that /sys/ has offer. Beware though that this is highly experimental territory. :slight_smile:

4 Likes

Thank you, @tkn,

I get the following report:

/sys/class/hwmon/hwmon0/fan1_input	atk0110
/sys/class/hwmon/hwmon0/fan1_label	atk0110
/sys/class/hwmon/hwmon0/fan1_max	atk0110
/sys/class/hwmon/hwmon0/fan1_min	atk0110
/sys/class/hwmon/hwmon0/fan2_input	atk0110
/sys/class/hwmon/hwmon0/fan2_label	atk0110
/sys/class/hwmon/hwmon0/fan2_max	atk0110
/sys/class/hwmon/hwmon0/fan2_min	atk0110
/sys/class/hwmon/hwmon0/fan3_input	atk0110
/sys/class/hwmon/hwmon0/fan3_label	atk0110
/sys/class/hwmon/hwmon0/fan3_max	atk0110
/sys/class/hwmon/hwmon0/fan3_min	atk0110

So, I will experiment with your fanspeed script and get back to you on the degree of success I get with that.

Thanks again!

2 Likes

@tkn,

I can't find an hwmon at the location

/sys/class/drm/card0/device/hwmon/hwmon1

you specify in your first script (fanctl). The listing of files in my tree under device are as follows:

~/systemd_local# ls -lL /sys/class/drm/card?/device
total 0
-r--r--r-- 1 root root      4096 Aug  8 19:54 ari_enabled
-r--r--r-- 1 root root      4096 Aug  8 17:13 boot_vga
-rw-r--r-- 1 root root      4096 Aug  8 19:54 broken_parity_status
-r--r--r-- 1 root root      4096 Aug  8 17:13 class
-rw-r--r-- 1 root root       256 Aug  8 17:13 config
-r--r--r-- 1 root root      4096 Aug  8 19:54 consistent_dma_mask_bits
drwxr-xr-x 2 root root         0 Aug  8 19:54 consumer:pci:0000:01:05.1
-rw-r--r-- 1 root root      4096 Aug  8 19:54 d3cold_allowed
-r--r--r-- 1 root root      4096 Aug  8 17:13 device
-r--r--r-- 1 root root      4096 Aug  8 19:54 dma_mask_bits
drwxr-xr-x 2 root root         0 Aug  8 17:12 driver
-rw-r--r-- 1 root root      4096 Aug  8 19:54 driver_override
drwxr-xr-x 4 root root         0 Aug  8 17:12 drm
-rw-r--r-- 1 root root      4096 Aug  8 19:54 enable
drwxr-xr-x 4 root root         0 Aug  8 19:54 firmware_node
drwxr-xr-x 3 root root         0 Aug  8 17:12 graphics
drwxr-xr-x 4 root root         0 Aug  8 17:12 i2c-0
drwxr-xr-x 4 root root         0 Aug  8 17:12 i2c-1
drwxr-xr-x 4 root root         0 Aug  8 17:12 i2c-2
drwxr-xr-x 4 root root         0 Aug  8 17:12 i2c-3
drwxr-xr-x 4 root root         0 Aug  8 17:12 i2c-4
-r--r--r-- 1 root root      4096 Aug  8 17:13 irq
drwxr-xr-x 2 root root         0 Aug  8 19:54 link
-r--r--r-- 1 root root      4096 Aug  8 19:54 local_cpulist
-r--r--r-- 1 root root      4096 Aug  8 19:54 local_cpus
-r--r--r-- 1 root root      4096 Aug  8 17:12 modalias
-rw-r--r-- 1 root root      4096 Aug  8 19:54 msi_bus
-rw-r--r-- 1 root root      4096 Aug  8 19:54 numa_node
drwxr-xr-x 2 root root         0 Aug  8 17:13 power
-rw-r--r-- 1 root root      4096 Aug  8 19:54 power_method
-rw-r--r-- 1 root root      4096 Aug  8 19:54 power_profile
-r--r--r-- 1 root root      4096 Aug  8 19:54 power_state
--w--w---- 1 root root      4096 Aug  8 19:54 remove
--w------- 1 root root      4096 Aug  8 19:54 rescan
--w------- 1 root root      4096 Aug  8 19:54 reset
-rw-r--r-- 1 root root      4096 Aug  8 19:54 reset_method
-r--r--r-- 1 root root      4096 Aug  8 17:13 resource
-rw------- 1 root root 268435456 Aug  8 19:54 resource0
-rw------- 1 root root 268435456 Aug  8 19:54 resource0_wc
-rw------- 1 root root       256 Aug  8 19:54 resource1
-rw------- 1 root root     65536 Aug  8 19:54 resource2
-rw------- 1 root root   1048576 Aug  8 19:54 resource5
-r--r--r-- 1 root root      4096 Aug  8 17:13 revision
-rw------- 1 root root    131072 Aug  8 19:54 rom
drwxr-xr-x 5 root root         0 Aug  8 17:12 subsystem
-r--r--r-- 1 root root      4096 Aug  8 17:13 subsystem_device
-r--r--r-- 1 root root      4096 Aug  8 17:13 subsystem_vendor
-rw-r--r-- 1 root root      4096 Aug  8 17:19 uevent
-r--r--r-- 1 root root      4096 Aug  8 17:12 vendor
~/systemd_local# 

Any suggestions as to what I should refer to instead for the corresponding line in your script?

Also, you reference GPU-specific utilities

  • pwm1,
  • pwm1_enable, and
  • pwm1_disable.

Would you happen to know what would be the corresponding utilities for a non-GPU fan control? I can't seem to locate that in the tree:


############################################################################
############################################################################
	BRANCH:	/sys/class/hwmon/hwmon0/*
############################################################################
############################################################################


root:~/systemd_local# more /sys/class/hwmon/hwmon0/*

*** /sys/class/hwmon/hwmon0/device: directory ***

*** /sys/class/hwmon/hwmon0/power: directory ***

*** /sys/class/hwmon/hwmon0/subsystem: directory ***

::::::::::::::
/sys/class/hwmon/hwmon0/fan1_input
::::::::::::::
4963

::::::::::::::
/sys/class/hwmon/hwmon0/fan1_label
::::::::::::::
CPU FAN Speed

::::::::::::::
/sys/class/hwmon/hwmon0/fan1_max
::::::::::::::
7200

::::::::::::::
/sys/class/hwmon/hwmon0/fan1_min
::::::::::::::
600

::::::::::::::
/sys/class/hwmon/hwmon0/fan2_input
::::::::::::::
0

::::::::::::::
/sys/class/hwmon/hwmon0/fan2_label
::::::::::::::
CHASSIS FAN Speed

::::::::::::::
/sys/class/hwmon/hwmon0/fan2_max
::::::::::::::
7200

::::::::::::::
/sys/class/hwmon/hwmon0/fan2_min
::::::::::::::
600

::::::::::::::
/sys/class/hwmon/hwmon0/fan3_input
::::::::::::::
0

::::::::::::::
/sys/class/hwmon/hwmon0/fan3_label
::::::::::::::
CHASSIS FAN 2 Speed

::::::::::::::
/sys/class/hwmon/hwmon0/fan3_max
::::::::::::::
7200

::::::::::::::
/sys/class/hwmon/hwmon0/fan3_min
::::::::::::::
600

::::::::::::::
/sys/class/hwmon/hwmon0/in0_input
::::::::::::::
1239

::::::::::::::
/sys/class/hwmon/hwmon0/in0_label
::::::::::::::
Vcore Voltage

::::::::::::::
/sys/class/hwmon/hwmon0/in0_max
::::::::::::::
1700

::::::::::::::
/sys/class/hwmon/hwmon0/in0_min
::::::::::::::
850

::::::::::::::
/sys/class/hwmon/hwmon0/in1_input
::::::::::::::
3465

::::::::::::::
/sys/class/hwmon/hwmon0/in1_label
::::::::::::::
 +3.3 Voltage

::::::::::::::
/sys/class/hwmon/hwmon0/in1_max
::::::::::::::
3630

::::::::::::::
/sys/class/hwmon/hwmon0/in1_min
::::::::::::::
2970

::::::::::::::
/sys/class/hwmon/hwmon0/in2_input
::::::::::::::
5107

::::::::::::::
/sys/class/hwmon/hwmon0/in2_label
::::::::::::::
 +5 Voltage

::::::::::::::
/sys/class/hwmon/hwmon0/in2_max
::::::::::::::
5500

::::::::::::::
/sys/class/hwmon/hwmon0/in2_min
::::::::::::::
4500

::::::::::::::
/sys/class/hwmon/hwmon0/in3_input
::::::::::::::
12364

::::::::::::::
/sys/class/hwmon/hwmon0/in3_label
::::::::::::::
 +12 Voltage

::::::::::::::
/sys/class/hwmon/hwmon0/in3_max
::::::::::::::
13800

::::::::::::::
/sys/class/hwmon/hwmon0/in3_min
::::::::::::::
10200

::::::::::::::
/sys/class/hwmon/hwmon0/name
::::::::::::::
atk0110

::::::::::::::
/sys/class/hwmon/hwmon0/temp1_crit
::::::::::::::
95000

::::::::::::::
/sys/class/hwmon/hwmon0/temp1_input
::::::::::::::
59000

::::::::::::::
/sys/class/hwmon/hwmon0/temp1_label
::::::::::::::
CPU Temperature

::::::::::::::
/sys/class/hwmon/hwmon0/temp1_max
::::::::::::::
60000

::::::::::::::
/sys/class/hwmon/hwmon0/temp2_crit
::::::::::::::
75000

::::::::::::::
/sys/class/hwmon/hwmon0/temp2_input
::::::::::::::
52000

::::::::::::::
/sys/class/hwmon/hwmon0/temp2_label
::::::::::::::
MB Temperature

::::::::::::::
/sys/class/hwmon/hwmon0/temp2_max
::::::::::::::
45000

::::::::::::::
/sys/class/hwmon/hwmon0/uevent
::::::::::::::
	{null return}

root:~/systemd_local#


############################################################################
############################################################################
	BRANCH:	/sys/class/hwmon/hwmon0/power/*
############################################################################
############################################################################


root:~/systemd_local# more /sys/class/hwmon/hwmon0/power/*

::::::::::::::
/sys/class/hwmon/hwmon0/power/async
::::::::::::::
disabled

::::::::::::::
/sys/class/hwmon/hwmon0/power/autosuspend_delay_ms
::::::::::::::
	{null return}

::::::::::::::
/sys/class/hwmon/hwmon0/power/control
::::::::::::::
auto

::::::::::::::
/sys/class/hwmon/hwmon0/power/runtime_active_kids
::::::::::::::
0

::::::::::::::
/sys/class/hwmon/hwmon0/power/runtime_active_time
::::::::::::::
0

::::::::::::::
/sys/class/hwmon/hwmon0/power/runtime_enabled
::::::::::::::
disabled

::::::::::::::
/sys/class/hwmon/hwmon0/power/runtime_status
::::::::::::::
unsupported

::::::::::::::
/sys/class/hwmon/hwmon0/power/runtime_suspended_time
::::::::::::::
0

::::::::::::::
/sys/class/hwmon/hwmon0/power/runtime_usage
::::::::::::::
0

root:~/systemd_local#


############################################################################
############################################################################
	BRANCH:	 /sys/class/hwmon/hwmon0/device/*
############################################################################
############################################################################


root:~/systemd_local# more /sys/class/hwmon/hwmon0/device/*

*** /sys/class/hwmon/hwmon0/device/driver: directory ***

*** /sys/class/hwmon/hwmon0/device/hwmon: directory ***

*** /sys/class/hwmon/hwmon0/device/physical_node: directory ***

*** /sys/class/hwmon/hwmon0/device/power: directory ***

*** /sys/class/hwmon/hwmon0/device/subsystem: directory ***

::::::::::::::
/sys/class/hwmon/hwmon0/device/hid
::::::::::::::
ATK0110

::::::::::::::
/sys/class/hwmon/hwmon0/device/modalias
::::::::::::::
acpi:ATK0110:

::::::::::::::
/sys/class/hwmon/hwmon0/device/path
::::::::::::::
\_SB_.PCI0.SBRG.ASOC

::::::::::::::
/sys/class/hwmon/hwmon0/device/status
::::::::::::::
15

::::::::::::::
/sys/class/hwmon/hwmon0/device/uevent
::::::::::::::
DRIVER=ATK0110
MODALIAS=acpi:ATK0110:

::::::::::::::
/sys/class/hwmon/hwmon0/device/uid
::::::::::::::
16843024

root:~/systemd_local#

@tkn,

Offering up a "tweaked" version of the systemd unit creation code. Hope you like. :slight_smile:

That is followed by tweaked version of the fanctl script. :slight_smile:

#!/bin/bash

####################################################################################################
###
###     REF:
###     https://ubuntu-mate.community/t/amd-gpu-advanced-but-very-simple-fanspeed-controller-script/28053
###	
###	Credits:
###	@tkn		Original version of this script was posted by @tkn on the UbuntuMATE Community Help Blog
###	@ericmarceau	Adaptation for hybrid CPU with GPU configuration on ASUSTeK M4A78-E motherboard
###
###	Script will create a systemd Service Unit to manage a custom utility
###	to control fan-speed in response to CPU temperature
###
###	The custom utility to dynamically control CPU fans-speed
###		custom_fanctl
###
###	A complementary GUI monitor is available and is started using the script
###		custom_fanctl_monitor
###
####################################################################################################
#23456789+123456789+123456789+123456789+123456789+123456789+123456789+123456789+123456789+123456789+



###
###	custom fan control utility setup
###
fan_utility="/usr/local/bin/fanctl"


###
###	systemd service setup
###
service_defn="/root/systemd_custom/fanctl.service"
service_file="/lib/systemd/system/fanctl.service"
service_want="/etc/systemd/system/multi-user.target.wants/fanctl.service"
service_trgt="multi-user.target"

cat >"${service_defn}"  <<FANCTL
[Unit]
Description=AMDGPU Fancontrol Daemon
ConditionPathExists=${fan_utility}
ConditionFileIsExecutable=${fan_utility}
After=${service_trgt}

[Service]
ExecStart=${fan_utility}

[Install]
WantedBy=${service_trgt}
FANCTL

printf "\n Contents of '${service_defn}':"
cat "${service_defn}"

echo ""
ls -l "${service_defn}"

sudo cp -pv "${service_defn}" "${service_file}"
sudo ln -sv "${service_file}" "${service_want}"

echo ""
ls -l "${service_file}" "${service_want}"

Similar tweak on the actual fanctl script. Again, hope you like. :slight_smile:

#!/bin/bash

####################################################################################################
###
###     REF:
###     https://ubuntu-mate.community/t/amd-gpu-advanced-but-very-simple-fanspeed-controller-script/28053
###	
###	Credits:
###	@tkn		Original version of this script was posted by @tkn on the UbuntuMATE Community Help Blog
###	@ericmarceau	Adaptation for hybrid CPU with GPU configuration on ASUSTeK M4A78-E motherboard
###
###	Script will run as systemd service in order 
###	to control fan-speed in response to CPU temperature
###
###	The systemd service is set up using the script
###		custom_fanctl_service
###
###	A complementary GUI monitor is available and is started using the script
###		custom_fanctl_monitor
###
####################################################################################################
#23456789+123456789+123456789+123456789+123456789+123456789+123456789+123456789+123456789+123456789+


mode=0
while [ $# -gt 0 ]
do
	case "${1}" in
		"--report" )	mode=0 ; shift ;;
		"--run" )	mode=1 ; shift ;;
		* )
			printf "\n Invalid parameter used.  Only options available: [ --report | --run ] \n Bye!\n\n" ; exit 1
			;;
	esac
done

if [ ${mode} -eq 0 ]
then
	printf "\n Detected '--report' mode.  Trying to identify name of driver controlling fans ...\n\n"

	find /sys/class/hwmon/*/fan* |
	while read line
	do
		echo -ne "$line\t"
		cat "${line%/*}/name"
	done 2>/dev/null

	###
	###	Generate report similar to this:
	###
	# /sys/class/hwmon/hwmon0/fan1_input	atk0110
	# /sys/class/hwmon/hwmon0/fan1_label	atk0110
	# /sys/class/hwmon/hwmon0/fan1_max	atk0110
	# /sys/class/hwmon/hwmon0/fan1_min	atk0110
	#            ***                          ***
	# /sys/class/hwmon/hwmon0/fan3_min	atk0110

	exit 0
fi

###
###	Start of logic for "responsive" fan control
###

## This is where the GPU-unit resides
unit_dir="/sys/class/drm/card0/device/hwmon/hwmon1"

## Set working directory that of the CPU-unit branch under /sys
test -d "${unit_dir}" || { printf "\n Unable to locate '${unit_dir}'.\n Cannot start fan controller!\n\n" ; exit 1 ; }
cd "${unit_dir}" || { printf "\n Unable to set '${unit_dir}' as working directory.\n Cannot start fan controller!\n\n" ; exit 1 ; }


###
###	Verifying existence of expected kernel taps for control interraction
###

for parameter in  pwm1  pwm1_enable  temp1_input
do
	test -f "${parameter}" || { printf "\n Unable to locate required kernel tap parameter '${parameter}' in '${unit_dir}'.\n Abandoning!\n\n" ; exit 1 ; }
done


gpu_fan="pwm1_enable"		## GPU-specific kernel tap for fan-speed control

trap "echo 2 >'${gpu_fan}' ; exit" INT TERM EXIT	## give control back to the GPU-unit whenever we exit:

## polling interval in seconds (default: 10)
scan_interval=10

## BEWARE!! temperature is expressed in °C * 1000 !!
#t_max=80000             ## temperature for maximum fanspeed
t_max=75000             ## temperature for maximum fanspeed
t_fan_on=50000 	        ## temperature where the fan kicks in
t_fan_off=45000	        ## temperature where the fan shuts off
f_min=100		## fansetting for minimum rpm (also prevents stalling)

## don't touch the values below
f_max=255 ; f_off=0   

## create endless loop for scan and react
while :
do
    ## get current temperature
    t_current=$(<temp1_input)

    ## low temperature -> shut fan off
    (( t_current < t_fan_off )) && (( f_set = f_off ))

    ## when in operational range ...
    if  (( t_current > t_fan_on ))
    then
        ## ... calculate fansetting ...
        (( f_set = f_max * ( t_current - t_fan_on ) / ( t_max - t_fan_on ) ))

        ## ... and prevent fan from dropping below minimum rpm
        (( f_set < f_min )) && (( f_set = f_min ))
    fi

    echo 1 >"${gpu_fan}"		## always take control of the GPU first
    
    echo ${f_set} >pwm1			## GPU-specific kernel tap for setting specific fan-speed

    /usr/bin/sleep ${scan_interval}
done


exit
1 Like

That is because you don't have the AMDGPU driver loaded.
(which would not work on your RS790GX anyway)

Yes it is GPU-specific
Sorry if I wasn't clear enough about that :innocent:

It is "AMDGPU" only, that means only for AMD GPUs that are able to use the "AMDGPU" driver. It is not designed to work for anything else.

Therefore, without AMDGPU driver the cited paramaters also will be absent from /sys/class/hwmon.
Therefore, there is not much to suggest as to what you should refer to instead.

According to your list, these are the corresponding parameters for your CPU fan:

fan1_input - - - - - - - - - - -> /sys/class/hwmon/hwmon0/fan1_input
temp1_input - - -> /sys/class/hwmon/hwmon0/temp1_input

An equivalent of pwm1 and pwm1_enable doesn't seem to exist as such in this virtual directory.

You will need at least an equivalent to pwm1 to control the fan so you have to search the /sys/ tree if there is any equivalent for it.
I can not do that search for you because I do not have your hardware.
What is in the /sys/ tree (and where) is completely dependent on your hardware (read: drivers) configuration and mine is definitely completely different from yours.

EDIT & UPDATE:
Since 'pwm1' seems to be missing I have searched for the ATK0110 thermal controller chip and the answers are not very hopeful.
This is what I found on the Arch Linux forum

asus_atk0110 reads the sensors using a proprietary interface provided by the ASUS ACPI firmware. IIRC this interface is read only and that's why (on) asus_atk0110 (you) will never be able to change fan configuration.

Yes, proprietary, closed source and read only, that explains why pwm is missing. Your fans are controlled by your BIOS and the kernel never gets a hold over it.
See this:

I was looking at the asus_atk0110 driver when trying to figure out why fan control is not possible on my ASUS M5A78L-MUSB3 and it comes down to the fact that the IC on the MB which is responsible for hardware monitoring and fan control is taken exclusive control over by the BIOS so the kernel has to communicate with the BIOS over ACPI instead of talking with the chip directly.

So, you are locked out of the possibility to do something with it without some extreme ACPI/BIOS hacking:

3 Likes

Thank you again, @tkn, for your extensive pathfinding and guidance.

I will dabble a bit more, but I think it is a foregone conclusion that, as you stated, it looks like I might be locked out from any direct control of the fan speed for my non-GPU configuration.

I will just have to go with the flow on that! :frowning:

1 Like