]> git.ipfire.org Git - thirdparty/kernel/stable.git/commitdiff
hwmon: (dell-smm) Add support for automatic fan mode
authorArmin Wolf <W_Armin@gmx.de>
Wed, 17 Sep 2025 18:10:35 +0000 (20:10 +0200)
committerGuenter Roeck <linux@roeck-us.net>
Wed, 24 Sep 2025 14:50:09 +0000 (07:50 -0700)
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@gmx.de>
Link: https://lore.kernel.org/r/20250917181036.10972-4-W_Armin@gmx.de
Signed-off-by: Guenter Roeck <linux@roeck-us.net>
Documentation/hwmon/dell-smm-hwmon.rst
drivers/hwmon/dell-smm-hwmon.c
include/uapi/linux/i8k.h

index 5a4edb6565cf9591cc67fc7d94402fea0fcf40a2..3e4e2d916ac5232e4a2884c39e11e2a7d6ee2826 100644 (file)
@@ -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
index 36576db097067f106bcb08736ec4a489f9d7e86f..79befa13b6990bd1776eee9d87ee0bdd6105b222 100644 (file)
@@ -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
 };
index 268e6268f6c808f81116d0775f3eeea2e7520287..a16e4049710fcc7f0ff7a270ded6df1e46a81ed2 100644 (file)
@@ -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