A workaround I found for now is to use nvidia-smi to set max clocks for core and memory. First, get the supported clocks with:
nvidia-smi -q -d SUPPORTED_CLOCKS
Before changing the clocks, you need to enable persistence mode on the GPU. I’m not sure why, but apparently the driver can forget the clock settings after a while:
sudo nvidia-smi -pm ENABLED
Note that for memory, you use the actual clock, not the effective one. For example if nvidia-settings showed 1620MHz memory clock before, nvidia-smi reports this as 810. So here I want to limit the maximum clocks to 405 core and 1620 memory, so I use:
sudo nvidia-smi -i 0 -ac 810,405
-i parameter is the GPU you want to change, numbered from 0.
You can only use clock values reported by nvidia-smi. You can’t use arbitrary values.
This needs to be done on each boot. Alternatively, you can set up the nvidia-persistenced service in systemd. I haven’t done that, but I believe this will remember GPU configuration between reboots and restore the values automatically.
Unfortunately, the clocks don’t seem to be limited exactly to the values I gave. With the previous method, core clock would only max out at 405MHz. Now, it goes up to 670Mhz. But it’s still better than it going all the way up into the GHz range.
Running games in Wine or Proton will ignore this. I don’t know why. Even if I run a lightweight or old game where low clocks are perfectly fine, the GPU will jump to max clocks anyway. So this is not a real workaround. It’s a “better than nothing” solution.