fanotify: info leak in copy_event_to_user()
[pandora-kernel.git] / drivers / hwmon / w83627ehf.c
index 36d7f27..b6a3ce3 100644 (file)
@@ -1,7 +1,7 @@
 /*
     w83627ehf - Driver for the hardware monitoring functionality of
                the Winbond W83627EHF Super-I/O chip
-    Copyright (C) 2005  Jean Delvare <khali@linux-fr.org>
+    Copyright (C) 2005-2011  Jean Delvare <khali@linux-fr.org>
     Copyright (C) 2006  Yuan Mu (Winbond),
                        Rudolf Marek <r.marek@assembler.cz>
                        David Hubbard <david.c.hubbard@gmail.com>
@@ -39,6 +39,7 @@
                                               0x8860 0xa1
     w83627dhg    9      5       4       3      0xa020 0xc1    0x5ca3
     w83627dhg-p  9      5       4       3      0xb070 0xc1    0x5ca3
+    w83627uhg    8      2       2       3      0xa230 0xc1    0x5ca3
     w83667hg     9      5       3       3      0xa510 0xc1    0x5ca3
     w83667hg-b   9      5       3       4      0xb350 0xc1    0x5ca3
     nct6775f     9      4       3       9      0xb470 0xc1    0x5ca3
 #include <linux/io.h>
 #include "lm75.h"
 
-enum kinds { w83627ehf, w83627dhg, w83627dhg_p, w83667hg, w83667hg_b, nct6775,
-       nct6776 };
+enum kinds {
+       w83627ehf, w83627dhg, w83627dhg_p, w83627uhg,
+       w83667hg, w83667hg_b, nct6775, nct6776,
+};
 
 /* used to set data->name = w83627ehf_device_names[data->sio_kind] */
 static const char * const w83627ehf_device_names[] = {
        "w83627ehf",
        "w83627dhg",
        "w83627dhg",
+       "w83627uhg",
        "w83667hg",
        "w83667hg",
        "nct6775",
@@ -104,6 +108,7 @@ MODULE_PARM_DESC(fan_debounce, "Enable debouncing for fan RPM signal");
 #define SIO_W83627EHG_ID       0x8860
 #define SIO_W83627DHG_ID       0xa020
 #define SIO_W83627DHG_P_ID     0xb070
+#define SIO_W83627UHG_ID       0xa230
 #define SIO_W83667HG_ID                0xa510
 #define SIO_W83667HG_B_ID      0xb350
 #define SIO_NCT6775_ID         0xb470
@@ -197,6 +202,9 @@ static const u16 W83627EHF_REG_TEMP_CONFIG[] = { 0, 0x152, 0x252, 0 };
 #define W83627EHF_REG_ALARM2           0x45A
 #define W83627EHF_REG_ALARM3           0x45B
 
+#define W83627EHF_REG_CASEOPEN_DET     0x42 /* SMI STATUS #2 */
+#define W83627EHF_REG_CASEOPEN_CLR     0x46 /* SMI MASK #3 */
+
 /* SmartFan registers */
 #define W83627EHF_REG_FAN_STEPUP_TIME 0x0f
 #define W83627EHF_REG_FAN_STEPDOWN_TIME 0x0e
@@ -316,7 +324,7 @@ static const char *const nct6776_temp_label[] = {
 
 #define NUM_REG_TEMP   ARRAY_SIZE(NCT6775_REG_TEMP)
 
-static inline int is_word_sized(u16 reg)
+static int is_word_sized(u16 reg)
 {
        return ((((reg & 0xff00) == 0x100
              || (reg & 0xff00) == 0x200)
@@ -385,35 +393,23 @@ div_from_reg(u8 reg)
        return 1 << reg;
 }
 
-static inline int
-temp_from_reg(u16 reg, s16 regval)
-{
-       if (is_word_sized(reg))
-               return LM75_TEMP_FROM_REG(regval);
-       return ((s8)regval) * 1000;
-}
-
-static inline u16
-temp_to_reg(u16 reg, long temp)
-{
-       if (is_word_sized(reg))
-               return LM75_TEMP_TO_REG(temp);
-       return (s8)DIV_ROUND_CLOSEST(SENSORS_LIMIT(temp, -127000, 128000),
-                                    1000);
-}
-
-/* Some of analog inputs have internal scaling (2x), 8mV is ADC LSB */
-
-static u8 scale_in[10] = { 8, 8, 16, 16, 8, 8, 8, 16, 16, 8 };
+/* Some of the voltage inputs have internal scaling, the tables below
+ * contain 8 (the ADC LSB in mV) * scaling factor * 100 */
+static const u16 scale_in_common[10] = {
+       800, 800, 1600, 1600, 800, 800, 800, 1600, 1600, 800
+};
+static const u16 scale_in_w83627uhg[9] = {
+       800, 800, 3328, 3424, 800, 800, 0, 3328, 3400
+};
 
-static inline long in_from_reg(u8 reg, u8 nr)
+static inline long in_from_reg(u8 reg, u8 nr, const u16 *scale_in)
 {
-       return reg * scale_in[nr];
+       return DIV_ROUND_CLOSEST(reg * scale_in[nr], 100);
 }
 
-static inline u8 in_to_reg(u32 val, u8 nr)
+static inline u8 in_to_reg(u32 val, u8 nr, const u16 *scale_in)
 {
-       return SENSORS_LIMIT(((val + (scale_in[nr] / 2)) / scale_in[nr]), 0,
+       return SENSORS_LIMIT(DIV_ROUND_CLOSEST(val * 100, scale_in[nr]), 0,
                             255);
 }
 
@@ -444,6 +440,7 @@ struct w83627ehf_data {
        const u16 *REG_FAN_STOP_TIME;
        const u16 *REG_FAN_MAX_OUTPUT;
        const u16 *REG_FAN_STEP_OUTPUT;
+       const u16 *scale_in;
 
        unsigned int (*fan_from_reg)(u16 reg, unsigned int divreg);
        unsigned int (*fan_from_reg_min)(u16 reg, unsigned int divreg);
@@ -469,6 +466,7 @@ struct w83627ehf_data {
        s16 temp_max[9];
        s16 temp_max_hyst[9];
        u32 alarms;
+       u8 caseopen;
 
        u8 pwm_mode[4]; /* 0->DC variable voltage, 1->PWM variable duty cycle */
        u8 pwm_enable[4]; /* 1->manual
@@ -494,7 +492,8 @@ struct w83627ehf_data {
        u8 vrm;
 
        u16 have_temp;
-       u8 in6_skip;
+       u8 in6_skip:1;
+       u8 temp3_val_only:1;
 };
 
 struct w83627ehf_sio_data {
@@ -557,6 +556,26 @@ static int w83627ehf_write_value(struct w83627ehf_data *data, u16 reg,
        return 0;
 }
 
+/* We left-align 8-bit temperature values to make the code simpler */
+static u16 w83627ehf_read_temp(struct w83627ehf_data *data, u16 reg)
+{
+       u16 res;
+
+       res = w83627ehf_read_value(data, reg);
+       if (!is_word_sized(reg))
+               res <<= 8;
+
+       return res;
+}
+
+static int w83627ehf_write_temp(struct w83627ehf_data *data, u16 reg,
+                                      u16 value)
+{
+       if (!is_word_sized(reg))
+               value >>= 8;
+       return w83627ehf_write_value(data, reg, value);
+}
+
 /* This function assumes that the caller holds data->update_lock */
 static void nct6775_write_fan_div(struct w83627ehf_data *data, int nr)
 {
@@ -771,6 +790,9 @@ static struct w83627ehf_data *w83627ehf_update_device(struct device *dev)
 
                /* Measured voltages and limits */
                for (i = 0; i < data->in_num; i++) {
+                       if ((i == 6) && data->in6_skip)
+                               continue;
+
                        data->in[i] = w83627ehf_read_value(data,
                                      W83627EHF_REG_IN(i));
                        data->in_min[i] = w83627ehf_read_value(data,
@@ -855,15 +877,15 @@ static struct w83627ehf_data *w83627ehf_update_device(struct device *dev)
                for (i = 0; i < NUM_REG_TEMP; i++) {
                        if (!(data->have_temp & (1 << i)))
                                continue;
-                       data->temp[i] = w83627ehf_read_value(data,
+                       data->temp[i] = w83627ehf_read_temp(data,
                                                data->reg_temp[i]);
                        if (data->reg_temp_over[i])
                                data->temp_max[i]
-                                 = w83627ehf_read_value(data,
+                                 = w83627ehf_read_temp(data,
                                                data->reg_temp_over[i]);
                        if (data->reg_temp_hyst[i])
                                data->temp_max_hyst[i]
-                                 = w83627ehf_read_value(data,
+                                 = w83627ehf_read_temp(data,
                                                data->reg_temp_hyst[i]);
                }
 
@@ -874,6 +896,9 @@ static struct w83627ehf_data *w83627ehf_update_device(struct device *dev)
                               (w83627ehf_read_value(data,
                                        W83627EHF_REG_ALARM3) << 16);
 
+               data->caseopen = w83627ehf_read_value(data,
+                                               W83627EHF_REG_CASEOPEN_DET);
+
                data->last_updated = jiffies;
                data->valid = 1;
        }
@@ -894,7 +919,8 @@ show_##reg(struct device *dev, struct device_attribute *attr, \
        struct sensor_device_attribute *sensor_attr = \
                to_sensor_dev_attr(attr); \
        int nr = sensor_attr->index; \
-       return sprintf(buf, "%ld\n", in_from_reg(data->reg[nr], nr)); \
+       return sprintf(buf, "%ld\n", in_from_reg(data->reg[nr], nr, \
+                      data->scale_in)); \
 }
 show_in_reg(in)
 show_in_reg(in_min)
@@ -915,7 +941,7 @@ store_in_##reg(struct device *dev, struct device_attribute *attr, \
        if (err < 0) \
                return err; \
        mutex_lock(&data->update_lock); \
-       data->in_##reg[nr] = in_to_reg(val, nr); \
+       data->in_##reg[nr] = in_to_reg(val, nr, data->scale_in); \
        w83627ehf_write_value(data, W83627EHF_REG_IN_##REG(nr), \
                              data->in_##reg[nr]); \
        mutex_unlock(&data->update_lock); \
@@ -1156,8 +1182,7 @@ show_##reg(struct device *dev, struct device_attribute *attr, \
        struct sensor_device_attribute *sensor_attr = \
                to_sensor_dev_attr(attr); \
        int nr = sensor_attr->index; \
-       return sprintf(buf, "%d\n", \
-                      temp_from_reg(data->addr[nr], data->reg[nr])); \
+       return sprintf(buf, "%d\n", LM75_TEMP_FROM_REG(data->reg[nr])); \
 }
 show_temp_reg(reg_temp, temp);
 show_temp_reg(reg_temp_over, temp_max);
@@ -1178,9 +1203,8 @@ store_##reg(struct device *dev, struct device_attribute *attr, \
        if (err < 0) \
                return err; \
        mutex_lock(&data->update_lock); \
-       data->reg[nr] = temp_to_reg(data->addr[nr], val); \
-       w83627ehf_write_value(data, data->addr[nr], \
-                             data->reg[nr]); \
+       data->reg[nr] = LM75_TEMP_TO_REG(val); \
+       w83627ehf_write_temp(data, data->addr[nr], data->reg[nr]); \
        mutex_unlock(&data->update_lock); \
        return count; \
 }
@@ -1295,6 +1319,7 @@ store_pwm_mode(struct device *dev, struct device_attribute *attr,
 {
        struct w83627ehf_data *data = dev_get_drvdata(dev);
        struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr);
+       struct w83627ehf_sio_data *sio_data = dev->platform_data;
        int nr = sensor_attr->index;
        unsigned long val;
        int err;
@@ -1306,6 +1331,11 @@ store_pwm_mode(struct device *dev, struct device_attribute *attr,
 
        if (val > 1)
                return -EINVAL;
+
+       /* On NCT67766F, DC mode is only supported for pwm1 */
+       if (sio_data->kind == nct6776 && nr && val != 1)
+               return -EINVAL;
+
        mutex_lock(&data->update_lock);
        reg = w83627ehf_read_value(data, W83627EHF_REG_PWM_ENABLE[nr]);
        data->pwm_mode[nr] = val;
@@ -1577,7 +1607,7 @@ store_##reg(struct device *dev, struct device_attribute *attr, \
        val = step_time_to_reg(val, data->pwm_mode[nr]); \
        mutex_lock(&data->update_lock); \
        data->reg[nr] = val; \
-       w83627ehf_write_value(data, W83627EHF_REG_##REG[nr], val); \
+       w83627ehf_write_value(data, data->REG_##REG[nr], val); \
        mutex_unlock(&data->update_lock); \
        return count; \
 } \
@@ -1606,25 +1636,28 @@ static struct sensor_device_attribute sda_sf3_arrays_fan4[] = {
                    store_fan_step_output, 3),
 };
 
+static struct sensor_device_attribute sda_sf3_arrays_fan3[] = {
+       SENSOR_ATTR(pwm3_stop_time, S_IWUSR | S_IRUGO, show_fan_stop_time,
+                   store_fan_stop_time, 2),
+       SENSOR_ATTR(pwm3_start_output, S_IWUSR | S_IRUGO, show_fan_start_output,
+                   store_fan_start_output, 2),
+       SENSOR_ATTR(pwm3_stop_output, S_IWUSR | S_IRUGO, show_fan_stop_output,
+                   store_fan_stop_output, 2),
+};
+
 static struct sensor_device_attribute sda_sf3_arrays[] = {
        SENSOR_ATTR(pwm1_stop_time, S_IWUSR | S_IRUGO, show_fan_stop_time,
                    store_fan_stop_time, 0),
        SENSOR_ATTR(pwm2_stop_time, S_IWUSR | S_IRUGO, show_fan_stop_time,
                    store_fan_stop_time, 1),
-       SENSOR_ATTR(pwm3_stop_time, S_IWUSR | S_IRUGO, show_fan_stop_time,
-                   store_fan_stop_time, 2),
        SENSOR_ATTR(pwm1_start_output, S_IWUSR | S_IRUGO, show_fan_start_output,
                    store_fan_start_output, 0),
        SENSOR_ATTR(pwm2_start_output, S_IWUSR | S_IRUGO, show_fan_start_output,
                    store_fan_start_output, 1),
-       SENSOR_ATTR(pwm3_start_output, S_IWUSR | S_IRUGO, show_fan_start_output,
-                   store_fan_start_output, 2),
        SENSOR_ATTR(pwm1_stop_output, S_IWUSR | S_IRUGO, show_fan_stop_output,
                    store_fan_stop_output, 0),
        SENSOR_ATTR(pwm2_stop_output, S_IWUSR | S_IRUGO, show_fan_stop_output,
                    store_fan_stop_output, 1),
-       SENSOR_ATTR(pwm3_stop_output, S_IWUSR | S_IRUGO, show_fan_stop_output,
-                   store_fan_stop_output, 2),
 };
 
 
@@ -1655,6 +1688,48 @@ show_vid(struct device *dev, struct device_attribute *attr, char *buf)
 }
 static DEVICE_ATTR(cpu0_vid, S_IRUGO, show_vid, NULL);
 
+
+/* Case open detection */
+
+static ssize_t
+show_caseopen(struct device *dev, struct device_attribute *attr, char *buf)
+{
+       struct w83627ehf_data *data = w83627ehf_update_device(dev);
+
+       return sprintf(buf, "%d\n",
+               !!(data->caseopen & to_sensor_dev_attr_2(attr)->index));
+}
+
+static ssize_t
+clear_caseopen(struct device *dev, struct device_attribute *attr,
+                       const char *buf, size_t count)
+{
+       struct w83627ehf_data *data = dev_get_drvdata(dev);
+       unsigned long val;
+       u16 reg, mask;
+
+       if (strict_strtoul(buf, 10, &val) || val != 0)
+               return -EINVAL;
+
+       mask = to_sensor_dev_attr_2(attr)->nr;
+
+       mutex_lock(&data->update_lock);
+       reg = w83627ehf_read_value(data, W83627EHF_REG_CASEOPEN_CLR);
+       w83627ehf_write_value(data, W83627EHF_REG_CASEOPEN_CLR, reg | mask);
+       w83627ehf_write_value(data, W83627EHF_REG_CASEOPEN_CLR, reg & ~mask);
+       data->valid = 0;        /* Force cache refresh */
+       mutex_unlock(&data->update_lock);
+
+       return count;
+}
+
+static struct sensor_device_attribute_2 sda_caseopen[] = {
+       SENSOR_ATTR_2(intrusion0_alarm, S_IWUSR | S_IRUGO, show_caseopen,
+                       clear_caseopen, 0x80, 0x10),
+       SENSOR_ATTR_2(intrusion1_alarm, S_IWUSR | S_IRUGO, show_caseopen,
+                       clear_caseopen, 0x40, 0x40),
+};
+
 /*
  * Driver and device management
  */
@@ -1675,6 +1750,8 @@ static void w83627ehf_device_remove_files(struct device *dev)
                    data->REG_FAN_STEP_OUTPUT[attr->index] != 0xff)
                        device_remove_file(dev, &attr->dev_attr);
        }
+       for (i = 0; i < ARRAY_SIZE(sda_sf3_arrays_fan3); i++)
+               device_remove_file(dev, &sda_sf3_arrays_fan3[i].dev_attr);
        for (i = 0; i < ARRAY_SIZE(sda_sf3_arrays_fan4); i++)
                device_remove_file(dev, &sda_sf3_arrays_fan4[i].dev_attr);
        for (i = 0; i < data->in_num; i++) {
@@ -1703,6 +1780,8 @@ static void w83627ehf_device_remove_files(struct device *dev)
                        continue;
                device_remove_file(dev, &sda_temp_input[i].dev_attr);
                device_remove_file(dev, &sda_temp_label[i].dev_attr);
+               if (i == 2 && data->temp3_val_only)
+                       continue;
                device_remove_file(dev, &sda_temp_max[i].dev_attr);
                device_remove_file(dev, &sda_temp_max_hyst[i].dev_attr);
                if (i > 2)
@@ -1711,6 +1790,9 @@ static void w83627ehf_device_remove_files(struct device *dev)
                device_remove_file(dev, &sda_temp_type[i].dev_attr);
        }
 
+       device_remove_file(dev, &sda_caseopen[0].dev_attr);
+       device_remove_file(dev, &sda_caseopen[1].dev_attr);
+
        device_remove_file(dev, &dev_attr_name);
        device_remove_file(dev, &dev_attr_cpu0_vid);
 }
@@ -1752,11 +1834,24 @@ static inline void __devinit w83627ehf_init_device(struct w83627ehf_data *data,
        case w83627ehf:
                diode = w83627ehf_read_value(data, W83627EHF_REG_DIODE);
                break;
+       case w83627uhg:
+               diode = 0x00;
+               break;
        default:
                diode = 0x70;
        }
        for (i = 0; i < 3; i++) {
-               if ((tmp & (0x02 << i)))
+               const char *label = NULL;
+
+               if (data->temp_label)
+                       label = data->temp_label[data->temp_src[i]];
+
+               /* Digital source overrides analog type */
+               if (label && strncmp(label, "PECI", 4) == 0)
+                       data->temp_type[i] = 6;
+               else if (label && strncmp(label, "AMD", 3) == 0)
+                       data->temp_type[i] = 5;
+               else if ((tmp & (0x02 << i)))
                        data->temp_type[i] = (diode & (0x10 << i)) ? 1 : 3;
                else
                        data->temp_type[i] = 4; /* thermistor */
@@ -1789,13 +1884,115 @@ static void w82627ehf_swap_tempreg(struct w83627ehf_data *data,
        data->reg_temp_config[r2] = tmp;
 }
 
+static void __devinit
+w83627ehf_set_temp_reg_ehf(struct w83627ehf_data *data, int n_temp)
+{
+       int i;
+
+       for (i = 0; i < n_temp; i++) {
+               data->reg_temp[i] = W83627EHF_REG_TEMP[i];
+               data->reg_temp_over[i] = W83627EHF_REG_TEMP_OVER[i];
+               data->reg_temp_hyst[i] = W83627EHF_REG_TEMP_HYST[i];
+               data->reg_temp_config[i] = W83627EHF_REG_TEMP_CONFIG[i];
+       }
+}
+
+static void __devinit
+w83627ehf_check_fan_inputs(const struct w83627ehf_sio_data *sio_data,
+                          struct w83627ehf_data *data)
+{
+       int fan3pin, fan4pin, fan4min, fan5pin, regval;
+
+       /* The W83627UHG is simple, only two fan inputs, no config */
+       if (sio_data->kind == w83627uhg) {
+               data->has_fan = 0x03; /* fan1 and fan2 */
+               data->has_fan_min = 0x03;
+               return;
+       }
+
+       superio_enter(sio_data->sioreg);
+
+       /* fan4 and fan5 share some pins with the GPIO and serial flash */
+       if (sio_data->kind == nct6775) {
+               /* On NCT6775, fan4 shares pins with the fdc interface */
+               fan3pin = 1;
+               fan4pin = !(superio_inb(sio_data->sioreg, 0x2A) & 0x80);
+               fan4min = 0;
+               fan5pin = 0;
+       } else if (sio_data->kind == nct6776) {
+               bool gpok = superio_inb(sio_data->sioreg, 0x27) & 0x80;
+
+               superio_select(sio_data->sioreg, W83627EHF_LD_HWM);
+               regval = superio_inb(sio_data->sioreg, SIO_REG_ENABLE);
+
+               if (regval & 0x80)
+                       fan3pin = gpok;
+               else
+                       fan3pin = !(superio_inb(sio_data->sioreg, 0x24) & 0x40);
+
+               if (regval & 0x40)
+                       fan4pin = gpok;
+               else
+                       fan4pin = !!(superio_inb(sio_data->sioreg, 0x1C) & 0x01);
+
+               if (regval & 0x20)
+                       fan5pin = gpok;
+               else
+                       fan5pin = !!(superio_inb(sio_data->sioreg, 0x1C) & 0x02);
+
+               fan4min = fan4pin;
+       } else if (sio_data->kind == w83667hg || sio_data->kind == w83667hg_b) {
+               fan3pin = 1;
+               fan4pin = superio_inb(sio_data->sioreg, 0x27) & 0x40;
+               fan5pin = superio_inb(sio_data->sioreg, 0x27) & 0x20;
+               fan4min = fan4pin;
+       } else {
+               fan3pin = 1;
+               fan4pin = !(superio_inb(sio_data->sioreg, 0x29) & 0x06);
+               fan5pin = !(superio_inb(sio_data->sioreg, 0x24) & 0x02);
+               fan4min = fan4pin;
+       }
+
+       superio_exit(sio_data->sioreg);
+
+       data->has_fan = data->has_fan_min = 0x03; /* fan1 and fan2 */
+       data->has_fan |= (fan3pin << 2);
+       data->has_fan_min |= (fan3pin << 2);
+
+       if (sio_data->kind == nct6775 || sio_data->kind == nct6776) {
+               /*
+                * NCT6775F and NCT6776F don't have the W83627EHF_REG_FANDIV1
+                * register
+                */
+               data->has_fan |= (fan4pin << 3) | (fan5pin << 4);
+               data->has_fan_min |= (fan4min << 3) | (fan5pin << 4);
+       } else {
+               /*
+                * It looks like fan4 and fan5 pins can be alternatively used
+                * as fan on/off switches, but fan5 control is write only :/
+                * We assume that if the serial interface is disabled, designers
+                * connected fan5 as input unless they are emitting log 1, which
+                * is not the default.
+                */
+               regval = w83627ehf_read_value(data, W83627EHF_REG_FANDIV1);
+               if ((regval & (1 << 2)) && fan4pin) {
+                       data->has_fan |= (1 << 3);
+                       data->has_fan_min |= (1 << 3);
+               }
+               if (!(regval & (1 << 1)) && fan5pin) {
+                       data->has_fan |= (1 << 4);
+                       data->has_fan_min |= (1 << 4);
+               }
+       }
+}
+
 static int __devinit w83627ehf_probe(struct platform_device *pdev)
 {
        struct device *dev = &pdev->dev;
        struct w83627ehf_sio_data *sio_data = dev->platform_data;
        struct w83627ehf_data *data;
        struct resource *res;
-       u8 fan3pin, fan4pin, fan4min, fan5pin, en_vrm10;
+       u8 en_vrm10;
        int i, err = 0;
 
        res = platform_get_resource(pdev, IORESOURCE_IO, 0);
@@ -1807,7 +2004,8 @@ static int __devinit w83627ehf_probe(struct platform_device *pdev)
                goto exit;
        }
 
-       data = kzalloc(sizeof(struct w83627ehf_data), GFP_KERNEL);
+       data = devm_kzalloc(&pdev->dev, sizeof(struct w83627ehf_data),
+                           GFP_KERNEL);
        if (!data) {
                err = -ENOMEM;
                goto exit_release;
@@ -1817,27 +2015,29 @@ static int __devinit w83627ehf_probe(struct platform_device *pdev)
        mutex_init(&data->lock);
        mutex_init(&data->update_lock);
        data->name = w83627ehf_device_names[sio_data->kind];
+       data->bank = 0xff;              /* Force initial bank selection */
        platform_set_drvdata(pdev, data);
 
        /* 627EHG and 627EHF have 10 voltage inputs; 627DHG and 667HG have 9 */
        data->in_num = (sio_data->kind == w83627ehf) ? 10 : 9;
-       /* 667HG, NCT6775F, and NCT6776F have 3 pwms */
-       data->pwm_num = (sio_data->kind == w83667hg
-                        || sio_data->kind == w83667hg_b
-                        || sio_data->kind == nct6775
-                        || sio_data->kind == nct6776) ? 3 : 4;
+       /* 667HG, NCT6775F, and NCT6776F have 3 pwms, and 627UHG has only 2 */
+       switch (sio_data->kind) {
+       default:
+               data->pwm_num = 4;
+               break;
+       case w83667hg:
+       case w83667hg_b:
+       case nct6775:
+       case nct6776:
+               data->pwm_num = 3;
+               break;
+       case w83627uhg:
+               data->pwm_num = 2;
+               break;
+       }
 
+       /* Default to 3 temperature inputs, code below will adjust as needed */
        data->have_temp = 0x07;
-       /* Check temp3 configuration bit for 667HG */
-       if (sio_data->kind == w83667hg) {
-               u8 reg;
-
-               reg = w83627ehf_read_value(data, W83627EHF_REG_TEMP_CONFIG[2]);
-               if (reg & 0x01)
-                       data->have_temp &= ~(1 << 2);
-               else
-                       data->in6_skip = 1;     /* either temp3 or in6 */
-       }
 
        /* Deal with temperature register setup first. */
        if (sio_data->kind == nct6775 || sio_data->kind == nct6776) {
@@ -1914,16 +2114,12 @@ static int __devinit w83627ehf_probe(struct platform_device *pdev)
        } else if (sio_data->kind == w83667hg_b) {
                u8 reg;
 
+               w83627ehf_set_temp_reg_ehf(data, 4);
+
                /*
                 * Temperature sources are selected with bank 0, registers 0x49
                 * and 0x4a.
                 */
-               for (i = 0; i < ARRAY_SIZE(W83627EHF_REG_TEMP); i++) {
-                       data->reg_temp[i] = W83627EHF_REG_TEMP[i];
-                       data->reg_temp_over[i] = W83627EHF_REG_TEMP_OVER[i];
-                       data->reg_temp_hyst[i] = W83627EHF_REG_TEMP_HYST[i];
-                       data->reg_temp_config[i] = W83627EHF_REG_TEMP_CONFIG[i];
-               }
                reg = w83627ehf_read_value(data, 0x4a);
                data->temp_src[0] = reg >> 5;
                reg = w83627ehf_read_value(data, 0x49);
@@ -1956,14 +2152,61 @@ static int __devinit w83627ehf_probe(struct platform_device *pdev)
                    || (data->temp_src[3] == 2 && (data->have_temp & (1 << 3))))
                        data->in6_skip = 1;
 
+               data->temp_label = w83667hg_b_temp_label;
+       } else if (sio_data->kind == w83627uhg) {
+               u8 reg;
+
+               w83627ehf_set_temp_reg_ehf(data, 3);
+
+               /*
+                * Temperature sources for temp2 and temp3 are selected with
+                * bank 0, registers 0x49 and 0x4a.
+                */
+               data->temp_src[0] = 0;  /* SYSTIN */
+               reg = w83627ehf_read_value(data, 0x49) & 0x07;
+               /* Adjust to have the same mapping as other source registers */
+               if (reg == 0)
+                       data->temp_src[1] = 1;
+               else if (reg >= 2 && reg <= 5)
+                       data->temp_src[1] = reg + 2;
+               else    /* should never happen */
+                       data->have_temp &= ~(1 << 1);
+               reg = w83627ehf_read_value(data, 0x4a);
+               data->temp_src[2] = reg >> 5;
+
+               /*
+                * Skip temp3 if source is invalid or the same as temp1
+                * or temp2.
+                */
+               if (data->temp_src[2] == 2 || data->temp_src[2] == 3 ||
+                   data->temp_src[2] == data->temp_src[0] ||
+                   ((data->have_temp & (1 << 1)) &&
+                    data->temp_src[2] == data->temp_src[1]))
+                       data->have_temp &= ~(1 << 2);
+               else
+                       data->temp3_val_only = 1;       /* No limit regs */
+
+               data->in6_skip = 1;                     /* No VIN3 */
+
                data->temp_label = w83667hg_b_temp_label;
        } else {
+               w83627ehf_set_temp_reg_ehf(data, 3);
+
                /* Temperature sources are fixed */
-               for (i = 0; i < 3; i++) {
-                       data->reg_temp[i] = W83627EHF_REG_TEMP[i];
-                       data->reg_temp_over[i] = W83627EHF_REG_TEMP_OVER[i];
-                       data->reg_temp_hyst[i] = W83627EHF_REG_TEMP_HYST[i];
-                       data->reg_temp_config[i] = W83627EHF_REG_TEMP_CONFIG[i];
+
+               if (sio_data->kind == w83667hg) {
+                       u8 reg;
+
+                       /*
+                        * Chip supports either AUXTIN or VIN3. Try to find
+                        * out which one.
+                        */
+                       reg = w83627ehf_read_value(data,
+                                               W83627EHF_REG_TEMP_CONFIG[2]);
+                       if (reg & 0x01)
+                               data->have_temp &= ~(1 << 2);
+                       else
+                               data->in6_skip = 1;
                }
        }
 
@@ -2023,6 +2266,12 @@ static int __devinit w83627ehf_probe(struct platform_device *pdev)
                  W83627EHF_REG_FAN_STEP_OUTPUT_COMMON;
        }
 
+       /* Setup input voltage scaling factors */
+       if (sio_data->kind == w83627uhg)
+               data->scale_in = scale_in_w83627uhg;
+       else
+               data->scale_in = scale_in_common;
+
        /* Initialize the chip */
        w83627ehf_init_device(data, sio_data->kind);
 
@@ -2039,7 +2288,7 @@ static int __devinit w83627ehf_probe(struct platform_device *pdev)
                err = device_create_file(dev, &dev_attr_cpu0_vid);
                if (err)
                        goto exit_release;
-       } else {
+       } else if (sio_data->kind != w83627uhg) {
                superio_select(sio_data->sioreg, W83627EHF_LD_HWM);
                if (superio_inb(sio_data->sioreg, SIO_REG_VID_CTRL) & 0x80) {
                        /* Set VID input sensibility if needed. In theory the
@@ -2080,30 +2329,6 @@ static int __devinit w83627ehf_probe(struct platform_device *pdev)
                }
        }
 
-       /* fan4 and fan5 share some pins with the GPIO and serial flash */
-       if (sio_data->kind == nct6775) {
-               /* On NCT6775, fan4 shares pins with the fdc interface */
-               fan3pin = 1;
-               fan4pin = !(superio_inb(sio_data->sioreg, 0x2A) & 0x80);
-               fan4min = 0;
-               fan5pin = 0;
-       } else if (sio_data->kind == nct6776) {
-               fan3pin = !(superio_inb(sio_data->sioreg, 0x24) & 0x40);
-               fan4pin = !!(superio_inb(sio_data->sioreg, 0x1C) & 0x01);
-               fan5pin = !!(superio_inb(sio_data->sioreg, 0x1C) & 0x02);
-               fan4min = fan4pin;
-       } else if (sio_data->kind == w83667hg || sio_data->kind == w83667hg_b) {
-               fan3pin = 1;
-               fan4pin = superio_inb(sio_data->sioreg, 0x27) & 0x40;
-               fan5pin = superio_inb(sio_data->sioreg, 0x27) & 0x20;
-               fan4min = fan4pin;
-       } else {
-               fan3pin = 1;
-               fan4pin = !(superio_inb(sio_data->sioreg, 0x29) & 0x06);
-               fan5pin = !(superio_inb(sio_data->sioreg, 0x24) & 0x02);
-               fan4min = fan4pin;
-       }
-
        if (fan_debounce &&
            (sio_data->kind == nct6775 || sio_data->kind == nct6776)) {
                u8 tmp;
@@ -2121,34 +2346,7 @@ static int __devinit w83627ehf_probe(struct platform_device *pdev)
 
        superio_exit(sio_data->sioreg);
 
-       /* It looks like fan4 and fan5 pins can be alternatively used
-          as fan on/off switches, but fan5 control is write only :/
-          We assume that if the serial interface is disabled, designers
-          connected fan5 as input unless they are emitting log 1, which
-          is not the default. */
-
-       data->has_fan = data->has_fan_min = 0x03; /* fan1 and fan2 */
-
-       data->has_fan |= (fan3pin << 2);
-       data->has_fan_min |= (fan3pin << 2);
-
-       /*
-        * NCT6775F and NCT6776F don't have the W83627EHF_REG_FANDIV1 register
-        */
-       if (sio_data->kind == nct6775 || sio_data->kind == nct6776) {
-               data->has_fan |= (fan4pin << 3) | (fan5pin << 4);
-               data->has_fan_min |= (fan4min << 3) | (fan5pin << 4);
-       } else {
-               i = w83627ehf_read_value(data, W83627EHF_REG_FANDIV1);
-               if ((i & (1 << 2)) && fan4pin) {
-                       data->has_fan |= (1 << 3);
-                       data->has_fan_min |= (1 << 3);
-               }
-               if (!(i & (1 << 1)) && fan5pin) {
-                       data->has_fan |= (1 << 4);
-                       data->has_fan_min |= (1 << 4);
-               }
-       }
+       w83627ehf_check_fan_inputs(sio_data, data);
 
        /* Read fan clock dividers immediately */
        w83627ehf_update_fan_div_common(dev, data);
@@ -2180,7 +2378,14 @@ static int __devinit w83627ehf_probe(struct platform_device *pdev)
                                goto exit_remove;
                }
        }
-       /* if fan4 is enabled create the sf3 files for it */
+       /* if fan3 and fan4 are enabled create the sf3 files for them */
+       if ((data->has_fan & (1 << 2)) && data->pwm_num >= 3)
+               for (i = 0; i < ARRAY_SIZE(sda_sf3_arrays_fan3); i++) {
+                       err = device_create_file(dev,
+                                       &sda_sf3_arrays_fan3[i].dev_attr);
+                       if (err)
+                               goto exit_remove;
+               }
        if ((data->has_fan & (1 << 3)) && data->pwm_num >= 4)
                for (i = 0; i < ARRAY_SIZE(sda_sf3_arrays_fan4); i++) {
                        err = device_create_file(dev,
@@ -2248,6 +2453,8 @@ static int __devinit w83627ehf_probe(struct platform_device *pdev)
                        if (err)
                                goto exit_remove;
                }
+               if (i == 2 && data->temp3_val_only)
+                       continue;
                if (data->reg_temp_over[i]) {
                        err = device_create_file(dev,
                                &sda_temp_max[i].dev_attr);
@@ -2269,6 +2476,16 @@ static int __devinit w83627ehf_probe(struct platform_device *pdev)
                        goto exit_remove;
        }
 
+       err = device_create_file(dev, &sda_caseopen[0].dev_attr);
+       if (err)
+               goto exit_remove;
+
+       if (sio_data->kind == nct6776) {
+               err = device_create_file(dev, &sda_caseopen[1].dev_attr);
+               if (err)
+                       goto exit_remove;
+       }
+
        err = device_create_file(dev, &dev_attr_name);
        if (err)
                goto exit_remove;
@@ -2283,9 +2500,8 @@ static int __devinit w83627ehf_probe(struct platform_device *pdev)
 
 exit_remove:
        w83627ehf_device_remove_files(dev);
-       kfree(data);
-       platform_set_drvdata(pdev, NULL);
 exit_release:
+       platform_set_drvdata(pdev, NULL);
        release_region(res->start, IOREGION_LENGTH);
 exit:
        return err;
@@ -2299,7 +2515,6 @@ static int __devexit w83627ehf_remove(struct platform_device *pdev)
        w83627ehf_device_remove_files(&pdev->dev);
        release_region(data->addr, IOREGION_LENGTH);
        platform_set_drvdata(pdev, NULL);
-       kfree(data);
 
        return 0;
 }
@@ -2321,6 +2536,7 @@ static int __init w83627ehf_find(int sioaddr, unsigned short *addr,
        static const char __initdata sio_name_W83627EHG[] = "W83627EHG";
        static const char __initdata sio_name_W83627DHG[] = "W83627DHG";
        static const char __initdata sio_name_W83627DHG_P[] = "W83627DHG-P";
+       static const char __initdata sio_name_W83627UHG[] = "W83627UHG";
        static const char __initdata sio_name_W83667HG[] = "W83667HG";
        static const char __initdata sio_name_W83667HG_B[] = "W83667HG-B";
        static const char __initdata sio_name_NCT6775[] = "NCT6775F";
@@ -2353,6 +2569,10 @@ static int __init w83627ehf_find(int sioaddr, unsigned short *addr,
                sio_data->kind = w83627dhg_p;
                sio_name = sio_name_W83627DHG_P;
                break;
+       case SIO_W83627UHG_ID:
+               sio_data->kind = w83627uhg;
+               sio_name = sio_name_W83627UHG;
+               break;
        case SIO_W83667HG_ID:
                sio_data->kind = w83667hg;
                sio_name = sio_name_W83667HG;