hwmon: (dell-smm) Add support for fanX_min, fanX_max and fanX_target

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>
This commit is contained in:
Armin Wolf 2021-09-27 00:10:43 +02:00 committed by Guenter Roeck
parent 51369c0f05
commit b1986c8e31
2 changed files with 58 additions and 6 deletions

View 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,

View 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,