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
|