[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-Id: <20250917181036.10972-4-W_Armin@gmx.de>
Date: Wed, 17 Sep 2025 20:10:35 +0200
From: Armin Wolf <W_Armin@....de>
To: pali@...nel.org
Cc: linux@...ck-us.net,
linux-hwmon@...r.kernel.org,
linux-kernel@...r.kernel.org
Subject: [PATCH 3/4] hwmon: (dell-smm) Add support for automatic fan mode
Many machines treat fan state 3 as some sort of automatic mode,
which is superior to the separate SMM calls for switching to
automatic fan mode for two reasons:
- the fan control mode can be controlled for each fan separately
- the current fan control mode can be retrieved from the BIOS
On some machines however, this special fan state does not exist.
Fan state 3 acts like a regular fan state on such machines or
does not exist at all. Such machines usually use separate SMM calls
for enabling/disabling automatic fan control.
Add support for it. If the machine supports separate SMM calls
for changing the fan control mode, then the other interface is
ignored.
Signed-off-by: Armin Wolf <W_Armin@....de>
---
Documentation/hwmon/dell-smm-hwmon.rst | 56 +++++++++++--------
drivers/hwmon/dell-smm-hwmon.c | 74 +++++++++++++++++++++-----
include/uapi/linux/i8k.h | 2 +
3 files changed, 98 insertions(+), 34 deletions(-)
diff --git a/Documentation/hwmon/dell-smm-hwmon.rst b/Documentation/hwmon/dell-smm-hwmon.rst
index 5a4edb6565cf..3e4e2d916ac5 100644
--- a/Documentation/hwmon/dell-smm-hwmon.rst
+++ b/Documentation/hwmon/dell-smm-hwmon.rst
@@ -38,7 +38,7 @@ fan[1-4]_min RO Minimal Fan speed in RPM
fan[1-4]_max RO Maximal Fan speed in RPM
fan[1-4]_target RO Expected Fan speed in RPM
pwm[1-4] RW Control the fan PWM duty-cycle.
-pwm1_enable WO Enable or disable automatic BIOS fan
+pwm[1-4]_enable RW/WO Enable or disable automatic BIOS fan
control (not supported on all laptops,
see below for details).
temp[1-10]_input RO Temperature reading in milli-degrees
@@ -49,26 +49,40 @@ temp[1-10]_label RO Temperature sensor label.
Due to the nature of the SMM interface, each pwmX attribute controls
fan number X.
-Disabling automatic BIOS fan control
-------------------------------------
-
-On some laptops the BIOS automatically sets fan speed every few
-seconds. Therefore the fan speed set by mean of this driver is quickly
-overwritten.
-
-There is experimental support for disabling automatic BIOS fan
-control, at least on laptops where the corresponding SMM command is
-known, by writing the value ``1`` in the attribute ``pwm1_enable``
-(writing ``2`` enables automatic BIOS control again). Even if you have
-more than one fan, all of them are set to either enabled or disabled
-automatic fan control at the same time and, notwithstanding the name,
-``pwm1_enable`` sets automatic control for all fans.
-
-If ``pwm1_enable`` is not available, then it means that SMM codes for
-enabling and disabling automatic BIOS fan control are not whitelisted
-for your hardware. It is possible that codes that work for other
-laptops actually work for yours as well, or that you have to discover
-new codes.
+Enabling/Disabling automatic BIOS fan control
+---------------------------------------------
+
+There exist two methods for enabling/disabling automatic BIOS fan control:
+
+1. Separate SMM commands to enable/disable automatic BIOS fan control for all fans.
+
+2. A special fan state that enables automatic BIOS fan control for a individual fan.
+
+The driver cannot reliably detect what method should be used on a given
+device, so instead the following heuristic is used:
+
+- use fan state 3 for enabling BIOS fan control if the maximum fan state
+ setable by the user is smaller than 3 (default setting).
+
+- use separate SMM commands if device is whitelisted to support them.
+
+When using the first method, each fan will have a standard ``pwmX_enable``
+sysfs attribute. Writing ``1`` into this attribute will disable automatic
+BIOS fan control for the associated fan and set it to maximum speed. Enabling
+BIOS fan control again can be achieved by writing ``2`` into this attribute.
+Reading this sysfs attributes returns the current setting as reported by
+the underlying hardware.
+
+When using the second method however, only the ``pwm1_enable`` sysfs attribute
+will be available to enable/disable automatic BIOS fan control globaly for all
+fans available on a given device. Additionally, this sysfs attribute is write-only
+as there exists no SMM command for reading the current fan control setting.
+
+If no ``pwmX_enable`` attributes are available, then it means that the driver
+cannot use the first method and the SMM codes for enabling and disabling automatic
+BIOS fan control are not whitelisted for your device. It is possible that codes
+that work for other laptops actually work for yours as well, or that you have to
+discover new codes.
Check the list ``i8k_whitelist_fan_control`` in file
``drivers/hwmon/dell-smm-hwmon.c`` in the kernel tree: as a first
diff --git a/drivers/hwmon/dell-smm-hwmon.c b/drivers/hwmon/dell-smm-hwmon.c
index 36576db09706..79befa13b699 100644
--- a/drivers/hwmon/dell-smm-hwmon.c
+++ b/drivers/hwmon/dell-smm-hwmon.c
@@ -764,6 +764,13 @@ static int dell_smm_get_cur_state(struct thermal_cooling_device *dev, unsigned l
if (ret < 0)
return ret;
+ /*
+ * A fan state bigger than i8k_fan_max might indicate that
+ * the fan is currently in automatic mode.
+ */
+ if (ret > cdata->data->i8k_fan_max)
+ return -ENODATA;
+
*state = ret;
return 0;
@@ -851,7 +858,14 @@ static umode_t dell_smm_is_visible(const void *drvdata, enum hwmon_sensor_types
break;
case hwmon_pwm_enable:
- if (auto_fan)
+ if (auto_fan) {
+ /*
+ * The setting affects all fans, so only create a
+ * single attribute.
+ */
+ if (channel != 1)
+ return 0;
+
/*
* There is no command for retrieve the current status
* from BIOS, and userspace/firmware itself can change
@@ -859,6 +873,10 @@ static umode_t dell_smm_is_visible(const void *drvdata, enum hwmon_sensor_types
* Thus we can only provide write-only access for now.
*/
return 0200;
+ }
+
+ if (data->fan[channel] && data->i8k_fan_max < I8K_FAN_AUTO)
+ return 0644;
break;
default:
@@ -928,14 +946,28 @@ static int dell_smm_read(struct device *dev, enum hwmon_sensor_types type, u32 a
}
break;
case hwmon_pwm:
+ ret = i8k_get_fan_status(data, channel);
+ if (ret < 0)
+ return ret;
+
switch (attr) {
case hwmon_pwm_input:
- ret = i8k_get_fan_status(data, channel);
- if (ret < 0)
- return ret;
+ /*
+ * A fan state bigger than i8k_fan_max might indicate that
+ * the fan is currently in automatic mode.
+ */
+ if (ret > data->i8k_fan_max)
+ return -ENODATA;
*val = clamp_val(ret * data->i8k_pwm_mult, 0, 255);
+ return 0;
+ case hwmon_pwm_enable:
+ if (ret == I8K_FAN_AUTO)
+ *val = 2;
+ else
+ *val = 1;
+
return 0;
default:
break;
@@ -1022,16 +1054,32 @@ static int dell_smm_write(struct device *dev, enum hwmon_sensor_types type, u32
return 0;
case hwmon_pwm_enable:
- if (!val)
- return -EINVAL;
-
- if (val == 1)
+ switch (val) {
+ case 1:
enable = false;
- else
+ break;
+ case 2:
enable = true;
+ break;
+ default:
+ return -EINVAL;
+ }
mutex_lock(&data->i8k_mutex);
- err = i8k_enable_fan_auto_mode(data, enable);
+ if (auto_fan) {
+ err = i8k_enable_fan_auto_mode(data, enable);
+ } else {
+ /*
+ * When putting the fan into manual control mode we have to ensure
+ * that the device does not overheat until the userspace fan control
+ * software takes over. Because of this we set the fan speed to
+ * i8k_fan_max when disabling automatic fan control.
+ */
+ if (enable)
+ err = i8k_set_fan(data, channel, I8K_FAN_AUTO);
+ else
+ err = i8k_set_fan(data, channel, data->i8k_fan_max);
+ }
mutex_unlock(&data->i8k_mutex);
if (err < 0)
@@ -1082,9 +1130,9 @@ static const struct hwmon_channel_info * const dell_smm_info[] = {
),
HWMON_CHANNEL_INFO(pwm,
HWMON_PWM_INPUT | HWMON_PWM_ENABLE,
- HWMON_PWM_INPUT,
- HWMON_PWM_INPUT,
- HWMON_PWM_INPUT
+ HWMON_PWM_INPUT | HWMON_PWM_ENABLE,
+ HWMON_PWM_INPUT | HWMON_PWM_ENABLE,
+ HWMON_PWM_INPUT | HWMON_PWM_ENABLE
),
NULL
};
diff --git a/include/uapi/linux/i8k.h b/include/uapi/linux/i8k.h
index 268e6268f6c8..a16e4049710f 100644
--- a/include/uapi/linux/i8k.h
+++ b/include/uapi/linux/i8k.h
@@ -36,6 +36,8 @@
#define I8K_FAN_LOW 1
#define I8K_FAN_HIGH 2
#define I8K_FAN_TURBO 3
+/* Many machines treat this mode as some sort of automatic mode */
+#define I8K_FAN_AUTO 3
#define I8K_FAN_MAX I8K_FAN_TURBO
#define I8K_VOL_UP 1
--
2.39.5
Powered by blists - more mailing lists