mirror of
https://github.com/Lurkki14/tuxclocker.git
synced 2024-11-21 15:57:25 -06:00
add AMD memory clock limit setting
This commit is contained in:
parent
3a695a780c
commit
eb2c4d1d1a
@ -30,6 +30,12 @@ enum VoltFreqType {
|
||||
CoreVFCurve
|
||||
};
|
||||
|
||||
// Minimum (where available) and max clocks
|
||||
enum SingleAssignableType {
|
||||
CoreClock,
|
||||
MemoryClock,
|
||||
};
|
||||
|
||||
EnumerationVec performanceLevelEnumVec = {{_("Automatic"), 0}, {_("Lowest"), 1}, {_("Highest"), 2},
|
||||
{_("Manual"), 3}, {_("Base Levels"), 4}, {_("Lowest Core Clock"), 5},
|
||||
{_("Lowest Memory Clock"), 6}, {_("Highest Clocks"), 7}};
|
||||
@ -145,6 +151,86 @@ std::optional<Assignable> vfPointClockAssignable(
|
||||
return Assignable{setWithPerfLevel, range, getFunc, _("MHz")};
|
||||
}
|
||||
|
||||
// Same as above, but for editing single values like min/max clocks
|
||||
std::optional<Assignable> singleValueAssignable(SingleAssignableType type, uint pointIndex,
|
||||
Range<int> range, std::string unit, AMDGPUData data) {
|
||||
auto path = data.hwmonPath + "/pp_od_clk_voltage";
|
||||
auto contents = fileContents(path);
|
||||
if (!contents.has_value())
|
||||
return {};
|
||||
|
||||
const char *typeString;
|
||||
const char *sectionHeader;
|
||||
|
||||
switch (type) {
|
||||
case MemoryClock: {
|
||||
typeString = "m";
|
||||
sectionHeader = "OD_MCLK";
|
||||
break;
|
||||
}
|
||||
case CoreClock: {
|
||||
typeString = "s";
|
||||
sectionHeader = "OD_SCLK";
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
auto lines = pstateSectionLines(sectionHeader, *contents);
|
||||
if (lines.size() < pointIndex + 1)
|
||||
return std::nullopt;
|
||||
|
||||
// The index in sysfs can be '1' even if there's only one point
|
||||
auto words = fplus::split_words(false, lines[pointIndex]);
|
||||
int sysFsIndex = std::stoi(words.front());
|
||||
|
||||
auto getFunc = [=]() -> std::optional<AssignmentArgument> {
|
||||
auto contents = fileContents(path);
|
||||
if (!contents.has_value())
|
||||
return std::nullopt;
|
||||
|
||||
auto lines = pstateSectionLines(sectionHeader, *contents);
|
||||
if (lines.size() < pointIndex + 1)
|
||||
return std::nullopt;
|
||||
|
||||
auto value = parseLineValue(lines[pointIndex]);
|
||||
if (!value.has_value())
|
||||
return std::nullopt;
|
||||
|
||||
// Memory clock -> controller clock for memory clocks
|
||||
if (type == MemoryClock)
|
||||
return toMemoryClock(*value, data);
|
||||
|
||||
return *value;
|
||||
};
|
||||
|
||||
auto setFunc = [=](AssignmentArgument a) -> std::optional<AssignmentError> {
|
||||
if (!std::holds_alternative<int>(a))
|
||||
return AssignmentError::InvalidType;
|
||||
|
||||
auto target = std::get<int>(a);
|
||||
if (target < range.min || target > range.max)
|
||||
return AssignmentError::OutOfRange;
|
||||
|
||||
// Memory clock -> controller clock for memory clocks
|
||||
if (type == MemoryClock)
|
||||
target = toControllerClock(target, data);
|
||||
|
||||
std::ofstream file{data.hwmonPath + "/pp_od_clk_voltage"};
|
||||
char cmdString[32];
|
||||
snprintf(cmdString, 32, "%s %i %i", typeString, sysFsIndex, target);
|
||||
|
||||
if (file << cmdString && file << "c")
|
||||
return std::nullopt;
|
||||
return AssignmentError::UnknownError;
|
||||
};
|
||||
|
||||
auto setWithPerfLevel = [=](AssignmentArgument a) -> std::optional<AssignmentError> {
|
||||
return withManualPerformanceLevel(setFunc, a, data);
|
||||
};
|
||||
|
||||
return Assignable{setWithPerfLevel, range, getFunc, unit};
|
||||
}
|
||||
|
||||
std::optional<Assignable> vfPointVoltageAssignable(
|
||||
VoltFreqType vfType, uint pointIndex, Range<int> range, AMDGPUData data) {
|
||||
// Eg. the 's' in s 0 400 500
|
||||
@ -858,6 +944,70 @@ std::vector<TreeNode<DeviceNode>> getMemoryUtilization(AMDGPUData data) {
|
||||
return {};
|
||||
}
|
||||
|
||||
std::vector<TreeNode<DeviceNode>> getMaxMemoryClock(AMDGPUData data) {
|
||||
if (!data.ppTableType.has_value())
|
||||
return {};
|
||||
|
||||
auto t = *data.ppTableType;
|
||||
if (t != Navi && t != SMU13 && t != Vega20Other)
|
||||
return {};
|
||||
|
||||
// Per kernel documentation, if there's only one index for memory clock, it means maximum
|
||||
auto lines = pstateSectionLinesWithRead("OD_MCLK", data);
|
||||
if (lines.size() != 1 || lines.size() != 2)
|
||||
return {};
|
||||
|
||||
auto index = (lines.size() == 1) ? 0 : 1;
|
||||
|
||||
auto controllerRange = parsePstateRangeLineWithRead("MCLK", data);
|
||||
if (!controllerRange.has_value())
|
||||
return {};
|
||||
|
||||
Range<int> range{
|
||||
toMemoryClock(controllerRange->min, data), toMemoryClock(controllerRange->max, data)};
|
||||
|
||||
auto assignable = singleValueAssignable(MemoryClock, index, range, _("MHz"), data);
|
||||
if (!assignable.has_value())
|
||||
return {};
|
||||
|
||||
return {DeviceNode{
|
||||
.name = _("Maximum Memory Clock"),
|
||||
.interface = *assignable,
|
||||
.hash = md5(data.pciId + "Maximum Memory Clock"),
|
||||
}};
|
||||
}
|
||||
|
||||
std::vector<TreeNode<DeviceNode>> getMinMemoryClock(AMDGPUData data) {
|
||||
if (!data.ppTableType.has_value())
|
||||
return {};
|
||||
|
||||
auto t = *data.ppTableType;
|
||||
if (t != Navi && t != SMU13 && t != Vega20Other)
|
||||
return {};
|
||||
|
||||
// Per kernel documentation, two indices means there's a minimum clock
|
||||
auto lines = pstateSectionLinesWithRead("OD_MCLK", data);
|
||||
if (lines.size() != 2)
|
||||
return {};
|
||||
|
||||
auto controllerRange = parsePstateRangeLineWithRead("MCLK", data);
|
||||
if (!controllerRange.has_value())
|
||||
return {};
|
||||
|
||||
Range<int> range{
|
||||
toMemoryClock(controllerRange->min, data), toMemoryClock(controllerRange->max, data)};
|
||||
|
||||
auto assignable = singleValueAssignable(MemoryClock, 0, range, _("MHz"), data);
|
||||
if (!assignable.has_value())
|
||||
return {};
|
||||
|
||||
return {DeviceNode{
|
||||
.name = _("Minimum Memory Clock"),
|
||||
.interface = *assignable,
|
||||
.hash = md5(data.pciId + "Minimum Memory Clock"),
|
||||
}};
|
||||
}
|
||||
|
||||
std::vector<TreeNode<DeviceNode>> getVoltFreqRoot(AMDGPUData data) {
|
||||
if (data.ppTableType.has_value() &&
|
||||
(*data.ppTableType == Navi || *data.ppTableType == SMU13))
|
||||
@ -965,7 +1115,9 @@ auto gpuTree = TreeConstructor<AMDGPUData, DeviceNode>{
|
||||
{getPerformanceRoot, {
|
||||
{getClocksRoot, {
|
||||
{getMemoryClockRead, {}},
|
||||
{getCoreClockRead, {}}
|
||||
{getCoreClockRead, {}},
|
||||
{getMinMemoryClock, {}},
|
||||
{getMaxMemoryClock, {}}
|
||||
}},
|
||||
{getVoltageRead, {}},
|
||||
{getForcePerfLevel, {}},
|
||||
|
@ -39,6 +39,14 @@ std::vector<std::string> pstateSectionLines(
|
||||
return retval;
|
||||
}
|
||||
|
||||
std::vector<std::string> pstateSectionLinesWithRead(const std::string &header, AMDGPUData data) {
|
||||
auto contents = fileContents(data.hwmonPath + "/pp_od_clk_voltage");
|
||||
if (!contents.has_value())
|
||||
return {};
|
||||
|
||||
return pstateSectionLines(header, *contents);
|
||||
}
|
||||
|
||||
std::optional<Range<int>> parsePstateRangeLine(std::string title, const std::string &contents) {
|
||||
// For example:
|
||||
// MCLK: 625Mhz 930Mhz
|
||||
@ -122,6 +130,9 @@ std::optional<PPTableType> fromPPTableContents(const std::string &contents) {
|
||||
// RDNA 3 (SMU13) has six points using offsets
|
||||
if (first.has_value() && fourth.has_value())
|
||||
return SMU13;
|
||||
|
||||
// Other tables of this type have min/max clock setting available
|
||||
return Vega20Other;
|
||||
}
|
||||
}
|
||||
return std::nullopt;
|
||||
|
@ -13,7 +13,8 @@ namespace fs = std::filesystem;
|
||||
enum PPTableType {
|
||||
Vega10,
|
||||
Navi,
|
||||
SMU13 // RDNA 3
|
||||
SMU13, // RDNA 3
|
||||
Vega20Other // Has at least clock limits
|
||||
};
|
||||
|
||||
struct VFPoint {
|
||||
@ -31,6 +32,7 @@ struct AMDGPUData {
|
||||
};
|
||||
|
||||
std::vector<std::string> pstateSectionLines(const std::string &header, const std::string &contents);
|
||||
std::vector<std::string> pstateSectionLinesWithRead(const std::string &header, AMDGPUData data);
|
||||
|
||||
std::optional<TuxClocker::Device::Range<int>> parsePstateRangeLine(
|
||||
std::string title, const std::string &contents);
|
||||
|
Loading…
Reference in New Issue
Block a user