summaryrefslogtreecommitdiff
path: root/hosts/iroha/scripts/amdgpu-clocks
blob: 275f90d3aab86d514fb163e2ae746f66ad0be2fb (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
#!/usr/bin/env bash

USER_STATES_PATH=${USER_STATES_PATH:-/etc/default/amdgpu-custom-state}
SYS_PPFMASK=/sys/module/amdgpu/parameters/ppfeaturemask
[ ! -r ${SYS_PPFMASK} ] && echo "Can't access ${SYS_PPFMASK}" && exit 2

if [ "$1" == "restore" ]; then
  RESTORE=true
fi

function check_ppfeaturemask() {
  CURRENT_HEX_MASK=$(printf '%#x' "$(( $(cat ${SYS_PPFMASK}) ))")
  # 0x4000 or 14th bit is one indicating if OverDrive has been enabled
  OVERDRIVE_MASK=$(printf '%#x' "$(( CURRENT_HEX_MASK & 0x4000 ))")
  [ "${OVERDRIVE_MASK}" == "0x4000" ] && return 0
  WANTED_MASK=$(printf '%#x' "$(( CURRENT_HEX_MASK | 0x4000 ))")
  echo -n "In order to set custom amdgpu power states, enable OverDrive by "
  echo -n "booting the machine with amdgpu.ppfeaturemask=${WANTED_MASK} "
  echo    "kernel option" && exit 2
}

function fill_sclks() {
  if [ -z "${3}" ]; then     # Vega20 and later ASICs
    echo "  SCLK state ${1}: ${2}"
    SCLK[${1}]="s ${1} ${2%*M[Hh]z}"
  else                       # Vega10 and previous ASICs
    echo "  SCLK state ${1}: ${2}, ${3}"
    SCLK[${1}]="s ${1} ${2%*M[Hh]z} ${3%*mV}"
  fi
}

function fill_mclks() {
  if [ -z "${3}" ]; then     # Vega20 and later ASICs
    echo "  MCLK state ${1}: ${2}"
    MCLK[${1}]="m ${1} ${2%*M[Hh]z}"
  else                       # Vega10 and previous ASICs
    echo "  MCLK state ${1}: ${2}, ${3}"
    MCLK[${1}]="m ${1} ${2%*M[Hh]z} ${3%*mV}"
  fi
}

function fill_vddccurve() {
  if [ $# -eq 3 ]; then
    echo "  VDDC Curve state $1: ${2} ${3}"
    VDDC_CURVE[${1}]="vc ${1} ${2%*M[Hh]z} ${3%*mV}"
  elif [ $# -eq 4 ]; then
    echo "  VDDC Curve state $1: ${2} @ ${4}"
    VDDC_CURVE[${1}]="vc ${1} ${2%*M[Hh]z} ${4%*mV}"
  else
    echo "ERROR: Can't parse '$*', did pp_od_clk_voltage API change again?"
    exit 2
  fi
}

function fill_vddgfx_offset() {
  echo "  VDD GFX Offset: $1"
  VDDGFX_OFFSET="vo ${1%*mV}"
}

function parse_states() {
  mapfile -t STATE_LINES < <(tr -d '\000' < "$1")
  for LNNO in "${!STATE_LINES[@]}"; do
    LINE="${STATE_LINES[$LNNO]}"
    case ${LINE} in
      "OD_SCLK:")
        state_fill_func=fill_sclks ;;
      "OD_MCLK:")
        state_fill_func=fill_mclks ;;
      "OD_VDDC_CURVE:")
        state_fill_func=fill_vddccurve ;;
      "VDDC_CURVE_SCLK["[012]"]: "*)
        ;; # Just ignoring these for now
      "VDDC_CURVE_VOLT["[012]"]: "*)
        ;; # Just ignoring these for now
      "OD_VDDGFX_OFFSET:")
        offset_fill_func=fill_vddgfx_offset ;;
      "OD_RANGE:")
        echo "  Maximum clocks & voltages:";;
      "SCLK: "*)
        echo "    SCLK clock ${LINE##* }"
        MAX_SCLK=${LINE##* }
        MAX_SCLK=${MAX_SCLK%*M[Hh]z}
        ;;
      "MCLK: "*)
        echo "    MCLK clock ${LINE##* }"
        MAX_MCLK=${LINE##* }
        MAX_MCLK=${MAX_MCLK%*M[Hh]z}
        ;;
      "VDDGFX_OFFSET: "*)
        echo "    VDDGFX offset range: ${LINE#*: }"
        OFFSET_RANGE=${LINE#*: }
        ;;
      "VDDC: "*)
        echo "    VDDC voltage ${LINE##* }"
        MAX_VDDC=${LINE##* }
        MAX_VDDC=${MAX_VDDC%*mV}
        ;;
      [0-9]": "*)
        $state_fill_func ${LINE%%:*} ${LINE#* }
        ;;
      [-0-9]*"mV")
        $offset_fill_func ${LINE%%:*} ${LINE#* }
        ;;
      "FORCE_SCLK: "[0-9]*)
        echo "  Force SCLK state to ${LINE#* }"
        FORCE_SCLK=${LINE#* }
        ;;
      "FORCE_MCLK: "[0-9]*)
        echo "  Force MCLK state to ${LINE#* }"
        FORCE_MCLK=${LINE#* }
        ;;
      "FORCE_POWER_CAP: "[0-9]*)
        MICROWATTS=${LINE#* }
        echo "  Force power cap to ${MICROWATTS%*000000}W"
        FORCE_POWER_CAP=${LINE#* }
        ;;
      "FORCE_PERF_LEVEL: "[a-z]*)
        echo "  Force performance level to ${LINE#* }"
        FORCE_LEVEL=${LINE#* }
        ;;
      "FORCE_POWER_PROFILE: "[0-9]*)
        echo "  Force power profile to ${LINE#* }"
        FORCE_PROFILE=${LINE#* }
        ;;
      "#"*) ;;
      "") ;;
      *)
        echo "  Unexpected value in ${1}:$((LNNO + 1))"
        exit 2
        ;;
    esac
  done
  if [ "$1" == "${SYS_PP_OD_CLK}" ]; then
    DETECTED_MCLK=("${MCLK[@]}")
    DETECTED_VDDC_CURVE=("${VDDC_CURVE[@]}")
    POWER_LIMIT=$(cat "$PWR_CAP_FILE")
    echo "  Curent power cap: ${POWER_LIMIT%*000000}W"
  fi
}

function set_custom_states() {
  if [ "${RESTORE}" == "true" ] && [ "${FORCE_LEVEL}" == "auto" ]; then
    echo manual > "${PWR_LEVEL}"
    sleep 0.25
    echo 0 > "${SYS_DPM_SCLK}" 2>&1
    echo 0 > "${SYS_DPM_MCLK}" 2>&1
    sleep 0.25
  fi
  for CSTATE in "${SCLK[@]}"; do
    echo "${CSTATE}" > "${SYS_PP_OD_CLK}"
  done
  for MSTATE in "${MCLK[@]}"; do
    OLD_MSTATE=${DETECTED_MCLK[$((${MSTATE:3:1}))]}
    if [ "${MSTATE}" != "${OLD_MSTATE}" ]; then
      echo "${MSTATE}" > "${SYS_PP_OD_CLK}" ||
        echo "ERROR: echo ${MSTATE} > ${SYS_PP_OD_CLK}"
    fi
  done
  for VDDC_CURVE_STATE in "${VDDC_CURVE[@]}"; do
    OLD_CURVE_STATE=${DETECTED_VDDC_CURVE[$((${VDDC_CURVE_STATE:3:1}))]}
    if [ "${VDDC_CURVE_STATE}" != "${OLD_CURVE_STATE}" ]; then
      echo "${VDDC_CURVE_STATE}" > "${SYS_PP_OD_CLK}" ||
        echo "ERROR: echo ${VDDC_CURVE_STATE} > ${SYS_PP_OD_CLK}"
    fi
  done
  if [ "${VDDGFX_OFFSET}" ]; then
    echo "${VDDGFX_OFFSET}" > "${SYS_PP_OD_CLK}"
  fi
  echo 'c' > "${SYS_PP_OD_CLK}"
  if [ "${FORCE_LEVEL}" ]; then
    echo ${FORCE_LEVEL} > ${PWR_LEVEL}
  fi
  if [ "${FORCE_PROFILE}" ]; then
    echo ${FORCE_PROFILE} > ${PWR_PROFILE}
  fi
  if [ "${FORCE_SCLK}" ]; then
    echo "${FORCE_SCLK}" > "${SYS_DPM_SCLK}"
  fi
  if [ "${FORCE_MCLK}" ]; then
    echo "${FORCE_MCLK}" > "${SYS_DPM_MCLK}"
  fi
  if [ "${FORCE_POWER_CAP}" ]; then
    echo "${FORCE_POWER_CAP}" > "${PWR_CAP_FILE}"
  fi
}

function backup_states() {
  if [ ! -r "${BACKUP_STATE_FILE}" ]; then
    cp "${SYS_PP_OD_CLK}" "${BACKUP_STATE_FILE}"
    if [ "${PWR_LEVEL}" == "manual" ]; then
      echo "FORCE_POWER_PROFILE: 0" >> "${BACKUP_STATE_FILE}"
    fi
    echo "FORCE_PERF_LEVEL: $(cat "${PWR_LEVEL}")" >> "${BACKUP_STATE_FILE}"
    echo "FORCE_POWER_CAP: $(cat "${PWR_CAP_FILE}")" >> "${BACKUP_STATE_FILE}"
    echo "Writen initial backup states to ${BACKUP_STATE_FILE}"
  else
    echo "Won't write initial state to ${BACKUP_STATE_FILE}, it already exists."
  fi
}

function restore_states() {
  if [ -f "${BACKUP_STATE_FILE}" ]; then
    echo "Restoring all states to the initial defaults from ${BACKUP_STATE_FILE}"
    USER_STATE_FILE=${BACKUP_STATE_FILE}
  else
    echo "Cant't access initial defaults at ${BACKUP_STATE_FILE}, aborting."
    exit 2
  fi
}

function clear_vars() {
    unset SCLK MCLK
    unset VDDC_CURVE VDDGFX_OFFSET
    unset sys_device sys_hwmons
}

check_ppfeaturemask

shopt -s extglob
for USER_STATE_FILE in ${USER_STATES_PATH}*.+(card?|card??|pci:????:??:??.?); do
  BACKUP_STATE_FILE=/tmp/${USER_STATE_FILE##*/}.initial
  if [[ "${USER_STATE_FILE}" == *"pci"* ]]; then
    STATE_PCIDEV=${USER_STATE_FILE#*.}
    for CARD in /sys/class/drm/card{?,??}; do
      LINK=$(readlink $CARD)
      if [ "${LINK}" ]; then
        CARD_PCDEV=${LINK%*/drm/card*}
        if [[ "${CARD_PCDEV##*/}" == "${STATE_PCIDEV#*:}" ]]; then
          sys_device="${CARD}"/device
        fi
      fi
    done
  else
    sys_device=/sys/class/drm/${USER_STATE_FILE##*.}/device
  fi
  sys_hwmons=( "${sys_device}"/hwmon/hwmon[0-9] )

  SYS_PP_OD_CLK=${sys_device}/pp_od_clk_voltage
  PWR_LEVEL=${sys_device}/power_dpm_force_performance_level
  PWR_PROFILE=${sys_device}/pp_power_profile_mode
  SYS_DPM_SCLK=${sys_device}/pp_dpm_sclk
  SYS_DPM_MCLK=${sys_device}/pp_dpm_mclk
  PWR_CAP_FILE=${sys_hwmons[0]}/power1_cap

  if [ -f "${SYS_PP_OD_CLK}" ]; then
    if [ "${RESTORE}" == "true" ]; then
      restore_states
    else
      backup_states
    fi
    echo "Detecting the state values at ${SYS_PP_OD_CLK}:"
    parse_states "${SYS_PP_OD_CLK}"
    echo "Verifying user state values at ${USER_STATE_FILE}:"
    parse_states "${USER_STATE_FILE}"
    echo "Committing custom states to ${SYS_PP_OD_CLK}:"
    set_custom_states
    echo "  Done"
    clear_vars
  else
    echo "WARNING: ${SYS_PP_OD_CLK} does not exist, skipping!"
  fi
done