|
@@ -0,0 +1,318 @@
|
|
|
+#!/usr/bin/env bash
|
|
|
+#
|
|
|
+# Basic idea stolen from here
|
|
|
+# https://www.phoronix.com/forums/forum/linux-graphics-x-org-drivers/amd-linux/918649-underclocking-undervolting-the-rx-470-with-amdgpu-pro-success
|
|
|
+#
|
|
|
+# After this I came to my own downclock idea.
|
|
|
+# It working with a little mess in pp_dpm_sclk output,
|
|
|
+# but unfortunately it seems that compiling downclock ratio
|
|
|
+# gives much better power saving.
|
|
|
+#
|
|
|
+# This tool modify amd gpu kernel module.
|
|
|
+# It allows you to undervolt and underclock your AMD RXxx GPUs under linux with amdgpu latest driver.
|
|
|
+#
|
|
|
+# COMPATIBILE with 17.20.* 17.30.* and 17.40.* amdgpu drivers
|
|
|
+# 17.40.* drivers require HWE kernel for Ubuntu 16.04
|
|
|
+#
|
|
|
+# BE CAREFULL!
|
|
|
+# I DO NOT FULLY UDERSTAND HOW DOES IT WORK AND WFT I'M DOING
|
|
|
+
|
|
|
+
|
|
|
+# colors
|
|
|
+CRED='\033[0;31m'; CYELL='\033[1;33m'; CGREE='\033[0;32m'; CBLUE='\033[0;34m'; NC='\033[0m';
|
|
|
+
|
|
|
+function logo {
|
|
|
+echo -e "${CBLUE}
|
|
|
+____________________________________________________
|
|
|
+| _ __ _ _ _ |
|
|
|
+| /\ |\/| | \ /__ |_) | | |\/| / \ | \ |
|
|
|
+| /--\ | | |_/ \_| | |_| | | \_/ |_/ |
|
|
|
+|__________________________________________________|${NC}
|
|
|
+"
|
|
|
+}
|
|
|
+
|
|
|
+function info { echo -e "${CGREE}${1}${NC}"; }
|
|
|
+function warn { echo -e "${CYELL}${1}${NC}"; }
|
|
|
+function error { echo -e "${CRED}${1}${NC}"; }
|
|
|
+
|
|
|
+FILE_DAG="/amd/amdgpu/amdgpu_vm.c"
|
|
|
+FILE_P10="/amd/powerplay/smumgr/polaris10_smc.c"
|
|
|
+FILE_HW_SMU7="/amd/powerplay/hwmgr/smu7_hwmgr.c"
|
|
|
+FILE_HW_VEGA="/amd/powerplay/hwmgr/vega10_hwmgr.c"
|
|
|
+
|
|
|
+function backup_src_file {
|
|
|
+ if [ ! -f "$1" ]; then error "Source not found $1"; exit 1; fi
|
|
|
+ if [ -f "$2" ]; then
|
|
|
+ info "SKIP bakup, file aready exist $2";
|
|
|
+ else
|
|
|
+ mkdir -p "$(dirname "$2")";
|
|
|
+ cp -f "$1" "$2";
|
|
|
+ if [ $? -ne 0 ]; then error "Can not backup $1 -> $2"; exit 1; fi
|
|
|
+ fi
|
|
|
+}
|
|
|
+
|
|
|
+function backup_src {
|
|
|
+ backup_src_file "${1}$FILE_DAG" "${2}$FILE_DAG"
|
|
|
+ backup_src_file "${1}$FILE_P10" "${2}$FILE_P10"
|
|
|
+ backup_src_file "${1}$FILE_HW_SMU7" "${2}$FILE_HW_SMU7"
|
|
|
+ backup_src_file "${1}$FILE_HW_VEGA" "${2}$FILE_HW_VEGA"
|
|
|
+}
|
|
|
+
|
|
|
+# $1-backup file
|
|
|
+# $2-amd source loc
|
|
|
+# $3-patch
|
|
|
+function patch_restore_file {
|
|
|
+ cp -f "$1" "$2"
|
|
|
+ if [ $RESTORE -eq 0 ]; then
|
|
|
+ info "Patch file $2"
|
|
|
+ echo "$3" | patch "$2"
|
|
|
+ if [ $? -ne 0 ]; then error "Patching failed with error code: $?"; echo "$3"; exit 1; fi
|
|
|
+ info "File patched: $2"
|
|
|
+ else
|
|
|
+ info "Source restored $2"
|
|
|
+ fi
|
|
|
+}
|
|
|
+
|
|
|
+PATCH_SMU7=$(cat <<EOF
|
|
|
+--- .smu7_hwmgr.c.orig 2017-08-04 12:59:00.000000000 +0300
|
|
|
++++ .smu7_hwmgr.c 2017-09-12 22:59:37.650864941 +0300
|
|
|
+@@ -4379,23 +4379,50 @@
|
|
|
+ struct smu7_hwmgr *data = (struct smu7_hwmgr *)(hwmgr->backend);
|
|
|
+ struct smu7_single_dpm_table *golden_sclk_table =
|
|
|
+ &(data->golden_dpm_table.sclk_table);
|
|
|
++ struct smu7_single_dpm_table *sclk_table =
|
|
|
++ &(data->dpm_table.sclk_table);
|
|
|
+ struct pp_power_state *ps;
|
|
|
+ struct smu7_power_state *smu7_ps;
|
|
|
+
|
|
|
+- if (value > 20)
|
|
|
+- value = 20;
|
|
|
+-
|
|
|
+ ps = hwmgr->request_ps;
|
|
|
+
|
|
|
+ if (ps == NULL)
|
|
|
+ return -EINVAL;
|
|
|
+
|
|
|
+ smu7_ps = cast_phw_smu7_power_state(&ps->hardware);
|
|
|
+-
|
|
|
+- smu7_ps->performance_levels[smu7_ps->performance_level_count - 1].engine_clock =
|
|
|
+- golden_sclk_table->dpm_levels[golden_sclk_table->count - 1].value *
|
|
|
+- value / 100 +
|
|
|
+- golden_sclk_table->dpm_levels[golden_sclk_table->count - 1].value;
|
|
|
++
|
|
|
++ bool up = true;
|
|
|
++ if (value >= 50) {
|
|
|
++ if (value > 99) value = 99;
|
|
|
++ value = 100 - value;
|
|
|
++ up = false;
|
|
|
++ } else if (value > 20)
|
|
|
++ value = 20;
|
|
|
++
|
|
|
++ int i;
|
|
|
++ for (i = 1; i <= smu7_ps->performance_level_count; i++) {
|
|
|
++ uint32_t clock = golden_sclk_table->dpm_levels[golden_sclk_table->count - i].value;
|
|
|
++ if (up)
|
|
|
++ clock += golden_sclk_table->dpm_levels[golden_sclk_table->count - i].value * value / 100;
|
|
|
++ else
|
|
|
++ clock -= golden_sclk_table->dpm_levels[golden_sclk_table->count - i].value * value / 100;
|
|
|
++ smu7_ps->performance_levels[smu7_ps->performance_level_count - i].engine_clock = clock;
|
|
|
++ }
|
|
|
++
|
|
|
++ // Only downclock or reset to normal (looks like it does not work properly)
|
|
|
++ if (value <= 20)
|
|
|
++ value = 0;
|
|
|
++
|
|
|
++ uint32_t min_clock = sclk_table->dpm_levels[0].value;
|
|
|
++ for (i = 1; i < sclk_table->count; i++) {
|
|
|
++ uint32_t clock = golden_sclk_table->dpm_levels[i].value;
|
|
|
++ clock -= golden_sclk_table->dpm_levels[i].value * value / 100;
|
|
|
++
|
|
|
++ if (clock < min_clock)
|
|
|
++ sclk_table->dpm_levels[i].value = min_clock;
|
|
|
++ else
|
|
|
++ sclk_table->dpm_levels[i].value = clock;
|
|
|
++ }
|
|
|
+
|
|
|
+ return 0;
|
|
|
+ }
|
|
|
+EOF
|
|
|
+)
|
|
|
+
|
|
|
+PATCH_VEGA=$(cat <<EOF
|
|
|
+
|
|
|
+EOF
|
|
|
+)
|
|
|
+
|
|
|
+function produce_patch {
|
|
|
+TPL=$(cat <<EOF
|
|
|
+--- ./polaris10_smc.c.orig 2017-04-27 12:00:59.492580016 +0300
|
|
|
++++ polaris10_smc.c 2017-04-27 15:27:55.451783666 +0300
|
|
|
+@@ -112,10 +112,13 @@
|
|
|
+ else if (dep_table->entries[i].mvdd)
|
|
|
+ *mvdd = (uint32_t) dep_table->entries[i].mvdd *
|
|
|
+ VOLTAGE_SCALE;
|
|
|
+
|
|
|
+ *voltage |= 1 << PHASES_SHIFT;
|
|
|
++ //MOD UNDERVOLT
|
|
|
++ //UVT_V*voltage = (*voltage & 0xFFFF0000) + (({uvolt}*VOLTAGE_SCALE) & 0xFFFF);
|
|
|
++ //END UNDRVOLT
|
|
|
+ return 0;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /* sclk is bigger than max sclk in the dependence table */
|
|
|
+@@ -134,10 +139,13 @@
|
|
|
+ if (SMU7_VOLTAGE_CONTROL_NONE == data->mvdd_control)
|
|
|
+ *mvdd = data->vbios_boot_state.mvdd_bootup_value * VOLTAGE_SCALE;
|
|
|
+ else if (dep_table->entries[i].mvdd)
|
|
|
+ *mvdd = (uint32_t) dep_table->entries[i - 1].mvdd * VOLTAGE_SCALE;
|
|
|
+
|
|
|
++ //MOD UNDERVOLT
|
|
|
++ //UVT_V*voltage = (*voltage & 0xFFFF0000) + (({uvolt}*VOLTAGE_SCALE) & 0xFFFF);
|
|
|
++ //END UNDRVOLT
|
|
|
+ return 0;
|
|
|
+ }
|
|
|
+
|
|
|
+ static uint16_t scale_fan_gain_settings(uint16_t raw_setting)
|
|
|
+ {
|
|
|
+@@ -770,10 +780,14 @@
|
|
|
+
|
|
|
+ polaris10_get_sclk_range_table(hwmgr, &(smu_data->smc_state_table));
|
|
|
+
|
|
|
+ for (i = 0; i < dpm_table->sclk_table.count; i++) {
|
|
|
+
|
|
|
++ //MOD UNDERCLOCK
|
|
|
++ int clk = dpm_table->sclk_table.dpm_levels[i].value;
|
|
|
++ dpm_table->sclk_table.dpm_levels[i].value -= (clk * {uclock}) / 100;
|
|
|
++ //END UNDERCLOCK
|
|
|
+ result = polaris10_populate_single_graphic_level(hwmgr,
|
|
|
+ dpm_table->sclk_table.dpm_levels[i].value,
|
|
|
+ (uint16_t)smu_data->activity_target[i],
|
|
|
+ &(smu_data->smc_state_table.GraphicsLevel[i]));
|
|
|
+ if (result)
|
|
|
+EOF
|
|
|
+)
|
|
|
+ PATCH=${TPL//"{uvolt}"/$1}
|
|
|
+ PATCH=${PATCH//"//UVT_V"/""}
|
|
|
+ echo "${PATCH//"{uclock}"/$2}"
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+function show_help {
|
|
|
+ echo -e "\nUSAGE:"
|
|
|
+ echo -e "Patch: $0 -d /usr/src/amdgpu-pro-YOURVERSION -v 800 -c 13 # voltage at 818mV underclock 13%"
|
|
|
+ echo -e "Restore: $0 -d /usr/src/amdgpu-pro-YOURVERSION -r"
|
|
|
+ echo -e "\nARGUMENTS:"
|
|
|
+ echo " -d DIRECTORY : Path to directory with your amdgpu driver files"
|
|
|
+ echo " -v VOLTAGE : Base voltage in mV as int"
|
|
|
+ echo " -c PERCENT : Underclock value in percents"
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+# START PROGRAM
|
|
|
+HELP=1
|
|
|
+LOGO=1
|
|
|
+RESTORE=0
|
|
|
+AMDGPUDIR=""
|
|
|
+UVOLT=0
|
|
|
+UCLOCK=0
|
|
|
+
|
|
|
+while getopts "h?rd:v:c:" opt; do
|
|
|
+ case "$opt" in
|
|
|
+ h|\?) logo; show_help; exit 0 ;;
|
|
|
+ d) AMDGPUDIR=$OPTARG; HELP=0 ;;
|
|
|
+ r) RESTORE=1; HELP=0 ;;
|
|
|
+ v) UVOLT=$((${OPTARG//[!0-9]/})) ;;
|
|
|
+ c) UCLOCK=$((${OPTARG//[!0-9]/})) ;;
|
|
|
+ esac
|
|
|
+done
|
|
|
+
|
|
|
+
|
|
|
+SNAME=$(basename "$0")
|
|
|
+APPDIR="$(dirname "$0")/.${SNAME%.*}"
|
|
|
+KERNEL=`uname -r`
|
|
|
+
|
|
|
+if [ $LOGO -eq 1 ]; then logo; fi
|
|
|
+
|
|
|
+# VALIDATE INPUT
|
|
|
+if [ ! -d "$AMDGPUDIR" ]; then
|
|
|
+ error "Provided AMD GPU dir does not exist: ${AMDGPUDIR}"
|
|
|
+ show_help; exit 1;
|
|
|
+else
|
|
|
+ SRCDIR="${APPDIR}/$(basename "$AMDGPUDIR")"
|
|
|
+fi
|
|
|
+
|
|
|
+if [ $RESTORE -eq 0 ]; then
|
|
|
+ if [ $UVOLT -gt 1200 ]; then warn "Undervolt value ${UVOLT}mV does not look like undervolt at all!"; fi
|
|
|
+ if [ $UVOLT -lt 800 ]; then warn "Are you shure that it whould work with voltage near ${UVOLT}mV ?"; fi
|
|
|
+ if [ $UVOLT -lt 700 ]; then error "Definitely wrong undervolt value ${UVOLT}mV"; HELP=1; fi
|
|
|
+ if [ $UCLOCK -lt 0 ] || [ $UCLOCK -gt 50 ]; then error "Can not allow you set underclock to ${UCLOCK}% !"; HELP=1; fi
|
|
|
+
|
|
|
+ info "Undervolt value ${UVOLT}mV underclock is ${UCLOCK}%"
|
|
|
+else
|
|
|
+ info "Restore configuration requested"
|
|
|
+fi
|
|
|
+
|
|
|
+#SHOW HELP on FUCKUP
|
|
|
+if [ $HELP -eq 1 ]; then show_help; exit 0; fi
|
|
|
+
|
|
|
+
|
|
|
+# BEGIN TO WORK
|
|
|
+if [ $EUID -ne 0 ]; then
|
|
|
+ error "No way dude, you have to be a root to do this!"
|
|
|
+ exit 1;
|
|
|
+fi
|
|
|
+
|
|
|
+if [ ! -d "$SRCDIR" ]; then mkdir -p "$SRCDIR"; fi #This also creates APPDIR
|
|
|
+
|
|
|
+# STARTING
|
|
|
+backup_src "$AMDGPUDIR" "$SRCDIR"
|
|
|
+
|
|
|
+info "We are ready to start"
|
|
|
+read -p "Continue (y/n)?" yn
|
|
|
+echo
|
|
|
+if [ $yn != "Y" ] && [ $yn != "y" ]; then
|
|
|
+ [ "$0" = "$BASH_SOURCE" ] && exit 1 || return 1 # handle exits from shell or function but don't exit interactive shell
|
|
|
+fi
|
|
|
+
|
|
|
+#PATCH MODE
|
|
|
+KOFILE_PTCH="${APPDIR}/${KERNEL}/amdgpu.ko_${KERNEL}_${UVOLT}_${UCLOCK}"
|
|
|
+
|
|
|
+if [ $RESTORE -eq 1 ]; then
|
|
|
+ KOFILE_PTCH="${APPDIR}/${KERNEL}/amdgpu.ko_${KERNEL}_orig"
|
|
|
+fi
|
|
|
+
|
|
|
+if [ ! -f $KOFILE_PTCH ]; then
|
|
|
+ info "BUILDING new amdgpu.ko file"
|
|
|
+ mkdir -p "$(dirname "$KOFILE_PTCH")"
|
|
|
+
|
|
|
+ PATCH_UVC=`produce_patch $UVOLT $UCLOCK`
|
|
|
+ patch_restore_file "${SRCDIR}${FILE_P10}" "${AMDGPUDIR}${FILE_P10}" "$PATCH_UVC"
|
|
|
+ patch_restore_file "${SRCDIR}${FILE_HW_SMU7}" "${AMDGPUDIR}${FILE_HW_SMU7}" "$PATCH_SMU7"
|
|
|
+
|
|
|
+ cd ${AMDGPUDIR}
|
|
|
+ ${AMDGPUDIR}/pre-build.sh "$KERNEL"
|
|
|
+ make KERNELRELEASE="$KERNEL" -C "/lib/modules/$KERNEL/build" M="$AMDGPUDIR"
|
|
|
+ res=$?
|
|
|
+ cd -
|
|
|
+ if [ $res -ne 0 ]; then error "Can not build! Error code: $?"; exit 1; fi
|
|
|
+ cp -f "${AMDGPUDIR}/amd/amdgpu/amdgpu.ko" "$KOFILE_PTCH"
|
|
|
+else
|
|
|
+ info "amdgpu.ko file already precompiled reusing it $KOFILE_PTCH"
|
|
|
+fi
|
|
|
+
|
|
|
+find "/lib/modules/$KERNEL" -name amdgpu.ko -exec rm -v {} \;
|
|
|
+
|
|
|
+cp -f "$KOFILE_PTCH" "/lib/modules/${KERNEL}/kernel/drivers/gpu/drm/amd/amdgpu/amdgpu.ko"
|
|
|
+
|
|
|
+depmod -a
|
|
|
+update-initramfs -u
|
|
|
+
|
|
|
+info "SUCCESS"
|
|
|
+echo "Looks like everything is ok. Please reboot now."
|
|
|
+info "DOWNCLOCK USAGE MANUAL"
|
|
|
+echo "By default you can write into hwmon file device/pp_sclk_od value from 0 to 20."
|
|
|
+echo "This will increase your clock up to 20%."
|
|
|
+echo "With my patch you can also decrease clock down from 99% to 50%."
|
|
|
+echo "Just write value from 50 to 99 to device/pp_sclk_od"
|
|
|
+echo "It may cause mess while reading pp_sclk_od and pp_dmp_sclk values."
|