hwmon: Remove Yuan Mu's address
[pandora-kernel.git] / drivers / hwmon / w83627ehf.c
index b6bd568..833faa2 100644 (file)
@@ -2,6 +2,9 @@
     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) 2006  Yuan Mu (Winbond),
+                        Rudolf Marek <r.marek@sh.cvut.cz>
+                        David Hubbard <david.c.hubbard@gmail.com>
 
     Shamelessly ripped from the w83627hf driver
     Copyright (C) 2003  Mark Studebaker
 
     Supports the following chips:
 
-    Chip        #vin    #fan    #pwm    #temp   chip_id man_id
-    w83627ehf   -       5       -       3       0x88    0x5ca3
-
-    This is a preliminary version of the driver, only supporting the
-    fan and temperature inputs. The chip does much more than that.
+    Chip        #vin    #fan    #pwm    #temp   chip_id    man_id
+    w83627ehf   10      5       4       3       0x88,0xa1  0x5ca3
 */
 
 #include <linux/module.h>
@@ -121,6 +121,14 @@ superio_exit(void)
 static const u16 W83627EHF_REG_FAN[] = { 0x28, 0x29, 0x2a, 0x3f, 0x553 };
 static const u16 W83627EHF_REG_FAN_MIN[] = { 0x3b, 0x3c, 0x3d, 0x3e, 0x55c };
 
+/* The W83627EHF registers for nr=7,8,9 are in bank 5 */
+#define W83627EHF_REG_IN_MAX(nr)       ((nr < 7) ? (0x2b + (nr) * 2) : \
+                                        (0x554 + (((nr) - 7) * 2)))
+#define W83627EHF_REG_IN_MIN(nr)       ((nr < 7) ? (0x2c + (nr) * 2) : \
+                                        (0x555 + (((nr) - 7) * 2)))
+#define W83627EHF_REG_IN(nr)           ((nr < 7) ? (0x20 + (nr)) : \
+                                        (0x550 + (nr) - 7))
+
 #define W83627EHF_REG_TEMP1            0x27
 #define W83627EHF_REG_TEMP1_HYST       0x3a
 #define W83627EHF_REG_TEMP1_OVER       0x39
@@ -136,10 +144,48 @@ static const u16 W83627EHF_REG_TEMP_CONFIG[] = { 0x152, 0x252 };
 #define W83627EHF_REG_DIODE            0x59
 #define W83627EHF_REG_SMI_OVT          0x4C
 
+#define W83627EHF_REG_ALARM1           0x459
+#define W83627EHF_REG_ALARM2           0x45A
+#define W83627EHF_REG_ALARM3           0x45B
+
+/* SmartFan registers */
+/* DC or PWM output fan configuration */
+static const u8 W83627EHF_REG_PWM_ENABLE[] = {
+       0x04,                   /* SYS FAN0 output mode and PWM mode */
+       0x04,                   /* CPU FAN0 output mode and PWM mode */
+       0x12,                   /* AUX FAN mode */
+       0x62,                   /* CPU fan1 mode */
+};
+
+static const u8 W83627EHF_PWM_MODE_SHIFT[] = { 0, 1, 0, 6 };
+static const u8 W83627EHF_PWM_ENABLE_SHIFT[] = { 2, 4, 1, 4 };
+
+/* FAN Duty Cycle, be used to control */
+static const u8 W83627EHF_REG_PWM[] = { 0x01, 0x03, 0x11, 0x61 };
+static const u8 W83627EHF_REG_TARGET[] = { 0x05, 0x06, 0x13, 0x63 };
+static const u8 W83627EHF_REG_TOLERANCE[] = { 0x07, 0x07, 0x14, 0x62 };
+
+
+/* Advanced Fan control, some values are common for all fans */
+static const u8 W83627EHF_REG_FAN_MIN_OUTPUT[] = { 0x08, 0x09, 0x15, 0x64 };
+static const u8 W83627EHF_REG_FAN_STOP_TIME[] = { 0x0C, 0x0D, 0x17, 0x66 };
+
 /*
  * Conversions
  */
 
+/* 1 is PWM mode, output in ms */
+static inline unsigned int step_time_from_reg(u8 reg, u8 mode)
+{
+       return mode ? 100 * reg : 400 * reg;
+}
+
+static inline u8 step_time_to_reg(unsigned int msec, u8 mode)
+{
+       return SENSORS_LIMIT((mode ? (msec + 50) / 100 :
+                                               (msec + 200) / 400), 1, 255);
+}
+
 static inline unsigned int
 fan_from_reg(u8 reg, unsigned int div)
 {
@@ -161,17 +207,31 @@ temp1_from_reg(s8 reg)
 }
 
 static inline s8
-temp1_to_reg(int temp)
+temp1_to_reg(int temp, int min, int max)
 {
-       if (temp <= -128000)
-               return -128;
-       if (temp >= 127000)
-               return 127;
+       if (temp <= min)
+               return min / 1000;
+       if (temp >= max)
+               return max / 1000;
        if (temp < 0)
                return (temp - 500) / 1000;
        return (temp + 500) / 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 };
+
+static inline long in_from_reg(u8 reg, u8 nr)
+{
+       return reg * scale_in[nr];
+}
+
+static inline u8 in_to_reg(u32 val, u8 nr)
+{
+       return SENSORS_LIMIT(((val + (scale_in[nr] / 2)) / scale_in[nr]), 0, 255);
+}
+
 /*
  * Data structures and manipulation thereof
  */
@@ -186,6 +246,9 @@ struct w83627ehf_data {
        unsigned long last_updated;     /* In jiffies */
 
        /* Register values */
+       u8 in[10];              /* Register value */
+       u8 in_max[10];          /* Register value */
+       u8 in_min[10];          /* Register value */
        u8 fan[5];
        u8 fan_min[5];
        u8 fan_div[5];
@@ -196,6 +259,17 @@ struct w83627ehf_data {
        s16 temp[2];
        s16 temp_max[2];
        s16 temp_max_hyst[2];
+       u32 alarms;
+
+       u8 pwm_mode[4]; /* 0->DC variable voltage, 1->PWM variable duty cycle */
+       u8 pwm_enable[4]; /* 1->manual
+                            2->thermal cruise (also called SmartFan I) */
+       u8 pwm[4];
+       u8 target_temp[4];
+       u8 tolerance[4];
+
+       u8 fan_min_output[4]; /* minimum fan speed */
+       u8 fan_stop_time[4];
 };
 
 static inline int is_word_sized(u16 reg)
@@ -322,6 +396,7 @@ static struct w83627ehf_data *w83627ehf_update_device(struct device *dev)
 {
        struct i2c_client *client = to_i2c_client(dev);
        struct w83627ehf_data *data = i2c_get_clientdata(client);
+       int pwmcfg = 0, tolerance = 0; /* shut up the compiler */
        int i;
 
        mutex_lock(&data->update_lock);
@@ -349,6 +424,16 @@ static struct w83627ehf_data *w83627ehf_update_device(struct device *dev)
                        data->fan_div[3] |= (i >> 5) & 0x04;
                }
 
+               /* Measured voltages and limits */
+               for (i = 0; i < 10; i++) {
+                       data->in[i] = w83627ehf_read_value(client,
+                                     W83627EHF_REG_IN(i));
+                       data->in_min[i] = w83627ehf_read_value(client,
+                                         W83627EHF_REG_IN_MIN(i));
+                       data->in_max[i] = w83627ehf_read_value(client,
+                                         W83627EHF_REG_IN_MAX(i));
+               }
+
                /* Measured fan speeds and limits */
                for (i = 0; i < 5; i++) {
                        if (!(data->has_fan & (1 << i)))
@@ -379,6 +464,34 @@ static struct w83627ehf_data *w83627ehf_update_device(struct device *dev)
                        }
                }
 
+               for (i = 0; i < 4; i++) {
+                       /* pwmcfg, tolarance mapped for i=0, i=1 to same reg */
+                       if (i != 1) {
+                               pwmcfg = w83627ehf_read_value(client,
+                                               W83627EHF_REG_PWM_ENABLE[i]);
+                               tolerance = w83627ehf_read_value(client,
+                                               W83627EHF_REG_TOLERANCE[i]);
+                       }
+                       data->pwm_mode[i] =
+                               ((pwmcfg >> W83627EHF_PWM_MODE_SHIFT[i]) & 1)
+                               ? 0 : 1;
+                       data->pwm_enable[i] =
+                                       ((pwmcfg >> W83627EHF_PWM_ENABLE_SHIFT[i])
+                                               & 3) + 1;
+                       data->pwm[i] = w83627ehf_read_value(client,
+                                               W83627EHF_REG_PWM[i]);
+                       data->fan_min_output[i] = w83627ehf_read_value(client,
+                                               W83627EHF_REG_FAN_MIN_OUTPUT[i]);
+                       data->fan_stop_time[i] = w83627ehf_read_value(client,
+                                               W83627EHF_REG_FAN_STOP_TIME[i]);
+                       data->target_temp[i] =
+                               w83627ehf_read_value(client,
+                                       W83627EHF_REG_TARGET[i]) &
+                                       (data->pwm_mode[i] == 1 ? 0x7f : 0xff);
+                       data->tolerance[i] = (tolerance >> (i == 1 ? 4 : 0))
+                                                                       & 0x0f;
+               }
+
                /* Measured temperatures and limits */
                data->temp1 = w83627ehf_read_value(client,
                              W83627EHF_REG_TEMP1);
@@ -395,6 +508,13 @@ static struct w83627ehf_data *w83627ehf_update_device(struct device *dev)
                                                 W83627EHF_REG_TEMP_HYST[i]);
                }
 
+               data->alarms = w83627ehf_read_value(client,
+                                       W83627EHF_REG_ALARM1) |
+                              (w83627ehf_read_value(client,
+                                       W83627EHF_REG_ALARM2) << 8) |
+                              (w83627ehf_read_value(client,
+                                       W83627EHF_REG_ALARM3) << 16);
+
                data->last_updated = jiffies;
                data->valid = 1;
        }
@@ -406,6 +526,101 @@ static struct w83627ehf_data *w83627ehf_update_device(struct device *dev)
 /*
  * Sysfs callback functions
  */
+#define show_in_reg(reg) \
+static ssize_t \
+show_##reg(struct device *dev, struct device_attribute *attr, \
+          char *buf) \
+{ \
+       struct w83627ehf_data *data = w83627ehf_update_device(dev); \
+       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)); \
+}
+show_in_reg(in)
+show_in_reg(in_min)
+show_in_reg(in_max)
+
+#define store_in_reg(REG, reg) \
+static ssize_t \
+store_in_##reg (struct device *dev, struct device_attribute *attr, \
+                       const char *buf, size_t count) \
+{ \
+       struct i2c_client *client = to_i2c_client(dev); \
+       struct w83627ehf_data *data = i2c_get_clientdata(client); \
+       struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr); \
+       int nr = sensor_attr->index; \
+       u32 val = simple_strtoul(buf, NULL, 10); \
+ \
+       mutex_lock(&data->update_lock); \
+       data->in_##reg[nr] = in_to_reg(val, nr); \
+       w83627ehf_write_value(client, W83627EHF_REG_IN_##REG(nr), \
+                             data->in_##reg[nr]); \
+       mutex_unlock(&data->update_lock); \
+       return count; \
+}
+
+store_in_reg(MIN, min)
+store_in_reg(MAX, max)
+
+static ssize_t show_alarm(struct device *dev, struct device_attribute *attr, char *buf)
+{
+       struct w83627ehf_data *data = w83627ehf_update_device(dev);
+       struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr);
+       int nr = sensor_attr->index;
+       return sprintf(buf, "%u\n", (data->alarms >> nr) & 0x01);
+}
+
+static struct sensor_device_attribute sda_in_input[] = {
+       SENSOR_ATTR(in0_input, S_IRUGO, show_in, NULL, 0),
+       SENSOR_ATTR(in1_input, S_IRUGO, show_in, NULL, 1),
+       SENSOR_ATTR(in2_input, S_IRUGO, show_in, NULL, 2),
+       SENSOR_ATTR(in3_input, S_IRUGO, show_in, NULL, 3),
+       SENSOR_ATTR(in4_input, S_IRUGO, show_in, NULL, 4),
+       SENSOR_ATTR(in5_input, S_IRUGO, show_in, NULL, 5),
+       SENSOR_ATTR(in6_input, S_IRUGO, show_in, NULL, 6),
+       SENSOR_ATTR(in7_input, S_IRUGO, show_in, NULL, 7),
+       SENSOR_ATTR(in8_input, S_IRUGO, show_in, NULL, 8),
+       SENSOR_ATTR(in9_input, S_IRUGO, show_in, NULL, 9),
+};
+
+static struct sensor_device_attribute sda_in_alarm[] = {
+       SENSOR_ATTR(in0_alarm, S_IRUGO, show_alarm, NULL, 0),
+       SENSOR_ATTR(in1_alarm, S_IRUGO, show_alarm, NULL, 1),
+       SENSOR_ATTR(in2_alarm, S_IRUGO, show_alarm, NULL, 2),
+       SENSOR_ATTR(in3_alarm, S_IRUGO, show_alarm, NULL, 3),
+       SENSOR_ATTR(in4_alarm, S_IRUGO, show_alarm, NULL, 8),
+       SENSOR_ATTR(in5_alarm, S_IRUGO, show_alarm, NULL, 21),
+       SENSOR_ATTR(in6_alarm, S_IRUGO, show_alarm, NULL, 20),
+       SENSOR_ATTR(in7_alarm, S_IRUGO, show_alarm, NULL, 16),
+       SENSOR_ATTR(in8_alarm, S_IRUGO, show_alarm, NULL, 17),
+       SENSOR_ATTR(in9_alarm, S_IRUGO, show_alarm, NULL, 19),
+};
+
+static struct sensor_device_attribute sda_in_min[] = {
+       SENSOR_ATTR(in0_min, S_IWUSR | S_IRUGO, show_in_min, store_in_min, 0),
+       SENSOR_ATTR(in1_min, S_IWUSR | S_IRUGO, show_in_min, store_in_min, 1),
+       SENSOR_ATTR(in2_min, S_IWUSR | S_IRUGO, show_in_min, store_in_min, 2),
+       SENSOR_ATTR(in3_min, S_IWUSR | S_IRUGO, show_in_min, store_in_min, 3),
+       SENSOR_ATTR(in4_min, S_IWUSR | S_IRUGO, show_in_min, store_in_min, 4),
+       SENSOR_ATTR(in5_min, S_IWUSR | S_IRUGO, show_in_min, store_in_min, 5),
+       SENSOR_ATTR(in6_min, S_IWUSR | S_IRUGO, show_in_min, store_in_min, 6),
+       SENSOR_ATTR(in7_min, S_IWUSR | S_IRUGO, show_in_min, store_in_min, 7),
+       SENSOR_ATTR(in8_min, S_IWUSR | S_IRUGO, show_in_min, store_in_min, 8),
+       SENSOR_ATTR(in9_min, S_IWUSR | S_IRUGO, show_in_min, store_in_min, 9),
+};
+
+static struct sensor_device_attribute sda_in_max[] = {
+       SENSOR_ATTR(in0_max, S_IWUSR | S_IRUGO, show_in_max, store_in_max, 0),
+       SENSOR_ATTR(in1_max, S_IWUSR | S_IRUGO, show_in_max, store_in_max, 1),
+       SENSOR_ATTR(in2_max, S_IWUSR | S_IRUGO, show_in_max, store_in_max, 2),
+       SENSOR_ATTR(in3_max, S_IWUSR | S_IRUGO, show_in_max, store_in_max, 3),
+       SENSOR_ATTR(in4_max, S_IWUSR | S_IRUGO, show_in_max, store_in_max, 4),
+       SENSOR_ATTR(in5_max, S_IWUSR | S_IRUGO, show_in_max, store_in_max, 5),
+       SENSOR_ATTR(in6_max, S_IWUSR | S_IRUGO, show_in_max, store_in_max, 6),
+       SENSOR_ATTR(in7_max, S_IWUSR | S_IRUGO, show_in_max, store_in_max, 7),
+       SENSOR_ATTR(in8_max, S_IWUSR | S_IRUGO, show_in_max, store_in_max, 8),
+       SENSOR_ATTR(in9_max, S_IWUSR | S_IRUGO, show_in_max, store_in_max, 9),
+};
 
 #define show_fan_reg(reg) \
 static ssize_t \
@@ -505,6 +720,14 @@ static struct sensor_device_attribute sda_fan_input[] = {
        SENSOR_ATTR(fan5_input, S_IRUGO, show_fan, NULL, 4),
 };
 
+static struct sensor_device_attribute sda_fan_alarm[] = {
+       SENSOR_ATTR(fan1_alarm, S_IRUGO, show_alarm, NULL, 6),
+       SENSOR_ATTR(fan2_alarm, S_IRUGO, show_alarm, NULL, 7),
+       SENSOR_ATTR(fan3_alarm, S_IRUGO, show_alarm, NULL, 11),
+       SENSOR_ATTR(fan4_alarm, S_IRUGO, show_alarm, NULL, 10),
+       SENSOR_ATTR(fan5_alarm, S_IRUGO, show_alarm, NULL, 23),
+};
+
 static struct sensor_device_attribute sda_fan_min[] = {
        SENSOR_ATTR(fan1_min, S_IWUSR | S_IRUGO, show_fan_min,
                    store_fan_min, 0),
@@ -526,13 +749,6 @@ static struct sensor_device_attribute sda_fan_div[] = {
        SENSOR_ATTR(fan5_div, S_IRUGO, show_fan_div, NULL, 4),
 };
 
-static void device_create_file_fan(struct device *dev, int i)
-{
-       device_create_file(dev, &sda_fan_input[i].dev_attr);
-       device_create_file(dev, &sda_fan_div[i].dev_attr);
-       device_create_file(dev, &sda_fan_min[i].dev_attr);
-}
-
 #define show_temp1_reg(reg) \
 static ssize_t \
 show_##reg(struct device *dev, struct device_attribute *attr, \
@@ -555,7 +771,7 @@ store_temp1_##reg(struct device *dev, struct device_attribute *attr, \
        u32 val = simple_strtoul(buf, NULL, 10); \
  \
        mutex_lock(&data->update_lock); \
-       data->temp1_##reg = temp1_to_reg(val); \
+       data->temp1_##reg = temp1_to_reg(val, -128000, 127000); \
        w83627ehf_write_value(client, W83627EHF_REG_TEMP1_##REG, \
                              data->temp1_##reg); \
        mutex_unlock(&data->update_lock); \
@@ -616,12 +832,314 @@ static struct sensor_device_attribute sda_temp[] = {
                    store_temp_max_hyst, 0),
        SENSOR_ATTR(temp3_max_hyst, S_IRUGO | S_IWUSR, show_temp_max_hyst,
                    store_temp_max_hyst, 1),
+       SENSOR_ATTR(temp1_alarm, S_IRUGO, show_alarm, NULL, 4),
+       SENSOR_ATTR(temp2_alarm, S_IRUGO, show_alarm, NULL, 5),
+       SENSOR_ATTR(temp3_alarm, S_IRUGO, show_alarm, NULL, 13),
+};
+
+#define show_pwm_reg(reg) \
+static ssize_t show_##reg (struct device *dev, struct device_attribute *attr, \
+                               char *buf) \
+{ \
+       struct w83627ehf_data *data = w83627ehf_update_device(dev); \
+       struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr); \
+       int nr = sensor_attr->index; \
+       return sprintf(buf, "%d\n", data->reg[nr]); \
+}
+
+show_pwm_reg(pwm_mode)
+show_pwm_reg(pwm_enable)
+show_pwm_reg(pwm)
+
+static ssize_t
+store_pwm_mode(struct device *dev, struct device_attribute *attr,
+                       const char *buf, size_t count)
+{
+       struct i2c_client *client = to_i2c_client(dev);
+       struct w83627ehf_data *data = i2c_get_clientdata(client);
+       struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr);
+       int nr = sensor_attr->index;
+       u32 val = simple_strtoul(buf, NULL, 10);
+       u16 reg;
+
+       if (val > 1)
+               return -EINVAL;
+       mutex_lock(&data->update_lock);
+       reg = w83627ehf_read_value(client, W83627EHF_REG_PWM_ENABLE[nr]);
+       data->pwm_mode[nr] = val;
+       reg &= ~(1 << W83627EHF_PWM_MODE_SHIFT[nr]);
+       if (!val)
+               reg |= 1 << W83627EHF_PWM_MODE_SHIFT[nr];
+       w83627ehf_write_value(client, W83627EHF_REG_PWM_ENABLE[nr], reg);
+       mutex_unlock(&data->update_lock);
+       return count;
+}
+
+static ssize_t
+store_pwm(struct device *dev, struct device_attribute *attr,
+                       const char *buf, size_t count)
+{
+       struct i2c_client *client = to_i2c_client(dev);
+       struct w83627ehf_data *data = i2c_get_clientdata(client);
+       struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr);
+       int nr = sensor_attr->index;
+       u32 val = SENSORS_LIMIT(simple_strtoul(buf, NULL, 10), 0, 255);
+
+       mutex_lock(&data->update_lock);
+       data->pwm[nr] = val;
+       w83627ehf_write_value(client, W83627EHF_REG_PWM[nr], val);
+       mutex_unlock(&data->update_lock);
+       return count;
+}
+
+static ssize_t
+store_pwm_enable(struct device *dev, struct device_attribute *attr,
+                       const char *buf, size_t count)
+{
+       struct i2c_client *client = to_i2c_client(dev);
+       struct w83627ehf_data *data = i2c_get_clientdata(client);
+       struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr);
+       int nr = sensor_attr->index;
+       u32 val = simple_strtoul(buf, NULL, 10);
+       u16 reg;
+
+       if (!val || (val > 2))  /* only modes 1 and 2 are supported */
+               return -EINVAL;
+       mutex_lock(&data->update_lock);
+       reg = w83627ehf_read_value(client, W83627EHF_REG_PWM_ENABLE[nr]);
+       data->pwm_enable[nr] = val;
+       reg &= ~(0x03 << W83627EHF_PWM_ENABLE_SHIFT[nr]);
+       reg |= (val - 1) << W83627EHF_PWM_ENABLE_SHIFT[nr];
+       w83627ehf_write_value(client, W83627EHF_REG_PWM_ENABLE[nr], reg);
+       mutex_unlock(&data->update_lock);
+       return count;
+}
+
+
+#define show_tol_temp(reg) \
+static ssize_t show_##reg(struct device *dev, struct device_attribute *attr, \
+                               char *buf) \
+{ \
+       struct w83627ehf_data *data = w83627ehf_update_device(dev); \
+       struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr); \
+       int nr = sensor_attr->index; \
+       return sprintf(buf, "%d\n", temp1_from_reg(data->reg[nr])); \
+}
+
+show_tol_temp(tolerance)
+show_tol_temp(target_temp)
+
+static ssize_t
+store_target_temp(struct device *dev, struct device_attribute *attr,
+                       const char *buf, size_t count)
+{
+       struct i2c_client *client = to_i2c_client(dev);
+       struct w83627ehf_data *data = i2c_get_clientdata(client);
+       struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr);
+       int nr = sensor_attr->index;
+       u8 val = temp1_to_reg(simple_strtoul(buf, NULL, 10), 0, 127000);
+
+       mutex_lock(&data->update_lock);
+       data->target_temp[nr] = val;
+       w83627ehf_write_value(client, W83627EHF_REG_TARGET[nr], val);
+       mutex_unlock(&data->update_lock);
+       return count;
+}
+
+static ssize_t
+store_tolerance(struct device *dev, struct device_attribute *attr,
+                       const char *buf, size_t count)
+{
+       struct i2c_client *client = to_i2c_client(dev);
+       struct w83627ehf_data *data = i2c_get_clientdata(client);
+       struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr);
+       int nr = sensor_attr->index;
+       u16 reg;
+       /* Limit the temp to 0C - 15C */
+       u8 val = temp1_to_reg(simple_strtoul(buf, NULL, 10), 0, 15000);
+
+       mutex_lock(&data->update_lock);
+       reg = w83627ehf_read_value(client, W83627EHF_REG_TOLERANCE[nr]);
+       data->tolerance[nr] = val;
+       if (nr == 1)
+               reg = (reg & 0x0f) | (val << 4);
+       else
+               reg = (reg & 0xf0) | val;
+       w83627ehf_write_value(client, W83627EHF_REG_TOLERANCE[nr], reg);
+       mutex_unlock(&data->update_lock);
+       return count;
+}
+
+static struct sensor_device_attribute sda_pwm[] = {
+       SENSOR_ATTR(pwm1, S_IWUSR | S_IRUGO, show_pwm, store_pwm, 0),
+       SENSOR_ATTR(pwm2, S_IWUSR | S_IRUGO, show_pwm, store_pwm, 1),
+       SENSOR_ATTR(pwm3, S_IWUSR | S_IRUGO, show_pwm, store_pwm, 2),
+       SENSOR_ATTR(pwm4, S_IWUSR | S_IRUGO, show_pwm, store_pwm, 3),
+};
+
+static struct sensor_device_attribute sda_pwm_mode[] = {
+       SENSOR_ATTR(pwm1_mode, S_IWUSR | S_IRUGO, show_pwm_mode,
+                   store_pwm_mode, 0),
+       SENSOR_ATTR(pwm2_mode, S_IWUSR | S_IRUGO, show_pwm_mode,
+                   store_pwm_mode, 1),
+       SENSOR_ATTR(pwm3_mode, S_IWUSR | S_IRUGO, show_pwm_mode,
+                   store_pwm_mode, 2),
+       SENSOR_ATTR(pwm4_mode, S_IWUSR | S_IRUGO, show_pwm_mode,
+                   store_pwm_mode, 3),
+};
+
+static struct sensor_device_attribute sda_pwm_enable[] = {
+       SENSOR_ATTR(pwm1_enable, S_IWUSR | S_IRUGO, show_pwm_enable,
+                   store_pwm_enable, 0),
+       SENSOR_ATTR(pwm2_enable, S_IWUSR | S_IRUGO, show_pwm_enable,
+                   store_pwm_enable, 1),
+       SENSOR_ATTR(pwm3_enable, S_IWUSR | S_IRUGO, show_pwm_enable,
+                   store_pwm_enable, 2),
+       SENSOR_ATTR(pwm4_enable, S_IWUSR | S_IRUGO, show_pwm_enable,
+                   store_pwm_enable, 3),
+};
+
+static struct sensor_device_attribute sda_target_temp[] = {
+       SENSOR_ATTR(pwm1_target, S_IWUSR | S_IRUGO, show_target_temp,
+                   store_target_temp, 0),
+       SENSOR_ATTR(pwm2_target, S_IWUSR | S_IRUGO, show_target_temp,
+                   store_target_temp, 1),
+       SENSOR_ATTR(pwm3_target, S_IWUSR | S_IRUGO, show_target_temp,
+                   store_target_temp, 2),
+       SENSOR_ATTR(pwm4_target, S_IWUSR | S_IRUGO, show_target_temp,
+                   store_target_temp, 3),
+};
+
+static struct sensor_device_attribute sda_tolerance[] = {
+       SENSOR_ATTR(pwm1_tolerance, S_IWUSR | S_IRUGO, show_tolerance,
+                   store_tolerance, 0),
+       SENSOR_ATTR(pwm2_tolerance, S_IWUSR | S_IRUGO, show_tolerance,
+                   store_tolerance, 1),
+       SENSOR_ATTR(pwm3_tolerance, S_IWUSR | S_IRUGO, show_tolerance,
+                   store_tolerance, 2),
+       SENSOR_ATTR(pwm4_tolerance, S_IWUSR | S_IRUGO, show_tolerance,
+                   store_tolerance, 3),
+};
+
+/* Smart Fan registers */
+
+#define fan_functions(reg, REG) \
+static ssize_t show_##reg(struct device *dev, struct device_attribute *attr, \
+                      char *buf) \
+{ \
+       struct w83627ehf_data *data = w83627ehf_update_device(dev); \
+       struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr); \
+       int nr = sensor_attr->index; \
+       return sprintf(buf, "%d\n", data->reg[nr]); \
+}\
+static ssize_t \
+store_##reg(struct device *dev, struct device_attribute *attr, \
+                           const char *buf, size_t count) \
+{\
+       struct i2c_client *client = to_i2c_client(dev); \
+       struct w83627ehf_data *data = i2c_get_clientdata(client); \
+       struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr); \
+       int nr = sensor_attr->index; \
+       u32 val = SENSORS_LIMIT(simple_strtoul(buf, NULL, 10), 1, 255); \
+       mutex_lock(&data->update_lock); \
+       data->reg[nr] = val; \
+       w83627ehf_write_value(client, W83627EHF_REG_##REG[nr],  val); \
+       mutex_unlock(&data->update_lock); \
+       return count; \
+}
+
+fan_functions(fan_min_output, FAN_MIN_OUTPUT)
+
+#define fan_time_functions(reg, REG) \
+static ssize_t show_##reg(struct device *dev, struct device_attribute *attr, \
+                               char *buf) \
+{ \
+       struct w83627ehf_data *data = w83627ehf_update_device(dev); \
+       struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr); \
+       int nr = sensor_attr->index; \
+       return sprintf(buf, "%d\n", \
+                       step_time_from_reg(data->reg[nr], data->pwm_mode[nr])); \
+} \
+\
+static ssize_t \
+store_##reg(struct device *dev, struct device_attribute *attr, \
+                       const char *buf, size_t count) \
+{ \
+       struct i2c_client *client = to_i2c_client(dev); \
+       struct w83627ehf_data *data = i2c_get_clientdata(client); \
+       struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr); \
+       int nr = sensor_attr->index; \
+       u8 val = step_time_to_reg(simple_strtoul(buf, NULL, 10), \
+                                       data->pwm_mode[nr]); \
+       mutex_lock(&data->update_lock); \
+       data->reg[nr] = val; \
+       w83627ehf_write_value(client, W83627EHF_REG_##REG[nr], val); \
+       mutex_unlock(&data->update_lock); \
+       return count; \
+} \
+
+fan_time_functions(fan_stop_time, FAN_STOP_TIME)
+
+
+static struct sensor_device_attribute sda_sf3_arrays_fan4[] = {
+       SENSOR_ATTR(pwm4_stop_time, S_IWUSR | S_IRUGO, show_fan_stop_time,
+                   store_fan_stop_time, 3),
+       SENSOR_ATTR(pwm4_min_output, S_IWUSR | S_IRUGO, show_fan_min_output,
+                   store_fan_min_output, 3),
+};
+
+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_min_output, S_IWUSR | S_IRUGO, show_fan_min_output,
+                   store_fan_min_output, 0),
+       SENSOR_ATTR(pwm2_min_output, S_IWUSR | S_IRUGO, show_fan_min_output,
+                   store_fan_min_output, 1),
+       SENSOR_ATTR(pwm3_min_output, S_IWUSR | S_IRUGO, show_fan_min_output,
+                   store_fan_min_output, 2),
 };
 
 /*
  * Driver and client management
  */
 
+static void w83627ehf_device_remove_files(struct device *dev)
+{
+       /* some entries in the following arrays may not have been used in
+        * device_create_file(), but device_remove_file() will ignore them */
+       int i;
+
+       for (i = 0; i < ARRAY_SIZE(sda_sf3_arrays); i++)
+               device_remove_file(dev, &sda_sf3_arrays[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 < 10; i++) {
+               device_remove_file(dev, &sda_in_input[i].dev_attr);
+               device_remove_file(dev, &sda_in_alarm[i].dev_attr);
+               device_remove_file(dev, &sda_in_min[i].dev_attr);
+               device_remove_file(dev, &sda_in_max[i].dev_attr);
+       }
+       for (i = 0; i < 5; i++) {
+               device_remove_file(dev, &sda_fan_input[i].dev_attr);
+               device_remove_file(dev, &sda_fan_alarm[i].dev_attr);
+               device_remove_file(dev, &sda_fan_div[i].dev_attr);
+               device_remove_file(dev, &sda_fan_min[i].dev_attr);
+       }
+       for (i = 0; i < 4; i++) {
+               device_remove_file(dev, &sda_pwm[i].dev_attr);
+               device_remove_file(dev, &sda_pwm_mode[i].dev_attr);
+               device_remove_file(dev, &sda_pwm_enable[i].dev_attr);
+               device_remove_file(dev, &sda_target_temp[i].dev_attr);
+               device_remove_file(dev, &sda_tolerance[i].dev_attr);
+       }
+       for (i = 0; i < ARRAY_SIZE(sda_temp); i++)
+               device_remove_file(dev, &sda_temp[i].dev_attr);
+}
+
 static struct i2c_driver w83627ehf_driver;
 
 static void w83627ehf_init_client(struct i2c_client *client)
@@ -651,6 +1169,7 @@ static int w83627ehf_detect(struct i2c_adapter *adapter)
        struct i2c_client *client;
        struct w83627ehf_data *data;
        struct device *dev;
+       u8 fan4pin, fan5pin;
        int i, err = 0;
 
        if (!request_region(address + REGION_OFFSET, REGION_LENGTH,
@@ -689,32 +1208,87 @@ static int w83627ehf_detect(struct i2c_adapter *adapter)
                data->fan_min[i] = w83627ehf_read_value(client,
                                   W83627EHF_REG_FAN_MIN[i]);
 
+       /* fan4 and fan5 share some pins with the GPIO and serial flash */
+
+       superio_enter();
+       fan5pin = superio_inb(0x24) & 0x2;
+       fan4pin = superio_inb(0x29) & 0x6;
+       superio_exit();
+
        /* It looks like fan4 and fan5 pins can be alternatively used
           as fan on/off switches */
+
        data->has_fan = 0x07; /* fan1, fan2 and fan3 */
        i = w83627ehf_read_value(client, W83627EHF_REG_FANDIV1);
-       if (i & (1 << 2))
+       if ((i & (1 << 2)) && (!fan4pin))
                data->has_fan |= (1 << 3);
-       if (i & (1 << 0))
+       if ((i & (1 << 0)) && (!fan5pin))
                data->has_fan |= (1 << 4);
 
        /* Register sysfs hooks */
-       data->class_dev = hwmon_device_register(dev);
-       if (IS_ERR(data->class_dev)) {
-               err = PTR_ERR(data->class_dev);
-               goto exit_detach;
-       }
+       for (i = 0; i < ARRAY_SIZE(sda_sf3_arrays); i++)
+               if ((err = device_create_file(dev,
+                       &sda_sf3_arrays[i].dev_attr)))
+                       goto exit_remove;
+
+       /* if fan4 is enabled create the sf3 files for it */
+       if (data->has_fan & (1 << 3))
+               for (i = 0; i < ARRAY_SIZE(sda_sf3_arrays_fan4); i++) {
+                       if ((err = device_create_file(dev,
+                               &sda_sf3_arrays_fan4[i].dev_attr)))
+                               goto exit_remove;
+               }
+
+       for (i = 0; i < 10; i++)
+               if ((err = device_create_file(dev, &sda_in_input[i].dev_attr))
+                       || (err = device_create_file(dev,
+                               &sda_in_alarm[i].dev_attr))
+                       || (err = device_create_file(dev,
+                               &sda_in_min[i].dev_attr))
+                       || (err = device_create_file(dev,
+                               &sda_in_max[i].dev_attr)))
+                       goto exit_remove;
 
        for (i = 0; i < 5; i++) {
-               if (data->has_fan & (1 << i))
-                       device_create_file_fan(dev, i);
+               if (data->has_fan & (1 << i)) {
+                       if ((err = device_create_file(dev,
+                                       &sda_fan_input[i].dev_attr))
+                               || (err = device_create_file(dev,
+                                       &sda_fan_alarm[i].dev_attr))
+                               || (err = device_create_file(dev,
+                                       &sda_fan_div[i].dev_attr))
+                               || (err = device_create_file(dev,
+                                       &sda_fan_min[i].dev_attr)))
+                               goto exit_remove;
+                       if (i < 4 && /* w83627ehf only has 4 pwm */
+                               ((err = device_create_file(dev,
+                                       &sda_pwm[i].dev_attr))
+                               || (err = device_create_file(dev,
+                                       &sda_pwm_mode[i].dev_attr))
+                               || (err = device_create_file(dev,
+                                       &sda_pwm_enable[i].dev_attr))
+                               || (err = device_create_file(dev,
+                                       &sda_target_temp[i].dev_attr))
+                               || (err = device_create_file(dev,
+                                       &sda_tolerance[i].dev_attr))))
+                               goto exit_remove;
+               }
        }
+
        for (i = 0; i < ARRAY_SIZE(sda_temp); i++)
-               device_create_file(dev, &sda_temp[i].dev_attr);
+               if ((err = device_create_file(dev, &sda_temp[i].dev_attr)))
+                       goto exit_remove;
+
+       data->class_dev = hwmon_device_register(dev);
+       if (IS_ERR(data->class_dev)) {
+               err = PTR_ERR(data->class_dev);
+               goto exit_remove;
+       }
 
        return 0;
 
-exit_detach:
+exit_remove:
+       w83627ehf_device_remove_files(dev);
        i2c_detach_client(client);
 exit_free:
        kfree(data);
@@ -730,6 +1304,7 @@ static int w83627ehf_detach_client(struct i2c_client *client)
        int err;
 
        hwmon_device_unregister(data->class_dev);
+       w83627ehf_device_remove_files(&client->dev);
 
        if ((err = i2c_detach_client(client)))
                return err;
@@ -741,6 +1316,7 @@ static int w83627ehf_detach_client(struct i2c_client *client)
 
 static struct i2c_driver w83627ehf_driver = {
        .driver = {
+               .owner  = THIS_MODULE,
                .name   = "w83627ehf",
        },
        .attach_adapter = w83627ehf_detect,