summaryrefslogtreecommitdiff
path: root/drivers
diff options
context:
space:
mode:
authorArmin Wolf <W_Armin@gmx.de>2025-09-17 20:10:35 +0200
committerGuenter Roeck <linux@roeck-us.net>2025-09-24 07:50:09 -0700
commit1c1658058c99bcfd3b2347e587a556986037f80a (patch)
treeaefc70fefe8cea4a960d57127d339c60416f7c47 /drivers
parent205c730262215fe1940668394a856f69ece55c66 (diff)
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@gmx.de> Link: https://lore.kernel.org/r/20250917181036.10972-4-W_Armin@gmx.de Signed-off-by: Guenter Roeck <linux@roeck-us.net>
Diffstat (limited to 'drivers')
-rw-r--r--drivers/hwmon/dell-smm-hwmon.c74
1 files changed, 61 insertions, 13 deletions
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,15 +946,29 @@ 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
};