amdgpu-mod.sh 9.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318
  1. #!/usr/bin/env bash
  2. #
  3. # Basic idea stolen from here
  4. # https://www.phoronix.com/forums/forum/linux-graphics-x-org-drivers/amd-linux/918649-underclocking-undervolting-the-rx-470-with-amdgpu-pro-success
  5. #
  6. # After this I came to my own downclock idea.
  7. # It working with a little mess in pp_dpm_sclk output,
  8. # but unfortunately it seems that compiling downclock ratio
  9. # gives much better power saving.
  10. #
  11. # This tool modify amd gpu kernel module.
  12. # It allows you to undervolt and underclock your AMD RXxx GPUs under linux with amdgpu latest driver.
  13. #
  14. # COMPATIBILE with 17.20.* 17.30.* and 17.40.* amdgpu drivers
  15. # 17.40.* drivers require HWE kernel for Ubuntu 16.04
  16. #
  17. # BE CAREFULL!
  18. # I DO NOT FULLY UDERSTAND HOW DOES IT WORK AND WFT I'M DOING
  19. # colors
  20. CRED='\033[0;31m'; CYELL='\033[1;33m'; CGREE='\033[0;32m'; CBLUE='\033[0;34m'; NC='\033[0m';
  21. function logo {
  22. echo -e "${CBLUE}
  23. ____________________________________________________
  24. | _ __ _ _ _ |
  25. | /\ |\/| | \ /__ |_) | | |\/| / \ | \ |
  26. | /--\ | | |_/ \_| | |_| | | \_/ |_/ |
  27. |__________________________________________________|${NC}
  28. "
  29. }
  30. function info { echo -e "${CGREE}${1}${NC}"; }
  31. function warn { echo -e "${CYELL}${1}${NC}"; }
  32. function error { echo -e "${CRED}${1}${NC}"; }
  33. FILE_DAG="/amd/amdgpu/amdgpu_vm.c"
  34. FILE_P10="/amd/powerplay/smumgr/polaris10_smumgr.c"
  35. FILE_HW_SMU7="/amd/powerplay/hwmgr/smu7_hwmgr.c"
  36. FILE_HW_VEGA="/amd/powerplay/hwmgr/vega10_hwmgr.c"
  37. function backup_src_file {
  38. if [ ! -f "$1" ]; then error "Source not found $1"; exit 1; fi
  39. if [ -f "$2" ]; then
  40. info "SKIP bakup, file aready exist $2";
  41. else
  42. mkdir -p "$(dirname "$2")";
  43. cp -f "$1" "$2";
  44. if [ $? -ne 0 ]; then error "Can not backup $1 -> $2"; exit 1; fi
  45. fi
  46. }
  47. function backup_src {
  48. backup_src_file "${1}$FILE_DAG" "${2}$FILE_DAG"
  49. backup_src_file "${1}$FILE_P10" "${2}$FILE_P10"
  50. backup_src_file "${1}$FILE_HW_SMU7" "${2}$FILE_HW_SMU7"
  51. backup_src_file "${1}$FILE_HW_VEGA" "${2}$FILE_HW_VEGA"
  52. }
  53. # $1-backup file
  54. # $2-amd source loc
  55. # $3-patch
  56. function patch_restore_file {
  57. cp -f "$1" "$2"
  58. if [ $RESTORE -eq 0 ]; then
  59. info "Patch file $2"
  60. echo "$3" | patch "$2"
  61. if [ $? -ne 0 ]; then error "Patching failed with error code: $?"; echo "$3"; exit 1; fi
  62. info "File patched: $2"
  63. else
  64. info "Source restored $2"
  65. fi
  66. }
  67. PATCH_SMU7=$(cat <<EOF
  68. --- .smu7_hwmgr.c.orig 2017-08-04 12:59:00.000000000 +0300
  69. +++ .smu7_hwmgr.c 2017-09-12 22:59:37.650864941 +0300
  70. @@ -4379,23 +4379,50 @@
  71. struct smu7_hwmgr *data = (struct smu7_hwmgr *)(hwmgr->backend);
  72. struct smu7_single_dpm_table *golden_sclk_table =
  73. &(data->golden_dpm_table.sclk_table);
  74. + struct smu7_single_dpm_table *sclk_table =
  75. + &(data->dpm_table.sclk_table);
  76. struct pp_power_state *ps;
  77. struct smu7_power_state *smu7_ps;
  78. - if (value > 20)
  79. - value = 20;
  80. -
  81. ps = hwmgr->request_ps;
  82. if (ps == NULL)
  83. return -EINVAL;
  84. smu7_ps = cast_phw_smu7_power_state(&ps->hardware);
  85. -
  86. - smu7_ps->performance_levels[smu7_ps->performance_level_count - 1].engine_clock =
  87. - golden_sclk_table->dpm_levels[golden_sclk_table->count - 1].value *
  88. - value / 100 +
  89. - golden_sclk_table->dpm_levels[golden_sclk_table->count - 1].value;
  90. +
  91. + bool up = true;
  92. + if (value >= 50) {
  93. + if (value > 99) value = 99;
  94. + value = 100 - value;
  95. + up = false;
  96. + } else if (value > 20)
  97. + value = 20;
  98. +
  99. + int i;
  100. + for (i = 1; i <= smu7_ps->performance_level_count; i++) {
  101. + uint32_t clock = golden_sclk_table->dpm_levels[golden_sclk_table->count - i].value;
  102. + if (up)
  103. + clock += golden_sclk_table->dpm_levels[golden_sclk_table->count - i].value * value / 100;
  104. + else
  105. + clock -= golden_sclk_table->dpm_levels[golden_sclk_table->count - i].value * value / 100;
  106. + smu7_ps->performance_levels[smu7_ps->performance_level_count - i].engine_clock = clock;
  107. + }
  108. +
  109. + // Only downclock or reset to normal (looks like it does not work properly)
  110. + if (value <= 20)
  111. + value = 0;
  112. +
  113. + uint32_t min_clock = sclk_table->dpm_levels[0].value;
  114. + for (i = 1; i < sclk_table->count; i++) {
  115. + uint32_t clock = golden_sclk_table->dpm_levels[i].value;
  116. + clock -= golden_sclk_table->dpm_levels[i].value * value / 100;
  117. +
  118. + if (clock < min_clock)
  119. + sclk_table->dpm_levels[i].value = min_clock;
  120. + else
  121. + sclk_table->dpm_levels[i].value = clock;
  122. + }
  123. return 0;
  124. }
  125. EOF
  126. )
  127. PATCH_VEGA=$(cat <<EOF
  128. EOF
  129. )
  130. function produce_patch {
  131. TPL=$(cat <<EOF
  132. --- ./polaris10_smc.c.orig 2017-04-27 12:00:59.492580016 +0300
  133. +++ polaris10_smc.c 2017-04-27 15:27:55.451783666 +0300
  134. @@ -112,10 +112,13 @@
  135. else if (dep_table->entries[i].mvdd)
  136. *mvdd = (uint32_t) dep_table->entries[i].mvdd *
  137. VOLTAGE_SCALE;
  138. *voltage |= 1 << PHASES_SHIFT;
  139. + //MOD UNDERVOLT
  140. + //UVT_V*voltage = (*voltage & 0xFFFF0000) + (({uvolt}*VOLTAGE_SCALE) & 0xFFFF);
  141. + //END UNDRVOLT
  142. return 0;
  143. }
  144. }
  145. /* sclk is bigger than max sclk in the dependence table */
  146. @@ -134,10 +139,13 @@
  147. if (SMU7_VOLTAGE_CONTROL_NONE == data->mvdd_control)
  148. *mvdd = data->vbios_boot_state.mvdd_bootup_value * VOLTAGE_SCALE;
  149. else if (dep_table->entries[i].mvdd)
  150. *mvdd = (uint32_t) dep_table->entries[i - 1].mvdd * VOLTAGE_SCALE;
  151. + //MOD UNDERVOLT
  152. + //UVT_V*voltage = (*voltage & 0xFFFF0000) + (({uvolt}*VOLTAGE_SCALE) & 0xFFFF);
  153. + //END UNDRVOLT
  154. return 0;
  155. }
  156. static uint16_t scale_fan_gain_settings(uint16_t raw_setting)
  157. {
  158. @@ -770,10 +780,14 @@
  159. polaris10_get_sclk_range_table(hwmgr, &(smu_data->smc_state_table));
  160. for (i = 0; i < dpm_table->sclk_table.count; i++) {
  161. + //MOD UNDERCLOCK
  162. + int clk = dpm_table->sclk_table.dpm_levels[i].value;
  163. + dpm_table->sclk_table.dpm_levels[i].value -= (clk * {uclock}) / 100;
  164. + //END UNDERCLOCK
  165. result = polaris10_populate_single_graphic_level(hwmgr,
  166. dpm_table->sclk_table.dpm_levels[i].value,
  167. (uint16_t)smu_data->activity_target[i],
  168. &(smu_data->smc_state_table.GraphicsLevel[i]));
  169. if (result)
  170. EOF
  171. )
  172. PATCH=${TPL//"{uvolt}"/$1}
  173. PATCH=${PATCH//"//UVT_V"/""}
  174. echo "${PATCH//"{uclock}"/$2}"
  175. }
  176. function show_help {
  177. echo -e "\nUSAGE:"
  178. echo -e "Patch: $0 -d /usr/src/amdgpu-pro-YOURVERSION -v 800 -c 13 # voltage at 818mV underclock 13%"
  179. echo -e "Restore: $0 -d /usr/src/amdgpu-pro-YOURVERSION -r"
  180. echo -e "\nARGUMENTS:"
  181. echo " -d DIRECTORY : Path to directory with your amdgpu driver files"
  182. echo " -v VOLTAGE : Base voltage in mV as int"
  183. echo " -c PERCENT : Underclock value in percents"
  184. }
  185. # START PROGRAM
  186. HELP=1
  187. LOGO=1
  188. RESTORE=0
  189. AMDGPUDIR=""
  190. UVOLT=0
  191. UCLOCK=0
  192. while getopts "h?rd:v:c:" opt; do
  193. case "$opt" in
  194. h|\?) logo; show_help; exit 0 ;;
  195. d) AMDGPUDIR=$OPTARG; HELP=0 ;;
  196. r) RESTORE=1; HELP=0 ;;
  197. v) UVOLT=$((${OPTARG//[!0-9]/})) ;;
  198. c) UCLOCK=$((${OPTARG//[!0-9]/})) ;;
  199. esac
  200. done
  201. SNAME=$(basename "$0")
  202. APPDIR="$(dirname "$0")/.${SNAME%.*}"
  203. KERNEL=`uname -r`
  204. if [ $LOGO -eq 1 ]; then logo; fi
  205. # VALIDATE INPUT
  206. if [ ! -d "$AMDGPUDIR" ]; then
  207. error "Provided AMD GPU dir does not exist: ${AMDGPUDIR}"
  208. show_help; exit 1;
  209. else
  210. SRCDIR="${APPDIR}/$(basename "$AMDGPUDIR")"
  211. fi
  212. if [ $RESTORE -eq 0 ]; then
  213. if [ $UVOLT -gt 1200 ]; then warn "Undervolt value ${UVOLT}mV does not look like undervolt at all!"; fi
  214. if [ $UVOLT -lt 800 ]; then warn "Are you shure that it whould work with voltage near ${UVOLT}mV ?"; fi
  215. if [ $UVOLT -lt 700 ]; then error "Definitely wrong undervolt value ${UVOLT}mV"; HELP=1; fi
  216. if [ $UCLOCK -lt 0 ] || [ $UCLOCK -gt 50 ]; then error "Can not allow you set underclock to ${UCLOCK}% !"; HELP=1; fi
  217. info "Undervolt value ${UVOLT}mV underclock is ${UCLOCK}%"
  218. else
  219. info "Restore configuration requested"
  220. fi
  221. #SHOW HELP on FUCKUP
  222. if [ $HELP -eq 1 ]; then show_help; exit 0; fi
  223. # BEGIN TO WORK
  224. if [ $EUID -ne 0 ]; then
  225. error "No way dude, you have to be a root to do this!"
  226. exit 1;
  227. fi
  228. if [ ! -d "$SRCDIR" ]; then mkdir -p "$SRCDIR"; fi #This also creates APPDIR
  229. # STARTING
  230. backup_src "$AMDGPUDIR" "$SRCDIR"
  231. info "We are ready to start"
  232. read -p "Continue (y/n)?" yn
  233. echo
  234. if [ $yn != "Y" ] && [ $yn != "y" ]; then
  235. [ "$0" = "$BASH_SOURCE" ] && exit 1 || return 1 # handle exits from shell or function but don't exit interactive shell
  236. fi
  237. #PATCH MODE
  238. KOFILE_PTCH="${APPDIR}/${KERNEL}/amdgpu.ko_${KERNEL}_${UVOLT}_${UCLOCK}"
  239. if [ $RESTORE -eq 1 ]; then
  240. KOFILE_PTCH="${APPDIR}/${KERNEL}/amdgpu.ko_${KERNEL}_orig"
  241. fi
  242. if [ ! -f $KOFILE_PTCH ]; then
  243. info "BUILDING new amdgpu.ko file"
  244. mkdir -p "$(dirname "$KOFILE_PTCH")"
  245. PATCH_UVC=`produce_patch $UVOLT $UCLOCK`
  246. patch_restore_file "${SRCDIR}${FILE_P10}" "${AMDGPUDIR}${FILE_P10}" "$PATCH_UVC"
  247. patch_restore_file "${SRCDIR}${FILE_HW_SMU7}" "${AMDGPUDIR}${FILE_HW_SMU7}" "$PATCH_SMU7"
  248. cd ${AMDGPUDIR}
  249. ${AMDGPUDIR}/pre-build.sh "$KERNEL"
  250. make KERNELRELEASE="$KERNEL" -C "/lib/modules/$KERNEL/build" M="$AMDGPUDIR"
  251. res=$?
  252. cd -
  253. if [ $res -ne 0 ]; then error "Can not build! Error code: $?"; exit 1; fi
  254. cp -f "${AMDGPUDIR}/amd/amdgpu/amdgpu.ko" "$KOFILE_PTCH"
  255. else
  256. info "amdgpu.ko file already precompiled reusing it $KOFILE_PTCH"
  257. fi
  258. find "/lib/modules/$KERNEL" -name amdgpu.ko -exec rm -v {} \;
  259. cp -f "$KOFILE_PTCH" "/lib/modules/${KERNEL}/kernel/drivers/gpu/drm/amd/amdgpu/amdgpu.ko"
  260. depmod -a
  261. update-initramfs -u
  262. info "SUCCESS"
  263. echo "Looks like everything is ok. Please reboot now."
  264. info "DOWNCLOCK USAGE MANUAL"
  265. echo "By default you can write into hwmon file device/pp_sclk_od value from 0 to 20."
  266. echo "This will increase your clock up to 20%."
  267. echo "With my patch you can also decrease clock down from 99% to 50%."
  268. echo "Just write value from 50 to 99 to device/pp_sclk_od"
  269. echo "It may cause mess while reading pp_sclk_od and pp_dmp_sclk values."