public inbox for linux-kernel@vger.kernel.org
 help / color / mirror / Atom feed
* [PATCH v6 0/5] Dell AWCC platform_profile support
@ 2024-10-17  8:12 Kurt Borja
  2024-10-17  8:13 ` [PATCH v6 1/5] alienware-wmi: fixed indentation and clean up Kurt Borja
                   ` (4 more replies)
  0 siblings, 5 replies; 15+ messages in thread
From: Kurt Borja @ 2024-10-17  8:12 UTC (permalink / raw)
  To: kuurtb; +Cc: hdegoede, W_Armin, ilpo.jarvinen, linux-kernel,
	platform-driver-x86

This patch adds platform_profile support for Dell devices which implement
User Selectable Thermal Tables (USTT) that are meant to be controlled by
Alienware Command Center (AWCC). These devices may include newer Alienware
M-Series, Alienware X-Series and Dell's G-Series. This patch, was tested
by me on an Alienware x15 R1.

---
v6:
 - Removed quirk thermal_ustt.
 - Now quirk thermal can take canonical thermal profile _tables_ defined
   in enum WMAX_THERMAL_TABLES
 - Added autodetect_thermal_profile
 - Proper removal of thermal profile
v5:
 - Better commit messages
 - insize renamed to in_size in alienware_wmax_command() to match other
   arguments.
 - Kudos in documentation now at the end of the file
v4:
 - Fixed indentation on previous code
 - Removed unnecessary (acpi_size) and (u32 *) casts
 - Return -EIO on ACPI_FAILURE
 - Appropiate prefixes given to macros
 - 0xFFFFFFFF named WMAX_FAILURE_CODE
 - Added support for a new set of thermal codes. Old ones now have USTT
   in their names
 - A new quirk has been added to differantiate between the two sets.
   thermal and thermal_ustt are mutually exclusive
 - Added documentation for WMAX interface
v3:
 - Removed extra empty line
 - 0x0B named WMAX_ARG_GET_CURRENT_PROF
 - Removed casts to the same type on functions added in this patch
 - Thermal profile to WMAX argument is now an static function and makes
   use of in-built kernel macros
 - Platform profile is now removed only if it was created first
 - create_platform_profile is now create_thermal_profile to avoid
   confusion
 - profile_get and profile_set functions renamed too to match the above
v2:
 - Moved functionality to alienware-wmi driver
 - Added thermal and gmode quirks to add support based on dmi match
 - Performance profile is now GMODE for devices that support it
 - alienware_wmax_command now is insize agnostic to support new thermal
   methods

Kurt Borja (5):
  alienware-wmi: fixed indentation and clean up
  alienware-wmi: alienware_wmax_command() is now input size agnostic
  alienware-wmi: added platform profile support
  alienware-wmi: added autodetect_thermal_profile for devices with
    quirk_unknown
  alienware-wmi: WMAX interface documentation

 Documentation/wmi/devices/alienware-wmi.rst | 366 ++++++++++++++++
 MAINTAINERS                                 |   1 +
 drivers/platform/x86/dell/Kconfig           |   1 +
 drivers/platform/x86/dell/alienware-wmi.c   | 446 ++++++++++++++++----
 4 files changed, 738 insertions(+), 76 deletions(-)
 create mode 100644 Documentation/wmi/devices/alienware-wmi.rst

-- 
2.47.0


^ permalink raw reply	[flat|nested] 15+ messages in thread

* [PATCH v6 1/5] alienware-wmi: fixed indentation and clean up
  2024-10-17  8:12 [PATCH v6 0/5] Dell AWCC platform_profile support Kurt Borja
@ 2024-10-17  8:13 ` Kurt Borja
  2024-10-20 20:04   ` Armin Wolf
  2024-10-17  8:14 ` [PATCH v6 2/5] alienware-wmi: alienware_wmax_command() is now input size agnostic Kurt Borja
                   ` (3 subsequent siblings)
  4 siblings, 1 reply; 15+ messages in thread
From: Kurt Borja @ 2024-10-17  8:13 UTC (permalink / raw)
  To: kuurtb; +Cc: W_Armin, hdegoede, ilpo.jarvinen, linux-kernel,
	platform-driver-x86

Fixed inconsistent indentation and removed unnecessary (acpi_size) and
(u32 *) casts.


Signed-off-by: Kurt Borja <kuurtb@gmail.com>

---
v6:
 - Unchanged
---
 drivers/platform/x86/dell/alienware-wmi.c | 134 +++++++++++-----------
 1 file changed, 67 insertions(+), 67 deletions(-)

diff --git a/drivers/platform/x86/dell/alienware-wmi.c b/drivers/platform/x86/dell/alienware-wmi.c
index f5ee62ce1..16a3fe9ac 100644
--- a/drivers/platform/x86/dell/alienware-wmi.c
+++ b/drivers/platform/x86/dell/alienware-wmi.c
@@ -116,68 +116,68 @@ static int __init dmi_matched(const struct dmi_system_id *dmi)
 
 static const struct dmi_system_id alienware_quirks[] __initconst = {
 	{
-	 .callback = dmi_matched,
-	 .ident = "Alienware X51 R3",
-	 .matches = {
-		     DMI_MATCH(DMI_SYS_VENDOR, "Alienware"),
-		     DMI_MATCH(DMI_PRODUCT_NAME, "Alienware X51 R3"),
-		     },
-	 .driver_data = &quirk_x51_r3,
-	 },
+		.callback = dmi_matched,
+		.ident = "Alienware X51 R3",
+		.matches = {
+			DMI_MATCH(DMI_SYS_VENDOR, "Alienware"),
+			DMI_MATCH(DMI_PRODUCT_NAME, "Alienware X51 R3"),
+		},
+		.driver_data = &quirk_x51_r3,
+	},
 	{
-	 .callback = dmi_matched,
-	 .ident = "Alienware X51 R2",
-	 .matches = {
-		     DMI_MATCH(DMI_SYS_VENDOR, "Alienware"),
-		     DMI_MATCH(DMI_PRODUCT_NAME, "Alienware X51 R2"),
-		     },
-	 .driver_data = &quirk_x51_r1_r2,
-	 },
+		.callback = dmi_matched,
+		.ident = "Alienware X51 R2",
+		.matches = {
+			DMI_MATCH(DMI_SYS_VENDOR, "Alienware"),
+			DMI_MATCH(DMI_PRODUCT_NAME, "Alienware X51 R2"),
+		},
+		.driver_data = &quirk_x51_r1_r2,
+	},
 	{
-	 .callback = dmi_matched,
-	 .ident = "Alienware X51 R1",
-	 .matches = {
-		     DMI_MATCH(DMI_SYS_VENDOR, "Alienware"),
-		     DMI_MATCH(DMI_PRODUCT_NAME, "Alienware X51"),
-		     },
-	 .driver_data = &quirk_x51_r1_r2,
-	 },
+		.callback = dmi_matched,
+		.ident = "Alienware X51 R1",
+		.matches = {
+			DMI_MATCH(DMI_SYS_VENDOR, "Alienware"),
+			DMI_MATCH(DMI_PRODUCT_NAME, "Alienware X51"),
+		},
+		.driver_data = &quirk_x51_r1_r2,
+	},
 	{
-	 .callback = dmi_matched,
-	 .ident = "Alienware ASM100",
-	 .matches = {
-		     DMI_MATCH(DMI_SYS_VENDOR, "Alienware"),
-		     DMI_MATCH(DMI_PRODUCT_NAME, "ASM100"),
-		     },
-	 .driver_data = &quirk_asm100,
-	 },
+		.callback = dmi_matched,
+		.ident = "Alienware ASM100",
+		.matches = {
+			DMI_MATCH(DMI_SYS_VENDOR, "Alienware"),
+			DMI_MATCH(DMI_PRODUCT_NAME, "ASM100"),
+		},
+		.driver_data = &quirk_asm100,
+	},
 	{
-	 .callback = dmi_matched,
-	 .ident = "Alienware ASM200",
-	 .matches = {
-		     DMI_MATCH(DMI_SYS_VENDOR, "Alienware"),
-		     DMI_MATCH(DMI_PRODUCT_NAME, "ASM200"),
-		     },
-	 .driver_data = &quirk_asm200,
-	 },
+		.callback = dmi_matched,
+		.ident = "Alienware ASM200",
+		.matches = {
+			DMI_MATCH(DMI_SYS_VENDOR, "Alienware"),
+			DMI_MATCH(DMI_PRODUCT_NAME, "ASM200"),
+		},
+		.driver_data = &quirk_asm200,
+	},
 	{
-	 .callback = dmi_matched,
-	 .ident = "Alienware ASM201",
-	 .matches = {
-		     DMI_MATCH(DMI_SYS_VENDOR, "Alienware"),
-		     DMI_MATCH(DMI_PRODUCT_NAME, "ASM201"),
-		     },
-	 .driver_data = &quirk_asm201,
-	 },
-	 {
-	 .callback = dmi_matched,
-	 .ident = "Dell Inc. Inspiron 5675",
-	 .matches = {
-		     DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
-		     DMI_MATCH(DMI_PRODUCT_NAME, "Inspiron 5675"),
-		     },
-	 .driver_data = &quirk_inspiron5675,
-	 },
+		.callback = dmi_matched,
+		.ident = "Alienware ASM201",
+		.matches = {
+			DMI_MATCH(DMI_SYS_VENDOR, "Alienware"),
+			DMI_MATCH(DMI_PRODUCT_NAME, "ASM201"),
+		},
+		.driver_data = &quirk_asm201,
+	},
+	{
+		.callback = dmi_matched,
+		.ident = "Dell Inc. Inspiron 5675",
+		.matches = {
+			DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
+			DMI_MATCH(DMI_PRODUCT_NAME, "Inspiron 5675"),
+		},
+		.driver_data = &quirk_inspiron5675,
+	},
 	{}
 };
 
@@ -221,8 +221,8 @@ static struct platform_zone *zone_data;
 
 static struct platform_driver platform_driver = {
 	.driver = {
-		   .name = "alienware-wmi",
-		   }
+		.name = "alienware-wmi",
+	}
 };
 
 static struct attribute_group zone_attribute_group = {
@@ -292,7 +292,7 @@ static int alienware_update_led(struct platform_zone *zone)
 		guid = WMAX_CONTROL_GUID;
 		method_id = WMAX_METHOD_ZONE_CONTROL;
 
-		input.length = (acpi_size) sizeof(wmax_basic_args);
+		input.length = sizeof(wmax_basic_args);
 		input.pointer = &wmax_basic_args;
 	} else {
 		legacy_args.colors = zone->colors;
@@ -306,7 +306,7 @@ static int alienware_update_led(struct platform_zone *zone)
 			guid = LEGACY_CONTROL_GUID;
 		method_id = zone->location + 1;
 
-		input.length = (acpi_size) sizeof(legacy_args);
+		input.length = sizeof(legacy_args);
 		input.pointer = &legacy_args;
 	}
 	pr_debug("alienware-wmi: guid %s method %d\n", guid, method_id);
@@ -358,7 +358,7 @@ static int wmax_brightness(int brightness)
 		.led_mask = 0xFF,
 		.percentage = brightness,
 	};
-	input.length = (acpi_size) sizeof(args);
+	input.length = sizeof(args);
 	input.pointer = &args;
 	status = wmi_evaluate_method(WMAX_CONTROL_GUID, 0,
 				     WMAX_METHOD_BRIGHTNESS, &input, NULL);
@@ -508,7 +508,7 @@ static acpi_status alienware_wmax_command(struct wmax_basic_args *in_args,
 	struct acpi_buffer input;
 	struct acpi_buffer output;
 
-	input.length = (acpi_size) sizeof(*in_args);
+	input.length = sizeof(*in_args);
 	input.pointer = in_args;
 	if (out_data) {
 		output.length = ACPI_ALLOCATE_BUFFER;
@@ -542,7 +542,7 @@ static ssize_t show_hdmi_cable(struct device *dev,
 	};
 	status =
 	    alienware_wmax_command(&in_args, WMAX_METHOD_HDMI_CABLE,
-				   (u32 *) &out_data);
+				   &out_data);
 	if (ACPI_SUCCESS(status)) {
 		if (out_data == 0)
 			return sysfs_emit(buf, "[unconnected] connected unknown\n");
@@ -563,7 +563,7 @@ static ssize_t show_hdmi_source(struct device *dev,
 	};
 	status =
 	    alienware_wmax_command(&in_args, WMAX_METHOD_HDMI_STATUS,
-				   (u32 *) &out_data);
+				   &out_data);
 
 	if (ACPI_SUCCESS(status)) {
 		if (out_data == 1)
@@ -643,7 +643,7 @@ static ssize_t show_amplifier_status(struct device *dev,
 	};
 	status =
 	    alienware_wmax_command(&in_args, WMAX_METHOD_AMPLIFIER_CABLE,
-				   (u32 *) &out_data);
+				   &out_data);
 	if (ACPI_SUCCESS(status)) {
 		if (out_data == 0)
 			return sysfs_emit(buf, "[unconnected] connected unknown\n");
@@ -695,7 +695,7 @@ static ssize_t show_deepsleep_status(struct device *dev,
 		.arg = 0,
 	};
 	status = alienware_wmax_command(&in_args, WMAX_METHOD_DEEP_SLEEP_STATUS,
-					(u32 *) &out_data);
+					&out_data);
 	if (ACPI_SUCCESS(status)) {
 		if (out_data == 0)
 			return sysfs_emit(buf, "[disabled] s5 s5_s4\n");
-- 
2.47.0


^ permalink raw reply related	[flat|nested] 15+ messages in thread

* [PATCH v6 2/5] alienware-wmi: alienware_wmax_command() is now input size agnostic
  2024-10-17  8:12 [PATCH v6 0/5] Dell AWCC platform_profile support Kurt Borja
  2024-10-17  8:13 ` [PATCH v6 1/5] alienware-wmi: fixed indentation and clean up Kurt Borja
@ 2024-10-17  8:14 ` Kurt Borja
  2024-10-20 20:05   ` Armin Wolf
  2024-10-17  8:15 ` [PATCH v6 3/5] alienware-wmi: added platform profile support Kurt Borja
                   ` (2 subsequent siblings)
  4 siblings, 1 reply; 15+ messages in thread
From: Kurt Borja @ 2024-10-17  8:14 UTC (permalink / raw)
  To: kuurtb; +Cc: W_Armin, hdegoede, ilpo.jarvinen, linux-kernel,
	platform-driver-x86

alienware_wmax_command() now takes void * and size_t instead of struct
wmax_basic_args to extend support to new WMAX methods. Also int *out_data
was changed to u32 *out_data, because new interface specifies u32 as output
parameter and all previous callers would pass u32 * regardless.

Signed-off-by: Kurt Borja <kuurtb@gmail.com>

---
v6:
 - Unchanged
---
 drivers/platform/x86/dell/alienware-wmi.c | 29 ++++++++++++-----------
 1 file changed, 15 insertions(+), 14 deletions(-)

diff --git a/drivers/platform/x86/dell/alienware-wmi.c b/drivers/platform/x86/dell/alienware-wmi.c
index 16a3fe9ac..b27f3b64c 100644
--- a/drivers/platform/x86/dell/alienware-wmi.c
+++ b/drivers/platform/x86/dell/alienware-wmi.c
@@ -500,15 +500,15 @@ static void alienware_zone_exit(struct platform_device *dev)
 	kfree(zone_attrs);
 }
 
-static acpi_status alienware_wmax_command(struct wmax_basic_args *in_args,
-					  u32 command, int *out_data)
+static acpi_status alienware_wmax_command(void *in_args, size_t in_size,
+					  u32 command, u32 *out_data)
 {
 	acpi_status status;
 	union acpi_object *obj;
 	struct acpi_buffer input;
 	struct acpi_buffer output;
 
-	input.length = sizeof(*in_args);
+	input.length = in_size;
 	input.pointer = in_args;
 	if (out_data) {
 		output.length = ACPI_ALLOCATE_BUFFER;
@@ -541,8 +541,8 @@ static ssize_t show_hdmi_cable(struct device *dev,
 		.arg = 0,
 	};
 	status =
-	    alienware_wmax_command(&in_args, WMAX_METHOD_HDMI_CABLE,
-				   &out_data);
+	    alienware_wmax_command(&in_args, sizeof(in_args),
+				   WMAX_METHOD_HDMI_CABLE, &out_data);
 	if (ACPI_SUCCESS(status)) {
 		if (out_data == 0)
 			return sysfs_emit(buf, "[unconnected] connected unknown\n");
@@ -562,8 +562,8 @@ static ssize_t show_hdmi_source(struct device *dev,
 		.arg = 0,
 	};
 	status =
-	    alienware_wmax_command(&in_args, WMAX_METHOD_HDMI_STATUS,
-				   &out_data);
+	    alienware_wmax_command(&in_args, sizeof(in_args),
+				   WMAX_METHOD_HDMI_STATUS, &out_data);
 
 	if (ACPI_SUCCESS(status)) {
 		if (out_data == 1)
@@ -589,7 +589,8 @@ static ssize_t toggle_hdmi_source(struct device *dev,
 		args.arg = 3;
 	pr_debug("alienware-wmi: setting hdmi to %d : %s", args.arg, buf);
 
-	status = alienware_wmax_command(&args, WMAX_METHOD_HDMI_SOURCE, NULL);
+	status = alienware_wmax_command(&args, sizeof(args),
+					WMAX_METHOD_HDMI_SOURCE, NULL);
 
 	if (ACPI_FAILURE(status))
 		pr_err("alienware-wmi: HDMI toggle failed: results: %u\n",
@@ -642,8 +643,8 @@ static ssize_t show_amplifier_status(struct device *dev,
 		.arg = 0,
 	};
 	status =
-	    alienware_wmax_command(&in_args, WMAX_METHOD_AMPLIFIER_CABLE,
-				   &out_data);
+	    alienware_wmax_command(&in_args, sizeof(in_args),
+				   WMAX_METHOD_AMPLIFIER_CABLE, &out_data);
 	if (ACPI_SUCCESS(status)) {
 		if (out_data == 0)
 			return sysfs_emit(buf, "[unconnected] connected unknown\n");
@@ -694,8 +695,8 @@ static ssize_t show_deepsleep_status(struct device *dev,
 	struct wmax_basic_args in_args = {
 		.arg = 0,
 	};
-	status = alienware_wmax_command(&in_args, WMAX_METHOD_DEEP_SLEEP_STATUS,
-					&out_data);
+	status = alienware_wmax_command(&in_args, sizeof(in_args),
+					WMAX_METHOD_DEEP_SLEEP_STATUS, &out_data);
 	if (ACPI_SUCCESS(status)) {
 		if (out_data == 0)
 			return sysfs_emit(buf, "[disabled] s5 s5_s4\n");
@@ -723,8 +724,8 @@ static ssize_t toggle_deepsleep(struct device *dev,
 		args.arg = 2;
 	pr_debug("alienware-wmi: setting deep sleep to %d : %s", args.arg, buf);
 
-	status = alienware_wmax_command(&args, WMAX_METHOD_DEEP_SLEEP_CONTROL,
-					NULL);
+	status = alienware_wmax_command(&args, sizeof(args),
+					WMAX_METHOD_DEEP_SLEEP_CONTROL, NULL);
 
 	if (ACPI_FAILURE(status))
 		pr_err("alienware-wmi: deep sleep control failed: results: %u\n",
-- 
2.47.0


^ permalink raw reply related	[flat|nested] 15+ messages in thread

* [PATCH v6 3/5] alienware-wmi: added platform profile support
  2024-10-17  8:12 [PATCH v6 0/5] Dell AWCC platform_profile support Kurt Borja
  2024-10-17  8:13 ` [PATCH v6 1/5] alienware-wmi: fixed indentation and clean up Kurt Borja
  2024-10-17  8:14 ` [PATCH v6 2/5] alienware-wmi: alienware_wmax_command() is now input size agnostic Kurt Borja
@ 2024-10-17  8:15 ` Kurt Borja
  2024-10-20 20:12   ` Armin Wolf
  2024-10-17  8:16 ` [PATCH v6 4/5] alienware-wmi: added autodetect_thermal_profile for devices with quirk_unknown Kurt Borja
  2024-10-17  8:17 ` [PATCH v6 5/5] alienware-wmi: WMAX interface documentation Kurt Borja
  4 siblings, 1 reply; 15+ messages in thread
From: Kurt Borja @ 2024-10-17  8:15 UTC (permalink / raw)
  To: kuurtb; +Cc: W_Armin, hdegoede, ilpo.jarvinen, linux-kernel,
	platform-driver-x86

Implements platform profile support for Dell laptops with new WMAX
thermal interface, present on some Alienware X-Series, Alienware
M-Series and Dell's G-Series laptops. 

This implementation supports three sets of thermal tables declared in
enum WMAX_THERMAL_TABLE and gmode, using quirks *thermal* and *gmode*
respectively. These sets are found in most Dell's devices that support
WMAX's thermal interface.

Signed-off-by: Kurt Borja <kuurtb@gmail.com>

---
v6:
 - Fixed alignment on some function definitions
 - Fixed braces on if statment
 - Removed quirk thermal_ustt
 - Now quirk thermal can take values defined in enum WMAX_THERMAL_TABLE.
 - Proper removal of thermal_profile
---
 drivers/platform/x86/dell/Kconfig         |   1 +
 drivers/platform/x86/dell/alienware-wmi.c | 251 ++++++++++++++++++++++
 2 files changed, 252 insertions(+)

diff --git a/drivers/platform/x86/dell/Kconfig b/drivers/platform/x86/dell/Kconfig
index 68a49788a..b06d634cd 100644
--- a/drivers/platform/x86/dell/Kconfig
+++ b/drivers/platform/x86/dell/Kconfig
@@ -21,6 +21,7 @@ config ALIENWARE_WMI
 	depends on LEDS_CLASS
 	depends on NEW_LEDS
 	depends on ACPI_WMI
+	select ACPI_PLATFORM_PROFILE
 	help
 	 This is a driver for controlling Alienware BIOS driven
 	 features.  It exposes an interface for controlling the AlienFX
diff --git a/drivers/platform/x86/dell/alienware-wmi.c b/drivers/platform/x86/dell/alienware-wmi.c
index b27f3b64c..37a898273 100644
--- a/drivers/platform/x86/dell/alienware-wmi.c
+++ b/drivers/platform/x86/dell/alienware-wmi.c
@@ -8,8 +8,11 @@
 #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
 
 #include <linux/acpi.h>
+#include <linux/bitfield.h>
+#include <linux/bits.h>
 #include <linux/module.h>
 #include <linux/platform_device.h>
+#include <linux/platform_profile.h>
 #include <linux/dmi.h>
 #include <linux/leds.h>
 
@@ -25,6 +28,12 @@
 #define WMAX_METHOD_AMPLIFIER_CABLE	0x6
 #define WMAX_METHOD_DEEP_SLEEP_CONTROL	0x0B
 #define WMAX_METHOD_DEEP_SLEEP_STATUS	0x0C
+#define WMAX_METHOD_THERMAL_INFORMATION	0x14
+#define WMAX_METHOD_THERMAL_CONTROL	0x15
+
+#define WMAX_ARG_GET_CURRENT_PROF	0x0B
+
+#define WMAX_FAILURE_CODE		0xFFFFFFFF
 
 MODULE_AUTHOR("Mario Limonciello <mario.limonciello@outlook.com>");
 MODULE_DESCRIPTION("Alienware special feature control");
@@ -49,11 +58,33 @@ enum WMAX_CONTROL_STATES {
 	WMAX_SUSPEND = 3,
 };
 
+enum WMAX_THERMAL_TABLE {
+	WMAX_THERMAL_TABLE_SIMPLE	= 1,
+	WMAX_THERMAL_TABLE_USTT		= 2,
+	WMAX_THERMAL_TABLE_USTT_COOL	= 3,
+};
+
+enum WMAX_THERMAL_PROFILE {
+	WMAX_THERMAL_QUIET			= 0x96,
+	WMAX_THERMAL_BALANCED			= 0x97,
+	WMAX_THERMAL_BALANCED_PERFORMANCE	= 0x98,
+	WMAX_THERMAL_PERFORMANCE		= 0x99,
+	WMAX_THERMAL_USTT_LOW_POWER		= 0xA5,
+	WMAX_THERMAL_USTT_COOL			= 0xA2,
+	WMAX_THERMAL_USTT_QUIET			= 0xA3,
+	WMAX_THERMAL_USTT_BALANCED		= 0xA0,
+	WMAX_THERMAL_USTT_BALANCED_PERFORMANCE	= 0xA1,
+	WMAX_THERMAL_USTT_PERFORMANCE		= 0xA4,
+	WMAX_THERMAL_GMODE			= 0xAB,
+};
+
 struct quirk_entry {
 	u8 num_zones;
 	u8 hdmi_mux;
 	u8 amplifier;
 	u8 deepslp;
+	u8 thermal;
+	u8 gmode;
 };
 
 static struct quirk_entry *quirks;
@@ -64,6 +95,8 @@ static struct quirk_entry quirk_inspiron5675 = {
 	.hdmi_mux = 0,
 	.amplifier = 0,
 	.deepslp = 0,
+	.thermal = 0,
+	.gmode = 0,
 };
 
 static struct quirk_entry quirk_unknown = {
@@ -71,6 +104,8 @@ static struct quirk_entry quirk_unknown = {
 	.hdmi_mux = 0,
 	.amplifier = 0,
 	.deepslp = 0,
+	.thermal = 0,
+	.gmode = 0,
 };
 
 static struct quirk_entry quirk_x51_r1_r2 = {
@@ -78,6 +113,8 @@ static struct quirk_entry quirk_x51_r1_r2 = {
 	.hdmi_mux = 0,
 	.amplifier = 0,
 	.deepslp = 0,
+	.thermal = 0,
+	.gmode = 0,
 };
 
 static struct quirk_entry quirk_x51_r3 = {
@@ -85,6 +122,8 @@ static struct quirk_entry quirk_x51_r3 = {
 	.hdmi_mux = 0,
 	.amplifier = 1,
 	.deepslp = 0,
+	.thermal = 0,
+	.gmode = 0,
 };
 
 static struct quirk_entry quirk_asm100 = {
@@ -92,6 +131,8 @@ static struct quirk_entry quirk_asm100 = {
 	.hdmi_mux = 1,
 	.amplifier = 0,
 	.deepslp = 0,
+	.thermal = 0,
+	.gmode = 0,
 };
 
 static struct quirk_entry quirk_asm200 = {
@@ -99,6 +140,8 @@ static struct quirk_entry quirk_asm200 = {
 	.hdmi_mux = 1,
 	.amplifier = 0,
 	.deepslp = 1,
+	.thermal = 0,
+	.gmode = 0,
 };
 
 static struct quirk_entry quirk_asm201 = {
@@ -106,6 +149,17 @@ static struct quirk_entry quirk_asm201 = {
 	.hdmi_mux = 1,
 	.amplifier = 1,
 	.deepslp = 1,
+	.thermal = 0,
+	.gmode = 0,
+};
+
+static struct quirk_entry quirk_x15_r1 = {
+	.num_zones = 2,
+	.hdmi_mux = 0,
+	.amplifier = 0,
+	.deepslp = 0,
+	.thermal = WMAX_THERMAL_TABLE_USTT,
+	.gmode = 0,
 };
 
 static int __init dmi_matched(const struct dmi_system_id *dmi)
@@ -169,6 +223,15 @@ static const struct dmi_system_id alienware_quirks[] __initconst = {
 		},
 		.driver_data = &quirk_asm201,
 	},
+	{
+		.callback = dmi_matched,
+		.ident = "Alienware x15 R1",
+		.matches = {
+			DMI_MATCH(DMI_SYS_VENDOR, "Alienware"),
+			DMI_MATCH(DMI_PRODUCT_NAME, "Alienware x15 R1")
+		},
+		.driver_data = &quirk_x15_r1,
+	},
 	{
 		.callback = dmi_matched,
 		.ident = "Dell Inc. Inspiron 5675",
@@ -218,6 +281,7 @@ static struct platform_device *platform_device;
 static struct device_attribute *zone_dev_attrs;
 static struct attribute **zone_attrs;
 static struct platform_zone *zone_data;
+static struct platform_profile_handler pp_handler;
 
 static struct platform_driver platform_driver = {
 	.driver = {
@@ -761,6 +825,184 @@ static int create_deepsleep(struct platform_device *dev)
 	return ret;
 }
 
+/*
+ * Thermal Profile control
+ *  - Provides thermal profile control through the Platform Profile API
+ */
+#define WMAX_ARGUMENT_MASK	GENMASK(15, 8)
+#define WMAX_PROFILE_ACTIVATE	0x01
+
+static u32 profile_to_wmax_arg(enum WMAX_THERMAL_PROFILE prof)
+{
+	return FIELD_PREP(WMAX_ARGUMENT_MASK, prof) | WMAX_PROFILE_ACTIVATE;
+}
+
+static int thermal_profile_get(struct platform_profile_handler *pprof,
+			       enum platform_profile_option *profile)
+{
+	acpi_status status;
+	u32 in_args = WMAX_ARG_GET_CURRENT_PROF;
+	u32 out_data;
+
+	status = alienware_wmax_command(&in_args, sizeof(in_args),
+					WMAX_METHOD_THERMAL_INFORMATION, &out_data);
+
+	if (ACPI_FAILURE(status))
+		return -EIO;
+
+	if (out_data == WMAX_FAILURE_CODE)
+		return -EBADRQC;
+
+	switch (out_data) {
+	case WMAX_THERMAL_USTT_LOW_POWER:
+		*profile = PLATFORM_PROFILE_LOW_POWER;
+		break;
+	case WMAX_THERMAL_USTT_COOL:
+		*profile = PLATFORM_PROFILE_COOL;
+		break;
+	case WMAX_THERMAL_QUIET:
+	case WMAX_THERMAL_USTT_QUIET:
+		*profile = PLATFORM_PROFILE_QUIET;
+		break;
+	case WMAX_THERMAL_BALANCED:
+	case WMAX_THERMAL_USTT_BALANCED:
+		*profile = PLATFORM_PROFILE_BALANCED;
+		break;
+	case WMAX_THERMAL_BALANCED_PERFORMANCE:
+	case WMAX_THERMAL_USTT_BALANCED_PERFORMANCE:
+		*profile = PLATFORM_PROFILE_BALANCED_PERFORMANCE;
+		break;
+	case WMAX_THERMAL_GMODE:
+	case WMAX_THERMAL_PERFORMANCE:
+	case WMAX_THERMAL_USTT_PERFORMANCE:
+		*profile = PLATFORM_PROFILE_PERFORMANCE;
+		break;
+	default:
+		return -ENODATA;
+	}
+
+	return 0;
+}
+
+static int thermal_profile_set(struct platform_profile_handler *pprof,
+			       enum platform_profile_option profile)
+{
+	acpi_status status;
+	u32 in_args;
+	u32 out_data;
+
+	switch (profile) {
+	case PLATFORM_PROFILE_QUIET:
+		in_args = profile_to_wmax_arg(WMAX_THERMAL_QUIET);
+		break;
+	case PLATFORM_PROFILE_BALANCED:
+		in_args = profile_to_wmax_arg(WMAX_THERMAL_BALANCED);
+		break;
+	case PLATFORM_PROFILE_BALANCED_PERFORMANCE:
+		in_args = profile_to_wmax_arg(WMAX_THERMAL_BALANCED_PERFORMANCE);
+		break;
+	case PLATFORM_PROFILE_PERFORMANCE:
+		if (quirks->gmode > 0)
+			in_args = profile_to_wmax_arg(WMAX_THERMAL_GMODE);
+		else
+			in_args = profile_to_wmax_arg(WMAX_THERMAL_PERFORMANCE);
+		break;
+	default:
+		return -EOPNOTSUPP;
+	}
+
+	status = alienware_wmax_command(&in_args, sizeof(in_args),
+					WMAX_METHOD_THERMAL_CONTROL, &out_data);
+
+	if (ACPI_FAILURE(status))
+		return -EIO;
+
+	if (out_data == WMAX_FAILURE_CODE)
+		return -EBADRQC;
+
+	return 0;
+}
+
+static int thermal_profile_set_ustt(struct platform_profile_handler *pprof,
+				    enum platform_profile_option profile)
+{
+	acpi_status status;
+	u32 in_args;
+	u32 out_data;
+
+	switch (profile) {
+	case PLATFORM_PROFILE_LOW_POWER:
+		in_args = profile_to_wmax_arg(WMAX_THERMAL_USTT_LOW_POWER);
+		break;
+	case PLATFORM_PROFILE_COOL:
+		in_args = profile_to_wmax_arg(WMAX_THERMAL_USTT_COOL);
+		break;
+	case PLATFORM_PROFILE_QUIET:
+		in_args = profile_to_wmax_arg(WMAX_THERMAL_USTT_QUIET);
+		break;
+	case PLATFORM_PROFILE_BALANCED:
+		in_args = profile_to_wmax_arg(WMAX_THERMAL_USTT_BALANCED);
+		break;
+	case PLATFORM_PROFILE_BALANCED_PERFORMANCE:
+		in_args = profile_to_wmax_arg(WMAX_THERMAL_USTT_BALANCED_PERFORMANCE);
+		break;
+	case PLATFORM_PROFILE_PERFORMANCE:
+		if (quirks->gmode > 0)
+			in_args = profile_to_wmax_arg(WMAX_THERMAL_GMODE);
+		else
+			in_args = profile_to_wmax_arg(WMAX_THERMAL_USTT_PERFORMANCE);
+		break;
+	default:
+		return -EOPNOTSUPP;
+	}
+
+	status = alienware_wmax_command(&in_args, sizeof(in_args),
+					WMAX_METHOD_THERMAL_CONTROL, &out_data);
+
+	if (ACPI_FAILURE(status))
+		return -EIO;
+
+	if (out_data == WMAX_FAILURE_CODE)
+		return -EBADRQC;
+
+	return 0;
+}
+
+static int create_thermal_profile(void)
+{
+	pp_handler.profile_get = thermal_profile_get;
+
+	switch (quirks->thermal) {
+	case WMAX_THERMAL_TABLE_SIMPLE:
+		pp_handler.profile_set = thermal_profile_set;
+		break;
+	case WMAX_THERMAL_TABLE_USTT:
+		pp_handler.profile_set = thermal_profile_set_ustt;
+		set_bit(PLATFORM_PROFILE_LOW_POWER, pp_handler.choices);
+		set_bit(PLATFORM_PROFILE_QUIET, pp_handler.choices);
+		set_bit(PLATFORM_PROFILE_BALANCED_PERFORMANCE, pp_handler.choices);
+		break;
+	case WMAX_THERMAL_TABLE_USTT_COOL:
+		pp_handler.profile_set = thermal_profile_set_ustt;
+		set_bit(PLATFORM_PROFILE_LOW_POWER, pp_handler.choices);
+		set_bit(PLATFORM_PROFILE_QUIET, pp_handler.choices);
+		set_bit(PLATFORM_PROFILE_COOL, pp_handler.choices);
+		set_bit(PLATFORM_PROFILE_BALANCED_PERFORMANCE, pp_handler.choices);
+		break;
+	}
+
+	set_bit(PLATFORM_PROFILE_BALANCED, pp_handler.choices);
+	set_bit(PLATFORM_PROFILE_PERFORMANCE, pp_handler.choices);
+
+	return platform_profile_register(&pp_handler);
+}
+
+static void remove_thermal_profile(void)
+{
+	if (quirks->thermal > 0)
+		platform_profile_remove();
+}
+
 static int __init alienware_wmi_init(void)
 {
 	int ret;
@@ -808,6 +1050,12 @@ static int __init alienware_wmi_init(void)
 			goto fail_prep_deepsleep;
 	}
 
+	if (quirks->thermal > 0) {
+		ret = create_thermal_profile();
+		if (ret)
+			goto fail_prep_thermal_profile;
+	}
+
 	ret = alienware_zone_init(platform_device);
 	if (ret)
 		goto fail_prep_zones;
@@ -816,6 +1064,8 @@ static int __init alienware_wmi_init(void)
 
 fail_prep_zones:
 	alienware_zone_exit(platform_device);
+	remove_thermal_profile();
+fail_prep_thermal_profile:
 fail_prep_deepsleep:
 fail_prep_amplifier:
 fail_prep_hdmi:
@@ -835,6 +1085,7 @@ static void __exit alienware_wmi_exit(void)
 	if (platform_device) {
 		alienware_zone_exit(platform_device);
 		remove_hdmi(platform_device);
+		remove_thermal_profile();
 		platform_device_unregister(platform_device);
 		platform_driver_unregister(&platform_driver);
 	}
-- 
2.47.0


^ permalink raw reply related	[flat|nested] 15+ messages in thread

* [PATCH v6 4/5] alienware-wmi: added autodetect_thermal_profile for devices with quirk_unknown
  2024-10-17  8:12 [PATCH v6 0/5] Dell AWCC platform_profile support Kurt Borja
                   ` (2 preceding siblings ...)
  2024-10-17  8:15 ` [PATCH v6 3/5] alienware-wmi: added platform profile support Kurt Borja
@ 2024-10-17  8:16 ` Kurt Borja
  2024-10-20 20:39   ` Armin Wolf
  2024-10-17  8:17 ` [PATCH v6 5/5] alienware-wmi: WMAX interface documentation Kurt Borja
  4 siblings, 1 reply; 15+ messages in thread
From: Kurt Borja @ 2024-10-17  8:16 UTC (permalink / raw)
  To: kuurtb; +Cc: W_Armin, hdegoede, ilpo.jarvinen, linux-kernel,
	platform-driver-x86

Added autodetect_thermal_profile for devices with quirk_unknown.
Autodetection is done through basic conditions most devices with WMAX's
thermal interface meet. Function exits returning 0 in case of errors.

Signed-off-by: Kurt Borja <kuurtb@gmail.com>

---
I apologize for the late inclusion. This feature can extend support to
many devices without having to list them in alienware_quirks.

The conditions for selecting the automatic thermal profile are based on
observations on a lot of *issues* in AWCC open source alternatives. 

I observed only Dell's G-Series laptops have WMAX_THERMAL_BALANCED
avaliable and when it's present none of the other profiles are
avaliable, except for GMODE. When a model has USTT profiles avaliable
usually they have all USTT profiles avaliable, except for cool on mostly
Alienware devices.

I made another implementation of this function, brute-forcing operation
0x03 of Thermal_Information, which is the operation that varies the most
across models. I found the implementation too cumbersome to include in
this series, but it could potentially extend support of this driver to
all posible devices with this interface automatically.

Another possibility is just including every device I observed into
alienware_quirks, which I can do but I want to know your opinion first.
---
 drivers/platform/x86/dell/alienware-wmi.c | 42 +++++++++++++++++++++++
 1 file changed, 42 insertions(+)

diff --git a/drivers/platform/x86/dell/alienware-wmi.c b/drivers/platform/x86/dell/alienware-wmi.c
index 37a898273..a11ff4851 100644
--- a/drivers/platform/x86/dell/alienware-wmi.c
+++ b/drivers/platform/x86/dell/alienware-wmi.c
@@ -30,8 +30,11 @@
 #define WMAX_METHOD_DEEP_SLEEP_STATUS	0x0C
 #define WMAX_METHOD_THERMAL_INFORMATION	0x14
 #define WMAX_METHOD_THERMAL_CONTROL	0x15
+#define WMAX_METHOD_GMODE_STATUS	0x25
 
+#define WMAX_ARG_GET_DEFAULT_PROF	0x0A
 #define WMAX_ARG_GET_CURRENT_PROF	0x0B
+#define WMAX_ARG_GET_GMODE_STATUS	0x02
 
 #define WMAX_FAILURE_CODE		0xFFFFFFFF
 
@@ -968,6 +971,42 @@ static int thermal_profile_set_ustt(struct platform_profile_handler *pprof,
 	return 0;
 }
 
+static int autodetect_thermal_profile(void)
+{
+	acpi_status status;
+	u32 in_args;
+	u32 default_profile;
+	u32 gmode;
+
+	in_args = WMAX_ARG_GET_DEFAULT_PROF;
+	status = alienware_wmax_command(&in_args, sizeof(in_args),
+					WMAX_METHOD_THERMAL_INFORMATION, &default_profile);
+
+	if (ACPI_FAILURE(status))
+		return 0;
+
+	in_args = WMAX_ARG_GET_GMODE_STATUS;
+	status = alienware_wmax_command(&in_args, sizeof(in_args),
+					WMAX_METHOD_GMODE_STATUS, &gmode);
+
+	if (ACPI_FAILURE(status))
+		return 0;
+
+	if (default_profile == WMAX_THERMAL_BALANCED && gmode == 1) {
+		quirks->thermal = WMAX_THERMAL_TABLE_SIMPLE;
+		quirks->gmode = 1;
+		return 0;
+	}
+
+	if (default_profile == WMAX_THERMAL_USTT_BALANCED)
+		quirks->thermal = WMAX_THERMAL_TABLE_USTT;
+
+	if (gmode == 0 || gmode == 1)
+		quirks->gmode = 1;
+
+	return 0;
+}
+
 static int create_thermal_profile(void)
 {
 	pp_handler.profile_get = thermal_profile_get;
@@ -1050,6 +1089,9 @@ static int __init alienware_wmi_init(void)
 			goto fail_prep_deepsleep;
 	}
 
+	if (interface == WMAX && quirks == &quirk_unknown)
+		autodetect_thermal_profile();
+
 	if (quirks->thermal > 0) {
 		ret = create_thermal_profile();
 		if (ret)
-- 
2.47.0


^ permalink raw reply related	[flat|nested] 15+ messages in thread

* [PATCH v6 5/5] alienware-wmi: WMAX interface documentation
  2024-10-17  8:12 [PATCH v6 0/5] Dell AWCC platform_profile support Kurt Borja
                   ` (3 preceding siblings ...)
  2024-10-17  8:16 ` [PATCH v6 4/5] alienware-wmi: added autodetect_thermal_profile for devices with quirk_unknown Kurt Borja
@ 2024-10-17  8:17 ` Kurt Borja
  2024-10-20 22:09   ` Armin Wolf
  4 siblings, 1 reply; 15+ messages in thread
From: Kurt Borja @ 2024-10-17  8:17 UTC (permalink / raw)
  To: kuurtb; +Cc: W_Armin, hdegoede, ilpo.jarvinen, linux-kernel,
	platform-driver-x86

Added documentation for new WMAX interface, present on some Alienware
X-Series, Alienware M-Series and Dell's G-Series laptops.

Signed-off-by: Kurt Borja <kuurtb@gmail.com>

---
v6:
 - Fixed typos
 - Included new file in MAINTAINERS
---
 Documentation/wmi/devices/alienware-wmi.rst | 366 ++++++++++++++++++++
 MAINTAINERS                                 |   1 +
 2 files changed, 367 insertions(+)
 create mode 100644 Documentation/wmi/devices/alienware-wmi.rst

diff --git a/Documentation/wmi/devices/alienware-wmi.rst b/Documentation/wmi/devices/alienware-wmi.rst
new file mode 100644
index 000000000..f2fb845d7
--- /dev/null
+++ b/Documentation/wmi/devices/alienware-wmi.rst
@@ -0,0 +1,366 @@
+.. SPDX-License-Identifier: GPL-2.0-or-later
+
+==============================================
+Dell AWCC WMI interface driver (alienware-wmi)
+==============================================
+
+Introduction
+============
+
+The WMI device WMAX has been implemented for many Alienware and Dell's G-Series
+models. Throughout these models, two implementations have been identified. The
+first one, used by older systems, deals with HDMI, brightness, RGB, amplifier
+and deep sleep control. The second one used by newer systems deals primarily
+with thermal, overclocking, and GPIO control.
+
+It is suspected that the latter is used by Alienware Command Center (AWCC) to
+manage manufacturer predefined thermal profiles. The alienware-wmi driver
+exposes Thermal_Information and Thermal_Control methods through the Platform
+Profile API to mimic AWCC's behavior.
+
+This newer interface, named AWCCMethodFunction has been reverse engineered, as
+Dell has not provided any official documentation. We will try to describe to the
+best of our ability its discovered inner workings.
+
+.. note::
+   The following method description may vary between models.
+
+WMI interface description
+-------------------------
+
+The WMI interface description can be decoded from the embedded binary MOF (bmof)
+data using the `bmfdec <https://github.com/pali/bmfdec>`_ utility:
+
+::
+
+ [WMI, Dynamic, Provider("WmiProv"), Locale("MS\\0x409"), Description("WMI Function"), guid("{A70591CE-A997-11DA-B012-B622A1EF5492}")]
+ class AWCCWmiMethodFunction {
+   [key, read] string InstanceName;
+   [read] boolean Active;
+
+   [WmiMethodId(13), Implemented, read, write, Description("Return Overclocking Report.")] void Return_OverclockingReport([out] uint32 argr);
+   [WmiMethodId(14), Implemented, read, write, Description("Set OCUIBIOS Control.")] void Set_OCUIBIOSControl([in] uint32 arg2, [out] uint32 argr);
+   [WmiMethodId(15), Implemented, read, write, Description("Clear OC FailSafe Flag.")] void Clear_OCFailSafeFlag([out] uint32 argr);
+   [WmiMethodId(19), Implemented, read, write, Description("Get Fan Sensors.")] void GetFanSensors([in] uint32 arg2, [out] uint32 argr);
+   [WmiMethodId(20), Implemented, read, write, Description("Thermal Information.")] void Thermal_Information([in] uint32 arg2, [out] uint32 argr);
+   [WmiMethodId(21), Implemented, read, write, Description("Thermal Control.")] void Thermal_Control([in] uint32 arg2, [out] uint32 argr);
+   [WmiMethodId(23), Implemented, read, write, Description("MemoryOCControl.")] void MemoryOCControl([in] uint32 arg2, [out] uint32 argr);
+   [WmiMethodId(26), Implemented, read, write, Description("System Information.")] void SystemInformation([in] uint32 arg2, [out] uint32 argr);
+   [WmiMethodId(28), Implemented, read, write, Description("Power Information.")] void PowerInformation([in] uint32 arg2, [out] uint32 argr);
+   [WmiMethodId(32), Implemented, read, write, Description("FW Update GPIO toggle.")] void FWUpdateGPIOtoggle([in] uint32 arg2, [out] uint32 argr);
+   [WmiMethodId(33), Implemented, read, write, Description("Read Total of GPIOs.")] void ReadTotalofGPIOs([out] uint32 argr);
+   [WmiMethodId(34), Implemented, read, write, Description("Read GPIO pin Status.")] void ReadGPIOpPinStatus([in] uint32 arg2, [out] uint32 argr);
+   [WmiMethodId(35), Implemented, read, write, Description("Read Chassis Color.")] void ReadChassisColor([out] uint32 argr);
+   [WmiMethodId(36), Implemented, read, write, Description("Read Platform Properties.")] void ReadPlatformProperties([out] uint32 argr);
+   [WmiMethodId(128), Implemented, read, write, Description("Caldera SW installation.")] void CalderaSWInstallation([out] uint32 argr);
+   [WmiMethodId(129), Implemented, read, write, Description("Caldera SW is released.")] void CalderaSWReleased([out] uint32 argr);
+   [WmiMethodId(130), Implemented, read, write, Description("Caldera Connection Status.")] void CalderaConnectionStatus([in] uint32 arg2, [out] uint32 argr);
+   [WmiMethodId(131), Implemented, read, write, Description("Surprise Unplugged Flag Status.")] void SurpriseUnpluggedFlagStatus([out] uint32 argr);
+   [WmiMethodId(132), Implemented, read, write, Description("Clear Surprise Unplugged Flag.")] void ClearSurpriseUnpluggedFlag([out] uint32 argr);
+   [WmiMethodId(133), Implemented, read, write, Description("Cancel Undock Request.")] void CancelUndockRequest([out] uint32 argr);
+   [WmiMethodId(135), Implemented, read, write, Description("Devices in Caldera.")] void DevicesInCaldera([in] uint32 arg2, [out] uint32 argr);
+   [WmiMethodId(136), Implemented, read, write, Description("Notify BIOS for SW ready to disconnect Caldera.")] void NotifyBIOSForSWReadyToDisconnectCaldera([out] uint32 argr);
+   [WmiMethodId(160), Implemented, read, write, Description("Tobii SW installation.")] void TobiiSWinstallation([out] uint32 argr);
+   [WmiMethodId(161), Implemented, read, write, Description("Tobii SW Released.")] void TobiiSWReleased([out] uint32 argr);
+   [WmiMethodId(162), Implemented, read, write, Description("Tobii Camera Power Reset.")] void TobiiCameraPowerReset([out] uint32 argr);
+   [WmiMethodId(163), Implemented, read, write, Description("Tobii Camera Power On.")] void TobiiCameraPowerOn([out] uint32 argr);
+   [WmiMethodId(164), Implemented, read, write, Description("Tobii Camera Power Off.")] void TobiiCameraPowerOff([out] uint32 argr);
+ };
+
+Some of these methods get quite intricate so we will describe them using
+pseudo-code that vaguely resembles the original ASL code.
+
+Argument Structure
+------------------
+
+All input arguments have type **uint32** and their structure is very similar
+between methods. Usually, the first byte corresponds to a specific *operation*
+the method performs, and the subsequent bytes correspond to *arguments* passed
+to this *operation*. For example, if an operation has code 0x01 and requires an
+ID 0xA0, the argument you would pass to the method is 0xA001.
+
+
+Thermal Methods
+===============
+
+WMI method Thermal_Information([in] uint32 arg2, [out] uint32 argr)
+-------------------------------------------------------------------
+
+::
+
+ if BYTE_0(arg2) == 0x01:
+         argr = 1
+
+ if BYTE_0(arg2) == 0x02:
+         argr = UNKNOWN_CONSTANT
+
+ if BYTE_0(arg2) == 0x03:
+         if BYTE_1(arg2) == 0x00:
+                 argr = FAN_ID_0
+
+         if BYTE_1(arg2) == 0x01:
+                 argr = FAN_ID_1
+
+         if BYTE_1(arg2) == 0x02:
+                 argr = FAN_ID_2
+
+         if BYTE_1(arg2) == 0x03:
+                 argr = FAN_ID_3
+
+         if BYTE_1(arg2) == 0x04:
+                 argr = SENSOR_ID_CPU | 0x0100
+
+         if BYTE_1(arg2) == 0x05:
+                 argr = SENSOR_ID_GPU | 0x0100
+
+         if BYTE_1(arg2) == 0x06:
+                 argr = THERMAL_MODE_QUIET_ID
+
+         if BYTE_1(arg2) == 0x07:
+                 argr = THERMAL_MODE_BALANCED_ID
+
+         if BYTE_1(arg2) == 0x08:
+                 argr = THERMAL_MODE_BALANCED_PERFORMANCE_ID
+
+         if BYTE_1(arg2) == 0x09:
+                 argr = THERMAL_MODE_PERFORMANCE_ID
+
+         if BYTE_1(arg2) == 0x0A:
+                 argr = THERMAL_MODE_LOW_POWER_ID
+
+         if BYTE_1(arg2) == 0x0B:
+                 argr = THERMAL_MODE_GMODE_ID
+
+         else:
+                 argr = 0xFFFFFFFF
+
+ if BYTE_0(arg2) == 0x04:
+         if is_valid_sensor(BYTE_1(arg2)):
+                 argr = SENSOR_TEMP_C
+         else:
+                 argr = 0xFFFFFFFF
+
+ if BYTE_0(arg2) == 0x05:
+         if is_valid_fan(BYTE_1(arg2)):
+                 argr = FAN_RPM()
+
+ if BYTE_0(arg2) == 0x06:
+         skip
+
+ if BYTE_0(arg2) == 0x07:
+         argr = 0
+
+ If BYTE_0(arg2) == 0x08:
+         if is_valid_fan(BYTE_1(arg2)):
+                 argr = 0
+         else:
+                 argr = 0xFFFFFFFF
+
+ if BYTE_0(arg2) == 0x09:
+         if is_valid_fan(BYTE_1(arg2)):
+                 argr = FAN_UNKNOWN_STAT_0()
+
+         else:
+                 argr = 0xFFFFFFFF
+
+ if BYTE_0(arg2) == 0x0A:
+         argr = THERMAL_MODE_BALANCED_ID
+
+ if BYTE_0(arg2) == 0x0B:
+         argr = CURRENT_THERMAL_MODE()
+
+ if BYTE_0(arg2) == 0x0C:
+         if is_valid_fan(BYTE_1(arg2)):
+                 argr = FAN_UNKNOWN_STAT_1()
+         else:
+                 argr = 0xFFFFFFFF
+
+WMI method Thermal_Control([in] uint32 arg2, [out] uint32 argr)
+---------------------------------------------------------------
+
+::
+
+ if BYTE_0(arg2) == 0x01:
+         if is_valid_thermal_profile(BYTE_1(arg2)):
+                 SET_THERMAL_PROFILE(BYTE_1(arg2))
+                 argr = 0
+
+ if BYTE_0(arg2) == 0x02:
+         if is_valid_fan(BYTE_1(arg2)):
+                 SET_FAN_SPEED_MULTIPLIER(BYTE_2(arg2))
+                 argr = 0
+         else:
+                 argr = 0xFFFFFFFF
+
+.. note::
+   While you can manually change the fan speed multiplier with this method,
+   Dell's BIOS tends to overwrite this changes anyway.
+
+These are the known thermal profile codes:
+
+::
+
+ CUSTOM                         0x00
+
+ QUIET                          0x96
+ BALANCED                       0x97
+ BALANCED_PERFORMANCE           0x98
+ PERFORMANCE                    0x99
+
+ QUIET_USTT                     0xA3
+ BALANCED_USTT                  0xA0
+ BALANCED_PERFORMANCE_USTT      0xA1
+ PERFORMANCE_USTT               0xA4
+ LOW_POWER_USTT                 0xA5
+
+ GMODE                          0xAB
+
+Usually if a model doesn't support the first four profiles they will support
+the User Selectable Thermal Tables (USTT) profiles and vice-versa.
+
+GMODE replaces PERFORMANCE in G-Series laptops.
+
+WMI method GetFanSensors([in] uint32 arg2, [out] uint32 argr)
+-------------------------------------------------------------
+
+::
+
+ if BYTE_0(arg2) == 1:
+        if is_valid_fan(BYTE_1(arg2)):
+                argr = 1
+        else:
+                argr = 0
+
+ if BYTE_0(arg2) == 2:
+        if is_valid_fan(BYTE_1(arg2)):
+                if BYTE_2(arg2) == 0:
+                        argr == SENSOR_ID
+                else
+                        argr == 0xFFFFFFFF
+        else:
+                argr = 0
+
+Overclocking Methods
+====================
+
+.. warning::
+   These methods have not been tested and are only partially reverse
+   engineered.
+
+WMI method Return_OverclockingReport([out] uint32 argr)
+-------------------------------------------------------
+
+::
+
+ CSMI (0xE3, 0x99)
+ argr = 0
+
+CSMI is an unknown operation.
+
+WMI method Set_OCUIBIOSControl([in] uint32 arg2, [out] uint32 argr)
+-------------------------------------------------------------------
+
+::
+
+ CSMI (0xE3, 0x99)
+ argr = 0
+
+CSMI is an unknown operation.
+
+WMI method Clear_OCFailSafeFlag([out] uint32 argr)
+--------------------------------------------------
+
+::
+
+ CSMI (0xE3, 0x99)
+ argr = 0
+
+CSMI is an unknown operation.
+
+
+WMI method MemoryOCControl([in] uint32 arg2, [out] uint32 argr)
+---------------------------------------------------------------
+
+AWCC supports memory overclocking, but this method is very intricate and has
+not been deciphered yet.
+
+GPIO methods
+============
+
+These methods are probably related to some kind of firmware update system,
+through a GPIO device.
+
+.. warning::
+   These methods have not been tested and are only partially reverse
+   engineered.
+
+WMI method FWUpdateGPIOtoggle([in] uint32 arg2, [out] uint32 argr)
+------------------------------------------------------------------
+
+::
+
+ if BYTE_0(arg2) == 0:
+         if BYTE_1(arg2) == 1:
+                 SET_PIN_A_HIGH()
+         else:
+                 SET_PIN_A_LOW()
+
+ if BYTE_0(arg2) == 1:
+         if BYTE_1(arg2) == 1:
+                 SET_PIN_B_HIGH()
+
+         else:
+                 SET_PIN_B_LOW()
+
+ else:
+         argr = 1
+
+WMI method ReadTotalofGPIOs([out] uint32 argr)
+----------------------------------------------
+
+::
+
+ argr = 0x02
+
+WMI method ReadGPIOpPinStatus([in] uint32 arg2, [out] uint32 argr)
+------------------------------------------------------------------
+
+::
+
+ if BYTE_0(arg2) == 0:
+         argr = PIN_A_STATUS
+
+ if BYTE_0(arg2) == 1:
+         argr = PIN_B_STATUS
+
+Other information Methods
+=========================
+
+WMI method SystemInformation([in] uint32 arg2, [out] uint32 argr)
+-----------------------------------------------------------------
+
+Returns unknown information.
+
+WMI method PowerInformation([in] uint32 arg2, [out] uint32 argr)
+----------------------------------------------------------------
+
+Returns unknown information.
+
+WMI method ReadChassisColor([out] uint32 argr)
+----------------------------------------------
+
+::
+
+ argr = CHASSIS_COLOR_ID
+
+WMI method ReadPlatformProperties([out] uint32 argr)
+----------------------------------------------------
+
+Returns unknown information.
+
+Acknowledgements
+================
+
+Kudos to `AlexIII <https://github.com/AlexIII/tcc-g15>`_ for documenting
+and testing available thermal profile codes.
+
diff --git a/MAINTAINERS b/MAINTAINERS
index c27f31907..25f6de4c2 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -792,6 +792,7 @@ F:	drivers/perf/alibaba_uncore_drw_pmu.c
 ALIENWARE WMI DRIVER
 L:	Dell.Client.Kernel@dell.com
 S:	Maintained
+F:	Documentation/wmi/devices/alienware-wmi.rst
 F:	drivers/platform/x86/dell/alienware-wmi.c
 
 ALLEGRO DVT VIDEO IP CORE DRIVER
-- 
2.47.0


^ permalink raw reply related	[flat|nested] 15+ messages in thread

* Re: [PATCH v6 1/5] alienware-wmi: fixed indentation and clean up
  2024-10-17  8:13 ` [PATCH v6 1/5] alienware-wmi: fixed indentation and clean up Kurt Borja
@ 2024-10-20 20:04   ` Armin Wolf
  0 siblings, 0 replies; 15+ messages in thread
From: Armin Wolf @ 2024-10-20 20:04 UTC (permalink / raw)
  To: Kurt Borja; +Cc: hdegoede, ilpo.jarvinen, linux-kernel, platform-driver-x86

Am 17.10.24 um 10:13 schrieb Kurt Borja:

> Fixed inconsistent indentation and removed unnecessary (acpi_size) and
> (u32 *) casts.
>
Please remove any unnecessary empty lines inside the commit description.
Other than that:

Reviewed-by: Armin Wolf <W_Armin@gmx.de>

> Signed-off-by: Kurt Borja <kuurtb@gmail.com>
>
> ---
> v6:
>   - Unchanged
> ---
>   drivers/platform/x86/dell/alienware-wmi.c | 134 +++++++++++-----------
>   1 file changed, 67 insertions(+), 67 deletions(-)
>
> diff --git a/drivers/platform/x86/dell/alienware-wmi.c b/drivers/platform/x86/dell/alienware-wmi.c
> index f5ee62ce1..16a3fe9ac 100644
> --- a/drivers/platform/x86/dell/alienware-wmi.c
> +++ b/drivers/platform/x86/dell/alienware-wmi.c
> @@ -116,68 +116,68 @@ static int __init dmi_matched(const struct dmi_system_id *dmi)
>
>   static const struct dmi_system_id alienware_quirks[] __initconst = {
>   	{
> -	 .callback = dmi_matched,
> -	 .ident = "Alienware X51 R3",
> -	 .matches = {
> -		     DMI_MATCH(DMI_SYS_VENDOR, "Alienware"),
> -		     DMI_MATCH(DMI_PRODUCT_NAME, "Alienware X51 R3"),
> -		     },
> -	 .driver_data = &quirk_x51_r3,
> -	 },
> +		.callback = dmi_matched,
> +		.ident = "Alienware X51 R3",
> +		.matches = {
> +			DMI_MATCH(DMI_SYS_VENDOR, "Alienware"),
> +			DMI_MATCH(DMI_PRODUCT_NAME, "Alienware X51 R3"),
> +		},
> +		.driver_data = &quirk_x51_r3,
> +	},
>   	{
> -	 .callback = dmi_matched,
> -	 .ident = "Alienware X51 R2",
> -	 .matches = {
> -		     DMI_MATCH(DMI_SYS_VENDOR, "Alienware"),
> -		     DMI_MATCH(DMI_PRODUCT_NAME, "Alienware X51 R2"),
> -		     },
> -	 .driver_data = &quirk_x51_r1_r2,
> -	 },
> +		.callback = dmi_matched,
> +		.ident = "Alienware X51 R2",
> +		.matches = {
> +			DMI_MATCH(DMI_SYS_VENDOR, "Alienware"),
> +			DMI_MATCH(DMI_PRODUCT_NAME, "Alienware X51 R2"),
> +		},
> +		.driver_data = &quirk_x51_r1_r2,
> +	},
>   	{
> -	 .callback = dmi_matched,
> -	 .ident = "Alienware X51 R1",
> -	 .matches = {
> -		     DMI_MATCH(DMI_SYS_VENDOR, "Alienware"),
> -		     DMI_MATCH(DMI_PRODUCT_NAME, "Alienware X51"),
> -		     },
> -	 .driver_data = &quirk_x51_r1_r2,
> -	 },
> +		.callback = dmi_matched,
> +		.ident = "Alienware X51 R1",
> +		.matches = {
> +			DMI_MATCH(DMI_SYS_VENDOR, "Alienware"),
> +			DMI_MATCH(DMI_PRODUCT_NAME, "Alienware X51"),
> +		},
> +		.driver_data = &quirk_x51_r1_r2,
> +	},
>   	{
> -	 .callback = dmi_matched,
> -	 .ident = "Alienware ASM100",
> -	 .matches = {
> -		     DMI_MATCH(DMI_SYS_VENDOR, "Alienware"),
> -		     DMI_MATCH(DMI_PRODUCT_NAME, "ASM100"),
> -		     },
> -	 .driver_data = &quirk_asm100,
> -	 },
> +		.callback = dmi_matched,
> +		.ident = "Alienware ASM100",
> +		.matches = {
> +			DMI_MATCH(DMI_SYS_VENDOR, "Alienware"),
> +			DMI_MATCH(DMI_PRODUCT_NAME, "ASM100"),
> +		},
> +		.driver_data = &quirk_asm100,
> +	},
>   	{
> -	 .callback = dmi_matched,
> -	 .ident = "Alienware ASM200",
> -	 .matches = {
> -		     DMI_MATCH(DMI_SYS_VENDOR, "Alienware"),
> -		     DMI_MATCH(DMI_PRODUCT_NAME, "ASM200"),
> -		     },
> -	 .driver_data = &quirk_asm200,
> -	 },
> +		.callback = dmi_matched,
> +		.ident = "Alienware ASM200",
> +		.matches = {
> +			DMI_MATCH(DMI_SYS_VENDOR, "Alienware"),
> +			DMI_MATCH(DMI_PRODUCT_NAME, "ASM200"),
> +		},
> +		.driver_data = &quirk_asm200,
> +	},
>   	{
> -	 .callback = dmi_matched,
> -	 .ident = "Alienware ASM201",
> -	 .matches = {
> -		     DMI_MATCH(DMI_SYS_VENDOR, "Alienware"),
> -		     DMI_MATCH(DMI_PRODUCT_NAME, "ASM201"),
> -		     },
> -	 .driver_data = &quirk_asm201,
> -	 },
> -	 {
> -	 .callback = dmi_matched,
> -	 .ident = "Dell Inc. Inspiron 5675",
> -	 .matches = {
> -		     DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
> -		     DMI_MATCH(DMI_PRODUCT_NAME, "Inspiron 5675"),
> -		     },
> -	 .driver_data = &quirk_inspiron5675,
> -	 },
> +		.callback = dmi_matched,
> +		.ident = "Alienware ASM201",
> +		.matches = {
> +			DMI_MATCH(DMI_SYS_VENDOR, "Alienware"),
> +			DMI_MATCH(DMI_PRODUCT_NAME, "ASM201"),
> +		},
> +		.driver_data = &quirk_asm201,
> +	},
> +	{
> +		.callback = dmi_matched,
> +		.ident = "Dell Inc. Inspiron 5675",
> +		.matches = {
> +			DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
> +			DMI_MATCH(DMI_PRODUCT_NAME, "Inspiron 5675"),
> +		},
> +		.driver_data = &quirk_inspiron5675,
> +	},
>   	{}
>   };
>
> @@ -221,8 +221,8 @@ static struct platform_zone *zone_data;
>
>   static struct platform_driver platform_driver = {
>   	.driver = {
> -		   .name = "alienware-wmi",
> -		   }
> +		.name = "alienware-wmi",
> +	}
>   };
>
>   static struct attribute_group zone_attribute_group = {
> @@ -292,7 +292,7 @@ static int alienware_update_led(struct platform_zone *zone)
>   		guid = WMAX_CONTROL_GUID;
>   		method_id = WMAX_METHOD_ZONE_CONTROL;
>
> -		input.length = (acpi_size) sizeof(wmax_basic_args);
> +		input.length = sizeof(wmax_basic_args);
>   		input.pointer = &wmax_basic_args;
>   	} else {
>   		legacy_args.colors = zone->colors;
> @@ -306,7 +306,7 @@ static int alienware_update_led(struct platform_zone *zone)
>   			guid = LEGACY_CONTROL_GUID;
>   		method_id = zone->location + 1;
>
> -		input.length = (acpi_size) sizeof(legacy_args);
> +		input.length = sizeof(legacy_args);
>   		input.pointer = &legacy_args;
>   	}
>   	pr_debug("alienware-wmi: guid %s method %d\n", guid, method_id);
> @@ -358,7 +358,7 @@ static int wmax_brightness(int brightness)
>   		.led_mask = 0xFF,
>   		.percentage = brightness,
>   	};
> -	input.length = (acpi_size) sizeof(args);
> +	input.length = sizeof(args);
>   	input.pointer = &args;
>   	status = wmi_evaluate_method(WMAX_CONTROL_GUID, 0,
>   				     WMAX_METHOD_BRIGHTNESS, &input, NULL);
> @@ -508,7 +508,7 @@ static acpi_status alienware_wmax_command(struct wmax_basic_args *in_args,
>   	struct acpi_buffer input;
>   	struct acpi_buffer output;
>
> -	input.length = (acpi_size) sizeof(*in_args);
> +	input.length = sizeof(*in_args);
>   	input.pointer = in_args;
>   	if (out_data) {
>   		output.length = ACPI_ALLOCATE_BUFFER;
> @@ -542,7 +542,7 @@ static ssize_t show_hdmi_cable(struct device *dev,
>   	};
>   	status =
>   	    alienware_wmax_command(&in_args, WMAX_METHOD_HDMI_CABLE,
> -				   (u32 *) &out_data);
> +				   &out_data);
>   	if (ACPI_SUCCESS(status)) {
>   		if (out_data == 0)
>   			return sysfs_emit(buf, "[unconnected] connected unknown\n");
> @@ -563,7 +563,7 @@ static ssize_t show_hdmi_source(struct device *dev,
>   	};
>   	status =
>   	    alienware_wmax_command(&in_args, WMAX_METHOD_HDMI_STATUS,
> -				   (u32 *) &out_data);
> +				   &out_data);
>
>   	if (ACPI_SUCCESS(status)) {
>   		if (out_data == 1)
> @@ -643,7 +643,7 @@ static ssize_t show_amplifier_status(struct device *dev,
>   	};
>   	status =
>   	    alienware_wmax_command(&in_args, WMAX_METHOD_AMPLIFIER_CABLE,
> -				   (u32 *) &out_data);
> +				   &out_data);
>   	if (ACPI_SUCCESS(status)) {
>   		if (out_data == 0)
>   			return sysfs_emit(buf, "[unconnected] connected unknown\n");
> @@ -695,7 +695,7 @@ static ssize_t show_deepsleep_status(struct device *dev,
>   		.arg = 0,
>   	};
>   	status = alienware_wmax_command(&in_args, WMAX_METHOD_DEEP_SLEEP_STATUS,
> -					(u32 *) &out_data);
> +					&out_data);
>   	if (ACPI_SUCCESS(status)) {
>   		if (out_data == 0)
>   			return sysfs_emit(buf, "[disabled] s5 s5_s4\n");

^ permalink raw reply	[flat|nested] 15+ messages in thread

* Re: [PATCH v6 2/5] alienware-wmi: alienware_wmax_command() is now input size agnostic
  2024-10-17  8:14 ` [PATCH v6 2/5] alienware-wmi: alienware_wmax_command() is now input size agnostic Kurt Borja
@ 2024-10-20 20:05   ` Armin Wolf
  0 siblings, 0 replies; 15+ messages in thread
From: Armin Wolf @ 2024-10-20 20:05 UTC (permalink / raw)
  To: Kurt Borja; +Cc: hdegoede, ilpo.jarvinen, linux-kernel, platform-driver-x86

Am 17.10.24 um 10:14 schrieb Kurt Borja:

> alienware_wmax_command() now takes void * and size_t instead of struct
> wmax_basic_args to extend support to new WMAX methods. Also int *out_data
> was changed to u32 *out_data, because new interface specifies u32 as output
> parameter and all previous callers would pass u32 * regardless.

Reviewed-by: Armin Wolf <W_Armin@gmx.de>

> Signed-off-by: Kurt Borja <kuurtb@gmail.com>
>
> ---
> v6:
>   - Unchanged
> ---
>   drivers/platform/x86/dell/alienware-wmi.c | 29 ++++++++++++-----------
>   1 file changed, 15 insertions(+), 14 deletions(-)
>
> diff --git a/drivers/platform/x86/dell/alienware-wmi.c b/drivers/platform/x86/dell/alienware-wmi.c
> index 16a3fe9ac..b27f3b64c 100644
> --- a/drivers/platform/x86/dell/alienware-wmi.c
> +++ b/drivers/platform/x86/dell/alienware-wmi.c
> @@ -500,15 +500,15 @@ static void alienware_zone_exit(struct platform_device *dev)
>   	kfree(zone_attrs);
>   }
>
> -static acpi_status alienware_wmax_command(struct wmax_basic_args *in_args,
> -					  u32 command, int *out_data)
> +static acpi_status alienware_wmax_command(void *in_args, size_t in_size,
> +					  u32 command, u32 *out_data)
>   {
>   	acpi_status status;
>   	union acpi_object *obj;
>   	struct acpi_buffer input;
>   	struct acpi_buffer output;
>
> -	input.length = sizeof(*in_args);
> +	input.length = in_size;
>   	input.pointer = in_args;
>   	if (out_data) {
>   		output.length = ACPI_ALLOCATE_BUFFER;
> @@ -541,8 +541,8 @@ static ssize_t show_hdmi_cable(struct device *dev,
>   		.arg = 0,
>   	};
>   	status =
> -	    alienware_wmax_command(&in_args, WMAX_METHOD_HDMI_CABLE,
> -				   &out_data);
> +	    alienware_wmax_command(&in_args, sizeof(in_args),
> +				   WMAX_METHOD_HDMI_CABLE, &out_data);
>   	if (ACPI_SUCCESS(status)) {
>   		if (out_data == 0)
>   			return sysfs_emit(buf, "[unconnected] connected unknown\n");
> @@ -562,8 +562,8 @@ static ssize_t show_hdmi_source(struct device *dev,
>   		.arg = 0,
>   	};
>   	status =
> -	    alienware_wmax_command(&in_args, WMAX_METHOD_HDMI_STATUS,
> -				   &out_data);
> +	    alienware_wmax_command(&in_args, sizeof(in_args),
> +				   WMAX_METHOD_HDMI_STATUS, &out_data);
>
>   	if (ACPI_SUCCESS(status)) {
>   		if (out_data == 1)
> @@ -589,7 +589,8 @@ static ssize_t toggle_hdmi_source(struct device *dev,
>   		args.arg = 3;
>   	pr_debug("alienware-wmi: setting hdmi to %d : %s", args.arg, buf);
>
> -	status = alienware_wmax_command(&args, WMAX_METHOD_HDMI_SOURCE, NULL);
> +	status = alienware_wmax_command(&args, sizeof(args),
> +					WMAX_METHOD_HDMI_SOURCE, NULL);
>
>   	if (ACPI_FAILURE(status))
>   		pr_err("alienware-wmi: HDMI toggle failed: results: %u\n",
> @@ -642,8 +643,8 @@ static ssize_t show_amplifier_status(struct device *dev,
>   		.arg = 0,
>   	};
>   	status =
> -	    alienware_wmax_command(&in_args, WMAX_METHOD_AMPLIFIER_CABLE,
> -				   &out_data);
> +	    alienware_wmax_command(&in_args, sizeof(in_args),
> +				   WMAX_METHOD_AMPLIFIER_CABLE, &out_data);
>   	if (ACPI_SUCCESS(status)) {
>   		if (out_data == 0)
>   			return sysfs_emit(buf, "[unconnected] connected unknown\n");
> @@ -694,8 +695,8 @@ static ssize_t show_deepsleep_status(struct device *dev,
>   	struct wmax_basic_args in_args = {
>   		.arg = 0,
>   	};
> -	status = alienware_wmax_command(&in_args, WMAX_METHOD_DEEP_SLEEP_STATUS,
> -					&out_data);
> +	status = alienware_wmax_command(&in_args, sizeof(in_args),
> +					WMAX_METHOD_DEEP_SLEEP_STATUS, &out_data);
>   	if (ACPI_SUCCESS(status)) {
>   		if (out_data == 0)
>   			return sysfs_emit(buf, "[disabled] s5 s5_s4\n");
> @@ -723,8 +724,8 @@ static ssize_t toggle_deepsleep(struct device *dev,
>   		args.arg = 2;
>   	pr_debug("alienware-wmi: setting deep sleep to %d : %s", args.arg, buf);
>
> -	status = alienware_wmax_command(&args, WMAX_METHOD_DEEP_SLEEP_CONTROL,
> -					NULL);
> +	status = alienware_wmax_command(&args, sizeof(args),
> +					WMAX_METHOD_DEEP_SLEEP_CONTROL, NULL);
>
>   	if (ACPI_FAILURE(status))
>   		pr_err("alienware-wmi: deep sleep control failed: results: %u\n",

^ permalink raw reply	[flat|nested] 15+ messages in thread

* Re: [PATCH v6 3/5] alienware-wmi: added platform profile support
  2024-10-17  8:15 ` [PATCH v6 3/5] alienware-wmi: added platform profile support Kurt Borja
@ 2024-10-20 20:12   ` Armin Wolf
  2024-10-22  7:35     ` Kurt Borja
  0 siblings, 1 reply; 15+ messages in thread
From: Armin Wolf @ 2024-10-20 20:12 UTC (permalink / raw)
  To: Kurt Borja; +Cc: hdegoede, ilpo.jarvinen, linux-kernel, platform-driver-x86

Am 17.10.24 um 10:15 schrieb Kurt Borja:

> Implements platform profile support for Dell laptops with new WMAX
> thermal interface, present on some Alienware X-Series, Alienware
> M-Series and Dell's G-Series laptops.
>
> This implementation supports three sets of thermal tables declared in
> enum WMAX_THERMAL_TABLE and gmode, using quirks *thermal* and *gmode*
> respectively. These sets are found in most Dell's devices that support
> WMAX's thermal interface.
>
> Signed-off-by: Kurt Borja <kuurtb@gmail.com>
>
> ---
> v6:
>   - Fixed alignment on some function definitions
>   - Fixed braces on if statment
>   - Removed quirk thermal_ustt
>   - Now quirk thermal can take values defined in enum WMAX_THERMAL_TABLE.
>   - Proper removal of thermal_profile
> ---
>   drivers/platform/x86/dell/Kconfig         |   1 +
>   drivers/platform/x86/dell/alienware-wmi.c | 251 ++++++++++++++++++++++
>   2 files changed, 252 insertions(+)
>
> diff --git a/drivers/platform/x86/dell/Kconfig b/drivers/platform/x86/dell/Kconfig
> index 68a49788a..b06d634cd 100644
> --- a/drivers/platform/x86/dell/Kconfig
> +++ b/drivers/platform/x86/dell/Kconfig
> @@ -21,6 +21,7 @@ config ALIENWARE_WMI
>   	depends on LEDS_CLASS
>   	depends on NEW_LEDS
>   	depends on ACPI_WMI
> +	select ACPI_PLATFORM_PROFILE
>   	help
>   	 This is a driver for controlling Alienware BIOS driven
>   	 features.  It exposes an interface for controlling the AlienFX
> diff --git a/drivers/platform/x86/dell/alienware-wmi.c b/drivers/platform/x86/dell/alienware-wmi.c
> index b27f3b64c..37a898273 100644
> --- a/drivers/platform/x86/dell/alienware-wmi.c
> +++ b/drivers/platform/x86/dell/alienware-wmi.c
> @@ -8,8 +8,11 @@
>   #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
>
>   #include <linux/acpi.h>
> +#include <linux/bitfield.h>
> +#include <linux/bits.h>
>   #include <linux/module.h>
>   #include <linux/platform_device.h>
> +#include <linux/platform_profile.h>
>   #include <linux/dmi.h>
>   #include <linux/leds.h>
>
> @@ -25,6 +28,12 @@
>   #define WMAX_METHOD_AMPLIFIER_CABLE	0x6
>   #define WMAX_METHOD_DEEP_SLEEP_CONTROL	0x0B
>   #define WMAX_METHOD_DEEP_SLEEP_STATUS	0x0C
> +#define WMAX_METHOD_THERMAL_INFORMATION	0x14
> +#define WMAX_METHOD_THERMAL_CONTROL	0x15
> +
> +#define WMAX_ARG_GET_CURRENT_PROF	0x0B
> +
> +#define WMAX_FAILURE_CODE		0xFFFFFFFF
>
>   MODULE_AUTHOR("Mario Limonciello <mario.limonciello@outlook.com>");
>   MODULE_DESCRIPTION("Alienware special feature control");
> @@ -49,11 +58,33 @@ enum WMAX_CONTROL_STATES {
>   	WMAX_SUSPEND = 3,
>   };
>
> +enum WMAX_THERMAL_TABLE {
> +	WMAX_THERMAL_TABLE_SIMPLE	= 1,
> +	WMAX_THERMAL_TABLE_USTT		= 2,
> +	WMAX_THERMAL_TABLE_USTT_COOL	= 3,
> +};
> +
> +enum WMAX_THERMAL_PROFILE {
> +	WMAX_THERMAL_QUIET			= 0x96,
> +	WMAX_THERMAL_BALANCED			= 0x97,
> +	WMAX_THERMAL_BALANCED_PERFORMANCE	= 0x98,
> +	WMAX_THERMAL_PERFORMANCE		= 0x99,
> +	WMAX_THERMAL_USTT_LOW_POWER		= 0xA5,
> +	WMAX_THERMAL_USTT_COOL			= 0xA2,
> +	WMAX_THERMAL_USTT_QUIET			= 0xA3,
> +	WMAX_THERMAL_USTT_BALANCED		= 0xA0,
> +	WMAX_THERMAL_USTT_BALANCED_PERFORMANCE	= 0xA1,
> +	WMAX_THERMAL_USTT_PERFORMANCE		= 0xA4,
> +	WMAX_THERMAL_GMODE			= 0xAB,
> +};
> +
>   struct quirk_entry {
>   	u8 num_zones;
>   	u8 hdmi_mux;
>   	u8 amplifier;
>   	u8 deepslp;
> +	u8 thermal;
> +	u8 gmode;
>   };
>
>   static struct quirk_entry *quirks;
> @@ -64,6 +95,8 @@ static struct quirk_entry quirk_inspiron5675 = {
>   	.hdmi_mux = 0,
>   	.amplifier = 0,
>   	.deepslp = 0,
> +	.thermal = 0,
> +	.gmode = 0,
>   };
>
>   static struct quirk_entry quirk_unknown = {
> @@ -71,6 +104,8 @@ static struct quirk_entry quirk_unknown = {
>   	.hdmi_mux = 0,
>   	.amplifier = 0,
>   	.deepslp = 0,
> +	.thermal = 0,
> +	.gmode = 0,
>   };
>
>   static struct quirk_entry quirk_x51_r1_r2 = {
> @@ -78,6 +113,8 @@ static struct quirk_entry quirk_x51_r1_r2 = {
>   	.hdmi_mux = 0,
>   	.amplifier = 0,
>   	.deepslp = 0,
> +	.thermal = 0,
> +	.gmode = 0,
>   };
>
>   static struct quirk_entry quirk_x51_r3 = {
> @@ -85,6 +122,8 @@ static struct quirk_entry quirk_x51_r3 = {
>   	.hdmi_mux = 0,
>   	.amplifier = 1,
>   	.deepslp = 0,
> +	.thermal = 0,
> +	.gmode = 0,
>   };
>
>   static struct quirk_entry quirk_asm100 = {
> @@ -92,6 +131,8 @@ static struct quirk_entry quirk_asm100 = {
>   	.hdmi_mux = 1,
>   	.amplifier = 0,
>   	.deepslp = 0,
> +	.thermal = 0,
> +	.gmode = 0,
>   };
>
>   static struct quirk_entry quirk_asm200 = {
> @@ -99,6 +140,8 @@ static struct quirk_entry quirk_asm200 = {
>   	.hdmi_mux = 1,
>   	.amplifier = 0,
>   	.deepslp = 1,
> +	.thermal = 0,
> +	.gmode = 0,
>   };
>
>   static struct quirk_entry quirk_asm201 = {
> @@ -106,6 +149,17 @@ static struct quirk_entry quirk_asm201 = {
>   	.hdmi_mux = 1,
>   	.amplifier = 1,
>   	.deepslp = 1,
> +	.thermal = 0,
> +	.gmode = 0,
> +};
> +
> +static struct quirk_entry quirk_x15_r1 = {
> +	.num_zones = 2,
> +	.hdmi_mux = 0,
> +	.amplifier = 0,
> +	.deepslp = 0,
> +	.thermal = WMAX_THERMAL_TABLE_USTT,
> +	.gmode = 0,
>   };
>
>   static int __init dmi_matched(const struct dmi_system_id *dmi)
> @@ -169,6 +223,15 @@ static const struct dmi_system_id alienware_quirks[] __initconst = {
>   		},
>   		.driver_data = &quirk_asm201,
>   	},
> +	{
> +		.callback = dmi_matched,
> +		.ident = "Alienware x15 R1",
> +		.matches = {
> +			DMI_MATCH(DMI_SYS_VENDOR, "Alienware"),
> +			DMI_MATCH(DMI_PRODUCT_NAME, "Alienware x15 R1")
> +		},
> +		.driver_data = &quirk_x15_r1,
> +	},
>   	{
>   		.callback = dmi_matched,
>   		.ident = "Dell Inc. Inspiron 5675",
> @@ -218,6 +281,7 @@ static struct platform_device *platform_device;
>   static struct device_attribute *zone_dev_attrs;
>   static struct attribute **zone_attrs;
>   static struct platform_zone *zone_data;
> +static struct platform_profile_handler pp_handler;
>
>   static struct platform_driver platform_driver = {
>   	.driver = {
> @@ -761,6 +825,184 @@ static int create_deepsleep(struct platform_device *dev)
>   	return ret;
>   }
>
> +/*
> + * Thermal Profile control
> + *  - Provides thermal profile control through the Platform Profile API
> + */
> +#define WMAX_ARGUMENT_MASK	GENMASK(15, 8)
> +#define WMAX_PROFILE_ACTIVATE	0x01
> +
> +static u32 profile_to_wmax_arg(enum WMAX_THERMAL_PROFILE prof)
> +{
> +	return FIELD_PREP(WMAX_ARGUMENT_MASK, prof) | WMAX_PROFILE_ACTIVATE;
> +}
> +
> +static int thermal_profile_get(struct platform_profile_handler *pprof,
> +			       enum platform_profile_option *profile)
> +{
> +	acpi_status status;
> +	u32 in_args = WMAX_ARG_GET_CURRENT_PROF;
> +	u32 out_data;
> +
> +	status = alienware_wmax_command(&in_args, sizeof(in_args),
> +					WMAX_METHOD_THERMAL_INFORMATION, &out_data);
> +
> +	if (ACPI_FAILURE(status))
> +		return -EIO;
> +
> +	if (out_data == WMAX_FAILURE_CODE)
> +		return -EBADRQC;
> +
> +	switch (out_data) {
> +	case WMAX_THERMAL_USTT_LOW_POWER:
> +		*profile = PLATFORM_PROFILE_LOW_POWER;
> +		break;
> +	case WMAX_THERMAL_USTT_COOL:
> +		*profile = PLATFORM_PROFILE_COOL;
> +		break;
> +	case WMAX_THERMAL_QUIET:
> +	case WMAX_THERMAL_USTT_QUIET:
> +		*profile = PLATFORM_PROFILE_QUIET;
> +		break;
> +	case WMAX_THERMAL_BALANCED:
> +	case WMAX_THERMAL_USTT_BALANCED:
> +		*profile = PLATFORM_PROFILE_BALANCED;
> +		break;
> +	case WMAX_THERMAL_BALANCED_PERFORMANCE:
> +	case WMAX_THERMAL_USTT_BALANCED_PERFORMANCE:
> +		*profile = PLATFORM_PROFILE_BALANCED_PERFORMANCE;
> +		break;
> +	case WMAX_THERMAL_GMODE:
> +	case WMAX_THERMAL_PERFORMANCE:
> +	case WMAX_THERMAL_USTT_PERFORMANCE:
> +		*profile = PLATFORM_PROFILE_PERFORMANCE;
> +		break;
> +	default:
> +		return -ENODATA;
> +	}
> +
> +	return 0;
> +}
> +
> +static int thermal_profile_set(struct platform_profile_handler *pprof,
> +			       enum platform_profile_option profile)
> +{
> +	acpi_status status;
> +	u32 in_args;
> +	u32 out_data;
> +
> +	switch (profile) {
> +	case PLATFORM_PROFILE_QUIET:
> +		in_args = profile_to_wmax_arg(WMAX_THERMAL_QUIET);
> +		break;
> +	case PLATFORM_PROFILE_BALANCED:
> +		in_args = profile_to_wmax_arg(WMAX_THERMAL_BALANCED);
> +		break;
> +	case PLATFORM_PROFILE_BALANCED_PERFORMANCE:
> +		in_args = profile_to_wmax_arg(WMAX_THERMAL_BALANCED_PERFORMANCE);
> +		break;
> +	case PLATFORM_PROFILE_PERFORMANCE:
> +		if (quirks->gmode > 0)
> +			in_args = profile_to_wmax_arg(WMAX_THERMAL_GMODE);
> +		else
> +			in_args = profile_to_wmax_arg(WMAX_THERMAL_PERFORMANCE);
> +		break;
> +	default:
> +		return -EOPNOTSUPP;
> +	}
> +
> +	status = alienware_wmax_command(&in_args, sizeof(in_args),
> +					WMAX_METHOD_THERMAL_CONTROL, &out_data);
> +
> +	if (ACPI_FAILURE(status))
> +		return -EIO;
> +
> +	if (out_data == WMAX_FAILURE_CODE)
> +		return -EBADRQC;
> +
> +	return 0;
> +}
> +
> +static int thermal_profile_set_ustt(struct platform_profile_handler *pprof,
> +				    enum platform_profile_option profile)
> +{
> +	acpi_status status;
> +	u32 in_args;
> +	u32 out_data;
> +
> +	switch (profile) {
> +	case PLATFORM_PROFILE_LOW_POWER:
> +		in_args = profile_to_wmax_arg(WMAX_THERMAL_USTT_LOW_POWER);
> +		break;
> +	case PLATFORM_PROFILE_COOL:
> +		in_args = profile_to_wmax_arg(WMAX_THERMAL_USTT_COOL);
> +		break;
> +	case PLATFORM_PROFILE_QUIET:
> +		in_args = profile_to_wmax_arg(WMAX_THERMAL_USTT_QUIET);
> +		break;
> +	case PLATFORM_PROFILE_BALANCED:
> +		in_args = profile_to_wmax_arg(WMAX_THERMAL_USTT_BALANCED);
> +		break;
> +	case PLATFORM_PROFILE_BALANCED_PERFORMANCE:
> +		in_args = profile_to_wmax_arg(WMAX_THERMAL_USTT_BALANCED_PERFORMANCE);
> +		break;
> +	case PLATFORM_PROFILE_PERFORMANCE:
> +		if (quirks->gmode > 0)
> +			in_args = profile_to_wmax_arg(WMAX_THERMAL_GMODE);
> +		else
> +			in_args = profile_to_wmax_arg(WMAX_THERMAL_USTT_PERFORMANCE);
> +		break;
> +	default:
> +		return -EOPNOTSUPP;
> +	}
> +
> +	status = alienware_wmax_command(&in_args, sizeof(in_args),
> +					WMAX_METHOD_THERMAL_CONTROL, &out_data);
> +
> +	if (ACPI_FAILURE(status))
> +		return -EIO;
> +
> +	if (out_data == WMAX_FAILURE_CODE)
> +		return -EBADRQC;
> +
> +	return 0;
> +}
> +
> +static int create_thermal_profile(void)
> +{
> +	pp_handler.profile_get = thermal_profile_get;
> +
> +	switch (quirks->thermal) {
> +	case WMAX_THERMAL_TABLE_SIMPLE:
> +		pp_handler.profile_set = thermal_profile_set;
> +		break;
> +	case WMAX_THERMAL_TABLE_USTT:
> +		pp_handler.profile_set = thermal_profile_set_ustt;
> +		set_bit(PLATFORM_PROFILE_LOW_POWER, pp_handler.choices);
> +		set_bit(PLATFORM_PROFILE_QUIET, pp_handler.choices);
> +		set_bit(PLATFORM_PROFILE_BALANCED_PERFORMANCE, pp_handler.choices);
> +		break;
> +	case WMAX_THERMAL_TABLE_USTT_COOL:
> +		pp_handler.profile_set = thermal_profile_set_ustt;
> +		set_bit(PLATFORM_PROFILE_LOW_POWER, pp_handler.choices);
> +		set_bit(PLATFORM_PROFILE_QUIET, pp_handler.choices);
> +		set_bit(PLATFORM_PROFILE_COOL, pp_handler.choices);
> +		set_bit(PLATFORM_PROFILE_BALANCED_PERFORMANCE, pp_handler.choices);
> +		break;
> +	}

Please add a default statement here to return -EINVAL just in case.

Other than that:

Reviewed-by: Armin Wolf <W_Armin@gmx.de>

> +
> +	set_bit(PLATFORM_PROFILE_BALANCED, pp_handler.choices);
> +	set_bit(PLATFORM_PROFILE_PERFORMANCE, pp_handler.choices);
> +
> +	return platform_profile_register(&pp_handler);
> +}
> +
> +static void remove_thermal_profile(void)
> +{
> +	if (quirks->thermal > 0)
> +		platform_profile_remove();
> +}
> +
>   static int __init alienware_wmi_init(void)
>   {
>   	int ret;
> @@ -808,6 +1050,12 @@ static int __init alienware_wmi_init(void)
>   			goto fail_prep_deepsleep;
>   	}
>
> +	if (quirks->thermal > 0) {
> +		ret = create_thermal_profile();
> +		if (ret)
> +			goto fail_prep_thermal_profile;
> +	}
> +
>   	ret = alienware_zone_init(platform_device);
>   	if (ret)
>   		goto fail_prep_zones;
> @@ -816,6 +1064,8 @@ static int __init alienware_wmi_init(void)
>
>   fail_prep_zones:
>   	alienware_zone_exit(platform_device);
> +	remove_thermal_profile();
> +fail_prep_thermal_profile:
>   fail_prep_deepsleep:
>   fail_prep_amplifier:
>   fail_prep_hdmi:
> @@ -835,6 +1085,7 @@ static void __exit alienware_wmi_exit(void)
>   	if (platform_device) {
>   		alienware_zone_exit(platform_device);
>   		remove_hdmi(platform_device);
> +		remove_thermal_profile();
>   		platform_device_unregister(platform_device);
>   		platform_driver_unregister(&platform_driver);
>   	}

^ permalink raw reply	[flat|nested] 15+ messages in thread

* Re: [PATCH v6 4/5] alienware-wmi: added autodetect_thermal_profile for devices with quirk_unknown
  2024-10-17  8:16 ` [PATCH v6 4/5] alienware-wmi: added autodetect_thermal_profile for devices with quirk_unknown Kurt Borja
@ 2024-10-20 20:39   ` Armin Wolf
  2024-10-20 20:40     ` Armin Wolf
  2024-10-22  7:58     ` Kurt Borja
  0 siblings, 2 replies; 15+ messages in thread
From: Armin Wolf @ 2024-10-20 20:39 UTC (permalink / raw)
  To: Kurt Borja; +Cc: hdegoede, ilpo.jarvinen, linux-kernel, platform-driver-x86

Am 17.10.24 um 10:16 schrieb Kurt Borja:

> Added autodetect_thermal_profile for devices with quirk_unknown.
> Autodetection is done through basic conditions most devices with WMAX's
> thermal interface meet. Function exits returning 0 in case of errors.
>
> Signed-off-by: Kurt Borja <kuurtb@gmail.com>
>
> ---
> I apologize for the late inclusion. This feature can extend support to
> many devices without having to list them in alienware_quirks.
>
> The conditions for selecting the automatic thermal profile are based on
> observations on a lot of *issues* in AWCC open source alternatives.
>
> I observed only Dell's G-Series laptops have WMAX_THERMAL_BALANCED
> avaliable and when it's present none of the other profiles are
> avaliable, except for GMODE. When a model has USTT profiles avaliable
> usually they have all USTT profiles avaliable, except for cool on mostly
> Alienware devices.
>
> I made another implementation of this function, brute-forcing operation
> 0x03 of Thermal_Information, which is the operation that varies the most
> across models. I found the implementation too cumbersome to include in
> this series, but it could potentially extend support of this driver to
> all posible devices with this interface automatically.

I like this patch, automatic configuration is always a nice feature.

Please add support for operation 0x03, this way the driver can work automatically
without users having to submit patches adding quirks for their machines.

Maybe you can use an array for storing the supported thermal mode ids, like this:

enum thermal_modes = {
	THERMAL_MODE_QUIET,
	...
	THERMAL_MODE_LOW_POWER,
	THERMAL_MODE_MAX
};

const enumplatform_profile_option  thermal_mode_to_platform_profile[THERMAL_MODE_MAX] = {
	[THERMAL_MODE_QUIET] =PLATFORM_PROFILE_QUIET, ...
};

const enumthermal_modes platform_profile_to_thermal_mode[PLATFORM_PROFILE_LAST] = {
	[PLATFORM_PROFILE_LOW_POWER] = THERMAL_MODE_LOW_POWER, ...
};


u8 thermal_modes[THERMAL_MODE_MAX] = {};

for (int i = 0; i < THERMAL_MODE_MAX; i++) {
	thermal_modes[i] = call_operation_3(0x06 + i);
	// TODO: Error handling
	if (thermal_modes[i] == 0xFFFFFFFF)
		continue;

	set_bit(supported_profiles, thermal_mode_to_platform_profile[i]);
}

then you can use platform_profile_to_thermal_mode[] when setting the platform profile
and thermal_mode_to_platform_profile[] when getting the platform profile.
I will leave it up to you on how to handle the existence of GMode.

This of course is only a rough idea, you can change anything you want in the above pseudo-code.

Thanks,
Armin Wolf

>
> Another possibility is just including every device I observed into
> alienware_quirks, which I can do but I want to know your opinion first.
> ---
>   drivers/platform/x86/dell/alienware-wmi.c | 42 +++++++++++++++++++++++
>   1 file changed, 42 insertions(+)
>
> diff --git a/drivers/platform/x86/dell/alienware-wmi.c b/drivers/platform/x86/dell/alienware-wmi.c
> index 37a898273..a11ff4851 100644
> --- a/drivers/platform/x86/dell/alienware-wmi.c
> +++ b/drivers/platform/x86/dell/alienware-wmi.c
> @@ -30,8 +30,11 @@
>   #define WMAX_METHOD_DEEP_SLEEP_STATUS	0x0C
>   #define WMAX_METHOD_THERMAL_INFORMATION	0x14
>   #define WMAX_METHOD_THERMAL_CONTROL	0x15
> +#define WMAX_METHOD_GMODE_STATUS	0x25
>
> +#define WMAX_ARG_GET_DEFAULT_PROF	0x0A
>   #define WMAX_ARG_GET_CURRENT_PROF	0x0B
> +#define WMAX_ARG_GET_GMODE_STATUS	0x02
>
>   #define WMAX_FAILURE_CODE		0xFFFFFFFF
>
> @@ -968,6 +971,42 @@ static int thermal_profile_set_ustt(struct platform_profile_handler *pprof,
>   	return 0;
>   }
>
> +static int autodetect_thermal_profile(void)
> +{
> +	acpi_status status;
> +	u32 in_args;
> +	u32 default_profile;
> +	u32 gmode;
> +
> +	in_args = WMAX_ARG_GET_DEFAULT_PROF;
> +	status = alienware_wmax_command(&in_args, sizeof(in_args),
> +					WMAX_METHOD_THERMAL_INFORMATION, &default_profile);
> +
> +	if (ACPI_FAILURE(status))
> +		return 0;
> +
> +	in_args = WMAX_ARG_GET_GMODE_STATUS;
> +	status = alienware_wmax_command(&in_args, sizeof(in_args),
> +					WMAX_METHOD_GMODE_STATUS, &gmode);
> +
> +	if (ACPI_FAILURE(status))
> +		return 0;
> +
> +	if (default_profile == WMAX_THERMAL_BALANCED && gmode == 1) {
> +		quirks->thermal = WMAX_THERMAL_TABLE_SIMPLE;
> +		quirks->gmode = 1;
> +		return 0;
> +	}
> +
> +	if (default_profile == WMAX_THERMAL_USTT_BALANCED)
> +		quirks->thermal = WMAX_THERMAL_TABLE_USTT;
> +
> +	if (gmode == 0 || gmode == 1)
> +		quirks->gmode = 1;
> +
> +	return 0;
> +}
> +
>   static int create_thermal_profile(void)
>   {
>   	pp_handler.profile_get = thermal_profile_get;
> @@ -1050,6 +1089,9 @@ static int __init alienware_wmi_init(void)
>   			goto fail_prep_deepsleep;
>   	}
>
> +	if (interface == WMAX && quirks == &quirk_unknown)
> +		autodetect_thermal_profile();
> +
>   	if (quirks->thermal > 0) {
>   		ret = create_thermal_profile();
>   		if (ret)

^ permalink raw reply	[flat|nested] 15+ messages in thread

* Re: [PATCH v6 4/5] alienware-wmi: added autodetect_thermal_profile for devices with quirk_unknown
  2024-10-20 20:39   ` Armin Wolf
@ 2024-10-20 20:40     ` Armin Wolf
  2024-10-20 22:09       ` Armin Wolf
  2024-10-22  7:58     ` Kurt Borja
  1 sibling, 1 reply; 15+ messages in thread
From: Armin Wolf @ 2024-10-20 20:40 UTC (permalink / raw)
  To: Kurt Borja; +Cc: hdegoede, ilpo.jarvinen, linux-kernel, platform-driver-x86

Am 20.10.24 um 22:39 schrieb Armin Wolf:

> Am 17.10.24 um 10:16 schrieb Kurt Borja:
>
>> Added autodetect_thermal_profile for devices with quirk_unknown.
>> Autodetection is done through basic conditions most devices with WMAX's
>> thermal interface meet. Function exits returning 0 in case of errors.
>>
Reviewed-by: Armin Wolf <W_Armin@gmx.de>

>> Signed-off-by: Kurt Borja <kuurtb@gmail.com>
>>
>> ---
>> I apologize for the late inclusion. This feature can extend support to
>> many devices without having to list them in alienware_quirks.
>>
>> The conditions for selecting the automatic thermal profile are based on
>> observations on a lot of *issues* in AWCC open source alternatives.
>>
>> I observed only Dell's G-Series laptops have WMAX_THERMAL_BALANCED
>> avaliable and when it's present none of the other profiles are
>> avaliable, except for GMODE. When a model has USTT profiles avaliable
>> usually they have all USTT profiles avaliable, except for cool on mostly
>> Alienware devices.
>>
>> I made another implementation of this function, brute-forcing operation
>> 0x03 of Thermal_Information, which is the operation that varies the most
>> across models. I found the implementation too cumbersome to include in
>> this series, but it could potentially extend support of this driver to
>> all posible devices with this interface automatically.
>
> I like this patch, automatic configuration is always a nice feature.
>
> Please add support for operation 0x03, this way the driver can work
> automatically
> without users having to submit patches adding quirks for their machines.
>
> Maybe you can use an array for storing the supported thermal mode ids,
> like this:
>
> enum thermal_modes = {
>     THERMAL_MODE_QUIET,
>     ...
>     THERMAL_MODE_LOW_POWER,
>     THERMAL_MODE_MAX
> };
>
> const enumplatform_profile_option
> thermal_mode_to_platform_profile[THERMAL_MODE_MAX] = {
>     [THERMAL_MODE_QUIET] =PLATFORM_PROFILE_QUIET, ...
> };
>
> const enumthermal_modes
> platform_profile_to_thermal_mode[PLATFORM_PROFILE_LAST] = {
>     [PLATFORM_PROFILE_LOW_POWER] = THERMAL_MODE_LOW_POWER, ...
> };
>
>
> u8 thermal_modes[THERMAL_MODE_MAX] = {};
>
> for (int i = 0; i < THERMAL_MODE_MAX; i++) {
>     thermal_modes[i] = call_operation_3(0x06 + i);
>     // TODO: Error handling
>     if (thermal_modes[i] == 0xFFFFFFFF)
>         continue;
>
>     set_bit(supported_profiles, thermal_mode_to_platform_profile[i]);
> }
>
> then you can use platform_profile_to_thermal_mode[] when setting the
> platform profile
> and thermal_mode_to_platform_profile[] when getting the platform profile.
> I will leave it up to you on how to handle the existence of GMode.
>
> This of course is only a rough idea, you can change anything you want
> in the above pseudo-code.
>
> Thanks,
> Armin Wolf
>
>>
>> Another possibility is just including every device I observed into
>> alienware_quirks, which I can do but I want to know your opinion first.
>> ---
>>   drivers/platform/x86/dell/alienware-wmi.c | 42 +++++++++++++++++++++++
>>   1 file changed, 42 insertions(+)
>>
>> diff --git a/drivers/platform/x86/dell/alienware-wmi.c
>> b/drivers/platform/x86/dell/alienware-wmi.c
>> index 37a898273..a11ff4851 100644
>> --- a/drivers/platform/x86/dell/alienware-wmi.c
>> +++ b/drivers/platform/x86/dell/alienware-wmi.c
>> @@ -30,8 +30,11 @@
>>   #define WMAX_METHOD_DEEP_SLEEP_STATUS    0x0C
>>   #define WMAX_METHOD_THERMAL_INFORMATION    0x14
>>   #define WMAX_METHOD_THERMAL_CONTROL    0x15
>> +#define WMAX_METHOD_GMODE_STATUS    0x25
>>
>> +#define WMAX_ARG_GET_DEFAULT_PROF    0x0A
>>   #define WMAX_ARG_GET_CURRENT_PROF    0x0B
>> +#define WMAX_ARG_GET_GMODE_STATUS    0x02
>>
>>   #define WMAX_FAILURE_CODE        0xFFFFFFFF
>>
>> @@ -968,6 +971,42 @@ static int thermal_profile_set_ustt(struct
>> platform_profile_handler *pprof,
>>       return 0;
>>   }
>>
>> +static int autodetect_thermal_profile(void)
>> +{
>> +    acpi_status status;
>> +    u32 in_args;
>> +    u32 default_profile;
>> +    u32 gmode;
>> +
>> +    in_args = WMAX_ARG_GET_DEFAULT_PROF;
>> +    status = alienware_wmax_command(&in_args, sizeof(in_args),
>> +                    WMAX_METHOD_THERMAL_INFORMATION, &default_profile);
>> +
>> +    if (ACPI_FAILURE(status))
>> +        return 0;
>> +
>> +    in_args = WMAX_ARG_GET_GMODE_STATUS;
>> +    status = alienware_wmax_command(&in_args, sizeof(in_args),
>> +                    WMAX_METHOD_GMODE_STATUS, &gmode);
>> +
>> +    if (ACPI_FAILURE(status))
>> +        return 0;
>> +
>> +    if (default_profile == WMAX_THERMAL_BALANCED && gmode == 1) {
>> +        quirks->thermal = WMAX_THERMAL_TABLE_SIMPLE;
>> +        quirks->gmode = 1;
>> +        return 0;
>> +    }
>> +
>> +    if (default_profile == WMAX_THERMAL_USTT_BALANCED)
>> +        quirks->thermal = WMAX_THERMAL_TABLE_USTT;
>> +
>> +    if (gmode == 0 || gmode == 1)
>> +        quirks->gmode = 1;
>> +
>> +    return 0;
>> +}
>> +
>>   static int create_thermal_profile(void)
>>   {
>>       pp_handler.profile_get = thermal_profile_get;
>> @@ -1050,6 +1089,9 @@ static int __init alienware_wmi_init(void)
>>               goto fail_prep_deepsleep;
>>       }
>>
>> +    if (interface == WMAX && quirks == &quirk_unknown)
>> +        autodetect_thermal_profile();
>> +
>>       if (quirks->thermal > 0) {
>>           ret = create_thermal_profile();
>>           if (ret)
>

^ permalink raw reply	[flat|nested] 15+ messages in thread

* Re: [PATCH v6 4/5] alienware-wmi: added autodetect_thermal_profile for devices with quirk_unknown
  2024-10-20 20:40     ` Armin Wolf
@ 2024-10-20 22:09       ` Armin Wolf
  0 siblings, 0 replies; 15+ messages in thread
From: Armin Wolf @ 2024-10-20 22:09 UTC (permalink / raw)
  To: Kurt Borja; +Cc: hdegoede, ilpo.jarvinen, linux-kernel, platform-driver-x86

Am 20.10.24 um 22:40 schrieb Armin Wolf:

> Am 20.10.24 um 22:39 schrieb Armin Wolf:
>
>> Am 17.10.24 um 10:16 schrieb Kurt Borja:
>>
>>> Added autodetect_thermal_profile for devices with quirk_unknown.
>>> Autodetection is done through basic conditions most devices with WMAX's
>>> thermal interface meet. Function exits returning 0 in case of errors.
>>>
> Reviewed-by: Armin Wolf <W_Armin@gmx.de>
>
Oops, this should have been for patch 5. Please ignore this.

>>> Signed-off-by: Kurt Borja <kuurtb@gmail.com>
>>>
>>> ---
>>> I apologize for the late inclusion. This feature can extend support to
>>> many devices without having to list them in alienware_quirks.
>>>
>>> The conditions for selecting the automatic thermal profile are based on
>>> observations on a lot of *issues* in AWCC open source alternatives.
>>>
>>> I observed only Dell's G-Series laptops have WMAX_THERMAL_BALANCED
>>> avaliable and when it's present none of the other profiles are
>>> avaliable, except for GMODE. When a model has USTT profiles avaliable
>>> usually they have all USTT profiles avaliable, except for cool on
>>> mostly
>>> Alienware devices.
>>>
>>> I made another implementation of this function, brute-forcing operation
>>> 0x03 of Thermal_Information, which is the operation that varies the
>>> most
>>> across models. I found the implementation too cumbersome to include in
>>> this series, but it could potentially extend support of this driver to
>>> all posible devices with this interface automatically.
>>
>> I like this patch, automatic configuration is always a nice feature.
>>
>> Please add support for operation 0x03, this way the driver can work
>> automatically
>> without users having to submit patches adding quirks for their machines.
>>
>> Maybe you can use an array for storing the supported thermal mode ids,
>> like this:
>>
>> enum thermal_modes = {
>>     THERMAL_MODE_QUIET,
>>     ...
>>     THERMAL_MODE_LOW_POWER,
>>     THERMAL_MODE_MAX
>> };
>>
>> const enumplatform_profile_option
>> thermal_mode_to_platform_profile[THERMAL_MODE_MAX] = {
>>     [THERMAL_MODE_QUIET] =PLATFORM_PROFILE_QUIET, ...
>> };
>>
>> const enumthermal_modes
>> platform_profile_to_thermal_mode[PLATFORM_PROFILE_LAST] = {
>>     [PLATFORM_PROFILE_LOW_POWER] = THERMAL_MODE_LOW_POWER, ...
>> };
>>
>>
>> u8 thermal_modes[THERMAL_MODE_MAX] = {};
>>
>> for (int i = 0; i < THERMAL_MODE_MAX; i++) {
>>     thermal_modes[i] = call_operation_3(0x06 + i);
>>     // TODO: Error handling
>>     if (thermal_modes[i] == 0xFFFFFFFF)
>>         continue;
>>
>>     set_bit(supported_profiles, thermal_mode_to_platform_profile[i]);
>> }
>>
>> then you can use platform_profile_to_thermal_mode[] when setting the
>> platform profile
>> and thermal_mode_to_platform_profile[] when getting the platform
>> profile.
>> I will leave it up to you on how to handle the existence of GMode.
>>
>> This of course is only a rough idea, you can change anything you want
>> in the above pseudo-code.
>>
>> Thanks,
>> Armin Wolf
>>
>>>
>>> Another possibility is just including every device I observed into
>>> alienware_quirks, which I can do but I want to know your opinion first.
>>> ---
>>>   drivers/platform/x86/dell/alienware-wmi.c | 42
>>> +++++++++++++++++++++++
>>>   1 file changed, 42 insertions(+)
>>>
>>> diff --git a/drivers/platform/x86/dell/alienware-wmi.c
>>> b/drivers/platform/x86/dell/alienware-wmi.c
>>> index 37a898273..a11ff4851 100644
>>> --- a/drivers/platform/x86/dell/alienware-wmi.c
>>> +++ b/drivers/platform/x86/dell/alienware-wmi.c
>>> @@ -30,8 +30,11 @@
>>>   #define WMAX_METHOD_DEEP_SLEEP_STATUS    0x0C
>>>   #define WMAX_METHOD_THERMAL_INFORMATION    0x14
>>>   #define WMAX_METHOD_THERMAL_CONTROL    0x15
>>> +#define WMAX_METHOD_GMODE_STATUS    0x25
>>>
>>> +#define WMAX_ARG_GET_DEFAULT_PROF    0x0A
>>>   #define WMAX_ARG_GET_CURRENT_PROF    0x0B
>>> +#define WMAX_ARG_GET_GMODE_STATUS    0x02
>>>
>>>   #define WMAX_FAILURE_CODE        0xFFFFFFFF
>>>
>>> @@ -968,6 +971,42 @@ static int thermal_profile_set_ustt(struct
>>> platform_profile_handler *pprof,
>>>       return 0;
>>>   }
>>>
>>> +static int autodetect_thermal_profile(void)
>>> +{
>>> +    acpi_status status;
>>> +    u32 in_args;
>>> +    u32 default_profile;
>>> +    u32 gmode;
>>> +
>>> +    in_args = WMAX_ARG_GET_DEFAULT_PROF;
>>> +    status = alienware_wmax_command(&in_args, sizeof(in_args),
>>> +                    WMAX_METHOD_THERMAL_INFORMATION,
>>> &default_profile);
>>> +
>>> +    if (ACPI_FAILURE(status))
>>> +        return 0;
>>> +
>>> +    in_args = WMAX_ARG_GET_GMODE_STATUS;
>>> +    status = alienware_wmax_command(&in_args, sizeof(in_args),
>>> +                    WMAX_METHOD_GMODE_STATUS, &gmode);
>>> +
>>> +    if (ACPI_FAILURE(status))
>>> +        return 0;
>>> +
>>> +    if (default_profile == WMAX_THERMAL_BALANCED && gmode == 1) {
>>> +        quirks->thermal = WMAX_THERMAL_TABLE_SIMPLE;
>>> +        quirks->gmode = 1;
>>> +        return 0;
>>> +    }
>>> +
>>> +    if (default_profile == WMAX_THERMAL_USTT_BALANCED)
>>> +        quirks->thermal = WMAX_THERMAL_TABLE_USTT;
>>> +
>>> +    if (gmode == 0 || gmode == 1)
>>> +        quirks->gmode = 1;
>>> +
>>> +    return 0;
>>> +}
>>> +
>>>   static int create_thermal_profile(void)
>>>   {
>>>       pp_handler.profile_get = thermal_profile_get;
>>> @@ -1050,6 +1089,9 @@ static int __init alienware_wmi_init(void)
>>>               goto fail_prep_deepsleep;
>>>       }
>>>
>>> +    if (interface == WMAX && quirks == &quirk_unknown)
>>> +        autodetect_thermal_profile();
>>> +
>>>       if (quirks->thermal > 0) {
>>>           ret = create_thermal_profile();
>>>           if (ret)
>>
>

^ permalink raw reply	[flat|nested] 15+ messages in thread

* Re: [PATCH v6 5/5] alienware-wmi: WMAX interface documentation
  2024-10-17  8:17 ` [PATCH v6 5/5] alienware-wmi: WMAX interface documentation Kurt Borja
@ 2024-10-20 22:09   ` Armin Wolf
  0 siblings, 0 replies; 15+ messages in thread
From: Armin Wolf @ 2024-10-20 22:09 UTC (permalink / raw)
  To: Kurt Borja; +Cc: hdegoede, ilpo.jarvinen, linux-kernel, platform-driver-x86

Am 17.10.24 um 10:17 schrieb Kurt Borja:

> Added documentation for new WMAX interface, present on some Alienware
> X-Series, Alienware M-Series and Dell's G-Series laptops.
Reviewed-by: Armin Wolf <W_Armin@gmx.de>
> Signed-off-by: Kurt Borja <kuurtb@gmail.com>
>
> ---
> v6:
>   - Fixed typos
>   - Included new file in MAINTAINERS
> ---
>   Documentation/wmi/devices/alienware-wmi.rst | 366 ++++++++++++++++++++
>   MAINTAINERS                                 |   1 +
>   2 files changed, 367 insertions(+)
>   create mode 100644 Documentation/wmi/devices/alienware-wmi.rst
>
> diff --git a/Documentation/wmi/devices/alienware-wmi.rst b/Documentation/wmi/devices/alienware-wmi.rst
> new file mode 100644
> index 000000000..f2fb845d7
> --- /dev/null
> +++ b/Documentation/wmi/devices/alienware-wmi.rst
> @@ -0,0 +1,366 @@
> +.. SPDX-License-Identifier: GPL-2.0-or-later
> +
> +==============================================
> +Dell AWCC WMI interface driver (alienware-wmi)
> +==============================================
> +
> +Introduction
> +============
> +
> +The WMI device WMAX has been implemented for many Alienware and Dell's G-Series
> +models. Throughout these models, two implementations have been identified. The
> +first one, used by older systems, deals with HDMI, brightness, RGB, amplifier
> +and deep sleep control. The second one used by newer systems deals primarily
> +with thermal, overclocking, and GPIO control.
> +
> +It is suspected that the latter is used by Alienware Command Center (AWCC) to
> +manage manufacturer predefined thermal profiles. The alienware-wmi driver
> +exposes Thermal_Information and Thermal_Control methods through the Platform
> +Profile API to mimic AWCC's behavior.
> +
> +This newer interface, named AWCCMethodFunction has been reverse engineered, as
> +Dell has not provided any official documentation. We will try to describe to the
> +best of our ability its discovered inner workings.
> +
> +.. note::
> +   The following method description may vary between models.
> +
> +WMI interface description
> +-------------------------
> +
> +The WMI interface description can be decoded from the embedded binary MOF (bmof)
> +data using the `bmfdec <https://github.com/pali/bmfdec>`_ utility:
> +
> +::
> +
> + [WMI, Dynamic, Provider("WmiProv"), Locale("MS\\0x409"), Description("WMI Function"), guid("{A70591CE-A997-11DA-B012-B622A1EF5492}")]
> + class AWCCWmiMethodFunction {
> +   [key, read] string InstanceName;
> +   [read] boolean Active;
> +
> +   [WmiMethodId(13), Implemented, read, write, Description("Return Overclocking Report.")] void Return_OverclockingReport([out] uint32 argr);
> +   [WmiMethodId(14), Implemented, read, write, Description("Set OCUIBIOS Control.")] void Set_OCUIBIOSControl([in] uint32 arg2, [out] uint32 argr);
> +   [WmiMethodId(15), Implemented, read, write, Description("Clear OC FailSafe Flag.")] void Clear_OCFailSafeFlag([out] uint32 argr);
> +   [WmiMethodId(19), Implemented, read, write, Description("Get Fan Sensors.")] void GetFanSensors([in] uint32 arg2, [out] uint32 argr);
> +   [WmiMethodId(20), Implemented, read, write, Description("Thermal Information.")] void Thermal_Information([in] uint32 arg2, [out] uint32 argr);
> +   [WmiMethodId(21), Implemented, read, write, Description("Thermal Control.")] void Thermal_Control([in] uint32 arg2, [out] uint32 argr);
> +   [WmiMethodId(23), Implemented, read, write, Description("MemoryOCControl.")] void MemoryOCControl([in] uint32 arg2, [out] uint32 argr);
> +   [WmiMethodId(26), Implemented, read, write, Description("System Information.")] void SystemInformation([in] uint32 arg2, [out] uint32 argr);
> +   [WmiMethodId(28), Implemented, read, write, Description("Power Information.")] void PowerInformation([in] uint32 arg2, [out] uint32 argr);
> +   [WmiMethodId(32), Implemented, read, write, Description("FW Update GPIO toggle.")] void FWUpdateGPIOtoggle([in] uint32 arg2, [out] uint32 argr);
> +   [WmiMethodId(33), Implemented, read, write, Description("Read Total of GPIOs.")] void ReadTotalofGPIOs([out] uint32 argr);
> +   [WmiMethodId(34), Implemented, read, write, Description("Read GPIO pin Status.")] void ReadGPIOpPinStatus([in] uint32 arg2, [out] uint32 argr);
> +   [WmiMethodId(35), Implemented, read, write, Description("Read Chassis Color.")] void ReadChassisColor([out] uint32 argr);
> +   [WmiMethodId(36), Implemented, read, write, Description("Read Platform Properties.")] void ReadPlatformProperties([out] uint32 argr);
> +   [WmiMethodId(128), Implemented, read, write, Description("Caldera SW installation.")] void CalderaSWInstallation([out] uint32 argr);
> +   [WmiMethodId(129), Implemented, read, write, Description("Caldera SW is released.")] void CalderaSWReleased([out] uint32 argr);
> +   [WmiMethodId(130), Implemented, read, write, Description("Caldera Connection Status.")] void CalderaConnectionStatus([in] uint32 arg2, [out] uint32 argr);
> +   [WmiMethodId(131), Implemented, read, write, Description("Surprise Unplugged Flag Status.")] void SurpriseUnpluggedFlagStatus([out] uint32 argr);
> +   [WmiMethodId(132), Implemented, read, write, Description("Clear Surprise Unplugged Flag.")] void ClearSurpriseUnpluggedFlag([out] uint32 argr);
> +   [WmiMethodId(133), Implemented, read, write, Description("Cancel Undock Request.")] void CancelUndockRequest([out] uint32 argr);
> +   [WmiMethodId(135), Implemented, read, write, Description("Devices in Caldera.")] void DevicesInCaldera([in] uint32 arg2, [out] uint32 argr);
> +   [WmiMethodId(136), Implemented, read, write, Description("Notify BIOS for SW ready to disconnect Caldera.")] void NotifyBIOSForSWReadyToDisconnectCaldera([out] uint32 argr);
> +   [WmiMethodId(160), Implemented, read, write, Description("Tobii SW installation.")] void TobiiSWinstallation([out] uint32 argr);
> +   [WmiMethodId(161), Implemented, read, write, Description("Tobii SW Released.")] void TobiiSWReleased([out] uint32 argr);
> +   [WmiMethodId(162), Implemented, read, write, Description("Tobii Camera Power Reset.")] void TobiiCameraPowerReset([out] uint32 argr);
> +   [WmiMethodId(163), Implemented, read, write, Description("Tobii Camera Power On.")] void TobiiCameraPowerOn([out] uint32 argr);
> +   [WmiMethodId(164), Implemented, read, write, Description("Tobii Camera Power Off.")] void TobiiCameraPowerOff([out] uint32 argr);
> + };
> +
> +Some of these methods get quite intricate so we will describe them using
> +pseudo-code that vaguely resembles the original ASL code.
> +
> +Argument Structure
> +------------------
> +
> +All input arguments have type **uint32** and their structure is very similar
> +between methods. Usually, the first byte corresponds to a specific *operation*
> +the method performs, and the subsequent bytes correspond to *arguments* passed
> +to this *operation*. For example, if an operation has code 0x01 and requires an
> +ID 0xA0, the argument you would pass to the method is 0xA001.
> +
> +
> +Thermal Methods
> +===============
> +
> +WMI method Thermal_Information([in] uint32 arg2, [out] uint32 argr)
> +-------------------------------------------------------------------
> +
> +::
> +
> + if BYTE_0(arg2) == 0x01:
> +         argr = 1
> +
> + if BYTE_0(arg2) == 0x02:
> +         argr = UNKNOWN_CONSTANT
> +
> + if BYTE_0(arg2) == 0x03:
> +         if BYTE_1(arg2) == 0x00:
> +                 argr = FAN_ID_0
> +
> +         if BYTE_1(arg2) == 0x01:
> +                 argr = FAN_ID_1
> +
> +         if BYTE_1(arg2) == 0x02:
> +                 argr = FAN_ID_2
> +
> +         if BYTE_1(arg2) == 0x03:
> +                 argr = FAN_ID_3
> +
> +         if BYTE_1(arg2) == 0x04:
> +                 argr = SENSOR_ID_CPU | 0x0100
> +
> +         if BYTE_1(arg2) == 0x05:
> +                 argr = SENSOR_ID_GPU | 0x0100
> +
> +         if BYTE_1(arg2) == 0x06:
> +                 argr = THERMAL_MODE_QUIET_ID
> +
> +         if BYTE_1(arg2) == 0x07:
> +                 argr = THERMAL_MODE_BALANCED_ID
> +
> +         if BYTE_1(arg2) == 0x08:
> +                 argr = THERMAL_MODE_BALANCED_PERFORMANCE_ID
> +
> +         if BYTE_1(arg2) == 0x09:
> +                 argr = THERMAL_MODE_PERFORMANCE_ID
> +
> +         if BYTE_1(arg2) == 0x0A:
> +                 argr = THERMAL_MODE_LOW_POWER_ID
> +
> +         if BYTE_1(arg2) == 0x0B:
> +                 argr = THERMAL_MODE_GMODE_ID
> +
> +         else:
> +                 argr = 0xFFFFFFFF
> +
> + if BYTE_0(arg2) == 0x04:
> +         if is_valid_sensor(BYTE_1(arg2)):
> +                 argr = SENSOR_TEMP_C
> +         else:
> +                 argr = 0xFFFFFFFF
> +
> + if BYTE_0(arg2) == 0x05:
> +         if is_valid_fan(BYTE_1(arg2)):
> +                 argr = FAN_RPM()
> +
> + if BYTE_0(arg2) == 0x06:
> +         skip
> +
> + if BYTE_0(arg2) == 0x07:
> +         argr = 0
> +
> + If BYTE_0(arg2) == 0x08:
> +         if is_valid_fan(BYTE_1(arg2)):
> +                 argr = 0
> +         else:
> +                 argr = 0xFFFFFFFF
> +
> + if BYTE_0(arg2) == 0x09:
> +         if is_valid_fan(BYTE_1(arg2)):
> +                 argr = FAN_UNKNOWN_STAT_0()
> +
> +         else:
> +                 argr = 0xFFFFFFFF
> +
> + if BYTE_0(arg2) == 0x0A:
> +         argr = THERMAL_MODE_BALANCED_ID
> +
> + if BYTE_0(arg2) == 0x0B:
> +         argr = CURRENT_THERMAL_MODE()
> +
> + if BYTE_0(arg2) == 0x0C:
> +         if is_valid_fan(BYTE_1(arg2)):
> +                 argr = FAN_UNKNOWN_STAT_1()
> +         else:
> +                 argr = 0xFFFFFFFF
> +
> +WMI method Thermal_Control([in] uint32 arg2, [out] uint32 argr)
> +---------------------------------------------------------------
> +
> +::
> +
> + if BYTE_0(arg2) == 0x01:
> +         if is_valid_thermal_profile(BYTE_1(arg2)):
> +                 SET_THERMAL_PROFILE(BYTE_1(arg2))
> +                 argr = 0
> +
> + if BYTE_0(arg2) == 0x02:
> +         if is_valid_fan(BYTE_1(arg2)):
> +                 SET_FAN_SPEED_MULTIPLIER(BYTE_2(arg2))
> +                 argr = 0
> +         else:
> +                 argr = 0xFFFFFFFF
> +
> +.. note::
> +   While you can manually change the fan speed multiplier with this method,
> +   Dell's BIOS tends to overwrite this changes anyway.
> +
> +These are the known thermal profile codes:
> +
> +::
> +
> + CUSTOM                         0x00
> +
> + QUIET                          0x96
> + BALANCED                       0x97
> + BALANCED_PERFORMANCE           0x98
> + PERFORMANCE                    0x99
> +
> + QUIET_USTT                     0xA3
> + BALANCED_USTT                  0xA0
> + BALANCED_PERFORMANCE_USTT      0xA1
> + PERFORMANCE_USTT               0xA4
> + LOW_POWER_USTT                 0xA5
> +
> + GMODE                          0xAB
> +
> +Usually if a model doesn't support the first four profiles they will support
> +the User Selectable Thermal Tables (USTT) profiles and vice-versa.
> +
> +GMODE replaces PERFORMANCE in G-Series laptops.
> +
> +WMI method GetFanSensors([in] uint32 arg2, [out] uint32 argr)
> +-------------------------------------------------------------
> +
> +::
> +
> + if BYTE_0(arg2) == 1:
> +        if is_valid_fan(BYTE_1(arg2)):
> +                argr = 1
> +        else:
> +                argr = 0
> +
> + if BYTE_0(arg2) == 2:
> +        if is_valid_fan(BYTE_1(arg2)):
> +                if BYTE_2(arg2) == 0:
> +                        argr == SENSOR_ID
> +                else
> +                        argr == 0xFFFFFFFF
> +        else:
> +                argr = 0
> +
> +Overclocking Methods
> +====================
> +
> +.. warning::
> +   These methods have not been tested and are only partially reverse
> +   engineered.
> +
> +WMI method Return_OverclockingReport([out] uint32 argr)
> +-------------------------------------------------------
> +
> +::
> +
> + CSMI (0xE3, 0x99)
> + argr = 0
> +
> +CSMI is an unknown operation.
> +
> +WMI method Set_OCUIBIOSControl([in] uint32 arg2, [out] uint32 argr)
> +-------------------------------------------------------------------
> +
> +::
> +
> + CSMI (0xE3, 0x99)
> + argr = 0
> +
> +CSMI is an unknown operation.
> +
> +WMI method Clear_OCFailSafeFlag([out] uint32 argr)
> +--------------------------------------------------
> +
> +::
> +
> + CSMI (0xE3, 0x99)
> + argr = 0
> +
> +CSMI is an unknown operation.
> +
> +
> +WMI method MemoryOCControl([in] uint32 arg2, [out] uint32 argr)
> +---------------------------------------------------------------
> +
> +AWCC supports memory overclocking, but this method is very intricate and has
> +not been deciphered yet.
> +
> +GPIO methods
> +============
> +
> +These methods are probably related to some kind of firmware update system,
> +through a GPIO device.
> +
> +.. warning::
> +   These methods have not been tested and are only partially reverse
> +   engineered.
> +
> +WMI method FWUpdateGPIOtoggle([in] uint32 arg2, [out] uint32 argr)
> +------------------------------------------------------------------
> +
> +::
> +
> + if BYTE_0(arg2) == 0:
> +         if BYTE_1(arg2) == 1:
> +                 SET_PIN_A_HIGH()
> +         else:
> +                 SET_PIN_A_LOW()
> +
> + if BYTE_0(arg2) == 1:
> +         if BYTE_1(arg2) == 1:
> +                 SET_PIN_B_HIGH()
> +
> +         else:
> +                 SET_PIN_B_LOW()
> +
> + else:
> +         argr = 1
> +
> +WMI method ReadTotalofGPIOs([out] uint32 argr)
> +----------------------------------------------
> +
> +::
> +
> + argr = 0x02
> +
> +WMI method ReadGPIOpPinStatus([in] uint32 arg2, [out] uint32 argr)
> +------------------------------------------------------------------
> +
> +::
> +
> + if BYTE_0(arg2) == 0:
> +         argr = PIN_A_STATUS
> +
> + if BYTE_0(arg2) == 1:
> +         argr = PIN_B_STATUS
> +
> +Other information Methods
> +=========================
> +
> +WMI method SystemInformation([in] uint32 arg2, [out] uint32 argr)
> +-----------------------------------------------------------------
> +
> +Returns unknown information.
> +
> +WMI method PowerInformation([in] uint32 arg2, [out] uint32 argr)
> +----------------------------------------------------------------
> +
> +Returns unknown information.
> +
> +WMI method ReadChassisColor([out] uint32 argr)
> +----------------------------------------------
> +
> +::
> +
> + argr = CHASSIS_COLOR_ID
> +
> +WMI method ReadPlatformProperties([out] uint32 argr)
> +----------------------------------------------------
> +
> +Returns unknown information.
> +
> +Acknowledgements
> +================
> +
> +Kudos to `AlexIII <https://github.com/AlexIII/tcc-g15>`_ for documenting
> +and testing available thermal profile codes.
> +
> diff --git a/MAINTAINERS b/MAINTAINERS
> index c27f31907..25f6de4c2 100644
> --- a/MAINTAINERS
> +++ b/MAINTAINERS
> @@ -792,6 +792,7 @@ F:	drivers/perf/alibaba_uncore_drw_pmu.c
>   ALIENWARE WMI DRIVER
>   L:	Dell.Client.Kernel@dell.com
>   S:	Maintained
> +F:	Documentation/wmi/devices/alienware-wmi.rst
>   F:	drivers/platform/x86/dell/alienware-wmi.c
>
>   ALLEGRO DVT VIDEO IP CORE DRIVER

^ permalink raw reply	[flat|nested] 15+ messages in thread

* Re: [PATCH v6 3/5] alienware-wmi: added platform profile support
  2024-10-20 20:12   ` Armin Wolf
@ 2024-10-22  7:35     ` Kurt Borja
  0 siblings, 0 replies; 15+ messages in thread
From: Kurt Borja @ 2024-10-22  7:35 UTC (permalink / raw)
  To: Armin Wolf; +Cc: hdegoede, ilpo.jarvinen, linux-kernel, platform-driver-x86

On Sun, Oct 20, 2024 at 10:12:48PM +0200, Armin Wolf wrote:
> Am 17.10.24 um 10:15 schrieb Kurt Borja:
> 
> > Implements platform profile support for Dell laptops with new WMAX
> > thermal interface, present on some Alienware X-Series, Alienware
> > M-Series and Dell's G-Series laptops.
> > 
> > This implementation supports three sets of thermal tables declared in
> > enum WMAX_THERMAL_TABLE and gmode, using quirks *thermal* and *gmode*
> > respectively. These sets are found in most Dell's devices that support
> > WMAX's thermal interface.
> > 
> > Signed-off-by: Kurt Borja <kuurtb@gmail.com>
> > 
> > ---
> > v6:
> >   - Fixed alignment on some function definitions
> >   - Fixed braces on if statment
> >   - Removed quirk thermal_ustt
> >   - Now quirk thermal can take values defined in enum WMAX_THERMAL_TABLE.
> >   - Proper removal of thermal_profile
> > ---
> >   drivers/platform/x86/dell/Kconfig         |   1 +
> >   drivers/platform/x86/dell/alienware-wmi.c | 251 ++++++++++++++++++++++
> >   2 files changed, 252 insertions(+)
> > 
> > diff --git a/drivers/platform/x86/dell/Kconfig b/drivers/platform/x86/dell/Kconfig
> > index 68a49788a..b06d634cd 100644
> > --- a/drivers/platform/x86/dell/Kconfig
> > +++ b/drivers/platform/x86/dell/Kconfig
> > @@ -21,6 +21,7 @@ config ALIENWARE_WMI
> >   	depends on LEDS_CLASS
> >   	depends on NEW_LEDS
> >   	depends on ACPI_WMI
> > +	select ACPI_PLATFORM_PROFILE
> >   	help
> >   	 This is a driver for controlling Alienware BIOS driven
> >   	 features.  It exposes an interface for controlling the AlienFX
> > diff --git a/drivers/platform/x86/dell/alienware-wmi.c b/drivers/platform/x86/dell/alienware-wmi.c
> > index b27f3b64c..37a898273 100644
> > --- a/drivers/platform/x86/dell/alienware-wmi.c
> > +++ b/drivers/platform/x86/dell/alienware-wmi.c
> > @@ -8,8 +8,11 @@
> >   #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
> > 
> >   #include <linux/acpi.h>
> > +#include <linux/bitfield.h>
> > +#include <linux/bits.h>
> >   #include <linux/module.h>
> >   #include <linux/platform_device.h>
> > +#include <linux/platform_profile.h>
> >   #include <linux/dmi.h>
> >   #include <linux/leds.h>
> > 
> > @@ -25,6 +28,12 @@
> >   #define WMAX_METHOD_AMPLIFIER_CABLE	0x6
> >   #define WMAX_METHOD_DEEP_SLEEP_CONTROL	0x0B
> >   #define WMAX_METHOD_DEEP_SLEEP_STATUS	0x0C
> > +#define WMAX_METHOD_THERMAL_INFORMATION	0x14
> > +#define WMAX_METHOD_THERMAL_CONTROL	0x15
> > +
> > +#define WMAX_ARG_GET_CURRENT_PROF	0x0B
> > +
> > +#define WMAX_FAILURE_CODE		0xFFFFFFFF
> > 
> >   MODULE_AUTHOR("Mario Limonciello <mario.limonciello@outlook.com>");
> >   MODULE_DESCRIPTION("Alienware special feature control");
> > @@ -49,11 +58,33 @@ enum WMAX_CONTROL_STATES {
> >   	WMAX_SUSPEND = 3,
> >   };
> > 
> > +enum WMAX_THERMAL_TABLE {
> > +	WMAX_THERMAL_TABLE_SIMPLE	= 1,
> > +	WMAX_THERMAL_TABLE_USTT		= 2,
> > +	WMAX_THERMAL_TABLE_USTT_COOL	= 3,
> > +};
> > +
> > +enum WMAX_THERMAL_PROFILE {
> > +	WMAX_THERMAL_QUIET			= 0x96,
> > +	WMAX_THERMAL_BALANCED			= 0x97,
> > +	WMAX_THERMAL_BALANCED_PERFORMANCE	= 0x98,
> > +	WMAX_THERMAL_PERFORMANCE		= 0x99,
> > +	WMAX_THERMAL_USTT_LOW_POWER		= 0xA5,
> > +	WMAX_THERMAL_USTT_COOL			= 0xA2,
> > +	WMAX_THERMAL_USTT_QUIET			= 0xA3,
> > +	WMAX_THERMAL_USTT_BALANCED		= 0xA0,
> > +	WMAX_THERMAL_USTT_BALANCED_PERFORMANCE	= 0xA1,
> > +	WMAX_THERMAL_USTT_PERFORMANCE		= 0xA4,
> > +	WMAX_THERMAL_GMODE			= 0xAB,
> > +};
> > +
> >   struct quirk_entry {
> >   	u8 num_zones;
> >   	u8 hdmi_mux;
> >   	u8 amplifier;
> >   	u8 deepslp;
> > +	u8 thermal;
> > +	u8 gmode;
> >   };
> > 
> >   static struct quirk_entry *quirks;
> > @@ -64,6 +95,8 @@ static struct quirk_entry quirk_inspiron5675 = {
> >   	.hdmi_mux = 0,
> >   	.amplifier = 0,
> >   	.deepslp = 0,
> > +	.thermal = 0,
> > +	.gmode = 0,
> >   };
> > 
> >   static struct quirk_entry quirk_unknown = {
> > @@ -71,6 +104,8 @@ static struct quirk_entry quirk_unknown = {
> >   	.hdmi_mux = 0,
> >   	.amplifier = 0,
> >   	.deepslp = 0,
> > +	.thermal = 0,
> > +	.gmode = 0,
> >   };
> > 
> >   static struct quirk_entry quirk_x51_r1_r2 = {
> > @@ -78,6 +113,8 @@ static struct quirk_entry quirk_x51_r1_r2 = {
> >   	.hdmi_mux = 0,
> >   	.amplifier = 0,
> >   	.deepslp = 0,
> > +	.thermal = 0,
> > +	.gmode = 0,
> >   };
> > 
> >   static struct quirk_entry quirk_x51_r3 = {
> > @@ -85,6 +122,8 @@ static struct quirk_entry quirk_x51_r3 = {
> >   	.hdmi_mux = 0,
> >   	.amplifier = 1,
> >   	.deepslp = 0,
> > +	.thermal = 0,
> > +	.gmode = 0,
> >   };
> > 
> >   static struct quirk_entry quirk_asm100 = {
> > @@ -92,6 +131,8 @@ static struct quirk_entry quirk_asm100 = {
> >   	.hdmi_mux = 1,
> >   	.amplifier = 0,
> >   	.deepslp = 0,
> > +	.thermal = 0,
> > +	.gmode = 0,
> >   };
> > 
> >   static struct quirk_entry quirk_asm200 = {
> > @@ -99,6 +140,8 @@ static struct quirk_entry quirk_asm200 = {
> >   	.hdmi_mux = 1,
> >   	.amplifier = 0,
> >   	.deepslp = 1,
> > +	.thermal = 0,
> > +	.gmode = 0,
> >   };
> > 
> >   static struct quirk_entry quirk_asm201 = {
> > @@ -106,6 +149,17 @@ static struct quirk_entry quirk_asm201 = {
> >   	.hdmi_mux = 1,
> >   	.amplifier = 1,
> >   	.deepslp = 1,
> > +	.thermal = 0,
> > +	.gmode = 0,
> > +};
> > +
> > +static struct quirk_entry quirk_x15_r1 = {
> > +	.num_zones = 2,
> > +	.hdmi_mux = 0,
> > +	.amplifier = 0,
> > +	.deepslp = 0,
> > +	.thermal = WMAX_THERMAL_TABLE_USTT,
> > +	.gmode = 0,
> >   };
> > 
> >   static int __init dmi_matched(const struct dmi_system_id *dmi)
> > @@ -169,6 +223,15 @@ static const struct dmi_system_id alienware_quirks[] __initconst = {
> >   		},
> >   		.driver_data = &quirk_asm201,
> >   	},
> > +	{
> > +		.callback = dmi_matched,
> > +		.ident = "Alienware x15 R1",
> > +		.matches = {
> > +			DMI_MATCH(DMI_SYS_VENDOR, "Alienware"),
> > +			DMI_MATCH(DMI_PRODUCT_NAME, "Alienware x15 R1")
> > +		},
> > +		.driver_data = &quirk_x15_r1,
> > +	},
> >   	{
> >   		.callback = dmi_matched,
> >   		.ident = "Dell Inc. Inspiron 5675",
> > @@ -218,6 +281,7 @@ static struct platform_device *platform_device;
> >   static struct device_attribute *zone_dev_attrs;
> >   static struct attribute **zone_attrs;
> >   static struct platform_zone *zone_data;
> > +static struct platform_profile_handler pp_handler;
> > 
> >   static struct platform_driver platform_driver = {
> >   	.driver = {
> > @@ -761,6 +825,184 @@ static int create_deepsleep(struct platform_device *dev)
> >   	return ret;
> >   }
> > 
> > +/*
> > + * Thermal Profile control
> > + *  - Provides thermal profile control through the Platform Profile API
> > + */
> > +#define WMAX_ARGUMENT_MASK	GENMASK(15, 8)
> > +#define WMAX_PROFILE_ACTIVATE	0x01
> > +
> > +static u32 profile_to_wmax_arg(enum WMAX_THERMAL_PROFILE prof)
> > +{
> > +	return FIELD_PREP(WMAX_ARGUMENT_MASK, prof) | WMAX_PROFILE_ACTIVATE;
> > +}
> > +
> > +static int thermal_profile_get(struct platform_profile_handler *pprof,
> > +			       enum platform_profile_option *profile)
> > +{
> > +	acpi_status status;
> > +	u32 in_args = WMAX_ARG_GET_CURRENT_PROF;
> > +	u32 out_data;
> > +
> > +	status = alienware_wmax_command(&in_args, sizeof(in_args),
> > +					WMAX_METHOD_THERMAL_INFORMATION, &out_data);
> > +
> > +	if (ACPI_FAILURE(status))
> > +		return -EIO;
> > +
> > +	if (out_data == WMAX_FAILURE_CODE)
> > +		return -EBADRQC;
> > +
> > +	switch (out_data) {
> > +	case WMAX_THERMAL_USTT_LOW_POWER:
> > +		*profile = PLATFORM_PROFILE_LOW_POWER;
> > +		break;
> > +	case WMAX_THERMAL_USTT_COOL:
> > +		*profile = PLATFORM_PROFILE_COOL;
> > +		break;
> > +	case WMAX_THERMAL_QUIET:
> > +	case WMAX_THERMAL_USTT_QUIET:
> > +		*profile = PLATFORM_PROFILE_QUIET;
> > +		break;
> > +	case WMAX_THERMAL_BALANCED:
> > +	case WMAX_THERMAL_USTT_BALANCED:
> > +		*profile = PLATFORM_PROFILE_BALANCED;
> > +		break;
> > +	case WMAX_THERMAL_BALANCED_PERFORMANCE:
> > +	case WMAX_THERMAL_USTT_BALANCED_PERFORMANCE:
> > +		*profile = PLATFORM_PROFILE_BALANCED_PERFORMANCE;
> > +		break;
> > +	case WMAX_THERMAL_GMODE:
> > +	case WMAX_THERMAL_PERFORMANCE:
> > +	case WMAX_THERMAL_USTT_PERFORMANCE:
> > +		*profile = PLATFORM_PROFILE_PERFORMANCE;
> > +		break;
> > +	default:
> > +		return -ENODATA;
> > +	}
> > +
> > +	return 0;
> > +}
> > +
> > +static int thermal_profile_set(struct platform_profile_handler *pprof,
> > +			       enum platform_profile_option profile)
> > +{
> > +	acpi_status status;
> > +	u32 in_args;
> > +	u32 out_data;
> > +
> > +	switch (profile) {
> > +	case PLATFORM_PROFILE_QUIET:
> > +		in_args = profile_to_wmax_arg(WMAX_THERMAL_QUIET);
> > +		break;
> > +	case PLATFORM_PROFILE_BALANCED:
> > +		in_args = profile_to_wmax_arg(WMAX_THERMAL_BALANCED);
> > +		break;
> > +	case PLATFORM_PROFILE_BALANCED_PERFORMANCE:
> > +		in_args = profile_to_wmax_arg(WMAX_THERMAL_BALANCED_PERFORMANCE);
> > +		break;
> > +	case PLATFORM_PROFILE_PERFORMANCE:
> > +		if (quirks->gmode > 0)
> > +			in_args = profile_to_wmax_arg(WMAX_THERMAL_GMODE);
> > +		else
> > +			in_args = profile_to_wmax_arg(WMAX_THERMAL_PERFORMANCE);
> > +		break;
> > +	default:
> > +		return -EOPNOTSUPP;
> > +	}
> > +
> > +	status = alienware_wmax_command(&in_args, sizeof(in_args),
> > +					WMAX_METHOD_THERMAL_CONTROL, &out_data);
> > +
> > +	if (ACPI_FAILURE(status))
> > +		return -EIO;
> > +
> > +	if (out_data == WMAX_FAILURE_CODE)
> > +		return -EBADRQC;
> > +
> > +	return 0;
> > +}
> > +
> > +static int thermal_profile_set_ustt(struct platform_profile_handler *pprof,
> > +				    enum platform_profile_option profile)
> > +{
> > +	acpi_status status;
> > +	u32 in_args;
> > +	u32 out_data;
> > +
> > +	switch (profile) {
> > +	case PLATFORM_PROFILE_LOW_POWER:
> > +		in_args = profile_to_wmax_arg(WMAX_THERMAL_USTT_LOW_POWER);
> > +		break;
> > +	case PLATFORM_PROFILE_COOL:
> > +		in_args = profile_to_wmax_arg(WMAX_THERMAL_USTT_COOL);
> > +		break;
> > +	case PLATFORM_PROFILE_QUIET:
> > +		in_args = profile_to_wmax_arg(WMAX_THERMAL_USTT_QUIET);
> > +		break;
> > +	case PLATFORM_PROFILE_BALANCED:
> > +		in_args = profile_to_wmax_arg(WMAX_THERMAL_USTT_BALANCED);
> > +		break;
> > +	case PLATFORM_PROFILE_BALANCED_PERFORMANCE:
> > +		in_args = profile_to_wmax_arg(WMAX_THERMAL_USTT_BALANCED_PERFORMANCE);
> > +		break;
> > +	case PLATFORM_PROFILE_PERFORMANCE:
> > +		if (quirks->gmode > 0)
> > +			in_args = profile_to_wmax_arg(WMAX_THERMAL_GMODE);
> > +		else
> > +			in_args = profile_to_wmax_arg(WMAX_THERMAL_USTT_PERFORMANCE);
> > +		break;
> > +	default:
> > +		return -EOPNOTSUPP;
> > +	}
> > +
> > +	status = alienware_wmax_command(&in_args, sizeof(in_args),
> > +					WMAX_METHOD_THERMAL_CONTROL, &out_data);
> > +
> > +	if (ACPI_FAILURE(status))
> > +		return -EIO;
> > +
> > +	if (out_data == WMAX_FAILURE_CODE)
> > +		return -EBADRQC;
> > +
> > +	return 0;
> > +}
> > +
> > +static int create_thermal_profile(void)
> > +{
> > +	pp_handler.profile_get = thermal_profile_get;
> > +
> > +	switch (quirks->thermal) {
> > +	case WMAX_THERMAL_TABLE_SIMPLE:
> > +		pp_handler.profile_set = thermal_profile_set;
> > +		break;
> > +	case WMAX_THERMAL_TABLE_USTT:
> > +		pp_handler.profile_set = thermal_profile_set_ustt;
> > +		set_bit(PLATFORM_PROFILE_LOW_POWER, pp_handler.choices);
> > +		set_bit(PLATFORM_PROFILE_QUIET, pp_handler.choices);
> > +		set_bit(PLATFORM_PROFILE_BALANCED_PERFORMANCE, pp_handler.choices);
> > +		break;
> > +	case WMAX_THERMAL_TABLE_USTT_COOL:
> > +		pp_handler.profile_set = thermal_profile_set_ustt;
> > +		set_bit(PLATFORM_PROFILE_LOW_POWER, pp_handler.choices);
> > +		set_bit(PLATFORM_PROFILE_QUIET, pp_handler.choices);
> > +		set_bit(PLATFORM_PROFILE_COOL, pp_handler.choices);
> > +		set_bit(PLATFORM_PROFILE_BALANCED_PERFORMANCE, pp_handler.choices);
> > +		break;
> > +	}
> 
> Please add a default statement here to return -EINVAL just in case.

Noted.

> 
> Other than that:
> 
> Reviewed-by: Armin Wolf <W_Armin@gmx.de>

Thank you so much for your reviews. I'll won't forget to add them on v7.

> 
> > +
> > +	set_bit(PLATFORM_PROFILE_BALANCED, pp_handler.choices);
> > +	set_bit(PLATFORM_PROFILE_PERFORMANCE, pp_handler.choices);
> > +
> > +	return platform_profile_register(&pp_handler);
> > +}
> > +
> > +static void remove_thermal_profile(void)
> > +{
> > +	if (quirks->thermal > 0)
> > +		platform_profile_remove();
> > +}
> > +
> >   static int __init alienware_wmi_init(void)
> >   {
> >   	int ret;
> > @@ -808,6 +1050,12 @@ static int __init alienware_wmi_init(void)
> >   			goto fail_prep_deepsleep;
> >   	}
> > 
> > +	if (quirks->thermal > 0) {
> > +		ret = create_thermal_profile();
> > +		if (ret)
> > +			goto fail_prep_thermal_profile;
> > +	}
> > +
> >   	ret = alienware_zone_init(platform_device);
> >   	if (ret)
> >   		goto fail_prep_zones;
> > @@ -816,6 +1064,8 @@ static int __init alienware_wmi_init(void)
> > 
> >   fail_prep_zones:
> >   	alienware_zone_exit(platform_device);
> > +	remove_thermal_profile();
> > +fail_prep_thermal_profile:
> >   fail_prep_deepsleep:
> >   fail_prep_amplifier:
> >   fail_prep_hdmi:
> > @@ -835,6 +1085,7 @@ static void __exit alienware_wmi_exit(void)
> >   	if (platform_device) {
> >   		alienware_zone_exit(platform_device);
> >   		remove_hdmi(platform_device);
> > +		remove_thermal_profile();
> >   		platform_device_unregister(platform_device);
> >   		platform_driver_unregister(&platform_driver);
> >   	}

^ permalink raw reply	[flat|nested] 15+ messages in thread

* Re: [PATCH v6 4/5] alienware-wmi: added autodetect_thermal_profile for devices with quirk_unknown
  2024-10-20 20:39   ` Armin Wolf
  2024-10-20 20:40     ` Armin Wolf
@ 2024-10-22  7:58     ` Kurt Borja
  1 sibling, 0 replies; 15+ messages in thread
From: Kurt Borja @ 2024-10-22  7:58 UTC (permalink / raw)
  To: Armin Wolf; +Cc: hdegoede, ilpo.jarvinen, linux-kernel, platform-driver-x86

On Sun, Oct 20, 2024 at 10:39:09PM +0200, Armin Wolf wrote:
> Am 17.10.24 um 10:16 schrieb Kurt Borja:
> 
> > Added autodetect_thermal_profile for devices with quirk_unknown.
> > Autodetection is done through basic conditions most devices with WMAX's
> > thermal interface meet. Function exits returning 0 in case of errors.
> > 
> > Signed-off-by: Kurt Borja <kuurtb@gmail.com>
> > 
> > ---
> > I apologize for the late inclusion. This feature can extend support to
> > many devices without having to list them in alienware_quirks.
> > 
> > The conditions for selecting the automatic thermal profile are based on
> > observations on a lot of *issues* in AWCC open source alternatives.
> > 
> > I observed only Dell's G-Series laptops have WMAX_THERMAL_BALANCED
> > avaliable and when it's present none of the other profiles are
> > avaliable, except for GMODE. When a model has USTT profiles avaliable
> > usually they have all USTT profiles avaliable, except for cool on mostly
> > Alienware devices.
> > 
> > I made another implementation of this function, brute-forcing operation
> > 0x03 of Thermal_Information, which is the operation that varies the most
> > across models. I found the implementation too cumbersome to include in
> > this series, but it could potentially extend support of this driver to
> > all posible devices with this interface automatically.
> 
> I like this patch, automatic configuration is always a nice feature.
> 
> Please add support for operation 0x03, this way the driver can work automatically
> without users having to submit patches adding quirks for their machines.
> 
> Maybe you can use an array for storing the supported thermal mode ids, like this:
> 
> enum thermal_modes = {
> 	THERMAL_MODE_QUIET,
> 	...
> 	THERMAL_MODE_LOW_POWER,
> 	THERMAL_MODE_MAX
> };
> 
> const enumplatform_profile_option  thermal_mode_to_platform_profile[THERMAL_MODE_MAX] = {
> 	[THERMAL_MODE_QUIET] =PLATFORM_PROFILE_QUIET, ...
> };
> 
> const enumthermal_modes platform_profile_to_thermal_mode[PLATFORM_PROFILE_LAST] = {
> 	[PLATFORM_PROFILE_LOW_POWER] = THERMAL_MODE_LOW_POWER, ...
> };
> 
> 
> u8 thermal_modes[THERMAL_MODE_MAX] = {};
> 
> for (int i = 0; i < THERMAL_MODE_MAX; i++) {
> 	thermal_modes[i] = call_operation_3(0x06 + i);
> 	// TODO: Error handling
> 	if (thermal_modes[i] == 0xFFFFFFFF)
> 		continue;
> 
> 	set_bit(supported_profiles, thermal_mode_to_platform_profile[i]);
> }
> 
> then you can use platform_profile_to_thermal_mode[] when setting the platform profile
> and thermal_mode_to_platform_profile[] when getting the platform profile.
> I will leave it up to you on how to handle the existence of GMode.
> 
> This of course is only a rough idea, you can change anything you want in the above pseudo-code.

Thank you. I successfully added support for operation 0x03 with this
pattern. I'll comment on details on v7. 

Now that we have automatic detection, I think we should do automatic
detection on all models, meaning that 3/5 and 4/5 should be squashed
together, because code corresponding to quirk profile configuration now
seems unnecesary.

I'll wait for your advice before submitting v7.

Thank you, 
Kurt

> 
> Thanks,
> Armin Wolf
> 
> > 
> > Another possibility is just including every device I observed into
> > alienware_quirks, which I can do but I want to know your opinion first.
> > ---
> >   drivers/platform/x86/dell/alienware-wmi.c | 42 +++++++++++++++++++++++
> >   1 file changed, 42 insertions(+)
> > 
> > diff --git a/drivers/platform/x86/dell/alienware-wmi.c b/drivers/platform/x86/dell/alienware-wmi.c
> > index 37a898273..a11ff4851 100644
> > --- a/drivers/platform/x86/dell/alienware-wmi.c
> > +++ b/drivers/platform/x86/dell/alienware-wmi.c
> > @@ -30,8 +30,11 @@
> >   #define WMAX_METHOD_DEEP_SLEEP_STATUS	0x0C
> >   #define WMAX_METHOD_THERMAL_INFORMATION	0x14
> >   #define WMAX_METHOD_THERMAL_CONTROL	0x15
> > +#define WMAX_METHOD_GMODE_STATUS	0x25
> > 
> > +#define WMAX_ARG_GET_DEFAULT_PROF	0x0A
> >   #define WMAX_ARG_GET_CURRENT_PROF	0x0B
> > +#define WMAX_ARG_GET_GMODE_STATUS	0x02
> > 
> >   #define WMAX_FAILURE_CODE		0xFFFFFFFF
> > 
> > @@ -968,6 +971,42 @@ static int thermal_profile_set_ustt(struct platform_profile_handler *pprof,
> >   	return 0;
> >   }
> > 
> > +static int autodetect_thermal_profile(void)
> > +{
> > +	acpi_status status;
> > +	u32 in_args;
> > +	u32 default_profile;
> > +	u32 gmode;
> > +
> > +	in_args = WMAX_ARG_GET_DEFAULT_PROF;
> > +	status = alienware_wmax_command(&in_args, sizeof(in_args),
> > +					WMAX_METHOD_THERMAL_INFORMATION, &default_profile);
> > +
> > +	if (ACPI_FAILURE(status))
> > +		return 0;
> > +
> > +	in_args = WMAX_ARG_GET_GMODE_STATUS;
> > +	status = alienware_wmax_command(&in_args, sizeof(in_args),
> > +					WMAX_METHOD_GMODE_STATUS, &gmode);
> > +
> > +	if (ACPI_FAILURE(status))
> > +		return 0;
> > +
> > +	if (default_profile == WMAX_THERMAL_BALANCED && gmode == 1) {
> > +		quirks->thermal = WMAX_THERMAL_TABLE_SIMPLE;
> > +		quirks->gmode = 1;
> > +		return 0;
> > +	}
> > +
> > +	if (default_profile == WMAX_THERMAL_USTT_BALANCED)
> > +		quirks->thermal = WMAX_THERMAL_TABLE_USTT;
> > +
> > +	if (gmode == 0 || gmode == 1)
> > +		quirks->gmode = 1;
> > +
> > +	return 0;
> > +}
> > +
> >   static int create_thermal_profile(void)
> >   {
> >   	pp_handler.profile_get = thermal_profile_get;
> > @@ -1050,6 +1089,9 @@ static int __init alienware_wmi_init(void)
> >   			goto fail_prep_deepsleep;
> >   	}
> > 
> > +	if (interface == WMAX && quirks == &quirk_unknown)
> > +		autodetect_thermal_profile();
> > +
> >   	if (quirks->thermal > 0) {
> >   		ret = create_thermal_profile();
> >   		if (ret)

^ permalink raw reply	[flat|nested] 15+ messages in thread

end of thread, other threads:[~2024-10-22  7:58 UTC | newest]

Thread overview: 15+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2024-10-17  8:12 [PATCH v6 0/5] Dell AWCC platform_profile support Kurt Borja
2024-10-17  8:13 ` [PATCH v6 1/5] alienware-wmi: fixed indentation and clean up Kurt Borja
2024-10-20 20:04   ` Armin Wolf
2024-10-17  8:14 ` [PATCH v6 2/5] alienware-wmi: alienware_wmax_command() is now input size agnostic Kurt Borja
2024-10-20 20:05   ` Armin Wolf
2024-10-17  8:15 ` [PATCH v6 3/5] alienware-wmi: added platform profile support Kurt Borja
2024-10-20 20:12   ` Armin Wolf
2024-10-22  7:35     ` Kurt Borja
2024-10-17  8:16 ` [PATCH v6 4/5] alienware-wmi: added autodetect_thermal_profile for devices with quirk_unknown Kurt Borja
2024-10-20 20:39   ` Armin Wolf
2024-10-20 20:40     ` Armin Wolf
2024-10-20 22:09       ` Armin Wolf
2024-10-22  7:58     ` Kurt Borja
2024-10-17  8:17 ` [PATCH v6 5/5] alienware-wmi: WMAX interface documentation Kurt Borja
2024-10-20 22:09   ` Armin Wolf

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox