Merge branch 'next' of git://git.kernel.org/pub/scm/linux/kernel/git/benh/powerpc
[cascardo/linux.git] / drivers / char / i8k.c
index 93dcad0..65525c7 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);
 
@@ -276,7 +282,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);
@@ -521,7 +527,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,
@@ -535,7 +541,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);
@@ -544,20 +550,6 @@ static ssize_t i8k_hwmon_set_pwm(struct device *dev,
        return err < 0 ? -EIO : count;
 }
 
-static ssize_t i8k_hwmon_show_label(struct device *dev,
-                                   struct device_attribute *devattr,
-                                   char *buf)
-{
-       static const char *labels[3] = {
-               "CPU",
-               "Left Fan",
-               "Right Fan",
-       };
-       int index = to_sensor_dev_attr(devattr)->index;
-
-       return sprintf(buf, "%s\n", labels[index]);
-}
-
 static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO, i8k_hwmon_show_temp, NULL, 0);
 static SENSOR_DEVICE_ATTR(temp2_input, S_IRUGO, i8k_hwmon_show_temp, NULL, 1);
 static SENSOR_DEVICE_ATTR(temp3_input, S_IRUGO, i8k_hwmon_show_temp, NULL, 2);
@@ -570,41 +562,34 @@ static SENSOR_DEVICE_ATTR(fan2_input, S_IRUGO, i8k_hwmon_show_fan, NULL,
                          I8K_FAN_RIGHT);
 static SENSOR_DEVICE_ATTR(pwm2, S_IRUGO | S_IWUSR, i8k_hwmon_show_pwm,
                          i8k_hwmon_set_pwm, I8K_FAN_RIGHT);
-static SENSOR_DEVICE_ATTR(temp1_label, S_IRUGO, i8k_hwmon_show_label, NULL, 0);
-static SENSOR_DEVICE_ATTR(fan1_label, S_IRUGO, i8k_hwmon_show_label, NULL, 1);
-static SENSOR_DEVICE_ATTR(fan2_label, S_IRUGO, i8k_hwmon_show_label, NULL, 2);
 
 static struct attribute *i8k_attrs[] = {
        &sensor_dev_attr_temp1_input.dev_attr.attr,     /* 0 */
-       &sensor_dev_attr_temp1_label.dev_attr.attr,     /* 1 */
-       &sensor_dev_attr_temp2_input.dev_attr.attr,     /* 2 */
-       &sensor_dev_attr_temp3_input.dev_attr.attr,     /* 3 */
-       &sensor_dev_attr_temp4_input.dev_attr.attr,     /* 4 */
-       &sensor_dev_attr_fan1_input.dev_attr.attr,      /* 5 */
-       &sensor_dev_attr_pwm1.dev_attr.attr,            /* 6 */
-       &sensor_dev_attr_fan1_label.dev_attr.attr,      /* 7 */
-       &sensor_dev_attr_fan2_input.dev_attr.attr,      /* 8 */
-       &sensor_dev_attr_pwm2.dev_attr.attr,            /* 9 */
-       &sensor_dev_attr_fan2_label.dev_attr.attr,      /* 10 */
+       &sensor_dev_attr_temp2_input.dev_attr.attr,     /* 1 */
+       &sensor_dev_attr_temp3_input.dev_attr.attr,     /* 2 */
+       &sensor_dev_attr_temp4_input.dev_attr.attr,     /* 3 */
+       &sensor_dev_attr_fan1_input.dev_attr.attr,      /* 4 */
+       &sensor_dev_attr_pwm1.dev_attr.attr,            /* 5 */
+       &sensor_dev_attr_fan2_input.dev_attr.attr,      /* 6 */
+       &sensor_dev_attr_pwm2.dev_attr.attr,            /* 7 */
        NULL
 };
 
 static umode_t i8k_is_visible(struct kobject *kobj, struct attribute *attr,
                              int index)
 {
-       if ((index == 0 || index == 1) &&
-           !(i8k_hwmon_flags & I8K_HWMON_HAVE_TEMP1))
+       if (index == 0 && !(i8k_hwmon_flags & I8K_HWMON_HAVE_TEMP1))
                return 0;
-       if (index == 2 && !(i8k_hwmon_flags & I8K_HWMON_HAVE_TEMP2))
+       if (index == 1 && !(i8k_hwmon_flags & I8K_HWMON_HAVE_TEMP2))
                return 0;
-       if (index == 3 && !(i8k_hwmon_flags & I8K_HWMON_HAVE_TEMP3))
+       if (index == 2 && !(i8k_hwmon_flags & I8K_HWMON_HAVE_TEMP3))
                return 0;
-       if (index == 4 && !(i8k_hwmon_flags & I8K_HWMON_HAVE_TEMP4))
+       if (index == 3 && !(i8k_hwmon_flags & I8K_HWMON_HAVE_TEMP4))
                return 0;
-       if (index >= 5 && index <= 7 &&
+       if (index >= 4 && index <= 5 &&
            !(i8k_hwmon_flags & I8K_HWMON_HAVE_FAN1))
                return 0;
-       if (index >= 8 && index <= 10 &&
+       if (index >= 6 && index <= 7 &&
            !(i8k_hwmon_flags & I8K_HWMON_HAVE_FAN2))
                return 0;
 
@@ -659,6 +644,37 @@ static int __init i8k_init_hwmon(void)
        return 0;
 }
 
+struct i8k_config_data {
+       int fan_mult;
+       int fan_max;
+};
+
+enum i8k_configs {
+       DELL_LATITUDE_D520,
+       DELL_PRECISION_490,
+       DELL_STUDIO,
+       DELL_XPS_M140,
+};
+
+static const struct i8k_config_data i8k_config_data[] = {
+       [DELL_LATITUDE_D520] = {
+               .fan_mult = 1,
+               .fan_max = I8K_FAN_TURBO,
+       },
+       [DELL_PRECISION_490] = {
+               .fan_mult = 1,
+               .fan_max = I8K_FAN_TURBO,
+       },
+       [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",
@@ -681,6 +697,14 @@ static struct dmi_system_id i8k_dmi_table[] __initdata = {
                        DMI_MATCH(DMI_PRODUCT_NAME, "Inspiron"),
                },
        },
+       {
+               .ident = "Dell Latitude D520",
+               .matches = {
+                       DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
+                       DMI_MATCH(DMI_PRODUCT_NAME, "Latitude D520"),
+               },
+               .driver_data = (void *)&i8k_config_data[DELL_LATITUDE_D520],
+       },
        {
                .ident = "Dell Latitude 2",
                .matches = {
@@ -702,6 +726,15 @@ static struct dmi_system_id i8k_dmi_table[] __initdata = {
                        DMI_MATCH(DMI_PRODUCT_NAME, "MP061"),
                },
        },
+       {
+               .ident = "Dell Precision 490",
+               .matches = {
+                       DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
+                       DMI_MATCH(DMI_PRODUCT_NAME,
+                                 "Precision WorkStation 490"),
+               },
+               .driver_data = (void *)&i8k_config_data[DELL_PRECISION_490],
+       },
        {
                .ident = "Dell Precision",
                .matches = {
@@ -729,7 +762,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",
@@ -737,7 +770,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],
        },
        { }
 };
@@ -777,9 +810,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;
 }