hwmon: (dell-smm) Add support for fanX_min, fanX_max and fanX_target
authorArmin Wolf <W_Armin@gmx.de>
Sun, 26 Sep 2021 22:10:43 +0000 (00:10 +0200)
committerGuenter Roeck <linux@roeck-us.net>
Sat, 16 Oct 2021 21:58:42 +0000 (14:58 -0700)
The nominal speed of each fan can be obtained with
i8k_get_fan_nominal_speed(), however the result is not available
from userspace.
Change that by adding fanX_min, fanX_max and fanX_target attributes.
All are RO since fan control happens over pwm.

Tested on a Dell Inspiron 3505 and a Dell Latitude C600.

Signed-off-by: Armin Wolf <W_Armin@gmx.de>
Reviewed-by: Pali Rohár <pali@kernel.org>
Link: https://lore.kernel.org/r/20210926221044.14327-2-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

index 3bf77a5df995c02e8a96604adfd62267e0c62c17..beec8849117154f9a9a5f2d49ba89ca289f48fa1 100644 (file)
@@ -34,6 +34,9 @@ Name                          Perm    Description
 =============================== ======= =======================================
 fan[1-3]_input                  RO      Fan speed in RPM.
 fan[1-3]_label                  RO      Fan label.
+fan[1-3]_min                    RO      Minimal Fan speed in RPM
+fan[1-3]_max                    RO      Maximal Fan speed in RPM
+fan[1-3]_target                 RO      Expected Fan speed in RPM
 pwm[1-3]                        RW      Control the fan PWM duty-cycle.
 pwm1_enable                     WO      Enable or disable automatic BIOS fan
                                         control (not supported on all laptops,
index 5936f2f812d46546eb3ac04cd6c570e0b6f08b6f..af0d0d2b6e998aca0f20eccfdfa357424d3aa50f 100644 (file)
@@ -74,6 +74,7 @@ struct dell_smm_data {
        int temp_type[DELL_SMM_NO_TEMP];
        bool fan[DELL_SMM_NO_FANS];
        int fan_type[DELL_SMM_NO_FANS];
+       int *fan_nominal_speed[DELL_SMM_NO_FANS];
 };
 
 MODULE_AUTHOR("Massimo Dal Zotto (dz@debian.org)");
@@ -671,6 +672,13 @@ static umode_t dell_smm_is_visible(const void *drvdata, enum hwmon_sensor_types
                        if (data->fan[channel] && !data->disallow_fan_type_call)
                                return 0444;
 
+                       break;
+               case hwmon_fan_min:
+               case hwmon_fan_max:
+               case hwmon_fan_target:
+                       if (data->fan_nominal_speed[channel])
+                               return 0444;
+
                        break;
                default:
                        break;
@@ -738,6 +746,25 @@ static int dell_smm_read(struct device *dev, enum hwmon_sensor_types type, u32 a
 
                        *val = ret;
 
+                       return 0;
+               case hwmon_fan_min:
+                       *val = data->fan_nominal_speed[channel][0];
+
+                       return 0;
+               case hwmon_fan_max:
+                       *val = data->fan_nominal_speed[channel][data->i8k_fan_max];
+
+                       return 0;
+               case hwmon_fan_target:
+                       ret = i8k_get_fan_status(data, channel);
+                       if (ret < 0)
+                               return ret;
+
+                       if (ret > data->i8k_fan_max)
+                               ret = data->i8k_fan_max;
+
+                       *val = data->fan_nominal_speed[channel][ret];
+
                        return 0;
                default:
                        break;
@@ -887,9 +914,12 @@ static const struct hwmon_channel_info *dell_smm_info[] = {
                           HWMON_T_INPUT | HWMON_T_LABEL
                           ),
        HWMON_CHANNEL_INFO(fan,
-                          HWMON_F_INPUT | HWMON_F_LABEL,
-                          HWMON_F_INPUT | HWMON_F_LABEL,
-                          HWMON_F_INPUT | HWMON_F_LABEL
+                          HWMON_F_INPUT | HWMON_F_LABEL | HWMON_F_MIN | HWMON_F_MAX |
+                          HWMON_F_TARGET,
+                          HWMON_F_INPUT | HWMON_F_LABEL | HWMON_F_MIN | HWMON_F_MAX |
+                          HWMON_F_TARGET,
+                          HWMON_F_INPUT | HWMON_F_LABEL | HWMON_F_MIN | HWMON_F_MAX |
+                          HWMON_F_TARGET
                           ),
        HWMON_CHANNEL_INFO(pwm,
                           HWMON_PWM_INPUT | HWMON_PWM_ENABLE,
@@ -908,7 +938,7 @@ static int __init dell_smm_init_hwmon(struct device *dev)
 {
        struct dell_smm_data *data = dev_get_drvdata(dev);
        struct device *dell_smm_hwmon_dev;
-       int i, err;
+       int i, state, err;
 
        for (i = 0; i < DELL_SMM_NO_TEMP; i++) {
                data->temp_type[i] = i8k_get_temp_type(i);
@@ -924,8 +954,27 @@ static int __init dell_smm_init_hwmon(struct device *dev)
                err = i8k_get_fan_status(data, i);
                if (err < 0)
                        err = i8k_get_fan_type(data, i);
-               if (err >= 0)
-                       data->fan[i] = true;
+
+               if (err < 0)
+                       continue;
+
+               data->fan[i] = true;
+               data->fan_nominal_speed[i] = devm_kmalloc_array(dev, data->i8k_fan_max + 1,
+                                                               sizeof(*data->fan_nominal_speed[i]),
+                                                               GFP_KERNEL);
+               if (!data->fan_nominal_speed[i])
+                       continue;
+
+               for (state = 0; state <= data->i8k_fan_max; state++) {
+                       err = i8k_get_fan_nominal_speed(data, i, state);
+                       if (err < 0) {
+                               /* Mark nominal speed table as invalid in case of error */
+                               devm_kfree(dev, data->fan_nominal_speed[i]);
+                               data->fan_nominal_speed[i] = NULL;
+                               break;
+                       }
+                       data->fan_nominal_speed[i][state] = err;
+               }
        }
 
        dell_smm_hwmon_dev = devm_hwmon_device_register_with_info(dev, "dell_smm", data,