HOWTO undervolt the AMD RX 4XX and RX 5XX GPUs

From LinuxReviews
Jump to navigationJump to search

You may or may not to manually adjust the clockspeeds on AMD GPUs and you can do it fairly easily if you are using the amdgpu driver on GNU/Linux.

Kernel parameters required

You need to add amdgpu.ppfeaturemask=0xfffd7fff to your kernel command-line to enable "overdrive" so you can manually fool around with GPU clocks. It is also possible to use amdgpu.ppfeaturemask=0xffffffff but that higher setting causes artifacts on many cards (mainly RX 470/570). Booting with this will make a special file called /sys/class/drm/card0/device/pp_od_clk_voltage appear. You can write values to this file and activate them.

HOWTO do it yourself - the manual labor way

Changing the clocks and voltages for individual GPU states

  • s maxpstate GPU-clock voltate will change the GPU clock for a specific p-state.
  • m max-memory-pstate memory-clock memory-voltate to change the memory clock for a p-state.

There are eight (0-7) GPU clock states and two (0-1) OR three (0-2) memory states on AMD RX GPUs.

This example changes the values for the highest CPU core and memory states:

Shell command:
echo "s 7 1209 900" > /sys/class/drm/card0/device/pp_od_clk_voltage
echo "m 2 1850 850" > /sys/class/drm/card0/device/pp_od_clk_voltage

The first line would set a maximum pstate of 7, set MHz to 1209 and a 900mV voltage. The second line would set the maximum memory pstate to 2, memory clock to 1850 and voltage to 850.

You can activate the settings written by writing c to pp_od_clk_voltage:

Shell command:
echo "c" > /sys/class/drm/card0/device/pp_od_clk_voltage

You can re-set to default settings by writing r:

Shell command:
echo "r" > /sys/class/drm/card0/device/pp_od_clk_voltage

You can force your card to only use selected GPU pstates by sending whole numbers to pp_dpm_sclk:

Shell command:
echo "5 6 7" > /sys/class/drm/card0/device/pp_dpm_sclk

You can look at your /sys/class/drm/card0/device/pp_od_clk_voltage with cat and see what you have available:

Shell command:
cat /sys/class/drm/card0/device/pp_od_clk_voltage

This will give you an output like this:

OD_SCLK:
0:        300MHz        800mV
1:        466MHz        818mV
2:        751MHz        824mV
3:       1019MHz        950mV
4:       1074MHz       1000mV
5:       1126MHz       1050mV
6:       1169MHz       1093mV
7:       1242MHz       1150mV
OD_MCLK:
0:        300MHz        800mV
1:       1650MHz       1000mV
OD_RANGE:
SCLK:     300MHz       2000MHz
MCLK:     300MHz       2100MHz
VDDC:     800mV        1175mV

Putting it all together

Here is an example of a complete underclock of a RX 580 done with a script file:

File: /usr/local/bin/Set_GPU_Settings.sh
echo "manual" > /sys/class/drm/card0/device/power_dpm_force_performance_level
echo 125000000 > /sys/class/hwmon/hwmon3/power1_cap
echo "s 0 300 750" > /sys/class/drm/card0/device/pp_od_clk_voltage
echo "s 1 600 769" > /sys/class/drm/card0/device/pp_od_clk_voltage
echo "s 2 918 912" > /sys/class/drm/card0/device/pp_od_clk_voltage
echo "s 3 1167 1075" > /sys/class/drm/card0/device/pp_od_clk_voltage
echo "s 4 1239 1075" > /sys/class/drm/card0/device/pp_od_clk_voltage
echo "s 5 1282 1075" > /sys/class/drm/card0/device/pp_od_clk_voltage
echo "s 6 1326 1075" > /sys/class/drm/card0/device/pp_od_clk_voltage
echo "s 7 1366 1075" > /sys/class/drm/card0/device/pp_od_clk_voltage
echo "m 0 300 750" > /sys/class/drm/card0/device/pp_od_clk_voltage
echo "m 1 1000 800" > /sys/class/drm/card0/device/pp_od_clk_voltage
echo "m 2 2000 875" > /sys/class/drm/card0/device/pp_od_clk_voltage
echo "c" > /sys/class/drm/card0/device/pp_od_clk_voltage

And here's an example for the RX 570:

File: /usr/local/bin/Set_GPU_Settings.sh
echo "manual" > /sys/class/drm/card0/device/power_dpm_force_performance_level
echo "s 3 1100 940" > /sys/class/drm/card0/device/pp_od_clk_voltage
echo "s 4 1150 960" > /sys/class/drm/card0/device/pp_od_clk_voltage
echo "s 5 1200 980" > /sys/class/drm/card0/device/pp_od_clk_voltage
echo "s 6 1250 1000" > /sys/class/drm/card0/device/pp_od_clk_voltage
echo "s 7 1300 1020" > /sys/class/drm/card0/device/pp_od_clk_voltage
echo "m 2 1800 850" > /sys/class/drm/card0/device/pp_od_clk_voltage
echo "c" > /sys/class/drm/card0/device/pp_od_clk_voltage

A script like that, once created, would be activated on each boot by making a simple systemd service script:

File: /etc/systemd/system/tunegpu.service
[Unit]
Description=Change GPU clocks
[Service]
Type=oneshot
ExecStart=/usr/local/bin/Set_GPU_Settings.sh
RemainAfterExit=yes

[Install]
WantedBy=multi-user.target

Run systemctl daemon-reload then systemctl enable tunegpu.service to activate.