Skip to content

Commit 39ae6c5

Browse files
flukejonesij-intel
authored andcommitted
platform/x86: asus-armoury: add ppt_* and nv_* tuning knobs
Adds the ppt_* and nv_* tuning knobs that are available via WMI methods and adds proper min/max levels plus defaults. The min/max are defined by ASUS and typically gained by looking at what they allow in the ASUS Armoury Crate application - ASUS does not share the values outside of this. It could also be possible to gain the AMD values by use of ryzenadj and testing for the minimum stable value. The general rule of thumb for adding to the match table is that if the model range has a single CPU used throughout, then the DMI match can omit the last letter of the model number as this is the GPU model. If a min or max value is not provided it is assumed that the particular setting is not supported. for example ppt_pl2_sppt_min/max is not set. If a <ppt_setting>_def is not set then the default is assumed to be <ppt_setting>_max It is assumed that at least AC settings are available so that the firmware attributes will be created - if no DC table is available and power is on DC, then reading the attributes is -ENODEV. Co-developed-by: Denis Benato <denis.benato@linux.dev> Signed-off-by: Denis Benato <denis.benato@linux.dev> Signed-off-by: Luke D. Jones <luke@ljones.dev> Reviewed-by: Mario Limonciello <mario.limonciello@amd.com> Tested-by: Mateusz Schyboll <dragonn@op.pl> Tested-by: Porfet Lillian <porfet828@gmail.com> Link: https://patch.msgid.link/20251102215319.3126879-10-denis.benato@linux.dev Reviewed-by: Ilpo Järvinen <ilpo.jarvinen@linux.intel.com> Signed-off-by: Ilpo Järvinen <ilpo.jarvinen@linux.intel.com>
1 parent d849a9f commit 39ae6c5

3 files changed

Lines changed: 1593 additions & 6 deletions

File tree

drivers/platform/x86/asus-armoury.c

Lines changed: 296 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@
3030
#include <linux/pci.h>
3131
#include <linux/platform_data/x86/asus-wmi.h>
3232
#include <linux/printk.h>
33+
#include <linux/power_supply.h>
3334
#include <linux/sysfs.h>
3435

3536
#include "asus-armoury.h"
@@ -48,6 +49,33 @@
4849
#define ASUS_MINI_LED_2024_STRONG 0x01
4950
#define ASUS_MINI_LED_2024_OFF 0x02
5051

52+
/* Power tunable attribute name defines */
53+
#define ATTR_PPT_PL1_SPL "ppt_pl1_spl"
54+
#define ATTR_PPT_PL2_SPPT "ppt_pl2_sppt"
55+
#define ATTR_PPT_PL3_FPPT "ppt_pl3_fppt"
56+
#define ATTR_PPT_APU_SPPT "ppt_apu_sppt"
57+
#define ATTR_PPT_PLATFORM_SPPT "ppt_platform_sppt"
58+
#define ATTR_NV_DYNAMIC_BOOST "nv_dynamic_boost"
59+
#define ATTR_NV_TEMP_TARGET "nv_temp_target"
60+
#define ATTR_NV_BASE_TGP "nv_base_tgp"
61+
#define ATTR_NV_TGP "nv_tgp"
62+
63+
#define ASUS_ROG_TUNABLE_DC 0
64+
#define ASUS_ROG_TUNABLE_AC 1
65+
66+
struct rog_tunables {
67+
const struct power_limits *power_limits;
68+
u32 ppt_pl1_spl; // cpu
69+
u32 ppt_pl2_sppt; // cpu
70+
u32 ppt_pl3_fppt; // cpu
71+
u32 ppt_apu_sppt; // plat
72+
u32 ppt_platform_sppt; // plat
73+
74+
u32 nv_dynamic_boost;
75+
u32 nv_temp_target;
76+
u32 nv_tgp;
77+
};
78+
5179
struct asus_armoury_priv {
5280
struct device *fw_attr_dev;
5381
struct kset *fw_attr_kset;
@@ -60,6 +88,9 @@ struct asus_armoury_priv {
6088
*/
6189
struct mutex egpu_mutex;
6290

91+
/* Index 0 for DC, 1 for AC */
92+
struct rog_tunables *rog_tunables[2];
93+
6394
u32 mini_led_dev_id;
6495
u32 gpu_mux_dev_id;
6596
};
@@ -290,6 +321,12 @@ static ssize_t enum_type_show(struct kobject *kobj, struct kobj_attribute *attr,
290321
return sysfs_emit(buf, "enumeration\n");
291322
}
292323

324+
static ssize_t int_type_show(struct kobject *kobj, struct kobj_attribute *attr,
325+
char *buf)
326+
{
327+
return sysfs_emit(buf, "integer\n");
328+
}
329+
293330
/* Mini-LED mode **************************************************************/
294331

295332
/* Values map for mini-led modes on 2023 and earlier models. */
@@ -696,6 +733,15 @@ static ssize_t apu_mem_possible_values_show(struct kobject *kobj, struct kobj_at
696733
}
697734
ASUS_ATTR_GROUP_ENUM(apu_mem, "apu_mem", "Set available system RAM (in GB) for the APU to use");
698735

736+
/* Define helper to access the current power mode tunable values */
737+
static inline struct rog_tunables *get_current_tunables(void)
738+
{
739+
if (power_supply_is_system_supplied())
740+
return asus_armoury.rog_tunables[ASUS_ROG_TUNABLE_AC];
741+
742+
return asus_armoury.rog_tunables[ASUS_ROG_TUNABLE_DC];
743+
}
744+
699745
/* Simple attribute creation */
700746
ASUS_ATTR_GROUP_ENUM_INT_RO(charge_mode, "charge_mode", ASUS_WMI_DEVID_CHARGE_MODE, "0;1;2\n",
701747
"Show the current mode of charging");
@@ -712,6 +758,24 @@ ASUS_ATTR_GROUP_BOOL_RW(screen_auto_brightness, "screen_auto_brightness",
712758
"Set the panel brightness to Off<0> or On<1>");
713759
ASUS_ATTR_GROUP_BOOL_RO(egpu_connected, "egpu_connected", ASUS_WMI_DEVID_EGPU_CONNECTED,
714760
"Show the eGPU connection status");
761+
ASUS_ATTR_GROUP_ROG_TUNABLE(ppt_pl1_spl, ATTR_PPT_PL1_SPL, ASUS_WMI_DEVID_PPT_PL1_SPL,
762+
"Set the CPU slow package limit");
763+
ASUS_ATTR_GROUP_ROG_TUNABLE(ppt_pl2_sppt, ATTR_PPT_PL2_SPPT, ASUS_WMI_DEVID_PPT_PL2_SPPT,
764+
"Set the CPU fast package limit");
765+
ASUS_ATTR_GROUP_ROG_TUNABLE(ppt_pl3_fppt, ATTR_PPT_PL3_FPPT, ASUS_WMI_DEVID_PPT_PL3_FPPT,
766+
"Set the CPU fastest package limit");
767+
ASUS_ATTR_GROUP_ROG_TUNABLE(ppt_apu_sppt, ATTR_PPT_APU_SPPT, ASUS_WMI_DEVID_PPT_APU_SPPT,
768+
"Set the APU package limit");
769+
ASUS_ATTR_GROUP_ROG_TUNABLE(ppt_platform_sppt, ATTR_PPT_PLATFORM_SPPT, ASUS_WMI_DEVID_PPT_PLAT_SPPT,
770+
"Set the platform package limit");
771+
ASUS_ATTR_GROUP_ROG_TUNABLE(nv_dynamic_boost, ATTR_NV_DYNAMIC_BOOST, ASUS_WMI_DEVID_NV_DYN_BOOST,
772+
"Set the Nvidia dynamic boost limit");
773+
ASUS_ATTR_GROUP_ROG_TUNABLE(nv_temp_target, ATTR_NV_TEMP_TARGET, ASUS_WMI_DEVID_NV_THERM_TARGET,
774+
"Set the Nvidia max thermal limit");
775+
ASUS_ATTR_GROUP_ROG_TUNABLE(nv_tgp, "nv_tgp", ASUS_WMI_DEVID_DGPU_SET_TGP,
776+
"Set the additional TGP on top of the base TGP");
777+
ASUS_ATTR_GROUP_INT_VALUE_ONLY_RO(nv_base_tgp, ATTR_NV_BASE_TGP, ASUS_WMI_DEVID_DGPU_BASE_TGP,
778+
"Read the base TGP value");
715779

716780
/* If an attribute does not require any special case handling add it here */
717781
static const struct asus_attr_group armoury_attr_groups[] = {
@@ -720,6 +784,16 @@ static const struct asus_attr_group armoury_attr_groups[] = {
720784
{ &dgpu_disable_attr_group, ASUS_WMI_DEVID_DGPU },
721785
{ &apu_mem_attr_group, ASUS_WMI_DEVID_APU_MEM },
722786

787+
{ &ppt_pl1_spl_attr_group, ASUS_WMI_DEVID_PPT_PL1_SPL },
788+
{ &ppt_pl2_sppt_attr_group, ASUS_WMI_DEVID_PPT_PL2_SPPT },
789+
{ &ppt_pl3_fppt_attr_group, ASUS_WMI_DEVID_PPT_PL3_FPPT },
790+
{ &ppt_apu_sppt_attr_group, ASUS_WMI_DEVID_PPT_APU_SPPT },
791+
{ &ppt_platform_sppt_attr_group, ASUS_WMI_DEVID_PPT_PLAT_SPPT },
792+
{ &nv_dynamic_boost_attr_group, ASUS_WMI_DEVID_NV_DYN_BOOST },
793+
{ &nv_temp_target_attr_group, ASUS_WMI_DEVID_NV_THERM_TARGET },
794+
{ &nv_base_tgp_attr_group, ASUS_WMI_DEVID_DGPU_BASE_TGP },
795+
{ &nv_tgp_attr_group, ASUS_WMI_DEVID_DGPU_SET_TGP },
796+
723797
{ &charge_mode_attr_group, ASUS_WMI_DEVID_CHARGE_MODE },
724798
{ &boot_sound_attr_group, ASUS_WMI_DEVID_BOOT_SOUND },
725799
{ &mcu_powersave_attr_group, ASUS_WMI_DEVID_MCU_POWERSAVE },
@@ -728,8 +802,76 @@ static const struct asus_attr_group armoury_attr_groups[] = {
728802
{ &screen_auto_brightness_attr_group, ASUS_WMI_DEVID_SCREEN_AUTO_BRIGHTNESS },
729803
};
730804

805+
/**
806+
* is_power_tunable_attr - Determines if an attribute is a power-related tunable
807+
* @name: The name of the attribute to check
808+
*
809+
* This function checks if the given attribute name is related to power tuning.
810+
*
811+
* Return: true if the attribute is a power-related tunable, false otherwise
812+
*/
813+
static bool is_power_tunable_attr(const char *name)
814+
{
815+
static const char * const power_tunable_attrs[] = {
816+
ATTR_PPT_PL1_SPL, ATTR_PPT_PL2_SPPT,
817+
ATTR_PPT_PL3_FPPT, ATTR_PPT_APU_SPPT,
818+
ATTR_PPT_PLATFORM_SPPT, ATTR_NV_DYNAMIC_BOOST,
819+
ATTR_NV_TEMP_TARGET, ATTR_NV_BASE_TGP,
820+
ATTR_NV_TGP
821+
};
822+
823+
for (unsigned int i = 0; i < ARRAY_SIZE(power_tunable_attrs); i++) {
824+
if (!strcmp(name, power_tunable_attrs[i]))
825+
return true;
826+
}
827+
828+
return false;
829+
}
830+
831+
/**
832+
* has_valid_limit - Checks if a power-related attribute has a valid limit value
833+
* @name: The name of the attribute to check
834+
* @limits: Pointer to the power_limits structure containing limit values
835+
*
836+
* This function checks if a power-related attribute has a valid limit value.
837+
* It returns false if limits is NULL or if the corresponding limit value is zero.
838+
*
839+
* Return: true if the attribute has a valid limit value, false otherwise
840+
*/
841+
static bool has_valid_limit(const char *name, const struct power_limits *limits)
842+
{
843+
u32 limit_value = 0;
844+
845+
if (!limits)
846+
return false;
847+
848+
if (!strcmp(name, ATTR_PPT_PL1_SPL))
849+
limit_value = limits->ppt_pl1_spl_max;
850+
else if (!strcmp(name, ATTR_PPT_PL2_SPPT))
851+
limit_value = limits->ppt_pl2_sppt_max;
852+
else if (!strcmp(name, ATTR_PPT_PL3_FPPT))
853+
limit_value = limits->ppt_pl3_fppt_max;
854+
else if (!strcmp(name, ATTR_PPT_APU_SPPT))
855+
limit_value = limits->ppt_apu_sppt_max;
856+
else if (!strcmp(name, ATTR_PPT_PLATFORM_SPPT))
857+
limit_value = limits->ppt_platform_sppt_max;
858+
else if (!strcmp(name, ATTR_NV_DYNAMIC_BOOST))
859+
limit_value = limits->nv_dynamic_boost_max;
860+
else if (!strcmp(name, ATTR_NV_TEMP_TARGET))
861+
limit_value = limits->nv_temp_target_max;
862+
else if (!strcmp(name, ATTR_NV_BASE_TGP) ||
863+
!strcmp(name, ATTR_NV_TGP))
864+
limit_value = limits->nv_tgp_max;
865+
866+
return limit_value > 0;
867+
}
868+
731869
static int asus_fw_attr_add(void)
732870
{
871+
const struct rog_tunables *const ac_rog_tunables = asus_armoury.rog_tunables[ASUS_ROG_TUNABLE_DC];
872+
const struct power_limits *limits;
873+
bool should_create;
874+
const char *name;
733875
int err, i;
734876

735877
asus_armoury.fw_attr_dev = device_create(&firmware_attributes_class, NULL, MKDEV(0, 0),
@@ -786,12 +928,28 @@ static int asus_fw_attr_add(void)
786928
if (!armoury_has_devstate(armoury_attr_groups[i].wmi_devid))
787929
continue;
788930

789-
err = sysfs_create_group(&asus_armoury.fw_attr_kset->kobj,
790-
armoury_attr_groups[i].attr_group);
791-
if (err) {
792-
pr_err("Failed to create sysfs-group for %s\n",
793-
armoury_attr_groups[i].attr_group->name);
794-
goto err_remove_groups;
931+
/* Always create by default, unless PPT is not present */
932+
should_create = true;
933+
name = armoury_attr_groups[i].attr_group->name;
934+
935+
/* Check if this is a power-related tunable requiring limits */
936+
if (ac_rog_tunables && ac_rog_tunables->power_limits &&
937+
is_power_tunable_attr(name)) {
938+
limits = ac_rog_tunables->power_limits;
939+
/* Check only AC: if not present then DC won't be either */
940+
should_create = has_valid_limit(name, limits);
941+
if (!should_create)
942+
pr_debug("Missing max value for tunable %s\n", name);
943+
}
944+
945+
if (should_create) {
946+
err = sysfs_create_group(&asus_armoury.fw_attr_kset->kobj,
947+
armoury_attr_groups[i].attr_group);
948+
if (err) {
949+
pr_err("Failed to create sysfs-group for %s\n",
950+
armoury_attr_groups[i].attr_group->name);
951+
goto err_remove_groups;
952+
}
795953
}
796954
}
797955

@@ -820,6 +978,132 @@ static int asus_fw_attr_add(void)
820978

821979
/* Init / exit ****************************************************************/
822980

981+
/* Set up the min/max and defaults for ROG tunables */
982+
static void init_rog_tunables(void)
983+
{
984+
const struct power_limits *ac_limits, *dc_limits;
985+
struct rog_tunables *ac_rog_tunables = NULL, *dc_rog_tunables = NULL;
986+
const struct power_data *power_data;
987+
const struct dmi_system_id *dmi_id;
988+
989+
/* Match the system against the power_limits table */
990+
dmi_id = dmi_first_match(power_limits);
991+
if (!dmi_id) {
992+
pr_warn("No matching power limits found for this system\n");
993+
return;
994+
}
995+
996+
/* Get the power data for this system */
997+
power_data = dmi_id->driver_data;
998+
if (!power_data) {
999+
pr_info("No power data available for this system\n");
1000+
return;
1001+
}
1002+
1003+
/* Initialize AC power tunables */
1004+
ac_limits = power_data->ac_data;
1005+
if (ac_limits) {
1006+
ac_rog_tunables = kzalloc(sizeof(*asus_armoury.rog_tunables[ASUS_ROG_TUNABLE_AC]),
1007+
GFP_KERNEL);
1008+
if (!ac_rog_tunables)
1009+
goto err_nomem;
1010+
1011+
asus_armoury.rog_tunables[ASUS_ROG_TUNABLE_AC] = ac_rog_tunables;
1012+
ac_rog_tunables->power_limits = ac_limits;
1013+
1014+
/* Set initial AC values */
1015+
ac_rog_tunables->ppt_pl1_spl =
1016+
ac_limits->ppt_pl1_spl_def ?
1017+
ac_limits->ppt_pl1_spl_def :
1018+
ac_limits->ppt_pl1_spl_max;
1019+
1020+
ac_rog_tunables->ppt_pl2_sppt =
1021+
ac_limits->ppt_pl2_sppt_def ?
1022+
ac_limits->ppt_pl2_sppt_def :
1023+
ac_limits->ppt_pl2_sppt_max;
1024+
1025+
ac_rog_tunables->ppt_pl3_fppt =
1026+
ac_limits->ppt_pl3_fppt_def ?
1027+
ac_limits->ppt_pl3_fppt_def :
1028+
ac_limits->ppt_pl3_fppt_max;
1029+
1030+
ac_rog_tunables->ppt_apu_sppt =
1031+
ac_limits->ppt_apu_sppt_def ?
1032+
ac_limits->ppt_apu_sppt_def :
1033+
ac_limits->ppt_apu_sppt_max;
1034+
1035+
ac_rog_tunables->ppt_platform_sppt =
1036+
ac_limits->ppt_platform_sppt_def ?
1037+
ac_limits->ppt_platform_sppt_def :
1038+
ac_limits->ppt_platform_sppt_max;
1039+
1040+
ac_rog_tunables->nv_dynamic_boost =
1041+
ac_limits->nv_dynamic_boost_max;
1042+
ac_rog_tunables->nv_temp_target =
1043+
ac_limits->nv_temp_target_max;
1044+
ac_rog_tunables->nv_tgp = ac_limits->nv_tgp_max;
1045+
1046+
pr_debug("AC power limits initialized for %s\n", dmi_id->matches[0].substr);
1047+
} else {
1048+
pr_debug("No AC PPT limits defined\n");
1049+
}
1050+
1051+
/* Initialize DC power tunables */
1052+
dc_limits = power_data->dc_data;
1053+
if (dc_limits) {
1054+
dc_rog_tunables = kzalloc(sizeof(*asus_armoury.rog_tunables[ASUS_ROG_TUNABLE_DC]),
1055+
GFP_KERNEL);
1056+
if (!dc_rog_tunables) {
1057+
kfree(ac_rog_tunables);
1058+
goto err_nomem;
1059+
}
1060+
1061+
asus_armoury.rog_tunables[ASUS_ROG_TUNABLE_DC] = dc_rog_tunables;
1062+
dc_rog_tunables->power_limits = dc_limits;
1063+
1064+
/* Set initial DC values */
1065+
dc_rog_tunables->ppt_pl1_spl =
1066+
dc_limits->ppt_pl1_spl_def ?
1067+
dc_limits->ppt_pl1_spl_def :
1068+
dc_limits->ppt_pl1_spl_max;
1069+
1070+
dc_rog_tunables->ppt_pl2_sppt =
1071+
dc_limits->ppt_pl2_sppt_def ?
1072+
dc_limits->ppt_pl2_sppt_def :
1073+
dc_limits->ppt_pl2_sppt_max;
1074+
1075+
dc_rog_tunables->ppt_pl3_fppt =
1076+
dc_limits->ppt_pl3_fppt_def ?
1077+
dc_limits->ppt_pl3_fppt_def :
1078+
dc_limits->ppt_pl3_fppt_max;
1079+
1080+
dc_rog_tunables->ppt_apu_sppt =
1081+
dc_limits->ppt_apu_sppt_def ?
1082+
dc_limits->ppt_apu_sppt_def :
1083+
dc_limits->ppt_apu_sppt_max;
1084+
1085+
dc_rog_tunables->ppt_platform_sppt =
1086+
dc_limits->ppt_platform_sppt_def ?
1087+
dc_limits->ppt_platform_sppt_def :
1088+
dc_limits->ppt_platform_sppt_max;
1089+
1090+
dc_rog_tunables->nv_dynamic_boost =
1091+
dc_limits->nv_dynamic_boost_max;
1092+
dc_rog_tunables->nv_temp_target =
1093+
dc_limits->nv_temp_target_max;
1094+
dc_rog_tunables->nv_tgp = dc_limits->nv_tgp_max;
1095+
1096+
pr_debug("DC power limits initialized for %s\n", dmi_id->matches[0].substr);
1097+
} else {
1098+
pr_debug("No DC PPT limits defined\n");
1099+
}
1100+
1101+
return;
1102+
1103+
err_nomem:
1104+
pr_err("Failed to allocate memory for tunables\n");
1105+
}
1106+
8231107
static int __init asus_fw_init(void)
8241108
{
8251109
char *wmi_uid;
@@ -835,6 +1119,9 @@ static int __init asus_fw_init(void)
8351119
if (!strcmp(wmi_uid, ASUS_ACPI_UID_ASUSWMI))
8361120
return -ENODEV;
8371121

1122+
init_rog_tunables();
1123+
1124+
/* Must always be last step to ensure data is available */
8381125
return asus_fw_attr_add();
8391126
}
8401127

@@ -857,6 +1144,9 @@ static void __exit asus_fw_exit(void)
8571144
sysfs_remove_file(&asus_armoury.fw_attr_kset->kobj, &pending_reboot.attr);
8581145
kset_unregister(asus_armoury.fw_attr_kset);
8591146
device_destroy(&firmware_attributes_class, MKDEV(0, 0));
1147+
1148+
kfree(asus_armoury.rog_tunables[ASUS_ROG_TUNABLE_AC]);
1149+
kfree(asus_armoury.rog_tunables[ASUS_ROG_TUNABLE_DC]);
8601150
}
8611151

8621152
module_init(asus_fw_init);

0 commit comments

Comments
 (0)