123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318 |
- #!/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_smumgr.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."
|