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"
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+
5179struct 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}
697734ASUS_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 */
700746ASUS_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>" );
713759ASUS_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 */
717781static 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+
731869static 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+
8231107static 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
8621152module_init (asus_fw_init );
0 commit comments