Until now, only the temperature sensors where exported thru the thermal subsystem. Export the fans as "dell-smm-fan" too to make them available as cooling devices. This could be a potential successor of the nonstandard procfs interface currently provided by the driver. Also update Documentation. Signed-off-by: Armin Wolf <W_Armin@xxxxxx> --- Documentation/hwmon/dell-smm-hwmon.rst | 7 +++ drivers/hwmon/dell-smm-hwmon.c | 68 ++++++++++++++++++++++++-- 2 files changed, 72 insertions(+), 3 deletions(-) diff --git a/Documentation/hwmon/dell-smm-hwmon.rst b/Documentation/hwmon/dell-smm-hwmon.rst index d3323a96665d..da07e39935f1 100644 --- a/Documentation/hwmon/dell-smm-hwmon.rst +++ b/Documentation/hwmon/dell-smm-hwmon.rst @@ -86,6 +86,13 @@ probe the BIOS on your machine and discover the appropriate codes. Again, when you find new codes, we'd be happy to have your patches! +``thermal`` interface +--------------------------- + +The driver also exports the fans as thermal cooling devices with +``type`` set to ``dell-smm-fan``. This allows for easy fan control +using one of the thermal governors. + Module parameters ----------------- diff --git a/drivers/hwmon/dell-smm-hwmon.c b/drivers/hwmon/dell-smm-hwmon.c index 84cb1ede7bc0..fb2c70875c0a 100644 --- a/drivers/hwmon/dell-smm-hwmon.c +++ b/drivers/hwmon/dell-smm-hwmon.c @@ -29,6 +29,7 @@ #include <linux/seq_file.h> #include <linux/string.h> #include <linux/smp.h> +#include <linux/thermal.h> #include <linux/types.h> #include <linux/uaccess.h> @@ -80,6 +81,11 @@ struct dell_smm_data { int *fan_nominal_speed[DELL_SMM_NO_FANS]; }; +struct dell_smm_cooling_data { + u8 fan_num; + struct dell_smm_data *data; +}; + MODULE_AUTHOR("Massimo Dal Zotto (dz@xxxxxxxxxx)"); MODULE_AUTHOR("Pali Rohár <pali@xxxxxxxxxx>"); MODULE_DESCRIPTION("Dell laptop SMM BIOS hwmon driver"); @@ -638,9 +644,50 @@ static void __init i8k_init_procfs(struct device *dev) #endif -/* - * Hwmon interface - */ +static int dell_smm_get_max_state(struct thermal_cooling_device *dev, unsigned long *state) +{ + struct dell_smm_cooling_data *cdata = dev->devdata; + + *state = cdata->data->i8k_fan_max; + + return 0; +} + +static int dell_smm_get_cur_state(struct thermal_cooling_device *dev, unsigned long *state) +{ + struct dell_smm_cooling_data *cdata = dev->devdata; + int ret; + + ret = i8k_get_fan_speed(cdata->data, cdata->fan_num); + if (ret < 0) + return ret; + + *state = ret; + + return 0; +} + +static int dell_smm_set_cur_state(struct thermal_cooling_device *dev, unsigned long state) +{ + struct dell_smm_cooling_data *cdata = dev->devdata; + struct dell_smm_data *data = cdata->data; + int ret; + + if (state > data->i8k_fan_max) + return -EINVAL; + + mutex_lock(&data->i8k_mutex); + ret = i8k_set_fan(data, cdata->fan_num, (int)state); + mutex_unlock(&data->i8k_mutex); + + return ret; +} + +static const struct thermal_cooling_device_ops dell_smm_cooling_ops = { + .get_max_state = dell_smm_get_max_state, + .get_cur_state = dell_smm_get_cur_state, + .set_cur_state = dell_smm_set_cur_state, +}; static umode_t dell_smm_is_visible(const void *drvdata, enum hwmon_sensor_types type, u32 attr, int channel) @@ -944,6 +991,8 @@ static const struct hwmon_chip_info dell_smm_chip_info = { static int __init dell_smm_init_hwmon(struct device *dev) { struct dell_smm_data *data = dev_get_drvdata(dev); + struct thermal_cooling_device *cdev; + struct dell_smm_cooling_data *cdata; struct device *dell_smm_hwmon_dev; int state, err; u8 i; @@ -967,6 +1016,19 @@ static int __init dell_smm_init_hwmon(struct device *dev) continue; data->fan[i] = true; + + /* the cooling device it not critical, ignore failures */ + cdata = devm_kmalloc(dev, sizeof(*cdata), GFP_KERNEL); + if (cdata) { + cdata->fan_num = i; + cdata->data = data; + cdev = devm_thermal_of_cooling_device_register(dev, NULL, "dell-smm-fan", + cdata, + &dell_smm_cooling_ops); + if (IS_ERR(cdev)) + devm_kfree(dev, cdata); + } + data->fan_nominal_speed[i] = devm_kmalloc_array(dev, data->i8k_fan_max + 1, sizeof(*data->fan_nominal_speed[i]), GFP_KERNEL); -- 2.30.2