I’ve always been a fan of tinkering with cooling setups on my computers. I’ve even went as far as writing crappy solutions to make up for deficiencies on the hardware level. After years of dumb experiments I’ve seen how little you can get away with in cooling and how to run your machines as quietly as possible without giving up too much performance.

I also appreciate simplicity, which is why I’m currently running the simplest damn solution you can imagine to control how my ThinkPad T430 and ASRock DeskMini X300 run: a shell script.

It also comes with some added benefits:

  • self-documenting
  • highly customizable
  • low resource usage

These scripts are not “battle-hardened” and using them incorrectly may or may not result in hardware failure. Use at your own risk. The only guarantee I can give is that it works on my machine™.

If you’re someone who doesn’t like tinkering with their computers and wants their machines to “just work”, then this article likely isn’t for you.

image

ThinkPad T430

With the ThinkPad T430, the script does two things:

  • control the fan speed
  • preemptively throttle the CPU and GPU

This type of behaviour is optimized for mostly quiet operation. The fan is running at its lowest speed when under a small load, and the CPU/GPU throttling has an added benefit of reducing the overall power usage, too. The difference between non-turbo and turbo speeds on the Intel i7-3820QM can be as much as 13-15 watts.

To enable fan control on ThinkPads, you need to have a file at /usr/lib/modprobe.d/thinkpad_acpi.conf with the following contents:

options thinkpad_acpi fan_control=1

Reboot, and you can now have full control over the fan speed in your ThinkPad!

The script itself looks like this:

#!/bin/bash

set -e

while true; do

  temp=$(cat /sys/class/thermal/thermal_zone1/temp)

  if ((temp > 90000)); then
    echo level 7 >/proc/acpi/ibm/fan
  elif ((temp > 75000)); then
    echo level 5 >/proc/acpi/ibm/fan
  elif ((temp > 60000)); then
    echo level 3 >/proc/acpi/ibm/fan
  elif ((temp > 30000)); then
    echo level 1 >/proc/acpi/ibm/fan
  else
    echo level 0 >/proc/acpi/ibm/fan
  fi

  if ((temp > 97000)); then
    echo 350 >/sys/class/drm/card*/gt_max_freq_mhz
    echo 350 >/sys/class/drm/card*/gt_boost_freq_mhz
  elif ((temp > 90000)); then
    echo 650 >/sys/class/drm/card*/gt_max_freq_mhz
    echo 650 >/sys/class/drm/card*/gt_boost_freq_mhz
  else
    echo 1250 >/sys/class/drm/card*/gt_max_freq_mhz
    echo 1250 >/sys/class/drm/card*/gt_boost_freq_mhz
  fi

  if ((temp > 70000)); then
    echo 1 >/sys/devices/system/cpu/intel_pstate/no_turbo
  else
    echo 0 >/sys/devices/system/cpu/intel_pstate/no_turbo
  fi

  sleep 0.5
done

What we’re doing is reading the temperature of the CPU package and then setting the fan speed and throttling based on that. Note that the temperatures are represented without any decimal places. A reading of 78000 means that the chip is running at 78 °C.

On my ThinkPad T430, the fan speed levels can be roughly described as such:

  • level 1: lowest speed, barely audible
  • level 3: audible, but tolerable
  • level 5 and above: it’s probably loud enough to bother you

The second section controls the integrated GPU. What I’ve found in my testing is that a weak integrated GPU can still chug up to 20 watts, which is a considerable amount, especially on a laptop. The card* wildcard is there because the integrated GPU may change between card0 and card1, and I’m too lazy to make the script any smarter.

The values written there correspond to the maximum GPU clock speed. 1250 happens to be the max clock speed (in MHz) and at that speed the GPU is using a lot of power. 650 is the speed at which the integrated GPU is running most efficiently. 350 is the lowest speed, and you will notice it due to the performance being extra crappy.

With the CPU, I chose to just turn the turbo boost on or off. With turbo boost off, the CPU will top out at 2.7 GHz and around 22 watts of power usage. With turbo boost, the CPU can run anywhere between 3.4-3.7 GHz, depending on the number of cores under load. You’ll likely notice much higher CPU temperatures and power usage climbing to around 35 watts. Fast at short and bursty workloads, and yet efficient and relatively cool at sustained loads.

And at the very end, you have a simple sleep statement. I’ve set it to half a second, but feel free to set it as you see fit. Higher intervals might not be that good of an idea because the script may not be able to respond quickly enough to changing thermals, just keep that in mind.

ASRock DeskMini X300

Although I’ve opted for a more experimental setup with my home server and the ASRock DeskMini X300 is no longer running as one, I think it’s still worthwhile to share how I configured the fan control logic on this machine.

With the ASRock DeskMini X300, my goal was simple: let it run as quietly as reasonably possible without losing too much performance.

This machine was running 24/7 as a server and has a powerful 8-core AMD Ryzen 7 5700G CPU in it. One thing this script is heavily relying on is the fact that Ryzen CPU-s have well-engineered boosting logic in them, meaning that they’re already designed to run at the highest speed possible and only limit their speed if they hit power or thermal limits.

One thing you may need to do first is installing lm-sensors and running sudo sensors-detect. By default the CPU fan speed controls were not properly exposed, but after running sensors-detect and saying yes at every prompt, they were picked up.

The script looks like this:

#!/bin/bash

set -e

# /sys/devices/platform/nct6775.656/hwmon/hwmon3

echo 1 >/sys/devices/platform/nct6775.656/hwmon/hwmon3/pwm2_enable

while true; do

  temp=$(cat /sys/class/hwmon/hwmon4/temp1_input)

  if ((temp > 94000)); then
    echo 170 >/sys/devices/platform/nct6775.656/hwmon/hwmon3/pwm2
  elif ((temp > 90000)); then
    echo 140 >/sys/devices/platform/nct6775.656/hwmon/hwmon3/pwm2
  elif ((temp > 70000)); then
    echo 120 >/sys/devices/platform/nct6775.656/hwmon/hwmon3/pwm2
  elif ((temp > 40000)); then
    echo 30 >/sys/devices/platform/nct6775.656/hwmon/hwmon3/pwm2
  else
    echo 0 >/sys/devices/platform/nct6775.656/hwmon/hwmon3/pwm2
  fi

  sleep 5
done

The hwmon values and which fan you’re controlling is likely different on your own PC. Take a look at the paths found in the script and poke around while also physically looking at your PC. Try enabling PWM control by writing to pwm*_enable handles and then setting the fan speed. As a result of your poking, you should see the CPU fan change its speed. If it doesn’t, try another value.

The values you can write are in the range 0-255, where the higher value corresponds to a higher speed.

I’ve limited the TDP of my CPU to 35 watts using a setting found in UEFI settings. This results in the CPU running cool and quiet even under a full load.

systemd service

To run your fan control script as a systemd service, drop the script to your preferred location (I placed it in /root/.local/bin/fancontrol), then create a file /etc/systemd/system/fancontrol.service with the following contents:

[Unit]
Description=Quick fan control software

[Service]
ExecStart=/root/.local/bin/fancontrol

[Install]
WantedBy=multi-user.target

And once that is done, enable and start your fan control service with sudo systemctl enable --now fancontrol.service.

If you encounter issues or something isn’t working right, check the status of the service with systemctl status fancontrol.service or full logs with journalctl -u fancontrol.service.

Conclusion

If you want something that works out of the box and does the smart stuff for you, then give this Arch Linux wiki page a look. If you’re not into that and want to easily tune the behaviour of your machine, then feel free to use these scripts as a baseline for your own experimentation.