* [PATCH v6 00/12] platform/x86: alienware-wmi-wmax: HWMON support + DebugFS + Improvements
@ 2025-03-13 14:29 Kurt Borja
2025-03-13 14:30 ` [PATCH v6 07/12] platform/x86: alienware-wmi-wmax: Add HWMON support Kurt Borja
` (3 more replies)
0 siblings, 4 replies; 8+ messages in thread
From: Kurt Borja @ 2025-03-13 14:29 UTC (permalink / raw)
To: Ilpo Järvinen, Armin Wolf
Cc: Kurt Borja, Hans de Goede, platform-driver-x86,
Dell.Client.Kernel, linux-kernel, Guenter Roeck, Jean Delvare,
linux-hwmon, Bagas Sanjaya
Hi all,
This set mainly adds hwmon and manual fan control support (patches 7-8)
to the alienware-wmi driver, after some improvements.
Thank you for your feedback :)
---
Changes in v6:
[08/12]
- Define dev_pm_ops statically (kernel test robot)
Link to v5: https://lore.kernel.org/r/20250312-hwm-v5-0-deb15ff8f3c6@gmail.com
---
Kurt Borja (12):
platform/x86: alienware-wmi-wmax: Rename thermal related symbols
platform/x86: alienware-wmi-wmax: Refactor is_awcc_thermal_mode()
platform/x86: alienware-wmi-wmax: Improve internal AWCC API
platform/x86: alienware-wmi-wmax: Modify supported_thermal_profiles[]
platform/x86: alienware-wmi-wmax: Improve platform profile probe
platform/x86: alienware-wmi-wmax: Add support for the "custom" thermal profile
platform/x86: alienware-wmi-wmax: Add HWMON support
platform/x86: alienware-wmi-wmax: Add support for manual fan control
platform/x86: alienware-wmi-wmax: Add a DebugFS interface
Documentation: wmi: Improve and update alienware-wmi documentation
Documentation: admin-guide: laptops: Add documentation for alienware-wmi
Documentation: ABI: Add sysfs platform and debugfs ABI documentation for alienware-wmi
Documentation/ABI/testing/debugfs-alienware-wmi | 44 +
.../ABI/testing/sysfs-platform-alienware-wmi | 14 +
.../admin-guide/laptops/alienware-wmi.rst | 128 +++
Documentation/admin-guide/laptops/index.rst | 1 +
Documentation/wmi/devices/alienware-wmi.rst | 383 +++-----
MAINTAINERS | 3 +
drivers/platform/x86/dell/Kconfig | 1 +
drivers/platform/x86/dell/alienware-wmi-wmax.c | 1023 +++++++++++++++++---
8 files changed, 1187 insertions(+), 410 deletions(-)
---
base-commit: f895f2493098b862f1ada0568aba278e49bf05b4
change-id: 20250305-hwm-f7bd91902b57
Best regards,
--
~ Kurt
^ permalink raw reply [flat|nested] 8+ messages in thread
* [PATCH v6 07/12] platform/x86: alienware-wmi-wmax: Add HWMON support
2025-03-13 14:29 [PATCH v6 00/12] platform/x86: alienware-wmi-wmax: HWMON support + DebugFS + Improvements Kurt Borja
@ 2025-03-13 14:30 ` Kurt Borja
2025-03-13 14:30 ` [PATCH v6 08/12] platform/x86: alienware-wmi-wmax: Add support for manual fan control Kurt Borja
` (2 subsequent siblings)
3 siblings, 0 replies; 8+ messages in thread
From: Kurt Borja @ 2025-03-13 14:30 UTC (permalink / raw)
To: Ilpo Järvinen, Armin Wolf
Cc: Kurt Borja, Hans de Goede, platform-driver-x86,
Dell.Client.Kernel, linux-kernel, Guenter Roeck, Jean Delvare,
linux-hwmon
All models with the "AWCC" WMAX device support monitoring fan speed and
temperature sensors. Expose this feature through the HWMON interface.
Cc: Guenter Roeck <linux@roeck-us.net>
Cc: Jean Delvare <jdelvare@suse.com>
Cc: linux-hwmon@vger.kernel.org
Reviewed-by: Armin Wolf <W_Armin@gmx.de>
Signed-off-by: Kurt Borja <kuurtb@gmail.com>
---
drivers/platform/x86/dell/Kconfig | 1 +
drivers/platform/x86/dell/alienware-wmi-wmax.c | 396 +++++++++++++++++++++++++
2 files changed, 397 insertions(+)
diff --git a/drivers/platform/x86/dell/Kconfig b/drivers/platform/x86/dell/Kconfig
index f8a0dffcaab7c3b423472c5b9093011334a698c8..f7107b0c55f27c9be70f50b36dd2e7bab42a6960 100644
--- a/drivers/platform/x86/dell/Kconfig
+++ b/drivers/platform/x86/dell/Kconfig
@@ -22,6 +22,7 @@ config ALIENWARE_WMI
depends on DMI
depends on LEDS_CLASS
depends on NEW_LEDS
+ depends on HWMON
help
This is a driver for controlling Alienware WMI driven features.
diff --git a/drivers/platform/x86/dell/alienware-wmi-wmax.c b/drivers/platform/x86/dell/alienware-wmi-wmax.c
index 3b37e4456482bc284b8e867c1c5b6255fc6c8ef2..0c30ffb6091a15d81f4dc959dfd28417249d3dda 100644
--- a/drivers/platform/x86/dell/alienware-wmi-wmax.c
+++ b/drivers/platform/x86/dell/alienware-wmi-wmax.c
@@ -9,10 +9,13 @@
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
#include <linux/bitfield.h>
+#include <linux/bitmap.h>
#include <linux/bits.h>
#include <linux/dmi.h>
+#include <linux/hwmon.h>
#include <linux/moduleparam.h>
#include <linux/platform_profile.h>
+#include <linux/units.h>
#include <linux/wmi.h>
#include "alienware-wmi.h"
@@ -25,6 +28,7 @@
#define WMAX_METHOD_BRIGHTNESS 0x3
#define WMAX_METHOD_ZONE_CONTROL 0x4
+#define AWCC_METHOD_GET_FAN_SENSORS 0x13
#define AWCC_METHOD_THERMAL_INFORMATION 0x14
#define AWCC_METHOD_THERMAL_CONTROL 0x15
#define AWCC_METHOD_GAME_SHIFT_STATUS 0x25
@@ -38,6 +42,12 @@
/* Arbitrary limit based on supported models */
#define AWCC_MAX_RES_COUNT 16
+#define AWCC_ID_BITMAP_SIZE (U8_MAX + 1)
+#define AWCC_ID_BITMAP_LONGS BITS_TO_LONGS(AWCC_ID_BITMAP_SIZE)
+
+static bool force_hwmon;
+module_param_unsafe(force_hwmon, bool, 0);
+MODULE_PARM_DESC(force_hwmon, "Force probing for HWMON support without checking if the WMI backend is available");
static bool force_platform_profile;
module_param_unsafe(force_platform_profile, bool, 0);
@@ -48,16 +58,19 @@ module_param_unsafe(force_gmode, bool, 0);
MODULE_PARM_DESC(force_gmode, "Forces G-Mode when performance profile is selected");
struct awcc_quirks {
+ bool hwmon;
bool pprof;
bool gmode;
};
static struct awcc_quirks g_series_quirks = {
+ .hwmon = true,
.pprof = true,
.gmode = true,
};
static struct awcc_quirks generic_quirks = {
+ .hwmon = true,
.pprof = true,
.gmode = false,
};
@@ -155,9 +168,18 @@ static const struct dmi_system_id awcc_dmi_table[] __initconst = {
},
};
+enum AWCC_GET_FAN_SENSORS_OPERATIONS {
+ AWCC_OP_GET_TOTAL_FAN_TEMPS = 0x01,
+ AWCC_OP_GET_FAN_TEMP_ID = 0x02,
+};
+
enum AWCC_THERMAL_INFORMATION_OPERATIONS {
AWCC_OP_GET_SYSTEM_DESCRIPTION = 0x02,
AWCC_OP_GET_RESOURCE_ID = 0x03,
+ AWCC_OP_GET_TEMPERATURE = 0x04,
+ AWCC_OP_GET_FAN_RPM = 0x05,
+ AWCC_OP_GET_FAN_MIN_RPM = 0x08,
+ AWCC_OP_GET_FAN_MAX_RPM = 0x09,
AWCC_OP_GET_CURRENT_PROFILE = 0x0B,
};
@@ -180,6 +202,11 @@ enum AWCC_SPECIAL_THERMAL_CODES {
AWCC_SPECIAL_PROFILE_GMODE = 0xAB,
};
+enum AWCC_TEMP_SENSOR_TYPES {
+ AWCC_TEMP_SENSOR_CPU = 0x01,
+ AWCC_TEMP_SENSOR_GPU = 0x06,
+};
+
enum awcc_thermal_profile {
AWCC_PROFILE_USTT_BALANCED,
AWCC_PROFILE_USTT_BALANCED_PERFORMANCE,
@@ -216,6 +243,14 @@ struct wmax_u32_args {
u8 arg3;
};
+struct awcc_fan_data {
+ unsigned long auto_channels_temp;
+ const char *label;
+ u32 min_rpm;
+ u32 max_rpm;
+ u8 id;
+};
+
struct awcc_priv {
struct wmi_device *wdev;
union {
@@ -231,6 +266,10 @@ struct awcc_priv {
struct device *ppdev;
u8 supported_profiles[PLATFORM_PROFILE_LAST];
+
+ struct device *hwdev;
+ struct awcc_fan_data **fan_data;
+ unsigned long temp_sensors[AWCC_ID_BITMAP_LONGS];
};
static const enum platform_profile_option awcc_mode_to_platform_profile[AWCC_PROFILE_LAST] = {
@@ -495,6 +534,19 @@ static int __awcc_wmi_command(struct wmi_device *wdev, u32 method_id,
return 0;
}
+static inline int awcc_get_fan_sensors(struct wmi_device *wdev, u8 operation,
+ u8 fan_id, u8 index, u32 *out)
+{
+ struct wmax_u32_args args = {
+ .operation = operation,
+ .arg1 = fan_id,
+ .arg2 = index,
+ .arg3 = 0,
+ };
+
+ return __awcc_wmi_command(wdev, AWCC_METHOD_GET_FAN_SENSORS, &args, out);
+}
+
static inline int awcc_thermal_information(struct wmi_device *wdev, u8 operation,
u8 arg, u32 *out)
{
@@ -552,6 +604,30 @@ static inline int awcc_op_get_resource_id(struct wmi_device *wdev, u8 index, u32
return __awcc_wmi_command(wdev, AWCC_METHOD_THERMAL_INFORMATION, &args, out);
}
+static inline int awcc_op_get_fan_rpm(struct wmi_device *wdev, u8 fan_id, u32 *out)
+{
+ struct wmax_u32_args args = {
+ .operation = AWCC_OP_GET_FAN_RPM,
+ .arg1 = fan_id,
+ .arg2 = 0,
+ .arg3 = 0,
+ };
+
+ return __awcc_wmi_command(wdev, AWCC_METHOD_THERMAL_INFORMATION, &args, out);
+}
+
+static inline int awcc_op_get_temperature(struct wmi_device *wdev, u8 temp_id, u32 *out)
+{
+ struct wmax_u32_args args = {
+ .operation = AWCC_OP_GET_TEMPERATURE,
+ .arg1 = temp_id,
+ .arg2 = 0,
+ .arg3 = 0,
+ };
+
+ return __awcc_wmi_command(wdev, AWCC_METHOD_THERMAL_INFORMATION, &args, out);
+}
+
static inline int awcc_op_get_current_profile(struct wmi_device *wdev, u32 *out)
{
struct wmax_u32_args args = {
@@ -577,6 +653,313 @@ static inline int awcc_op_activate_profile(struct wmi_device *wdev, u8 profile)
return __awcc_wmi_command(wdev, AWCC_METHOD_THERMAL_CONTROL, &args, &out);
}
+/*
+ * HWMON
+ * - Provides temperature and fan speed monitoring as well as manual fan
+ * control
+ */
+static umode_t awcc_hwmon_is_visible(const void *drvdata, enum hwmon_sensor_types type,
+ u32 attr, int channel)
+{
+ const struct awcc_priv *priv = drvdata;
+ unsigned int temp_count;
+
+ switch (type) {
+ case hwmon_temp:
+ temp_count = bitmap_weight(priv->temp_sensors, AWCC_ID_BITMAP_SIZE);
+
+ return channel < temp_count ? 0444 : 0;
+ case hwmon_fan:
+ return channel < priv->fan_count ? 0444 : 0;
+ case hwmon_pwm:
+ return channel < priv->fan_count ? 0444 : 0;
+ default:
+ return 0;
+ }
+}
+
+static int awcc_hwmon_read(struct device *dev, enum hwmon_sensor_types type,
+ u32 attr, int channel, long *val)
+{
+ struct awcc_priv *priv = dev_get_drvdata(dev);
+ const struct awcc_fan_data *fan;
+ u32 state;
+ int ret;
+ u8 temp;
+
+ switch (type) {
+ case hwmon_temp:
+ temp = find_nth_bit(priv->temp_sensors, AWCC_ID_BITMAP_SIZE, channel);
+
+ switch (attr) {
+ case hwmon_temp_input:
+ ret = awcc_op_get_temperature(priv->wdev, temp, &state);
+ if (ret)
+ return ret;
+
+ *val = state * MILLIDEGREE_PER_DEGREE;
+ break;
+ default:
+ return -EOPNOTSUPP;
+ }
+
+ break;
+ case hwmon_fan:
+ fan = priv->fan_data[channel];
+
+ switch (attr) {
+ case hwmon_fan_input:
+ ret = awcc_op_get_fan_rpm(priv->wdev, fan->id, &state);
+ if (ret)
+ return ret;
+
+ *val = state;
+ break;
+ case hwmon_fan_min:
+ *val = fan->min_rpm;
+ break;
+ case hwmon_fan_max:
+ *val = fan->max_rpm;
+ break;
+ default:
+ return -EOPNOTSUPP;
+ }
+
+ break;
+ case hwmon_pwm:
+ fan = priv->fan_data[channel];
+
+ switch (attr) {
+ case hwmon_pwm_auto_channels_temp:
+ *val = fan->auto_channels_temp;
+ break;
+ default:
+ return -EOPNOTSUPP;
+ }
+
+ break;
+ default:
+ return -EOPNOTSUPP;
+ }
+
+ return 0;
+}
+
+static int awcc_hwmon_read_string(struct device *dev, enum hwmon_sensor_types type,
+ u32 attr, int channel, const char **str)
+{
+ struct awcc_priv *priv = dev_get_drvdata(dev);
+ u8 temp;
+
+ switch (type) {
+ case hwmon_temp:
+ temp = find_nth_bit(priv->temp_sensors, AWCC_ID_BITMAP_SIZE, channel);
+
+ switch (temp) {
+ case AWCC_TEMP_SENSOR_CPU:
+ *str = "CPU";
+ break;
+ case AWCC_TEMP_SENSOR_GPU:
+ *str = "GPU";
+ break;
+ default:
+ *str = "Unknown";
+ break;
+ }
+
+ break;
+ case hwmon_fan:
+ *str = priv->fan_data[channel]->label;
+ break;
+ default:
+ return -EOPNOTSUPP;
+ }
+
+ return 0;
+}
+
+static const struct hwmon_ops awcc_hwmon_ops = {
+ .is_visible = awcc_hwmon_is_visible,
+ .read = awcc_hwmon_read,
+ .read_string = awcc_hwmon_read_string,
+};
+
+static const struct hwmon_channel_info * const awcc_hwmon_info[] = {
+ HWMON_CHANNEL_INFO(temp,
+ HWMON_T_LABEL | HWMON_T_INPUT,
+ HWMON_T_LABEL | HWMON_T_INPUT,
+ HWMON_T_LABEL | HWMON_T_INPUT,
+ HWMON_T_LABEL | HWMON_T_INPUT,
+ HWMON_T_LABEL | HWMON_T_INPUT,
+ HWMON_T_LABEL | HWMON_T_INPUT
+ ),
+ HWMON_CHANNEL_INFO(fan,
+ HWMON_F_LABEL | HWMON_F_INPUT | HWMON_F_MIN | HWMON_F_MAX,
+ HWMON_F_LABEL | HWMON_F_INPUT | HWMON_F_MIN | HWMON_F_MAX,
+ HWMON_F_LABEL | HWMON_F_INPUT | HWMON_F_MIN | HWMON_F_MAX,
+ HWMON_F_LABEL | HWMON_F_INPUT | HWMON_F_MIN | HWMON_F_MAX,
+ HWMON_F_LABEL | HWMON_F_INPUT | HWMON_F_MIN | HWMON_F_MAX,
+ HWMON_F_LABEL | HWMON_F_INPUT | HWMON_F_MIN | HWMON_F_MAX
+ ),
+ HWMON_CHANNEL_INFO(pwm,
+ HWMON_PWM_AUTO_CHANNELS_TEMP,
+ HWMON_PWM_AUTO_CHANNELS_TEMP,
+ HWMON_PWM_AUTO_CHANNELS_TEMP,
+ HWMON_PWM_AUTO_CHANNELS_TEMP,
+ HWMON_PWM_AUTO_CHANNELS_TEMP,
+ HWMON_PWM_AUTO_CHANNELS_TEMP
+ ),
+ NULL
+};
+
+static const struct hwmon_chip_info awcc_hwmon_chip_info = {
+ .ops = &awcc_hwmon_ops,
+ .info = awcc_hwmon_info,
+};
+
+static int awcc_hwmon_temps_init(struct wmi_device *wdev)
+{
+ struct awcc_priv *priv = dev_get_drvdata(&wdev->dev);
+ unsigned int i;
+ int ret;
+ u32 id;
+
+ for (i = 0; i < priv->temp_count; i++) {
+ /*
+ * Temperature sensors IDs are listed after the fan IDs at
+ * offset `fan_count`
+ */
+ ret = awcc_op_get_resource_id(wdev, i + priv->fan_count, &id);
+ if (ret)
+ return ret;
+
+ __set_bit(FIELD_GET(AWCC_RESOURCE_ID_MASK, id), priv->temp_sensors);
+ }
+
+ return 0;
+}
+
+static char *awcc_get_fan_label(struct device *dev, u32 temp_count, u8 temp_id)
+{
+ char *label;
+
+ switch (temp_count) {
+ case 0:
+ label = "Independent Fan";
+ break;
+ case 1:
+ switch (temp_id) {
+ case AWCC_TEMP_SENSOR_CPU:
+ label = "Processor Fan";
+ break;
+ case AWCC_TEMP_SENSOR_GPU:
+ label = "Video Fan";
+ break;
+ default:
+ label = "Unknown Fan";
+ break;
+ }
+
+ break;
+ default:
+ label = "Shared Fan";
+ break;
+ }
+
+ return label;
+}
+
+static int awcc_hwmon_fans_init(struct wmi_device *wdev)
+{
+ struct awcc_priv *priv = dev_get_drvdata(&wdev->dev);
+ u32 id, min_rpm, max_rpm, temp_count, temp_id;
+ unsigned long fan_temps[AWCC_ID_BITMAP_LONGS];
+ unsigned long gather[AWCC_ID_BITMAP_LONGS];
+ struct awcc_fan_data *fan_data;
+ unsigned int i, j;
+ char *label;
+ int ret;
+
+ for (i = 0; i < priv->fan_count; i++) {
+ fan_data = devm_kzalloc(&wdev->dev, sizeof(*fan_data), GFP_KERNEL);
+ if (!fan_data)
+ return -ENOMEM;
+
+ /*
+ * Fan IDs are listed first at offset 0
+ */
+ ret = awcc_op_get_resource_id(wdev, i, &id);
+ if (ret)
+ return ret;
+ id = FIELD_GET(AWCC_RESOURCE_ID_MASK, id);
+
+ ret = awcc_thermal_information(wdev, AWCC_OP_GET_FAN_MIN_RPM, id,
+ &min_rpm);
+ if (ret)
+ return ret;
+
+ ret = awcc_thermal_information(wdev, AWCC_OP_GET_FAN_MAX_RPM, id,
+ &max_rpm);
+ if (ret)
+ return ret;
+
+ ret = awcc_get_fan_sensors(wdev, AWCC_OP_GET_TOTAL_FAN_TEMPS, id,
+ 0, &temp_count);
+ if (ret)
+ return ret;
+
+ for (j = 0; j < temp_count; j++) {
+ ret = awcc_get_fan_sensors(wdev, AWCC_OP_GET_FAN_TEMP_ID,
+ id, j, &temp_id);
+ if (ret)
+ break;
+
+ temp_id = FIELD_GET(AWCC_RESOURCE_ID_MASK, temp_id);
+ __set_bit(temp_id, fan_temps);
+ }
+
+ label = awcc_get_fan_label(&wdev->dev, temp_count, temp_id);
+ if (!label)
+ return -ENOMEM;
+
+ fan_data->id = id;
+ fan_data->min_rpm = min_rpm;
+ fan_data->max_rpm = max_rpm;
+ fan_data->label = label;
+ bitmap_gather(gather, fan_temps, priv->temp_sensors, AWCC_ID_BITMAP_SIZE);
+ bitmap_copy(&fan_data->auto_channels_temp, gather, BITS_PER_LONG);
+ priv->fan_data[i] = fan_data;
+
+ bitmap_zero(fan_temps, AWCC_ID_BITMAP_SIZE);
+ }
+
+ return 0;
+}
+
+static int awcc_hwmon_init(struct wmi_device *wdev)
+{
+ struct awcc_priv *priv = dev_get_drvdata(&wdev->dev);
+ int ret;
+
+ priv->fan_data = devm_kcalloc(&wdev->dev, priv->fan_count,
+ sizeof(*priv->fan_data), GFP_KERNEL);
+ if (!priv->fan_data)
+ return -ENOMEM;
+
+ ret = awcc_hwmon_temps_init(wdev);
+ if (ret)
+ return ret;
+
+ ret = awcc_hwmon_fans_init(wdev);
+ if (ret)
+ return ret;
+
+ priv->hwdev = devm_hwmon_device_register_with_info(&wdev->dev, "alienware_wmi", priv,
+ &awcc_hwmon_chip_info, NULL);
+
+ return PTR_ERR_OR_ZERO(priv->hwdev);
+}
+
/*
* Thermal Profile control
* - Provides thermal profile control through the Platform Profile API
@@ -744,6 +1127,12 @@ static int alienware_awcc_setup(struct wmi_device *wdev)
priv->wdev = wdev;
dev_set_drvdata(&wdev->dev, priv);
+ if (awcc->hwmon) {
+ ret = awcc_hwmon_init(wdev);
+ if (ret)
+ return ret;
+ }
+
if (awcc->pprof) {
ret = awcc_platform_profile_init(wdev);
if (ret)
@@ -824,6 +1213,13 @@ int __init alienware_wmax_wmi_init(void)
if (id)
awcc = id->driver_data;
+ if (force_hwmon) {
+ if (!awcc)
+ awcc = &empty_quirks;
+
+ awcc->hwmon = true;
+ }
+
if (force_platform_profile) {
if (!awcc)
awcc = &empty_quirks;
--
2.48.1
^ permalink raw reply related [flat|nested] 8+ messages in thread
* [PATCH v6 08/12] platform/x86: alienware-wmi-wmax: Add support for manual fan control
2025-03-13 14:29 [PATCH v6 00/12] platform/x86: alienware-wmi-wmax: HWMON support + DebugFS + Improvements Kurt Borja
2025-03-13 14:30 ` [PATCH v6 07/12] platform/x86: alienware-wmi-wmax: Add HWMON support Kurt Borja
@ 2025-03-13 14:30 ` Kurt Borja
2025-03-28 16:15 ` Ilpo Järvinen
2025-03-17 0:32 ` [PATCH v6 00/12] platform/x86: alienware-wmi-wmax: HWMON support + DebugFS + Improvements Armin Wolf
2025-03-25 20:14 ` Kurt Borja
3 siblings, 1 reply; 8+ messages in thread
From: Kurt Borja @ 2025-03-13 14:30 UTC (permalink / raw)
To: Ilpo Järvinen, Armin Wolf
Cc: Kurt Borja, Hans de Goede, platform-driver-x86,
Dell.Client.Kernel, linux-kernel, Guenter Roeck, Jean Delvare,
linux-hwmon
All models with the "AWCC" WMAX device support a way of manually
controlling fans.
The PWM duty cycle of a fan can't be controlled directly. Instead the
AWCC interface let's us tune a fan `boost` value, which has the
following empirically discovered, aproximate behavior over the PWM
value:
pwm = pwm_base + (fan_boost / 255) * (pwm_max - pwm_base)
Where the pwm_base is the locked PWM value controlled by the FW and
fan_boost is a value between 0 and 255.
Expose this fan_boost knob as a custom HWMON attribute.
Cc: Guenter Roeck <linux@roeck-us.net>
Cc: Jean Delvare <jdelvare@suse.com>
Cc: linux-hwmon@vger.kernel.org
Reviewed-by: Armin Wolf <W_Armin@gmx.de>
Signed-off-by: Kurt Borja <kuurtb@gmail.com>
---
drivers/platform/x86/dell/alienware-wmi-wmax.c | 174 ++++++++++++++++++++++++-
1 file changed, 172 insertions(+), 2 deletions(-)
diff --git a/drivers/platform/x86/dell/alienware-wmi-wmax.c b/drivers/platform/x86/dell/alienware-wmi-wmax.c
index 0c30ffb6091a15d81f4dc959dfd28417249d3dda..823b579260555085ef6ac793b806738a756bb9da 100644
--- a/drivers/platform/x86/dell/alienware-wmi-wmax.c
+++ b/drivers/platform/x86/dell/alienware-wmi-wmax.c
@@ -13,8 +13,12 @@
#include <linux/bits.h>
#include <linux/dmi.h>
#include <linux/hwmon.h>
+#include <linux/hwmon-sysfs.h>
+#include <linux/kstrtox.h>
+#include <linux/minmax.h>
#include <linux/moduleparam.h>
#include <linux/platform_profile.h>
+#include <linux/pm.h>
#include <linux/units.h>
#include <linux/wmi.h>
#include "alienware-wmi.h"
@@ -181,10 +185,12 @@ enum AWCC_THERMAL_INFORMATION_OPERATIONS {
AWCC_OP_GET_FAN_MIN_RPM = 0x08,
AWCC_OP_GET_FAN_MAX_RPM = 0x09,
AWCC_OP_GET_CURRENT_PROFILE = 0x0B,
+ AWCC_OP_GET_FAN_BOOST = 0x0C,
};
enum AWCC_THERMAL_CONTROL_OPERATIONS {
AWCC_OP_ACTIVATE_PROFILE = 0x01,
+ AWCC_OP_SET_FAN_BOOST = 0x02,
};
enum AWCC_GAME_SHIFT_STATUS_OPERATIONS {
@@ -248,6 +254,7 @@ struct awcc_fan_data {
const char *label;
u32 min_rpm;
u32 max_rpm;
+ u8 suspend_cache;
u8 id;
};
@@ -628,6 +635,18 @@ static inline int awcc_op_get_temperature(struct wmi_device *wdev, u8 temp_id, u
return __awcc_wmi_command(wdev, AWCC_METHOD_THERMAL_INFORMATION, &args, out);
}
+static inline int awcc_op_get_fan_boost(struct wmi_device *wdev, u8 fan_id, u32 *out)
+{
+ struct wmax_u32_args args = {
+ .operation = AWCC_OP_GET_FAN_BOOST,
+ .arg1 = fan_id,
+ .arg2 = 0,
+ .arg3 = 0,
+ };
+
+ return __awcc_wmi_command(wdev, AWCC_METHOD_THERMAL_INFORMATION, &args, out);
+}
+
static inline int awcc_op_get_current_profile(struct wmi_device *wdev, u32 *out)
{
struct wmax_u32_args args = {
@@ -653,6 +672,19 @@ static inline int awcc_op_activate_profile(struct wmi_device *wdev, u8 profile)
return __awcc_wmi_command(wdev, AWCC_METHOD_THERMAL_CONTROL, &args, &out);
}
+static inline int awcc_op_set_fan_boost(struct wmi_device *wdev, u8 fan_id, u8 boost)
+{
+ struct wmax_u32_args args = {
+ .operation = AWCC_OP_SET_FAN_BOOST,
+ .arg1 = fan_id,
+ .arg2 = boost,
+ .arg3 = 0,
+ };
+ u32 out;
+
+ return __awcc_wmi_command(wdev, AWCC_METHOD_THERMAL_CONTROL, &args, &out);
+}
+
/*
* HWMON
* - Provides temperature and fan speed monitoring as well as manual fan
@@ -817,6 +849,81 @@ static const struct hwmon_chip_info awcc_hwmon_chip_info = {
.info = awcc_hwmon_info,
};
+static ssize_t fan_boost_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct awcc_priv *priv = dev_get_drvdata(dev);
+ int index = to_sensor_dev_attr(attr)->index;
+ struct awcc_fan_data *fan = priv->fan_data[index];
+ u32 boost;
+ int ret;
+
+ ret = awcc_op_get_fan_boost(priv->wdev, fan->id, &boost);
+ if (ret)
+ return ret;
+
+ return sysfs_emit(buf, "%u\n", boost);
+}
+
+static ssize_t fan_boost_store(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct awcc_priv *priv = dev_get_drvdata(dev);
+ int index = to_sensor_dev_attr(attr)->index;
+ struct awcc_fan_data *fan = priv->fan_data[index];
+ unsigned long val;
+ int ret;
+
+ ret = kstrtoul(buf, 0, &val);
+ if (ret)
+ return ret;
+
+ ret = awcc_op_set_fan_boost(priv->wdev, fan->id, clamp_val(val, 0, 255));
+
+ return ret ? ret : count;
+}
+
+static SENSOR_DEVICE_ATTR_RW(fan1_boost, fan_boost, 0);
+static SENSOR_DEVICE_ATTR_RW(fan2_boost, fan_boost, 1);
+static SENSOR_DEVICE_ATTR_RW(fan3_boost, fan_boost, 2);
+static SENSOR_DEVICE_ATTR_RW(fan4_boost, fan_boost, 3);
+static SENSOR_DEVICE_ATTR_RW(fan5_boost, fan_boost, 4);
+static SENSOR_DEVICE_ATTR_RW(fan6_boost, fan_boost, 5);
+
+static umode_t fan_boost_attr_visible(struct kobject *kobj, struct attribute *attr, int n)
+{
+ struct awcc_priv *priv = dev_get_drvdata(kobj_to_dev(kobj));
+
+ return n < priv->fan_count ? attr->mode : 0;
+}
+
+static bool fan_boost_group_visible(struct kobject *kobj)
+{
+ return true;
+}
+
+DEFINE_SYSFS_GROUP_VISIBLE(fan_boost);
+
+static struct attribute *fan_boost_attrs[] = {
+ &sensor_dev_attr_fan1_boost.dev_attr.attr,
+ &sensor_dev_attr_fan2_boost.dev_attr.attr,
+ &sensor_dev_attr_fan3_boost.dev_attr.attr,
+ &sensor_dev_attr_fan4_boost.dev_attr.attr,
+ &sensor_dev_attr_fan5_boost.dev_attr.attr,
+ &sensor_dev_attr_fan6_boost.dev_attr.attr,
+ NULL
+};
+
+static const struct attribute_group fan_boost_group = {
+ .attrs = fan_boost_attrs,
+ .is_visible = SYSFS_GROUP_VISIBLE(fan_boost),
+};
+
+static const struct attribute_group *awcc_hwmon_groups[] = {
+ &fan_boost_group,
+ NULL
+};
+
static int awcc_hwmon_temps_init(struct wmi_device *wdev)
{
struct awcc_priv *priv = dev_get_drvdata(&wdev->dev);
@@ -954,12 +1061,56 @@ static int awcc_hwmon_init(struct wmi_device *wdev)
if (ret)
return ret;
- priv->hwdev = devm_hwmon_device_register_with_info(&wdev->dev, "alienware_wmi", priv,
- &awcc_hwmon_chip_info, NULL);
+ priv->hwdev = devm_hwmon_device_register_with_info(&wdev->dev, "alienware_wmi",
+ priv, &awcc_hwmon_chip_info,
+ awcc_hwmon_groups);
return PTR_ERR_OR_ZERO(priv->hwdev);
}
+static void awcc_hwmon_suspend(struct device *dev)
+{
+ struct awcc_priv *priv = dev_get_drvdata(dev);
+ struct awcc_fan_data *fan;
+ unsigned int i;
+ u32 boost;
+ int ret;
+
+ for (i = 0; i < priv->fan_count; i++) {
+ fan = priv->fan_data[i];
+
+ ret = awcc_thermal_information(priv->wdev, AWCC_OP_GET_FAN_BOOST,
+ fan->id, &boost);
+ if (ret)
+ dev_err(dev, "Failed to store Fan %u boost while suspending\n", i);
+
+ fan->suspend_cache = ret ? 0 : clamp_val(boost, 0, 255);
+
+ awcc_op_set_fan_boost(priv->wdev, fan->id, 0);
+ if (ret)
+ dev_err(dev, "Failed to set Fan %u boost to 0 while suspending\n", i);
+ }
+}
+
+static void awcc_hwmon_resume(struct device *dev)
+{
+ struct awcc_priv *priv = dev_get_drvdata(dev);
+ struct awcc_fan_data *fan;
+ unsigned int i;
+ int ret;
+
+ for (i = 0; i < priv->fan_count; i++) {
+ fan = priv->fan_data[i];
+
+ if (!fan->suspend_cache)
+ continue;
+
+ ret = awcc_op_set_fan_boost(priv->wdev, fan->id, fan->suspend_cache);
+ if (ret)
+ dev_err(dev, "Failed to restore Fan %u boost while resuming\n", i);
+ }
+}
+
/*
* Thermal Profile control
* - Provides thermal profile control through the Platform Profile API
@@ -1189,6 +1340,24 @@ static int wmax_wmi_probe(struct wmi_device *wdev, const void *context)
return ret;
}
+static int wmax_wmi_suspend(struct device *dev)
+{
+ if (awcc->hwmon)
+ awcc_hwmon_suspend(dev);
+
+ return 0;
+}
+
+static int wmax_wmi_resume(struct device *dev)
+{
+ if (awcc->hwmon)
+ awcc_hwmon_resume(dev);
+
+ return 0;
+}
+
+static DEFINE_SIMPLE_DEV_PM_OPS(wmax_wmi_pm_ops, wmax_wmi_suspend, wmax_wmi_resume);
+
static const struct wmi_device_id alienware_wmax_device_id_table[] = {
{ WMAX_CONTROL_GUID, NULL },
{ },
@@ -1199,6 +1368,7 @@ static struct wmi_driver alienware_wmax_wmi_driver = {
.driver = {
.name = "alienware-wmi-wmax",
.probe_type = PROBE_PREFER_ASYNCHRONOUS,
+ .pm = pm_sleep_ptr(&wmax_wmi_pm_ops),
},
.id_table = alienware_wmax_device_id_table,
.probe = wmax_wmi_probe,
--
2.48.1
^ permalink raw reply related [flat|nested] 8+ messages in thread
* Re: [PATCH v6 00/12] platform/x86: alienware-wmi-wmax: HWMON support + DebugFS + Improvements
2025-03-13 14:29 [PATCH v6 00/12] platform/x86: alienware-wmi-wmax: HWMON support + DebugFS + Improvements Kurt Borja
2025-03-13 14:30 ` [PATCH v6 07/12] platform/x86: alienware-wmi-wmax: Add HWMON support Kurt Borja
2025-03-13 14:30 ` [PATCH v6 08/12] platform/x86: alienware-wmi-wmax: Add support for manual fan control Kurt Borja
@ 2025-03-17 0:32 ` Armin Wolf
2025-03-25 20:14 ` Kurt Borja
3 siblings, 0 replies; 8+ messages in thread
From: Armin Wolf @ 2025-03-17 0:32 UTC (permalink / raw)
To: Kurt Borja, Ilpo Järvinen
Cc: Hans de Goede, platform-driver-x86, Dell.Client.Kernel,
linux-kernel, Guenter Roeck, Jean Delvare, linux-hwmon,
Bagas Sanjaya
Am 13.03.25 um 15:29 schrieb Kurt Borja:
> Hi all,
>
> This set mainly adds hwmon and manual fan control support (patches 7-8)
> to the alienware-wmi driver, after some improvements.
>
> Thank you for your feedback :)
From my perspective the whole series is ready for inclusion into the mainline kernel.
Thanks,
Armin Wolf
> ---
> Changes in v6:
>
> [08/12]
> - Define dev_pm_ops statically (kernel test robot)
>
> Link to v5: https://lore.kernel.org/r/20250312-hwm-v5-0-deb15ff8f3c6@gmail.com
>
> ---
> Kurt Borja (12):
> platform/x86: alienware-wmi-wmax: Rename thermal related symbols
> platform/x86: alienware-wmi-wmax: Refactor is_awcc_thermal_mode()
> platform/x86: alienware-wmi-wmax: Improve internal AWCC API
> platform/x86: alienware-wmi-wmax: Modify supported_thermal_profiles[]
> platform/x86: alienware-wmi-wmax: Improve platform profile probe
> platform/x86: alienware-wmi-wmax: Add support for the "custom" thermal profile
> platform/x86: alienware-wmi-wmax: Add HWMON support
> platform/x86: alienware-wmi-wmax: Add support for manual fan control
> platform/x86: alienware-wmi-wmax: Add a DebugFS interface
> Documentation: wmi: Improve and update alienware-wmi documentation
> Documentation: admin-guide: laptops: Add documentation for alienware-wmi
> Documentation: ABI: Add sysfs platform and debugfs ABI documentation for alienware-wmi
>
> Documentation/ABI/testing/debugfs-alienware-wmi | 44 +
> .../ABI/testing/sysfs-platform-alienware-wmi | 14 +
> .../admin-guide/laptops/alienware-wmi.rst | 128 +++
> Documentation/admin-guide/laptops/index.rst | 1 +
> Documentation/wmi/devices/alienware-wmi.rst | 383 +++-----
> MAINTAINERS | 3 +
> drivers/platform/x86/dell/Kconfig | 1 +
> drivers/platform/x86/dell/alienware-wmi-wmax.c | 1023 +++++++++++++++++---
> 8 files changed, 1187 insertions(+), 410 deletions(-)
> ---
> base-commit: f895f2493098b862f1ada0568aba278e49bf05b4
> change-id: 20250305-hwm-f7bd91902b57
>
> Best regards,
^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: [PATCH v6 00/12] platform/x86: alienware-wmi-wmax: HWMON support + DebugFS + Improvements
2025-03-13 14:29 [PATCH v6 00/12] platform/x86: alienware-wmi-wmax: HWMON support + DebugFS + Improvements Kurt Borja
` (2 preceding siblings ...)
2025-03-17 0:32 ` [PATCH v6 00/12] platform/x86: alienware-wmi-wmax: HWMON support + DebugFS + Improvements Armin Wolf
@ 2025-03-25 20:14 ` Kurt Borja
2025-03-26 8:34 ` Ilpo Järvinen
3 siblings, 1 reply; 8+ messages in thread
From: Kurt Borja @ 2025-03-25 20:14 UTC (permalink / raw)
To: Kurt Borja, Ilpo Järvinen, Armin Wolf
Cc: Hans de Goede, platform-driver-x86, Dell.Client.Kernel,
linux-kernel, Guenter Roeck, Jean Delvare, linux-hwmon,
Bagas Sanjaya
On Thu Mar 13, 2025 at 11:29 AM -03, Kurt Borja wrote:
> Hi all,
>
> This set mainly adds hwmon and manual fan control support (patches 7-8)
> to the alienware-wmi driver, after some improvements.
>
> Thank you for your feedback :)
>
> ---
> Changes in v6:
>
> [08/12]
> - Define dev_pm_ops statically (kernel test robot)
>
> Link to v5: https://lore.kernel.org/r/20250312-hwm-v5-0-deb15ff8f3c6@gmail.com
>
> ---
> Kurt Borja (12):
> platform/x86: alienware-wmi-wmax: Rename thermal related symbols
> platform/x86: alienware-wmi-wmax: Refactor is_awcc_thermal_mode()
> platform/x86: alienware-wmi-wmax: Improve internal AWCC API
> platform/x86: alienware-wmi-wmax: Modify supported_thermal_profiles[]
> platform/x86: alienware-wmi-wmax: Improve platform profile probe
> platform/x86: alienware-wmi-wmax: Add support for the "custom" thermal profile
> platform/x86: alienware-wmi-wmax: Add HWMON support
> platform/x86: alienware-wmi-wmax: Add support for manual fan control
> platform/x86: alienware-wmi-wmax: Add a DebugFS interface
> Documentation: wmi: Improve and update alienware-wmi documentation
> Documentation: admin-guide: laptops: Add documentation for alienware-wmi
> Documentation: ABI: Add sysfs platform and debugfs ABI documentation for alienware-wmi
>
> Documentation/ABI/testing/debugfs-alienware-wmi | 44 +
> .../ABI/testing/sysfs-platform-alienware-wmi | 14 +
> .../admin-guide/laptops/alienware-wmi.rst | 128 +++
> Documentation/admin-guide/laptops/index.rst | 1 +
> Documentation/wmi/devices/alienware-wmi.rst | 383 +++-----
> MAINTAINERS | 3 +
> drivers/platform/x86/dell/Kconfig | 1 +
> drivers/platform/x86/dell/alienware-wmi-wmax.c | 1023 +++++++++++++++++---
> 8 files changed, 1187 insertions(+), 410 deletions(-)
> ---
> base-commit: f895f2493098b862f1ada0568aba278e49bf05b4
> change-id: 20250305-hwm-f7bd91902b57
>
> Best regards,
Hi Ilpo,
Is there still a chance for this to go into v6.15? or are you planning
to review it on the next cycle?
Thank you either way!
--
~ Kurt
^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: [PATCH v6 00/12] platform/x86: alienware-wmi-wmax: HWMON support + DebugFS + Improvements
2025-03-25 20:14 ` Kurt Borja
@ 2025-03-26 8:34 ` Ilpo Järvinen
2025-03-26 14:11 ` Kurt Borja
0 siblings, 1 reply; 8+ messages in thread
From: Ilpo Järvinen @ 2025-03-26 8:34 UTC (permalink / raw)
To: Kurt Borja
Cc: Armin Wolf, Hans de Goede, platform-driver-x86,
Dell.Client.Kernel, LKML, Guenter Roeck, Jean Delvare,
linux-hwmon, Bagas Sanjaya
On Tue, 25 Mar 2025, Kurt Borja wrote:
> On Thu Mar 13, 2025 at 11:29 AM -03, Kurt Borja wrote:
> > Hi all,
> >
> > This set mainly adds hwmon and manual fan control support (patches 7-8)
> > to the alienware-wmi driver, after some improvements.
> >
> > Thank you for your feedback :)
> >
> > ---
> > Changes in v6:
> >
> > [08/12]
> > - Define dev_pm_ops statically (kernel test robot)
> >
> > Link to v5: https://lore.kernel.org/r/20250312-hwm-v5-0-deb15ff8f3c6@gmail.com
> >
> > ---
> > Kurt Borja (12):
> > platform/x86: alienware-wmi-wmax: Rename thermal related symbols
> > platform/x86: alienware-wmi-wmax: Refactor is_awcc_thermal_mode()
> > platform/x86: alienware-wmi-wmax: Improve internal AWCC API
> > platform/x86: alienware-wmi-wmax: Modify supported_thermal_profiles[]
> > platform/x86: alienware-wmi-wmax: Improve platform profile probe
> > platform/x86: alienware-wmi-wmax: Add support for the "custom" thermal profile
> > platform/x86: alienware-wmi-wmax: Add HWMON support
> > platform/x86: alienware-wmi-wmax: Add support for manual fan control
> > platform/x86: alienware-wmi-wmax: Add a DebugFS interface
> > Documentation: wmi: Improve and update alienware-wmi documentation
> > Documentation: admin-guide: laptops: Add documentation for alienware-wmi
> > Documentation: ABI: Add sysfs platform and debugfs ABI documentation for alienware-wmi
> >
> > Documentation/ABI/testing/debugfs-alienware-wmi | 44 +
> > .../ABI/testing/sysfs-platform-alienware-wmi | 14 +
> > .../admin-guide/laptops/alienware-wmi.rst | 128 +++
> > Documentation/admin-guide/laptops/index.rst | 1 +
> > Documentation/wmi/devices/alienware-wmi.rst | 383 +++-----
> > MAINTAINERS | 3 +
> > drivers/platform/x86/dell/Kconfig | 1 +
> > drivers/platform/x86/dell/alienware-wmi-wmax.c | 1023 +++++++++++++++++---
> > 8 files changed, 1187 insertions(+), 410 deletions(-)
> > ---
> > base-commit: f895f2493098b862f1ada0568aba278e49bf05b4
> > change-id: 20250305-hwm-f7bd91902b57
> >
> > Best regards,
>
> Hi Ilpo,
>
> Is there still a chance for this to go into v6.15? or are you planning
> to review it on the next cycle?
Hi,
I'm almost there to make the main PR for 6.15 from what's in for-next
currently, so no, this won't be part of it.
In general, I don't usually take large series after -rc6 timeframe to give
time for things to settle and problems to be brought to surface.
Especially if the series is interfacing with other subsystems which is
prone to lack of select/depends on clauses, etc. so more likely to break
the build than changes that seem immune to the .config variations.
--
i.
^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: [PATCH v6 00/12] platform/x86: alienware-wmi-wmax: HWMON support + DebugFS + Improvements
2025-03-26 8:34 ` Ilpo Järvinen
@ 2025-03-26 14:11 ` Kurt Borja
0 siblings, 0 replies; 8+ messages in thread
From: Kurt Borja @ 2025-03-26 14:11 UTC (permalink / raw)
To: Ilpo Järvinen
Cc: Armin Wolf, Hans de Goede, platform-driver-x86,
Dell.Client.Kernel, LKML, Guenter Roeck, Jean Delvare,
linux-hwmon, Bagas Sanjaya
On Wed Mar 26, 2025 at 5:34 AM -03, Ilpo Järvinen wrote:
> On Tue, 25 Mar 2025, Kurt Borja wrote:
>> On Thu Mar 13, 2025 at 11:29 AM -03, Kurt Borja wrote:
>> > Hi all,
>> >
>> > This set mainly adds hwmon and manual fan control support (patches 7-8)
>> > to the alienware-wmi driver, after some improvements.
>> >
>> > Thank you for your feedback :)
>> >
>> > ---
>> > Changes in v6:
>> >
>> > [08/12]
>> > - Define dev_pm_ops statically (kernel test robot)
>> >
>> > Link to v5: https://lore.kernel.org/r/20250312-hwm-v5-0-deb15ff8f3c6@gmail.com
>> >
>> > ---
>> > Kurt Borja (12):
>> > platform/x86: alienware-wmi-wmax: Rename thermal related symbols
>> > platform/x86: alienware-wmi-wmax: Refactor is_awcc_thermal_mode()
>> > platform/x86: alienware-wmi-wmax: Improve internal AWCC API
>> > platform/x86: alienware-wmi-wmax: Modify supported_thermal_profiles[]
>> > platform/x86: alienware-wmi-wmax: Improve platform profile probe
>> > platform/x86: alienware-wmi-wmax: Add support for the "custom" thermal profile
>> > platform/x86: alienware-wmi-wmax: Add HWMON support
>> > platform/x86: alienware-wmi-wmax: Add support for manual fan control
>> > platform/x86: alienware-wmi-wmax: Add a DebugFS interface
>> > Documentation: wmi: Improve and update alienware-wmi documentation
>> > Documentation: admin-guide: laptops: Add documentation for alienware-wmi
>> > Documentation: ABI: Add sysfs platform and debugfs ABI documentation for alienware-wmi
>> >
>> > Documentation/ABI/testing/debugfs-alienware-wmi | 44 +
>> > .../ABI/testing/sysfs-platform-alienware-wmi | 14 +
>> > .../admin-guide/laptops/alienware-wmi.rst | 128 +++
>> > Documentation/admin-guide/laptops/index.rst | 1 +
>> > Documentation/wmi/devices/alienware-wmi.rst | 383 +++-----
>> > MAINTAINERS | 3 +
>> > drivers/platform/x86/dell/Kconfig | 1 +
>> > drivers/platform/x86/dell/alienware-wmi-wmax.c | 1023 +++++++++++++++++---
>> > 8 files changed, 1187 insertions(+), 410 deletions(-)
>> > ---
>> > base-commit: f895f2493098b862f1ada0568aba278e49bf05b4
>> > change-id: 20250305-hwm-f7bd91902b57
>> >
>> > Best regards,
>>
>> Hi Ilpo,
>>
>> Is there still a chance for this to go into v6.15? or are you planning
>> to review it on the next cycle?
>
> Hi,
>
> I'm almost there to make the main PR for 6.15 from what's in for-next
> currently, so no, this won't be part of it.
>
> In general, I don't usually take large series after -rc6 timeframe to give
> time for things to settle and problems to be brought to surface.
> Especially if the series is interfacing with other subsystems which is
> prone to lack of select/depends on clauses, etc. so more likely to break
> the build than changes that seem immune to the .config variations.
Oh - I'll keep in mind this in the future. Thank you!
--
~ Kurt
^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: [PATCH v6 08/12] platform/x86: alienware-wmi-wmax: Add support for manual fan control
2025-03-13 14:30 ` [PATCH v6 08/12] platform/x86: alienware-wmi-wmax: Add support for manual fan control Kurt Borja
@ 2025-03-28 16:15 ` Ilpo Järvinen
0 siblings, 0 replies; 8+ messages in thread
From: Ilpo Järvinen @ 2025-03-28 16:15 UTC (permalink / raw)
To: Kurt Borja
Cc: Armin Wolf, Hans de Goede, platform-driver-x86,
Dell.Client.Kernel, LKML, Guenter Roeck, Jean Delvare,
linux-hwmon
On Thu, 13 Mar 2025, Kurt Borja wrote:
> All models with the "AWCC" WMAX device support a way of manually
> controlling fans.
>
> The PWM duty cycle of a fan can't be controlled directly. Instead the
> AWCC interface let's us tune a fan `boost` value, which has the
> following empirically discovered, aproximate behavior over the PWM
approximate
> value:
>
> pwm = pwm_base + (fan_boost / 255) * (pwm_max - pwm_base)
>
> Where the pwm_base is the locked PWM value controlled by the FW and
> fan_boost is a value between 0 and 255.
>
> Expose this fan_boost knob as a custom HWMON attribute.
>
> Cc: Guenter Roeck <linux@roeck-us.net>
> Cc: Jean Delvare <jdelvare@suse.com>
> Cc: linux-hwmon@vger.kernel.org
> Reviewed-by: Armin Wolf <W_Armin@gmx.de>
> Signed-off-by: Kurt Borja <kuurtb@gmail.com>
> ---
> drivers/platform/x86/dell/alienware-wmi-wmax.c | 174 ++++++++++++++++++++++++-
> 1 file changed, 172 insertions(+), 2 deletions(-)
>
> diff --git a/drivers/platform/x86/dell/alienware-wmi-wmax.c b/drivers/platform/x86/dell/alienware-wmi-wmax.c
> index 0c30ffb6091a15d81f4dc959dfd28417249d3dda..823b579260555085ef6ac793b806738a756bb9da 100644
> --- a/drivers/platform/x86/dell/alienware-wmi-wmax.c
> +++ b/drivers/platform/x86/dell/alienware-wmi-wmax.c
> @@ -13,8 +13,12 @@
> #include <linux/bits.h>
> #include <linux/dmi.h>
> #include <linux/hwmon.h>
> +#include <linux/hwmon-sysfs.h>
> +#include <linux/kstrtox.h>
> +#include <linux/minmax.h>
> #include <linux/moduleparam.h>
> #include <linux/platform_profile.h>
> +#include <linux/pm.h>
> #include <linux/units.h>
> #include <linux/wmi.h>
> #include "alienware-wmi.h"
> @@ -181,10 +185,12 @@ enum AWCC_THERMAL_INFORMATION_OPERATIONS {
> AWCC_OP_GET_FAN_MIN_RPM = 0x08,
> AWCC_OP_GET_FAN_MAX_RPM = 0x09,
> AWCC_OP_GET_CURRENT_PROFILE = 0x0B,
> + AWCC_OP_GET_FAN_BOOST = 0x0C,
> };
>
> enum AWCC_THERMAL_CONTROL_OPERATIONS {
> AWCC_OP_ACTIVATE_PROFILE = 0x01,
> + AWCC_OP_SET_FAN_BOOST = 0x02,
> };
>
> enum AWCC_GAME_SHIFT_STATUS_OPERATIONS {
> @@ -248,6 +254,7 @@ struct awcc_fan_data {
> const char *label;
> u32 min_rpm;
> u32 max_rpm;
> + u8 suspend_cache;
> u8 id;
> };
>
> @@ -628,6 +635,18 @@ static inline int awcc_op_get_temperature(struct wmi_device *wdev, u8 temp_id, u
> return __awcc_wmi_command(wdev, AWCC_METHOD_THERMAL_INFORMATION, &args, out);
> }
>
> +static inline int awcc_op_get_fan_boost(struct wmi_device *wdev, u8 fan_id, u32 *out)
> +{
> + struct wmax_u32_args args = {
> + .operation = AWCC_OP_GET_FAN_BOOST,
> + .arg1 = fan_id,
> + .arg2 = 0,
> + .arg3 = 0,
> + };
> +
> + return __awcc_wmi_command(wdev, AWCC_METHOD_THERMAL_INFORMATION, &args, out);
> +}
> +
> static inline int awcc_op_get_current_profile(struct wmi_device *wdev, u32 *out)
> {
> struct wmax_u32_args args = {
> @@ -653,6 +672,19 @@ static inline int awcc_op_activate_profile(struct wmi_device *wdev, u8 profile)
> return __awcc_wmi_command(wdev, AWCC_METHOD_THERMAL_CONTROL, &args, &out);
> }
>
> +static inline int awcc_op_set_fan_boost(struct wmi_device *wdev, u8 fan_id, u8 boost)
> +{
> + struct wmax_u32_args args = {
> + .operation = AWCC_OP_SET_FAN_BOOST,
> + .arg1 = fan_id,
> + .arg2 = boost,
> + .arg3 = 0,
> + };
> + u32 out;
> +
> + return __awcc_wmi_command(wdev, AWCC_METHOD_THERMAL_CONTROL, &args, &out);
> +}
> +
> /*
> * HWMON
> * - Provides temperature and fan speed monitoring as well as manual fan
> @@ -817,6 +849,81 @@ static const struct hwmon_chip_info awcc_hwmon_chip_info = {
> .info = awcc_hwmon_info,
> };
>
> +static ssize_t fan_boost_show(struct device *dev, struct device_attribute *attr,
> + char *buf)
> +{
> + struct awcc_priv *priv = dev_get_drvdata(dev);
> + int index = to_sensor_dev_attr(attr)->index;
> + struct awcc_fan_data *fan = priv->fan_data[index];
> + u32 boost;
> + int ret;
> +
> + ret = awcc_op_get_fan_boost(priv->wdev, fan->id, &boost);
> + if (ret)
> + return ret;
> +
> + return sysfs_emit(buf, "%u\n", boost);
> +}
> +
> +static ssize_t fan_boost_store(struct device *dev, struct device_attribute *attr,
> + const char *buf, size_t count)
> +{
> + struct awcc_priv *priv = dev_get_drvdata(dev);
> + int index = to_sensor_dev_attr(attr)->index;
> + struct awcc_fan_data *fan = priv->fan_data[index];
> + unsigned long val;
> + int ret;
> +
> + ret = kstrtoul(buf, 0, &val);
> + if (ret)
> + return ret;
> +
> + ret = awcc_op_set_fan_boost(priv->wdev, fan->id, clamp_val(val, 0, 255));
> +
> + return ret ? ret : count;
> +}
> +
> +static SENSOR_DEVICE_ATTR_RW(fan1_boost, fan_boost, 0);
> +static SENSOR_DEVICE_ATTR_RW(fan2_boost, fan_boost, 1);
> +static SENSOR_DEVICE_ATTR_RW(fan3_boost, fan_boost, 2);
> +static SENSOR_DEVICE_ATTR_RW(fan4_boost, fan_boost, 3);
> +static SENSOR_DEVICE_ATTR_RW(fan5_boost, fan_boost, 4);
> +static SENSOR_DEVICE_ATTR_RW(fan6_boost, fan_boost, 5);
> +
> +static umode_t fan_boost_attr_visible(struct kobject *kobj, struct attribute *attr, int n)
> +{
> + struct awcc_priv *priv = dev_get_drvdata(kobj_to_dev(kobj));
> +
> + return n < priv->fan_count ? attr->mode : 0;
> +}
> +
> +static bool fan_boost_group_visible(struct kobject *kobj)
> +{
> + return true;
> +}
> +
> +DEFINE_SYSFS_GROUP_VISIBLE(fan_boost);
> +
> +static struct attribute *fan_boost_attrs[] = {
> + &sensor_dev_attr_fan1_boost.dev_attr.attr,
> + &sensor_dev_attr_fan2_boost.dev_attr.attr,
> + &sensor_dev_attr_fan3_boost.dev_attr.attr,
> + &sensor_dev_attr_fan4_boost.dev_attr.attr,
> + &sensor_dev_attr_fan5_boost.dev_attr.attr,
> + &sensor_dev_attr_fan6_boost.dev_attr.attr,
> + NULL
> +};
> +
> +static const struct attribute_group fan_boost_group = {
> + .attrs = fan_boost_attrs,
> + .is_visible = SYSFS_GROUP_VISIBLE(fan_boost),
> +};
> +
> +static const struct attribute_group *awcc_hwmon_groups[] = {
> + &fan_boost_group,
> + NULL
> +};
> +
> static int awcc_hwmon_temps_init(struct wmi_device *wdev)
> {
> struct awcc_priv *priv = dev_get_drvdata(&wdev->dev);
> @@ -954,12 +1061,56 @@ static int awcc_hwmon_init(struct wmi_device *wdev)
> if (ret)
> return ret;
>
> - priv->hwdev = devm_hwmon_device_register_with_info(&wdev->dev, "alienware_wmi", priv,
> - &awcc_hwmon_chip_info, NULL);
> + priv->hwdev = devm_hwmon_device_register_with_info(&wdev->dev, "alienware_wmi",
> + priv, &awcc_hwmon_chip_info,
> + awcc_hwmon_groups);
>
> return PTR_ERR_OR_ZERO(priv->hwdev);
> }
>
> +static void awcc_hwmon_suspend(struct device *dev)
> +{
> + struct awcc_priv *priv = dev_get_drvdata(dev);
> + struct awcc_fan_data *fan;
> + unsigned int i;
> + u32 boost;
> + int ret;
> +
> + for (i = 0; i < priv->fan_count; i++) {
> + fan = priv->fan_data[i];
> +
> + ret = awcc_thermal_information(priv->wdev, AWCC_OP_GET_FAN_BOOST,
> + fan->id, &boost);
> + if (ret)
> + dev_err(dev, "Failed to store Fan %u boost while suspending\n", i);
> +
> + fan->suspend_cache = ret ? 0 : clamp_val(boost, 0, 255);
> +
> + awcc_op_set_fan_boost(priv->wdev, fan->id, 0);
> + if (ret)
> + dev_err(dev, "Failed to set Fan %u boost to 0 while suspending\n", i);
> + }
> +}
> +
> +static void awcc_hwmon_resume(struct device *dev)
> +{
> + struct awcc_priv *priv = dev_get_drvdata(dev);
> + struct awcc_fan_data *fan;
> + unsigned int i;
> + int ret;
> +
> + for (i = 0; i < priv->fan_count; i++) {
> + fan = priv->fan_data[i];
> +
> + if (!fan->suspend_cache)
> + continue;
> +
> + ret = awcc_op_set_fan_boost(priv->wdev, fan->id, fan->suspend_cache);
> + if (ret)
> + dev_err(dev, "Failed to restore Fan %u boost while resuming\n", i);
> + }
> +}
> +
> /*
> * Thermal Profile control
> * - Provides thermal profile control through the Platform Profile API
> @@ -1189,6 +1340,24 @@ static int wmax_wmi_probe(struct wmi_device *wdev, const void *context)
> return ret;
> }
>
> +static int wmax_wmi_suspend(struct device *dev)
> +{
> + if (awcc->hwmon)
> + awcc_hwmon_suspend(dev);
> +
> + return 0;
> +}
> +
> +static int wmax_wmi_resume(struct device *dev)
> +{
> + if (awcc->hwmon)
> + awcc_hwmon_resume(dev);
> +
> + return 0;
> +}
> +
> +static DEFINE_SIMPLE_DEV_PM_OPS(wmax_wmi_pm_ops, wmax_wmi_suspend, wmax_wmi_resume);
> +
> static const struct wmi_device_id alienware_wmax_device_id_table[] = {
> { WMAX_CONTROL_GUID, NULL },
> { },
> @@ -1199,6 +1368,7 @@ static struct wmi_driver alienware_wmax_wmi_driver = {
> .driver = {
> .name = "alienware-wmi-wmax",
> .probe_type = PROBE_PREFER_ASYNCHRONOUS,
> + .pm = pm_sleep_ptr(&wmax_wmi_pm_ops),
> },
> .id_table = alienware_wmax_device_id_table,
> .probe = wmax_wmi_probe,
>
>
--
i.
^ permalink raw reply [flat|nested] 8+ messages in thread
end of thread, other threads:[~2025-03-28 16:15 UTC | newest]
Thread overview: 8+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2025-03-13 14:29 [PATCH v6 00/12] platform/x86: alienware-wmi-wmax: HWMON support + DebugFS + Improvements Kurt Borja
2025-03-13 14:30 ` [PATCH v6 07/12] platform/x86: alienware-wmi-wmax: Add HWMON support Kurt Borja
2025-03-13 14:30 ` [PATCH v6 08/12] platform/x86: alienware-wmi-wmax: Add support for manual fan control Kurt Borja
2025-03-28 16:15 ` Ilpo Järvinen
2025-03-17 0:32 ` [PATCH v6 00/12] platform/x86: alienware-wmi-wmax: HWMON support + DebugFS + Improvements Armin Wolf
2025-03-25 20:14 ` Kurt Borja
2025-03-26 8:34 ` Ilpo Järvinen
2025-03-26 14:11 ` Kurt Borja
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox