i8k: Add support for configurable maximum fan speed value
authorGuenter Roeck <linux@roeck-us.net>
Sat, 21 Jun 2014 15:08:10 +0000 (08:08 -0700)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Wed, 9 Jul 2014 23:41:36 +0000 (16:41 -0700)
Newer Dell systems provide more granular fan speed selection.
Add support for it.

Signed-off-by: Guenter Roeck <linux@roeck-us.net>
Cc: Andreas Mohr <andi@lisas.de>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
drivers/char/i8k.c

index 3dc1a724ecc2e586865958e58fd984a33f0583aa..6a6c085efa69de73353f5b4b38c0f87e88f9382e 100644 (file)
@@ -65,6 +65,8 @@ static char bios_version[4];
 static struct device *i8k_hwmon_dev;
 static u32 i8k_hwmon_flags;
 static int i8k_fan_mult;
+static int i8k_pwm_mult;
+static int i8k_fan_max = I8K_FAN_HIGH;
 
 #define I8K_HWMON_HAVE_TEMP1   (1 << 0)
 #define I8K_HWMON_HAVE_TEMP2   (1 << 1)
@@ -97,6 +99,10 @@ static int fan_mult = I8K_FAN_MULT;
 module_param(fan_mult, int, 0);
 MODULE_PARM_DESC(fan_mult, "Factor to multiply fan speed with");
 
+static int fan_max = I8K_FAN_HIGH;
+module_param(fan_max, int, 0);
+MODULE_PARM_DESC(fan_max, "Maximum configurable fan speed");
+
 static int i8k_open_fs(struct inode *inode, struct file *file);
 static long i8k_ioctl(struct file *, unsigned int, unsigned long);
 
@@ -274,7 +280,7 @@ static int i8k_set_fan(int fan, int speed)
 {
        struct smm_regs regs = { .eax = I8K_SMM_SET_FAN, };
 
-       speed = (speed < 0) ? 0 : ((speed > I8K_FAN_MAX) ? I8K_FAN_MAX : speed);
+       speed = (speed < 0) ? 0 : ((speed > i8k_fan_max) ? i8k_fan_max : speed);
        regs.ebx = (fan & 0xff) | (speed << 8);
 
        return i8k_smm(&regs) ? : i8k_get_fan_status(fan);
@@ -519,7 +525,7 @@ static ssize_t i8k_hwmon_show_pwm(struct device *dev,
        status = i8k_get_fan_status(index);
        if (status < 0)
                return -EIO;
-       return sprintf(buf, "%d\n", clamp_val(status * 128, 0, 255));
+       return sprintf(buf, "%d\n", clamp_val(status * i8k_pwm_mult, 0, 255));
 }
 
 static ssize_t i8k_hwmon_set_pwm(struct device *dev,
@@ -533,7 +539,7 @@ static ssize_t i8k_hwmon_set_pwm(struct device *dev,
        err = kstrtoul(buf, 10, &val);
        if (err)
                return err;
-       val = clamp_val(DIV_ROUND_CLOSEST(val, 128), 0, 2);
+       val = clamp_val(DIV_ROUND_CLOSEST(val, i8k_pwm_mult), 0, i8k_fan_max);
 
        mutex_lock(&i8k_mutex);
        err = i8k_set_fan(index, val);
@@ -636,6 +642,27 @@ static int __init i8k_init_hwmon(void)
        return 0;
 }
 
+struct i8k_config_data {
+       int fan_mult;
+       int fan_max;
+};
+
+enum i8k_configs {
+       DELL_STUDIO,
+       DELL_XPS_M140,
+};
+
+static const struct i8k_config_data i8k_config_data[] = {
+       [DELL_STUDIO] = {
+               .fan_mult = 1,
+               .fan_max = I8K_FAN_HIGH,
+       },
+       [DELL_XPS_M140] = {
+               .fan_mult = 1,
+               .fan_max = I8K_FAN_HIGH,
+       },
+};
+
 static struct dmi_system_id i8k_dmi_table[] __initdata = {
        {
                .ident = "Dell Inspiron",
@@ -706,7 +733,7 @@ static struct dmi_system_id i8k_dmi_table[] __initdata = {
                        DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
                        DMI_MATCH(DMI_PRODUCT_NAME, "Studio"),
                },
-               .driver_data = (void *)1,       /* fan multiplier override */
+               .driver_data = (void *)&i8k_config_data[DELL_STUDIO],
        },
        {
                .ident = "Dell XPS M140",
@@ -714,7 +741,7 @@ static struct dmi_system_id i8k_dmi_table[] __initdata = {
                        DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
                        DMI_MATCH(DMI_PRODUCT_NAME, "MXC051"),
                },
-               .driver_data = (void *)1,       /* fan multiplier override */
+               .driver_data = (void *)&i8k_config_data[DELL_XPS_M140],
        },
        { }
 };
@@ -754,9 +781,17 @@ static int __init i8k_probe(void)
        }
 
        i8k_fan_mult = fan_mult;
+       i8k_fan_max = fan_max ? : I8K_FAN_HIGH; /* Must not be 0 */
        id = dmi_first_match(i8k_dmi_table);
-       if (id && fan_mult == I8K_FAN_MULT && id->driver_data)
-               i8k_fan_mult = (unsigned long)id->driver_data;
+       if (id && id->driver_data) {
+               const struct i8k_config_data *conf = id->driver_data;
+
+               if (fan_mult == I8K_FAN_MULT && conf->fan_mult)
+                       i8k_fan_mult = conf->fan_mult;
+               if (fan_max == I8K_FAN_HIGH && conf->fan_max)
+                       i8k_fan_max = conf->fan_max;
+       }
+       i8k_pwm_mult = DIV_ROUND_UP(255, i8k_fan_max);
 
        return 0;
 }