* [PATCH 1/5] platform/x86 asus-bioscfg: move existing tunings to asus-bioscfg module
2024-07-16 5:16 [PATCH 0/5] platform/x86: introduce asus-bioscfg Luke D. Jones
@ 2024-07-16 5:16 ` Luke D. Jones
2024-07-16 9:45 ` Ilpo Järvinen
2024-07-16 9:50 ` Hans de Goede
2024-07-16 5:16 ` [PATCH 2/5] platform/x86: asus-bioscfg: add dgpu tgp control Luke D. Jones
` (4 subsequent siblings)
5 siblings, 2 replies; 22+ messages in thread
From: Luke D. Jones @ 2024-07-16 5:16 UTC (permalink / raw)
To: platform-driver-x86
Cc: corentin.chary, hdegoede, ilpo.jarvinen, mario.limonciello,
linux-kernel, Luke D. Jones
The fw_attributes_class provides a much cleaner interface to all of the
attributes introduced to asus-wmi. This patch moves all of these extra
attributes over to fw_attributes_class, and shifts the bulk of these
definitions to a new kernel module to reduce the clutter of asus-wmi
with the intention of deprecating the asus-wmi attributes in future.
The work applies only to WMI methods which don't have a clearly defined
place within the sysfs and as a result ended up lumped together in
/sys/devices/platform/asus-nb-wmi/ with no standard API.
Where possible the fw attrs now implement defaults, min, max, scalar,
choices, etc. As en example dgpu_disable becomes:
/sys/class/firmware-attributes/asus-bioscfg/attributes/dgpu_disable/
├── current_value
├── display_name
├── possible_values
└── type
as do other attributes.
Signed-off-by: Luke D. Jones <luke@ljones.dev>
---
drivers/platform/x86/Kconfig | 14 +
drivers/platform/x86/Makefile | 1 +
drivers/platform/x86/asus-bioscfg.c | 666 +++++++++++++++++++++
drivers/platform/x86/asus-bioscfg.h | 243 ++++++++
drivers/platform/x86/asus-wmi.c | 18 +-
include/linux/platform_data/x86/asus-wmi.h | 11 +
6 files changed, 952 insertions(+), 1 deletion(-)
create mode 100644 drivers/platform/x86/asus-bioscfg.c
create mode 100644 drivers/platform/x86/asus-bioscfg.h
diff --git a/drivers/platform/x86/Kconfig b/drivers/platform/x86/Kconfig
index 665fa9524986..b4a5a5bec7f3 100644
--- a/drivers/platform/x86/Kconfig
+++ b/drivers/platform/x86/Kconfig
@@ -265,6 +265,18 @@ config ASUS_WIRELESS
If you choose to compile this driver as a module the module will be
called asus-wireless.
+config ASUS_BIOS
+ tristate "ASUS BIOS Driver"
+ depends on ACPI_WMI
+ depends on ASUS_WMI
+ select FW_ATTR_CLASS
+ help
+ Say Y here if you have a WMI aware Asus laptop and would like to use the
+ firmware_attributes API.
+
+ To compile this driver as a module, choose M here: the module will
+ be called asus-bios.
+
config ASUS_WMI
tristate "ASUS WMI Driver"
depends on ACPI_WMI
@@ -276,6 +288,8 @@ config ASUS_WMI
depends on HOTPLUG_PCI
depends on ACPI_VIDEO || ACPI_VIDEO = n
depends on SERIO_I8042 || SERIO_I8042 = n
+ select ASUS_BIOS
+ select ASUS_WMI_BIOS
select INPUT_SPARSEKMAP
select LEDS_CLASS
select NEW_LEDS
diff --git a/drivers/platform/x86/Makefile b/drivers/platform/x86/Makefile
index e1b142947067..d9b5b3f3b241 100644
--- a/drivers/platform/x86/Makefile
+++ b/drivers/platform/x86/Makefile
@@ -32,6 +32,7 @@ obj-$(CONFIG_APPLE_GMUX) += apple-gmux.o
# ASUS
obj-$(CONFIG_ASUS_LAPTOP) += asus-laptop.o
obj-$(CONFIG_ASUS_WIRELESS) += asus-wireless.o
+obj-$(CONFIG_ASUS_BIOS) += asus-bios.o
obj-$(CONFIG_ASUS_WMI) += asus-wmi.o
obj-$(CONFIG_ASUS_NB_WMI) += asus-nb-wmi.o
obj-$(CONFIG_ASUS_TF103C_DOCK) += asus-tf103c-dock.o
diff --git a/drivers/platform/x86/asus-bioscfg.c b/drivers/platform/x86/asus-bioscfg.c
new file mode 100644
index 000000000000..0b34e727aab4
--- /dev/null
+++ b/drivers/platform/x86/asus-bioscfg.c
@@ -0,0 +1,666 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Asus BIOS attributes driver
+ *
+ * Copyright(C) 2010 Intel Corporation.
+ * Copyright(C) 2024-2024 Luke Jones <luke@ljones.dev>
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/platform_data/x86/asus-wmi.h>
+#include <linux/errno.h>
+#include <linux/fs.h>
+#include <linux/types.h>
+#include <linux/dmi.h>
+#include <linux/device.h>
+#include <linux/kmod.h>
+#include <linux/kobject.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/kernel.h>
+#include "asus-bios.h"
+#include "firmware_attributes_class.h"
+
+MODULE_AUTHOR("Luke Jones <luke@ljones.dev>");
+MODULE_DESCRIPTION("ASUS BIOS Configuration Driver");
+MODULE_LICENSE("GPL");
+
+#define ASUS_NB_WMI_EVENT_GUID "0B3CBB35-E3C2-45ED-91C2-4C5A6D195D1C"
+
+MODULE_ALIAS("wmi:"ASUS_NB_WMI_EVENT_GUID);
+
+#define ASUS_MINI_LED_MODE_MASK 0x03
+/* Standard modes for devices with only on/off */
+#define ASUS_MINI_LED_OFF 0x00
+#define ASUS_MINI_LED_ON 0x01
+/* New mode on some devices, define here to clarify remapping later */
+#define ASUS_MINI_LED_STRONG_MODE 0x02
+/* New modes for devices with 3 mini-led mode types */
+#define ASUS_MINI_LED_2024_WEAK 0x00
+#define ASUS_MINI_LED_2024_STRONG 0x01
+#define ASUS_MINI_LED_2024_OFF 0x02
+
+/* Default limits for tunables available on ASUS ROG laptops */
+#define PPT_CPU_LIMIT_MIN 5
+#define PPT_CPU_LIMIT_MAX 150
+#define PPT_CPU_LIMIT_DEFAULT 80
+#define PPT_PLATFORM_MIN 5
+#define PPT_PLATFORM_MAX 100
+#define PPT_PLATFORM_DEFAULT 80
+#define NVIDIA_BOOST_MIN 5
+#define NVIDIA_BOOST_MAX 25
+#define NVIDIA_TEMP_MIN 75
+#define NVIDIA_TEMP_MAX 87
+
+/* Tunables provided by ASUS for gaming laptops */
+struct rog_tunables {
+ u32 cpu_default;
+ u32 cpu_max;
+
+ u32 platform_default;
+ u32 platform_max;
+
+ u32 ppt_pl1_spl; // cpu
+ u32 ppt_pl2_sppt; // cpu
+ u32 ppt_apu_sppt; // plat
+ u32 ppt_platform_sppt; // plat
+ u32 ppt_fppt; // cpu
+
+ u32 nv_boost_default;
+ u32 nv_boost_max;
+ u32 nv_dynamic_boost;
+
+ u32 nv_temp_default;
+ u32 nv_temp_max;
+ u32 nv_temp_target;
+};
+
+static const struct class *fw_attr_class;
+
+struct asus_bios_priv {
+ struct device *fw_attr_dev;
+ struct kset *fw_attr_kset;
+
+ struct rog_tunables *rog_tunables;
+ u32 mini_led_dev_id;
+ u32 gpu_mux_dev_id;
+ bool dgpu_disable_available;
+ bool egpu_enable_available;
+
+ struct mutex mutex;
+} asus_bioscfg = {
+ .mutex = __MUTEX_INITIALIZER(asus_bioscfg.mutex),
+};
+
+static struct fw_attrs_group {
+ u32 pending_reboot;
+} fw_attrs = {
+ .pending_reboot = 0,
+};
+
+/* WMI helper methods */
+static bool asus_wmi_is_present(u32 dev_id)
+{
+ int status = asus_wmi_get_devstate_dsts(dev_id, &retval);
+ u32 retval;
+
+ pr_debug("%s called (0x%08x), retval: 0x%08x\n", __func__, dev_id, retval);
+
+ return status == 0 && (retval & ASUS_WMI_DSTS_PRESENCE_BIT);
+}
+
+static void asus_set_reboot_and_signal_event(void)
+{
+ fw_attrs.pending_reboot = 1;
+ kobject_uevent(&asus_bioscfg.fw_attr_dev->kobj, KOBJ_CHANGE);
+}
+
+static ssize_t pending_reboot_show(struct kobject *kobj,
+ struct kobj_attribute *attr,
+ char *buf)
+{
+ return sysfs_emit(buf, "%d\n", fw_attrs.pending_reboot);
+}
+
+static struct kobj_attribute pending_reboot = __ATTR_RO(pending_reboot);
+
+static bool asus_bios_requires_reboot(struct kobj_attribute *attr)
+{
+ return !strcmp(attr->attr.name, "gpu_mux_mode");
+ !strcmp(attr->attr.name, "panel_hd_mode");
+}
+
+/*
+ * Generic store function for use with many ROG tunables
+ */
+static ssize_t attr_int_store(struct kobject *kobj,
+ struct kobj_attribute *attr,
+ const char *buf, size_t count,
+ u32 min, u32 max, u32 *store_value, u32 wmi_dev)
+{
+ int result, value;
+
+ result = kstrtoint(buf, 10, &value);
+ if (result)
+ return result;
+
+ if (value < min || value > max)
+ return -EINVAL;
+
+ asus_wmi_set_devstate(wmi_dev, value, &result);
+ if (result) {
+ pr_err("Failed to set %s: %d\n", attr->attr.name, result);
+ return result;
+ }
+
+ if (result > 1) {
+ pr_err("Failed to set %s (result): 0x%x\n", attr->attr.name, result);
+ return -EIO;
+ }
+
+ if (store_value != NULL)
+ *store_value = value;
+ sysfs_notify(kobj, NULL, attr->attr.name);
+
+ if (asus_bios_requires_reboot(attr))
+ asus_set_reboot_and_signal_event();
+
+ return count;
+}
+
+/* Mini-LED mode **************************************************************/
+static ssize_t mini_led_mode_current_value_show(struct kobject *kobj,
+ struct kobj_attribute *attr, char *buf)
+{
+ u32 value;
+ int err;
+
+ err = asus_wmi_get_devstate_dsts(asus_bioscfg.mini_led_dev_id, &value);
+ if (err)
+ return err;
+
+ value = value & ASUS_MINI_LED_MODE_MASK;
+
+ /*
+ * Remap the mode values to match previous generation mini-led. The last gen
+ * WMI 0 == off, while on this version WMI 2 ==off (flipped).
+ */
+ if (asus_bioscfg.mini_led_dev_id == ASUS_WMI_DEVID_MINI_LED_MODE2) {
+ switch (value) {
+ case ASUS_MINI_LED_2024_WEAK:
+ value = ASUS_MINI_LED_ON;
+ break;
+ case ASUS_MINI_LED_2024_STRONG:
+ value = ASUS_MINI_LED_STRONG_MODE;
+ break;
+ case ASUS_MINI_LED_2024_OFF:
+ value = ASUS_MINI_LED_OFF;
+ break;
+ }
+ }
+
+ return sysfs_emit(buf, "%d\n", value);
+}
+
+static ssize_t mini_led_mode_current_value_store(struct kobject *kobj,
+ struct kobj_attribute *attr,
+ const char *buf, size_t count)
+{
+ int result, err;
+ u32 mode;
+
+ result = kstrtou32(buf, 10, &mode);
+ if (result)
+ return result;
+
+ if (asus_bioscfg.mini_led_dev_id == ASUS_WMI_DEVID_MINI_LED_MODE &&
+ mode > ASUS_MINI_LED_ON)
+ return -EINVAL;
+ if (asus_bioscfg.mini_led_dev_id == ASUS_WMI_DEVID_MINI_LED_MODE2 &&
+ mode > ASUS_MINI_LED_STRONG_MODE)
+ return -EINVAL;
+
+ /*
+ * Remap the mode values so expected behaviour is the same as the last
+ * generation of mini-LED with 0 == off, 1 == on.
+ */
+ if (asus_bioscfg.mini_led_dev_id == ASUS_WMI_DEVID_MINI_LED_MODE2) {
+ switch (mode) {
+ case ASUS_MINI_LED_OFF:
+ mode = ASUS_MINI_LED_2024_OFF;
+ break;
+ case ASUS_MINI_LED_ON:
+ mode = ASUS_MINI_LED_2024_WEAK;
+ break;
+ case ASUS_MINI_LED_STRONG_MODE:
+ mode = ASUS_MINI_LED_2024_STRONG;
+ break;
+ }
+ }
+
+ err = asus_wmi_set_devstate(asus_bioscfg.mini_led_dev_id, mode, &result);
+ if (err) {
+ pr_warn("Failed to set mini-LED: %d\n", err);
+ return err;
+ }
+
+ if (result > 1) {
+ pr_warn("Failed to set mini-LED mode (result): 0x%x\n", result);
+ return -EIO;
+ }
+
+ sysfs_notify(kobj, NULL, attr->attr.name);
+
+ return count;
+}
+
+static ssize_t mini_led_mode_possible_values_show(struct kobject *kobj,
+ struct kobj_attribute *attr, char *buf)
+{
+ switch (asus_bioscfg.mini_led_dev_id) {
+ case ASUS_WMI_DEVID_MINI_LED_MODE:
+ return sysfs_emit(buf, "0;1\n");
+ case ASUS_WMI_DEVID_MINI_LED_MODE2:
+ return sysfs_emit(buf, "0;1;2\n");
+ }
+
+ return sysfs_emit(buf, "0\n");
+}
+
+ATTR_GROUP_ENUM_CUSTOM(mini_led_mode, "mini_led_mode", "Set the mini-LED backlight mode");
+
+static ssize_t gpu_mux_mode_current_value_store(struct kobject *kobj,
+ struct kobj_attribute *attr,
+ const char *buf, size_t count)
+{
+ int result, err;
+ u32 optimus;
+
+ err = kstrtou32(buf, 10, &optimus);
+ if (err)
+ return err;
+
+ if (optimus > 1)
+ return -EINVAL;
+
+ if (asus_bioscfg.dgpu_disable_available) {
+ err = asus_wmi_get_devstate_dsts(ASUS_WMI_DEVID_DGPU, &result);
+ if (err)
+ return err;
+ if (err && !optimus) {
+ err = -ENODEV;
+ pr_warn("Can not switch MUX to dGPU mode when dGPU is disabled: %d\n", err);
+ return err;
+ }
+ }
+
+ if (asus_bioscfg.egpu_enable_available) {
+ err = asus_wmi_get_devstate_dsts(ASUS_WMI_DEVID_EGPU, &result);
+ if (err)
+ return err;
+ if (result && !optimus) {
+ err = -ENODEV;
+ pr_warn("Can not switch MUX to dGPU mode when eGPU is enabled: %d\n", err);
+ return err;
+ }
+ }
+
+ err = asus_wmi_set_devstate(asus_bioscfg.gpu_mux_dev_id, optimus, &result);
+ if (err) {
+ pr_err("%s Failed to set GPU MUX mode: %d\nn", __func__, err);
+ return err;
+ }
+ /* !1 is considered a fail by ASUS */
+ if (result != 1) {
+ pr_warn("%s Failed to set GPU MUX mode (result): 0x%x\n", __func__, result);
+ return -EIO;
+ }
+
+ sysfs_notify(kobj, NULL, attr->attr.name);
+
+ return count;
+}
+WMI_SHOW_INT(gpu_mux_mode_current_value, "%d\n", asus_bioscfg.gpu_mux_dev_id);
+ATTR_GROUP_BOOL_CUSTOM(gpu_mux_mode, "gpu_mux_mode", "Set the GPU display MUX mode");
+
+/*
+ * A user may be required to store the value twice, typcial store first, then
+ * rescan PCI bus to activate power, then store a second time to save correctly.
+ * The reason for this is that an extra code path in the ACPI is enabled when
+ * the device and bus are powered.
+ */
+static ssize_t dgpu_disable_current_value_store(struct kobject *kobj,
+ struct kobj_attribute *attr,
+ const char *buf, size_t count)
+{
+ int result, err;
+ u32 disable;
+
+ result = kstrtou32(buf, 10, &disable);
+ if (result)
+ return result;
+
+ if (disable > 1)
+ return -EINVAL;
+
+ if (asus_bioscfg.gpu_mux_dev_id) {
+ err = asus_wmi_get_devstate_dsts(asus_bioscfg.gpu_mux_dev_id, &result);
+ if (err)
+ return err;
+ if (!result && disable) {
+ err = -ENODEV;
+ pr_warn("Can not disable dGPU when the MUX is in dGPU mode: %d\n", err);
+ return err;
+ }
+ }
+
+ err = asus_wmi_set_devstate(ASUS_WMI_DEVID_DGPU, disable, &result);
+ if (err) {
+ pr_warn("Failed to set dgpu disable: %d\n", err);
+ return err;
+ }
+
+ if (result > 1) {
+ pr_warn("Failed to set dgpu disable (result): 0x%x\n", result);
+ return -EIO;
+ }
+
+ sysfs_notify(kobj, NULL, attr->attr.name);
+
+ return count;
+}
+WMI_SHOW_INT(dgpu_disable_current_value, "%d\n", ASUS_WMI_DEVID_DGPU);
+ATTR_GROUP_BOOL_CUSTOM(dgpu_disable, "dgpu_disable", "Disable the dGPU");
+
+/* The ACPI call to enable the eGPU also disables the internal dGPU */
+static ssize_t egpu_enable_current_value_store(struct kobject *kobj,
+ struct kobj_attribute *attr,
+ const char *buf, size_t count)
+{
+ int result, err;
+ u32 enable;
+
+ err = kstrtou32(buf, 10, &enable);
+ if (err)
+ return err;
+
+ if (enable > 1)
+ return -EINVAL;
+
+ err = asus_wmi_get_devstate_dsts(ASUS_WMI_DEVID_EGPU_CONNECTED, &result);
+ if (err) {
+ pr_warn("Failed to get egpu connection status: %d\n", err);
+ return err;
+ }
+
+ if (asus_bioscfg.gpu_mux_dev_id) {
+ err = asus_wmi_get_devstate_dsts(asus_bioscfg.gpu_mux_dev_id, &result);
+ if (err) {
+ pr_warn("Failed to get gpu mux status: %d\n", result);
+ return result;
+ }
+ if (!result && enable) {
+ err = -ENODEV;
+ pr_warn("Can not enable eGPU when the MUX is in dGPU mode: %d\n", err);
+ return err;
+ }
+ }
+
+ err = asus_wmi_set_devstate(ASUS_WMI_DEVID_EGPU, enable, &result);
+ if (err) {
+ pr_warn("Failed to set egpu state: %d\n", err);
+ return err;
+ }
+
+ if (result > 1) {
+ pr_warn("Failed to set egpu state (retval): 0x%x\n", result);
+ return -EIO;
+ }
+
+ sysfs_notify(kobj, NULL, attr->attr.name);
+
+ return count;
+}
+WMI_SHOW_INT(egpu_enable_current_value, "%d\n", ASUS_WMI_DEVID_EGPU);
+ATTR_GROUP_BOOL_CUSTOM(egpu_enable, "egpu_enable", "Enable the eGPU (also disables dGPU)");
+
+/* Simple attribute creation */
+ATTR_GROUP_ENUM_INT_RW(thermal_policy, "thermal_policy", ASUS_WMI_DEVID_THROTTLE_THERMAL_POLICY,
+ 0, 3, "0;1;2", "Set the thermal profile: 0=normal, 1=performance, 2=quiet");
+ATTR_GROUP_PPT_RW(ppt_pl1_spl, "ppt_pl1_spl", ASUS_WMI_DEVID_PPT_PL1_SPL,
+ cpu_default, 5, cpu_max, 1, "Set the CPU slow package limit");
+ATTR_GROUP_PPT_RW(ppt_pl2_sppt, "ppt_pl2_sppt", ASUS_WMI_DEVID_PPT_PL2_SPPT,
+ cpu_default, 5, cpu_max, 1, "Set the CPU fast package limit");
+ATTR_GROUP_PPT_RW(ppt_apu_sppt, "ppt_apu_sppt", ASUS_WMI_DEVID_PPT_APU_SPPT,
+ platform_default, 5, platform_max, 1, "Set the CPU slow package limit");
+ATTR_GROUP_PPT_RW(ppt_platform_sppt, "ppt_platform_sppt", ASUS_WMI_DEVID_PPT_PLAT_SPPT,
+ platform_default, 5, platform_max, 1, "Set the CPU slow package limit");
+ATTR_GROUP_PPT_RW(ppt_fppt, "ppt_fppt", ASUS_WMI_DEVID_PPT_FPPT,
+ cpu_default, 5, cpu_max, 1, "Set the CPU slow package limit");
+
+ATTR_GROUP_PPT_RW(nv_dynamic_boost, "nv_dynamic_boost", ASUS_WMI_DEVID_NV_DYN_BOOST,
+ nv_boost_default, 5, nv_boost_max, 1, "Set the Nvidia dynamic boost limit");
+ATTR_GROUP_PPT_RW(nv_temp_target, "nv_temp_target", ASUS_WMI_DEVID_NV_THERM_TARGET,
+ nv_temp_default, 75, nv_temp_max, 1, "Set the Nvidia max thermal limit");
+
+ATTR_GROUP_ENUM_INT_RO(charge_mode, "charge_mode", ASUS_WMI_DEVID_CHARGE_MODE,
+ "0;1;2", "Show the current mode of charging");
+ATTR_GROUP_BOOL_RW(boot_sound, "boot_sound", ASUS_WMI_DEVID_BOOT_SOUND,
+ "Set the boot POST sound");
+ATTR_GROUP_BOOL_RW(mcu_powersave, "mcu_powersave", ASUS_WMI_DEVID_MCU_POWERSAVE,
+ "Set MCU powersaving mode");
+ATTR_GROUP_BOOL_RW(panel_od, "panel_overdrive", ASUS_WMI_DEVID_PANEL_OD,
+ "Set the panel refresh overdrive");
+ATTR_GROUP_BOOL_RW(panel_hd_mode, "panel_hd_mode", ASUS_WMI_DEVID_PANEL_HD,
+ "Set the panel HD mode to UHD<0> or FHD<1>");
+ATTR_GROUP_BOOL_RO(egpu_connected, "egpu_connected", ASUS_WMI_DEVID_EGPU_CONNECTED,
+ "Show the eGPU connection status");
+
+static int asus_fw_attr_add(void)
+{
+ int ret;
+
+ ret = fw_attributes_class_get(&fw_attr_class);
+ if (ret)
+ goto fail_class_created;
+ else
+ asus_bioscfg.fw_attr_dev = device_create(fw_attr_class, NULL,
+ MKDEV(0, 0), NULL, "%s", DRIVER_NAME);
+
+ if (IS_ERR(asus_bioscfg.fw_attr_dev)) {
+ ret = PTR_ERR(asus_bioscfg.fw_attr_dev);
+ goto fail_class_created;
+ }
+
+ asus_bioscfg.fw_attr_kset = kset_create_and_add("attributes", NULL,
+ &asus_bioscfg.fw_attr_dev->kobj);
+ if (!asus_bioscfg.fw_attr_dev) {
+ ret = -ENOMEM;
+ pr_debug("Failed to create and add attributes\n");
+ goto err_destroy_classdev;
+ }
+
+ /* Add any firmware_attributes required */
+ ret = sysfs_create_file(&asus_bioscfg.fw_attr_kset->kobj, &pending_reboot.attr);
+ if (ret) {
+ pr_warn("Failed to create sysfs level attributes\n");
+ goto fail_class_created;
+ }
+
+ // TODO: logging
+ asus_bioscfg.mini_led_dev_id = 0;
+ if (asus_wmi_is_present(ASUS_WMI_DEVID_MINI_LED_MODE)) {
+ asus_bioscfg.mini_led_dev_id = ASUS_WMI_DEVID_MINI_LED_MODE;
+ sysfs_create_group(&asus_bioscfg.fw_attr_kset->kobj, &mini_led_mode_attr_group);
+ } else if (asus_wmi_is_present(ASUS_WMI_DEVID_MINI_LED_MODE2)) {
+ asus_bioscfg.mini_led_dev_id = ASUS_WMI_DEVID_MINI_LED_MODE2;
+ sysfs_create_group(&asus_bioscfg.fw_attr_kset->kobj, &mini_led_mode_attr_group);
+ }
+
+ if (asus_wmi_is_present(ASUS_WMI_DEVID_GPU_MUX)) {
+ asus_bioscfg.gpu_mux_dev_id = ASUS_WMI_DEVID_GPU_MUX;
+ sysfs_create_group(&asus_bioscfg.fw_attr_kset->kobj, &gpu_mux_mode_attr_group);
+ } else if (asus_wmi_is_present(ASUS_WMI_DEVID_GPU_MUX_VIVO)) {
+ asus_bioscfg.gpu_mux_dev_id = ASUS_WMI_DEVID_GPU_MUX_VIVO;
+ sysfs_create_group(&asus_bioscfg.fw_attr_kset->kobj, &gpu_mux_mode_attr_group);
+ }
+
+ if (asus_wmi_is_present(ASUS_WMI_DEVID_DGPU)) {
+ asus_bioscfg.dgpu_disable_available = true;
+ sysfs_create_group(&asus_bioscfg.fw_attr_kset->kobj, &dgpu_disable_attr_group);
+ }
+ if (asus_wmi_is_present(ASUS_WMI_DEVID_EGPU)) {
+ asus_bioscfg.egpu_enable_available = true;
+ sysfs_create_group(&asus_bioscfg.fw_attr_kset->kobj, &egpu_enable_attr_group);
+ }
+ if (asus_wmi_is_present(ASUS_WMI_DEVID_EGPU_CONNECTED))
+ sysfs_create_group(&asus_bioscfg.fw_attr_kset->kobj, &egpu_connected_attr_group);
+
+ if (asus_wmi_is_present(ASUS_WMI_DEVID_THROTTLE_THERMAL_POLICY))
+ sysfs_create_group(&asus_bioscfg.fw_attr_kset->kobj, &thermal_policy_attr_group);
+ if (asus_wmi_is_present(ASUS_WMI_DEVID_PPT_PL1_SPL))
+ sysfs_create_group(&asus_bioscfg.fw_attr_kset->kobj, &ppt_pl1_spl_attr_group);
+ if (asus_wmi_is_present(ASUS_WMI_DEVID_PPT_PL2_SPPT))
+ sysfs_create_group(&asus_bioscfg.fw_attr_kset->kobj, &ppt_pl2_sppt_attr_group);
+ if (asus_wmi_is_present(ASUS_WMI_DEVID_PPT_APU_SPPT))
+ sysfs_create_group(&asus_bioscfg.fw_attr_kset->kobj, &ppt_apu_sppt_attr_group);
+ if (asus_wmi_is_present(ASUS_WMI_DEVID_PPT_PLAT_SPPT))
+ sysfs_create_group(&asus_bioscfg.fw_attr_kset->kobj, &ppt_platform_sppt_attr_group);
+ if (asus_wmi_is_present(ASUS_WMI_DEVID_PPT_FPPT))
+ sysfs_create_group(&asus_bioscfg.fw_attr_kset->kobj, &ppt_fppt_attr_group);
+
+ if (asus_wmi_is_present(ASUS_WMI_DEVID_NV_DYN_BOOST))
+ sysfs_create_group(&asus_bioscfg.fw_attr_kset->kobj, &nv_dynamic_boost_attr_group);
+ if (asus_wmi_is_present(ASUS_WMI_DEVID_NV_THERM_TARGET))
+ sysfs_create_group(&asus_bioscfg.fw_attr_kset->kobj, &nv_temp_target_attr_group);
+
+ if (asus_wmi_is_present(ASUS_WMI_DEVID_CHARGE_MODE))
+ sysfs_create_group(&asus_bioscfg.fw_attr_kset->kobj, &charge_mode_attr_group);
+ if (asus_wmi_is_present(ASUS_WMI_DEVID_BOOT_SOUND))
+ sysfs_create_group(&asus_bioscfg.fw_attr_kset->kobj, &boot_sound_attr_group);
+ if (asus_wmi_is_present(ASUS_WMI_DEVID_MCU_POWERSAVE))
+ sysfs_create_group(&asus_bioscfg.fw_attr_kset->kobj, &mcu_powersave_attr_group);
+ if (asus_wmi_is_present(ASUS_WMI_DEVID_PANEL_OD))
+ sysfs_create_group(&asus_bioscfg.fw_attr_kset->kobj, &panel_od_attr_group);
+ if (asus_wmi_is_present(ASUS_WMI_DEVID_PANEL_HD))
+ sysfs_create_group(&asus_bioscfg.fw_attr_kset->kobj, &panel_hd_mode_attr_group);
+
+ return 0;
+
+err_destroy_classdev:
+ device_destroy(fw_attr_class, MKDEV(0, 0));
+
+fail_class_created:
+ fw_attributes_class_put();
+ return ret;
+}
+
+/* Init / exit ****************************************************************/
+
+/* Set up the min/max and defaults for ROG tunables */
+static void init_rog_tunables(struct rog_tunables *rog)
+{
+ const char *product;
+ u32 max_boost = NVIDIA_BOOST_MAX;
+ u32 cpu_default = PPT_CPU_LIMIT_DEFAULT;
+ u32 cpu_max = PPT_CPU_LIMIT_MAX;
+ u32 platform_default = PPT_PLATFORM_DEFAULT;
+ u32 platform_max = PPT_PLATFORM_MAX;
+
+ /*
+ * ASUS product_name contains everything required, e.g,
+ * "ROG Flow X16 GV601VV_GV601VV_00185149B"
+ */
+ product = dmi_get_system_info(DMI_PRODUCT_NAME);
+
+ if (strstr(product, "GA402R")) {
+ cpu_default = 125;
+ } else if (strstr(product, "13QY")) {
+ cpu_max = 250;
+ } else if (strstr(product, "X13")) {
+ cpu_max = 75;
+ cpu_default = 50;
+ } else if (strstr(product, "RC71")) {
+ cpu_max = 50;
+ cpu_default = 30;
+ } else if (strstr(product, "G814")
+ || strstr(product, "G614")
+ || strstr(product, "G834")
+ || strstr(product, "G634")) {
+ cpu_max = 175;
+ } else if (strstr(product, "GA402X")
+ || strstr(product, "GA403")
+ || strstr(product, "FA507N")
+ || strstr(product, "FA507X")
+ || strstr(product, "FA707N")
+ || strstr(product, "FA707X")) {
+ cpu_max = 90;
+ }
+
+ if (strstr(product, "GZ301ZE"))
+ max_boost = 5;
+ else if (strstr(product, "FX507ZC4"))
+ max_boost = 15;
+ else if (strstr(product, "GU605"))
+ max_boost = 20;
+
+ /* ensure defaults for tunables */
+ rog->cpu_default = cpu_default;
+ rog->cpu_max = cpu_max;
+
+ rog->platform_default = platform_default;
+ rog->platform_max = platform_max;
+
+ rog->ppt_pl1_spl = cpu_default;
+ rog->ppt_pl2_sppt = cpu_default;
+ rog->ppt_apu_sppt = cpu_default;
+
+ rog->ppt_platform_sppt = platform_default;
+ rog->ppt_fppt = platform_default;
+
+ rog->nv_boost_default = NVIDIA_BOOST_MAX;
+ rog->nv_boost_max = max_boost;
+ rog->nv_dynamic_boost = NVIDIA_BOOST_MIN;
+
+ rog->nv_temp_default = NVIDIA_TEMP_MAX;
+ rog->nv_temp_max = NVIDIA_TEMP_MAX;
+ rog->nv_temp_target = NVIDIA_TEMP_MIN;
+
+}
+
+static int __init asus_fw_init(void)
+{
+ int err;
+
+ mutex_lock(&asus_bioscfg.mutex);
+
+ asus_bioscfg.rog_tunables = kzalloc(sizeof(struct rog_tunables), GFP_KERNEL);
+ if (!asus_bioscfg.rog_tunables) {
+ mutex_unlock(&asus_bioscfg.mutex);
+ return -ENOMEM;
+ }
+ init_rog_tunables(asus_bioscfg.rog_tunables);
+
+ err = asus_fw_attr_add();
+ mutex_unlock(&asus_bioscfg.mutex);
+ if (err)
+ return err;
+
+ return 0;
+}
+
+static void __exit asus_fw_exit(void)
+{
+ mutex_lock(&asus_bioscfg.mutex);
+
+ sysfs_remove_file(&asus_bioscfg.fw_attr_kset->kobj, &pending_reboot.attr);
+ kset_unregister(asus_bioscfg.fw_attr_kset);
+ device_destroy(fw_attr_class, MKDEV(0, 0));
+ fw_attributes_class_put();
+
+ mutex_unlock(&asus_bioscfg.mutex);
+}
+
+module_init(asus_fw_init);
+module_exit(asus_fw_exit);
diff --git a/drivers/platform/x86/asus-bioscfg.h b/drivers/platform/x86/asus-bioscfg.h
new file mode 100644
index 000000000000..403563c25f53
--- /dev/null
+++ b/drivers/platform/x86/asus-bioscfg.h
@@ -0,0 +1,243 @@
+/* SPDX-License-Identifier: GPL-2.0
+ *
+ * Definitions for kernel modules using asus-bios driver
+ *
+ * Copyright (c) 2024 Luke Jones <luke@ljones.dev>
+ */
+
+#ifndef _ASUS_BIOSCFG_H_
+#define _ASUS_BIOSCFG_H_
+
+#include "firmware_attributes_class.h"
+#include <linux/types.h>
+
+#define DRIVER_NAME "asus-bioscfg"
+
+static ssize_t attr_int_store(struct kobject *kobj, struct kobj_attribute *attr,
+ const char *buf, size_t count,
+ u32 min, u32 max, u32 *store_value, u32 wmi_dev);
+
+
+static ssize_t int_type_show(struct kobject *kobj, struct kobj_attribute *attr,
+ char *buf)
+{
+ return sysfs_emit(buf, "integer\n");
+}
+
+static ssize_t enum_type_show(struct kobject *kobj, struct kobj_attribute *attr,
+ char *buf)
+{
+ return sysfs_emit(buf, "enumeration\n");
+}
+
+#define __ASUS_ATTR_RO(_func, _name) { \
+ .attr = { .name = __stringify(_name), .mode = 0444 }, \
+ .show = _func##_##_name##_show, \
+}
+
+#define __ASUS_ATTR_RO_AS(_name, _show) { \
+ .attr = { .name = __stringify(_name), .mode = 0444 }, \
+ .show = _show, \
+}
+
+#define __ASUS_ATTR_RW(_func, _name) __ATTR(_name, 0644, \
+ _func##_##_name##_show, _func##_##_name##_store)
+
+#define __WMI_STORE_INT(_attr, _min, _max, _wmi) \
+static ssize_t _attr##_store(struct kobject *kobj, \
+ struct kobj_attribute *attr, \
+ const char *buf, size_t count) \
+{ \
+ return attr_int_store(kobj, attr, buf, count, _min, _max, NULL, _wmi); \
+}
+
+#define WMI_SHOW_INT(_attr, _fmt, _wmi) \
+static ssize_t _attr##_show(struct kobject *kobj, \
+ struct kobj_attribute *attr, char *buf) \
+{ \
+ u32 result; \
+ int err; \
+ err = asus_wmi_get_devstate_dsts(_wmi, &result); \
+ if (err) \
+ return err; \
+ return sysfs_emit(buf, _fmt, \
+ result & ~ASUS_WMI_DSTS_PRESENCE_BIT); \
+}
+
+/* Create functions and attributes for use in other macros or on their own */
+
+#define __ROG_TUNABLE_RW(_attr, _min, _max, _wmi) \
+static ssize_t _attr##_current_value_store(struct kobject *kobj, \
+ struct kobj_attribute *attr, \
+ const char *buf, size_t count) \
+{ \
+ return attr_int_store(kobj, attr, buf, count, \
+ _min, asus_bioscfg.rog_tunables->_max, \
+ &asus_bioscfg.rog_tunables->_attr, _wmi); \
+} \
+static ssize_t _attr##_current_value_show(struct kobject *kobj, \
+ struct kobj_attribute *attr, char *buf) \
+{ \
+ return sysfs_emit(buf, "%u\n", asus_bioscfg.rog_tunables->_attr);\
+} \
+static struct kobj_attribute attr_##_attr##_current_value = \
+ __ASUS_ATTR_RW(_attr, current_value)
+
+#define __ROG_TUNABLE_SHOW(_prop, _attrname, _val) \
+static ssize_t _attrname##_##_prop##_show(struct kobject *kobj, \
+ struct kobj_attribute *attr, char *buf) \
+{ \
+ return sysfs_emit(buf, "%d\n", asus_bioscfg.rog_tunables->_val);\
+} \
+static struct kobj_attribute attr_##_attrname##_##_prop = \
+ __ASUS_ATTR_RO(_attrname, _prop)
+
+#define __ATTR_CURRENT_INT_RO(_attr, _wmi) \
+WMI_SHOW_INT(_attr##_current_value, "%d\n", _wmi); \
+static struct kobj_attribute attr_##_attr##_current_value = \
+ __ASUS_ATTR_RO(_attr, current_value)
+
+#define __ATTR_CURRENT_INT_RW(_attr, _minv, _maxv, _wmi) \
+__WMI_STORE_INT(_attr##_current_value, _minv, _maxv, _wmi); \
+WMI_SHOW_INT(_attr##_current_value, "%d\n", _wmi); \
+static struct kobj_attribute attr_##_attr##_current_value = \
+ __ASUS_ATTR_RW(_attr, current_value)
+
+/* Shows a formatted static variable */
+#define __ATTR_SHOW_FMT(_prop, _attrname, _fmt, _val) \
+static ssize_t _attrname##_##_prop##_show(struct kobject *kobj, \
+ struct kobj_attribute *attr, char *buf) \
+{ \
+ return sysfs_emit(buf, _fmt, _val); \
+} \
+static struct kobj_attribute attr_##_attrname##_##_prop = \
+ __ASUS_ATTR_RO(_attrname, _prop)
+
+/* Int style min/max range, base macro. Requires current_value show&|store */
+#define __ATTR_GROUP_INT(_attrname, _fsname, _default, \
+ _min, _max, _incstep, _dispname)\
+__ATTR_SHOW_FMT(default_value, _attrname, "%d\n", _default); \
+__ATTR_SHOW_FMT(min_value, _attrname, "%d\n", _min); \
+__ATTR_SHOW_FMT(max_value, _attrname, "%d\n", _max); \
+__ATTR_SHOW_FMT(scalar_increment, _attrname, "%d\n", _incstep); \
+__ATTR_SHOW_FMT(display_name, _attrname, "%s\n", _dispname); \
+static struct kobj_attribute attr_##_attrname##_type = \
+ __ASUS_ATTR_RO_AS(type, int_type_show); \
+static struct attribute *_attrname##_attrs[] = { \
+ &attr_##_attrname##_current_value.attr, \
+ &attr_##_attrname##_default_value.attr, \
+ &attr_##_attrname##_min_value.attr, \
+ &attr_##_attrname##_max_value.attr, \
+ &attr_##_attrname##_scalar_increment.attr, \
+ &attr_##_attrname##_display_name.attr, \
+ &attr_##_attrname##_type.attr, \
+ NULL \
+}; \
+static const struct attribute_group _attrname##_attr_group = { \
+ .name = _fsname, \
+ .attrs = _attrname##_attrs \
+}
+
+/* Boolean style enumeration, base macro. Requires adding show/store */
+#define __ATTR_GROUP_ENUM(_attrname, _fsname, _possible, _dispname) \
+__ATTR_SHOW_FMT(display_name, _attrname, "%s\n", _dispname); \
+__ATTR_SHOW_FMT(possible_values, _attrname, "%s\n", _possible); \
+static struct kobj_attribute attr_##_attrname##_type = \
+ __ASUS_ATTR_RO_AS(type, enum_type_show); \
+static struct attribute *_attrname##_attrs[] = { \
+ &attr_##_attrname##_current_value.attr, \
+ &attr_##_attrname##_display_name.attr, \
+ &attr_##_attrname##_possible_values.attr, \
+ &attr_##_attrname##_type.attr, \
+ NULL \
+}; \
+static const struct attribute_group _attrname##_attr_group = { \
+ .name = _fsname, \
+ .attrs = _attrname##_attrs \
+}
+
+#define ATTR_GROUP_BOOL_RO(_attrname, _fsname, _wmi, _dispname) \
+do { \
+ __ATTR_CURRENT_INT_RO(_attrname, _wmi); \
+ __ATTR_GROUP_ENUM(_attrname, _fsname, "0;1", _dispname);\
+} while (0)
+
+#define ATTR_GROUP_BOOL_RW(_attrname, _fsname, _wmi, _dispname) \
+do { \
+ __ATTR_CURRENT_INT_RW(_attrname, 0, 1, _wmi); \
+ __ATTR_GROUP_ENUM(_attrname, _fsname, "0;1", _dispname);\
+} while (0)
+
+/*
+ * Requires <name>_current_value_show(), <name>_current_value_show()
+ */
+#define ATTR_GROUP_BOOL_CUSTOM(_attrname, _fsname, _dispname) \
+static struct kobj_attribute attr_##_attrname##_current_value = \
+ __ASUS_ATTR_RW(_attrname, current_value); \
+__ATTR_GROUP_ENUM(_attrname, _fsname, "0;1", _dispname)
+
+#define ATTR_GROUP_ENUM_INT_RO(_attrname, _fsname, _wmi, \
+ _possible, _dispname) \
+do { \
+ __ATTR_CURRENT_INT_RO(_attrname, _wmi); \
+ __ATTR_GROUP_ENUM(_attrname, _fsname, _possible, _dispname); \
+} while (0)
+
+#define ATTR_GROUP_ENUM_INT_RW(_attrname, _fsname, _wmi, _min, \
+ _max, _possible, _dispname) \
+do { \
+ __ATTR_CURRENT_INT_RW(_attrname, _min, _max, _wmi); \
+ __ATTR_GROUP_ENUM(_attrname, _fsname, _possible, _dispname); \
+} while (0)
+
+/*
+ * Requires <name>_current_value_show(), <name>_current_value_show()
+ * and <name>_possible_values_show()
+ */
+#define ATTR_GROUP_ENUM_CUSTOM(_attrname, _fsname, _dispname) \
+__ATTR_SHOW_FMT(display_name, _attrname, "%s\n", _dispname); \
+static struct kobj_attribute attr_##_attrname##_current_value = \
+ __ASUS_ATTR_RW(_attrname, current_value); \
+static struct kobj_attribute attr_##_attrname##_possible_values = \
+ __ASUS_ATTR_RO(_attrname, possible_values); \
+static struct kobj_attribute attr_##_attrname##_type = \
+ __ASUS_ATTR_RO_AS(type, enum_type_show); \
+static struct attribute *_attrname##_attrs[] = { \
+ &attr_##_attrname##_current_value.attr, \
+ &attr_##_attrname##_display_name.attr, \
+ &attr_##_attrname##_possible_values.attr, \
+ &attr_##_attrname##_type.attr, \
+ NULL \
+}; \
+static const struct attribute_group _attrname##_attr_group = { \
+ .name = _fsname, \
+ .attrs = _attrname##_attrs \
+}
+
+/* ROG PPT attributes need a little different in setup */
+#define ATTR_GROUP_PPT_RW(_attrname, _fsname, _wmi, _default, \
+ _min, _max, _incstep, _dispname) \
+__ROG_TUNABLE_RW(_attrname, _min, _max, _wmi); \
+__ROG_TUNABLE_SHOW(default_value, _attrname, _default); \
+__ATTR_SHOW_FMT(min_value, _attrname, "%d\n", _min); \
+__ROG_TUNABLE_SHOW(max_value, _attrname, _max); \
+__ATTR_SHOW_FMT(scalar_increment, _attrname, "%d\n", _incstep); \
+__ATTR_SHOW_FMT(display_name, _attrname, "%s\n", _dispname); \
+static struct kobj_attribute attr_##_attrname##_type = \
+ __ASUS_ATTR_RO_AS(type, int_type_show); \
+static struct attribute *_attrname##_attrs[] = { \
+ &attr_##_attrname##_current_value.attr, \
+ &attr_##_attrname##_default_value.attr, \
+ &attr_##_attrname##_min_value.attr, \
+ &attr_##_attrname##_max_value.attr, \
+ &attr_##_attrname##_scalar_increment.attr, \
+ &attr_##_attrname##_display_name.attr, \
+ &attr_##_attrname##_type.attr, \
+ NULL \
+}; \
+static const struct attribute_group _attrname##_attr_group = { \
+ .name = _fsname, \
+ .attrs = _attrname##_attrs \
+}
+
+#endif /* _ASUS_BIOSCFG_H_ */
diff --git a/drivers/platform/x86/asus-wmi.c b/drivers/platform/x86/asus-wmi.c
index 2b968003cb9b..3f1998638fea 100644
--- a/drivers/platform/x86/asus-wmi.c
+++ b/drivers/platform/x86/asus-wmi.c
@@ -529,12 +529,28 @@ static int asus_wmi_get_devstate(struct asus_wmi *asus, u32 dev_id, u32 *retval)
return 0;
}
-static int asus_wmi_set_devstate(u32 dev_id, u32 ctrl_param,
+int asus_wmi_get_devstate_dsts(u32 dev_id, u32 *retval)
+{
+ int err;
+
+ err = asus_wmi_evaluate_method(ASUS_WMI_METHODID_DSTS, dev_id, 0, retval);
+ if (err)
+ return err;
+
+ if (*retval == ~0)
+ return -ENODEV;
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(asus_wmi_get_devstate_dsts);
+
+int asus_wmi_set_devstate(u32 dev_id, u32 ctrl_param,
u32 *retval)
{
return asus_wmi_evaluate_method(ASUS_WMI_METHODID_DEVS, dev_id,
ctrl_param, retval);
}
+EXPORT_SYMBOL_GPL(asus_wmi_set_devstate);
/* Helper for special devices with magic return codes */
static int asus_wmi_get_devstate_bits(struct asus_wmi *asus,
diff --git a/include/linux/platform_data/x86/asus-wmi.h b/include/linux/platform_data/x86/asus-wmi.h
index 74b32e1d6735..889336a932fb 100644
--- a/include/linux/platform_data/x86/asus-wmi.h
+++ b/include/linux/platform_data/x86/asus-wmi.h
@@ -67,6 +67,7 @@
#define ASUS_WMI_DEVID_THROTTLE_THERMAL_POLICY 0x00120075
/* Misc */
+#define ASUS_WMI_DEVID_PANEL_HD 0x0005001C
#define ASUS_WMI_DEVID_PANEL_OD 0x00050019
#define ASUS_WMI_DEVID_CAMERA 0x00060013
#define ASUS_WMI_DEVID_LID_FLIP 0x00060062
@@ -152,8 +153,18 @@
#define ASUS_WMI_DSTS_LIGHTBAR_MASK 0x0000000F
#if IS_REACHABLE(CONFIG_ASUS_WMI)
+int asus_wmi_get_devstate_dsts(u32 dev_id, u32 *retval);
+int asus_wmi_set_devstate(u32 dev_id, u32 ctrl_param, u32 *retval);
int asus_wmi_evaluate_method(u32 method_id, u32 arg0, u32 arg1, u32 *retval);
#else
+static int asus_wmi_get_devstate_dsts(u32 dev_id, u32 *retval)
+{
+ return -ENODEV;
+}
+static int asus_wmi_set_devstate(u32 dev_id, u32 ctrl_param, u32 *retval)
+{
+ return -ENODEV;
+}
static inline int asus_wmi_evaluate_method(u32 method_id, u32 arg0, u32 arg1,
u32 *retval)
{
--
2.45.2
^ permalink raw reply related [flat|nested] 22+ messages in thread* Re: [PATCH 1/5] platform/x86 asus-bioscfg: move existing tunings to asus-bioscfg module
2024-07-16 5:16 ` [PATCH 1/5] platform/x86 asus-bioscfg: move existing tunings to asus-bioscfg module Luke D. Jones
@ 2024-07-16 9:45 ` Ilpo Järvinen
2024-07-16 10:41 ` Luke Jones
2024-07-16 9:50 ` Hans de Goede
1 sibling, 1 reply; 22+ messages in thread
From: Ilpo Järvinen @ 2024-07-16 9:45 UTC (permalink / raw)
To: Luke D. Jones
Cc: platform-driver-x86, corentin.chary, Hans de Goede,
mario.limonciello, LKML
[-- Attachment #1: Type: text/plain, Size: 39749 bytes --]
On Tue, 16 Jul 2024, Luke D. Jones wrote:
> The fw_attributes_class provides a much cleaner interface to all of the
> attributes introduced to asus-wmi. This patch moves all of these extra
> attributes over to fw_attributes_class, and shifts the bulk of these
> definitions to a new kernel module to reduce the clutter of asus-wmi
> with the intention of deprecating the asus-wmi attributes in future.
>
> The work applies only to WMI methods which don't have a clearly defined
> place within the sysfs and as a result ended up lumped together in
> /sys/devices/platform/asus-nb-wmi/ with no standard API.
>
> Where possible the fw attrs now implement defaults, min, max, scalar,
> choices, etc. As en example dgpu_disable becomes:
>
> /sys/class/firmware-attributes/asus-bioscfg/attributes/dgpu_disable/
> ├── current_value
> ├── display_name
> ├── possible_values
> └── type
>
> as do other attributes.
>
> Signed-off-by: Luke D. Jones <luke@ljones.dev>
> ---
> drivers/platform/x86/Kconfig | 14 +
> drivers/platform/x86/Makefile | 1 +
> drivers/platform/x86/asus-bioscfg.c | 666 +++++++++++++++++++++
> drivers/platform/x86/asus-bioscfg.h | 243 ++++++++
> drivers/platform/x86/asus-wmi.c | 18 +-
> include/linux/platform_data/x86/asus-wmi.h | 11 +
> 6 files changed, 952 insertions(+), 1 deletion(-)
> create mode 100644 drivers/platform/x86/asus-bioscfg.c
> create mode 100644 drivers/platform/x86/asus-bioscfg.h
>
> diff --git a/drivers/platform/x86/Kconfig b/drivers/platform/x86/Kconfig
> index 665fa9524986..b4a5a5bec7f3 100644
> --- a/drivers/platform/x86/Kconfig
> +++ b/drivers/platform/x86/Kconfig
> @@ -265,6 +265,18 @@ config ASUS_WIRELESS
> If you choose to compile this driver as a module the module will be
> called asus-wireless.
>
> +config ASUS_BIOS
> + tristate "ASUS BIOS Driver"
> + depends on ACPI_WMI
> + depends on ASUS_WMI
> + select FW_ATTR_CLASS
> + help
> + Say Y here if you have a WMI aware Asus laptop and would like to use the
> + firmware_attributes API.
> +
> + To compile this driver as a module, choose M here: the module will
> + be called asus-bios.
> +
> config ASUS_WMI
> tristate "ASUS WMI Driver"
> depends on ACPI_WMI
> @@ -276,6 +288,8 @@ config ASUS_WMI
> depends on HOTPLUG_PCI
> depends on ACPI_VIDEO || ACPI_VIDEO = n
> depends on SERIO_I8042 || SERIO_I8042 = n
> + select ASUS_BIOS
Selecting user visible configs is not a good idea. Also, there
seems to be circular dependency now between ASUS_BIOS & ASUS_WMI ?
> + select ASUS_WMI_BIOS
> select INPUT_SPARSEKMAP
> select LEDS_CLASS
> select NEW_LEDS
> diff --git a/drivers/platform/x86/Makefile b/drivers/platform/x86/Makefile
> index e1b142947067..d9b5b3f3b241 100644
> --- a/drivers/platform/x86/Makefile
> +++ b/drivers/platform/x86/Makefile
> @@ -32,6 +32,7 @@ obj-$(CONFIG_APPLE_GMUX) += apple-gmux.o
> # ASUS
> obj-$(CONFIG_ASUS_LAPTOP) += asus-laptop.o
> obj-$(CONFIG_ASUS_WIRELESS) += asus-wireless.o
> +obj-$(CONFIG_ASUS_BIOS) += asus-bios.o
> obj-$(CONFIG_ASUS_WMI) += asus-wmi.o
> obj-$(CONFIG_ASUS_NB_WMI) += asus-nb-wmi.o
> obj-$(CONFIG_ASUS_TF103C_DOCK) += asus-tf103c-dock.o
> diff --git a/drivers/platform/x86/asus-bioscfg.c b/drivers/platform/x86/asus-bioscfg.c
> new file mode 100644
> index 000000000000..0b34e727aab4
> --- /dev/null
> +++ b/drivers/platform/x86/asus-bioscfg.c
> @@ -0,0 +1,666 @@
> +// SPDX-License-Identifier: GPL-2.0-or-later
> +/*
> + * Asus BIOS attributes driver
> + *
> + * Copyright(C) 2010 Intel Corporation.
> + * Copyright(C) 2024-2024 Luke Jones <luke@ljones.dev>
> + */
> +
> +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
> +
> +#include <linux/platform_data/x86/asus-wmi.h>
> +#include <linux/errno.h>
> +#include <linux/fs.h>
> +#include <linux/types.h>
> +#include <linux/dmi.h>
> +#include <linux/device.h>
> +#include <linux/kmod.h>
> +#include <linux/kobject.h>
> +#include <linux/module.h>
> +#include <linux/mutex.h>
> +#include <linux/kernel.h>
> +#include "asus-bios.h"
> +#include "firmware_attributes_class.h"
> +
> +MODULE_AUTHOR("Luke Jones <luke@ljones.dev>");
> +MODULE_DESCRIPTION("ASUS BIOS Configuration Driver");
> +MODULE_LICENSE("GPL");
The usual place for these is at the end of file
> +#define ASUS_NB_WMI_EVENT_GUID "0B3CBB35-E3C2-45ED-91C2-4C5A6D195D1C"
> +
> +MODULE_ALIAS("wmi:"ASUS_NB_WMI_EVENT_GUID);
Ditto.
> +#define ASUS_MINI_LED_MODE_MASK 0x03
> +/* Standard modes for devices with only on/off */
> +#define ASUS_MINI_LED_OFF 0x00
> +#define ASUS_MINI_LED_ON 0x01
> +/* New mode on some devices, define here to clarify remapping later */
> +#define ASUS_MINI_LED_STRONG_MODE 0x02
> +/* New modes for devices with 3 mini-led mode types */
> +#define ASUS_MINI_LED_2024_WEAK 0x00
> +#define ASUS_MINI_LED_2024_STRONG 0x01
> +#define ASUS_MINI_LED_2024_OFF 0x02
> +
> +/* Default limits for tunables available on ASUS ROG laptops */
> +#define PPT_CPU_LIMIT_MIN 5
> +#define PPT_CPU_LIMIT_MAX 150
> +#define PPT_CPU_LIMIT_DEFAULT 80
> +#define PPT_PLATFORM_MIN 5
> +#define PPT_PLATFORM_MAX 100
> +#define PPT_PLATFORM_DEFAULT 80
> +#define NVIDIA_BOOST_MIN 5
> +#define NVIDIA_BOOST_MAX 25
> +#define NVIDIA_TEMP_MIN 75
> +#define NVIDIA_TEMP_MAX 87
> +
> +/* Tunables provided by ASUS for gaming laptops */
> +struct rog_tunables {
> + u32 cpu_default;
> + u32 cpu_max;
> +
> + u32 platform_default;
> + u32 platform_max;
> +
> + u32 ppt_pl1_spl; // cpu
> + u32 ppt_pl2_sppt; // cpu
> + u32 ppt_apu_sppt; // plat
> + u32 ppt_platform_sppt; // plat
> + u32 ppt_fppt; // cpu
> +
> + u32 nv_boost_default;
> + u32 nv_boost_max;
> + u32 nv_dynamic_boost;
> +
> + u32 nv_temp_default;
> + u32 nv_temp_max;
> + u32 nv_temp_target;
> +};
> +
> +static const struct class *fw_attr_class;
> +
> +struct asus_bios_priv {
> + struct device *fw_attr_dev;
> + struct kset *fw_attr_kset;
> +
> + struct rog_tunables *rog_tunables;
> + u32 mini_led_dev_id;
> + u32 gpu_mux_dev_id;
> + bool dgpu_disable_available;
> + bool egpu_enable_available;
> +
> + struct mutex mutex;
> +} asus_bioscfg = {
> + .mutex = __MUTEX_INITIALIZER(asus_bioscfg.mutex),
Don't try to initialize it on the same go like this.
You might want static too.
> +};
> +
> +static struct fw_attrs_group {
> + u32 pending_reboot;
> +} fw_attrs = {
> + .pending_reboot = 0,
> +};
Same here.
> +
> +/* WMI helper methods */
> +static bool asus_wmi_is_present(u32 dev_id)
> +{
> + int status = asus_wmi_get_devstate_dsts(dev_id, &retval);
> + u32 retval;
> +
> + pr_debug("%s called (0x%08x), retval: 0x%08x\n", __func__, dev_id, retval);
> +
> + return status == 0 && (retval & ASUS_WMI_DSTS_PRESENCE_BIT);
> +}
> +
> +static void asus_set_reboot_and_signal_event(void)
> +{
> + fw_attrs.pending_reboot = 1;
> + kobject_uevent(&asus_bioscfg.fw_attr_dev->kobj, KOBJ_CHANGE);
> +}
> +
> +static ssize_t pending_reboot_show(struct kobject *kobj,
> + struct kobj_attribute *attr,
> + char *buf)
> +{
> + return sysfs_emit(buf, "%d\n", fw_attrs.pending_reboot);
%u
> +}
> +
> +static struct kobj_attribute pending_reboot = __ATTR_RO(pending_reboot);
> +
> +static bool asus_bios_requires_reboot(struct kobj_attribute *attr)
> +{
> + return !strcmp(attr->attr.name, "gpu_mux_mode");
> + !strcmp(attr->attr.name, "panel_hd_mode");
???
Semicolon and && confusion here?
> +}
> +
> +/*
> + * Generic store function for use with many ROG tunables
> + */
> +static ssize_t attr_int_store(struct kobject *kobj,
> + struct kobj_attribute *attr,
> + const char *buf, size_t count,
> + u32 min, u32 max, u32 *store_value, u32 wmi_dev)
> +{
> + int result, value;
> +
> + result = kstrtoint(buf, 10, &value);
Is signed value needed, if not, use unsigned variant of kstrto*?
> + if (result)
> + return result;
> +
> + if (value < min || value > max)
> + return -EINVAL;
> +
> + asus_wmi_set_devstate(wmi_dev, value, &result);
Type confusion, u32 * vs int pointer being passed.
> + if (result) {
> + pr_err("Failed to set %s: %d\n", attr->attr.name, result);
> + return result;
> + }
> +
> + if (result > 1) {
What's this supposed to mean given you've the type confusion to begin
with and return on the earlier line if result is non-zero?
Did you mean to capture the return value of asus_wmi_set_devstate() and
test that in the first if ()?
If you make a previously internal function such as asus_wmi_set_devstate()
EXPORTed, you should document it with kerneldoc so the interface is clear.
> + pr_err("Failed to set %s (result): 0x%x\n", attr->attr.name, result);
> + return -EIO;
> + }
> +
> + if (store_value != NULL)
> + *store_value = value;
> + sysfs_notify(kobj, NULL, attr->attr.name);
> +
> + if (asus_bios_requires_reboot(attr))
> + asus_set_reboot_and_signal_event();
> +
> + return count;
> +}
> +
> +/* Mini-LED mode **************************************************************/
> +static ssize_t mini_led_mode_current_value_show(struct kobject *kobj,
> + struct kobj_attribute *attr, char *buf)
> +{
> + u32 value;
> + int err;
> +
> + err = asus_wmi_get_devstate_dsts(asus_bioscfg.mini_led_dev_id, &value);
> + if (err)
> + return err;
> +
> + value = value & ASUS_MINI_LED_MODE_MASK;
> +
> + /*
> + * Remap the mode values to match previous generation mini-led. The last gen
> + * WMI 0 == off, while on this version WMI 2 ==off (flipped).
> + */
> + if (asus_bioscfg.mini_led_dev_id == ASUS_WMI_DEVID_MINI_LED_MODE2) {
> + switch (value) {
> + case ASUS_MINI_LED_2024_WEAK:
> + value = ASUS_MINI_LED_ON;
> + break;
> + case ASUS_MINI_LED_2024_STRONG:
> + value = ASUS_MINI_LED_STRONG_MODE;
> + break;
> + case ASUS_MINI_LED_2024_OFF:
> + value = ASUS_MINI_LED_OFF;
> + break;
> + }
> + }
> +
> + return sysfs_emit(buf, "%d\n", value);
%u
> +}
> +
> +static ssize_t mini_led_mode_current_value_store(struct kobject *kobj,
> + struct kobj_attribute *attr,
> + const char *buf, size_t count)
> +{
> + int result, err;
> + u32 mode;
> +
> + result = kstrtou32(buf, 10, &mode);
> + if (result)
> + return result;
> +
> + if (asus_bioscfg.mini_led_dev_id == ASUS_WMI_DEVID_MINI_LED_MODE &&
> + mode > ASUS_MINI_LED_ON)
> + return -EINVAL;
> + if (asus_bioscfg.mini_led_dev_id == ASUS_WMI_DEVID_MINI_LED_MODE2 &&
> + mode > ASUS_MINI_LED_STRONG_MODE)
> + return -EINVAL;
> +
> + /*
> + * Remap the mode values so expected behaviour is the same as the last
> + * generation of mini-LED with 0 == off, 1 == on.
> + */
> + if (asus_bioscfg.mini_led_dev_id == ASUS_WMI_DEVID_MINI_LED_MODE2) {
> + switch (mode) {
> + case ASUS_MINI_LED_OFF:
> + mode = ASUS_MINI_LED_2024_OFF;
> + break;
> + case ASUS_MINI_LED_ON:
> + mode = ASUS_MINI_LED_2024_WEAK;
> + break;
> + case ASUS_MINI_LED_STRONG_MODE:
> + mode = ASUS_MINI_LED_2024_STRONG;
> + break;
> + }
> + }
> +
> + err = asus_wmi_set_devstate(asus_bioscfg.mini_led_dev_id, mode, &result);
> + if (err) {
> + pr_warn("Failed to set mini-LED: %d\n", err);
> + return err;
> + }
> +
> + if (result > 1) {
> + pr_warn("Failed to set mini-LED mode (result): 0x%x\n", result);
> + return -EIO;
> + }
> +
> + sysfs_notify(kobj, NULL, attr->attr.name);
> +
> + return count;
> +}
> +
> +static ssize_t mini_led_mode_possible_values_show(struct kobject *kobj,
> + struct kobj_attribute *attr, char *buf)
> +{
> + switch (asus_bioscfg.mini_led_dev_id) {
> + case ASUS_WMI_DEVID_MINI_LED_MODE:
> + return sysfs_emit(buf, "0;1\n");
> + case ASUS_WMI_DEVID_MINI_LED_MODE2:
> + return sysfs_emit(buf, "0;1;2\n");
> + }
> +
> + return sysfs_emit(buf, "0\n");
> +}
> +
> +ATTR_GROUP_ENUM_CUSTOM(mini_led_mode, "mini_led_mode", "Set the mini-LED backlight mode");
> +
> +static ssize_t gpu_mux_mode_current_value_store(struct kobject *kobj,
> + struct kobj_attribute *attr,
> + const char *buf, size_t count)
> +{
> + int result, err;
> + u32 optimus;
> +
> + err = kstrtou32(buf, 10, &optimus);
> + if (err)
> + return err;
> +
> + if (optimus > 1)
> + return -EINVAL;
> +
> + if (asus_bioscfg.dgpu_disable_available) {
> + err = asus_wmi_get_devstate_dsts(ASUS_WMI_DEVID_DGPU, &result);
> + if (err)
> + return err;
> + if (err && !optimus) {
How can err be non-zero at this point??? Did you mean result like below?
> + err = -ENODEV;
> + pr_warn("Can not switch MUX to dGPU mode when dGPU is disabled: %d\n", err);
> + return err;
> + }
> + }
> +
> + if (asus_bioscfg.egpu_enable_available) {
> + err = asus_wmi_get_devstate_dsts(ASUS_WMI_DEVID_EGPU, &result);
> + if (err)
> + return err;
> + if (result && !optimus) {
> + err = -ENODEV;
> + pr_warn("Can not switch MUX to dGPU mode when eGPU is enabled: %d\n", err);
> + return err;
> + }
> + }
> +
> + err = asus_wmi_set_devstate(asus_bioscfg.gpu_mux_dev_id, optimus, &result);
> + if (err) {
> + pr_err("%s Failed to set GPU MUX mode: %d\nn", __func__, err);
Never use __func__ for messages shown to normal user.
> + return err;
> + }
> + /* !1 is considered a fail by ASUS */
If the interface is documented with kerneldoc, this is unnecessary
comment. Is 0 also a failure (this differs from >1 checks elsewhere)?
> + if (result != 1) {
> + pr_warn("%s Failed to set GPU MUX mode (result): 0x%x\n", __func__, result);
Ditto.
> + return -EIO;
> + }
> +
> + sysfs_notify(kobj, NULL, attr->attr.name);
> +
> + return count;
> +}
> +WMI_SHOW_INT(gpu_mux_mode_current_value, "%d\n", asus_bioscfg.gpu_mux_dev_id);
> +ATTR_GROUP_BOOL_CUSTOM(gpu_mux_mode, "gpu_mux_mode", "Set the GPU display MUX mode");
> +
> +/*
> + * A user may be required to store the value twice, typcial store first, then
> + * rescan PCI bus to activate power, then store a second time to save correctly.
> + * The reason for this is that an extra code path in the ACPI is enabled when
> + * the device and bus are powered.
> + */
> +static ssize_t dgpu_disable_current_value_store(struct kobject *kobj,
> + struct kobj_attribute *attr,
> + const char *buf, size_t count)
> +{
> + int result, err;
> + u32 disable;
> +
> + result = kstrtou32(buf, 10, &disable);
> + if (result)
> + return result;
You're likely shooting yourself into your foot by not having consistency
in variable naming. Here you store the return value into "result", whereas
you used "err" in the previous function. Try to maintain consistency
across the entire driver.
> +
> + if (disable > 1)
> + return -EINVAL;
> +
> + if (asus_bioscfg.gpu_mux_dev_id) {
> + err = asus_wmi_get_devstate_dsts(asus_bioscfg.gpu_mux_dev_id, &result);
> + if (err)
> + return err;
> + if (!result && disable) {
> + err = -ENODEV;
> + pr_warn("Can not disable dGPU when the MUX is in dGPU mode: %d\n", err);
> + return err;
> + }
> + }
> +
> + err = asus_wmi_set_devstate(ASUS_WMI_DEVID_DGPU, disable, &result);
> + if (err) {
> + pr_warn("Failed to set dgpu disable: %d\n", err);
> + return err;
> + }
> +
> + if (result > 1) {
> + pr_warn("Failed to set dgpu disable (result): 0x%x\n", result);
> + return -EIO;
> + }
> +
> + sysfs_notify(kobj, NULL, attr->attr.name);
> +
> + return count;
> +}
> +WMI_SHOW_INT(dgpu_disable_current_value, "%d\n", ASUS_WMI_DEVID_DGPU);
> +ATTR_GROUP_BOOL_CUSTOM(dgpu_disable, "dgpu_disable", "Disable the dGPU");
> +
> +/* The ACPI call to enable the eGPU also disables the internal dGPU */
> +static ssize_t egpu_enable_current_value_store(struct kobject *kobj,
> + struct kobj_attribute *attr,
> + const char *buf, size_t count)
> +{
> + int result, err;
> + u32 enable;
> +
> + err = kstrtou32(buf, 10, &enable);
> + if (err)
> + return err;
> +
> + if (enable > 1)
> + return -EINVAL;
> +
> + err = asus_wmi_get_devstate_dsts(ASUS_WMI_DEVID_EGPU_CONNECTED, &result);
> + if (err) {
> + pr_warn("Failed to get egpu connection status: %d\n", err);
> + return err;
> + }
> +
> + if (asus_bioscfg.gpu_mux_dev_id) {
> + err = asus_wmi_get_devstate_dsts(asus_bioscfg.gpu_mux_dev_id, &result);
> + if (err) {
> + pr_warn("Failed to get gpu mux status: %d\n", result);
> + return result;
> + }
> + if (!result && enable) {
> + err = -ENODEV;
> + pr_warn("Can not enable eGPU when the MUX is in dGPU mode: %d\n", err);
> + return err;
> + }
> + }
> +
> + err = asus_wmi_set_devstate(ASUS_WMI_DEVID_EGPU, enable, &result);
> + if (err) {
> + pr_warn("Failed to set egpu state: %d\n", err);
> + return err;
> + }
> +
> + if (result > 1) {
> + pr_warn("Failed to set egpu state (retval): 0x%x\n", result);
> + return -EIO;
> + }
> +
> + sysfs_notify(kobj, NULL, attr->attr.name);
> +
> + return count;
> +}
> +WMI_SHOW_INT(egpu_enable_current_value, "%d\n", ASUS_WMI_DEVID_EGPU);
> +ATTR_GROUP_BOOL_CUSTOM(egpu_enable, "egpu_enable", "Enable the eGPU (also disables dGPU)");
> +
> +/* Simple attribute creation */
> +ATTR_GROUP_ENUM_INT_RW(thermal_policy, "thermal_policy", ASUS_WMI_DEVID_THROTTLE_THERMAL_POLICY,
> + 0, 3, "0;1;2", "Set the thermal profile: 0=normal, 1=performance, 2=quiet");
> +ATTR_GROUP_PPT_RW(ppt_pl1_spl, "ppt_pl1_spl", ASUS_WMI_DEVID_PPT_PL1_SPL,
> + cpu_default, 5, cpu_max, 1, "Set the CPU slow package limit");
> +ATTR_GROUP_PPT_RW(ppt_pl2_sppt, "ppt_pl2_sppt", ASUS_WMI_DEVID_PPT_PL2_SPPT,
> + cpu_default, 5, cpu_max, 1, "Set the CPU fast package limit");
> +ATTR_GROUP_PPT_RW(ppt_apu_sppt, "ppt_apu_sppt", ASUS_WMI_DEVID_PPT_APU_SPPT,
> + platform_default, 5, platform_max, 1, "Set the CPU slow package limit");
> +ATTR_GROUP_PPT_RW(ppt_platform_sppt, "ppt_platform_sppt", ASUS_WMI_DEVID_PPT_PLAT_SPPT,
> + platform_default, 5, platform_max, 1, "Set the CPU slow package limit");
> +ATTR_GROUP_PPT_RW(ppt_fppt, "ppt_fppt", ASUS_WMI_DEVID_PPT_FPPT,
> + cpu_default, 5, cpu_max, 1, "Set the CPU slow package limit");
> +
> +ATTR_GROUP_PPT_RW(nv_dynamic_boost, "nv_dynamic_boost", ASUS_WMI_DEVID_NV_DYN_BOOST,
> + nv_boost_default, 5, nv_boost_max, 1, "Set the Nvidia dynamic boost limit");
> +ATTR_GROUP_PPT_RW(nv_temp_target, "nv_temp_target", ASUS_WMI_DEVID_NV_THERM_TARGET,
> + nv_temp_default, 75, nv_temp_max, 1, "Set the Nvidia max thermal limit");
> +
> +ATTR_GROUP_ENUM_INT_RO(charge_mode, "charge_mode", ASUS_WMI_DEVID_CHARGE_MODE,
> + "0;1;2", "Show the current mode of charging");
> +ATTR_GROUP_BOOL_RW(boot_sound, "boot_sound", ASUS_WMI_DEVID_BOOT_SOUND,
> + "Set the boot POST sound");
> +ATTR_GROUP_BOOL_RW(mcu_powersave, "mcu_powersave", ASUS_WMI_DEVID_MCU_POWERSAVE,
> + "Set MCU powersaving mode");
> +ATTR_GROUP_BOOL_RW(panel_od, "panel_overdrive", ASUS_WMI_DEVID_PANEL_OD,
> + "Set the panel refresh overdrive");
> +ATTR_GROUP_BOOL_RW(panel_hd_mode, "panel_hd_mode", ASUS_WMI_DEVID_PANEL_HD,
> + "Set the panel HD mode to UHD<0> or FHD<1>");
> +ATTR_GROUP_BOOL_RO(egpu_connected, "egpu_connected", ASUS_WMI_DEVID_EGPU_CONNECTED,
> + "Show the eGPU connection status");
> +
> +static int asus_fw_attr_add(void)
> +{
> + int ret;
> +
> + ret = fw_attributes_class_get(&fw_attr_class);
> + if (ret)
> + goto fail_class_created;
> + else
Unnecessary else since you goto in the if block.
> + asus_bioscfg.fw_attr_dev = device_create(fw_attr_class, NULL,
> + MKDEV(0, 0), NULL, "%s", DRIVER_NAME);
> +
> + if (IS_ERR(asus_bioscfg.fw_attr_dev)) {
> + ret = PTR_ERR(asus_bioscfg.fw_attr_dev);
> + goto fail_class_created;
> + }
> +
> + asus_bioscfg.fw_attr_kset = kset_create_and_add("attributes", NULL,
> + &asus_bioscfg.fw_attr_dev->kobj);
> + if (!asus_bioscfg.fw_attr_dev) {
> + ret = -ENOMEM;
> + pr_debug("Failed to create and add attributes\n");
> + goto err_destroy_classdev;
> + }
> +
> + /* Add any firmware_attributes required */
Unnecessary comment.
> + ret = sysfs_create_file(&asus_bioscfg.fw_attr_kset->kobj, &pending_reboot.attr);
> + if (ret) {
> + pr_warn("Failed to create sysfs level attributes\n");
> + goto fail_class_created;
> + }
> +
> + // TODO: logging
?? Logging of what if you intend to keep this message around.
> + asus_bioscfg.mini_led_dev_id = 0;
> + if (asus_wmi_is_present(ASUS_WMI_DEVID_MINI_LED_MODE)) {
> + asus_bioscfg.mini_led_dev_id = ASUS_WMI_DEVID_MINI_LED_MODE;
> + sysfs_create_group(&asus_bioscfg.fw_attr_kset->kobj, &mini_led_mode_attr_group);
> + } else if (asus_wmi_is_present(ASUS_WMI_DEVID_MINI_LED_MODE2)) {
> + asus_bioscfg.mini_led_dev_id = ASUS_WMI_DEVID_MINI_LED_MODE2;
> + sysfs_create_group(&asus_bioscfg.fw_attr_kset->kobj, &mini_led_mode_attr_group);
> + }
> +
> + if (asus_wmi_is_present(ASUS_WMI_DEVID_GPU_MUX)) {
> + asus_bioscfg.gpu_mux_dev_id = ASUS_WMI_DEVID_GPU_MUX;
> + sysfs_create_group(&asus_bioscfg.fw_attr_kset->kobj, &gpu_mux_mode_attr_group);
> + } else if (asus_wmi_is_present(ASUS_WMI_DEVID_GPU_MUX_VIVO)) {
> + asus_bioscfg.gpu_mux_dev_id = ASUS_WMI_DEVID_GPU_MUX_VIVO;
> + sysfs_create_group(&asus_bioscfg.fw_attr_kset->kobj, &gpu_mux_mode_attr_group);
> + }
> +
> + if (asus_wmi_is_present(ASUS_WMI_DEVID_DGPU)) {
> + asus_bioscfg.dgpu_disable_available = true;
> + sysfs_create_group(&asus_bioscfg.fw_attr_kset->kobj, &dgpu_disable_attr_group);
> + }
> + if (asus_wmi_is_present(ASUS_WMI_DEVID_EGPU)) {
> + asus_bioscfg.egpu_enable_available = true;
> + sysfs_create_group(&asus_bioscfg.fw_attr_kset->kobj, &egpu_enable_attr_group);
> + }
> + if (asus_wmi_is_present(ASUS_WMI_DEVID_EGPU_CONNECTED))
> + sysfs_create_group(&asus_bioscfg.fw_attr_kset->kobj, &egpu_connected_attr_group);
> +
> + if (asus_wmi_is_present(ASUS_WMI_DEVID_THROTTLE_THERMAL_POLICY))
> + sysfs_create_group(&asus_bioscfg.fw_attr_kset->kobj, &thermal_policy_attr_group);
> + if (asus_wmi_is_present(ASUS_WMI_DEVID_PPT_PL1_SPL))
> + sysfs_create_group(&asus_bioscfg.fw_attr_kset->kobj, &ppt_pl1_spl_attr_group);
> + if (asus_wmi_is_present(ASUS_WMI_DEVID_PPT_PL2_SPPT))
> + sysfs_create_group(&asus_bioscfg.fw_attr_kset->kobj, &ppt_pl2_sppt_attr_group);
> + if (asus_wmi_is_present(ASUS_WMI_DEVID_PPT_APU_SPPT))
> + sysfs_create_group(&asus_bioscfg.fw_attr_kset->kobj, &ppt_apu_sppt_attr_group);
> + if (asus_wmi_is_present(ASUS_WMI_DEVID_PPT_PLAT_SPPT))
> + sysfs_create_group(&asus_bioscfg.fw_attr_kset->kobj, &ppt_platform_sppt_attr_group);
> + if (asus_wmi_is_present(ASUS_WMI_DEVID_PPT_FPPT))
> + sysfs_create_group(&asus_bioscfg.fw_attr_kset->kobj, &ppt_fppt_attr_group);
> +
> + if (asus_wmi_is_present(ASUS_WMI_DEVID_NV_DYN_BOOST))
> + sysfs_create_group(&asus_bioscfg.fw_attr_kset->kobj, &nv_dynamic_boost_attr_group);
> + if (asus_wmi_is_present(ASUS_WMI_DEVID_NV_THERM_TARGET))
> + sysfs_create_group(&asus_bioscfg.fw_attr_kset->kobj, &nv_temp_target_attr_group);
> +
> + if (asus_wmi_is_present(ASUS_WMI_DEVID_CHARGE_MODE))
> + sysfs_create_group(&asus_bioscfg.fw_attr_kset->kobj, &charge_mode_attr_group);
> + if (asus_wmi_is_present(ASUS_WMI_DEVID_BOOT_SOUND))
> + sysfs_create_group(&asus_bioscfg.fw_attr_kset->kobj, &boot_sound_attr_group);
> + if (asus_wmi_is_present(ASUS_WMI_DEVID_MCU_POWERSAVE))
> + sysfs_create_group(&asus_bioscfg.fw_attr_kset->kobj, &mcu_powersave_attr_group);
> + if (asus_wmi_is_present(ASUS_WMI_DEVID_PANEL_OD))
> + sysfs_create_group(&asus_bioscfg.fw_attr_kset->kobj, &panel_od_attr_group);
> + if (asus_wmi_is_present(ASUS_WMI_DEVID_PANEL_HD))
> + sysfs_create_group(&asus_bioscfg.fw_attr_kset->kobj, &panel_hd_mode_attr_group);
> +
> + return 0;
> +
> +err_destroy_classdev:
> + device_destroy(fw_attr_class, MKDEV(0, 0));
> +
> +fail_class_created:
> + fw_attributes_class_put();
> + return ret;
> +}
> +
> +/* Init / exit ****************************************************************/
> +
> +/* Set up the min/max and defaults for ROG tunables */
> +static void init_rog_tunables(struct rog_tunables *rog)
> +{
> + const char *product;
> + u32 max_boost = NVIDIA_BOOST_MAX;
> + u32 cpu_default = PPT_CPU_LIMIT_DEFAULT;
> + u32 cpu_max = PPT_CPU_LIMIT_MAX;
> + u32 platform_default = PPT_PLATFORM_DEFAULT;
> + u32 platform_max = PPT_PLATFORM_MAX;
> +
> + /*
> + * ASUS product_name contains everything required, e.g,
> + * "ROG Flow X16 GV601VV_GV601VV_00185149B"
> + */
> + product = dmi_get_system_info(DMI_PRODUCT_NAME);
> +
> + if (strstr(product, "GA402R")) {
> + cpu_default = 125;
> + } else if (strstr(product, "13QY")) {
> + cpu_max = 250;
> + } else if (strstr(product, "X13")) {
> + cpu_max = 75;
> + cpu_default = 50;
> + } else if (strstr(product, "RC71")) {
> + cpu_max = 50;
> + cpu_default = 30;
> + } else if (strstr(product, "G814")
> + || strstr(product, "G614")
> + || strstr(product, "G834")
> + || strstr(product, "G634")) {
> + cpu_max = 175;
> + } else if (strstr(product, "GA402X")
> + || strstr(product, "GA403")
> + || strstr(product, "FA507N")
> + || strstr(product, "FA507X")
> + || strstr(product, "FA707N")
> + || strstr(product, "FA707X")) {
> + cpu_max = 90;
> + }
> +
> + if (strstr(product, "GZ301ZE"))
> + max_boost = 5;
> + else if (strstr(product, "FX507ZC4"))
> + max_boost = 15;
> + else if (strstr(product, "GU605"))
> + max_boost = 20;
> +
> + /* ensure defaults for tunables */
> + rog->cpu_default = cpu_default;
> + rog->cpu_max = cpu_max;
> +
> + rog->platform_default = platform_default;
> + rog->platform_max = platform_max;
> +
> + rog->ppt_pl1_spl = cpu_default;
> + rog->ppt_pl2_sppt = cpu_default;
> + rog->ppt_apu_sppt = cpu_default;
> +
> + rog->ppt_platform_sppt = platform_default;
> + rog->ppt_fppt = platform_default;
> +
> + rog->nv_boost_default = NVIDIA_BOOST_MAX;
> + rog->nv_boost_max = max_boost;
> + rog->nv_dynamic_boost = NVIDIA_BOOST_MIN;
> +
> + rog->nv_temp_default = NVIDIA_TEMP_MAX;
> + rog->nv_temp_max = NVIDIA_TEMP_MAX;
> + rog->nv_temp_target = NVIDIA_TEMP_MIN;
> +
> +}
> +
> +static int __init asus_fw_init(void)
> +{
> + int err;
> +
> + mutex_lock(&asus_bioscfg.mutex);
> +
> + asus_bioscfg.rog_tunables = kzalloc(sizeof(struct rog_tunables), GFP_KERNEL);
> + if (!asus_bioscfg.rog_tunables) {
> + mutex_unlock(&asus_bioscfg.mutex);
> + return -ENOMEM;
> + }
> + init_rog_tunables(asus_bioscfg.rog_tunables);
> +
> + err = asus_fw_attr_add();
> + mutex_unlock(&asus_bioscfg.mutex);
> + if (err)
> + return err;
> +
> + return 0;
> +}
> +
> +static void __exit asus_fw_exit(void)
> +{
> + mutex_lock(&asus_bioscfg.mutex);
> +
> + sysfs_remove_file(&asus_bioscfg.fw_attr_kset->kobj, &pending_reboot.attr);
> + kset_unregister(asus_bioscfg.fw_attr_kset);
> + device_destroy(fw_attr_class, MKDEV(0, 0));
> + fw_attributes_class_put();
> +
> + mutex_unlock(&asus_bioscfg.mutex);
> +}
> +
> +module_init(asus_fw_init);
> +module_exit(asus_fw_exit);
> diff --git a/drivers/platform/x86/asus-bioscfg.h b/drivers/platform/x86/asus-bioscfg.h
> new file mode 100644
> index 000000000000..403563c25f53
> --- /dev/null
> +++ b/drivers/platform/x86/asus-bioscfg.h
> @@ -0,0 +1,243 @@
> +/* SPDX-License-Identifier: GPL-2.0
> + *
> + * Definitions for kernel modules using asus-bios driver
> + *
> + * Copyright (c) 2024 Luke Jones <luke@ljones.dev>
> + */
> +
> +#ifndef _ASUS_BIOSCFG_H_
> +#define _ASUS_BIOSCFG_H_
> +
> +#include "firmware_attributes_class.h"
> +#include <linux/types.h>
> +
> +#define DRIVER_NAME "asus-bioscfg"
> +
> +static ssize_t attr_int_store(struct kobject *kobj, struct kobj_attribute *attr,
> + const char *buf, size_t count,
> + u32 min, u32 max, u32 *store_value, u32 wmi_dev);
> +
> +
> +static ssize_t int_type_show(struct kobject *kobj, struct kobj_attribute *attr,
> + char *buf)
> +{
> + return sysfs_emit(buf, "integer\n");
> +}
> +
> +static ssize_t enum_type_show(struct kobject *kobj, struct kobj_attribute *attr,
> + char *buf)
> +{
> + return sysfs_emit(buf, "enumeration\n");
> +}
> +
> +#define __ASUS_ATTR_RO(_func, _name) { \
> + .attr = { .name = __stringify(_name), .mode = 0444 }, \
> + .show = _func##_##_name##_show, \
> +}
> +
> +#define __ASUS_ATTR_RO_AS(_name, _show) { \
> + .attr = { .name = __stringify(_name), .mode = 0444 }, \
> + .show = _show, \
> +}
> +
> +#define __ASUS_ATTR_RW(_func, _name) __ATTR(_name, 0644, \
> + _func##_##_name##_show, _func##_##_name##_store)
> +
> +#define __WMI_STORE_INT(_attr, _min, _max, _wmi) \
> +static ssize_t _attr##_store(struct kobject *kobj, \
> + struct kobj_attribute *attr, \
> + const char *buf, size_t count) \
> +{ \
> + return attr_int_store(kobj, attr, buf, count, _min, _max, NULL, _wmi); \
> +}
> +
> +#define WMI_SHOW_INT(_attr, _fmt, _wmi) \
> +static ssize_t _attr##_show(struct kobject *kobj, \
> + struct kobj_attribute *attr, char *buf) \
> +{ \
> + u32 result; \
> + int err; \
> + err = asus_wmi_get_devstate_dsts(_wmi, &result); \
> + if (err) \
> + return err; \
> + return sysfs_emit(buf, _fmt, \
> + result & ~ASUS_WMI_DSTS_PRESENCE_BIT); \
> +}
> +
> +/* Create functions and attributes for use in other macros or on their own */
> +
> +#define __ROG_TUNABLE_RW(_attr, _min, _max, _wmi) \
> +static ssize_t _attr##_current_value_store(struct kobject *kobj, \
> + struct kobj_attribute *attr, \
> + const char *buf, size_t count) \
> +{ \
> + return attr_int_store(kobj, attr, buf, count, \
> + _min, asus_bioscfg.rog_tunables->_max, \
> + &asus_bioscfg.rog_tunables->_attr, _wmi); \
> +} \
> +static ssize_t _attr##_current_value_show(struct kobject *kobj, \
> + struct kobj_attribute *attr, char *buf) \
> +{ \
> + return sysfs_emit(buf, "%u\n", asus_bioscfg.rog_tunables->_attr);\
> +} \
> +static struct kobj_attribute attr_##_attr##_current_value = \
> + __ASUS_ATTR_RW(_attr, current_value)
> +
> +#define __ROG_TUNABLE_SHOW(_prop, _attrname, _val) \
> +static ssize_t _attrname##_##_prop##_show(struct kobject *kobj, \
> + struct kobj_attribute *attr, char *buf) \
> +{ \
> + return sysfs_emit(buf, "%d\n", asus_bioscfg.rog_tunables->_val);\
> +} \
> +static struct kobj_attribute attr_##_attrname##_##_prop = \
> + __ASUS_ATTR_RO(_attrname, _prop)
> +
> +#define __ATTR_CURRENT_INT_RO(_attr, _wmi) \
> +WMI_SHOW_INT(_attr##_current_value, "%d\n", _wmi); \
> +static struct kobj_attribute attr_##_attr##_current_value = \
> + __ASUS_ATTR_RO(_attr, current_value)
> +
> +#define __ATTR_CURRENT_INT_RW(_attr, _minv, _maxv, _wmi) \
> +__WMI_STORE_INT(_attr##_current_value, _minv, _maxv, _wmi); \
> +WMI_SHOW_INT(_attr##_current_value, "%d\n", _wmi); \
> +static struct kobj_attribute attr_##_attr##_current_value = \
> + __ASUS_ATTR_RW(_attr, current_value)
> +
> +/* Shows a formatted static variable */
> +#define __ATTR_SHOW_FMT(_prop, _attrname, _fmt, _val) \
> +static ssize_t _attrname##_##_prop##_show(struct kobject *kobj, \
> + struct kobj_attribute *attr, char *buf) \
> +{ \
> + return sysfs_emit(buf, _fmt, _val); \
> +} \
> +static struct kobj_attribute attr_##_attrname##_##_prop = \
> + __ASUS_ATTR_RO(_attrname, _prop)
> +
> +/* Int style min/max range, base macro. Requires current_value show&|store */
> +#define __ATTR_GROUP_INT(_attrname, _fsname, _default, \
> + _min, _max, _incstep, _dispname)\
> +__ATTR_SHOW_FMT(default_value, _attrname, "%d\n", _default); \
> +__ATTR_SHOW_FMT(min_value, _attrname, "%d\n", _min); \
> +__ATTR_SHOW_FMT(max_value, _attrname, "%d\n", _max); \
> +__ATTR_SHOW_FMT(scalar_increment, _attrname, "%d\n", _incstep); \
> +__ATTR_SHOW_FMT(display_name, _attrname, "%s\n", _dispname); \
> +static struct kobj_attribute attr_##_attrname##_type = \
> + __ASUS_ATTR_RO_AS(type, int_type_show); \
> +static struct attribute *_attrname##_attrs[] = { \
> + &attr_##_attrname##_current_value.attr, \
> + &attr_##_attrname##_default_value.attr, \
> + &attr_##_attrname##_min_value.attr, \
> + &attr_##_attrname##_max_value.attr, \
> + &attr_##_attrname##_scalar_increment.attr, \
> + &attr_##_attrname##_display_name.attr, \
> + &attr_##_attrname##_type.attr, \
> + NULL \
> +}; \
> +static const struct attribute_group _attrname##_attr_group = { \
> + .name = _fsname, \
> + .attrs = _attrname##_attrs \
> +}
> +
> +/* Boolean style enumeration, base macro. Requires adding show/store */
> +#define __ATTR_GROUP_ENUM(_attrname, _fsname, _possible, _dispname) \
> +__ATTR_SHOW_FMT(display_name, _attrname, "%s\n", _dispname); \
> +__ATTR_SHOW_FMT(possible_values, _attrname, "%s\n", _possible); \
> +static struct kobj_attribute attr_##_attrname##_type = \
> + __ASUS_ATTR_RO_AS(type, enum_type_show); \
> +static struct attribute *_attrname##_attrs[] = { \
> + &attr_##_attrname##_current_value.attr, \
> + &attr_##_attrname##_display_name.attr, \
> + &attr_##_attrname##_possible_values.attr, \
> + &attr_##_attrname##_type.attr, \
> + NULL \
> +}; \
> +static const struct attribute_group _attrname##_attr_group = { \
> + .name = _fsname, \
> + .attrs = _attrname##_attrs \
> +}
> +
> +#define ATTR_GROUP_BOOL_RO(_attrname, _fsname, _wmi, _dispname) \
> +do { \
> + __ATTR_CURRENT_INT_RO(_attrname, _wmi); \
> + __ATTR_GROUP_ENUM(_attrname, _fsname, "0;1", _dispname);\
> +} while (0)
> +
> +#define ATTR_GROUP_BOOL_RW(_attrname, _fsname, _wmi, _dispname) \
> +do { \
> + __ATTR_CURRENT_INT_RW(_attrname, 0, 1, _wmi); \
> + __ATTR_GROUP_ENUM(_attrname, _fsname, "0;1", _dispname);\
> +} while (0)
> +
> +/*
> + * Requires <name>_current_value_show(), <name>_current_value_show()
> + */
> +#define ATTR_GROUP_BOOL_CUSTOM(_attrname, _fsname, _dispname) \
> +static struct kobj_attribute attr_##_attrname##_current_value = \
> + __ASUS_ATTR_RW(_attrname, current_value); \
> +__ATTR_GROUP_ENUM(_attrname, _fsname, "0;1", _dispname)
> +
> +#define ATTR_GROUP_ENUM_INT_RO(_attrname, _fsname, _wmi, \
> + _possible, _dispname) \
> +do { \
> + __ATTR_CURRENT_INT_RO(_attrname, _wmi); \
> + __ATTR_GROUP_ENUM(_attrname, _fsname, _possible, _dispname); \
> +} while (0)
> +
> +#define ATTR_GROUP_ENUM_INT_RW(_attrname, _fsname, _wmi, _min, \
> + _max, _possible, _dispname) \
> +do { \
> + __ATTR_CURRENT_INT_RW(_attrname, _min, _max, _wmi); \
> + __ATTR_GROUP_ENUM(_attrname, _fsname, _possible, _dispname); \
> +} while (0)
> +
> +/*
> + * Requires <name>_current_value_show(), <name>_current_value_show()
> + * and <name>_possible_values_show()
> + */
> +#define ATTR_GROUP_ENUM_CUSTOM(_attrname, _fsname, _dispname) \
> +__ATTR_SHOW_FMT(display_name, _attrname, "%s\n", _dispname); \
> +static struct kobj_attribute attr_##_attrname##_current_value = \
> + __ASUS_ATTR_RW(_attrname, current_value); \
> +static struct kobj_attribute attr_##_attrname##_possible_values = \
> + __ASUS_ATTR_RO(_attrname, possible_values); \
> +static struct kobj_attribute attr_##_attrname##_type = \
> + __ASUS_ATTR_RO_AS(type, enum_type_show); \
> +static struct attribute *_attrname##_attrs[] = { \
> + &attr_##_attrname##_current_value.attr, \
> + &attr_##_attrname##_display_name.attr, \
> + &attr_##_attrname##_possible_values.attr, \
> + &attr_##_attrname##_type.attr, \
> + NULL \
> +}; \
> +static const struct attribute_group _attrname##_attr_group = { \
> + .name = _fsname, \
> + .attrs = _attrname##_attrs \
> +}
> +
> +/* ROG PPT attributes need a little different in setup */
> +#define ATTR_GROUP_PPT_RW(_attrname, _fsname, _wmi, _default, \
> + _min, _max, _incstep, _dispname) \
> +__ROG_TUNABLE_RW(_attrname, _min, _max, _wmi); \
> +__ROG_TUNABLE_SHOW(default_value, _attrname, _default); \
> +__ATTR_SHOW_FMT(min_value, _attrname, "%d\n", _min); \
> +__ROG_TUNABLE_SHOW(max_value, _attrname, _max); \
> +__ATTR_SHOW_FMT(scalar_increment, _attrname, "%d\n", _incstep); \
> +__ATTR_SHOW_FMT(display_name, _attrname, "%s\n", _dispname); \
> +static struct kobj_attribute attr_##_attrname##_type = \
> + __ASUS_ATTR_RO_AS(type, int_type_show); \
> +static struct attribute *_attrname##_attrs[] = { \
> + &attr_##_attrname##_current_value.attr, \
> + &attr_##_attrname##_default_value.attr, \
> + &attr_##_attrname##_min_value.attr, \
> + &attr_##_attrname##_max_value.attr, \
> + &attr_##_attrname##_scalar_increment.attr, \
> + &attr_##_attrname##_display_name.attr, \
> + &attr_##_attrname##_type.attr, \
> + NULL \
> +}; \
> +static const struct attribute_group _attrname##_attr_group = { \
> + .name = _fsname, \
> + .attrs = _attrname##_attrs \
> +}
> +
> +#endif /* _ASUS_BIOSCFG_H_ */
> diff --git a/drivers/platform/x86/asus-wmi.c b/drivers/platform/x86/asus-wmi.c
> index 2b968003cb9b..3f1998638fea 100644
> --- a/drivers/platform/x86/asus-wmi.c
> +++ b/drivers/platform/x86/asus-wmi.c
> @@ -529,12 +529,28 @@ static int asus_wmi_get_devstate(struct asus_wmi *asus, u32 dev_id, u32 *retval)
> return 0;
> }
>
> -static int asus_wmi_set_devstate(u32 dev_id, u32 ctrl_param,
> +int asus_wmi_get_devstate_dsts(u32 dev_id, u32 *retval)
> +{
> + int err;
> +
> + err = asus_wmi_evaluate_method(ASUS_WMI_METHODID_DSTS, dev_id, 0, retval);
> + if (err)
> + return err;
> +
> + if (*retval == ~0)
> + return -ENODEV;
> +
> + return 0;
> +}
> +EXPORT_SYMBOL_GPL(asus_wmi_get_devstate_dsts);
> +
> +int asus_wmi_set_devstate(u32 dev_id, u32 ctrl_param,
> u32 *retval)
Should fit to one line just fine.
As general feel of the readiness of this code, I suspect there were many
more problems which I failed to notice :-(.
--
i.
> {
> return asus_wmi_evaluate_method(ASUS_WMI_METHODID_DEVS, dev_id,
> ctrl_param, retval);
> }
> +EXPORT_SYMBOL_GPL(asus_wmi_set_devstate);
>
> /* Helper for special devices with magic return codes */
> static int asus_wmi_get_devstate_bits(struct asus_wmi *asus,
> diff --git a/include/linux/platform_data/x86/asus-wmi.h b/include/linux/platform_data/x86/asus-wmi.h
> index 74b32e1d6735..889336a932fb 100644
> --- a/include/linux/platform_data/x86/asus-wmi.h
> +++ b/include/linux/platform_data/x86/asus-wmi.h
> @@ -67,6 +67,7 @@
> #define ASUS_WMI_DEVID_THROTTLE_THERMAL_POLICY 0x00120075
>
> /* Misc */
> +#define ASUS_WMI_DEVID_PANEL_HD 0x0005001C
> #define ASUS_WMI_DEVID_PANEL_OD 0x00050019
> #define ASUS_WMI_DEVID_CAMERA 0x00060013
> #define ASUS_WMI_DEVID_LID_FLIP 0x00060062
> @@ -152,8 +153,18 @@
> #define ASUS_WMI_DSTS_LIGHTBAR_MASK 0x0000000F
>
> #if IS_REACHABLE(CONFIG_ASUS_WMI)
> +int asus_wmi_get_devstate_dsts(u32 dev_id, u32 *retval);
> +int asus_wmi_set_devstate(u32 dev_id, u32 ctrl_param, u32 *retval);
> int asus_wmi_evaluate_method(u32 method_id, u32 arg0, u32 arg1, u32 *retval);
> #else
> +static int asus_wmi_get_devstate_dsts(u32 dev_id, u32 *retval)
> +{
> + return -ENODEV;
> +}
> +static int asus_wmi_set_devstate(u32 dev_id, u32 ctrl_param, u32 *retval)
> +{
> + return -ENODEV;
> +}
> static inline int asus_wmi_evaluate_method(u32 method_id, u32 arg0, u32 arg1,
> u32 *retval)
> {
>
^ permalink raw reply [flat|nested] 22+ messages in thread* Re: [PATCH 1/5] platform/x86 asus-bioscfg: move existing tunings to asus-bioscfg module
2024-07-16 9:45 ` Ilpo Järvinen
@ 2024-07-16 10:41 ` Luke Jones
2024-07-16 11:06 ` Ilpo Järvinen
0 siblings, 1 reply; 22+ messages in thread
From: Luke Jones @ 2024-07-16 10:41 UTC (permalink / raw)
To: Ilpo Järvinen
Cc: platform-driver-x86, corentin.chary, Hans de Goede,
Mario Limonciello, LKML
On Tue, 16 Jul 2024, at 9:45 PM, Ilpo Järvinen wrote:
> On Tue, 16 Jul 2024, Luke D. Jones wrote:
>
> > The fw_attributes_class provides a much cleaner interface to all of the
> > attributes introduced to asus-wmi. This patch moves all of these extra
> > attributes over to fw_attributes_class, and shifts the bulk of these
> > definitions to a new kernel module to reduce the clutter of asus-wmi
> > with the intention of deprecating the asus-wmi attributes in future.
> >
> > The work applies only to WMI methods which don't have a clearly defined
> > place within the sysfs and as a result ended up lumped together in
> > /sys/devices/platform/asus-nb-wmi/ with no standard API.
> >
> > Where possible the fw attrs now implement defaults, min, max, scalar,
> > choices, etc. As en example dgpu_disable becomes:
> >
> > /sys/class/firmware-attributes/asus-bioscfg/attributes/dgpu_disable/
> > ├── current_value
> > ├── display_name
> > ├── possible_values
> > └── type
> >
> > as do other attributes.
> >
> > Signed-off-by: Luke D. Jones <luke@ljones.dev>
> > ---
> > drivers/platform/x86/Kconfig | 14 +
> > drivers/platform/x86/Makefile | 1 +
> > drivers/platform/x86/asus-bioscfg.c | 666 +++++++++++++++++++++
> > drivers/platform/x86/asus-bioscfg.h | 243 ++++++++
> > drivers/platform/x86/asus-wmi.c | 18 +-
> > include/linux/platform_data/x86/asus-wmi.h | 11 +
> > 6 files changed, 952 insertions(+), 1 deletion(-)
> > create mode 100644 drivers/platform/x86/asus-bioscfg.c
> > create mode 100644 drivers/platform/x86/asus-bioscfg.h
> >
> > diff --git a/drivers/platform/x86/Kconfig b/drivers/platform/x86/Kconfig
> > index 665fa9524986..b4a5a5bec7f3 100644
> > --- a/drivers/platform/x86/Kconfig
> > +++ b/drivers/platform/x86/Kconfig
> > @@ -265,6 +265,18 @@ config ASUS_WIRELESS
> > If you choose to compile this driver as a module the module will be
> > called asus-wireless.
> >
> > +config ASUS_BIOS
> > + tristate "ASUS BIOS Driver"
> > + depends on ACPI_WMI
> > + depends on ASUS_WMI
> > + select FW_ATTR_CLASS
> > + help
> > + Say Y here if you have a WMI aware Asus laptop and would like to use the
> > + firmware_attributes API.
> > +
> > + To compile this driver as a module, choose M here: the module will
> > + be called asus-bios.
> > +
> > config ASUS_WMI
> > tristate "ASUS WMI Driver"
> > depends on ACPI_WMI
> > @@ -276,6 +288,8 @@ config ASUS_WMI
> > depends on HOTPLUG_PCI
> > depends on ACPI_VIDEO || ACPI_VIDEO = n
> > depends on SERIO_I8042 || SERIO_I8042 = n
> > + select ASUS_BIOS
>
> Selecting user visible configs is not a good idea. Also, there
> seems to be circular dependency now between ASUS_BIOS & ASUS_WMI ?
Is "selects" the same as "depends"?
I did just remove:
select ASUS_WMI_BIOS
which should not be there at all.
ASUS_BIOS does need ASUS_WMI. And I'd like ASUS_BIOS to be selected by defualt, is this not the right way to do that?
>
> > + select ASUS_WMI_BIOS
> > select INPUT_SPARSEKMAP
> > select LEDS_CLASS
> > select NEW_LEDS
> > diff --git a/drivers/platform/x86/Makefile b/drivers/platform/x86/Makefile
> > index e1b142947067..d9b5b3f3b241 100644
> > --- a/drivers/platform/x86/Makefile
> > +++ b/drivers/platform/x86/Makefile
> > @@ -32,6 +32,7 @@ obj-$(CONFIG_APPLE_GMUX) += apple-gmux.o
> > # ASUS
> > obj-$(CONFIG_ASUS_LAPTOP) += asus-laptop.o
> > obj-$(CONFIG_ASUS_WIRELESS) += asus-wireless.o
> > +obj-$(CONFIG_ASUS_BIOS) += asus-bios.o
> > obj-$(CONFIG_ASUS_WMI) += asus-wmi.o
> > obj-$(CONFIG_ASUS_NB_WMI) += asus-nb-wmi.o
> > obj-$(CONFIG_ASUS_TF103C_DOCK) += asus-tf103c-dock.o
> > diff --git a/drivers/platform/x86/asus-bioscfg.c b/drivers/platform/x86/asus-bioscfg.c
> > new file mode 100644
> > index 000000000000..0b34e727aab4
> > --- /dev/null
> > +++ b/drivers/platform/x86/asus-bioscfg.c
> > @@ -0,0 +1,666 @@
> > +// SPDX-License-Identifier: GPL-2.0-or-later
> > +/*
> > + * Asus BIOS attributes driver
> > + *
> > + * Copyright(C) 2010 Intel Corporation.
> > + * Copyright(C) 2024-2024 Luke Jones <luke@ljones.dev>
> > + */
> > +
> > +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
> > +
> > +#include <linux/platform_data/x86/asus-wmi.h>
> > +#include <linux/errno.h>
> > +#include <linux/fs.h>
> > +#include <linux/types.h>
> > +#include <linux/dmi.h>
> > +#include <linux/device.h>
> > +#include <linux/kmod.h>
> > +#include <linux/kobject.h>
> > +#include <linux/module.h>
> > +#include <linux/mutex.h>
> > +#include <linux/kernel.h>
> > +#include "asus-bios.h"
> > +#include "firmware_attributes_class.h"
> > +
> > +MODULE_AUTHOR("Luke Jones <luke@ljones.dev>");
> > +MODULE_DESCRIPTION("ASUS BIOS Configuration Driver");
> > +MODULE_LICENSE("GPL");
>
> The usual place for these is at the end of file
Thanks. There is quite a mix of top/bottom for this. As I was going by other code examples the convention I ended up on is top. I'll move to bottom if that is where they really are supposed to be.
>
> > +#define ASUS_NB_WMI_EVENT_GUID "0B3CBB35-E3C2-45ED-91C2-4C5A6D195D1C"
> > +
> > +MODULE_ALIAS("wmi:"ASUS_NB_WMI_EVENT_GUID);
>
> Ditto.
Ack
>
> > +#define ASUS_MINI_LED_MODE_MASK 0x03
> > +/* Standard modes for devices with only on/off */
> > +#define ASUS_MINI_LED_OFF 0x00
> > +#define ASUS_MINI_LED_ON 0x01
> > +/* New mode on some devices, define here to clarify remapping later */
> > +#define ASUS_MINI_LED_STRONG_MODE 0x02
> > +/* New modes for devices with 3 mini-led mode types */
> > +#define ASUS_MINI_LED_2024_WEAK 0x00
> > +#define ASUS_MINI_LED_2024_STRONG 0x01
> > +#define ASUS_MINI_LED_2024_OFF 0x02
> > +
> > +/* Default limits for tunables available on ASUS ROG laptops */
> > +#define PPT_CPU_LIMIT_MIN 5
> > +#define PPT_CPU_LIMIT_MAX 150
> > +#define PPT_CPU_LIMIT_DEFAULT 80
> > +#define PPT_PLATFORM_MIN 5
> > +#define PPT_PLATFORM_MAX 100
> > +#define PPT_PLATFORM_DEFAULT 80
> > +#define NVIDIA_BOOST_MIN 5
> > +#define NVIDIA_BOOST_MAX 25
> > +#define NVIDIA_TEMP_MIN 75
> > +#define NVIDIA_TEMP_MAX 87
> > +
> > +/* Tunables provided by ASUS for gaming laptops */
> > +struct rog_tunables {
> > + u32 cpu_default;
> > + u32 cpu_max;
> > +
> > + u32 platform_default;
> > + u32 platform_max;
> > +
> > + u32 ppt_pl1_spl; // cpu
> > + u32 ppt_pl2_sppt; // cpu
> > + u32 ppt_apu_sppt; // plat
> > + u32 ppt_platform_sppt; // plat
> > + u32 ppt_fppt; // cpu
> > +
> > + u32 nv_boost_default;
> > + u32 nv_boost_max;
> > + u32 nv_dynamic_boost;
> > +
> > + u32 nv_temp_default;
> > + u32 nv_temp_max;
> > + u32 nv_temp_target;
> > +};
> > +
> > +static const struct class *fw_attr_class;
> > +
> > +struct asus_bios_priv {
> > + struct device *fw_attr_dev;
> > + struct kset *fw_attr_kset;
> > +
> > + struct rog_tunables *rog_tunables;
> > + u32 mini_led_dev_id;
> > + u32 gpu_mux_dev_id;
> > + bool dgpu_disable_available;
> > + bool egpu_enable_available;
> > +
> > + struct mutex mutex;
> > +} asus_bioscfg = {
> > + .mutex = __MUTEX_INITIALIZER(asus_bioscfg.mutex),
>
> Don't try to initialize it on the same go like this.
>
> You might want static too.
Ack both
>
> > +};
> > +
> > +static struct fw_attrs_group {
> > + u32 pending_reboot;
> > +} fw_attrs = {
> > + .pending_reboot = 0,
> > +};
>
> Same here.
It was probably done like this in code I read as a reference. I'll shift to the module init function.
>
> > +
> > +/* WMI helper methods */
> > +static bool asus_wmi_is_present(u32 dev_id)
> > +{
> > + int status = asus_wmi_get_devstate_dsts(dev_id, &retval);
> > + u32 retval;
> > +
> > + pr_debug("%s called (0x%08x), retval: 0x%08x\n", __func__, dev_id, retval);
> > +
> > + return status == 0 && (retval & ASUS_WMI_DSTS_PRESENCE_BIT);
> > +}
> > +
> > +static void asus_set_reboot_and_signal_event(void)
> > +{
> > + fw_attrs.pending_reboot = 1;
> > + kobject_uevent(&asus_bioscfg.fw_attr_dev->kobj, KOBJ_CHANGE);
> > +}
> > +
> > +static ssize_t pending_reboot_show(struct kobject *kobj,
> > + struct kobj_attribute *attr,
> > + char *buf)
> > +{
> > + return sysfs_emit(buf, "%d\n", fw_attrs.pending_reboot);
>
> %u
Ack
> > +}
> > +
> > +static struct kobj_attribute pending_reboot = __ATTR_RO(pending_reboot);
> > +
> > +static bool asus_bios_requires_reboot(struct kobj_attribute *attr)
> > +{
> > + return !strcmp(attr->attr.name, "gpu_mux_mode");
> > + !strcmp(attr->attr.name, "panel_hd_mode");
>
> ???
>
> Semicolon and && confusion here?
Yeah i know, bad rebase I didn't catch.
> > +}
> > +
> > +/*
> > + * Generic store function for use with many ROG tunables
> > + */
> > +static ssize_t attr_int_store(struct kobject *kobj,
> > + struct kobj_attribute *attr,
> > + const char *buf, size_t count,
> > + u32 min, u32 max, u32 *store_value, u32 wmi_dev)
> > +{
> > + int result, value;
> > +
> > + result = kstrtoint(buf, 10, &value);
>
> Is signed value needed, if not, use unsigned variant of kstrto*?
Since the min,max,default can only ever be u32.. Yeah. I'll change that.
>
> > + if (result)
> > + return result;
> > +
> > + if (value < min || value > max)
> > + return -EINVAL;
> > +
> > + asus_wmi_set_devstate(wmi_dev, value, &result);
>
> Type confusion, u32 * vs int pointer being passed.
I miss rust...
>
> > + if (result) {
> > + pr_err("Failed to set %s: %d\n", attr->attr.name, result);
> > + return result;
> > + }
> > +
> > + if (result > 1) {
>
> What's this supposed to mean given you've the type confusion to begin
> with and return on the earlier line if result is non-zero?
>
> Did you mean to capture the return value of asus_wmi_set_devstate() and
> test that in the first if ()?
Yep.. this whole bit is a mess. I've fixed the type mess, and added a comment to clarify the "if (result > 1) {"
(WMI methods return 0 = fail, 1 = success, anything else is error)
> If you make a previously internal function such as asus_wmi_set_devstate()
> EXPORTed, you should document it with kerneldoc so the interface is clear.
I'm not sure how to do this, I'll read up. Also didn't know about it so thanks for the pointer.
> > + pr_err("Failed to set %s (result): 0x%x\n", attr->attr.name, result);
> > + return -EIO;
> > + }
> > +
> > + if (store_value != NULL)
> > + *store_value = value;
> > + sysfs_notify(kobj, NULL, attr->attr.name);
> > +
> > + if (asus_bios_requires_reboot(attr))
> > + asus_set_reboot_and_signal_event();
> > +
> > + return count;
> > +}
> > +
> > +/* Mini-LED mode **************************************************************/
> > +static ssize_t mini_led_mode_current_value_show(struct kobject *kobj,
> > + struct kobj_attribute *attr, char *buf)
> > +{
> > + u32 value;
> > + int err;
> > +
> > + err = asus_wmi_get_devstate_dsts(asus_bioscfg.mini_led_dev_id, &value);
> > + if (err)
> > + return err;
> > +
> > + value = value & ASUS_MINI_LED_MODE_MASK;
> > +
> > + /*
> > + * Remap the mode values to match previous generation mini-led. The last gen
> > + * WMI 0 == off, while on this version WMI 2 ==off (flipped).
> > + */
> > + if (asus_bioscfg.mini_led_dev_id == ASUS_WMI_DEVID_MINI_LED_MODE2) {
> > + switch (value) {
> > + case ASUS_MINI_LED_2024_WEAK:
> > + value = ASUS_MINI_LED_ON;
> > + break;
> > + case ASUS_MINI_LED_2024_STRONG:
> > + value = ASUS_MINI_LED_STRONG_MODE;
> > + break;
> > + case ASUS_MINI_LED_2024_OFF:
> > + value = ASUS_MINI_LED_OFF;
> > + break;
> > + }
> > + }
> > +
> > + return sysfs_emit(buf, "%d\n", value);
>
> %u
Ack
>
> > +}
> > +
> > +static ssize_t mini_led_mode_current_value_store(struct kobject *kobj,
> > + struct kobj_attribute *attr,
> > + const char *buf, size_t count)
> > +{
> > + int result, err;
> > + u32 mode;
> > +
> > + result = kstrtou32(buf, 10, &mode);
> > + if (result)
> > + return result;
> > +
> > + if (asus_bioscfg.mini_led_dev_id == ASUS_WMI_DEVID_MINI_LED_MODE &&
> > + mode > ASUS_MINI_LED_ON)
> > + return -EINVAL;
> > + if (asus_bioscfg.mini_led_dev_id == ASUS_WMI_DEVID_MINI_LED_MODE2 &&
> > + mode > ASUS_MINI_LED_STRONG_MODE)
> > + return -EINVAL;
> > +
> > + /*
> > + * Remap the mode values so expected behaviour is the same as the last
> > + * generation of mini-LED with 0 == off, 1 == on.
> > + */
> > + if (asus_bioscfg.mini_led_dev_id == ASUS_WMI_DEVID_MINI_LED_MODE2) {
> > + switch (mode) {
> > + case ASUS_MINI_LED_OFF:
> > + mode = ASUS_MINI_LED_2024_OFF;
> > + break;
> > + case ASUS_MINI_LED_ON:
> > + mode = ASUS_MINI_LED_2024_WEAK;
> > + break;
> > + case ASUS_MINI_LED_STRONG_MODE:
> > + mode = ASUS_MINI_LED_2024_STRONG;
> > + break;
> > + }
> > + }
> > +
> > + err = asus_wmi_set_devstate(asus_bioscfg.mini_led_dev_id, mode, &result);
> > + if (err) {
> > + pr_warn("Failed to set mini-LED: %d\n", err);
> > + return err;
> > + }
> > +
> > + if (result > 1) {
> > + pr_warn("Failed to set mini-LED mode (result): 0x%x\n", result);
> > + return -EIO;
> > + }
> > +
> > + sysfs_notify(kobj, NULL, attr->attr.name);
> > +
> > + return count;
> > +}
> > +
> > +static ssize_t mini_led_mode_possible_values_show(struct kobject *kobj,
> > + struct kobj_attribute *attr, char *buf)
> > +{
> > + switch (asus_bioscfg.mini_led_dev_id) {
> > + case ASUS_WMI_DEVID_MINI_LED_MODE:
> > + return sysfs_emit(buf, "0;1\n");
> > + case ASUS_WMI_DEVID_MINI_LED_MODE2:
> > + return sysfs_emit(buf, "0;1;2\n");
> > + }
> > +
> > + return sysfs_emit(buf, "0\n");
> > +}
> > +
> > +ATTR_GROUP_ENUM_CUSTOM(mini_led_mode, "mini_led_mode", "Set the mini-LED backlight mode");
> > +
> > +static ssize_t gpu_mux_mode_current_value_store(struct kobject *kobj,
> > + struct kobj_attribute *attr,
> > + const char *buf, size_t count)
> > +{
> > + int result, err;
> > + u32 optimus;
> > +
> > + err = kstrtou32(buf, 10, &optimus);
> > + if (err)
> > + return err;
> > +
> > + if (optimus > 1)
> > + return -EINVAL;
> > +
> > + if (asus_bioscfg.dgpu_disable_available) {
> > + err = asus_wmi_get_devstate_dsts(ASUS_WMI_DEVID_DGPU, &result);
> > + if (err)
> > + return err;
> > + if (err && !optimus) {
>
> How can err be non-zero at this point??? Did you mean result like below?
Yes. Thank you.
> > + err = -ENODEV;
> > + pr_warn("Can not switch MUX to dGPU mode when dGPU is disabled: %d\n", err);
> > + return err;
> > + }
> > + }
> > +
> > + if (asus_bioscfg.egpu_enable_available) {
> > + err = asus_wmi_get_devstate_dsts(ASUS_WMI_DEVID_EGPU, &result);
> > + if (err)
> > + return err;
> > + if (result && !optimus) {
> > + err = -ENODEV;
> > + pr_warn("Can not switch MUX to dGPU mode when eGPU is enabled: %d\n", err);
> > + return err;
> > + }
> > + }
> > +
> > + err = asus_wmi_set_devstate(asus_bioscfg.gpu_mux_dev_id, optimus, &result);
> > + if (err) {
> > + pr_err("%s Failed to set GPU MUX mode: %d\nn", __func__, err);
>
> Never use __func__ for messages shown to normal user.
Must have been a holdover from debug. Also wasn't aware of that rule, thanks.
> > + return err;
> > + }
> > + /* !1 is considered a fail by ASUS */
>
> If the interface is documented with kerneldoc, this is unnecessary
> comment. Is 0 also a failure (this differs from >1 checks elsewhere)?
I've changed the other checks to match. But I'll also try and do a deeper analysis of those particular WMI functions to see if I can find the actual causes for other returns and their significance (0 and 2). 1 is most definitely success though.
>
> > + if (result != 1) {
> > + pr_warn("%s Failed to set GPU MUX mode (result): 0x%x\n", __func__, result);
>
> Ditto.
Ack
>
> > + return -EIO;
> > + }
> > +
> > + sysfs_notify(kobj, NULL, attr->attr.name);
> > +
> > + return count;
> > +}
> > +WMI_SHOW_INT(gpu_mux_mode_current_value, "%d\n", asus_bioscfg.gpu_mux_dev_id);
> > +ATTR_GROUP_BOOL_CUSTOM(gpu_mux_mode, "gpu_mux_mode", "Set the GPU display MUX mode");
> > +
> > +/*
> > + * A user may be required to store the value twice, typcial store first, then
> > + * rescan PCI bus to activate power, then store a second time to save correctly.
> > + * The reason for this is that an extra code path in the ACPI is enabled when
> > + * the device and bus are powered.
> > + */
> > +static ssize_t dgpu_disable_current_value_store(struct kobject *kobj,
> > + struct kobj_attribute *attr,
> > + const char *buf, size_t count)
> > +{
> > + int result, err;
> > + u32 disable;
> > +
> > + result = kstrtou32(buf, 10, &disable);
> > + if (result)
> > + return result;
>
> You're likely shooting yourself into your foot by not having consistency
> in variable naming. Here you store the return value into "result", whereas
> you used "err" in the previous function. Try to maintain consistency
> across the entire driver.
Brain afk. I better read everything when i'm refreshed.
>
> > +
> > + if (disable > 1)
> > + return -EINVAL;
> > +
> > + if (asus_bioscfg.gpu_mux_dev_id) {
> > + err = asus_wmi_get_devstate_dsts(asus_bioscfg.gpu_mux_dev_id, &result);
> > + if (err)
> > + return err;
> > + if (!result && disable) {
> > + err = -ENODEV;
> > + pr_warn("Can not disable dGPU when the MUX is in dGPU mode: %d\n", err);
> > + return err;
> > + }
> > + }
> > +
> > + err = asus_wmi_set_devstate(ASUS_WMI_DEVID_DGPU, disable, &result);
> > + if (err) {
> > + pr_warn("Failed to set dgpu disable: %d\n", err);
> > + return err;
> > + }
> > +
> > + if (result > 1) {
> > + pr_warn("Failed to set dgpu disable (result): 0x%x\n", result);
> > + return -EIO;
> > + }
> > +
> > + sysfs_notify(kobj, NULL, attr->attr.name);
> > +
> > + return count;
> > +}
> > +WMI_SHOW_INT(dgpu_disable_current_value, "%d\n", ASUS_WMI_DEVID_DGPU);
> > +ATTR_GROUP_BOOL_CUSTOM(dgpu_disable, "dgpu_disable", "Disable the dGPU");
> > +
> > +/* The ACPI call to enable the eGPU also disables the internal dGPU */
> > +static ssize_t egpu_enable_current_value_store(struct kobject *kobj,
> > + struct kobj_attribute *attr,
> > + const char *buf, size_t count)
> > +{
> > + int result, err;
> > + u32 enable;
> > +
> > + err = kstrtou32(buf, 10, &enable);
> > + if (err)
> > + return err;
> > +
> > + if (enable > 1)
> > + return -EINVAL;
> > +
> > + err = asus_wmi_get_devstate_dsts(ASUS_WMI_DEVID_EGPU_CONNECTED, &result);
> > + if (err) {
> > + pr_warn("Failed to get egpu connection status: %d\n", err);
> > + return err;
> > + }
> > +
> > + if (asus_bioscfg.gpu_mux_dev_id) {
> > + err = asus_wmi_get_devstate_dsts(asus_bioscfg.gpu_mux_dev_id, &result);
> > + if (err) {
> > + pr_warn("Failed to get gpu mux status: %d\n", result);
> > + return result;
> > + }
> > + if (!result && enable) {
> > + err = -ENODEV;
> > + pr_warn("Can not enable eGPU when the MUX is in dGPU mode: %d\n", err);
> > + return err;
> > + }
> > + }
> > +
> > + err = asus_wmi_set_devstate(ASUS_WMI_DEVID_EGPU, enable, &result);
> > + if (err) {
> > + pr_warn("Failed to set egpu state: %d\n", err);
> > + return err;
> > + }
> > +
> > + if (result > 1) {
> > + pr_warn("Failed to set egpu state (retval): 0x%x\n", result);
> > + return -EIO;
> > + }
> > +
> > + sysfs_notify(kobj, NULL, attr->attr.name);
> > +
> > + return count;
> > +}
> > +WMI_SHOW_INT(egpu_enable_current_value, "%d\n", ASUS_WMI_DEVID_EGPU);
> > +ATTR_GROUP_BOOL_CUSTOM(egpu_enable, "egpu_enable", "Enable the eGPU (also disables dGPU)");
> > +
> > +/* Simple attribute creation */
> > +ATTR_GROUP_ENUM_INT_RW(thermal_policy, "thermal_policy", ASUS_WMI_DEVID_THROTTLE_THERMAL_POLICY,
> > + 0, 3, "0;1;2", "Set the thermal profile: 0=normal, 1=performance, 2=quiet");
> > +ATTR_GROUP_PPT_RW(ppt_pl1_spl, "ppt_pl1_spl", ASUS_WMI_DEVID_PPT_PL1_SPL,
> > + cpu_default, 5, cpu_max, 1, "Set the CPU slow package limit");
> > +ATTR_GROUP_PPT_RW(ppt_pl2_sppt, "ppt_pl2_sppt", ASUS_WMI_DEVID_PPT_PL2_SPPT,
> > + cpu_default, 5, cpu_max, 1, "Set the CPU fast package limit");
> > +ATTR_GROUP_PPT_RW(ppt_apu_sppt, "ppt_apu_sppt", ASUS_WMI_DEVID_PPT_APU_SPPT,
> > + platform_default, 5, platform_max, 1, "Set the CPU slow package limit");
> > +ATTR_GROUP_PPT_RW(ppt_platform_sppt, "ppt_platform_sppt", ASUS_WMI_DEVID_PPT_PLAT_SPPT,
> > + platform_default, 5, platform_max, 1, "Set the CPU slow package limit");
> > +ATTR_GROUP_PPT_RW(ppt_fppt, "ppt_fppt", ASUS_WMI_DEVID_PPT_FPPT,
> > + cpu_default, 5, cpu_max, 1, "Set the CPU slow package limit");
> > +
> > +ATTR_GROUP_PPT_RW(nv_dynamic_boost, "nv_dynamic_boost", ASUS_WMI_DEVID_NV_DYN_BOOST,
> > + nv_boost_default, 5, nv_boost_max, 1, "Set the Nvidia dynamic boost limit");
> > +ATTR_GROUP_PPT_RW(nv_temp_target, "nv_temp_target", ASUS_WMI_DEVID_NV_THERM_TARGET,
> > + nv_temp_default, 75, nv_temp_max, 1, "Set the Nvidia max thermal limit");
> > +
> > +ATTR_GROUP_ENUM_INT_RO(charge_mode, "charge_mode", ASUS_WMI_DEVID_CHARGE_MODE,
> > + "0;1;2", "Show the current mode of charging");
> > +ATTR_GROUP_BOOL_RW(boot_sound, "boot_sound", ASUS_WMI_DEVID_BOOT_SOUND,
> > + "Set the boot POST sound");
> > +ATTR_GROUP_BOOL_RW(mcu_powersave, "mcu_powersave", ASUS_WMI_DEVID_MCU_POWERSAVE,
> > + "Set MCU powersaving mode");
> > +ATTR_GROUP_BOOL_RW(panel_od, "panel_overdrive", ASUS_WMI_DEVID_PANEL_OD,
> > + "Set the panel refresh overdrive");
> > +ATTR_GROUP_BOOL_RW(panel_hd_mode, "panel_hd_mode", ASUS_WMI_DEVID_PANEL_HD,
> > + "Set the panel HD mode to UHD<0> or FHD<1>");
> > +ATTR_GROUP_BOOL_RO(egpu_connected, "egpu_connected", ASUS_WMI_DEVID_EGPU_CONNECTED,
> > + "Show the eGPU connection status");
> > +
> > +static int asus_fw_attr_add(void)
> > +{
> > + int ret;
> > +
> > + ret = fw_attributes_class_get(&fw_attr_class);
> > + if (ret)
> > + goto fail_class_created;
> > + else
>
> Unnecessary else since you goto in the if block.
Ack
>
> > + asus_bioscfg.fw_attr_dev = device_create(fw_attr_class, NULL,
> > + MKDEV(0, 0), NULL, "%s", DRIVER_NAME);
> > +
> > + if (IS_ERR(asus_bioscfg.fw_attr_dev)) {
> > + ret = PTR_ERR(asus_bioscfg.fw_attr_dev);
> > + goto fail_class_created;
> > + }
> > +
> > + asus_bioscfg.fw_attr_kset = kset_create_and_add("attributes", NULL,
> > + &asus_bioscfg.fw_attr_dev->kobj);
> > + if (!asus_bioscfg.fw_attr_dev) {
> > + ret = -ENOMEM;
> > + pr_debug("Failed to create and add attributes\n");
> > + goto err_destroy_classdev;
> > + }
> > +
> > + /* Add any firmware_attributes required */
>
> Unnecessary comment.
Ack
>
> > + ret = sysfs_create_file(&asus_bioscfg.fw_attr_kset->kobj, &pending_reboot.attr);
> > + if (ret) {
> > + pr_warn("Failed to create sysfs level attributes\n");
> > + goto fail_class_created;
> > + }
> > +
> > + // TODO: logging
>
> ?? Logging of what if you intend to keep this message around.
Shouldn't have been there.
> > + asus_bioscfg.mini_led_dev_id = 0;
> > + if (asus_wmi_is_present(ASUS_WMI_DEVID_MINI_LED_MODE)) {
> > + asus_bioscfg.mini_led_dev_id = ASUS_WMI_DEVID_MINI_LED_MODE;
> > + sysfs_create_group(&asus_bioscfg.fw_attr_kset->kobj, &mini_led_mode_attr_group);
> > + } else if (asus_wmi_is_present(ASUS_WMI_DEVID_MINI_LED_MODE2)) {
> > + asus_bioscfg.mini_led_dev_id = ASUS_WMI_DEVID_MINI_LED_MODE2;
> > + sysfs_create_group(&asus_bioscfg.fw_attr_kset->kobj, &mini_led_mode_attr_group);
> > + }
> > +
> > + if (asus_wmi_is_present(ASUS_WMI_DEVID_GPU_MUX)) {
> > + asus_bioscfg.gpu_mux_dev_id = ASUS_WMI_DEVID_GPU_MUX;
> > + sysfs_create_group(&asus_bioscfg.fw_attr_kset->kobj, &gpu_mux_mode_attr_group);
> > + } else if (asus_wmi_is_present(ASUS_WMI_DEVID_GPU_MUX_VIVO)) {
> > + asus_bioscfg.gpu_mux_dev_id = ASUS_WMI_DEVID_GPU_MUX_VIVO;
> > + sysfs_create_group(&asus_bioscfg.fw_attr_kset->kobj, &gpu_mux_mode_attr_group);
> > + }
> > +
> > + if (asus_wmi_is_present(ASUS_WMI_DEVID_DGPU)) {
> > + asus_bioscfg.dgpu_disable_available = true;
> > + sysfs_create_group(&asus_bioscfg.fw_attr_kset->kobj, &dgpu_disable_attr_group);
> > + }
> > + if (asus_wmi_is_present(ASUS_WMI_DEVID_EGPU)) {
> > + asus_bioscfg.egpu_enable_available = true;
> > + sysfs_create_group(&asus_bioscfg.fw_attr_kset->kobj, &egpu_enable_attr_group);
> > + }
> > + if (asus_wmi_is_present(ASUS_WMI_DEVID_EGPU_CONNECTED))
> > + sysfs_create_group(&asus_bioscfg.fw_attr_kset->kobj, &egpu_connected_attr_group);
> > +
> > + if (asus_wmi_is_present(ASUS_WMI_DEVID_THROTTLE_THERMAL_POLICY))
> > + sysfs_create_group(&asus_bioscfg.fw_attr_kset->kobj, &thermal_policy_attr_group);
> > + if (asus_wmi_is_present(ASUS_WMI_DEVID_PPT_PL1_SPL))
> > + sysfs_create_group(&asus_bioscfg.fw_attr_kset->kobj, &ppt_pl1_spl_attr_group);
> > + if (asus_wmi_is_present(ASUS_WMI_DEVID_PPT_PL2_SPPT))
> > + sysfs_create_group(&asus_bioscfg.fw_attr_kset->kobj, &ppt_pl2_sppt_attr_group);
> > + if (asus_wmi_is_present(ASUS_WMI_DEVID_PPT_APU_SPPT))
> > + sysfs_create_group(&asus_bioscfg.fw_attr_kset->kobj, &ppt_apu_sppt_attr_group);
> > + if (asus_wmi_is_present(ASUS_WMI_DEVID_PPT_PLAT_SPPT))
> > + sysfs_create_group(&asus_bioscfg.fw_attr_kset->kobj, &ppt_platform_sppt_attr_group);
> > + if (asus_wmi_is_present(ASUS_WMI_DEVID_PPT_FPPT))
> > + sysfs_create_group(&asus_bioscfg.fw_attr_kset->kobj, &ppt_fppt_attr_group);
> > +
> > + if (asus_wmi_is_present(ASUS_WMI_DEVID_NV_DYN_BOOST))
> > + sysfs_create_group(&asus_bioscfg.fw_attr_kset->kobj, &nv_dynamic_boost_attr_group);
> > + if (asus_wmi_is_present(ASUS_WMI_DEVID_NV_THERM_TARGET))
> > + sysfs_create_group(&asus_bioscfg.fw_attr_kset->kobj, &nv_temp_target_attr_group);
> > +
> > + if (asus_wmi_is_present(ASUS_WMI_DEVID_CHARGE_MODE))
> > + sysfs_create_group(&asus_bioscfg.fw_attr_kset->kobj, &charge_mode_attr_group);
> > + if (asus_wmi_is_present(ASUS_WMI_DEVID_BOOT_SOUND))
> > + sysfs_create_group(&asus_bioscfg.fw_attr_kset->kobj, &boot_sound_attr_group);
> > + if (asus_wmi_is_present(ASUS_WMI_DEVID_MCU_POWERSAVE))
> > + sysfs_create_group(&asus_bioscfg.fw_attr_kset->kobj, &mcu_powersave_attr_group);
> > + if (asus_wmi_is_present(ASUS_WMI_DEVID_PANEL_OD))
> > + sysfs_create_group(&asus_bioscfg.fw_attr_kset->kobj, &panel_od_attr_group);
> > + if (asus_wmi_is_present(ASUS_WMI_DEVID_PANEL_HD))
> > + sysfs_create_group(&asus_bioscfg.fw_attr_kset->kobj, &panel_hd_mode_attr_group);
> > +
> > + return 0;
> > +
> > +err_destroy_classdev:
> > + device_destroy(fw_attr_class, MKDEV(0, 0));
> > +
> > +fail_class_created:
> > + fw_attributes_class_put();
> > + return ret;
> > +}
> > +
> > +/* Init / exit ****************************************************************/
> > +
> > +/* Set up the min/max and defaults for ROG tunables */
> > +static void init_rog_tunables(struct rog_tunables *rog)
> > +{
> > + const char *product;
> > + u32 max_boost = NVIDIA_BOOST_MAX;
> > + u32 cpu_default = PPT_CPU_LIMIT_DEFAULT;
> > + u32 cpu_max = PPT_CPU_LIMIT_MAX;
> > + u32 platform_default = PPT_PLATFORM_DEFAULT;
> > + u32 platform_max = PPT_PLATFORM_MAX;
> > +
> > + /*
> > + * ASUS product_name contains everything required, e.g,
> > + * "ROG Flow X16 GV601VV_GV601VV_00185149B"
> > + */
> > + product = dmi_get_system_info(DMI_PRODUCT_NAME);
> > +
> > + if (strstr(product, "GA402R")) {
> > + cpu_default = 125;
> > + } else if (strstr(product, "13QY")) {
> > + cpu_max = 250;
> > + } else if (strstr(product, "X13")) {
> > + cpu_max = 75;
> > + cpu_default = 50;
> > + } else if (strstr(product, "RC71")) {
> > + cpu_max = 50;
> > + cpu_default = 30;
> > + } else if (strstr(product, "G814")
> > + || strstr(product, "G614")
> > + || strstr(product, "G834")
> > + || strstr(product, "G634")) {
> > + cpu_max = 175;
> > + } else if (strstr(product, "GA402X")
> > + || strstr(product, "GA403")
> > + || strstr(product, "FA507N")
> > + || strstr(product, "FA507X")
> > + || strstr(product, "FA707N")
> > + || strstr(product, "FA707X")) {
> > + cpu_max = 90;
> > + }
> > +
> > + if (strstr(product, "GZ301ZE"))
> > + max_boost = 5;
> > + else if (strstr(product, "FX507ZC4"))
> > + max_boost = 15;
> > + else if (strstr(product, "GU605"))
> > + max_boost = 20;
> > +
> > + /* ensure defaults for tunables */
> > + rog->cpu_default = cpu_default;
> > + rog->cpu_max = cpu_max;
> > +
> > + rog->platform_default = platform_default;
> > + rog->platform_max = platform_max;
> > +
> > + rog->ppt_pl1_spl = cpu_default;
> > + rog->ppt_pl2_sppt = cpu_default;
> > + rog->ppt_apu_sppt = cpu_default;
> > +
> > + rog->ppt_platform_sppt = platform_default;
> > + rog->ppt_fppt = platform_default;
> > +
> > + rog->nv_boost_default = NVIDIA_BOOST_MAX;
> > + rog->nv_boost_max = max_boost;
> > + rog->nv_dynamic_boost = NVIDIA_BOOST_MIN;
> > +
> > + rog->nv_temp_default = NVIDIA_TEMP_MAX;
> > + rog->nv_temp_max = NVIDIA_TEMP_MAX;
> > + rog->nv_temp_target = NVIDIA_TEMP_MIN;
> > +
> > +}
> > +
> > +static int __init asus_fw_init(void)
> > +{
> > + int err;
> > +
> > + mutex_lock(&asus_bioscfg.mutex);
> > +
> > + asus_bioscfg.rog_tunables = kzalloc(sizeof(struct rog_tunables), GFP_KERNEL);
> > + if (!asus_bioscfg.rog_tunables) {
> > + mutex_unlock(&asus_bioscfg.mutex);
> > + return -ENOMEM;
> > + }
> > + init_rog_tunables(asus_bioscfg.rog_tunables);
> > +
> > + err = asus_fw_attr_add();
> > + mutex_unlock(&asus_bioscfg.mutex);
> > + if (err)
> > + return err;
> > +
> > + return 0;
> > +}
> > +
> > +static void __exit asus_fw_exit(void)
> > +{
> > + mutex_lock(&asus_bioscfg.mutex);
> > +
> > + sysfs_remove_file(&asus_bioscfg.fw_attr_kset->kobj, &pending_reboot.attr);
> > + kset_unregister(asus_bioscfg.fw_attr_kset);
> > + device_destroy(fw_attr_class, MKDEV(0, 0));
> > + fw_attributes_class_put();
> > +
> > + mutex_unlock(&asus_bioscfg.mutex);
> > +}
> > +
> > +module_init(asus_fw_init);
> > +module_exit(asus_fw_exit);
> > diff --git a/drivers/platform/x86/asus-bioscfg.h b/drivers/platform/x86/asus-bioscfg.h
> > new file mode 100644
> > index 000000000000..403563c25f53
> > --- /dev/null
> > +++ b/drivers/platform/x86/asus-bioscfg.h
> > @@ -0,0 +1,243 @@
> > +/* SPDX-License-Identifier: GPL-2.0
> > + *
> > + * Definitions for kernel modules using asus-bios driver
> > + *
> > + * Copyright (c) 2024 Luke Jones <luke@ljones.dev>
> > + */
> > +
> > +#ifndef _ASUS_BIOSCFG_H_
> > +#define _ASUS_BIOSCFG_H_
> > +
> > +#include "firmware_attributes_class.h"
> > +#include <linux/types.h>
> > +
> > +#define DRIVER_NAME "asus-bioscfg"
> > +
> > +static ssize_t attr_int_store(struct kobject *kobj, struct kobj_attribute *attr,
> > + const char *buf, size_t count,
> > + u32 min, u32 max, u32 *store_value, u32 wmi_dev);
> > +
> > +
> > +static ssize_t int_type_show(struct kobject *kobj, struct kobj_attribute *attr,
> > + char *buf)
> > +{
> > + return sysfs_emit(buf, "integer\n");
> > +}
> > +
> > +static ssize_t enum_type_show(struct kobject *kobj, struct kobj_attribute *attr,
> > + char *buf)
> > +{
> > + return sysfs_emit(buf, "enumeration\n");
> > +}
> > +
> > +#define __ASUS_ATTR_RO(_func, _name) { \
> > + .attr = { .name = __stringify(_name), .mode = 0444 }, \
> > + .show = _func##_##_name##_show, \
> > +}
> > +
> > +#define __ASUS_ATTR_RO_AS(_name, _show) { \
> > + .attr = { .name = __stringify(_name), .mode = 0444 }, \
> > + .show = _show, \
> > +}
> > +
> > +#define __ASUS_ATTR_RW(_func, _name) __ATTR(_name, 0644, \
> > + _func##_##_name##_show, _func##_##_name##_store)
> > +
> > +#define __WMI_STORE_INT(_attr, _min, _max, _wmi) \
> > +static ssize_t _attr##_store(struct kobject *kobj, \
> > + struct kobj_attribute *attr, \
> > + const char *buf, size_t count) \
> > +{ \
> > + return attr_int_store(kobj, attr, buf, count, _min, _max, NULL, _wmi); \
> > +}
> > +
> > +#define WMI_SHOW_INT(_attr, _fmt, _wmi) \
> > +static ssize_t _attr##_show(struct kobject *kobj, \
> > + struct kobj_attribute *attr, char *buf) \
> > +{ \
> > + u32 result; \
> > + int err; \
> > + err = asus_wmi_get_devstate_dsts(_wmi, &result); \
> > + if (err) \
> > + return err; \
> > + return sysfs_emit(buf, _fmt, \
> > + result & ~ASUS_WMI_DSTS_PRESENCE_BIT); \
> > +}
> > +
> > +/* Create functions and attributes for use in other macros or on their own */
> > +
> > +#define __ROG_TUNABLE_RW(_attr, _min, _max, _wmi) \
> > +static ssize_t _attr##_current_value_store(struct kobject *kobj, \
> > + struct kobj_attribute *attr, \
> > + const char *buf, size_t count) \
> > +{ \
> > + return attr_int_store(kobj, attr, buf, count, \
> > + _min, asus_bioscfg.rog_tunables->_max, \
> > + &asus_bioscfg.rog_tunables->_attr, _wmi); \
> > +} \
> > +static ssize_t _attr##_current_value_show(struct kobject *kobj, \
> > + struct kobj_attribute *attr, char *buf) \
> > +{ \
> > + return sysfs_emit(buf, "%u\n", asus_bioscfg.rog_tunables->_attr);\
> > +} \
> > +static struct kobj_attribute attr_##_attr##_current_value = \
> > + __ASUS_ATTR_RW(_attr, current_value)
> > +
> > +#define __ROG_TUNABLE_SHOW(_prop, _attrname, _val) \
> > +static ssize_t _attrname##_##_prop##_show(struct kobject *kobj, \
> > + struct kobj_attribute *attr, char *buf) \
> > +{ \
> > + return sysfs_emit(buf, "%d\n", asus_bioscfg.rog_tunables->_val);\
> > +} \
> > +static struct kobj_attribute attr_##_attrname##_##_prop = \
> > + __ASUS_ATTR_RO(_attrname, _prop)
> > +
> > +#define __ATTR_CURRENT_INT_RO(_attr, _wmi) \
> > +WMI_SHOW_INT(_attr##_current_value, "%d\n", _wmi); \
> > +static struct kobj_attribute attr_##_attr##_current_value = \
> > + __ASUS_ATTR_RO(_attr, current_value)
> > +
> > +#define __ATTR_CURRENT_INT_RW(_attr, _minv, _maxv, _wmi) \
> > +__WMI_STORE_INT(_attr##_current_value, _minv, _maxv, _wmi); \
> > +WMI_SHOW_INT(_attr##_current_value, "%d\n", _wmi); \
> > +static struct kobj_attribute attr_##_attr##_current_value = \
> > + __ASUS_ATTR_RW(_attr, current_value)
> > +
> > +/* Shows a formatted static variable */
> > +#define __ATTR_SHOW_FMT(_prop, _attrname, _fmt, _val) \
> > +static ssize_t _attrname##_##_prop##_show(struct kobject *kobj, \
> > + struct kobj_attribute *attr, char *buf) \
> > +{ \
> > + return sysfs_emit(buf, _fmt, _val); \
> > +} \
> > +static struct kobj_attribute attr_##_attrname##_##_prop = \
> > + __ASUS_ATTR_RO(_attrname, _prop)
> > +
> > +/* Int style min/max range, base macro. Requires current_value show&|store */
> > +#define __ATTR_GROUP_INT(_attrname, _fsname, _default, \
> > + _min, _max, _incstep, _dispname)\
> > +__ATTR_SHOW_FMT(default_value, _attrname, "%d\n", _default); \
> > +__ATTR_SHOW_FMT(min_value, _attrname, "%d\n", _min); \
> > +__ATTR_SHOW_FMT(max_value, _attrname, "%d\n", _max); \
> > +__ATTR_SHOW_FMT(scalar_increment, _attrname, "%d\n", _incstep); \
> > +__ATTR_SHOW_FMT(display_name, _attrname, "%s\n", _dispname); \
> > +static struct kobj_attribute attr_##_attrname##_type = \
> > + __ASUS_ATTR_RO_AS(type, int_type_show); \
> > +static struct attribute *_attrname##_attrs[] = { \
> > + &attr_##_attrname##_current_value.attr, \
> > + &attr_##_attrname##_default_value.attr, \
> > + &attr_##_attrname##_min_value.attr, \
> > + &attr_##_attrname##_max_value.attr, \
> > + &attr_##_attrname##_scalar_increment.attr, \
> > + &attr_##_attrname##_display_name.attr, \
> > + &attr_##_attrname##_type.attr, \
> > + NULL \
> > +}; \
> > +static const struct attribute_group _attrname##_attr_group = { \
> > + .name = _fsname, \
> > + .attrs = _attrname##_attrs \
> > +}
> > +
> > +/* Boolean style enumeration, base macro. Requires adding show/store */
> > +#define __ATTR_GROUP_ENUM(_attrname, _fsname, _possible, _dispname) \
> > +__ATTR_SHOW_FMT(display_name, _attrname, "%s\n", _dispname); \
> > +__ATTR_SHOW_FMT(possible_values, _attrname, "%s\n", _possible); \
> > +static struct kobj_attribute attr_##_attrname##_type = \
> > + __ASUS_ATTR_RO_AS(type, enum_type_show); \
> > +static struct attribute *_attrname##_attrs[] = { \
> > + &attr_##_attrname##_current_value.attr, \
> > + &attr_##_attrname##_display_name.attr, \
> > + &attr_##_attrname##_possible_values.attr, \
> > + &attr_##_attrname##_type.attr, \
> > + NULL \
> > +}; \
> > +static const struct attribute_group _attrname##_attr_group = { \
> > + .name = _fsname, \
> > + .attrs = _attrname##_attrs \
> > +}
> > +
> > +#define ATTR_GROUP_BOOL_RO(_attrname, _fsname, _wmi, _dispname) \
> > +do { \
> > + __ATTR_CURRENT_INT_RO(_attrname, _wmi); \
> > + __ATTR_GROUP_ENUM(_attrname, _fsname, "0;1", _dispname);\
> > +} while (0)
> > +
> > +#define ATTR_GROUP_BOOL_RW(_attrname, _fsname, _wmi, _dispname) \
> > +do { \
> > + __ATTR_CURRENT_INT_RW(_attrname, 0, 1, _wmi); \
> > + __ATTR_GROUP_ENUM(_attrname, _fsname, "0;1", _dispname);\
> > +} while (0)
> > +
> > +/*
> > + * Requires <name>_current_value_show(), <name>_current_value_show()
> > + */
> > +#define ATTR_GROUP_BOOL_CUSTOM(_attrname, _fsname, _dispname) \
> > +static struct kobj_attribute attr_##_attrname##_current_value = \
> > + __ASUS_ATTR_RW(_attrname, current_value); \
> > +__ATTR_GROUP_ENUM(_attrname, _fsname, "0;1", _dispname)
> > +
> > +#define ATTR_GROUP_ENUM_INT_RO(_attrname, _fsname, _wmi, \
> > + _possible, _dispname) \
> > +do { \
> > + __ATTR_CURRENT_INT_RO(_attrname, _wmi); \
> > + __ATTR_GROUP_ENUM(_attrname, _fsname, _possible, _dispname); \
> > +} while (0)
> > +
> > +#define ATTR_GROUP_ENUM_INT_RW(_attrname, _fsname, _wmi, _min, \
> > + _max, _possible, _dispname) \
> > +do { \
> > + __ATTR_CURRENT_INT_RW(_attrname, _min, _max, _wmi); \
> > + __ATTR_GROUP_ENUM(_attrname, _fsname, _possible, _dispname); \
> > +} while (0)
> > +
> > +/*
> > + * Requires <name>_current_value_show(), <name>_current_value_show()
> > + * and <name>_possible_values_show()
> > + */
> > +#define ATTR_GROUP_ENUM_CUSTOM(_attrname, _fsname, _dispname) \
> > +__ATTR_SHOW_FMT(display_name, _attrname, "%s\n", _dispname); \
> > +static struct kobj_attribute attr_##_attrname##_current_value = \
> > + __ASUS_ATTR_RW(_attrname, current_value); \
> > +static struct kobj_attribute attr_##_attrname##_possible_values = \
> > + __ASUS_ATTR_RO(_attrname, possible_values); \
> > +static struct kobj_attribute attr_##_attrname##_type = \
> > + __ASUS_ATTR_RO_AS(type, enum_type_show); \
> > +static struct attribute *_attrname##_attrs[] = { \
> > + &attr_##_attrname##_current_value.attr, \
> > + &attr_##_attrname##_display_name.attr, \
> > + &attr_##_attrname##_possible_values.attr, \
> > + &attr_##_attrname##_type.attr, \
> > + NULL \
> > +}; \
> > +static const struct attribute_group _attrname##_attr_group = { \
> > + .name = _fsname, \
> > + .attrs = _attrname##_attrs \
> > +}
> > +
> > +/* ROG PPT attributes need a little different in setup */
> > +#define ATTR_GROUP_PPT_RW(_attrname, _fsname, _wmi, _default, \
> > + _min, _max, _incstep, _dispname) \
> > +__ROG_TUNABLE_RW(_attrname, _min, _max, _wmi); \
> > +__ROG_TUNABLE_SHOW(default_value, _attrname, _default); \
> > +__ATTR_SHOW_FMT(min_value, _attrname, "%d\n", _min); \
> > +__ROG_TUNABLE_SHOW(max_value, _attrname, _max); \
> > +__ATTR_SHOW_FMT(scalar_increment, _attrname, "%d\n", _incstep); \
> > +__ATTR_SHOW_FMT(display_name, _attrname, "%s\n", _dispname); \
> > +static struct kobj_attribute attr_##_attrname##_type = \
> > + __ASUS_ATTR_RO_AS(type, int_type_show); \
> > +static struct attribute *_attrname##_attrs[] = { \
> > + &attr_##_attrname##_current_value.attr, \
> > + &attr_##_attrname##_default_value.attr, \
> > + &attr_##_attrname##_min_value.attr, \
> > + &attr_##_attrname##_max_value.attr, \
> > + &attr_##_attrname##_scalar_increment.attr, \
> > + &attr_##_attrname##_display_name.attr, \
> > + &attr_##_attrname##_type.attr, \
> > + NULL \
> > +}; \
> > +static const struct attribute_group _attrname##_attr_group = { \
> > + .name = _fsname, \
> > + .attrs = _attrname##_attrs \
> > +}
> > +
> > +#endif /* _ASUS_BIOSCFG_H_ */
> > diff --git a/drivers/platform/x86/asus-wmi.c b/drivers/platform/x86/asus-wmi.c
> > index 2b968003cb9b..3f1998638fea 100644
> > --- a/drivers/platform/x86/asus-wmi.c
> > +++ b/drivers/platform/x86/asus-wmi.c
> > @@ -529,12 +529,28 @@ static int asus_wmi_get_devstate(struct asus_wmi *asus, u32 dev_id, u32 *retval)
> > return 0;
> > }
> >
> > -static int asus_wmi_set_devstate(u32 dev_id, u32 ctrl_param,
> > +int asus_wmi_get_devstate_dsts(u32 dev_id, u32 *retval)
> > +{
> > + int err;
> > +
> > + err = asus_wmi_evaluate_method(ASUS_WMI_METHODID_DSTS, dev_id, 0, retval);
> > + if (err)
> > + return err;
> > +
> > + if (*retval == ~0)
> > + return -ENODEV;
> > +
> > + return 0;
> > +}
> > +EXPORT_SYMBOL_GPL(asus_wmi_get_devstate_dsts);
> > +
> > +int asus_wmi_set_devstate(u32 dev_id, u32 ctrl_param,
> > u32 *retval)
>
> Should fit to one line just fine.
Not sure why it wasn't. Might have been a longer function name to start with.
> As general feel of the readiness of this code, I suspect there were many
> more problems which I failed to notice :-(.
I'd put money on it (sorry). I definitely should have cleaned up better than I did so you weren't pointing out silly little things, but I was never expecting to get over the line on the first try and desperately needed some insight for the overall patch series to see if what I was doing was actually going to be acceptable or not.
As always, thanks so much for your time and review.
Regards,
Luke.
>
> --
> i.
>
> > {
> > return asus_wmi_evaluate_method(ASUS_WMI_METHODID_DEVS, dev_id,
> > ctrl_param, retval);
> > }
> > +EXPORT_SYMBOL_GPL(asus_wmi_set_devstate);
> >
> > /* Helper for special devices with magic return codes */
> > static int asus_wmi_get_devstate_bits(struct asus_wmi *asus,
> > diff --git a/include/linux/platform_data/x86/asus-wmi.h b/include/linux/platform_data/x86/asus-wmi.h
> > index 74b32e1d6735..889336a932fb 100644
> > --- a/include/linux/platform_data/x86/asus-wmi.h
> > +++ b/include/linux/platform_data/x86/asus-wmi.h
> > @@ -67,6 +67,7 @@
> > #define ASUS_WMI_DEVID_THROTTLE_THERMAL_POLICY 0x00120075
> >
> > /* Misc */
> > +#define ASUS_WMI_DEVID_PANEL_HD 0x0005001C
> > #define ASUS_WMI_DEVID_PANEL_OD 0x00050019
> > #define ASUS_WMI_DEVID_CAMERA 0x00060013
> > #define ASUS_WMI_DEVID_LID_FLIP 0x00060062
> > @@ -152,8 +153,18 @@
> > #define ASUS_WMI_DSTS_LIGHTBAR_MASK 0x0000000F
> >
> > #if IS_REACHABLE(CONFIG_ASUS_WMI)
> > +int asus_wmi_get_devstate_dsts(u32 dev_id, u32 *retval);
> > +int asus_wmi_set_devstate(u32 dev_id, u32 ctrl_param, u32 *retval);
> > int asus_wmi_evaluate_method(u32 method_id, u32 arg0, u32 arg1, u32 *retval);
> > #else
> > +static int asus_wmi_get_devstate_dsts(u32 dev_id, u32 *retval)
> > +{
> > + return -ENODEV;
> > +}
> > +static int asus_wmi_set_devstate(u32 dev_id, u32 ctrl_param, u32 *retval)
> > +{
> > + return -ENODEV;
> > +}
> > static inline int asus_wmi_evaluate_method(u32 method_id, u32 arg0, u32 arg1,
> > u32 *retval)
> > {
> >
^ permalink raw reply [flat|nested] 22+ messages in thread* Re: [PATCH 1/5] platform/x86 asus-bioscfg: move existing tunings to asus-bioscfg module
2024-07-16 10:41 ` Luke Jones
@ 2024-07-16 11:06 ` Ilpo Järvinen
0 siblings, 0 replies; 22+ messages in thread
From: Ilpo Järvinen @ 2024-07-16 11:06 UTC (permalink / raw)
To: Luke Jones
Cc: platform-driver-x86, corentin.chary, Hans de Goede,
Mario Limonciello, LKML
[-- Attachment #1: Type: text/plain, Size: 9620 bytes --]
On Tue, 16 Jul 2024, Luke Jones wrote:
> On Tue, 16 Jul 2024, at 9:45 PM, Ilpo Järvinen wrote:
> > On Tue, 16 Jul 2024, Luke D. Jones wrote:
> >
> > > The fw_attributes_class provides a much cleaner interface to all of the
> > > attributes introduced to asus-wmi. This patch moves all of these extra
> > > attributes over to fw_attributes_class, and shifts the bulk of these
> > > definitions to a new kernel module to reduce the clutter of asus-wmi
> > > with the intention of deprecating the asus-wmi attributes in future.
> > >
> > > The work applies only to WMI methods which don't have a clearly defined
> > > place within the sysfs and as a result ended up lumped together in
> > > /sys/devices/platform/asus-nb-wmi/ with no standard API.
> > >
> > > Where possible the fw attrs now implement defaults, min, max, scalar,
> > > choices, etc. As en example dgpu_disable becomes:
> > >
> > > /sys/class/firmware-attributes/asus-bioscfg/attributes/dgpu_disable/
> > > ├── current_value
> > > ├── display_name
> > > ├── possible_values
> > > └── type
> > >
> > > as do other attributes.
> > >
> > > Signed-off-by: Luke D. Jones <luke@ljones.dev>
> > > ---
> > > drivers/platform/x86/Kconfig | 14 +
> > > drivers/platform/x86/Makefile | 1 +
> > > drivers/platform/x86/asus-bioscfg.c | 666 +++++++++++++++++++++
> > > drivers/platform/x86/asus-bioscfg.h | 243 ++++++++
> > > drivers/platform/x86/asus-wmi.c | 18 +-
> > > include/linux/platform_data/x86/asus-wmi.h | 11 +
> > > 6 files changed, 952 insertions(+), 1 deletion(-)
> > > create mode 100644 drivers/platform/x86/asus-bioscfg.c
> > > create mode 100644 drivers/platform/x86/asus-bioscfg.h
> > >
> > > diff --git a/drivers/platform/x86/Kconfig b/drivers/platform/x86/Kconfig
> > > index 665fa9524986..b4a5a5bec7f3 100644
> > > --- a/drivers/platform/x86/Kconfig
> > > +++ b/drivers/platform/x86/Kconfig
> > > @@ -265,6 +265,18 @@ config ASUS_WIRELESS
> > > If you choose to compile this driver as a module the module will be
> > > called asus-wireless.
> > >
> > > +config ASUS_BIOS
> > > + tristate "ASUS BIOS Driver"
> > > + depends on ACPI_WMI
> > > + depends on ASUS_WMI
> > > + select FW_ATTR_CLASS
> > > + help
> > > + Say Y here if you have a WMI aware Asus laptop and would like to use the
> > > + firmware_attributes API.
> > > +
> > > + To compile this driver as a module, choose M here: the module will
> > > + be called asus-bios.
> > > +
> > > config ASUS_WMI
> > > tristate "ASUS WMI Driver"
> > > depends on ACPI_WMI
> > > @@ -276,6 +288,8 @@ config ASUS_WMI
> > > depends on HOTPLUG_PCI
> > > depends on ACPI_VIDEO || ACPI_VIDEO = n
> > > depends on SERIO_I8042 || SERIO_I8042 = n
> > > + select ASUS_BIOS
> >
> > Selecting user visible configs is not a good idea. Also, there
> > seems to be circular dependency now between ASUS_BIOS & ASUS_WMI ?
>
> Is "selects" the same as "depends"?
It's not the same. Selects ask to enabled another symbol (with caveats)
and depends only shows this symbol if the other symbol is already enabled.
Select comes with many many caveats and should only be used for the
config symbols which are truly library type (and not presented to user in
the first place).
> I did just remove:
> select ASUS_WMI_BIOS
> which should not be there at all.
>
> ASUS_BIOS does need ASUS_WMI. And I'd like ASUS_BIOS to be selected by
> defualt, is this not the right way to do that?
Default should not be handled with either depends on / select I think,
but I'm not Kconfig expert.
There's also default clause but it should be used sparingly as each and
every developer naturally thinks their thing is so important it must be on
by default so we know where that thinking ends to. :-)
Distros tend enable about everything anyway so it might not be so
important in the end what the default is.
> > > + select ASUS_WMI_BIOS
> > > select INPUT_SPARSEKMAP
> > > select LEDS_CLASS
> > > select NEW_LEDS
> > > + struct mutex mutex;
> > > +} asus_bioscfg = {
> > > + .mutex = __MUTEX_INITIALIZER(asus_bioscfg.mutex),
> >
> > Don't try to initialize it on the same go like this.
> >
> > You might want static too.
>
> Ack both
>
> >
> > > +};
> > > +
> > > +static struct fw_attrs_group {
> > > + u32 pending_reboot;
> > > +} fw_attrs = {
> > > + .pending_reboot = 0,
> > > +};
> >
> > Same here.
>
> It was probably done like this in code I read as a reference. I'll shift
> to the module init function.
???
I just meant this split:
struct fw_attrs_group {
u32 pending_reboot;
};
static struct fw_attrs_group fw_attrs = {
.pending_reboot = 0,
};
> > > +static bool asus_bios_requires_reboot(struct kobj_attribute *attr)
> > > +{
> > > + return !strcmp(attr->attr.name, "gpu_mux_mode");
> > > + !strcmp(attr->attr.name, "panel_hd_mode");
> >
> > ???
> >
> > Semicolon and && confusion here?
>
> Yeah i know, bad rebase I didn't catch.
For the record, || is correct here, not && but you probably know this
already.
> > > + if (result)
> > > + return result;
> > > +
> > > + if (value < min || value > max)
> > > + return -EINVAL;
> > > +
> > > + asus_wmi_set_devstate(wmi_dev, value, &result);
> >
> > Type confusion, u32 * vs int pointer being passed.
>
> I miss rust...
> > > + if (result) {
> > > + pr_err("Failed to set %s: %d\n", attr->attr.name, result);
> > > + return result;
> > > + }
> > > +
> > > + if (result > 1) {
> >
> > What's this supposed to mean given you've the type confusion to begin
> > with and return on the earlier line if result is non-zero?
> >
> > Did you mean to capture the return value of asus_wmi_set_devstate() and
> > test that in the first if ()?
>
> Yep.. this whole bit is a mess. I've fixed the type mess, and added a
> comment to clarify the "if (result > 1) {"
> (WMI methods return 0 = fail, 1 = success, anything else is error)
Don't add these comments into everywhere in the code but document
asus_wmi_set_devstate() instead with kerneldoc.
> > If you make a previously internal function such as asus_wmi_set_devstate()
> > EXPORTed, you should document it with kerneldoc so the interface is clear.
>
> I'm not sure how to do this, I'll read up. Also didn't know about it so
> thanks for the pointer.
It's this format:
/**
* funcname - func short desc
* @param1: foo
* ...
*
* Func long description (IMHO, often optional if not some major API).
*
* Returns: important info about return value
*/
You'll find plenty of examples with varying quality with grep but the most
imporant bits are the return value and parameters, and if there are
caveats the caller should know, the long desciption might be handy.
> > > + err = -ENODEV;
> > > + pr_warn("Can not switch MUX to dGPU mode when dGPU is disabled: %d\n", err);
> > > + return err;
> > > + }
> > > + }
> > > +
> > > + if (asus_bioscfg.egpu_enable_available) {
> > > + err = asus_wmi_get_devstate_dsts(ASUS_WMI_DEVID_EGPU, &result);
> > > + if (err)
> > > + return err;
> > > + if (result && !optimus) {
> > > + err = -ENODEV;
> > > + pr_warn("Can not switch MUX to dGPU mode when eGPU is enabled: %d\n", err);
> > > + return err;
> > > + }
> > > + }
> > > +
> > > + err = asus_wmi_set_devstate(asus_bioscfg.gpu_mux_dev_id, optimus, &result);
> > > + if (err) {
> > > + pr_err("%s Failed to set GPU MUX mode: %d\nn", __func__, err);
> >
> > Never use __func__ for messages shown to normal user.
>
> Must have been a holdover from debug. Also wasn't aware of that rule, thanks.
For pr_debug() I can give some leeway but for anything info/warn/error
should definitely be user readable and preferrably understandable too :-D.
> > > + return err;
> > > + }
> > > + /* !1 is considered a fail by ASUS */
> >
> > If the interface is documented with kerneldoc, this is unnecessary
> > comment. Is 0 also a failure (this differs from >1 checks elsewhere)?
>
> I've changed the other checks to match. But I'll also try and do a
> deeper analysis of those particular WMI functions to see if I can find
> the actual causes for other returns and their significance (0 and 2). 1
> is most definitely success though.
Understood, I don't expect you to know everything about these interfaces
but the difference just caught my eye so I mentioned it in case it's a
mistake.
> > As general feel of the readiness of this code, I suspect there were many
> > more problems which I failed to notice :-(.
>
> I'd put money on it (sorry). I definitely should have cleaned up better
> than I did so you weren't pointing out silly little things, but I was
> never expecting to get over the line on the first try and desperately
> needed some insight for the overall patch series to see if what I was
> doing was actually going to be acceptable or not.
It's not problem in itself if you were just after an early review of the
concept but there are little things like not maintaining consistency in
variable naming which easily trips one here and there which I really
recommend you change because then many patterns can be checked with grep /
find if things are consistent (and it helps the code reader too if the
variable naming doesn't suddenly became opposite of what it was in the
previous function).
> As always, thanks so much for your time and review.
--
i.
^ permalink raw reply [flat|nested] 22+ messages in thread
* Re: [PATCH 1/5] platform/x86 asus-bioscfg: move existing tunings to asus-bioscfg module
2024-07-16 5:16 ` [PATCH 1/5] platform/x86 asus-bioscfg: move existing tunings to asus-bioscfg module Luke D. Jones
2024-07-16 9:45 ` Ilpo Järvinen
@ 2024-07-16 9:50 ` Hans de Goede
2024-07-16 10:48 ` Luke Jones
1 sibling, 1 reply; 22+ messages in thread
From: Hans de Goede @ 2024-07-16 9:50 UTC (permalink / raw)
To: Luke D. Jones, platform-driver-x86
Cc: corentin.chary, ilpo.jarvinen, mario.limonciello, linux-kernel
Hi Luke,
On 7/16/24 7:16 AM, Luke D. Jones wrote:
> The fw_attributes_class provides a much cleaner interface to all of the
> attributes introduced to asus-wmi. This patch moves all of these extra
> attributes over to fw_attributes_class, and shifts the bulk of these
> definitions to a new kernel module to reduce the clutter of asus-wmi
> with the intention of deprecating the asus-wmi attributes in future.
>
> The work applies only to WMI methods which don't have a clearly defined
> place within the sysfs and as a result ended up lumped together in
> /sys/devices/platform/asus-nb-wmi/ with no standard API.
>
> Where possible the fw attrs now implement defaults, min, max, scalar,
> choices, etc. As en example dgpu_disable becomes:
>
> /sys/class/firmware-attributes/asus-bioscfg/attributes/dgpu_disable/
> ├── current_value
> ├── display_name
> ├── possible_values
> └── type
>
> as do other attributes.
>
> Signed-off-by: Luke D. Jones <luke@ljones.dev>
Interesting (also see my reply to the cover-letter).
Note this is not a full review, just a few small remarks
with things which I noticed while taking a quick look.
<snip>
> +static bool asus_bios_requires_reboot(struct kobj_attribute *attr)
> +{
> + return !strcmp(attr->attr.name, "gpu_mux_mode");
> + !strcmp(attr->attr.name, "panel_hd_mode");
> +}
The second statement here is never reached, I believe you want
a || between the 2 strcmp() calls:
static bool asus_bios_requires_reboot(struct kobj_attribute *attr)
{
return !strcmp(attr->attr.name, "gpu_mux_mode") ||
!strcmp(attr->attr.name, "panel_hd_mode");
}
<snip>
> +/* Simple attribute creation */
I think it would be good to do the following for registering
these "simple" attributes ... continued below.
> +ATTR_GROUP_ENUM_INT_RW(thermal_policy, "thermal_policy", ASUS_WMI_DEVID_THROTTLE_THERMAL_POLICY,
> + 0, 3, "0;1;2", "Set the thermal profile: 0=normal, 1=performance, 2=quiet");
> +ATTR_GROUP_PPT_RW(ppt_pl2_sppt, "ppt_pl2_sppt", ASUS_WMI_DEVID_PPT_PL2_SPPT,
> + cpu_default, 5, cpu_max, 1, "Set the CPU fast package limit");
> +ATTR_GROUP_PPT_RW(ppt_apu_sppt, "ppt_apu_sppt", ASUS_WMI_DEVID_PPT_APU_SPPT,
> + platform_default, 5, platform_max, 1, "Set the CPU slow package limit");
> +ATTR_GROUP_PPT_RW(ppt_platform_sppt, "ppt_platform_sppt", ASUS_WMI_DEVID_PPT_PLAT_SPPT,
> + platform_default, 5, platform_max, 1, "Set the CPU slow package limit");
> +ATTR_GROUP_PPT_RW(ppt_fppt, "ppt_fppt", ASUS_WMI_DEVID_PPT_FPPT,
> + cpu_default, 5, cpu_max, 1, "Set the CPU slow package limit");
> +
> +ATTR_GROUP_PPT_RW(nv_dynamic_boost, "nv_dynamic_boost", ASUS_WMI_DEVID_NV_DYN_BOOST,
> + nv_boost_default, 5, nv_boost_max, 1, "Set the Nvidia dynamic boost limit");
> +ATTR_GROUP_PPT_RW(nv_temp_target, "nv_temp_target", ASUS_WMI_DEVID_NV_THERM_TARGET,
> + nv_temp_default, 75, nv_temp_max, 1, "Set the Nvidia max thermal limit");
> +
> +ATTR_GROUP_ENUM_INT_RO(charge_mode, "charge_mode", ASUS_WMI_DEVID_CHARGE_MODE,
> + "0;1;2", "Show the current mode of charging");
> +ATTR_GROUP_BOOL_RW(boot_sound, "boot_sound", ASUS_WMI_DEVID_BOOT_SOUND,
> + "Set the boot POST sound");
> +ATTR_GROUP_BOOL_RW(mcu_powersave, "mcu_powersave", ASUS_WMI_DEVID_MCU_POWERSAVE,
> + "Set MCU powersaving mode");
> +ATTR_GROUP_BOOL_RW(panel_od, "panel_overdrive", ASUS_WMI_DEVID_PANEL_OD,
> + "Set the panel refresh overdrive");
> +ATTR_GROUP_BOOL_RW(panel_hd_mode, "panel_hd_mode", ASUS_WMI_DEVID_PANEL_HD,
> + "Set the panel HD mode to UHD<0> or FHD<1>");
> +ATTR_GROUP_BOOL_RO(egpu_connected, "egpu_connected", ASUS_WMI_DEVID_EGPU_CONNECTED,
> + "Show the eGPU connection status");
Create an array of simple attribute groups for this
entire set of simple attributes:
struct asus_attr_group {
const struct attribute_group *attr_group;
u32 wmi_devid;
};
static const struct asus_attr_group simple_attribute_groups[] = {
{ &thermal_policy_attr_group, ASUS_WMI_DEVID_THROTTLE_THERMAL_POLICY },
{ &ppt_pl2_sppt_attr_group, ASUS_WMI_DEVID_THROTTLE_THERMAL_POLICY },
...
{ &egpu_connected_attr_group, ASUS_WMI_DEVID_EGPU_CONNECTED },
};
And then in asus_fw_attr_add() .. continued below:
> +static int asus_fw_attr_add(void)
> +{
> + int ret;
> +
> + ret = fw_attributes_class_get(&fw_attr_class);
> + if (ret)
> + goto fail_class_created;
> + else
> + asus_bioscfg.fw_attr_dev = device_create(fw_attr_class, NULL,
> + MKDEV(0, 0), NULL, "%s", DRIVER_NAME);
> +
> + if (IS_ERR(asus_bioscfg.fw_attr_dev)) {
> + ret = PTR_ERR(asus_bioscfg.fw_attr_dev);
> + goto fail_class_created;
> + }
> +
> + asus_bioscfg.fw_attr_kset = kset_create_and_add("attributes", NULL,
> + &asus_bioscfg.fw_attr_dev->kobj);
> + if (!asus_bioscfg.fw_attr_dev) {
> + ret = -ENOMEM;
> + pr_debug("Failed to create and add attributes\n");
> + goto err_destroy_classdev;
> + }
> +
> + /* Add any firmware_attributes required */
> + ret = sysfs_create_file(&asus_bioscfg.fw_attr_kset->kobj, &pending_reboot.attr);
> + if (ret) {
> + pr_warn("Failed to create sysfs level attributes\n");
> + goto fail_class_created;
> + }
> +
> + // TODO: logging
> + asus_bioscfg.mini_led_dev_id = 0;
> + if (asus_wmi_is_present(ASUS_WMI_DEVID_MINI_LED_MODE)) {
> + asus_bioscfg.mini_led_dev_id = ASUS_WMI_DEVID_MINI_LED_MODE;
> + sysfs_create_group(&asus_bioscfg.fw_attr_kset->kobj, &mini_led_mode_attr_group);
> + } else if (asus_wmi_is_present(ASUS_WMI_DEVID_MINI_LED_MODE2)) {
> + asus_bioscfg.mini_led_dev_id = ASUS_WMI_DEVID_MINI_LED_MODE2;
> + sysfs_create_group(&asus_bioscfg.fw_attr_kset->kobj, &mini_led_mode_attr_group);
> + }
> +
> + if (asus_wmi_is_present(ASUS_WMI_DEVID_GPU_MUX)) {
> + asus_bioscfg.gpu_mux_dev_id = ASUS_WMI_DEVID_GPU_MUX;
> + sysfs_create_group(&asus_bioscfg.fw_attr_kset->kobj, &gpu_mux_mode_attr_group);
> + } else if (asus_wmi_is_present(ASUS_WMI_DEVID_GPU_MUX_VIVO)) {
> + asus_bioscfg.gpu_mux_dev_id = ASUS_WMI_DEVID_GPU_MUX_VIVO;
> + sysfs_create_group(&asus_bioscfg.fw_attr_kset->kobj, &gpu_mux_mode_attr_group);
> + }
> +
> + if (asus_wmi_is_present(ASUS_WMI_DEVID_DGPU)) {
> + asus_bioscfg.dgpu_disable_available = true;
> + sysfs_create_group(&asus_bioscfg.fw_attr_kset->kobj, &dgpu_disable_attr_group);
> + }
> + if (asus_wmi_is_present(ASUS_WMI_DEVID_EGPU)) {
> + asus_bioscfg.egpu_enable_available = true;
> + sysfs_create_group(&asus_bioscfg.fw_attr_kset->kobj, &egpu_enable_attr_group);
> + }
Replace the block starting here and ending ...
> + if (asus_wmi_is_present(ASUS_WMI_DEVID_EGPU_CONNECTED))
> + sysfs_create_group(&asus_bioscfg.fw_attr_kset->kobj, &egpu_connected_attr_group);
> +
> + if (asus_wmi_is_present(ASUS_WMI_DEVID_THROTTLE_THERMAL_POLICY))
> + sysfs_create_group(&asus_bioscfg.fw_attr_kset->kobj, &thermal_policy_attr_group);
> + if (asus_wmi_is_present(ASUS_WMI_DEVID_PPT_PL1_SPL))
> + sysfs_create_group(&asus_bioscfg.fw_attr_kset->kobj, &ppt_pl1_spl_attr_group);
> + if (asus_wmi_is_present(ASUS_WMI_DEVID_PPT_PL2_SPPT))
> + sysfs_create_group(&asus_bioscfg.fw_attr_kset->kobj, &ppt_pl2_sppt_attr_group);
> + if (asus_wmi_is_present(ASUS_WMI_DEVID_PPT_APU_SPPT))
> + sysfs_create_group(&asus_bioscfg.fw_attr_kset->kobj, &ppt_apu_sppt_attr_group);
> + if (asus_wmi_is_present(ASUS_WMI_DEVID_PPT_PLAT_SPPT))
> + sysfs_create_group(&asus_bioscfg.fw_attr_kset->kobj, &ppt_platform_sppt_attr_group);
> + if (asus_wmi_is_present(ASUS_WMI_DEVID_PPT_FPPT))
> + sysfs_create_group(&asus_bioscfg.fw_attr_kset->kobj, &ppt_fppt_attr_group);
> +
> + if (asus_wmi_is_present(ASUS_WMI_DEVID_NV_DYN_BOOST))
> + sysfs_create_group(&asus_bioscfg.fw_attr_kset->kobj, &nv_dynamic_boost_attr_group);
> + if (asus_wmi_is_present(ASUS_WMI_DEVID_NV_THERM_TARGET))
> + sysfs_create_group(&asus_bioscfg.fw_attr_kset->kobj, &nv_temp_target_attr_group);
> +
> + if (asus_wmi_is_present(ASUS_WMI_DEVID_CHARGE_MODE))
> + sysfs_create_group(&asus_bioscfg.fw_attr_kset->kobj, &charge_mode_attr_group);
> + if (asus_wmi_is_present(ASUS_WMI_DEVID_BOOT_SOUND))
> + sysfs_create_group(&asus_bioscfg.fw_attr_kset->kobj, &boot_sound_attr_group);
> + if (asus_wmi_is_present(ASUS_WMI_DEVID_MCU_POWERSAVE))
> + sysfs_create_group(&asus_bioscfg.fw_attr_kset->kobj, &mcu_powersave_attr_group);
> + if (asus_wmi_is_present(ASUS_WMI_DEVID_PANEL_OD))
> + sysfs_create_group(&asus_bioscfg.fw_attr_kset->kobj, &panel_od_attr_group);
> + if (asus_wmi_is_present(ASUS_WMI_DEVID_PANEL_HD))
> + sysfs_create_group(&asus_bioscfg.fw_attr_kset->kobj, &panel_hd_mode_attr_group);
here, with:
for (i = 0; i < ARRAY_SIZE(simple_attribute_groups); i++) {
if (!asus_wmi_is_present(simple_attribute_groups[i].wmi_devid))
continue;
sysfs_create_group(&asus_bioscfg.fw_attr_kset->kobj, simple_attribute_groups[i].attr_group);
pr_dbg("Created sysfs-group for %s\n", simple_attribute_groups[i].attr_group->name);
}
This will make adding new simple attributes a matter of just:
1. Declaring the new attr using one of the macros
2. Adding it to simple_attribute_groups[]
And this also takes care of you logging TODO for simple attributes
without needing to add a ton of pr_debug() calls.
Regards,
Hans
^ permalink raw reply [flat|nested] 22+ messages in thread* Re: [PATCH 1/5] platform/x86 asus-bioscfg: move existing tunings to asus-bioscfg module
2024-07-16 9:50 ` Hans de Goede
@ 2024-07-16 10:48 ` Luke Jones
0 siblings, 0 replies; 22+ messages in thread
From: Luke Jones @ 2024-07-16 10:48 UTC (permalink / raw)
To: Hans de Goede, platform-driver-x86
Cc: corentin.chary, Ilpo Järvinen, Mario Limonciello,
linux-kernel
On Tue, 16 Jul 2024, at 9:50 PM, Hans de Goede wrote:
> Hi Luke,
>
> On 7/16/24 7:16 AM, Luke D. Jones wrote:
> > The fw_attributes_class provides a much cleaner interface to all of the
> > attributes introduced to asus-wmi. This patch moves all of these extra
> > attributes over to fw_attributes_class, and shifts the bulk of these
> > definitions to a new kernel module to reduce the clutter of asus-wmi
> > with the intention of deprecating the asus-wmi attributes in future.
> >
> > The work applies only to WMI methods which don't have a clearly defined
> > place within the sysfs and as a result ended up lumped together in
> > /sys/devices/platform/asus-nb-wmi/ with no standard API.
> >
> > Where possible the fw attrs now implement defaults, min, max, scalar,
> > choices, etc. As en example dgpu_disable becomes:
> >
> > /sys/class/firmware-attributes/asus-bioscfg/attributes/dgpu_disable/
> > ├── current_value
> > ├── display_name
> > ├── possible_values
> > └── type
> >
> > as do other attributes.
> >
> > Signed-off-by: Luke D. Jones <luke@ljones.dev>
>
> Interesting (also see my reply to the cover-letter).
>
> Note this is not a full review, just a few small remarks
> with things which I noticed while taking a quick look.
>
> <snip>
>
> > +static bool asus_bios_requires_reboot(struct kobj_attribute *attr)
> > +{
> > + return !strcmp(attr->attr.name, "gpu_mux_mode");
> > + !strcmp(attr->attr.name, "panel_hd_mode");
> > +}
>
> The second statement here is never reached, I believe you want
> a || between the 2 strcmp() calls:
>
> static bool asus_bios_requires_reboot(struct kobj_attribute *attr)
> {
> return !strcmp(attr->attr.name, "gpu_mux_mode") ||
> !strcmp(attr->attr.name, "panel_hd_mode");
> }
>
> <snip>
>
> > +/* Simple attribute creation */
>
> I think it would be good to do the following for registering
> these "simple" attributes ... continued below.
>
> > +ATTR_GROUP_ENUM_INT_RW(thermal_policy, "thermal_policy", ASUS_WMI_DEVID_THROTTLE_THERMAL_POLICY,
> > + 0, 3, "0;1;2", "Set the thermal profile: 0=normal, 1=performance, 2=quiet");
> > +ATTR_GROUP_PPT_RW(ppt_pl2_sppt, "ppt_pl2_sppt", ASUS_WMI_DEVID_PPT_PL2_SPPT,
> > + cpu_default, 5, cpu_max, 1, "Set the CPU fast package limit");
> > +ATTR_GROUP_PPT_RW(ppt_apu_sppt, "ppt_apu_sppt", ASUS_WMI_DEVID_PPT_APU_SPPT,
> > + platform_default, 5, platform_max, 1, "Set the CPU slow package limit");
> > +ATTR_GROUP_PPT_RW(ppt_platform_sppt, "ppt_platform_sppt", ASUS_WMI_DEVID_PPT_PLAT_SPPT,
> > + platform_default, 5, platform_max, 1, "Set the CPU slow package limit");
> > +ATTR_GROUP_PPT_RW(ppt_fppt, "ppt_fppt", ASUS_WMI_DEVID_PPT_FPPT,
> > + cpu_default, 5, cpu_max, 1, "Set the CPU slow package limit");
> > +
> > +ATTR_GROUP_PPT_RW(nv_dynamic_boost, "nv_dynamic_boost", ASUS_WMI_DEVID_NV_DYN_BOOST,
> > + nv_boost_default, 5, nv_boost_max, 1, "Set the Nvidia dynamic boost limit");
> > +ATTR_GROUP_PPT_RW(nv_temp_target, "nv_temp_target", ASUS_WMI_DEVID_NV_THERM_TARGET,
> > + nv_temp_default, 75, nv_temp_max, 1, "Set the Nvidia max thermal limit");
> > +
> > +ATTR_GROUP_ENUM_INT_RO(charge_mode, "charge_mode", ASUS_WMI_DEVID_CHARGE_MODE,
> > + "0;1;2", "Show the current mode of charging");
> > +ATTR_GROUP_BOOL_RW(boot_sound, "boot_sound", ASUS_WMI_DEVID_BOOT_SOUND,
> > + "Set the boot POST sound");
> > +ATTR_GROUP_BOOL_RW(mcu_powersave, "mcu_powersave", ASUS_WMI_DEVID_MCU_POWERSAVE,
> > + "Set MCU powersaving mode");
> > +ATTR_GROUP_BOOL_RW(panel_od, "panel_overdrive", ASUS_WMI_DEVID_PANEL_OD,
> > + "Set the panel refresh overdrive");
> > +ATTR_GROUP_BOOL_RW(panel_hd_mode, "panel_hd_mode", ASUS_WMI_DEVID_PANEL_HD,
> > + "Set the panel HD mode to UHD<0> or FHD<1>");
> > +ATTR_GROUP_BOOL_RO(egpu_connected, "egpu_connected", ASUS_WMI_DEVID_EGPU_CONNECTED,
> > + "Show the eGPU connection status");
>
> Create an array of simple attribute groups for this
> entire set of simple attributes:
>
> struct asus_attr_group {
> const struct attribute_group *attr_group;
> u32 wmi_devid;
> };
>
> static const struct asus_attr_group simple_attribute_groups[] = {
> { &thermal_policy_attr_group, ASUS_WMI_DEVID_THROTTLE_THERMAL_POLICY },
> { &ppt_pl2_sppt_attr_group, ASUS_WMI_DEVID_THROTTLE_THERMAL_POLICY },
> ...
> { &egpu_connected_attr_group, ASUS_WMI_DEVID_EGPU_CONNECTED },
> };
>
> And then in asus_fw_attr_add() .. continued below:
>
> > +static int asus_fw_attr_add(void)
> > +{
> > + int ret;
> > +
> > + ret = fw_attributes_class_get(&fw_attr_class);
> > + if (ret)
> > + goto fail_class_created;
> > + else
> > + asus_bioscfg.fw_attr_dev = device_create(fw_attr_class, NULL,
> > + MKDEV(0, 0), NULL, "%s", DRIVER_NAME);
> > +
> > + if (IS_ERR(asus_bioscfg.fw_attr_dev)) {
> > + ret = PTR_ERR(asus_bioscfg.fw_attr_dev);
> > + goto fail_class_created;
> > + }
> > +
> > + asus_bioscfg.fw_attr_kset = kset_create_and_add("attributes", NULL,
> > + &asus_bioscfg.fw_attr_dev->kobj);
> > + if (!asus_bioscfg.fw_attr_dev) {
> > + ret = -ENOMEM;
> > + pr_debug("Failed to create and add attributes\n");
> > + goto err_destroy_classdev;
> > + }
> > +
> > + /* Add any firmware_attributes required */
> > + ret = sysfs_create_file(&asus_bioscfg.fw_attr_kset->kobj, &pending_reboot.attr);
> > + if (ret) {
> > + pr_warn("Failed to create sysfs level attributes\n");
> > + goto fail_class_created;
> > + }
> > +
> > + // TODO: logging
> > + asus_bioscfg.mini_led_dev_id = 0;
> > + if (asus_wmi_is_present(ASUS_WMI_DEVID_MINI_LED_MODE)) {
> > + asus_bioscfg.mini_led_dev_id = ASUS_WMI_DEVID_MINI_LED_MODE;
> > + sysfs_create_group(&asus_bioscfg.fw_attr_kset->kobj, &mini_led_mode_attr_group);
> > + } else if (asus_wmi_is_present(ASUS_WMI_DEVID_MINI_LED_MODE2)) {
> > + asus_bioscfg.mini_led_dev_id = ASUS_WMI_DEVID_MINI_LED_MODE2;
> > + sysfs_create_group(&asus_bioscfg.fw_attr_kset->kobj, &mini_led_mode_attr_group);
> > + }
> > +
> > + if (asus_wmi_is_present(ASUS_WMI_DEVID_GPU_MUX)) {
> > + asus_bioscfg.gpu_mux_dev_id = ASUS_WMI_DEVID_GPU_MUX;
> > + sysfs_create_group(&asus_bioscfg.fw_attr_kset->kobj, &gpu_mux_mode_attr_group);
> > + } else if (asus_wmi_is_present(ASUS_WMI_DEVID_GPU_MUX_VIVO)) {
> > + asus_bioscfg.gpu_mux_dev_id = ASUS_WMI_DEVID_GPU_MUX_VIVO;
> > + sysfs_create_group(&asus_bioscfg.fw_attr_kset->kobj, &gpu_mux_mode_attr_group);
> > + }
> > +
> > + if (asus_wmi_is_present(ASUS_WMI_DEVID_DGPU)) {
> > + asus_bioscfg.dgpu_disable_available = true;
> > + sysfs_create_group(&asus_bioscfg.fw_attr_kset->kobj, &dgpu_disable_attr_group);
> > + }
> > + if (asus_wmi_is_present(ASUS_WMI_DEVID_EGPU)) {
> > + asus_bioscfg.egpu_enable_available = true;
> > + sysfs_create_group(&asus_bioscfg.fw_attr_kset->kobj, &egpu_enable_attr_group);
> > + }
>
> Replace the block starting here and ending ...
>
> > + if (asus_wmi_is_present(ASUS_WMI_DEVID_EGPU_CONNECTED))
> > + sysfs_create_group(&asus_bioscfg.fw_attr_kset->kobj, &egpu_connected_attr_group);
> > +
> > + if (asus_wmi_is_present(ASUS_WMI_DEVID_THROTTLE_THERMAL_POLICY))
> > + sysfs_create_group(&asus_bioscfg.fw_attr_kset->kobj, &thermal_policy_attr_group);
> > + if (asus_wmi_is_present(ASUS_WMI_DEVID_PPT_PL1_SPL))
> > + sysfs_create_group(&asus_bioscfg.fw_attr_kset->kobj, &ppt_pl1_spl_attr_group);
> > + if (asus_wmi_is_present(ASUS_WMI_DEVID_PPT_PL2_SPPT))
> > + sysfs_create_group(&asus_bioscfg.fw_attr_kset->kobj, &ppt_pl2_sppt_attr_group);
> > + if (asus_wmi_is_present(ASUS_WMI_DEVID_PPT_APU_SPPT))
> > + sysfs_create_group(&asus_bioscfg.fw_attr_kset->kobj, &ppt_apu_sppt_attr_group);
> > + if (asus_wmi_is_present(ASUS_WMI_DEVID_PPT_PLAT_SPPT))
> > + sysfs_create_group(&asus_bioscfg.fw_attr_kset->kobj, &ppt_platform_sppt_attr_group);
> > + if (asus_wmi_is_present(ASUS_WMI_DEVID_PPT_FPPT))
> > + sysfs_create_group(&asus_bioscfg.fw_attr_kset->kobj, &ppt_fppt_attr_group);
> > +
> > + if (asus_wmi_is_present(ASUS_WMI_DEVID_NV_DYN_BOOST))
> > + sysfs_create_group(&asus_bioscfg.fw_attr_kset->kobj, &nv_dynamic_boost_attr_group);
> > + if (asus_wmi_is_present(ASUS_WMI_DEVID_NV_THERM_TARGET))
> > + sysfs_create_group(&asus_bioscfg.fw_attr_kset->kobj, &nv_temp_target_attr_group);
> > +
> > + if (asus_wmi_is_present(ASUS_WMI_DEVID_CHARGE_MODE))
> > + sysfs_create_group(&asus_bioscfg.fw_attr_kset->kobj, &charge_mode_attr_group);
> > + if (asus_wmi_is_present(ASUS_WMI_DEVID_BOOT_SOUND))
> > + sysfs_create_group(&asus_bioscfg.fw_attr_kset->kobj, &boot_sound_attr_group);
> > + if (asus_wmi_is_present(ASUS_WMI_DEVID_MCU_POWERSAVE))
> > + sysfs_create_group(&asus_bioscfg.fw_attr_kset->kobj, &mcu_powersave_attr_group);
> > + if (asus_wmi_is_present(ASUS_WMI_DEVID_PANEL_OD))
> > + sysfs_create_group(&asus_bioscfg.fw_attr_kset->kobj, &panel_od_attr_group);
> > + if (asus_wmi_is_present(ASUS_WMI_DEVID_PANEL_HD))
> > + sysfs_create_group(&asus_bioscfg.fw_attr_kset->kobj, &panel_hd_mode_attr_group);
>
> here, with:
>
> for (i = 0; i < ARRAY_SIZE(simple_attribute_groups); i++) {
> if (!asus_wmi_is_present(simple_attribute_groups[i].wmi_devid))
> continue;
>
> sysfs_create_group(&asus_bioscfg.fw_attr_kset->kobj, simple_attribute_groups[i].attr_group);
> pr_dbg("Created sysfs-group for %s\n", simple_attribute_groups[i].attr_group->name);
> }
>
> This will make adding new simple attributes a matter of just:
>
> 1. Declaring the new attr using one of the macros
> 2. Adding it to simple_attribute_groups[]
>
> And this also takes care of you logging TODO for simple attributes
> without needing to add a ton of pr_debug() calls.
Ah perfect, this was one problem I ws trying to figure out a better way of doing. I'll have a crack at this after addressing all other concerns mentioned so far.
Cheers,
Luke.
>
> Regards,
>
> Hans
>
>
>
^ permalink raw reply [flat|nested] 22+ messages in thread
* [PATCH 2/5] platform/x86: asus-bioscfg: add dgpu tgp control
2024-07-16 5:16 [PATCH 0/5] platform/x86: introduce asus-bioscfg Luke D. Jones
2024-07-16 5:16 ` [PATCH 1/5] platform/x86 asus-bioscfg: move existing tunings to asus-bioscfg module Luke D. Jones
@ 2024-07-16 5:16 ` Luke D. Jones
2024-08-21 21:18 ` Mario Limonciello
2024-07-16 5:16 ` [PATCH 3/5] platform/x86: asus-bioscfg: add apu-mem control support Luke D. Jones
` (3 subsequent siblings)
5 siblings, 1 reply; 22+ messages in thread
From: Luke D. Jones @ 2024-07-16 5:16 UTC (permalink / raw)
To: platform-driver-x86
Cc: corentin.chary, hdegoede, ilpo.jarvinen, mario.limonciello,
linux-kernel, Luke D. Jones
Implement the dgpu TGP control under the asus-bioscfg module using the
fw_attributes class.
Signed-off-by: Luke D. Jones <luke@ljones.dev>
---
drivers/platform/x86/asus-bioscfg.c | 9 +++++++
drivers/platform/x86/asus-bioscfg.h | 30 ++++++++++++++++++++++
include/linux/platform_data/x86/asus-wmi.h | 3 +++
3 files changed, 42 insertions(+)
diff --git a/drivers/platform/x86/asus-bioscfg.c b/drivers/platform/x86/asus-bioscfg.c
index 0b34e727aab4..551b7dbd8fe7 100644
--- a/drivers/platform/x86/asus-bioscfg.c
+++ b/drivers/platform/x86/asus-bioscfg.c
@@ -52,6 +52,7 @@ MODULE_ALIAS("wmi:"ASUS_NB_WMI_EVENT_GUID);
#define NVIDIA_BOOST_MAX 25
#define NVIDIA_TEMP_MIN 75
#define NVIDIA_TEMP_MAX 87
+#define NVIDIA_GPU_POWER_MAX 70
/* Tunables provided by ASUS for gaming laptops */
struct rog_tunables {
@@ -443,6 +444,10 @@ ATTR_GROUP_PPT_RW(nv_dynamic_boost, "nv_dynamic_boost", ASUS_WMI_DEVID_NV_DYN_BO
nv_boost_default, 5, nv_boost_max, 1, "Set the Nvidia dynamic boost limit");
ATTR_GROUP_PPT_RW(nv_temp_target, "nv_temp_target", ASUS_WMI_DEVID_NV_THERM_TARGET,
nv_temp_default, 75, nv_temp_max, 1, "Set the Nvidia max thermal limit");
+ATTR_GROUP_INT_VALUE_ONLY_RO(dgpu_base_tgp, "dgpu_base_tgp", ASUS_WMI_DEVID_DGPU_BASE_TGP,
+ "Read the base TGP value")
+ATTR_GROUP_INT_RW(dgpu_tgp, "dgpu_tgp", ASUS_WMI_DEVID_DGPU_SET_TGP,
+ 70, 0, NVIDIA_GPU_POWER_MAX, 1, "Set the additional TGP on top of the base TGP");
ATTR_GROUP_ENUM_INT_RO(charge_mode, "charge_mode", ASUS_WMI_DEVID_CHARGE_MODE,
"0;1;2", "Show the current mode of charging");
@@ -534,6 +539,10 @@ static int asus_fw_attr_add(void)
sysfs_create_group(&asus_bioscfg.fw_attr_kset->kobj, &nv_dynamic_boost_attr_group);
if (asus_wmi_is_present(ASUS_WMI_DEVID_NV_THERM_TARGET))
sysfs_create_group(&asus_bioscfg.fw_attr_kset->kobj, &nv_temp_target_attr_group);
+ if (asus_wmi_is_present(ASUS_WMI_DEVID_DGPU_BASE_TGP))
+ sysfs_create_group(&asus_bioscfg.fw_attr_kset->kobj, &dgpu_base_tgp_attr_group);
+ if (asus_wmi_is_present(ASUS_WMI_DEVID_DGPU_SET_TGP))
+ sysfs_create_group(&asus_bioscfg.fw_attr_kset->kobj, &dgpu_tgp_attr_group);
if (asus_wmi_is_present(ASUS_WMI_DEVID_CHARGE_MODE))
sysfs_create_group(&asus_bioscfg.fw_attr_kset->kobj, &charge_mode_attr_group);
diff --git a/drivers/platform/x86/asus-bioscfg.h b/drivers/platform/x86/asus-bioscfg.h
index 403563c25f53..2da55a91ff0b 100644
--- a/drivers/platform/x86/asus-bioscfg.h
+++ b/drivers/platform/x86/asus-bioscfg.h
@@ -113,6 +113,22 @@ static ssize_t _attrname##_##_prop##_show(struct kobject *kobj, \
static struct kobj_attribute attr_##_attrname##_##_prop = \
__ASUS_ATTR_RO(_attrname, _prop)
+/* Requires current_value show&|store */
+#define __ATTR_GROUP_INT_VALUE_ONLY(_attrname, _fsname, _dispname) \
+__ATTR_SHOW_FMT(display_name, _attrname, "%s\n", _dispname); \
+static struct kobj_attribute attr_##_attrname##_type = \
+ __ASUS_ATTR_RO_AS(type, int_type_show); \
+static struct attribute *_attrname##_attrs[] = { \
+ &attr_##_attrname##_current_value.attr, \
+ &attr_##_attrname##_display_name.attr, \
+ &attr_##_attrname##_type.attr, \
+ NULL \
+}; \
+static const struct attribute_group _attrname##_attr_group = { \
+ .name = _fsname, \
+ .attrs = _attrname##_attrs \
+}
+
/* Int style min/max range, base macro. Requires current_value show&|store */
#define __ATTR_GROUP_INT(_attrname, _fsname, _default, \
_min, _max, _incstep, _dispname)\
@@ -156,6 +172,20 @@ static const struct attribute_group _attrname##_attr_group = { \
.attrs = _attrname##_attrs \
}
+#define ATTR_GROUP_INT_VALUE_ONLY_RO(_attrname, _fsname, _wmi, _dispname) \
+do { \
+ __ATTR_CURRENT_INT_RO(_attrname, _wmi); \
+ __ATTR_GROUP_INT_VALUE_ONLY(_attrname, _fsname, _dispname); \
+} while (0)
+
+#define ATTR_GROUP_INT_RW(_attrname, _fsname, _wmi, _default, _min, \
+ _max, _incstep, _dispname) \
+do { \
+ __ATTR_CURRENT_INT_RW(_attrname, _min, _max, _wmi); \
+ __ATTR_GROUP_INT(_attrname, _fsname, _default, _min, _max, \
+ _incstep, _dispname); \
+} while (0)
+
#define ATTR_GROUP_BOOL_RO(_attrname, _fsname, _wmi, _dispname) \
do { \
__ATTR_CURRENT_INT_RO(_attrname, _wmi); \
diff --git a/include/linux/platform_data/x86/asus-wmi.h b/include/linux/platform_data/x86/asus-wmi.h
index 889336a932fb..c54264653d75 100644
--- a/include/linux/platform_data/x86/asus-wmi.h
+++ b/include/linux/platform_data/x86/asus-wmi.h
@@ -128,6 +128,9 @@
/* dgpu on/off */
#define ASUS_WMI_DEVID_DGPU 0x00090020
+#define ASUS_WMI_DEVID_DGPU_BASE_TGP 0x00120099
+#define ASUS_WMI_DEVID_DGPU_SET_TGP 0x00120098
+
/* gpu mux switch, 0 = dGPU, 1 = Optimus */
#define ASUS_WMI_DEVID_GPU_MUX 0x00090016
#define ASUS_WMI_DEVID_GPU_MUX_VIVO 0x00090026
--
2.45.2
^ permalink raw reply related [flat|nested] 22+ messages in thread* Re: [PATCH 2/5] platform/x86: asus-bioscfg: add dgpu tgp control
2024-07-16 5:16 ` [PATCH 2/5] platform/x86: asus-bioscfg: add dgpu tgp control Luke D. Jones
@ 2024-08-21 21:18 ` Mario Limonciello
0 siblings, 0 replies; 22+ messages in thread
From: Mario Limonciello @ 2024-08-21 21:18 UTC (permalink / raw)
To: Luke D. Jones, platform-driver-x86
Cc: corentin.chary, hdegoede, ilpo.jarvinen, linux-kernel
On 7/16/2024 00:16, Luke D. Jones wrote:
> Implement the dgpu TGP control under the asus-bioscfg module using the
> fw_attributes class.
A layman will probably have no idea what a TGP is.
Can you please explain what TGP is in the commit message?
>
> Signed-off-by: Luke D. Jones <luke@ljones.dev>
> ---
> drivers/platform/x86/asus-bioscfg.c | 9 +++++++
> drivers/platform/x86/asus-bioscfg.h | 30 ++++++++++++++++++++++
> include/linux/platform_data/x86/asus-wmi.h | 3 +++
> 3 files changed, 42 insertions(+)
>
> diff --git a/drivers/platform/x86/asus-bioscfg.c b/drivers/platform/x86/asus-bioscfg.c
> index 0b34e727aab4..551b7dbd8fe7 100644
> --- a/drivers/platform/x86/asus-bioscfg.c
> +++ b/drivers/platform/x86/asus-bioscfg.c
> @@ -52,6 +52,7 @@ MODULE_ALIAS("wmi:"ASUS_NB_WMI_EVENT_GUID);
> #define NVIDIA_BOOST_MAX 25
> #define NVIDIA_TEMP_MIN 75
> #define NVIDIA_TEMP_MAX 87
> +#define NVIDIA_GPU_POWER_MAX 70
>
> /* Tunables provided by ASUS for gaming laptops */
> struct rog_tunables {
> @@ -443,6 +444,10 @@ ATTR_GROUP_PPT_RW(nv_dynamic_boost, "nv_dynamic_boost", ASUS_WMI_DEVID_NV_DYN_BO
> nv_boost_default, 5, nv_boost_max, 1, "Set the Nvidia dynamic boost limit");
> ATTR_GROUP_PPT_RW(nv_temp_target, "nv_temp_target", ASUS_WMI_DEVID_NV_THERM_TARGET,
> nv_temp_default, 75, nv_temp_max, 1, "Set the Nvidia max thermal limit");
> +ATTR_GROUP_INT_VALUE_ONLY_RO(dgpu_base_tgp, "dgpu_base_tgp", ASUS_WMI_DEVID_DGPU_BASE_TGP,
> + "Read the base TGP value")
> +ATTR_GROUP_INT_RW(dgpu_tgp, "dgpu_tgp", ASUS_WMI_DEVID_DGPU_SET_TGP,
> + 70, 0, NVIDIA_GPU_POWER_MAX, 1, "Set the additional TGP on top of the base TGP");
Could you expand the description for these in the next version?
>
> ATTR_GROUP_ENUM_INT_RO(charge_mode, "charge_mode", ASUS_WMI_DEVID_CHARGE_MODE,
> "0;1;2", "Show the current mode of charging");
> @@ -534,6 +539,10 @@ static int asus_fw_attr_add(void)
> sysfs_create_group(&asus_bioscfg.fw_attr_kset->kobj, &nv_dynamic_boost_attr_group);
> if (asus_wmi_is_present(ASUS_WMI_DEVID_NV_THERM_TARGET))
> sysfs_create_group(&asus_bioscfg.fw_attr_kset->kobj, &nv_temp_target_attr_group);
> + if (asus_wmi_is_present(ASUS_WMI_DEVID_DGPU_BASE_TGP))
> + sysfs_create_group(&asus_bioscfg.fw_attr_kset->kobj, &dgpu_base_tgp_attr_group);
> + if (asus_wmi_is_present(ASUS_WMI_DEVID_DGPU_SET_TGP))
> + sysfs_create_group(&asus_bioscfg.fw_attr_kset->kobj, &dgpu_tgp_attr_group);
>
> if (asus_wmi_is_present(ASUS_WMI_DEVID_CHARGE_MODE))
> sysfs_create_group(&asus_bioscfg.fw_attr_kset->kobj, &charge_mode_attr_group);
> diff --git a/drivers/platform/x86/asus-bioscfg.h b/drivers/platform/x86/asus-bioscfg.h
> index 403563c25f53..2da55a91ff0b 100644
> --- a/drivers/platform/x86/asus-bioscfg.h
> +++ b/drivers/platform/x86/asus-bioscfg.h
> @@ -113,6 +113,22 @@ static ssize_t _attrname##_##_prop##_show(struct kobject *kobj, \
> static struct kobj_attribute attr_##_attrname##_##_prop = \
> __ASUS_ATTR_RO(_attrname, _prop)
>
> +/* Requires current_value show&|store */
> +#define __ATTR_GROUP_INT_VALUE_ONLY(_attrname, _fsname, _dispname) \
> +__ATTR_SHOW_FMT(display_name, _attrname, "%s\n", _dispname); \
> +static struct kobj_attribute attr_##_attrname##_type = \
> + __ASUS_ATTR_RO_AS(type, int_type_show); \
> +static struct attribute *_attrname##_attrs[] = { \
> + &attr_##_attrname##_current_value.attr, \
> + &attr_##_attrname##_display_name.attr, \
> + &attr_##_attrname##_type.attr, \
> + NULL \
> +}; \
> +static const struct attribute_group _attrname##_attr_group = { \
> + .name = _fsname, \
> + .attrs = _attrname##_attrs \
> +}
> +
> /* Int style min/max range, base macro. Requires current_value show&|store */
> #define __ATTR_GROUP_INT(_attrname, _fsname, _default, \
> _min, _max, _incstep, _dispname)\
> @@ -156,6 +172,20 @@ static const struct attribute_group _attrname##_attr_group = { \
> .attrs = _attrname##_attrs \
> }
>
> +#define ATTR_GROUP_INT_VALUE_ONLY_RO(_attrname, _fsname, _wmi, _dispname) \
> +do { \
> + __ATTR_CURRENT_INT_RO(_attrname, _wmi); \
> + __ATTR_GROUP_INT_VALUE_ONLY(_attrname, _fsname, _dispname); \
> +} while (0)
> +
> +#define ATTR_GROUP_INT_RW(_attrname, _fsname, _wmi, _default, _min, \
> + _max, _incstep, _dispname) \
> +do { \
> + __ATTR_CURRENT_INT_RW(_attrname, _min, _max, _wmi); \
> + __ATTR_GROUP_INT(_attrname, _fsname, _default, _min, _max, \
> + _incstep, _dispname); \
> +} while (0)
> +
> #define ATTR_GROUP_BOOL_RO(_attrname, _fsname, _wmi, _dispname) \
> do { \
> __ATTR_CURRENT_INT_RO(_attrname, _wmi); \
> diff --git a/include/linux/platform_data/x86/asus-wmi.h b/include/linux/platform_data/x86/asus-wmi.h
> index 889336a932fb..c54264653d75 100644
> --- a/include/linux/platform_data/x86/asus-wmi.h
> +++ b/include/linux/platform_data/x86/asus-wmi.h
> @@ -128,6 +128,9 @@
> /* dgpu on/off */
> #define ASUS_WMI_DEVID_DGPU 0x00090020
>
> +#define ASUS_WMI_DEVID_DGPU_BASE_TGP 0x00120099
> +#define ASUS_WMI_DEVID_DGPU_SET_TGP 0x00120098
> +
> /* gpu mux switch, 0 = dGPU, 1 = Optimus */
> #define ASUS_WMI_DEVID_GPU_MUX 0x00090016
> #define ASUS_WMI_DEVID_GPU_MUX_VIVO 0x00090026
^ permalink raw reply [flat|nested] 22+ messages in thread
* [PATCH 3/5] platform/x86: asus-bioscfg: add apu-mem control support
2024-07-16 5:16 [PATCH 0/5] platform/x86: introduce asus-bioscfg Luke D. Jones
2024-07-16 5:16 ` [PATCH 1/5] platform/x86 asus-bioscfg: move existing tunings to asus-bioscfg module Luke D. Jones
2024-07-16 5:16 ` [PATCH 2/5] platform/x86: asus-bioscfg: add dgpu tgp control Luke D. Jones
@ 2024-07-16 5:16 ` Luke D. Jones
2024-07-16 5:16 ` [PATCH 4/5] platform/x86: asus-bios: add core count control Luke D. Jones
` (2 subsequent siblings)
5 siblings, 0 replies; 22+ messages in thread
From: Luke D. Jones @ 2024-07-16 5:16 UTC (permalink / raw)
To: platform-driver-x86
Cc: corentin.chary, hdegoede, ilpo.jarvinen, mario.limonciello,
linux-kernel, Luke D. Jones
Implement the APU memory size control under the asus-bioscfg module using
the fw_attributes class.
This allows the APU allocated memory size to be adjusted depending on
the users priority. A reboot is required after change.
Signed-off-by: Luke D. Jones <luke@ljones.dev>
---
drivers/platform/x86/asus-bioscfg.c | 116 +++++++++++++++++++++
include/linux/platform_data/x86/asus-wmi.h | 1 +
2 files changed, 117 insertions(+)
diff --git a/drivers/platform/x86/asus-bioscfg.c b/drivers/platform/x86/asus-bioscfg.c
index 551b7dbd8fe7..57efe50e0d19 100644
--- a/drivers/platform/x86/asus-bioscfg.c
+++ b/drivers/platform/x86/asus-bioscfg.c
@@ -426,6 +426,120 @@ static ssize_t egpu_enable_current_value_store(struct kobject *kobj,
WMI_SHOW_INT(egpu_enable_current_value, "%d\n", ASUS_WMI_DEVID_EGPU);
ATTR_GROUP_BOOL_CUSTOM(egpu_enable, "egpu_enable", "Enable the eGPU (also disables dGPU)");
+/* Device memory available to APU */
+
+static ssize_t apu_mem_current_value_show(struct kobject *kobj,
+ struct kobj_attribute *attr, char *buf)
+{
+ int err;
+ u32 mem;
+
+ err = asus_wmi_get_devstate_dsts(ASUS_WMI_DEVID_APU_MEM, &mem);
+ if (err)
+ return err;
+
+ switch (mem) {
+ case 256:
+ mem = 0;
+ break;
+ case 258:
+ mem = 1;
+ break;
+ case 259:
+ mem = 2;
+ break;
+ case 260:
+ mem = 3;
+ break;
+ case 261:
+ mem = 4;
+ break;
+ case 262:
+ /* This is out of order and looks wrong but is correct */
+ mem = 8;
+ break;
+ case 263:
+ mem = 5;
+ break;
+ case 264:
+ mem = 6;
+ break;
+ case 265:
+ mem = 7;
+ break;
+ default:
+ mem = 4;
+ break;
+ }
+
+ return sysfs_emit(buf, "%d\n", mem);
+}
+
+static ssize_t apu_mem_current_value_store(struct kobject *kobj,
+ struct kobj_attribute *attr,
+ const char *buf, size_t count)
+{
+ int result, err;
+ u32 requested, mem;
+
+ result = kstrtou32(buf, 10, &requested);
+ if (result)
+ return result;
+
+ switch (requested) {
+ case 0:
+ mem = 0;
+ break;
+ case 1:
+ mem = 258;
+ break;
+ case 2:
+ mem = 259;
+ break;
+ case 3:
+ mem = 260;
+ break;
+ case 4:
+ mem = 261;
+ break;
+ case 5:
+ mem = 263;
+ break;
+ case 6:
+ mem = 264;
+ break;
+ case 7:
+ mem = 265;
+ break;
+ case 8:
+ /* This is outof order and looks wrong but is correct */
+ mem = 262;
+ break;
+ default:
+ return -EIO;
+ }
+
+ err = asus_wmi_set_devstate(ASUS_WMI_DEVID_APU_MEM, mem, &result);
+ if (err) {
+ pr_warn("Failed to set apu_mem: %d\n", err);
+ return err;
+ }
+
+ pr_info("APU memory changed to %dGB, reboot required\n", requested);
+ sysfs_notify(kobj, NULL, attr->attr.name);
+
+ asus_set_reboot_and_signal_event();
+
+ return count;
+}
+
+static ssize_t apu_mem_possible_values_show(struct kobject *kobj,
+ struct kobj_attribute *attr, char *buf)
+{
+ return sysfs_emit(buf, "0;1;2;3;4;5;6;7;8\n");
+}
+ATTR_GROUP_ENUM_CUSTOM(apu_mem, "apu_mem", "Set the available system memory for the APU to use");
+
/* Simple attribute creation */
ATTR_GROUP_ENUM_INT_RW(thermal_policy, "thermal_policy", ASUS_WMI_DEVID_THROTTLE_THERMAL_POLICY,
0, 3, "0;1;2", "Set the thermal profile: 0=normal, 1=performance, 2=quiet");
@@ -543,6 +657,8 @@ static int asus_fw_attr_add(void)
sysfs_create_group(&asus_bioscfg.fw_attr_kset->kobj, &dgpu_base_tgp_attr_group);
if (asus_wmi_is_present(ASUS_WMI_DEVID_DGPU_SET_TGP))
sysfs_create_group(&asus_bioscfg.fw_attr_kset->kobj, &dgpu_tgp_attr_group);
+ if (asus_wmi_is_present(ASUS_WMI_DEVID_APU_MEM))
+ sysfs_create_group(&asus_bioscfg.fw_attr_kset->kobj, &apu_mem_attr_group);
if (asus_wmi_is_present(ASUS_WMI_DEVID_CHARGE_MODE))
sysfs_create_group(&asus_bioscfg.fw_attr_kset->kobj, &charge_mode_attr_group);
diff --git a/include/linux/platform_data/x86/asus-wmi.h b/include/linux/platform_data/x86/asus-wmi.h
index c54264653d75..e3d511918416 100644
--- a/include/linux/platform_data/x86/asus-wmi.h
+++ b/include/linux/platform_data/x86/asus-wmi.h
@@ -130,6 +130,7 @@
#define ASUS_WMI_DEVID_DGPU_BASE_TGP 0x00120099
#define ASUS_WMI_DEVID_DGPU_SET_TGP 0x00120098
+#define ASUS_WMI_DEVID_APU_MEM 0x000600C1
/* gpu mux switch, 0 = dGPU, 1 = Optimus */
#define ASUS_WMI_DEVID_GPU_MUX 0x00090016
--
2.45.2
^ permalink raw reply related [flat|nested] 22+ messages in thread* [PATCH 4/5] platform/x86: asus-bios: add core count control
2024-07-16 5:16 [PATCH 0/5] platform/x86: introduce asus-bioscfg Luke D. Jones
` (2 preceding siblings ...)
2024-07-16 5:16 ` [PATCH 3/5] platform/x86: asus-bioscfg: add apu-mem control support Luke D. Jones
@ 2024-07-16 5:16 ` Luke D. Jones
2024-07-16 9:25 ` Hans de Goede
2024-07-16 5:16 ` [PATCH 5/5] asus-wmi: deprecate bios features Luke D. Jones
2024-07-16 9:20 ` [PATCH 0/5] platform/x86: introduce asus-bioscfg Hans de Goede
5 siblings, 1 reply; 22+ messages in thread
From: Luke D. Jones @ 2024-07-16 5:16 UTC (permalink / raw)
To: platform-driver-x86
Cc: corentin.chary, hdegoede, ilpo.jarvinen, mario.limonciello,
linux-kernel, Luke D. Jones
Implement Intel core enablement under the asus-bioscfg module using the
fw_attributes class.
This allows users to enable or disable preformance or efficiency cores
depending on their requirements. After change a reboot is required.
Signed-off-by: Luke D. Jones <luke@ljones.dev>
---
drivers/platform/x86/asus-bioscfg.c | 206 +++++++++++++++++++++
drivers/platform/x86/asus-bioscfg.h | 29 +++
include/linux/platform_data/x86/asus-wmi.h | 4 +
3 files changed, 239 insertions(+)
diff --git a/drivers/platform/x86/asus-bioscfg.c b/drivers/platform/x86/asus-bioscfg.c
index 57efe50e0d19..6d39e45591e8 100644
--- a/drivers/platform/x86/asus-bioscfg.c
+++ b/drivers/platform/x86/asus-bioscfg.c
@@ -41,6 +41,18 @@ MODULE_ALIAS("wmi:"ASUS_NB_WMI_EVENT_GUID);
#define ASUS_MINI_LED_2024_STRONG 0x01
#define ASUS_MINI_LED_2024_OFF 0x02
+enum cpu_core_type {
+ CPU_CORE_PERF = 0,
+ CPU_CORE_POWER,
+};
+
+enum cpu_core_value {
+ CPU_CORE_DEFAULT = 0,
+ CPU_CORE_MIN,
+ CPU_CORE_MAX,
+ CPU_CORE_CURRENT,
+};
+
/* Default limits for tunables available on ASUS ROG laptops */
#define PPT_CPU_LIMIT_MIN 5
#define PPT_CPU_LIMIT_MAX 150
@@ -75,6 +87,10 @@ struct rog_tunables {
u32 nv_temp_default;
u32 nv_temp_max;
u32 nv_temp_target;
+
+ u32 min_perf_cores;
+ u32 max_perf_cores;
+ u32 max_power_cores;
};
static const struct class *fw_attr_class;
@@ -540,6 +556,191 @@ static ssize_t apu_mem_possible_values_show(struct kobject *kobj,
}
ATTR_GROUP_ENUM_CUSTOM(apu_mem, "apu_mem", "Set the available system memory for the APU to use");
+static int asus_bios_set_max_cores(void)
+{
+ u32 cores;
+ int err;
+
+ asus_bios.rog_tunables->min_perf_cores = 4;
+ asus_bios.rog_tunables->max_perf_cores = 4;
+ asus_bios.rog_tunables->max_power_cores = 8;
+
+ err = asus_wmi_get_devstate_dsts(ASUS_WMI_DEVID_CORES_MAX, &cores);
+ if (err)
+ return err;
+
+ cores &= ~ASUS_WMI_DSTS_PRESENCE_BIT;
+ asus_bios.rog_tunables->max_power_cores = (cores & 0xff00) >> 8;
+ asus_bios.rog_tunables->max_perf_cores = cores & 0xff;
+
+ return 0;
+}
+
+static ssize_t cores_value_show(struct kobject *kobj,
+ struct kobj_attribute *attr, char *buf,
+ enum cpu_core_type core_type,
+ enum cpu_core_value core_value)
+{
+ u32 cores;
+ int err;
+
+ switch (core_value) {
+ case CPU_CORE_DEFAULT:
+ case CPU_CORE_MAX:
+ if (core_type == CPU_CORE_PERF)
+ return sysfs_emit(buf, "%d\n", asus_bios.rog_tunables->max_perf_cores);
+ else
+ return sysfs_emit(buf, "%d\n", asus_bios.rog_tunables->max_power_cores);
+ case CPU_CORE_MIN:
+ if (core_type == CPU_CORE_PERF)
+ return sysfs_emit(buf, "%d\n", asus_bios.rog_tunables->min_perf_cores);
+ else
+ return sysfs_emit(buf, "%d\n", 0);
+ default:
+ break;
+ }
+
+ err = asus_wmi_get_devstate_dsts(ASUS_WMI_DEVID_CORES, &cores);
+ if (err)
+ return err;
+
+ cores &= ~ASUS_WMI_DSTS_PRESENCE_BIT;
+ if (core_type == CPU_CORE_PERF)
+ cores &= 0xff;
+ else
+ cores = (cores & 0xff00) >> 8;
+ return sysfs_emit(buf, "%d\n", cores);
+}
+
+static ssize_t cores_current_value_store(struct kobject *kobj,
+ struct kobj_attribute *attr, const char *buf,
+ enum cpu_core_type core_type)
+{
+ int result, err;
+ u32 cores, currentv, min, max;
+
+ result = kstrtou32(buf, 10, &cores);
+ if (result)
+ return result;
+
+ if (core_type == CPU_CORE_PERF) {
+ min = asus_bios.rog_tunables->min_perf_cores;
+ max = asus_bios.rog_tunables->max_perf_cores;
+ } else {
+ min = 0;
+ max = asus_bios.rog_tunables->max_power_cores;
+ }
+ if (cores < min || cores > max)
+ return -EINVAL;
+
+ err = asus_wmi_get_devstate_dsts(ASUS_WMI_DEVID_CORES, ¤tv);
+ if (err)
+ return err;
+
+ if (core_type == CPU_CORE_PERF)
+ cores |= (currentv & 0xff00);
+ else
+ cores |= currentv & 0xff;
+
+ if (cores == currentv)
+ return 0;
+
+ err = asus_wmi_set_devstate(ASUS_WMI_DEVID_CORES, cores, &result);
+ if (err) {
+ pr_warn("Failed to set perfromance core count: %d\n", err);
+ return err;
+ }
+
+ if (result > 1) {
+ pr_warn("Failed to set performance core count (result): 0x%x\n", result);
+ return -EIO;
+ }
+
+ pr_info("CPU core count changed, reboot required\n");
+ sysfs_notify(kobj, NULL, attr->attr.name);
+ asus_set_reboot_and_signal_event();
+
+ return 0;
+}
+
+static ssize_t cores_performance_min_value_show(struct kobject *kobj,
+ struct kobj_attribute *attr, char *buf)
+{
+ return cores_value_show(kobj, attr, buf, CPU_CORE_PERF, CPU_CORE_MIN);
+}
+
+static ssize_t cores_performance_max_value_show(struct kobject *kobj,
+ struct kobj_attribute *attr, char *buf)
+{
+ return cores_value_show(kobj, attr, buf, CPU_CORE_PERF, CPU_CORE_MAX);
+}
+
+static ssize_t cores_performance_default_value_show(struct kobject *kobj,
+ struct kobj_attribute *attr, char *buf)
+{
+ return cores_value_show(kobj, attr, buf, CPU_CORE_PERF, CPU_CORE_DEFAULT);
+}
+
+static ssize_t cores_performance_current_value_show(struct kobject *kobj,
+ struct kobj_attribute *attr, char *buf)
+{
+ return cores_value_show(kobj, attr, buf, CPU_CORE_PERF, CPU_CORE_CURRENT);
+}
+
+static ssize_t cores_performance_current_value_store(struct kobject *kobj,
+ struct kobj_attribute *attr,
+ const char *buf, size_t count)
+{
+ int err;
+
+ err = cores_current_value_store(kobj, attr, buf, CPU_CORE_PERF);
+ if (err)
+ return err;
+
+ return count;
+}
+ATTR_GROUP_CORES_RW(cores_performance, "cores_performance", ASUS_WMI_DEVID_CORES,
+ "Set the max available performance cores");
+
+static ssize_t cores_efficiency_min_value_show(struct kobject *kobj,
+ struct kobj_attribute *attr, char *buf)
+{
+ return cores_value_show(kobj, attr, buf, CPU_CORE_POWER, CPU_CORE_MIN);
+}
+
+static ssize_t cores_efficiency_max_value_show(struct kobject *kobj,
+ struct kobj_attribute *attr, char *buf)
+{
+ return cores_value_show(kobj, attr, buf, CPU_CORE_POWER, CPU_CORE_MAX);
+}
+
+static ssize_t cores_efficiency_default_value_show(struct kobject *kobj,
+ struct kobj_attribute *attr, char *buf)
+{
+ return cores_value_show(kobj, attr, buf, CPU_CORE_POWER, CPU_CORE_DEFAULT);
+}
+
+static ssize_t cores_efficiency_current_value_show(struct kobject *kobj,
+ struct kobj_attribute *attr, char *buf)
+{
+ return cores_value_show(kobj, attr, buf, CPU_CORE_POWER, CPU_CORE_CURRENT);
+}
+
+static ssize_t cores_efficiency_current_value_store(struct kobject *kobj,
+ struct kobj_attribute *attr,
+ const char *buf, size_t count)
+{
+ int err;
+
+ err = cores_current_value_store(kobj, attr, buf, CPU_CORE_POWER);
+ if (err)
+ return err;
+
+ return count;
+}
+ATTR_GROUP_CORES_RW(cores_efficiency, "cores_efficiency", ASUS_WMI_DEVID_CORES,
+ "Set the max available efficiency cores");
+
/* Simple attribute creation */
ATTR_GROUP_ENUM_INT_RW(thermal_policy, "thermal_policy", ASUS_WMI_DEVID_THROTTLE_THERMAL_POLICY,
0, 3, "0;1;2", "Set the thermal profile: 0=normal, 1=performance, 2=quiet");
@@ -636,6 +837,11 @@ static int asus_fw_attr_add(void)
if (asus_wmi_is_present(ASUS_WMI_DEVID_EGPU_CONNECTED))
sysfs_create_group(&asus_bioscfg.fw_attr_kset->kobj, &egpu_connected_attr_group);
+ if (asus_wmi_is_present(ASUS_WMI_DEVID_CORES_MAX) && !asus_bios_set_max_cores()) {
+ sysfs_create_group(&asus_bios.fw_attr_kset->kobj, &cores_performance_attr_group);
+ sysfs_create_group(&asus_bios.fw_attr_kset->kobj, &cores_efficiency_attr_group);
+ }
+
if (asus_wmi_is_present(ASUS_WMI_DEVID_THROTTLE_THERMAL_POLICY))
sysfs_create_group(&asus_bioscfg.fw_attr_kset->kobj, &thermal_policy_attr_group);
if (asus_wmi_is_present(ASUS_WMI_DEVID_PPT_PL1_SPL))
diff --git a/drivers/platform/x86/asus-bioscfg.h b/drivers/platform/x86/asus-bioscfg.h
index 2da55a91ff0b..bae1976eda3f 100644
--- a/drivers/platform/x86/asus-bioscfg.h
+++ b/drivers/platform/x86/asus-bioscfg.h
@@ -244,6 +244,35 @@ static const struct attribute_group _attrname##_attr_group = { \
.attrs = _attrname##_attrs \
}
+/* CPU core attributes need a little different in setup */
+#define ATTR_GROUP_CORES_RW(_attrname, _fsname, _dispname) \
+__ATTR_SHOW_FMT(scalar_increment, _attrname, "%d\n", 1); \
+__ATTR_SHOW_FMT(display_name, _attrname, "%s\n", _dispname); \
+static struct kobj_attribute attr_##_attrname##_current_value = \
+ __ASUS_ATTR_RW(_attrname, current_value); \
+static struct kobj_attribute attr_##_attrname##_default_value = \
+ __ASUS_ATTR_RO(_attrname, default_value); \
+static struct kobj_attribute attr_##_attrname##_min_value = \
+ __ASUS_ATTR_RO(_attrname, min_value); \
+static struct kobj_attribute attr_##_attrname##_max_value = \
+ __ASUS_ATTR_RO(_attrname, max_value); \
+static struct kobj_attribute attr_##_attrname##_type = \
+ __ASUS_ATTR_RO_AS(type, int_type_show); \
+static struct attribute *_attrname##_attrs[] = { \
+ &attr_##_attrname##_current_value.attr, \
+ &attr_##_attrname##_default_value.attr, \
+ &attr_##_attrname##_min_value.attr, \
+ &attr_##_attrname##_max_value.attr, \
+ &attr_##_attrname##_scalar_increment.attr, \
+ &attr_##_attrname##_display_name.attr, \
+ &attr_##_attrname##_type.attr, \
+ NULL \
+}; \
+static const struct attribute_group _attrname##_attr_group = { \
+ .name = _fsname, \
+ .attrs = _attrname##_attrs \
+}
+
/* ROG PPT attributes need a little different in setup */
#define ATTR_GROUP_PPT_RW(_attrname, _fsname, _wmi, _default, \
_min, _max, _incstep, _dispname) \
diff --git a/include/linux/platform_data/x86/asus-wmi.h b/include/linux/platform_data/x86/asus-wmi.h
index e3d511918416..d8f713ed3ea3 100644
--- a/include/linux/platform_data/x86/asus-wmi.h
+++ b/include/linux/platform_data/x86/asus-wmi.h
@@ -128,6 +128,10 @@
/* dgpu on/off */
#define ASUS_WMI_DEVID_DGPU 0x00090020
+/* Intel E-core and P-core configuration in a format 0x0[E]0[P] */
+#define ASUS_WMI_DEVID_CORES 0x001200D2
+ /* Maximum Intel E-core and P-core availability */
+#define ASUS_WMI_DEVID_CORES_MAX 0x001200D3
#define ASUS_WMI_DEVID_DGPU_BASE_TGP 0x00120099
#define ASUS_WMI_DEVID_DGPU_SET_TGP 0x00120098
#define ASUS_WMI_DEVID_APU_MEM 0x000600C1
--
2.45.2
^ permalink raw reply related [flat|nested] 22+ messages in thread* Re: [PATCH 4/5] platform/x86: asus-bios: add core count control
2024-07-16 5:16 ` [PATCH 4/5] platform/x86: asus-bios: add core count control Luke D. Jones
@ 2024-07-16 9:25 ` Hans de Goede
2024-07-16 9:41 ` Luke Jones
0 siblings, 1 reply; 22+ messages in thread
From: Hans de Goede @ 2024-07-16 9:25 UTC (permalink / raw)
To: Luke D. Jones, platform-driver-x86
Cc: corentin.chary, ilpo.jarvinen, mario.limonciello, linux-kernel
Hi,
On 7/16/24 7:16 AM, Luke D. Jones wrote:
> Implement Intel core enablement under the asus-bioscfg module using the
> fw_attributes class.
>
> This allows users to enable or disable preformance or efficiency cores
> depending on their requirements. After change a reboot is required.
Not a full revieew, just a quick remark. You say this this
requires a reboot, but this patch does not update asus_bios_requires_reboot()
Regards,
Hans
>
> Signed-off-by: Luke D. Jones <luke@ljones.dev>
> ---
> drivers/platform/x86/asus-bioscfg.c | 206 +++++++++++++++++++++
> drivers/platform/x86/asus-bioscfg.h | 29 +++
> include/linux/platform_data/x86/asus-wmi.h | 4 +
> 3 files changed, 239 insertions(+)
>
> diff --git a/drivers/platform/x86/asus-bioscfg.c b/drivers/platform/x86/asus-bioscfg.c
> index 57efe50e0d19..6d39e45591e8 100644
> --- a/drivers/platform/x86/asus-bioscfg.c
> +++ b/drivers/platform/x86/asus-bioscfg.c
> @@ -41,6 +41,18 @@ MODULE_ALIAS("wmi:"ASUS_NB_WMI_EVENT_GUID);
> #define ASUS_MINI_LED_2024_STRONG 0x01
> #define ASUS_MINI_LED_2024_OFF 0x02
>
> +enum cpu_core_type {
> + CPU_CORE_PERF = 0,
> + CPU_CORE_POWER,
> +};
> +
> +enum cpu_core_value {
> + CPU_CORE_DEFAULT = 0,
> + CPU_CORE_MIN,
> + CPU_CORE_MAX,
> + CPU_CORE_CURRENT,
> +};
> +
> /* Default limits for tunables available on ASUS ROG laptops */
> #define PPT_CPU_LIMIT_MIN 5
> #define PPT_CPU_LIMIT_MAX 150
> @@ -75,6 +87,10 @@ struct rog_tunables {
> u32 nv_temp_default;
> u32 nv_temp_max;
> u32 nv_temp_target;
> +
> + u32 min_perf_cores;
> + u32 max_perf_cores;
> + u32 max_power_cores;
> };
>
> static const struct class *fw_attr_class;
> @@ -540,6 +556,191 @@ static ssize_t apu_mem_possible_values_show(struct kobject *kobj,
> }
> ATTR_GROUP_ENUM_CUSTOM(apu_mem, "apu_mem", "Set the available system memory for the APU to use");
>
> +static int asus_bios_set_max_cores(void)
> +{
> + u32 cores;
> + int err;
> +
> + asus_bios.rog_tunables->min_perf_cores = 4;
> + asus_bios.rog_tunables->max_perf_cores = 4;
> + asus_bios.rog_tunables->max_power_cores = 8;
> +
> + err = asus_wmi_get_devstate_dsts(ASUS_WMI_DEVID_CORES_MAX, &cores);
> + if (err)
> + return err;
> +
> + cores &= ~ASUS_WMI_DSTS_PRESENCE_BIT;
> + asus_bios.rog_tunables->max_power_cores = (cores & 0xff00) >> 8;
> + asus_bios.rog_tunables->max_perf_cores = cores & 0xff;
> +
> + return 0;
> +}
> +
> +static ssize_t cores_value_show(struct kobject *kobj,
> + struct kobj_attribute *attr, char *buf,
> + enum cpu_core_type core_type,
> + enum cpu_core_value core_value)
> +{
> + u32 cores;
> + int err;
> +
> + switch (core_value) {
> + case CPU_CORE_DEFAULT:
> + case CPU_CORE_MAX:
> + if (core_type == CPU_CORE_PERF)
> + return sysfs_emit(buf, "%d\n", asus_bios.rog_tunables->max_perf_cores);
> + else
> + return sysfs_emit(buf, "%d\n", asus_bios.rog_tunables->max_power_cores);
> + case CPU_CORE_MIN:
> + if (core_type == CPU_CORE_PERF)
> + return sysfs_emit(buf, "%d\n", asus_bios.rog_tunables->min_perf_cores);
> + else
> + return sysfs_emit(buf, "%d\n", 0);
> + default:
> + break;
> + }
> +
> + err = asus_wmi_get_devstate_dsts(ASUS_WMI_DEVID_CORES, &cores);
> + if (err)
> + return err;
> +
> + cores &= ~ASUS_WMI_DSTS_PRESENCE_BIT;
> + if (core_type == CPU_CORE_PERF)
> + cores &= 0xff;
> + else
> + cores = (cores & 0xff00) >> 8;
> + return sysfs_emit(buf, "%d\n", cores);
> +}
> +
> +static ssize_t cores_current_value_store(struct kobject *kobj,
> + struct kobj_attribute *attr, const char *buf,
> + enum cpu_core_type core_type)
> +{
> + int result, err;
> + u32 cores, currentv, min, max;
> +
> + result = kstrtou32(buf, 10, &cores);
> + if (result)
> + return result;
> +
> + if (core_type == CPU_CORE_PERF) {
> + min = asus_bios.rog_tunables->min_perf_cores;
> + max = asus_bios.rog_tunables->max_perf_cores;
> + } else {
> + min = 0;
> + max = asus_bios.rog_tunables->max_power_cores;
> + }
> + if (cores < min || cores > max)
> + return -EINVAL;
> +
> + err = asus_wmi_get_devstate_dsts(ASUS_WMI_DEVID_CORES, ¤tv);
> + if (err)
> + return err;
> +
> + if (core_type == CPU_CORE_PERF)
> + cores |= (currentv & 0xff00);
> + else
> + cores |= currentv & 0xff;
> +
> + if (cores == currentv)
> + return 0;
> +
> + err = asus_wmi_set_devstate(ASUS_WMI_DEVID_CORES, cores, &result);
> + if (err) {
> + pr_warn("Failed to set perfromance core count: %d\n", err);
> + return err;
> + }
> +
> + if (result > 1) {
> + pr_warn("Failed to set performance core count (result): 0x%x\n", result);
> + return -EIO;
> + }
> +
> + pr_info("CPU core count changed, reboot required\n");
> + sysfs_notify(kobj, NULL, attr->attr.name);
> + asus_set_reboot_and_signal_event();
> +
> + return 0;
> +}
> +
> +static ssize_t cores_performance_min_value_show(struct kobject *kobj,
> + struct kobj_attribute *attr, char *buf)
> +{
> + return cores_value_show(kobj, attr, buf, CPU_CORE_PERF, CPU_CORE_MIN);
> +}
> +
> +static ssize_t cores_performance_max_value_show(struct kobject *kobj,
> + struct kobj_attribute *attr, char *buf)
> +{
> + return cores_value_show(kobj, attr, buf, CPU_CORE_PERF, CPU_CORE_MAX);
> +}
> +
> +static ssize_t cores_performance_default_value_show(struct kobject *kobj,
> + struct kobj_attribute *attr, char *buf)
> +{
> + return cores_value_show(kobj, attr, buf, CPU_CORE_PERF, CPU_CORE_DEFAULT);
> +}
> +
> +static ssize_t cores_performance_current_value_show(struct kobject *kobj,
> + struct kobj_attribute *attr, char *buf)
> +{
> + return cores_value_show(kobj, attr, buf, CPU_CORE_PERF, CPU_CORE_CURRENT);
> +}
> +
> +static ssize_t cores_performance_current_value_store(struct kobject *kobj,
> + struct kobj_attribute *attr,
> + const char *buf, size_t count)
> +{
> + int err;
> +
> + err = cores_current_value_store(kobj, attr, buf, CPU_CORE_PERF);
> + if (err)
> + return err;
> +
> + return count;
> +}
> +ATTR_GROUP_CORES_RW(cores_performance, "cores_performance", ASUS_WMI_DEVID_CORES,
> + "Set the max available performance cores");
> +
> +static ssize_t cores_efficiency_min_value_show(struct kobject *kobj,
> + struct kobj_attribute *attr, char *buf)
> +{
> + return cores_value_show(kobj, attr, buf, CPU_CORE_POWER, CPU_CORE_MIN);
> +}
> +
> +static ssize_t cores_efficiency_max_value_show(struct kobject *kobj,
> + struct kobj_attribute *attr, char *buf)
> +{
> + return cores_value_show(kobj, attr, buf, CPU_CORE_POWER, CPU_CORE_MAX);
> +}
> +
> +static ssize_t cores_efficiency_default_value_show(struct kobject *kobj,
> + struct kobj_attribute *attr, char *buf)
> +{
> + return cores_value_show(kobj, attr, buf, CPU_CORE_POWER, CPU_CORE_DEFAULT);
> +}
> +
> +static ssize_t cores_efficiency_current_value_show(struct kobject *kobj,
> + struct kobj_attribute *attr, char *buf)
> +{
> + return cores_value_show(kobj, attr, buf, CPU_CORE_POWER, CPU_CORE_CURRENT);
> +}
> +
> +static ssize_t cores_efficiency_current_value_store(struct kobject *kobj,
> + struct kobj_attribute *attr,
> + const char *buf, size_t count)
> +{
> + int err;
> +
> + err = cores_current_value_store(kobj, attr, buf, CPU_CORE_POWER);
> + if (err)
> + return err;
> +
> + return count;
> +}
> +ATTR_GROUP_CORES_RW(cores_efficiency, "cores_efficiency", ASUS_WMI_DEVID_CORES,
> + "Set the max available efficiency cores");
> +
> /* Simple attribute creation */
> ATTR_GROUP_ENUM_INT_RW(thermal_policy, "thermal_policy", ASUS_WMI_DEVID_THROTTLE_THERMAL_POLICY,
> 0, 3, "0;1;2", "Set the thermal profile: 0=normal, 1=performance, 2=quiet");
> @@ -636,6 +837,11 @@ static int asus_fw_attr_add(void)
> if (asus_wmi_is_present(ASUS_WMI_DEVID_EGPU_CONNECTED))
> sysfs_create_group(&asus_bioscfg.fw_attr_kset->kobj, &egpu_connected_attr_group);
>
> + if (asus_wmi_is_present(ASUS_WMI_DEVID_CORES_MAX) && !asus_bios_set_max_cores()) {
> + sysfs_create_group(&asus_bios.fw_attr_kset->kobj, &cores_performance_attr_group);
> + sysfs_create_group(&asus_bios.fw_attr_kset->kobj, &cores_efficiency_attr_group);
> + }
> +
> if (asus_wmi_is_present(ASUS_WMI_DEVID_THROTTLE_THERMAL_POLICY))
> sysfs_create_group(&asus_bioscfg.fw_attr_kset->kobj, &thermal_policy_attr_group);
> if (asus_wmi_is_present(ASUS_WMI_DEVID_PPT_PL1_SPL))
> diff --git a/drivers/platform/x86/asus-bioscfg.h b/drivers/platform/x86/asus-bioscfg.h
> index 2da55a91ff0b..bae1976eda3f 100644
> --- a/drivers/platform/x86/asus-bioscfg.h
> +++ b/drivers/platform/x86/asus-bioscfg.h
> @@ -244,6 +244,35 @@ static const struct attribute_group _attrname##_attr_group = { \
> .attrs = _attrname##_attrs \
> }
>
> +/* CPU core attributes need a little different in setup */
> +#define ATTR_GROUP_CORES_RW(_attrname, _fsname, _dispname) \
> +__ATTR_SHOW_FMT(scalar_increment, _attrname, "%d\n", 1); \
> +__ATTR_SHOW_FMT(display_name, _attrname, "%s\n", _dispname); \
> +static struct kobj_attribute attr_##_attrname##_current_value = \
> + __ASUS_ATTR_RW(_attrname, current_value); \
> +static struct kobj_attribute attr_##_attrname##_default_value = \
> + __ASUS_ATTR_RO(_attrname, default_value); \
> +static struct kobj_attribute attr_##_attrname##_min_value = \
> + __ASUS_ATTR_RO(_attrname, min_value); \
> +static struct kobj_attribute attr_##_attrname##_max_value = \
> + __ASUS_ATTR_RO(_attrname, max_value); \
> +static struct kobj_attribute attr_##_attrname##_type = \
> + __ASUS_ATTR_RO_AS(type, int_type_show); \
> +static struct attribute *_attrname##_attrs[] = { \
> + &attr_##_attrname##_current_value.attr, \
> + &attr_##_attrname##_default_value.attr, \
> + &attr_##_attrname##_min_value.attr, \
> + &attr_##_attrname##_max_value.attr, \
> + &attr_##_attrname##_scalar_increment.attr, \
> + &attr_##_attrname##_display_name.attr, \
> + &attr_##_attrname##_type.attr, \
> + NULL \
> +}; \
> +static const struct attribute_group _attrname##_attr_group = { \
> + .name = _fsname, \
> + .attrs = _attrname##_attrs \
> +}
> +
> /* ROG PPT attributes need a little different in setup */
> #define ATTR_GROUP_PPT_RW(_attrname, _fsname, _wmi, _default, \
> _min, _max, _incstep, _dispname) \
> diff --git a/include/linux/platform_data/x86/asus-wmi.h b/include/linux/platform_data/x86/asus-wmi.h
> index e3d511918416..d8f713ed3ea3 100644
> --- a/include/linux/platform_data/x86/asus-wmi.h
> +++ b/include/linux/platform_data/x86/asus-wmi.h
> @@ -128,6 +128,10 @@
> /* dgpu on/off */
> #define ASUS_WMI_DEVID_DGPU 0x00090020
>
> +/* Intel E-core and P-core configuration in a format 0x0[E]0[P] */
> +#define ASUS_WMI_DEVID_CORES 0x001200D2
> + /* Maximum Intel E-core and P-core availability */
> +#define ASUS_WMI_DEVID_CORES_MAX 0x001200D3
> #define ASUS_WMI_DEVID_DGPU_BASE_TGP 0x00120099
> #define ASUS_WMI_DEVID_DGPU_SET_TGP 0x00120098
> #define ASUS_WMI_DEVID_APU_MEM 0x000600C1
^ permalink raw reply [flat|nested] 22+ messages in thread* Re: [PATCH 4/5] platform/x86: asus-bios: add core count control
2024-07-16 9:25 ` Hans de Goede
@ 2024-07-16 9:41 ` Luke Jones
2024-07-16 9:43 ` Luke Jones
0 siblings, 1 reply; 22+ messages in thread
From: Luke Jones @ 2024-07-16 9:41 UTC (permalink / raw)
To: Hans de Goede, platform-driver-x86
Cc: corentin.chary, Ilpo Järvinen, Mario Limonciello,
linux-kernel
On Tue, 16 Jul 2024, at 9:25 PM, Hans de Goede wrote:
> Hi,
>
> On 7/16/24 7:16 AM, Luke D. Jones wrote:
> > Implement Intel core enablement under the asus-bioscfg module using the
> > fw_attributes class.
> >
> > This allows users to enable or disable preformance or efficiency cores
> > depending on their requirements. After change a reboot is required.
>
> Not a full revieew, just a quick remark. You say this this
> requires a reboot, but this patch does not update asus_bios_requires_reboot()
>
Thanks for catching that!
Regards,
Luke.
>
>
> >
> > Signed-off-by: Luke D. Jones <luke@ljones.dev>
> > ---
> > drivers/platform/x86/asus-bioscfg.c | 206 +++++++++++++++++++++
> > drivers/platform/x86/asus-bioscfg.h | 29 +++
> > include/linux/platform_data/x86/asus-wmi.h | 4 +
> > 3 files changed, 239 insertions(+)
> >
> > diff --git a/drivers/platform/x86/asus-bioscfg.c b/drivers/platform/x86/asus-bioscfg.c
> > index 57efe50e0d19..6d39e45591e8 100644
> > --- a/drivers/platform/x86/asus-bioscfg.c
> > +++ b/drivers/platform/x86/asus-bioscfg.c
> > @@ -41,6 +41,18 @@ MODULE_ALIAS("wmi:"ASUS_NB_WMI_EVENT_GUID);
> > #define ASUS_MINI_LED_2024_STRONG 0x01
> > #define ASUS_MINI_LED_2024_OFF 0x02
> >
> > +enum cpu_core_type {
> > + CPU_CORE_PERF = 0,
> > + CPU_CORE_POWER,
> > +};
> > +
> > +enum cpu_core_value {
> > + CPU_CORE_DEFAULT = 0,
> > + CPU_CORE_MIN,
> > + CPU_CORE_MAX,
> > + CPU_CORE_CURRENT,
> > +};
> > +
> > /* Default limits for tunables available on ASUS ROG laptops */
> > #define PPT_CPU_LIMIT_MIN 5
> > #define PPT_CPU_LIMIT_MAX 150
> > @@ -75,6 +87,10 @@ struct rog_tunables {
> > u32 nv_temp_default;
> > u32 nv_temp_max;
> > u32 nv_temp_target;
> > +
> > + u32 min_perf_cores;
> > + u32 max_perf_cores;
> > + u32 max_power_cores;
> > };
> >
> > static const struct class *fw_attr_class;
> > @@ -540,6 +556,191 @@ static ssize_t apu_mem_possible_values_show(struct kobject *kobj,
> > }
> > ATTR_GROUP_ENUM_CUSTOM(apu_mem, "apu_mem", "Set the available system memory for the APU to use");
> >
> > +static int asus_bios_set_max_cores(void)
> > +{
> > + u32 cores;
> > + int err;
> > +
> > + asus_bios.rog_tunables->min_perf_cores = 4;
> > + asus_bios.rog_tunables->max_perf_cores = 4;
> > + asus_bios.rog_tunables->max_power_cores = 8;
> > +
> > + err = asus_wmi_get_devstate_dsts(ASUS_WMI_DEVID_CORES_MAX, &cores);
> > + if (err)
> > + return err;
> > +
> > + cores &= ~ASUS_WMI_DSTS_PRESENCE_BIT;
> > + asus_bios.rog_tunables->max_power_cores = (cores & 0xff00) >> 8;
> > + asus_bios.rog_tunables->max_perf_cores = cores & 0xff;
> > +
> > + return 0;
> > +}
> > +
> > +static ssize_t cores_value_show(struct kobject *kobj,
> > + struct kobj_attribute *attr, char *buf,
> > + enum cpu_core_type core_type,
> > + enum cpu_core_value core_value)
> > +{
> > + u32 cores;
> > + int err;
> > +
> > + switch (core_value) {
> > + case CPU_CORE_DEFAULT:
> > + case CPU_CORE_MAX:
> > + if (core_type == CPU_CORE_PERF)
> > + return sysfs_emit(buf, "%d\n", asus_bios.rog_tunables->max_perf_cores);
> > + else
> > + return sysfs_emit(buf, "%d\n", asus_bios.rog_tunables->max_power_cores);
> > + case CPU_CORE_MIN:
> > + if (core_type == CPU_CORE_PERF)
> > + return sysfs_emit(buf, "%d\n", asus_bios.rog_tunables->min_perf_cores);
> > + else
> > + return sysfs_emit(buf, "%d\n", 0);
> > + default:
> > + break;
> > + }
> > +
> > + err = asus_wmi_get_devstate_dsts(ASUS_WMI_DEVID_CORES, &cores);
> > + if (err)
> > + return err;
> > +
> > + cores &= ~ASUS_WMI_DSTS_PRESENCE_BIT;
> > + if (core_type == CPU_CORE_PERF)
> > + cores &= 0xff;
> > + else
> > + cores = (cores & 0xff00) >> 8;
> > + return sysfs_emit(buf, "%d\n", cores);
> > +}
> > +
> > +static ssize_t cores_current_value_store(struct kobject *kobj,
> > + struct kobj_attribute *attr, const char *buf,
> > + enum cpu_core_type core_type)
> > +{
> > + int result, err;
> > + u32 cores, currentv, min, max;
> > +
> > + result = kstrtou32(buf, 10, &cores);
> > + if (result)
> > + return result;
> > +
> > + if (core_type == CPU_CORE_PERF) {
> > + min = asus_bios.rog_tunables->min_perf_cores;
> > + max = asus_bios.rog_tunables->max_perf_cores;
> > + } else {
> > + min = 0;
> > + max = asus_bios.rog_tunables->max_power_cores;
> > + }
> > + if (cores < min || cores > max)
> > + return -EINVAL;
> > +
> > + err = asus_wmi_get_devstate_dsts(ASUS_WMI_DEVID_CORES, ¤tv);
> > + if (err)
> > + return err;
> > +
> > + if (core_type == CPU_CORE_PERF)
> > + cores |= (currentv & 0xff00);
> > + else
> > + cores |= currentv & 0xff;
> > +
> > + if (cores == currentv)
> > + return 0;
> > +
> > + err = asus_wmi_set_devstate(ASUS_WMI_DEVID_CORES, cores, &result);
> > + if (err) {
> > + pr_warn("Failed to set perfromance core count: %d\n", err);
> > + return err;
> > + }
> > +
> > + if (result > 1) {
> > + pr_warn("Failed to set performance core count (result): 0x%x\n", result);
> > + return -EIO;
> > + }
> > +
> > + pr_info("CPU core count changed, reboot required\n");
> > + sysfs_notify(kobj, NULL, attr->attr.name);
> > + asus_set_reboot_and_signal_event();
> > +
> > + return 0;
> > +}
> > +
> > +static ssize_t cores_performance_min_value_show(struct kobject *kobj,
> > + struct kobj_attribute *attr, char *buf)
> > +{
> > + return cores_value_show(kobj, attr, buf, CPU_CORE_PERF, CPU_CORE_MIN);
> > +}
> > +
> > +static ssize_t cores_performance_max_value_show(struct kobject *kobj,
> > + struct kobj_attribute *attr, char *buf)
> > +{
> > + return cores_value_show(kobj, attr, buf, CPU_CORE_PERF, CPU_CORE_MAX);
> > +}
> > +
> > +static ssize_t cores_performance_default_value_show(struct kobject *kobj,
> > + struct kobj_attribute *attr, char *buf)
> > +{
> > + return cores_value_show(kobj, attr, buf, CPU_CORE_PERF, CPU_CORE_DEFAULT);
> > +}
> > +
> > +static ssize_t cores_performance_current_value_show(struct kobject *kobj,
> > + struct kobj_attribute *attr, char *buf)
> > +{
> > + return cores_value_show(kobj, attr, buf, CPU_CORE_PERF, CPU_CORE_CURRENT);
> > +}
> > +
> > +static ssize_t cores_performance_current_value_store(struct kobject *kobj,
> > + struct kobj_attribute *attr,
> > + const char *buf, size_t count)
> > +{
> > + int err;
> > +
> > + err = cores_current_value_store(kobj, attr, buf, CPU_CORE_PERF);
> > + if (err)
> > + return err;
> > +
> > + return count;
> > +}
> > +ATTR_GROUP_CORES_RW(cores_performance, "cores_performance", ASUS_WMI_DEVID_CORES,
> > + "Set the max available performance cores");
> > +
> > +static ssize_t cores_efficiency_min_value_show(struct kobject *kobj,
> > + struct kobj_attribute *attr, char *buf)
> > +{
> > + return cores_value_show(kobj, attr, buf, CPU_CORE_POWER, CPU_CORE_MIN);
> > +}
> > +
> > +static ssize_t cores_efficiency_max_value_show(struct kobject *kobj,
> > + struct kobj_attribute *attr, char *buf)
> > +{
> > + return cores_value_show(kobj, attr, buf, CPU_CORE_POWER, CPU_CORE_MAX);
> > +}
> > +
> > +static ssize_t cores_efficiency_default_value_show(struct kobject *kobj,
> > + struct kobj_attribute *attr, char *buf)
> > +{
> > + return cores_value_show(kobj, attr, buf, CPU_CORE_POWER, CPU_CORE_DEFAULT);
> > +}
> > +
> > +static ssize_t cores_efficiency_current_value_show(struct kobject *kobj,
> > + struct kobj_attribute *attr, char *buf)
> > +{
> > + return cores_value_show(kobj, attr, buf, CPU_CORE_POWER, CPU_CORE_CURRENT);
> > +}
> > +
> > +static ssize_t cores_efficiency_current_value_store(struct kobject *kobj,
> > + struct kobj_attribute *attr,
> > + const char *buf, size_t count)
> > +{
> > + int err;
> > +
> > + err = cores_current_value_store(kobj, attr, buf, CPU_CORE_POWER);
> > + if (err)
> > + return err;
> > +
> > + return count;
> > +}
> > +ATTR_GROUP_CORES_RW(cores_efficiency, "cores_efficiency", ASUS_WMI_DEVID_CORES,
> > + "Set the max available efficiency cores");
> > +
> > /* Simple attribute creation */
> > ATTR_GROUP_ENUM_INT_RW(thermal_policy, "thermal_policy", ASUS_WMI_DEVID_THROTTLE_THERMAL_POLICY,
> > 0, 3, "0;1;2", "Set the thermal profile: 0=normal, 1=performance, 2=quiet");
> > @@ -636,6 +837,11 @@ static int asus_fw_attr_add(void)
> > if (asus_wmi_is_present(ASUS_WMI_DEVID_EGPU_CONNECTED))
> > sysfs_create_group(&asus_bioscfg.fw_attr_kset->kobj, &egpu_connected_attr_group);
> >
> > + if (asus_wmi_is_present(ASUS_WMI_DEVID_CORES_MAX) && !asus_bios_set_max_cores()) {
> > + sysfs_create_group(&asus_bios.fw_attr_kset->kobj, &cores_performance_attr_group);
> > + sysfs_create_group(&asus_bios.fw_attr_kset->kobj, &cores_efficiency_attr_group);
> > + }
> > +
> > if (asus_wmi_is_present(ASUS_WMI_DEVID_THROTTLE_THERMAL_POLICY))
> > sysfs_create_group(&asus_bioscfg.fw_attr_kset->kobj, &thermal_policy_attr_group);
> > if (asus_wmi_is_present(ASUS_WMI_DEVID_PPT_PL1_SPL))
> > diff --git a/drivers/platform/x86/asus-bioscfg.h b/drivers/platform/x86/asus-bioscfg.h
> > index 2da55a91ff0b..bae1976eda3f 100644
> > --- a/drivers/platform/x86/asus-bioscfg.h
> > +++ b/drivers/platform/x86/asus-bioscfg.h
> > @@ -244,6 +244,35 @@ static const struct attribute_group _attrname##_attr_group = { \
> > .attrs = _attrname##_attrs \
> > }
> >
> > +/* CPU core attributes need a little different in setup */
> > +#define ATTR_GROUP_CORES_RW(_attrname, _fsname, _dispname) \
> > +__ATTR_SHOW_FMT(scalar_increment, _attrname, "%d\n", 1); \
> > +__ATTR_SHOW_FMT(display_name, _attrname, "%s\n", _dispname); \
> > +static struct kobj_attribute attr_##_attrname##_current_value = \
> > + __ASUS_ATTR_RW(_attrname, current_value); \
> > +static struct kobj_attribute attr_##_attrname##_default_value = \
> > + __ASUS_ATTR_RO(_attrname, default_value); \
> > +static struct kobj_attribute attr_##_attrname##_min_value = \
> > + __ASUS_ATTR_RO(_attrname, min_value); \
> > +static struct kobj_attribute attr_##_attrname##_max_value = \
> > + __ASUS_ATTR_RO(_attrname, max_value); \
> > +static struct kobj_attribute attr_##_attrname##_type = \
> > + __ASUS_ATTR_RO_AS(type, int_type_show); \
> > +static struct attribute *_attrname##_attrs[] = { \
> > + &attr_##_attrname##_current_value.attr, \
> > + &attr_##_attrname##_default_value.attr, \
> > + &attr_##_attrname##_min_value.attr, \
> > + &attr_##_attrname##_max_value.attr, \
> > + &attr_##_attrname##_scalar_increment.attr, \
> > + &attr_##_attrname##_display_name.attr, \
> > + &attr_##_attrname##_type.attr, \
> > + NULL \
> > +}; \
> > +static const struct attribute_group _attrname##_attr_group = { \
> > + .name = _fsname, \
> > + .attrs = _attrname##_attrs \
> > +}
> > +
> > /* ROG PPT attributes need a little different in setup */
> > #define ATTR_GROUP_PPT_RW(_attrname, _fsname, _wmi, _default, \
> > _min, _max, _incstep, _dispname) \
> > diff --git a/include/linux/platform_data/x86/asus-wmi.h b/include/linux/platform_data/x86/asus-wmi.h
> > index e3d511918416..d8f713ed3ea3 100644
> > --- a/include/linux/platform_data/x86/asus-wmi.h
> > +++ b/include/linux/platform_data/x86/asus-wmi.h
> > @@ -128,6 +128,10 @@
> > /* dgpu on/off */
> > #define ASUS_WMI_DEVID_DGPU 0x00090020
> >
> > +/* Intel E-core and P-core configuration in a format 0x0[E]0[P] */
> > +#define ASUS_WMI_DEVID_CORES 0x001200D2
> > + /* Maximum Intel E-core and P-core availability */
> > +#define ASUS_WMI_DEVID_CORES_MAX 0x001200D3
> > #define ASUS_WMI_DEVID_DGPU_BASE_TGP 0x00120099
> > #define ASUS_WMI_DEVID_DGPU_SET_TGP 0x00120098
> > #define ASUS_WMI_DEVID_APU_MEM 0x000600C1
>
>
^ permalink raw reply [flat|nested] 22+ messages in thread* Re: [PATCH 4/5] platform/x86: asus-bios: add core count control
2024-07-16 9:41 ` Luke Jones
@ 2024-07-16 9:43 ` Luke Jones
0 siblings, 0 replies; 22+ messages in thread
From: Luke Jones @ 2024-07-16 9:43 UTC (permalink / raw)
To: Hans de Goede, platform-driver-x86
Cc: corentin.chary, Ilpo Järvinen, Mario Limonciello,
linux-kernel
On Tue, 16 Jul 2024, at 9:41 PM, Luke Jones wrote:
>
>
> On Tue, 16 Jul 2024, at 9:25 PM, Hans de Goede wrote:
> > Hi,
> >
> > On 7/16/24 7:16 AM, Luke D. Jones wrote:
> > > Implement Intel core enablement under the asus-bioscfg module using the
> > > fw_attributes class.
> > >
> > > This allows users to enable or disable preformance or efficiency cores
> > > depending on their requirements. After change a reboot is required.
> >
> > Not a full revieew, just a quick remark. You say this this
> > requires a reboot, but this patch does not update asus_bios_requires_reboot()
> >
>
> Thanks for catching that!
Huh.. Okay looks like asus_bios_requires_reboot() got screwed up with the rebase editing I've done. The return statement is followed by a line that will never ge run too.
>
> >
> >
> > >
> > > Signed-off-by: Luke D. Jones <luke@ljones.dev>
> > > ---
> > > drivers/platform/x86/asus-bioscfg.c | 206 +++++++++++++++++++++
> > > drivers/platform/x86/asus-bioscfg.h | 29 +++
> > > include/linux/platform_data/x86/asus-wmi.h | 4 +
> > > 3 files changed, 239 insertions(+)
> > >
> > > diff --git a/drivers/platform/x86/asus-bioscfg.c b/drivers/platform/x86/asus-bioscfg.c
> > > index 57efe50e0d19..6d39e45591e8 100644
> > > --- a/drivers/platform/x86/asus-bioscfg.c
> > > +++ b/drivers/platform/x86/asus-bioscfg.c
> > > @@ -41,6 +41,18 @@ MODULE_ALIAS("wmi:"ASUS_NB_WMI_EVENT_GUID);
> > > #define ASUS_MINI_LED_2024_STRONG 0x01
> > > #define ASUS_MINI_LED_2024_OFF 0x02
> > >
> > > +enum cpu_core_type {
> > > + CPU_CORE_PERF = 0,
> > > + CPU_CORE_POWER,
> > > +};
> > > +
> > > +enum cpu_core_value {
> > > + CPU_CORE_DEFAULT = 0,
> > > + CPU_CORE_MIN,
> > > + CPU_CORE_MAX,
> > > + CPU_CORE_CURRENT,
> > > +};
> > > +
> > > /* Default limits for tunables available on ASUS ROG laptops */
> > > #define PPT_CPU_LIMIT_MIN 5
> > > #define PPT_CPU_LIMIT_MAX 150
> > > @@ -75,6 +87,10 @@ struct rog_tunables {
> > > u32 nv_temp_default;
> > > u32 nv_temp_max;
> > > u32 nv_temp_target;
> > > +
> > > + u32 min_perf_cores;
> > > + u32 max_perf_cores;
> > > + u32 max_power_cores;
> > > };
> > >
> > > static const struct class *fw_attr_class;
> > > @@ -540,6 +556,191 @@ static ssize_t apu_mem_possible_values_show(struct kobject *kobj,
> > > }
> > > ATTR_GROUP_ENUM_CUSTOM(apu_mem, "apu_mem", "Set the available system memory for the APU to use");
> > >
> > > +static int asus_bios_set_max_cores(void)
> > > +{
> > > + u32 cores;
> > > + int err;
> > > +
> > > + asus_bios.rog_tunables->min_perf_cores = 4;
> > > + asus_bios.rog_tunables->max_perf_cores = 4;
> > > + asus_bios.rog_tunables->max_power_cores = 8;
> > > +
> > > + err = asus_wmi_get_devstate_dsts(ASUS_WMI_DEVID_CORES_MAX, &cores);
> > > + if (err)
> > > + return err;
> > > +
> > > + cores &= ~ASUS_WMI_DSTS_PRESENCE_BIT;
> > > + asus_bios.rog_tunables->max_power_cores = (cores & 0xff00) >> 8;
> > > + asus_bios.rog_tunables->max_perf_cores = cores & 0xff;
> > > +
> > > + return 0;
> > > +}
> > > +
> > > +static ssize_t cores_value_show(struct kobject *kobj,
> > > + struct kobj_attribute *attr, char *buf,
> > > + enum cpu_core_type core_type,
> > > + enum cpu_core_value core_value)
> > > +{
> > > + u32 cores;
> > > + int err;
> > > +
> > > + switch (core_value) {
> > > + case CPU_CORE_DEFAULT:
> > > + case CPU_CORE_MAX:
> > > + if (core_type == CPU_CORE_PERF)
> > > + return sysfs_emit(buf, "%d\n", asus_bios.rog_tunables->max_perf_cores);
> > > + else
> > > + return sysfs_emit(buf, "%d\n", asus_bios.rog_tunables->max_power_cores);
> > > + case CPU_CORE_MIN:
> > > + if (core_type == CPU_CORE_PERF)
> > > + return sysfs_emit(buf, "%d\n", asus_bios.rog_tunables->min_perf_cores);
> > > + else
> > > + return sysfs_emit(buf, "%d\n", 0);
> > > + default:
> > > + break;
> > > + }
> > > +
> > > + err = asus_wmi_get_devstate_dsts(ASUS_WMI_DEVID_CORES, &cores);
> > > + if (err)
> > > + return err;
> > > +
> > > + cores &= ~ASUS_WMI_DSTS_PRESENCE_BIT;
> > > + if (core_type == CPU_CORE_PERF)
> > > + cores &= 0xff;
> > > + else
> > > + cores = (cores & 0xff00) >> 8;
> > > + return sysfs_emit(buf, "%d\n", cores);
> > > +}
> > > +
> > > +static ssize_t cores_current_value_store(struct kobject *kobj,
> > > + struct kobj_attribute *attr, const char *buf,
> > > + enum cpu_core_type core_type)
> > > +{
> > > + int result, err;
> > > + u32 cores, currentv, min, max;
> > > +
> > > + result = kstrtou32(buf, 10, &cores);
> > > + if (result)
> > > + return result;
> > > +
> > > + if (core_type == CPU_CORE_PERF) {
> > > + min = asus_bios.rog_tunables->min_perf_cores;
> > > + max = asus_bios.rog_tunables->max_perf_cores;
> > > + } else {
> > > + min = 0;
> > > + max = asus_bios.rog_tunables->max_power_cores;
> > > + }
> > > + if (cores < min || cores > max)
> > > + return -EINVAL;
> > > +
> > > + err = asus_wmi_get_devstate_dsts(ASUS_WMI_DEVID_CORES, ¤tv);
> > > + if (err)
> > > + return err;
> > > +
> > > + if (core_type == CPU_CORE_PERF)
> > > + cores |= (currentv & 0xff00);
> > > + else
> > > + cores |= currentv & 0xff;
> > > +
> > > + if (cores == currentv)
> > > + return 0;
> > > +
> > > + err = asus_wmi_set_devstate(ASUS_WMI_DEVID_CORES, cores, &result);
> > > + if (err) {
> > > + pr_warn("Failed to set perfromance core count: %d\n", err);
> > > + return err;
> > > + }
> > > +
> > > + if (result > 1) {
> > > + pr_warn("Failed to set performance core count (result): 0x%x\n", result);
> > > + return -EIO;
> > > + }
> > > +
> > > + pr_info("CPU core count changed, reboot required\n");
> > > + sysfs_notify(kobj, NULL, attr->attr.name);
> > > + asus_set_reboot_and_signal_event();
> > > +
> > > + return 0;
> > > +}
> > > +
> > > +static ssize_t cores_performance_min_value_show(struct kobject *kobj,
> > > + struct kobj_attribute *attr, char *buf)
> > > +{
> > > + return cores_value_show(kobj, attr, buf, CPU_CORE_PERF, CPU_CORE_MIN);
> > > +}
> > > +
> > > +static ssize_t cores_performance_max_value_show(struct kobject *kobj,
> > > + struct kobj_attribute *attr, char *buf)
> > > +{
> > > + return cores_value_show(kobj, attr, buf, CPU_CORE_PERF, CPU_CORE_MAX);
> > > +}
> > > +
> > > +static ssize_t cores_performance_default_value_show(struct kobject *kobj,
> > > + struct kobj_attribute *attr, char *buf)
> > > +{
> > > + return cores_value_show(kobj, attr, buf, CPU_CORE_PERF, CPU_CORE_DEFAULT);
> > > +}
> > > +
> > > +static ssize_t cores_performance_current_value_show(struct kobject *kobj,
> > > + struct kobj_attribute *attr, char *buf)
> > > +{
> > > + return cores_value_show(kobj, attr, buf, CPU_CORE_PERF, CPU_CORE_CURRENT);
> > > +}
> > > +
> > > +static ssize_t cores_performance_current_value_store(struct kobject *kobj,
> > > + struct kobj_attribute *attr,
> > > + const char *buf, size_t count)
> > > +{
> > > + int err;
> > > +
> > > + err = cores_current_value_store(kobj, attr, buf, CPU_CORE_PERF);
> > > + if (err)
> > > + return err;
> > > +
> > > + return count;
> > > +}
> > > +ATTR_GROUP_CORES_RW(cores_performance, "cores_performance", ASUS_WMI_DEVID_CORES,
> > > + "Set the max available performance cores");
> > > +
> > > +static ssize_t cores_efficiency_min_value_show(struct kobject *kobj,
> > > + struct kobj_attribute *attr, char *buf)
> > > +{
> > > + return cores_value_show(kobj, attr, buf, CPU_CORE_POWER, CPU_CORE_MIN);
> > > +}
> > > +
> > > +static ssize_t cores_efficiency_max_value_show(struct kobject *kobj,
> > > + struct kobj_attribute *attr, char *buf)
> > > +{
> > > + return cores_value_show(kobj, attr, buf, CPU_CORE_POWER, CPU_CORE_MAX);
> > > +}
> > > +
> > > +static ssize_t cores_efficiency_default_value_show(struct kobject *kobj,
> > > + struct kobj_attribute *attr, char *buf)
> > > +{
> > > + return cores_value_show(kobj, attr, buf, CPU_CORE_POWER, CPU_CORE_DEFAULT);
> > > +}
> > > +
> > > +static ssize_t cores_efficiency_current_value_show(struct kobject *kobj,
> > > + struct kobj_attribute *attr, char *buf)
> > > +{
> > > + return cores_value_show(kobj, attr, buf, CPU_CORE_POWER, CPU_CORE_CURRENT);
> > > +}
> > > +
> > > +static ssize_t cores_efficiency_current_value_store(struct kobject *kobj,
> > > + struct kobj_attribute *attr,
> > > + const char *buf, size_t count)
> > > +{
> > > + int err;
> > > +
> > > + err = cores_current_value_store(kobj, attr, buf, CPU_CORE_POWER);
> > > + if (err)
> > > + return err;
> > > +
> > > + return count;
> > > +}
> > > +ATTR_GROUP_CORES_RW(cores_efficiency, "cores_efficiency", ASUS_WMI_DEVID_CORES,
> > > + "Set the max available efficiency cores");
> > > +
> > > /* Simple attribute creation */
> > > ATTR_GROUP_ENUM_INT_RW(thermal_policy, "thermal_policy", ASUS_WMI_DEVID_THROTTLE_THERMAL_POLICY,
> > > 0, 3, "0;1;2", "Set the thermal profile: 0=normal, 1=performance, 2=quiet");
> > > @@ -636,6 +837,11 @@ static int asus_fw_attr_add(void)
> > > if (asus_wmi_is_present(ASUS_WMI_DEVID_EGPU_CONNECTED))
> > > sysfs_create_group(&asus_bioscfg.fw_attr_kset->kobj, &egpu_connected_attr_group);
> > >
> > > + if (asus_wmi_is_present(ASUS_WMI_DEVID_CORES_MAX) && !asus_bios_set_max_cores()) {
> > > + sysfs_create_group(&asus_bios.fw_attr_kset->kobj, &cores_performance_attr_group);
> > > + sysfs_create_group(&asus_bios.fw_attr_kset->kobj, &cores_efficiency_attr_group);
> > > + }
> > > +
> > > if (asus_wmi_is_present(ASUS_WMI_DEVID_THROTTLE_THERMAL_POLICY))
> > > sysfs_create_group(&asus_bioscfg.fw_attr_kset->kobj, &thermal_policy_attr_group);
> > > if (asus_wmi_is_present(ASUS_WMI_DEVID_PPT_PL1_SPL))
> > > diff --git a/drivers/platform/x86/asus-bioscfg.h b/drivers/platform/x86/asus-bioscfg.h
> > > index 2da55a91ff0b..bae1976eda3f 100644
> > > --- a/drivers/platform/x86/asus-bioscfg.h
> > > +++ b/drivers/platform/x86/asus-bioscfg.h
> > > @@ -244,6 +244,35 @@ static const struct attribute_group _attrname##_attr_group = { \
> > > .attrs = _attrname##_attrs \
> > > }
> > >
> > > +/* CPU core attributes need a little different in setup */
> > > +#define ATTR_GROUP_CORES_RW(_attrname, _fsname, _dispname) \
> > > +__ATTR_SHOW_FMT(scalar_increment, _attrname, "%d\n", 1); \
> > > +__ATTR_SHOW_FMT(display_name, _attrname, "%s\n", _dispname); \
> > > +static struct kobj_attribute attr_##_attrname##_current_value = \
> > > + __ASUS_ATTR_RW(_attrname, current_value); \
> > > +static struct kobj_attribute attr_##_attrname##_default_value = \
> > > + __ASUS_ATTR_RO(_attrname, default_value); \
> > > +static struct kobj_attribute attr_##_attrname##_min_value = \
> > > + __ASUS_ATTR_RO(_attrname, min_value); \
> > > +static struct kobj_attribute attr_##_attrname##_max_value = \
> > > + __ASUS_ATTR_RO(_attrname, max_value); \
> > > +static struct kobj_attribute attr_##_attrname##_type = \
> > > + __ASUS_ATTR_RO_AS(type, int_type_show); \
> > > +static struct attribute *_attrname##_attrs[] = { \
> > > + &attr_##_attrname##_current_value.attr, \
> > > + &attr_##_attrname##_default_value.attr, \
> > > + &attr_##_attrname##_min_value.attr, \
> > > + &attr_##_attrname##_max_value.attr, \
> > > + &attr_##_attrname##_scalar_increment.attr, \
> > > + &attr_##_attrname##_display_name.attr, \
> > > + &attr_##_attrname##_type.attr, \
> > > + NULL \
> > > +}; \
> > > +static const struct attribute_group _attrname##_attr_group = { \
> > > + .name = _fsname, \
> > > + .attrs = _attrname##_attrs \
> > > +}
> > > +
> > > /* ROG PPT attributes need a little different in setup */
> > > #define ATTR_GROUP_PPT_RW(_attrname, _fsname, _wmi, _default, \
> > > _min, _max, _incstep, _dispname) \
> > > diff --git a/include/linux/platform_data/x86/asus-wmi.h b/include/linux/platform_data/x86/asus-wmi.h
> > > index e3d511918416..d8f713ed3ea3 100644
> > > --- a/include/linux/platform_data/x86/asus-wmi.h
> > > +++ b/include/linux/platform_data/x86/asus-wmi.h
> > > @@ -128,6 +128,10 @@
> > > /* dgpu on/off */
> > > #define ASUS_WMI_DEVID_DGPU 0x00090020
> > >
> > > +/* Intel E-core and P-core configuration in a format 0x0[E]0[P] */
> > > +#define ASUS_WMI_DEVID_CORES 0x001200D2
> > > + /* Maximum Intel E-core and P-core availability */
> > > +#define ASUS_WMI_DEVID_CORES_MAX 0x001200D3
> > > #define ASUS_WMI_DEVID_DGPU_BASE_TGP 0x00120099
> > > #define ASUS_WMI_DEVID_DGPU_SET_TGP 0x00120098
> > > #define ASUS_WMI_DEVID_APU_MEM 0x000600C1
> >
> >
^ permalink raw reply [flat|nested] 22+ messages in thread
* [PATCH 5/5] asus-wmi: deprecate bios features
2024-07-16 5:16 [PATCH 0/5] platform/x86: introduce asus-bioscfg Luke D. Jones
` (3 preceding siblings ...)
2024-07-16 5:16 ` [PATCH 4/5] platform/x86: asus-bios: add core count control Luke D. Jones
@ 2024-07-16 5:16 ` Luke D. Jones
2024-07-16 9:20 ` [PATCH 0/5] platform/x86: introduce asus-bioscfg Hans de Goede
5 siblings, 0 replies; 22+ messages in thread
From: Luke D. Jones @ 2024-07-16 5:16 UTC (permalink / raw)
To: platform-driver-x86
Cc: corentin.chary, hdegoede, ilpo.jarvinen, mario.limonciello,
linux-kernel, Luke D. Jones
With the existence of the asus-bioscfg module the attributes no-longer
need to live under the /sys/devices/platform/asus-nb-wmi/ path.
Deprecate all those that were implemented in asus-bioscfg with the goal
of removing them fully in the next LTS cycle.
Signed-off-by: Luke D. Jones <luke@ljones.dev>
---
.../ABI/testing/sysfs-platform-asus-wmi | 17 +++
drivers/platform/x86/Kconfig | 8 ++
drivers/platform/x86/asus-wmi.c | 126 ++++++++++++++----
3 files changed, 123 insertions(+), 28 deletions(-)
diff --git a/Documentation/ABI/testing/sysfs-platform-asus-wmi b/Documentation/ABI/testing/sysfs-platform-asus-wmi
index 28144371a0f1..765d50b0d9df 100644
--- a/Documentation/ABI/testing/sysfs-platform-asus-wmi
+++ b/Documentation/ABI/testing/sysfs-platform-asus-wmi
@@ -63,6 +63,7 @@ Date: Aug 2022
KernelVersion: 6.1
Contact: "Luke Jones" <luke@ljones.dev>
Description:
+ DEPRECATED, WILL BE REMOVED SOON
Switch the GPU hardware MUX mode. Laptops with this feature can
can be toggled to boot with only the dGPU (discrete mode) or in
standard Optimus/Hybrid mode. On switch a reboot is required:
@@ -75,6 +76,7 @@ Date: Aug 2022
KernelVersion: 5.17
Contact: "Luke Jones" <luke@ljones.dev>
Description:
+ DEPRECATED, WILL BE REMOVED SOON
Disable discrete GPU:
* 0 - Enable dGPU,
* 1 - Disable dGPU
@@ -84,6 +86,7 @@ Date: Aug 2022
KernelVersion: 5.17
Contact: "Luke Jones" <luke@ljones.dev>
Description:
+ DEPRECATED, WILL BE REMOVED SOON
Enable the external GPU paired with ROG X-Flow laptops.
Toggling this setting will also trigger ACPI to disable the dGPU:
@@ -95,6 +98,7 @@ Date: Aug 2022
KernelVersion: 5.17
Contact: "Luke Jones" <luke@ljones.dev>
Description:
+ DEPRECATED, WILL BE REMOVED SOON
Enable an LCD response-time boost to reduce or remove ghosting:
* 0 - Disable,
* 1 - Enable
@@ -104,6 +108,7 @@ Date: Jun 2023
KernelVersion: 6.5
Contact: "Luke Jones" <luke@ljones.dev>
Description:
+ DEPRECATED, WILL BE REMOVED SOON
Get the current charging mode being used:
* 1 - Barrel connected charger,
* 2 - USB-C charging
@@ -114,6 +119,7 @@ Date: Jun 2023
KernelVersion: 6.5
Contact: "Luke Jones" <luke@ljones.dev>
Description:
+ DEPRECATED, WILL BE REMOVED SOON
Show if the egpu (XG Mobile) is correctly connected:
* 0 - False,
* 1 - True
@@ -123,6 +129,7 @@ Date: Jun 2023
KernelVersion: 6.5
Contact: "Luke Jones" <luke@ljones.dev>
Description:
+ DEPRECATED, WILL BE REMOVED SOON
Change the mini-LED mode:
* 0 - Single-zone,
* 1 - Multi-zone
@@ -133,6 +140,7 @@ Date: Apr 2024
KernelVersion: 6.10
Contact: "Luke Jones" <luke@ljones.dev>
Description:
+ DEPRECATED, WILL BE REMOVED SOON
List the available mini-led modes.
What: /sys/devices/platform/<platform>/ppt_pl1_spl
@@ -140,6 +148,7 @@ Date: Jun 2023
KernelVersion: 6.5
Contact: "Luke Jones" <luke@ljones.dev>
Description:
+ DEPRECATED, WILL BE REMOVED SOON
Set the Package Power Target total of CPU: PL1 on Intel, SPL on AMD.
Shown on Intel+Nvidia or AMD+Nvidia based systems:
@@ -150,6 +159,7 @@ Date: Jun 2023
KernelVersion: 6.5
Contact: "Luke Jones" <luke@ljones.dev>
Description:
+ DEPRECATED, WILL BE REMOVED SOON
Set the Slow Package Power Tracking Limit of CPU: PL2 on Intel, SPPT,
on AMD. Shown on Intel+Nvidia or AMD+Nvidia based systems:
@@ -160,6 +170,7 @@ Date: Jun 2023
KernelVersion: 6.5
Contact: "Luke Jones" <luke@ljones.dev>
Description:
+ DEPRECATED, WILL BE REMOVED SOON
Set the Fast Package Power Tracking Limit of CPU. AMD+Nvidia only:
* min=5, max=250
@@ -168,6 +179,7 @@ Date: Jun 2023
KernelVersion: 6.5
Contact: "Luke Jones" <luke@ljones.dev>
Description:
+ DEPRECATED, WILL BE REMOVED SOON
Set the APU SPPT limit. Shown on full AMD systems only:
* min=5, max=130
@@ -176,6 +188,7 @@ Date: Jun 2023
KernelVersion: 6.5
Contact: "Luke Jones" <luke@ljones.dev>
Description:
+ DEPRECATED, WILL BE REMOVED SOON
Set the platform SPPT limit. Shown on full AMD systems only:
* min=5, max=130
@@ -184,6 +197,7 @@ Date: Jun 2023
KernelVersion: 6.5
Contact: "Luke Jones" <luke@ljones.dev>
Description:
+ DEPRECATED, WILL BE REMOVED SOON
Set the dynamic boost limit of the Nvidia dGPU:
* min=5, max=25
@@ -192,6 +206,7 @@ Date: Jun 2023
KernelVersion: 6.5
Contact: "Luke Jones" <luke@ljones.dev>
Description:
+ DEPRECATED, WILL BE REMOVED SOON
Set the target temperature limit of the Nvidia dGPU:
* min=75, max=87
@@ -200,6 +215,7 @@ Date: Apr 2024
KernelVersion: 6.10
Contact: "Luke Jones" <luke@ljones.dev>
Description:
+ DEPRECATED, WILL BE REMOVED SOON
Set if the BIOS POST sound is played on boot.
* 0 - False,
* 1 - True
@@ -209,6 +225,7 @@ Date: Apr 2024
KernelVersion: 6.10
Contact: "Luke Jones" <luke@ljones.dev>
Description:
+ DEPRECATED, WILL BE REMOVED SOON
Set if the MCU can go in to low-power mode on system sleep
* 0 - False,
* 1 - True
diff --git a/drivers/platform/x86/Kconfig b/drivers/platform/x86/Kconfig
index b4a5a5bec7f3..164c53ed06c3 100644
--- a/drivers/platform/x86/Kconfig
+++ b/drivers/platform/x86/Kconfig
@@ -301,6 +301,14 @@ config ASUS_WMI
To compile this driver as a module, choose M here: the module will
be called asus-wmi.
+config ASUS_WMI_BIOS
+ bool "BIOS option support in WMI platform (DEPRECATED)"
+ depends on ASUS_WMI
+ help
+ Say Y to expose the configurable BIOS options through the asus-wmi
+ driver. This can be used with or without the new asus-bios driver as
+ the options are the same but the asus-bios driver has more features.
+
config ASUS_NB_WMI
tristate "Asus Notebook WMI Driver"
depends on ASUS_WMI
diff --git a/drivers/platform/x86/asus-wmi.c b/drivers/platform/x86/asus-wmi.c
index 3f1998638fea..e5ae1b766929 100644
--- a/drivers/platform/x86/asus-wmi.c
+++ b/drivers/platform/x86/asus-wmi.c
@@ -269,11 +269,12 @@ struct asus_wmi {
u8 fan_boost_mode_mask;
u8 fan_boost_mode;
+
+ /* Tunables provided by ASUS for gaming laptops */
+ #if IS_ENABLED(CONFIG_ASUS_WMI_BIOS)
bool egpu_enable_available;
bool dgpu_disable_available;
u32 gpu_mux_dev;
-
- /* Tunables provided by ASUS for gaming laptops */
u32 ppt_pl2_sppt;
u32 ppt_pl1_spl;
u32 ppt_apu_sppt;
@@ -281,6 +282,9 @@ struct asus_wmi {
u32 ppt_fppt;
u32 nv_dynamic_boost;
u32 nv_temp_target;
+ bool panel_overdrive_available;
+ u32 mini_led_dev_id;
+ #endif
u32 kbd_rgb_dev;
bool kbd_rgb_state_available;
@@ -299,9 +303,6 @@ struct asus_wmi {
// The RSOC controls the maximum charging percentage.
bool battery_rsoc_available;
- bool panel_overdrive_available;
- u32 mini_led_dev_id;
-
struct hotplug_slot hotplug_slot;
struct mutex hotplug_lock;
struct mutex wmi_lock;
@@ -315,6 +316,15 @@ struct asus_wmi {
struct asus_wmi_driver *driver;
};
+#if IS_ENABLED(CONFIG_ASUS_WMI_BIOS)
+void asus_wmi_show_deprecated(void)
+{
+ pr_notice_once("Accessing attributes through /sys/bus/platform/asus_wmi is \
+ deprecated and will be removed in a future release. Please switch \
+ over to /sys/class/firmware_attributes.");
+}
+#endif
+
/* WMI ************************************************************************/
static int asus_wmi_evaluate_method3(u32 method_id,
@@ -683,6 +693,7 @@ static void asus_wmi_tablet_mode_get_state(struct asus_wmi *asus)
}
/* Charging mode, 1=Barrel, 2=USB ******************************************/
+#if IS_ENABLED(CONFIG_ASUS_WMI_BIOS)
static ssize_t charge_mode_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
@@ -693,12 +704,16 @@ static ssize_t charge_mode_show(struct device *dev,
if (result < 0)
return result;
+ asus_wmi_show_deprecated();
+
return sysfs_emit(buf, "%d\n", value & 0xff);
}
static DEVICE_ATTR_RO(charge_mode);
+#endif
/* dGPU ********************************************************************/
+#if IS_ENABLED(CONFIG_ASUS_WMI_BIOS)
static ssize_t dgpu_disable_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
@@ -709,6 +724,8 @@ static ssize_t dgpu_disable_show(struct device *dev,
if (result < 0)
return result;
+ asus_wmi_show_deprecated();
+
return sysfs_emit(buf, "%d\n", result);
}
@@ -762,8 +779,10 @@ static ssize_t dgpu_disable_store(struct device *dev,
return count;
}
static DEVICE_ATTR_RW(dgpu_disable);
+#endif
/* eGPU ********************************************************************/
+#if IS_ENABLED(CONFIG_ASUS_WMI_BIOS)
static ssize_t egpu_enable_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
@@ -774,6 +793,8 @@ static ssize_t egpu_enable_show(struct device *dev,
if (result < 0)
return result;
+ asus_wmi_show_deprecated();
+
return sysfs_emit(buf, "%d\n", result);
}
@@ -830,8 +851,10 @@ static ssize_t egpu_enable_store(struct device *dev,
return count;
}
static DEVICE_ATTR_RW(egpu_enable);
+#endif
/* Is eGPU connected? *********************************************************/
+#if IS_ENABLED(CONFIG_ASUS_WMI_BIOS)
static ssize_t egpu_connected_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
@@ -842,12 +865,16 @@ static ssize_t egpu_connected_show(struct device *dev,
if (result < 0)
return result;
+ asus_wmi_show_deprecated();
+
return sysfs_emit(buf, "%d\n", result);
}
static DEVICE_ATTR_RO(egpu_connected);
+#endif
/* gpu mux switch *************************************************************/
+#if IS_ENABLED(CONFIG_ASUS_WMI_BIOS)
static ssize_t gpu_mux_mode_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
@@ -858,6 +885,8 @@ static ssize_t gpu_mux_mode_show(struct device *dev,
if (result < 0)
return result;
+ asus_wmi_show_deprecated();
+
return sysfs_emit(buf, "%d\n", result);
}
@@ -916,6 +945,7 @@ static ssize_t gpu_mux_mode_store(struct device *dev,
return count;
}
static DEVICE_ATTR_RW(gpu_mux_mode);
+#endif
/* TUF Laptop Keyboard RGB Modes **********************************************/
static ssize_t kbd_rgb_mode_store(struct device *dev,
@@ -1039,6 +1069,7 @@ static const struct attribute_group *kbd_rgb_mode_groups[] = {
};
/* Tunable: PPT: Intel=PL1, AMD=SPPT *****************************************/
+#if IS_ENABLED(CONFIG_ASUS_WMI_BIOS)
static ssize_t ppt_pl2_sppt_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
@@ -1077,6 +1108,8 @@ static ssize_t ppt_pl2_sppt_show(struct device *dev,
{
struct asus_wmi *asus = dev_get_drvdata(dev);
+ asus_wmi_show_deprecated();
+
return sysfs_emit(buf, "%u\n", asus->ppt_pl2_sppt);
}
static DEVICE_ATTR_RW(ppt_pl2_sppt);
@@ -1119,6 +1152,8 @@ static ssize_t ppt_pl1_spl_show(struct device *dev,
{
struct asus_wmi *asus = dev_get_drvdata(dev);
+ asus_wmi_show_deprecated();
+
return sysfs_emit(buf, "%u\n", asus->ppt_pl1_spl);
}
static DEVICE_ATTR_RW(ppt_pl1_spl);
@@ -1162,6 +1197,8 @@ static ssize_t ppt_fppt_show(struct device *dev,
{
struct asus_wmi *asus = dev_get_drvdata(dev);
+ asus_wmi_show_deprecated();
+
return sysfs_emit(buf, "%u\n", asus->ppt_fppt);
}
static DEVICE_ATTR_RW(ppt_fppt);
@@ -1205,6 +1242,8 @@ static ssize_t ppt_apu_sppt_show(struct device *dev,
{
struct asus_wmi *asus = dev_get_drvdata(dev);
+ asus_wmi_show_deprecated();
+
return sysfs_emit(buf, "%u\n", asus->ppt_apu_sppt);
}
static DEVICE_ATTR_RW(ppt_apu_sppt);
@@ -1248,6 +1287,8 @@ static ssize_t ppt_platform_sppt_show(struct device *dev,
{
struct asus_wmi *asus = dev_get_drvdata(dev);
+ asus_wmi_show_deprecated();
+
return sysfs_emit(buf, "%u\n", asus->ppt_platform_sppt);
}
static DEVICE_ATTR_RW(ppt_platform_sppt);
@@ -1291,6 +1332,8 @@ static ssize_t nv_dynamic_boost_show(struct device *dev,
{
struct asus_wmi *asus = dev_get_drvdata(dev);
+ asus_wmi_show_deprecated();
+
return sysfs_emit(buf, "%u\n", asus->nv_dynamic_boost);
}
static DEVICE_ATTR_RW(nv_dynamic_boost);
@@ -1334,11 +1377,15 @@ static ssize_t nv_temp_target_show(struct device *dev,
{
struct asus_wmi *asus = dev_get_drvdata(dev);
+ asus_wmi_show_deprecated();
+
return sysfs_emit(buf, "%u\n", asus->nv_temp_target);
}
static DEVICE_ATTR_RW(nv_temp_target);
+#endif
/* Ally MCU Powersave ********************************************************/
+#if IS_ENABLED(CONFIG_ASUS_WMI_BIOS)
static ssize_t mcu_powersave_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
@@ -1349,6 +1396,8 @@ static ssize_t mcu_powersave_show(struct device *dev,
if (result < 0)
return result;
+ asus_wmi_show_deprecated();
+
return sysfs_emit(buf, "%d\n", result);
}
@@ -1384,6 +1433,7 @@ static ssize_t mcu_powersave_store(struct device *dev,
return count;
}
static DEVICE_ATTR_RW(mcu_powersave);
+#endif
/* Battery ********************************************************************/
@@ -2213,6 +2263,7 @@ static int asus_wmi_rfkill_init(struct asus_wmi *asus)
}
/* Panel Overdrive ************************************************************/
+#if IS_ENABLED(CONFIG_ASUS_WMI_BIOS)
static ssize_t panel_od_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
@@ -2223,6 +2274,8 @@ static ssize_t panel_od_show(struct device *dev,
if (result < 0)
return result;
+ asus_wmi_show_deprecated();
+
return sysfs_emit(buf, "%d\n", result);
}
@@ -2259,9 +2312,10 @@ static ssize_t panel_od_store(struct device *dev,
return count;
}
static DEVICE_ATTR_RW(panel_od);
+#endif
/* Bootup sound ***************************************************************/
-
+#if IS_ENABLED(CONFIG_ASUS_WMI_BIOS)
static ssize_t boot_sound_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
@@ -2272,6 +2326,8 @@ static ssize_t boot_sound_show(struct device *dev,
if (result < 0)
return result;
+ asus_wmi_show_deprecated();
+
return sysfs_emit(buf, "%d\n", result);
}
@@ -2307,8 +2363,10 @@ static ssize_t boot_sound_store(struct device *dev,
return count;
}
static DEVICE_ATTR_RW(boot_sound);
+#endif
/* Mini-LED mode **************************************************************/
+#if IS_ENABLED(CONFIG_ASUS_WMI_BIOS)
static ssize_t mini_led_mode_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
@@ -2339,6 +2397,8 @@ static ssize_t mini_led_mode_show(struct device *dev,
}
}
+ asus_wmi_show_deprecated();
+
return sysfs_emit(buf, "%d\n", value);
}
@@ -2409,10 +2469,13 @@ static ssize_t available_mini_led_mode_show(struct device *dev,
return sysfs_emit(buf, "0 1 2\n");
}
+ asus_wmi_show_deprecated();
+
return sysfs_emit(buf, "0\n");
}
static DEVICE_ATTR_RO(available_mini_led_mode);
+#endif
/* Quirks *********************************************************************/
@@ -4378,27 +4441,29 @@ static struct attribute *platform_attributes[] = {
&dev_attr_camera.attr,
&dev_attr_cardr.attr,
&dev_attr_touchpad.attr,
- &dev_attr_charge_mode.attr,
- &dev_attr_egpu_enable.attr,
- &dev_attr_egpu_connected.attr,
- &dev_attr_dgpu_disable.attr,
- &dev_attr_gpu_mux_mode.attr,
&dev_attr_lid_resume.attr,
&dev_attr_als_enable.attr,
&dev_attr_fan_boost_mode.attr,
&dev_attr_throttle_thermal_policy.attr,
- &dev_attr_ppt_pl2_sppt.attr,
- &dev_attr_ppt_pl1_spl.attr,
- &dev_attr_ppt_fppt.attr,
- &dev_attr_ppt_apu_sppt.attr,
- &dev_attr_ppt_platform_sppt.attr,
- &dev_attr_nv_dynamic_boost.attr,
- &dev_attr_nv_temp_target.attr,
- &dev_attr_mcu_powersave.attr,
- &dev_attr_boot_sound.attr,
- &dev_attr_panel_od.attr,
- &dev_attr_mini_led_mode.attr,
- &dev_attr_available_mini_led_mode.attr,
+ #if IS_ENABLED(CONFIG_ASUS_WMI_BIOS)
+ &dev_attr_charge_mode.attr,
+ &dev_attr_egpu_enable.attr,
+ &dev_attr_egpu_connected.attr,
+ &dev_attr_dgpu_disable.attr,
+ &dev_attr_gpu_mux_mode.attr,
+ &dev_attr_ppt_pl2_sppt.attr,
+ &dev_attr_ppt_pl1_spl.attr,
+ &dev_attr_ppt_fppt.attr,
+ &dev_attr_ppt_apu_sppt.attr,
+ &dev_attr_ppt_platform_sppt.attr,
+ &dev_attr_nv_dynamic_boost.attr,
+ &dev_attr_nv_temp_target.attr,
+ &dev_attr_mcu_powersave.attr,
+ &dev_attr_boot_sound.attr,
+ &dev_attr_panel_od.attr,
+ &dev_attr_mini_led_mode.attr,
+ &dev_attr_available_mini_led_mode.attr,
+ #endif
NULL
};
@@ -4420,7 +4485,13 @@ static umode_t asus_sysfs_is_visible(struct kobject *kobj,
devid = ASUS_WMI_DEVID_LID_RESUME;
else if (attr == &dev_attr_als_enable.attr)
devid = ASUS_WMI_DEVID_ALS_ENABLE;
- else if (attr == &dev_attr_charge_mode.attr)
+ else if (attr == &dev_attr_fan_boost_mode.attr)
+ ok = asus->fan_boost_mode_available;
+ else if (attr == &dev_attr_throttle_thermal_policy.attr)
+ ok = asus->throttle_thermal_policy_available;
+
+ #if IS_ENABLED(CONFIG_ASUS_WMI_BIOS)
+ if (attr == &dev_attr_charge_mode.attr)
devid = ASUS_WMI_DEVID_CHARGE_MODE;
else if (attr == &dev_attr_egpu_enable.attr)
ok = asus->egpu_enable_available;
@@ -4430,10 +4501,6 @@ static umode_t asus_sysfs_is_visible(struct kobject *kobj,
ok = asus->dgpu_disable_available;
else if (attr == &dev_attr_gpu_mux_mode.attr)
ok = asus->gpu_mux_dev != 0;
- else if (attr == &dev_attr_fan_boost_mode.attr)
- ok = asus->fan_boost_mode_available;
- else if (attr == &dev_attr_throttle_thermal_policy.attr)
- ok = asus->throttle_thermal_policy_available;
else if (attr == &dev_attr_ppt_pl2_sppt.attr)
devid = ASUS_WMI_DEVID_PPT_PL2_SPPT;
else if (attr == &dev_attr_ppt_pl1_spl.attr)
@@ -4458,6 +4525,7 @@ static umode_t asus_sysfs_is_visible(struct kobject *kobj,
ok = asus->mini_led_dev_id != 0;
else if (attr == &dev_attr_available_mini_led_mode.attr)
ok = asus->mini_led_dev_id != 0;
+ #endif
if (devid != -1) {
ok = !(asus_wmi_get_devstate_simple(asus, devid) < 0);
@@ -4698,6 +4766,7 @@ static int asus_wmi_add(struct platform_device *pdev)
goto fail_platform;
/* ensure defaults for tunables */
+ #if IS_ENABLED(CONFIG_ASUS_WMI_BIOS)
asus->ppt_pl2_sppt = 5;
asus->ppt_pl1_spl = 5;
asus->ppt_apu_sppt = 5;
@@ -4721,6 +4790,7 @@ static int asus_wmi_add(struct platform_device *pdev)
asus->gpu_mux_dev = ASUS_WMI_DEVID_GPU_MUX;
else if (asus_wmi_dev_is_present(asus, ASUS_WMI_DEVID_GPU_MUX_VIVO))
asus->gpu_mux_dev = ASUS_WMI_DEVID_GPU_MUX_VIVO;
+ #endif
if (asus_wmi_dev_is_present(asus, ASUS_WMI_DEVID_TUF_RGB_MODE))
asus->kbd_rgb_dev = ASUS_WMI_DEVID_TUF_RGB_MODE;
--
2.45.2
^ permalink raw reply related [flat|nested] 22+ messages in thread* Re: [PATCH 0/5] platform/x86: introduce asus-bioscfg
2024-07-16 5:16 [PATCH 0/5] platform/x86: introduce asus-bioscfg Luke D. Jones
` (4 preceding siblings ...)
2024-07-16 5:16 ` [PATCH 5/5] asus-wmi: deprecate bios features Luke D. Jones
@ 2024-07-16 9:20 ` Hans de Goede
2024-07-16 15:11 ` Ilpo Järvinen
5 siblings, 1 reply; 22+ messages in thread
From: Hans de Goede @ 2024-07-16 9:20 UTC (permalink / raw)
To: Luke D. Jones, platform-driver-x86
Cc: corentin.chary, ilpo.jarvinen, mario.limonciello, linux-kernel
Hi Luke, Mario,
On 7/16/24 7:16 AM, Luke D. Jones wrote:
> This is the first major patch I've ever done with the intention of
> introducing a new module, so it's highly likely I've made some mistakes
> or misunderstood something.
>
> TL;DR:
> 1. introduce new module to contain bios attributes, using fw_attributes_class
> 2. deprecate all possible attributes from asus-wmi that were added ad-hoc
> 3. remove those in the next LTS cycle
>
> The idea for this originates from a conversation with Mario Limonciello
> https://lore.kernel.org/platform-driver-x86/371d4109-a3bb-4c3b-802f-4ec27a945c99@amd.com/
>
> It is without a doubt much cleaner to use, easier to discover, and the
> API is well defined as opposed to the random clutter of attributes I had
> been placing in the platform sysfs.
This is a bit of a novel use of the fw_attributes_class and I'm not
entirely sure of what to think of this.
The fw_attributes_class API was designed for (mostly enterprise)
x86 machines where it is possible to change all BIOS settings directly
from the OS without entering the BIOS.
Here some ACPI or WMI function is present to actually enumerate all
the BIOS options (which can be set this way) and get there type.
IOW there is not a static list of options inside the driver, nor
is there special handling in the driver other then handling differences
per type.
And if a new BIOS version has new options or a different machine model
has different options then these are discovered automatically.
This new use is quite different from this. Although I do see that
at least for the attributes using WMI_STORE_INT() + WMI_SHOW_INT()
that there is quite some commonality between some of the attributes.
I see how using the existing firmware-attributes class API definition
for this, including allow tweaking this with some of the fwupd
firmware-attributes class commandline util work Mario did is a useful
thing to have.
I guess using the firmware-attributes class for this is ok, but
this _must_ not be named bioscfg, since the existing hp-bioscfg
driver is a "classic" firmware-attributes drivers enumerating all
the options through BIOS provided enumeration functions and I want
the name to make it clear that this is not that. And the Dell
implementation is called dell-wmi-sysman so lets also avoid sysman
as name.
Maybe call it "asus-bios-tunables" ? And then if Asus actually
implements some more classic firmware-attributes enumerable interface
we can use "asus-bioscfg" for that.
Mario, Ilpo what is your opinion on this ?
Regards,
Hans
^ permalink raw reply [flat|nested] 22+ messages in thread* Re: [PATCH 0/5] platform/x86: introduce asus-bioscfg
2024-07-16 9:20 ` [PATCH 0/5] platform/x86: introduce asus-bioscfg Hans de Goede
@ 2024-07-16 15:11 ` Ilpo Järvinen
2024-07-17 2:34 ` Luke Jones
0 siblings, 1 reply; 22+ messages in thread
From: Ilpo Järvinen @ 2024-07-16 15:11 UTC (permalink / raw)
To: Hans de Goede
Cc: Luke D. Jones, platform-driver-x86, corentin.chary,
mario.limonciello, LKML
On Tue, 16 Jul 2024, Hans de Goede wrote:
> On 7/16/24 7:16 AM, Luke D. Jones wrote:
> > This is the first major patch I've ever done with the intention of
> > introducing a new module, so it's highly likely I've made some mistakes
> > or misunderstood something.
> >
> > TL;DR:
> > 1. introduce new module to contain bios attributes, using fw_attributes_class
> > 2. deprecate all possible attributes from asus-wmi that were added ad-hoc
> > 3. remove those in the next LTS cycle
> >
> > The idea for this originates from a conversation with Mario Limonciello
> > https://lore.kernel.org/platform-driver-x86/371d4109-a3bb-4c3b-802f-4ec27a945c99@amd.com/
> >
> > It is without a doubt much cleaner to use, easier to discover, and the
> > API is well defined as opposed to the random clutter of attributes I had
> > been placing in the platform sysfs.
>
> This is a bit of a novel use of the fw_attributes_class and I'm not
> entirely sure of what to think of this.
>
> The fw_attributes_class API was designed for (mostly enterprise)
> x86 machines where it is possible to change all BIOS settings directly
> from the OS without entering the BIOS.
>
> Here some ACPI or WMI function is present to actually enumerate all
> the BIOS options (which can be set this way) and get there type.
>
> IOW there is not a static list of options inside the driver, nor
> is there special handling in the driver other then handling differences
> per type.
>
> And if a new BIOS version has new options or a different machine model
> has different options then these are discovered automatically.
>
> This new use is quite different from this. Although I do see that
> at least for the attributes using WMI_STORE_INT() + WMI_SHOW_INT()
> that there is quite some commonality between some of the attributes.
>
> I see how using the existing firmware-attributes class API definition
> for this, including allow tweaking this with some of the fwupd
> firmware-attributes class commandline util work Mario did is a useful
> thing to have.
>
> I guess using the firmware-attributes class for this is ok, but
> this _must_ not be named bioscfg, since the existing hp-bioscfg
> driver is a "classic" firmware-attributes drivers enumerating all
> the options through BIOS provided enumeration functions and I want
> the name to make it clear that this is not that. And the Dell
> implementation is called dell-wmi-sysman so lets also avoid sysman
> as name.
>
> Maybe call it "asus-bios-tunables" ? And then if Asus actually
> implements some more classic firmware-attributes enumerable interface
> we can use "asus-bioscfg" for that.
>
> Mario, Ilpo what is your opinion on this ?
What you suggested sounds good to me.
--
i.
^ permalink raw reply [flat|nested] 22+ messages in thread
* Re: [PATCH 0/5] platform/x86: introduce asus-bioscfg
2024-07-16 15:11 ` Ilpo Järvinen
@ 2024-07-17 2:34 ` Luke Jones
2024-08-05 15:18 ` Hans de Goede
0 siblings, 1 reply; 22+ messages in thread
From: Luke Jones @ 2024-07-17 2:34 UTC (permalink / raw)
To: Ilpo Järvinen, Hans de Goede
Cc: platform-driver-x86, corentin.chary, Mario Limonciello, LKML
On Wed, 17 Jul 2024, at 3:11 AM, Ilpo Järvinen wrote:
> On Tue, 16 Jul 2024, Hans de Goede wrote:
> > On 7/16/24 7:16 AM, Luke D. Jones wrote:
> > > This is the first major patch I've ever done with the intention of
> > > introducing a new module, so it's highly likely I've made some mistakes
> > > or misunderstood something.
> > >
> > > TL;DR:
> > > 1. introduce new module to contain bios attributes, using fw_attributes_class
> > > 2. deprecate all possible attributes from asus-wmi that were added ad-hoc
> > > 3. remove those in the next LTS cycle
> > >
> > > The idea for this originates from a conversation with Mario Limonciello
> > > https://lore.kernel.org/platform-driver-x86/371d4109-a3bb-4c3b-802f-4ec27a945c99@amd.com/
> > >
> > > It is without a doubt much cleaner to use, easier to discover, and the
> > > API is well defined as opposed to the random clutter of attributes I had
> > > been placing in the platform sysfs.
> >
> > This is a bit of a novel use of the fw_attributes_class and I'm not
> > entirely sure of what to think of this.
> >
> > The fw_attributes_class API was designed for (mostly enterprise)
> > x86 machines where it is possible to change all BIOS settings directly
> > from the OS without entering the BIOS.
> >
> > Here some ACPI or WMI function is present to actually enumerate all
> > the BIOS options (which can be set this way) and get there type.
> >
> > IOW there is not a static list of options inside the driver, nor
> > is there special handling in the driver other then handling differences
> > per type.
> >
> > And if a new BIOS version has new options or a different machine model
> > has different options then these are discovered automatically.
> >
> > This new use is quite different from this. Although I do see that
> > at least for the attributes using WMI_STORE_INT() + WMI_SHOW_INT()
> > that there is quite some commonality between some of the attributes.
> >
> > I see how using the existing firmware-attributes class API definition
> > for this, including allow tweaking this with some of the fwupd
> > firmware-attributes class commandline util work Mario did is a useful
> > thing to have.
> >
> > I guess using the firmware-attributes class for this is ok, but
> > this _must_ not be named bioscfg, since the existing hp-bioscfg
> > driver is a "classic" firmware-attributes drivers enumerating all
> > the options through BIOS provided enumeration functions and I want
> > the name to make it clear that this is not that. And the Dell
> > implementation is called dell-wmi-sysman so lets also avoid sysman
> > as name.
> >
> > Maybe call it "asus-bios-tunables" ? And then if Asus actually
> > implements some more classic firmware-attributes enumerable interface
> > we can use "asus-bioscfg" for that.
> >
> > Mario, Ilpo what is your opinion on this ?
>
> What you suggested sounds good to me.
Thanks guys. I think there might be a few names that could be suitable
1. asus_bios_tuning/tunables
2. asus_fw_tuning/tunables
3. asus_fw_settings
4. asus_armoury
I'm shying away from the "tuning/tunables" label since there are also a lot of settings which are just toggles for various features rather than actual tunable things. It makes sense in some contexts at least.
Armoury Crate is the software collection that Asus uses for the gaming laptops, and they tend to lump these settings under that label.
Personally I'm leaning towards "asus_fw_settings" as it more accurately describes what the module does.
^ permalink raw reply [flat|nested] 22+ messages in thread
* Re: [PATCH 0/5] platform/x86: introduce asus-bioscfg
2024-07-17 2:34 ` Luke Jones
@ 2024-08-05 15:18 ` Hans de Goede
2024-08-05 21:41 ` Luke Jones
0 siblings, 1 reply; 22+ messages in thread
From: Hans de Goede @ 2024-08-05 15:18 UTC (permalink / raw)
To: Luke Jones, Ilpo Järvinen
Cc: platform-driver-x86, corentin.chary, Mario Limonciello, LKML
Hi Luke,
On 7/17/24 4:34 AM, Luke Jones wrote:
> On Wed, 17 Jul 2024, at 3:11 AM, Ilpo Järvinen wrote:
>> On Tue, 16 Jul 2024, Hans de Goede wrote:
>>> On 7/16/24 7:16 AM, Luke D. Jones wrote:
>>>> This is the first major patch I've ever done with the intention of
>>>> introducing a new module, so it's highly likely I've made some mistakes
>>>> or misunderstood something.
>>>>
>>>> TL;DR:
>>>> 1. introduce new module to contain bios attributes, using fw_attributes_class
>>>> 2. deprecate all possible attributes from asus-wmi that were added ad-hoc
>>>> 3. remove those in the next LTS cycle
>>>>
>>>> The idea for this originates from a conversation with Mario Limonciello
>>>> https://lore.kernel.org/platform-driver-x86/371d4109-a3bb-4c3b-802f-4ec27a945c99@amd.com/
>>>>
>>>> It is without a doubt much cleaner to use, easier to discover, and the
>>>> API is well defined as opposed to the random clutter of attributes I had
>>>> been placing in the platform sysfs.
>>>
>>> This is a bit of a novel use of the fw_attributes_class and I'm not
>>> entirely sure of what to think of this.
>>>
>>> The fw_attributes_class API was designed for (mostly enterprise)
>>> x86 machines where it is possible to change all BIOS settings directly
>>> from the OS without entering the BIOS.
>>>
>>> Here some ACPI or WMI function is present to actually enumerate all
>>> the BIOS options (which can be set this way) and get there type.
>>>
>>> IOW there is not a static list of options inside the driver, nor
>>> is there special handling in the driver other then handling differences
>>> per type.
>>>
>>> And if a new BIOS version has new options or a different machine model
>>> has different options then these are discovered automatically.
>>>
>>> This new use is quite different from this. Although I do see that
>>> at least for the attributes using WMI_STORE_INT() + WMI_SHOW_INT()
>>> that there is quite some commonality between some of the attributes.
>>>
>>> I see how using the existing firmware-attributes class API definition
>>> for this, including allow tweaking this with some of the fwupd
>>> firmware-attributes class commandline util work Mario did is a useful
>>> thing to have.
>>>
>>> I guess using the firmware-attributes class for this is ok, but
>>> this _must_ not be named bioscfg, since the existing hp-bioscfg
>>> driver is a "classic" firmware-attributes drivers enumerating all
>>> the options through BIOS provided enumeration functions and I want
>>> the name to make it clear that this is not that. And the Dell
>>> implementation is called dell-wmi-sysman so lets also avoid sysman
>>> as name.
>>>
>>> Maybe call it "asus-bios-tunables" ? And then if Asus actually
>>> implements some more classic firmware-attributes enumerable interface
>>> we can use "asus-bioscfg" for that.
>>>
>>> Mario, Ilpo what is your opinion on this ?
>>
>> What you suggested sounds good to me.
>
> Thanks guys. I think there might be a few names that could be suitable
>
> 1. asus_bios_tuning/tunables
> 2. asus_fw_tuning/tunables
> 3. asus_fw_settings
> 4. asus_armoury
>
> I'm shying away from the "tuning/tunables" label since there are also a lot of settings which are just toggles for various features rather than actual tunable things. It makes sense in some contexts at least.
>
> Armoury Crate is the software collection that Asus uses for the gaming laptops, and they tend to lump these settings under that label.
>
> Personally I'm leaning towards "asus_fw_settings" as it more accurately describes what the module does.
"asus_fw_settings" sounds good to me. I'm looking forward to v2 of this series.
Regards,
Hans
^ permalink raw reply [flat|nested] 22+ messages in thread
* Re: [PATCH 0/5] platform/x86: introduce asus-bioscfg
2024-08-05 15:18 ` Hans de Goede
@ 2024-08-05 21:41 ` Luke Jones
2024-08-06 9:34 ` Hans de Goede
0 siblings, 1 reply; 22+ messages in thread
From: Luke Jones @ 2024-08-05 21:41 UTC (permalink / raw)
To: Hans de Goede, Ilpo Järvinen
Cc: platform-driver-x86, corentin.chary, Mario Limonciello, LKML
On Tue, 6 Aug 2024, at 3:18 AM, Hans de Goede wrote:
> Hi Luke,
>
> On 7/17/24 4:34 AM, Luke Jones wrote:
> > On Wed, 17 Jul 2024, at 3:11 AM, Ilpo Järvinen wrote:
> >> On Tue, 16 Jul 2024, Hans de Goede wrote:
> >>> On 7/16/24 7:16 AM, Luke D. Jones wrote:
> >>>> This is the first major patch I've ever done with the intention of
> >>>> introducing a new module, so it's highly likely I've made some mistakes
> >>>> or misunderstood something.
> >>>>
> >>>> TL;DR:
> >>>> 1. introduce new module to contain bios attributes, using fw_attributes_class
> >>>> 2. deprecate all possible attributes from asus-wmi that were added ad-hoc
> >>>> 3. remove those in the next LTS cycle
> >>>>
> >>>> The idea for this originates from a conversation with Mario Limonciello
> >>>> https://lore.kernel.org/platform-driver-x86/371d4109-a3bb-4c3b-802f-4ec27a945c99@amd.com/
> >>>>
> >>>> It is without a doubt much cleaner to use, easier to discover, and the
> >>>> API is well defined as opposed to the random clutter of attributes I had
> >>>> been placing in the platform sysfs.
> >>>
> >>> This is a bit of a novel use of the fw_attributes_class and I'm not
> >>> entirely sure of what to think of this.
> >>>
> >>> The fw_attributes_class API was designed for (mostly enterprise)
> >>> x86 machines where it is possible to change all BIOS settings directly
> >>> from the OS without entering the BIOS.
> >>>
> >>> Here some ACPI or WMI function is present to actually enumerate all
> >>> the BIOS options (which can be set this way) and get there type.
> >>>
> >>> IOW there is not a static list of options inside the driver, nor
> >>> is there special handling in the driver other then handling differences
> >>> per type.
> >>>
> >>> And if a new BIOS version has new options or a different machine model
> >>> has different options then these are discovered automatically.
> >>>
> >>> This new use is quite different from this. Although I do see that
> >>> at least for the attributes using WMI_STORE_INT() + WMI_SHOW_INT()
> >>> that there is quite some commonality between some of the attributes.
> >>>
> >>> I see how using the existing firmware-attributes class API definition
> >>> for this, including allow tweaking this with some of the fwupd
> >>> firmware-attributes class commandline util work Mario did is a useful
> >>> thing to have.
> >>>
> >>> I guess using the firmware-attributes class for this is ok, but
> >>> this _must_ not be named bioscfg, since the existing hp-bioscfg
> >>> driver is a "classic" firmware-attributes drivers enumerating all
> >>> the options through BIOS provided enumeration functions and I want
> >>> the name to make it clear that this is not that. And the Dell
> >>> implementation is called dell-wmi-sysman so lets also avoid sysman
> >>> as name.
> >>>
> >>> Maybe call it "asus-bios-tunables" ? And then if Asus actually
> >>> implements some more classic firmware-attributes enumerable interface
> >>> we can use "asus-bioscfg" for that.
> >>>
> >>> Mario, Ilpo what is your opinion on this ?
> >>
> >> What you suggested sounds good to me.
> >
> > Thanks guys. I think there might be a few names that could be suitable
> >
> > 1. asus_bios_tuning/tunables
> > 2. asus_fw_tuning/tunables
> > 3. asus_fw_settings
> > 4. asus_armoury
> >
> > I'm shying away from the "tuning/tunables" label since there are also a lot of settings which are just toggles for various features rather than actual tunable things. It makes sense in some contexts at least.
> >
> > Armoury Crate is the software collection that Asus uses for the gaming laptops, and they tend to lump these settings under that label.
> >
> > Personally I'm leaning towards "asus_fw_settings" as it more accurately describes what the module does.
>
> "asus_fw_settings" sounds good to me. I'm looking forward to v2 of this series.
I've actually done a poll on my discord, folks voted overwhelmingly for `asus-armoury` and I went with this before your response. The reasoning here is that many of these users are coming from windows where Armoury Crate is an app that exposes all the same functions and so that's what they look for on linux.
If you don't think this is suitable I'm happy to change.
> Regards,
>
> Hans
>
>
>
^ permalink raw reply [flat|nested] 22+ messages in thread
* Re: [PATCH 0/5] platform/x86: introduce asus-bioscfg
2024-08-05 21:41 ` Luke Jones
@ 2024-08-06 9:34 ` Hans de Goede
2024-08-21 21:16 ` Mario Limonciello
0 siblings, 1 reply; 22+ messages in thread
From: Hans de Goede @ 2024-08-06 9:34 UTC (permalink / raw)
To: Luke Jones, Ilpo Järvinen
Cc: platform-driver-x86, corentin.chary, Mario Limonciello, LKML
Hi,
On 8/5/24 11:41 PM, Luke Jones wrote:
> On Tue, 6 Aug 2024, at 3:18 AM, Hans de Goede wrote:
>> Hi Luke,
>>
>> On 7/17/24 4:34 AM, Luke Jones wrote:
>>> On Wed, 17 Jul 2024, at 3:11 AM, Ilpo Järvinen wrote:
>>>> On Tue, 16 Jul 2024, Hans de Goede wrote:
>>>>> On 7/16/24 7:16 AM, Luke D. Jones wrote:
>>>>>> This is the first major patch I've ever done with the intention of
>>>>>> introducing a new module, so it's highly likely I've made some mistakes
>>>>>> or misunderstood something.
>>>>>>
>>>>>> TL;DR:
>>>>>> 1. introduce new module to contain bios attributes, using fw_attributes_class
>>>>>> 2. deprecate all possible attributes from asus-wmi that were added ad-hoc
>>>>>> 3. remove those in the next LTS cycle
>>>>>>
>>>>>> The idea for this originates from a conversation with Mario Limonciello
>>>>>> https://lore.kernel.org/platform-driver-x86/371d4109-a3bb-4c3b-802f-4ec27a945c99@amd.com/
>>>>>>
>>>>>> It is without a doubt much cleaner to use, easier to discover, and the
>>>>>> API is well defined as opposed to the random clutter of attributes I had
>>>>>> been placing in the platform sysfs.
>>>>>
>>>>> This is a bit of a novel use of the fw_attributes_class and I'm not
>>>>> entirely sure of what to think of this.
>>>>>
>>>>> The fw_attributes_class API was designed for (mostly enterprise)
>>>>> x86 machines where it is possible to change all BIOS settings directly
>>>>> from the OS without entering the BIOS.
>>>>>
>>>>> Here some ACPI or WMI function is present to actually enumerate all
>>>>> the BIOS options (which can be set this way) and get there type.
>>>>>
>>>>> IOW there is not a static list of options inside the driver, nor
>>>>> is there special handling in the driver other then handling differences
>>>>> per type.
>>>>>
>>>>> And if a new BIOS version has new options or a different machine model
>>>>> has different options then these are discovered automatically.
>>>>>
>>>>> This new use is quite different from this. Although I do see that
>>>>> at least for the attributes using WMI_STORE_INT() + WMI_SHOW_INT()
>>>>> that there is quite some commonality between some of the attributes.
>>>>>
>>>>> I see how using the existing firmware-attributes class API definition
>>>>> for this, including allow tweaking this with some of the fwupd
>>>>> firmware-attributes class commandline util work Mario did is a useful
>>>>> thing to have.
>>>>>
>>>>> I guess using the firmware-attributes class for this is ok, but
>>>>> this _must_ not be named bioscfg, since the existing hp-bioscfg
>>>>> driver is a "classic" firmware-attributes drivers enumerating all
>>>>> the options through BIOS provided enumeration functions and I want
>>>>> the name to make it clear that this is not that. And the Dell
>>>>> implementation is called dell-wmi-sysman so lets also avoid sysman
>>>>> as name.
>>>>>
>>>>> Maybe call it "asus-bios-tunables" ? And then if Asus actually
>>>>> implements some more classic firmware-attributes enumerable interface
>>>>> we can use "asus-bioscfg" for that.
>>>>>
>>>>> Mario, Ilpo what is your opinion on this ?
>>>>
>>>> What you suggested sounds good to me.
>>>
>>> Thanks guys. I think there might be a few names that could be suitable
>>>
>>> 1. asus_bios_tuning/tunables
>>> 2. asus_fw_tuning/tunables
>>> 3. asus_fw_settings
>>> 4. asus_armoury
>>>
>>> I'm shying away from the "tuning/tunables" label since there are also a lot of settings which are just toggles for various features rather than actual tunable things. It makes sense in some contexts at least.
>>>
>>> Armoury Crate is the software collection that Asus uses for the gaming laptops, and they tend to lump these settings under that label.
>>>
>>> Personally I'm leaning towards "asus_fw_settings" as it more accurately describes what the module does.
>>
>> "asus_fw_settings" sounds good to me. I'm looking forward to v2 of this series.
>
> I've actually done a poll on my discord, folks voted overwhelmingly for `asus-armoury` and I went with this before your response. The reasoning here is that many of these users are coming from windows where Armoury Crate is an app that exposes all the same functions and so that's what they look for on linux.
>
> If you don't think this is suitable I'm happy to change.
asus-armoury works for me, I like that that name makes it clear that this
is not changing BIOS settings like other firmware_attributes class devices
are doing.
So asus-armoury it is.
Regards,
Hans
>
>> Regards,
>>
>> Hans
>>
>>
>>
>
^ permalink raw reply [flat|nested] 22+ messages in thread
* Re: [PATCH 0/5] platform/x86: introduce asus-bioscfg
2024-08-06 9:34 ` Hans de Goede
@ 2024-08-21 21:16 ` Mario Limonciello
0 siblings, 0 replies; 22+ messages in thread
From: Mario Limonciello @ 2024-08-21 21:16 UTC (permalink / raw)
To: Hans de Goede, Luke Jones, Ilpo Järvinen
Cc: platform-driver-x86, corentin.chary, LKML
On 8/6/2024 04:34, Hans de Goede wrote:
> Hi,
>
> On 8/5/24 11:41 PM, Luke Jones wrote:
>> On Tue, 6 Aug 2024, at 3:18 AM, Hans de Goede wrote:
>>> Hi Luke,
>>>
>>> On 7/17/24 4:34 AM, Luke Jones wrote:
>>>> On Wed, 17 Jul 2024, at 3:11 AM, Ilpo Järvinen wrote:
>>>>> On Tue, 16 Jul 2024, Hans de Goede wrote:
>>>>>> On 7/16/24 7:16 AM, Luke D. Jones wrote:
>>>>>>> This is the first major patch I've ever done with the intention of
>>>>>>> introducing a new module, so it's highly likely I've made some mistakes
>>>>>>> or misunderstood something.
>>>>>>>
>>>>>>> TL;DR:
>>>>>>> 1. introduce new module to contain bios attributes, using fw_attributes_class
>>>>>>> 2. deprecate all possible attributes from asus-wmi that were added ad-hoc
>>>>>>> 3. remove those in the next LTS cycle
>>>>>>>
>>>>>>> The idea for this originates from a conversation with Mario Limonciello
>>>>>>> https://lore.kernel.org/platform-driver-x86/371d4109-a3bb-4c3b-802f-4ec27a945c99@amd.com/
>>>>>>>
>>>>>>> It is without a doubt much cleaner to use, easier to discover, and the
>>>>>>> API is well defined as opposed to the random clutter of attributes I had
>>>>>>> been placing in the platform sysfs.
>>>>>>
>>>>>> This is a bit of a novel use of the fw_attributes_class and I'm not
>>>>>> entirely sure of what to think of this.
>>>>>>
>>>>>> The fw_attributes_class API was designed for (mostly enterprise)
>>>>>> x86 machines where it is possible to change all BIOS settings directly
>>>>>> from the OS without entering the BIOS.
>>>>>>
>>>>>> Here some ACPI or WMI function is present to actually enumerate all
>>>>>> the BIOS options (which can be set this way) and get there type.
>>>>>>
>>>>>> IOW there is not a static list of options inside the driver, nor
>>>>>> is there special handling in the driver other then handling differences
>>>>>> per type.
>>>>>>
>>>>>> And if a new BIOS version has new options or a different machine model
>>>>>> has different options then these are discovered automatically.
>>>>>>
>>>>>> This new use is quite different from this. Although I do see that
>>>>>> at least for the attributes using WMI_STORE_INT() + WMI_SHOW_INT()
>>>>>> that there is quite some commonality between some of the attributes.
>>>>>>
>>>>>> I see how using the existing firmware-attributes class API definition
>>>>>> for this, including allow tweaking this with some of the fwupd
>>>>>> firmware-attributes class commandline util work Mario did is a useful
>>>>>> thing to have.
>>>>>>
>>>>>> I guess using the firmware-attributes class for this is ok, but
>>>>>> this _must_ not be named bioscfg, since the existing hp-bioscfg
>>>>>> driver is a "classic" firmware-attributes drivers enumerating all
>>>>>> the options through BIOS provided enumeration functions and I want
>>>>>> the name to make it clear that this is not that. And the Dell
>>>>>> implementation is called dell-wmi-sysman so lets also avoid sysman
>>>>>> as name.
>>>>>>
>>>>>> Maybe call it "asus-bios-tunables" ? And then if Asus actually
>>>>>> implements some more classic firmware-attributes enumerable interface
>>>>>> we can use "asus-bioscfg" for that.
>>>>>>
>>>>>> Mario, Ilpo what is your opinion on this ?
>>>>>
>>>>> What you suggested sounds good to me.
>>>>
>>>> Thanks guys. I think there might be a few names that could be suitable
>>>>
>>>> 1. asus_bios_tuning/tunables
>>>> 2. asus_fw_tuning/tunables
>>>> 3. asus_fw_settings
>>>> 4. asus_armoury
>>>>
>>>> I'm shying away from the "tuning/tunables" label since there are also a lot of settings which are just toggles for various features rather than actual tunable things. It makes sense in some contexts at least.
>>>>
>>>> Armoury Crate is the software collection that Asus uses for the gaming laptops, and they tend to lump these settings under that label.
>>>>
>>>> Personally I'm leaning towards "asus_fw_settings" as it more accurately describes what the module does.
>>>
>>> "asus_fw_settings" sounds good to me. I'm looking forward to v2 of this series.
>>
>> I've actually done a poll on my discord, folks voted overwhelmingly for `asus-armoury` and I went with this before your response. The reasoning here is that many of these users are coming from windows where Armoury Crate is an app that exposes all the same functions and so that's what they look for on linux.
>>
>> If you don't think this is suitable I'm happy to change.
>
> asus-armoury works for me, I like that that name makes it clear that this
> is not changing BIOS settings like other firmware_attributes class devices
> are doing.
>
> So asus-armoury it is.
>
I have been OOO so I'm late to the party, but yes I agree with adding a
prefix and the proposal for asus-armoury makes sense to me.
And I had suggested Luke in this direction of this API in use by this
series with fwupd's use of BIOS settings on other laptops in mind. It
feels like a really good pairing to me.
Luke also mentioned to me (and maybe somewhere else in this patch
series?) that the ASUS tools will be converted to use this interface. I
think standardized interfaces are the way to go, and that way if we ever
end up with GNOME or KDE GUI that changes firmware settings we cover all
the laptops and handhelds at the exact same time.
^ permalink raw reply [flat|nested] 22+ messages in thread