public inbox for linux-kernel@vger.kernel.org
 help / color / mirror / Atom feed
From: Kurt Borja <kuurtb@gmail.com>
To: hdegoede@redhat.com, ilpo.jarvinen@linux.intel.com
Cc: platform-driver-x86@vger.kernel.org,
	linux-kernel@vger.kernel.org, Kurt Borja <kuurtb@gmail.com>
Subject: [PATCH] Dell AWCC platform_profile support
Date: Mon,  7 Oct 2024 06:33:26 -0300	[thread overview]
Message-ID: <20241007093324.49631-3-kuurtb@gmail.com> (raw)

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.

It is suspected that Alienware Command Center manages thermal profiles
through the WMI interface, specifically through a device with identifier
\_SB_.AMW1.WMAX. This device was reverse engineered and the relevant
functionality is documented here [1]. This driver interacts with this
WMI device and thus is able to mimic AWCC's thermal profiles functionality
through the platform_profile API. In consequence the user would be able
to set and retrieve thermal profiles, which are just fan speed profiles.

This driver was heavily inspired on inspur_platform_profile, special
thanks.

Notes:
 - Performance (FullSpeed) profile is a special profile which has it's own
   entry in the Firmware Settings of the Alienware x15 R1. It also changes
   the color of the F1 key. I suspect this behavior would be replicated in
   other X-Series or M-Series laptops.
 - G-Mode is a profile documented on [1] which mimics the behavior of
   FullSpeed mode but it does not have an entry on the Firmware Settings of
   the Alienware x15 R1, this may correspond to the G-Mode functionality on
   G-Series laptops (activated by a special button) but I cannot test it. I
   did not include this code in the driver as G-Mode causes unexpected
   behavior on X-Series laptops.

Thanks for your time and patiente in advance.

Regards,

Kurt

[1] https://gist.github.com/kuu-rt/b22328ff2b454be505387e2a38c61ee4

Signed-off-by: Kurt Borja <kuurtb@gmail.com>
---
 drivers/platform/x86/dell/Kconfig         |   9 +
 drivers/platform/x86/dell/Makefile        |   1 +
 drivers/platform/x86/dell/dell-wmi-awcc.c | 204 ++++++++++++++++++++++
 3 files changed, 214 insertions(+)
 create mode 100644 drivers/platform/x86/dell/dell-wmi-awcc.c

diff --git a/drivers/platform/x86/dell/Kconfig b/drivers/platform/x86/dell/Kconfig
index 68a49788a..20300ff98 100644
--- a/drivers/platform/x86/dell/Kconfig
+++ b/drivers/platform/x86/dell/Kconfig
@@ -27,6 +27,15 @@ config ALIENWARE_WMI
 	 zones on Alienware machines that don't contain a dedicated AlienFX
 	 USB MCU such as the X51 and X51-R2.
 
+config AWCC_PLATFORM_PROFILE
+	tristate "AWCC Platform Profile support"
+	depends on ACPI_WMI
+	select ACPI_PLATFORM_PROFILE
+	help
+	 This driver provides platform_profile support for selecting thermal
+	 profiles on Dell devices with User Selectable Thermal Tables,
+	 controlled by AWCC's WMI interface.
+
 config DCDBAS
 	tristate "Dell Systems Management Base Driver"
 	default m
diff --git a/drivers/platform/x86/dell/Makefile b/drivers/platform/x86/dell/Makefile
index 79d60f1bf..bfef99580 100644
--- a/drivers/platform/x86/dell/Makefile
+++ b/drivers/platform/x86/dell/Makefile
@@ -23,4 +23,5 @@ obj-$(CONFIG_DELL_WMI_AIO)		+= dell-wmi-aio.o
 obj-$(CONFIG_DELL_WMI_DESCRIPTOR)	+= dell-wmi-descriptor.o
 obj-$(CONFIG_DELL_WMI_DDV)		+= dell-wmi-ddv.o
 obj-$(CONFIG_DELL_WMI_LED)		+= dell-wmi-led.o
+obj-$(CONFIG_AWCC_PLATFORM_PROFILE)	+= dell-wmi-awcc.o
 obj-$(CONFIG_DELL_WMI_SYSMAN)		+= dell-wmi-sysman/
diff --git a/drivers/platform/x86/dell/dell-wmi-awcc.c b/drivers/platform/x86/dell/dell-wmi-awcc.c
new file mode 100644
index 000000000..0837d1bc6
--- /dev/null
+++ b/drivers/platform/x86/dell/dell-wmi-awcc.c
@@ -0,0 +1,204 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ *  WMI driver for Dell's AWCC platform_profile
+ *
+ *  Copyright (c) Kurt Borja <kuurtb@gmail.com>
+ *
+ */
+
+#include <linux/acpi.h>
+#include <linux/device.h>
+#include <linux/module.h>
+#include <linux/platform_profile.h>
+#include <linux/wmi.h>
+
+#define PROF_TO_ARG(mode) ((mode << 8) | 1)
+
+#define DELL_AWCC_GUID "A70591CE-A997-11DA-B012-B622A1EF5492"
+
+enum awcc_wmi_method {
+	AWCC_WMI_THERMAL_INFORMATION = 0x14,
+	AWCC_WMI_THERMAL_CONTROL = 0x15,
+};
+
+enum awcc_tmp_profile {
+	AWCC_TMP_PROFILE_BALANCED = 0xA0,
+	AWCC_TMP_PROFILE_BALANCED_PERFORMANCE = 0xA1,
+	AWCC_TMP_PROFILE_COOL = 0xA2,
+	AWCC_TMP_PROFILE_QUIET = 0xA3,
+	AWCC_TMP_PROFILE_PERFORMANCE = 0xA4,
+	AWCC_TMP_PROFILE_LOW_POWER = 0xA5,
+};
+
+struct awcc_wmi_priv {
+	struct wmi_device *wdev;
+	struct platform_profile_handler handler;
+};
+
+static int awcc_wmi_query(struct wmi_device *wdev, enum awcc_wmi_method method,
+			  u32 arg, u32 *res)
+{
+	struct acpi_buffer out = { ACPI_ALLOCATE_BUFFER, NULL };
+	const struct acpi_buffer in = { sizeof(arg), &arg };
+	union acpi_object *obj;
+	acpi_status status;
+	int ret = 0;
+
+	status = wmidev_evaluate_method(wdev, 0x0, method, &in, &out);
+
+	if (ACPI_FAILURE(status))
+		return -EIO;
+
+	obj = out.pointer;
+	if (!obj)
+		return -ENODATA;
+
+	if (obj->type != ACPI_TYPE_INTEGER) {
+		ret = -EINVAL;
+		goto out_free;
+	}
+
+	if (obj->integer.value <= U32_MAX)
+		*res = (u32)obj->integer.value;
+	else
+		ret = -ERANGE;
+
+out_free:
+	kfree(obj);
+
+	return ret;
+}
+
+static int awcc_platform_profile_get(struct platform_profile_handler *pprof,
+				     enum platform_profile_option *profile)
+{
+	struct awcc_wmi_priv *priv =
+		container_of(pprof, struct awcc_wmi_priv, handler);
+
+	u32 res;
+	int ret;
+
+	ret = awcc_wmi_query(priv->wdev, AWCC_WMI_THERMAL_INFORMATION, 0x0B,
+			     &res);
+
+	if (ret < 0)
+		return ret;
+
+	if (res < 0)
+		return -EBADRQC;
+
+	switch (res) {
+	case AWCC_TMP_PROFILE_LOW_POWER:
+		*profile = PLATFORM_PROFILE_LOW_POWER;
+		break;
+	case AWCC_TMP_PROFILE_QUIET:
+		*profile = PLATFORM_PROFILE_QUIET;
+		break;
+	case AWCC_TMP_PROFILE_BALANCED:
+		*profile = PLATFORM_PROFILE_BALANCED;
+		break;
+	case AWCC_TMP_PROFILE_BALANCED_PERFORMANCE:
+		*profile = PLATFORM_PROFILE_BALANCED_PERFORMANCE;
+		break;
+	case AWCC_TMP_PROFILE_PERFORMANCE:
+		*profile = PLATFORM_PROFILE_PERFORMANCE;
+		break;
+	default:
+		return -ENODATA;
+	}
+
+	return 0;
+}
+
+static int awcc_platform_profile_set(struct platform_profile_handler *pprof,
+				     enum platform_profile_option profile)
+{
+	struct awcc_wmi_priv *priv =
+		container_of(pprof, struct awcc_wmi_priv, handler);
+
+	u32 arg;
+	u32 res;
+	int ret;
+
+	switch (profile) {
+	case PLATFORM_PROFILE_LOW_POWER:
+		arg = PROF_TO_ARG(AWCC_TMP_PROFILE_LOW_POWER);
+		break;
+	case PLATFORM_PROFILE_QUIET:
+		arg = PROF_TO_ARG(AWCC_TMP_PROFILE_QUIET);
+		break;
+	case PLATFORM_PROFILE_BALANCED:
+		arg = PROF_TO_ARG(AWCC_TMP_PROFILE_BALANCED);
+		break;
+	case PLATFORM_PROFILE_BALANCED_PERFORMANCE:
+		arg = PROF_TO_ARG(AWCC_TMP_PROFILE_BALANCED_PERFORMANCE);
+		break;
+	case PLATFORM_PROFILE_PERFORMANCE:
+		arg = PROF_TO_ARG(AWCC_TMP_PROFILE_PERFORMANCE);
+		break;
+	default:
+		return -EOPNOTSUPP;
+	}
+
+	ret = awcc_wmi_query(priv->wdev, AWCC_WMI_THERMAL_CONTROL, arg, &res);
+
+	if (ret < 0)
+		return ret;
+
+	if (res < 0)
+		return -EBADRQC;
+
+	return 0;
+}
+
+static int awcc_wmi_probe(struct wmi_device *wdev, const void *context)
+{
+	struct awcc_wmi_priv *priv;
+
+	priv = devm_kzalloc(&wdev->dev, sizeof(*priv), GFP_KERNEL);
+	if (!priv)
+		return -ENOMEM;
+
+	priv->wdev = wdev;
+	dev_set_drvdata(&wdev->dev, priv);
+
+	priv->handler.profile_set = awcc_platform_profile_set;
+	priv->handler.profile_get = awcc_platform_profile_get;
+
+	set_bit(PLATFORM_PROFILE_LOW_POWER, priv->handler.choices);
+	set_bit(PLATFORM_PROFILE_QUIET, priv->handler.choices);
+	set_bit(PLATFORM_PROFILE_BALANCED, priv->handler.choices);
+	set_bit(PLATFORM_PROFILE_BALANCED_PERFORMANCE, priv->handler.choices);
+	set_bit(PLATFORM_PROFILE_PERFORMANCE, priv->handler.choices);
+
+	return platform_profile_register(&priv->handler);
+}
+
+static void awcc_wmi_remove(struct wmi_device *wdev)
+{
+	platform_profile_remove();
+}
+
+static const struct wmi_device_id awcc_wmi_id_table[] = {
+	{ .guid_string = DELL_AWCC_GUID },
+	{},
+};
+
+MODULE_DEVICE_TABLE(wmi, awcc_wmi_id_table);
+
+static struct wmi_driver awcc_wmi_driver = {
+	.driver = {
+		.name = "dell-wmi-awcc-platform-profile",
+		.probe_type = PROBE_PREFER_ASYNCHRONOUS,
+	},
+	.id_table = awcc_wmi_id_table,
+	.probe = awcc_wmi_probe,
+	.remove = awcc_wmi_remove,
+	.no_singleton = true,
+};
+
+module_wmi_driver(awcc_wmi_driver);
+
+MODULE_AUTHOR("Kurt Borja");
+MODULE_DESCRIPTION("Dell AWCC WMI driver");
+MODULE_LICENSE("GPL");
-- 
2.46.2


             reply	other threads:[~2024-10-07  9:41 UTC|newest]

Thread overview: 42+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2024-10-07  9:33 Kurt Borja [this message]
2024-10-07 12:24 ` [PATCH] Dell AWCC platform_profile support Armin Wolf
2024-10-07 17:24   ` Kurt Borja
2024-10-08  4:42 ` [PATCH v2] alienware-wmi: " Kurt Borja
2024-10-08 10:18   ` Ilpo Järvinen
2024-10-08 19:34     ` Kurt Borja
2024-10-08 19:37 ` [PATCH] " Kurt Borja
2024-10-08 20:04   ` Kurt Borja
2024-10-08 19:56 ` [PATCH v3] " Kurt Borja
2024-10-09  8:42   ` Armin Wolf
2024-10-09 14:48     ` Kurt Borja
2024-10-09 15:48       ` Armin Wolf
2024-10-09  8:56   ` Ilpo Järvinen
2024-10-09 15:39     ` Kurt Borja
2024-10-10  3:44   ` kernel test robot
2024-10-10  9:46     ` Ilpo Järvinen
2024-10-10 18:39       ` Kurt Borja
2024-10-15  1:35   ` kernel test robot
2024-10-11  6:40 ` [PATCH 0/4] " Kurt Borja
2024-10-11  6:43 ` [PATCH v4 " Kurt Borja
2024-10-11  6:46   ` [PATCH v4 1/4] alienware-wmi: fixed indentation and clean up Kurt Borja
2024-10-11 11:04     ` Ilpo Järvinen
2024-10-12  1:57       ` Kurt Borja
2024-10-11  6:47   ` [PATCH v4 2/4] alienware-wmi: alienware_wmax_command() is now input size agnostic Kurt Borja
2024-10-11 11:07     ` Ilpo Järvinen
2024-10-11  6:47   ` [PATCH v4 3/4] alienware-wmi: added platform profile support Kurt Borja
2024-10-11  6:48   ` [PATCH v4 4/4] alienware-wmi: WMAX interface documentation Kurt Borja
2024-10-11 11:11     ` Ilpo Järvinen
2024-10-12  1:58 ` [PATCH v5 0/4] Dell AWCC platform_profile support Kurt Borja
2024-10-12  2:01   ` [PATCH v5 1/4] alienware-wmi: fixed indentation and clean up Kurt Borja
2024-10-14 16:26     ` Armin Wolf
2024-10-15  2:12       ` Kurt Borja
2024-10-12  2:01   ` [PATCH v5 2/4] alienware-wmi: alienware_wmax_command() is now input size agnostic Kurt Borja
2024-10-14 16:30     ` Armin Wolf
2024-10-15  2:14       ` Kurt Borja
2024-10-12  2:02   ` [PATCH v5 3/4] alienware-wmi: added platform profile support Kurt Borja
2024-10-14 16:40     ` Armin Wolf
2024-10-15  2:22       ` Kurt Borja
2024-10-15  8:20     ` Ilpo Järvinen
2024-10-12  2:03   ` [PATCH v5 4/4] alienware-wmi: WMAX interface documentation Kurt Borja
2024-10-14 17:10     ` Armin Wolf
2024-10-15  2:26       ` Kurt Borja

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20241007093324.49631-3-kuurtb@gmail.com \
    --to=kuurtb@gmail.com \
    --cc=hdegoede@redhat.com \
    --cc=ilpo.jarvinen@linux.intel.com \
    --cc=linux-kernel@vger.kernel.org \
    --cc=platform-driver-x86@vger.kernel.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox