shtools/zgoc/zgoc
2020-04-18 16:51:22 +02:00

308 lines
7.5 KiB
Bash
Executable file

#!/bin/sh
#
# This script does not support multiple GPUs
# Only radeon GPUs supported
#
config_path="/etc/zgoc"
config_file="$config_path/zgoc.conf"
# generate default conf file
if [ ! -f "$config_file" ]
then
mkdir -p "$config_path" 2>/dev/null && touch "$config_file" 2>/dev/null &&\
echo "# zgoc config file
GPU_PROFILE_PATH=profiles/gpu
GPU_MEM_PATH=profiles/mem
GPU_POWER_PATH=profiles/power
GPU_NAME=
GPU_PCI_ID=
GPU_AUTOMEMFIX=true
GPU_MIN_POWER=30
FANC_PROFILE_PATH=profiles/fan
FANC_DEFAULT_PROFILE=default
FANC_TIME_INTERVAL=2
FANC_SMOOTH=true" > "$config_file" 2>/dev/null
fi
# load config file
[ -f "$config_file" ] && . "$config_file"
# default path values
[ -z "$GPU_PROFILE_PATH" ] && GPU_PROFILE_PATH=profiles/gpu
[ -z "$GPU_MEM_PATH" ] && GPU_MEM_PATH=profiles/mem
[ -z "$GPU_POWER_PATH" ] && GPU_POWER_PATH=profiles/power
# relative path resolution
[ $(echo "$GPU_PROFILE_PATH" | cut -c1) != '/' ] && GPU_PROFILE_PATH="$config_path/$GPU_PROFILE_PATH"
[ $(echo "$GPU_MEM_PATH" | cut -c1) != '/' ] && GPU_MEM_PATH="$config_path/$GPU_MEM_PATH"
[ $(echo "$GPU_POWER_PATH" | cut -c1) != '/' ] && GPU_POWER_PATH="$config_path/$GPU_POWER_PATH"
if [ "$GPU_AUTOMEMFIX" != "true" ]
then
GPU_AUTOMEMFIX=""
fi
usage() {
echo "Usage: zgoc <arguments>"
echo "Arguments:"
echo " print show current state"
echo " gpu GPU overclocking"
echo " mem memory overclocking"
echo " apply apply GPU and memory profiles"
echo " reset reset GPU and memory to stock values"
echo " memfix fix screen artifacting caused by low memory state"
echo " power power cap"
echo ""
echo "Config file: $config_file"
}
error () {
printf "\033[1;31m%s\033[0m\n" "$1" >&2
}
root_check () {
if [ "$(id | cut -d'=' -f2 | cut -d'(' -f1)" -ne 0 ]
then
echo "Root privileges required" > /dev/stderr
exit 10
fi
}
_stop() {
echo ""
exit
}
trap '_stop' INT
# val_convert <reduction factor n> <precision>
# convert an integer to a lower decimal
# output = input*10^-n
# reads from input
val_convert () {
read -r in
size=${#in}
n2=$((size - $1))
integer=$(echo "$in" | cut -c-$n2)
if [ "$2" != 0 ] ; then
decimal=$(echo "$in" | cut -c$((n2+1))-"$size" | cut -c1-"$2")
fi
if [ -z "$integer" ] ; then
integer=0
fi
printf "%s" "$integer"
if [ -n "$decimal" ] ; then
printf ".%s" "$decimal"
fi
echo ""
}
kernel_req ()
{
MAIN_KERNELVER=$(uname -r | cut -d'.' -f1) # Get kernel version.
SEC_KERNELVER=$(uname -r | cut -d'.' -f2) # Get kernel version.
if [ "$MAIN_KERNELVER" -le "$1" ]; then
if [ "$SEC_KERNELVER" -lt "$2" ]; then
error "Kernel $1.$2 or higher is required for this feature"
exit 13
fi
fi
}
# kernel version check
kernel_req 4 15
file_report () {
if [ -f "$(pwd)/$1" ]
then
printf "%s = " "$(pwd)/$1"
cat "$(pwd)/$1"
elif [ -f "$1" ]
then
printf "%s = " "$1"
cat "$1"
else
error "File $1 from $(pwd) unaccessible"
fi
}
file_write () {
if ! echo "$1" > "$2" ;
then
error "Error writing to $2 from $(pwd)"
exit 14
fi
file_report "$2"
}
high_mem_state () {
if [ -z "$HIGH_MEM_STATE" ]
then
_t=$(grep OD_MCLK "$path/device/pp_od_clk_voltage" -A6)
I=0
while echo "$_t" | grep -q "$I:" ; do
I=$((I + 1))
done
HIGH_MEM_STATE=$((I - 1))
fi
echo $HIGH_MEM_STATE
}
set_core_state () {
root_check
echo "s$1: $2 MHz $3 mV"
echo "s$1 $2 $3" > "$path/device/pp_od_clk_voltage"
}
set_mem_state () {
root_check
echo "m$1: $2 MHz $3 mV"
echo "m $1 $2 $3" > "$path/device/pp_od_clk_voltage"
}
force_mem_state_high () {
root_check
echo "manual" > "$path/device/power_dpm_force_performance_level"
high_mem_state > "$path/device/pp_dpm_mclk"
echo "Forcing memory state to $(high_mem_state) (fix artifacting)"
}
apply () {
root_check
echo "c" > "$path/device/pp_od_clk_voltage"
echo "Profile applied"
if [ -n "$GPU_AUTOMEMFIX" ] ; then
force_mem_state_high
fi
}
reset_stock () {
root_check
if echo "0 1 2 3 4 5 6 7" > "$path/device/pp_dpm_sclk" && echo "r" > "$path/device/pp_od_clk_voltage" ; then
echo "Reset to default"
fi
}
apply_power () {
root_check
if ! echo "$1" | grep -Eq '^[0-9]+$' ; then
echo "Invalid power value" >&2
exit 2
elif [ "$1" -lt "$GPU_MIN_POWER" ] ; then
echo "Power value too low" >&2
exit 2
elif [ -z "$1" ] ; then
echo "No power value given" >&2
exit 2
else
if echo "$1""000000" > $hwmon/power1_cap ; then
echo "Power cap set to $1 W"
fi
fi
}
set_profile () {
read -r _states
read -r _frequencies
read -r _voltages
I=1
for T in $_states
do
if [ "$1" = "gpu" ] ; then
set_core_state "$(echo "$_states" | awk -v I=$I '{print $I}')" "$(echo "$_frequencies" | awk -v I=$I '{print $I}')" "$(echo "$_voltages" | awk -v I=$I '{print $I}')"
elif [ "$1" = "mem" ] ; then
set_mem_state "$(echo "$_states" | awk -v I=$I '{print $I}')" "$(echo "$_frequencies" | awk -v I=$I '{print $I}')" "$(echo "$_voltages" | awk -v I=$I '{print $I}')"
fi
I=$((I + 1))
done
}
## locate device path
if [ "$(lspci | grep -c VGA)" -gt 1 ]
then
if [ -z "$GPU_NAME" ] && [ -z "$GPU_PCI_ID" ] ; then
error "Several GPUs are present and no GPU is specified"
error "Edit GPU_NAME or GPU_PCI_ID in '$config_file'"
exit 20
fi
if [ -z "$GPU_PCI_ID" ] ; then
GPU_PCI_ID=$(lspci | grep VGA | grep "$GPU_NAME" | cut -d ' ' -f1)
if [ "$(echo "$GPU_PCI_ID" | wc -l)" -gt 1 ] ; then
error "More than one name match for GPU"
exit 21
elif [ "$(echo "$GPU_PCI_ID" | wc -l)" -lt 1 ] ; then
error "No name match for GPU"
exit 22
fi
fi
if ! path=$(ls -l /sys/class/drm/card? | grep "$GPU_PCI_ID" | tr -s ' ' | cut -d ' ' -f9) ; then
error "Error finding pci device"
echo 23
fi
else
path=/sys/class/drm/card0
fi
hwmon=$path/device/hwmon/$(ls $path/device/hwmon)
if [ "$( printf "0x%08x\n" "$(cat /sys/module/amdgpu/parameters/ppfeaturemask)" )" != "0xffffffff" ] ; then
error "PP feature not enabled"
echo "Append 'amdgpu.ppfeaturemask=0xffffffff' to boot parameters to enable it" >&2
exit 11
fi
if [ ! -f "$path/device/pp_od_clk_voltage" ]
then
error "FATAL: overclocking file not found"
error "Either feature is not properly enabled or this isn't a Radeon GPU"
exit 12
fi
# read arguments
shift $((OPTIND-1))
case "$1" in
reset) reset_stock ;;
apply) apply ;;
memfix) force_mem_state_high ;;
print) cat $path/device/pp_od_clk_voltage ;;
gpu)
case "$2" in
profile)
if [ -f "$GPU_PROFILE_PATH/$3" ]
then set_profile gpu < "$GPU_PROFILE_PATH/$3"
else cd "$GPU_PROFILE_PATH" 2>/dev/null && ls -p | grep -v /
fi ;;
file) set_profile gpu < "$3" && apply ;;
*) echo "zgoc gpu <profile/file>" && exit 1 ;;
esac
;;
mem)
case "$2" in
profile)
if [ -f "$GPU_MEM_PATH/$3" ]
then set_profile mem < "$GPU_MEM_PATH/$3"
else cd "$GPU_MEM_PATH" && ls -p | grep -v /
fi ;;
file) set_profile mem < "$3" && apply ;;
*) echo "zgoc mem <profile/file>" && exit 1
esac
;;
power)
kernel_req 4 20
case "$2" in
profile)
if [ -f "$GPU_POWER_PATH/$3" ]
then apply_power "$(cat "$GPU_POWER_PATH/$3")"
else cd "$GPU_POWER_PATH" 2>/dev/null && ls -p | grep -v /
fi ;;
max) apply_power "$(val_convert 6 0 < $hwmon/power1_cap_max)" ;;
set) apply_power "$3" ;;
print) echo $(val_convert 6 0 < $hwmon/power1_cap) W ;;
*) echo "zgoc power <max/profile/set/print>" && exit 1
esac
;;
*) usage && exit 1;;
esac