amdgpu_sysfs/gpu_handle/overdrive/
gcn.rs1use super::{parse_range_line, push_level_line, ClocksLevel, ClocksTable, ClocksTableGen, Range};
3use crate::{
4 error::{Error, ErrorKind::ParseError},
5 Result,
6};
7#[cfg(feature = "serde")]
8use serde::{Deserialize, Serialize};
9use std::{cmp, io::Write, str::FromStr};
10
11#[derive(Debug, Clone)]
13#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
14pub struct Table {
15 pub sclk_levels: Vec<ClocksLevel>,
17 pub mclk_levels: Vec<ClocksLevel>,
19 pub od_range: OdRange,
21}
22
23impl ClocksTable for Table {
24 fn write_commands<W: Write>(
25 &self,
26 writer: &mut W,
27 _previous_table: &ClocksTableGen,
28 ) -> Result<()> {
29 let max_len = cmp::max(self.sclk_levels.len(), self.mclk_levels.len());
30 for i in 0..max_len {
31 if let Some(sclk_level) = self.sclk_levels.get(i) {
32 let command = level_command(*sclk_level, i, 's');
33 writer.write_all(command.as_bytes())?;
34 }
35
36 if let Some(mclk_level) = self.mclk_levels.get(i) {
37 let command = level_command(*mclk_level, i, 'm');
38 writer.write_all(command.as_bytes())?;
39 }
40 }
41
42 Ok(())
43 }
44
45 fn get_max_sclk_range(&self) -> Option<Range> {
46 Some(self.od_range.sclk)
47 }
48
49 fn get_min_sclk_range(&self) -> Option<Range> {
50 Some(self.od_range.sclk)
51 }
52
53 fn get_max_mclk_range(&self) -> Option<Range> {
54 self.od_range.mclk
55 }
56
57 fn get_min_mclk_range(&self) -> Option<Range> {
58 self.od_range.mclk
59 }
60
61 fn get_max_voltage_range(&self) -> Option<Range> {
62 self.od_range.vddc
63 }
64
65 fn get_min_voltage_range(&self) -> Option<Range> {
66 self.od_range.vddc
67 }
68
69 fn get_current_voltage_range(&self) -> Option<Range> {
70 let min = self.sclk_levels.first().map(|level| level.voltage);
71 let max = self.sclk_levels.last().map(|level| level.voltage);
72 Some(Range { min, max })
73 }
74
75 fn get_current_sclk_range(&self) -> Range {
76 let min = self.sclk_levels.first().map(|level| level.clockspeed);
77 let max = self.sclk_levels.last().map(|level| level.clockspeed);
78 Range { min, max }
79 }
80
81 fn get_current_mclk_range(&self) -> Range {
82 let min = self.mclk_levels.first().map(|level| level.clockspeed);
83 let max = self.mclk_levels.last().map(|level| level.clockspeed);
84 Range { min, max }
85 }
86
87 fn set_max_sclk_unchecked(&mut self, clockspeed: i32) -> Result<()> {
88 let len = self.sclk_levels.len();
89 if len == 0 {
90 return Ok(());
91 }
92
93 self.sclk_levels[len - 1].clockspeed = clockspeed;
94 for clock_level in &mut self.sclk_levels[0..len - 1] {
95 clock_level.clockspeed = cmp::min(clock_level.clockspeed, clockspeed);
96 }
97
98 Ok(())
99 }
100
101 fn set_min_sclk_unchecked(&mut self, clockspeed: i32) -> Result<()> {
102 let len = self.sclk_levels.len();
103 if len == 0 {
104 return Ok(());
105 }
106
107 self.sclk_levels[0].clockspeed = clockspeed;
108 for clock_level in &mut self.sclk_levels[1..len] {
109 clock_level.clockspeed = cmp::max(clock_level.clockspeed, clockspeed);
110 }
111
112 Ok(())
113 }
114
115 fn set_max_mclk_unchecked(&mut self, clockspeed: i32) -> Result<()> {
116 let len = self.mclk_levels.len();
117 if len == 0 {
118 return Ok(());
119 }
120
121 self.mclk_levels[len - 1].clockspeed = clockspeed;
122 for clock_level in &mut self.mclk_levels[0..len - 1] {
123 clock_level.clockspeed = cmp::min(clock_level.clockspeed, clockspeed);
124 }
125
126 Ok(())
127 }
128
129 fn set_min_mclk_unchecked(&mut self, clockspeed: i32) -> Result<()> {
130 let len = self.mclk_levels.len();
131 if len == 0 {
132 return Ok(());
133 }
134
135 self.mclk_levels[0].clockspeed = clockspeed;
136 for clock_level in &mut self.mclk_levels[1..len] {
137 clock_level.clockspeed = cmp::max(clock_level.clockspeed, clockspeed);
138 }
139
140 Ok(())
141 }
142
143 fn set_max_voltage_unchecked(&mut self, voltage: i32) -> Result<()> {
144 let len = self.sclk_levels.len();
145 if len == 0 {
146 return Ok(());
147 }
148
149 self.sclk_levels[len - 1].voltage = voltage;
150 for clock_level in &mut self.sclk_levels[0..len - 1] {
151 clock_level.voltage = cmp::min(clock_level.voltage, voltage);
152 }
153
154 Ok(())
155 }
156
157 fn set_min_voltage_unchecked(&mut self, voltage: i32) -> Result<()> {
158 let len = self.sclk_levels.len();
159 if len == 0 {
160 return Ok(());
161 }
162
163 self.sclk_levels[0].voltage = voltage;
164 for clock_level in &mut self.sclk_levels[1..len] {
165 clock_level.voltage = cmp::max(clock_level.voltage, voltage);
166 }
167
168 Ok(())
169 }
170
171 fn get_max_sclk_voltage(&self) -> Option<i32> {
172 self.sclk_levels.last().map(|level| level.voltage)
173 }
174}
175
176#[derive(Debug, Clone, Copy, PartialEq, Eq)]
178#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
179pub struct OdRange {
180 pub sclk: Range,
182 pub mclk: Option<Range>,
184 pub vddc: Option<Range>,
186}
187
188impl FromStr for Table {
189 type Err = Error;
190
191 fn from_str(s: &str) -> Result<Self> {
192 let mut sclk_levels = Vec::with_capacity(7);
193 let mut mclk_levels = Vec::with_capacity(2);
194 let mut sclk_range = None;
195 let mut mclk_range = None;
196 let mut vddc_range = None;
197
198 let mut current_section = None;
199
200 let mut i = 1;
201 for line in s.lines().map(str::trim).filter(|line| !line.is_empty()) {
202 match line {
203 "OD_SCLK:" => current_section = Some(Section::Sclk),
204 "OD_MCLK:" => current_section = Some(Section::Mclk),
205 "OD_RANGE:" => current_section = Some(Section::Range),
206 line => match current_section {
207 Some(Section::Sclk) => {
208 push_level_line(line, &mut sclk_levels, i)?;
209 }
210 Some(Section::Mclk) => {
211 push_level_line(line, &mut mclk_levels, i)?;
212 }
213 Some(Section::Range) => {
214 let (range, name) = parse_range_line(line, i)?;
215 match name {
216 "SCLK" => sclk_range = Some(range),
217 "MCLK" => mclk_range = Some(range),
218 "VDDC" => vddc_range = Some(range),
219 other => {
220 return Err(ParseError {
221 msg: format!("Unexpected range item: {other}"),
222 line: i,
223 }
224 .into())
225 }
226 }
227 }
228 None => {
229 return Err(ParseError {
230 msg: "Could not find section".to_owned(),
231 line: i,
232 }
233 .into())
234 }
235 },
236 }
237 i += 1;
238 }
239
240 sclk_levels.shrink_to_fit();
241 mclk_levels.shrink_to_fit();
242
243 let od_range = OdRange {
244 sclk: sclk_range.ok_or_else(|| ParseError {
245 msg: "No sclk range found".to_owned(),
246 line: i,
247 })?,
248 mclk: mclk_range,
249 vddc: vddc_range,
250 };
251
252 Ok(Self {
253 sclk_levels,
254 mclk_levels,
255 od_range,
256 })
257 }
258}
259
260fn level_command(level: ClocksLevel, i: usize, symbol: char) -> String {
261 let ClocksLevel {
262 clockspeed,
263 voltage,
264 } = level;
265 format!("{symbol} {i} {clockspeed} {voltage}\n")
266}
267
268#[derive(PartialEq)]
269enum Section {
270 Sclk,
271 Mclk,
272 Range,
273}
274
275#[cfg(test)]
276mod tests {
277 use super::{ClocksLevel, Table};
278 use crate::{
279 gpu_handle::overdrive::{arr_commands, gcn::OdRange, ClocksTable, Range},
280 include_table,
281 };
282 use pretty_assertions::assert_eq;
283 use std::str::FromStr;
284
285 const TABLE_RX580: &str = include_table!("rx580");
286
287 #[test]
288 fn parse_full_table() {
289 let table = Table::from_str(TABLE_RX580).unwrap();
290
291 let sclk_levels = [
292 (300, 750),
293 (600, 769),
294 (900, 912),
295 (1145, 1125),
296 (1215, 1150),
297 (1257, 1150),
298 (1300, 1150),
299 (1366, 1150),
300 ]
301 .map(|(clockspeed, voltage)| ClocksLevel {
302 clockspeed,
303 voltage,
304 });
305 let mclk_levels =
306 [(300, 750), (1000, 825), (1750, 975)].map(|(clockspeed, voltage)| ClocksLevel {
307 clockspeed,
308 voltage,
309 });
310 let ranges = OdRange {
311 sclk: Range::full(300, 2000),
312 mclk: Some(Range::full(300, 2250)),
313 vddc: Some(Range::full(750, 1200)),
314 };
315
316 assert_eq!(table.sclk_levels, sclk_levels);
317 assert_eq!(table.mclk_levels, mclk_levels);
318 assert_eq!(table.od_range, ranges);
319 }
320
321 #[test]
322 fn table_into_commands() {
323 let mut table = Table::from_str(TABLE_RX580).unwrap();
324
325 table.set_max_sclk(1500).unwrap();
326 table.set_max_mclk(2250).unwrap();
327
328 table.set_min_sclk(350).unwrap();
329 table.set_min_mclk(360).unwrap();
330
331 table.set_min_voltage(800).unwrap();
332 table.set_max_voltage(1200).unwrap();
333
334 let mut buf = Vec::new();
335 table
336 .write_commands(&mut buf, &table.clone().into())
337 .unwrap();
338 let commands = String::from_utf8(buf).unwrap();
339
340 let expected_commands = arr_commands([
341 "s 0 350 800",
342 "m 0 360 750",
343 "s 1 600 800",
344 "m 1 1000 825",
345 "s 2 900 912",
346 "m 2 2250 975",
347 "s 3 1145 1125",
348 "s 4 1215 1150",
349 "s 5 1257 1150",
350 "s 6 1300 1150",
351 "s 7 1500 1200",
352 ]);
353
354 assert_eq!(expected_commands, commands);
355 }
356
357 #[test]
358 fn generic_actions() {
359 let mut table = Table::from_str(TABLE_RX580).unwrap();
360 let sclk = table.get_max_sclk().unwrap();
361 assert_eq!(sclk, 1366);
362 let mclk = table.get_max_mclk().unwrap();
363 assert_eq!(mclk, 1750);
364 let voltage = table.get_max_sclk_voltage().unwrap();
365 assert_eq!(voltage, 1150);
366
367 table.set_max_sclk(1400).unwrap();
368 let sclk = table.get_max_sclk().unwrap();
369 assert_eq!(sclk, 1400);
370 assert_eq!(table.sclk_levels[7].clockspeed, 1400);
371
372 table.set_max_mclk(1800).unwrap();
373 let mclk = table.get_max_mclk().unwrap();
374 assert_eq!(mclk, 1800);
375 assert_eq!(table.mclk_levels[2].clockspeed, 1800);
376
377 let sclk_range = table.get_max_sclk_range();
378 let mclk_range = table.get_max_mclk_range();
379 let voltage_range = table.get_max_voltage_range();
380 assert_eq!(sclk_range, Some(Range::full(300, 2000)));
381 assert_eq!(mclk_range, Some(Range::full(300, 2250)));
382 assert_eq!(voltage_range, Some(Range::full(750, 1200)));
383 }
384
385 #[test]
386 fn undervolt_normalize() {
387 let mut table = Table::from_str(TABLE_RX580).unwrap();
388 table.set_max_voltage(1100).unwrap();
389 assert!(table.sclk_levels.iter().all(|level| level.voltage <= 1100));
390 }
391
392 #[test]
393 fn underclock_normalize() {
394 let mut table = Table::from_str(TABLE_RX580).unwrap();
395 table.set_max_sclk(1200).unwrap();
396 assert!(table
397 .sclk_levels
398 .iter()
399 .all(|level| level.clockspeed <= 1200));
400 }
401
402 #[test]
403 fn underclock_memory_normalize() {
404 let mut table = Table::from_str(TABLE_RX580).unwrap();
405 table.set_max_mclk(900).unwrap();
406 assert!(table
407 .mclk_levels
408 .iter()
409 .all(|level| level.clockspeed <= 900));
410 }
411
412 #[test]
413 fn min_voltage_normalize() {
414 let mut table = Table::from_str(TABLE_RX580).unwrap();
415 table.set_min_voltage(800).unwrap();
416 assert!(table.sclk_levels.iter().all(|level| level.voltage >= 800));
417 }
418
419 #[test]
420 fn min_clockspeed_normalize() {
421 let mut table = Table::from_str(TABLE_RX580).unwrap();
422 table.set_min_sclk(750).unwrap();
423 assert!(table
424 .sclk_levels
425 .iter()
426 .all(|level| level.clockspeed >= 750));
427 }
428
429 #[test]
430 fn min_memory_clockspeed_normalize() {
431 let mut table = Table::from_str(TABLE_RX580).unwrap();
432 table.set_min_mclk(1100).unwrap();
433 assert!(table
434 .mclk_levels
435 .iter()
436 .all(|level| level.clockspeed >= 1100));
437 }
438}