hwmon: (w83795) Add support for dynamic in0-2 limits
[cascardo/linux.git] / drivers / hwmon / w83795.c
index c6984c8..458fb29 100644 (file)
@@ -36,9 +36,9 @@
 #include <linux/delay.h>
 
 /* Addresses to scan */
-static unsigned short normal_i2c[] = { 0x2c, 0x2d, 0x2e, 0x2f, I2C_CLIENT_END };
-
-enum chips { w83795 };
+static const unsigned short normal_i2c[] = {
+       0x2c, 0x2d, 0x2e, 0x2f, I2C_CLIENT_END
+};
 
 
 static int reset;
@@ -50,10 +50,12 @@ MODULE_PARM_DESC(reset, "Set to 1 to reset chip, not recommended");
 #define W83795_REG_VENDORID            0xfd
 #define W83795_REG_CHIPID              0xfe
 #define W83795_REG_DEVICEID            0xfb
+#define W83795_REG_DEVICEID_A          0xff
 
 #define W83795_REG_I2C_ADDR            0xfc
 #define W83795_REG_CONFIG              0x01
 #define W83795_REG_CONFIG_CONFIG48     0x04
+#define W83795_REG_CONFIG_START        0x01
 
 /* Multi-Function Pin Ctrl Registers */
 #define W83795_REG_VOLT_CTRL1          0x02
@@ -71,7 +73,7 @@ MODULE_PARM_DESC(reset, "Set to 1 to reset chip, not recommended");
 #define TEMP_CTRL_SHIFT                        4
 #define TEMP_CTRL_HASIN_SHIFT          5
 /* temp mode may effect VSEN17-12 (in20-15) */
-static u16 W83795_REG_TEMP_CTRL[][6] = {
+static const u16 W83795_REG_TEMP_CTRL[][6] = {
        /* Disable, TD, VSEN, TR, register shift value, has_in shift num */
        {0x00, 0x01, 0x02, 0x03, 0, 17},        /* TR1 */
        {0x00, 0x04, 0x08, 0x0C, 2, 18},        /* TR2 */
@@ -88,7 +90,7 @@ static u16 W83795_REG_TEMP_CTRL[][6] = {
 #define TEMP_WARN_HYST                 4
 /* only crit and crit_hyst affect real-time alarm status
  * current crit crit_hyst warn warn_hyst */
-static u16 W83795_REG_TEMP[][5] = {
+static const u16 W83795_REG_TEMP[][5] = {
        {0x21, 0x96, 0x97, 0x98, 0x99}, /* TD1/TR1 */
        {0x22, 0x9a, 0x9b, 0x9c, 0x9d}, /* TD2/TR2 */
        {0x23, 0x9e, 0x9f, 0xa0, 0xa1}, /* TD3/TR3 */
@@ -183,8 +185,6 @@ static const u8 IN_LSB_SHIFT_IDX[][2] = {
 #define W83795_REG_FAN_MIN_LSB(index)  (0xC4 + (index) / 2)
 #define W83795_REG_FAN_MIN_LSB_SHIFT(index) \
        (((index) % 1) ? 4 : 0)
-#define W83795_REG_FAN_CTRL_SHIFT(index) \
-       (((index) > 7) ? ((index) - 8) : (index))
 
 #define W83795_REG_VID_CTRL            0x6A
 
@@ -272,7 +272,7 @@ static inline u16 in_to_reg(u8 index, u16 val)
 
 static inline unsigned long fan_from_reg(u16 val)
 {
-       if ((val >= 0xff0) || (val == 0))
+       if ((val == 0xfff) || (val == 0))
                return 0;
        return 1350000UL / val;
 }
@@ -316,6 +316,7 @@ struct w83795_data {
        u8 bank;
 
        u32 has_in;             /* Enable monitor VIN or not */
+       u8 has_dyn_in;          /* Only in2-0 can have this */
        u16 in[21][3];          /* Register value, read/high/low */
        u8 in_lsb[10][3];       /* LSB Register value, high/low */
        u8 has_gain;            /* has gain: in17-20 * 8 */
@@ -364,60 +365,67 @@ struct w83795_data {
 
 /*
  * Hardware access
+ * We assume that nobdody can change the bank outside the driver.
  */
 
-/* Ignore the possibility that somebody change bank outside the driver
- * Must be called with data->update_lock held, except during initialization */
-static u8 w83795_read(struct i2c_client *client, u16 reg)
+/* Must be called with data->update_lock held, except during initialization */
+static int w83795_set_bank(struct i2c_client *client, u8 bank)
 {
        struct w83795_data *data = i2c_get_clientdata(client);
-       u8 res = 0xff;
-       u8 new_bank = reg >> 8;
-
-       new_bank |= data->bank & 0xfc;
-       if (data->bank != new_bank) {
-               if (i2c_smbus_write_byte_data
-                   (client, W83795_REG_BANKSEL, new_bank) >= 0)
-                       data->bank = new_bank;
-               else {
-                       dev_err(&client->dev,
-                               "set bank to %d failed, fall back "
-                               "to bank %d, read reg 0x%x error\n",
-                               new_bank, data->bank, reg);
-                       res = 0x0;      /* read 0x0 from the chip */
-                       goto END;
-               }
+       int err;
+
+       /* If the same bank is already set, nothing to do */
+       if ((data->bank & 0x07) == bank)
+               return 0;
+
+       /* Change to new bank, preserve all other bits */
+       bank |= data->bank & ~0x07;
+       err = i2c_smbus_write_byte_data(client, W83795_REG_BANKSEL, bank);
+       if (err < 0) {
+               dev_err(&client->dev,
+                       "Failed to set bank to %d, err %d\n",
+                       (int)bank, err);
+               return err;
        }
-       res = i2c_smbus_read_byte_data(client, reg & 0xff);
-END:
-       return res;
+       data->bank = bank;
+
+       return 0;
 }
 
 /* Must be called with data->update_lock held, except during initialization */
-static int w83795_write(struct i2c_client *client, u16 reg, u8 value)
+static u8 w83795_read(struct i2c_client *client, u16 reg)
 {
-       struct w83795_data *data = i2c_get_clientdata(client);
-       int res;
-       u8 new_bank = reg >> 8;
-
-       new_bank |= data->bank & 0xfc;
-       if (data->bank != new_bank) {
-               res = i2c_smbus_write_byte_data(client, W83795_REG_BANKSEL,
-                                               new_bank);
-               if (res >= 0)
-                       data->bank = new_bank;
-               else {
-                       dev_err(&client->dev,
-                               "set bank to %d failed, fall back "
-                               "to bank %d, write reg 0x%x error\n",
-                               new_bank, data->bank, reg);
-                       goto END;
-               }
+       int err;
+
+       err = w83795_set_bank(client, reg >> 8);
+       if (err < 0)
+               return 0x00;    /* Arbitrary */
+
+       err = i2c_smbus_read_byte_data(client, reg & 0xff);
+       if (err < 0) {
+               dev_err(&client->dev,
+                       "Failed to read from register 0x%03x, err %d\n",
+                       (int)reg, err);
+               return 0x00;    /* Arbitrary */
        }
+       return err;
+}
 
-       res = i2c_smbus_write_byte_data(client, reg & 0xff, value);
-END:
-       return res;
+/* Must be called with data->update_lock held, except during initialization */
+static int w83795_write(struct i2c_client *client, u16 reg, u8 value)
+{
+       int err;
+
+       err = w83795_set_bank(client, reg >> 8);
+       if (err < 0)
+               return err;
+
+       err = i2c_smbus_write_byte_data(client, reg & 0xff, value);
+       if (err < 0)
+               dev_err(&client->dev,
+                       "Failed to write to register 0x%03x, err %d\n",
+                       (int)reg, err);
+       return err;
 }
 
 static struct w83795_data *w83795_update_device(struct device *dev)
@@ -443,13 +451,30 @@ static struct w83795_data *w83795_update_device(struct device *dev)
                data->in[i][IN_READ] = tmp;
        }
 
+       /* in0-2 can have dynamic limits (W83795G only) */
+       if (data->has_dyn_in) {
+               u8 lsb_max = w83795_read(client, IN_LSB_REG(0, IN_MAX));
+               u8 lsb_low = w83795_read(client, IN_LSB_REG(0, IN_LOW));
+
+               for (i = 0; i < 3; i++) {
+                       if (!(data->has_dyn_in & (1 << i)))
+                               continue;
+                       data->in[i][IN_MAX] =
+                               w83795_read(client, W83795_REG_IN[i][IN_MAX]);
+                       data->in[i][IN_LOW] =
+                               w83795_read(client, W83795_REG_IN[i][IN_LOW]);
+                       data->in_lsb[i][IN_MAX] = (lsb_max >> (2 * i)) & 0x03;
+                       data->in_lsb[i][IN_LOW] = (lsb_low >> (2 * i)) & 0x03;
+               }
+       }
+
        /* Update fan */
        for (i = 0; i < ARRAY_SIZE(data->fan); i++) {
                if (!(data->has_fan & (1 << i)))
                        continue;
                data->fan[i] = w83795_read(client, W83795_REG_FAN(i)) << 4;
                data->fan[i] |=
-                 (w83795_read(client, W83795_REG_VRLSB >> 4)) & 0x0F;
+                 (w83795_read(client, W83795_REG_VRLSB) >> 4) & 0x0F;
        }
 
        /* Update temperature */
@@ -793,9 +818,6 @@ store_pwm_enable(struct device *dev, struct device_attribute *attr,
        }
        mutex_unlock(&data->update_lock);
        return count;
-
-       mutex_unlock(&data->update_lock);
-       return -EINVAL;
 }
 
 static ssize_t
@@ -1331,7 +1353,7 @@ show_in(struct device *dev, struct device_attribute *attr, char *buf)
        case IN_READ:
                /* calculate this value again by sensors as sensors3.conf */
                if ((index >= 17) &&
-                   ((data->has_gain >> (index - 17)) & 1))
+                   !((data->has_gain >> (index - 17)) & 1))
                        val *= 8;
                break;
        case IN_MAX:
@@ -1341,7 +1363,7 @@ show_in(struct device *dev, struct device_attribute *attr, char *buf)
                val |= (data->in_lsb[lsb_idx][nr] >>
                        IN_LSB_SHIFT_IDX[lsb_idx][IN_LSB_SHIFT]) & 0x03;
                if ((index >= 17) &&
-                   ((data->has_gain >> (index - 17)) & 1))
+                   !((data->has_gain >> (index - 17)) & 1))
                        val *= 8;
                break;
        }
@@ -1369,7 +1391,7 @@ store_in(struct device *dev, struct device_attribute *attr,
        val = in_to_reg(index, val);
 
        if ((index >= 17) &&
-           ((data->has_gain >> (index - 17)) & 1))
+           !((data->has_gain >> (index - 17)) & 1))
                val /= 8;
        val = SENSORS_LIMIT(val, 0, 0x3FF);
        mutex_lock(&data->update_lock);
@@ -1446,7 +1468,9 @@ store_sf_setup(struct device *dev, struct device_attribute *attr,
 
 #define NOT_USED                       -1
 
-#define SENSOR_ATTR_IN(index)          \
+/* Don't change the attribute order, _max and _min are accessed by index
+ * somewhere else in the code */
+#define SENSOR_ATTR_IN(index) {                                                \
        SENSOR_ATTR_2(in##index##_input, S_IRUGO, show_in, NULL,        \
                IN_READ, index), \
        SENSOR_ATTR_2(in##index##_max, S_IRUGO | S_IWUSR, show_in,      \
@@ -1457,9 +1481,9 @@ store_sf_setup(struct device *dev, struct device_attribute *attr,
                NULL, ALARM_STATUS, index + ((index > 14) ? 1 : 0)), \
        SENSOR_ATTR_2(in##index##_beep, S_IWUSR | S_IRUGO,              \
                show_alarm_beep, store_beep, BEEP_ENABLE,               \
-               index + ((index > 14) ? 1 : 0))
+               index + ((index > 14) ? 1 : 0)) }
 
-#define SENSOR_ATTR_FAN(index)                                         \
+#define SENSOR_ATTR_FAN(index) {                                       \
        SENSOR_ATTR_2(fan##index##_input, S_IRUGO, show_fan,            \
                NULL, FAN_INPUT, index - 1), \
        SENSOR_ATTR_2(fan##index##_min, S_IWUSR | S_IRUGO,              \
@@ -1467,9 +1491,9 @@ store_sf_setup(struct device *dev, struct device_attribute *attr,
        SENSOR_ATTR_2(fan##index##_alarm, S_IRUGO, show_alarm_beep,     \
                NULL, ALARM_STATUS, index + 31),                        \
        SENSOR_ATTR_2(fan##index##_beep, S_IWUSR | S_IRUGO,             \
-               show_alarm_beep, store_beep, BEEP_ENABLE, index + 31)
+               show_alarm_beep, store_beep, BEEP_ENABLE, index + 31) }
 
-#define SENSOR_ATTR_PWM(index)                                         \
+#define SENSOR_ATTR_PWM(index) {                                       \
        SENSOR_ATTR_2(pwm##index, S_IWUSR | S_IRUGO, show_pwm,          \
                store_pwm, PWM_OUTPUT, index - 1),                      \
        SENSOR_ATTR_2(pwm##index##_nonstop, S_IWUSR | S_IRUGO,          \
@@ -1481,13 +1505,11 @@ store_sf_setup(struct device *dev, struct device_attribute *attr,
        SENSOR_ATTR_2(fan##index##_div, S_IWUSR | S_IRUGO,      \
                show_pwm, store_pwm, PWM_DIV, index - 1),        \
        SENSOR_ATTR_2(pwm##index##_enable, S_IWUSR | S_IRUGO,           \
-               show_pwm_enable, store_pwm_enable, NOT_USED, index - 1)
+               show_pwm_enable, store_pwm_enable, NOT_USED, index - 1), \
+       SENSOR_ATTR_2(fan##index##_target, S_IWUSR | S_IRUGO, \
+               show_fanin, store_fanin, FANIN_TARGET, index - 1) }
 
-#define SENSOR_ATTR_FANIN_TARGET(index)                                        \
-       SENSOR_ATTR_2(speed_cruise##index##_target, S_IWUSR | S_IRUGO, \
-               show_fanin, store_fanin, FANIN_TARGET, index - 1)
-
-#define SENSOR_ATTR_DTS(index)                                         \
+#define SENSOR_ATTR_DTS(index) {                                       \
        SENSOR_ATTR_2(temp##index##_type, S_IRUGO ,             \
                show_dts_mode, NULL, NOT_USED, index - 7),      \
        SENSOR_ATTR_2(temp##index##_input, S_IRUGO, show_dts,           \
@@ -1503,9 +1525,9 @@ store_sf_setup(struct device *dev, struct device_attribute *attr,
        SENSOR_ATTR_2(temp##index##_alarm, S_IRUGO,                     \
                show_alarm_beep, NULL, ALARM_STATUS, index + 17),       \
        SENSOR_ATTR_2(temp##index##_beep, S_IWUSR | S_IRUGO,            \
-               show_alarm_beep, store_beep, BEEP_ENABLE, index + 17)
+               show_alarm_beep, store_beep, BEEP_ENABLE, index + 17) }
 
-#define SENSOR_ATTR_TEMP(index)                                                \
+#define SENSOR_ATTR_TEMP(index) {                                      \
        SENSOR_ATTR_2(temp##index##_type, S_IRUGO | S_IWUSR,            \
                show_temp_mode, store_temp_mode, NOT_USED, index - 1),  \
        SENSOR_ATTR_2(temp##index##_input, S_IRUGO, show_temp,          \
@@ -1567,10 +1589,10 @@ store_sf_setup(struct device *dev, struct device_attribute *attr,
        SENSOR_ATTR_2(temp##index##_auto_point6_temp, S_IRUGO | S_IWUSR,\
                show_sf4_temp, store_sf4_temp, 5, index - 1),           \
        SENSOR_ATTR_2(temp##index##_auto_point7_temp, S_IRUGO | S_IWUSR,\
-               show_sf4_temp, store_sf4_temp, 6, index - 1)
+               show_sf4_temp, store_sf4_temp, 6, index - 1) }
 
 
-static struct sensor_device_attribute_2 w83795_in[] = {
+static struct sensor_device_attribute_2 w83795_in[][5] = {
        SENSOR_ATTR_IN(0),
        SENSOR_ATTR_IN(1),
        SENSOR_ATTR_IN(2),
@@ -1594,7 +1616,7 @@ static struct sensor_device_attribute_2 w83795_in[] = {
        SENSOR_ATTR_IN(20),
 };
 
-static struct sensor_device_attribute_2 w83795_fan[] = {
+static const struct sensor_device_attribute_2 w83795_fan[][4] = {
        SENSOR_ATTR_FAN(1),
        SENSOR_ATTR_FAN(2),
        SENSOR_ATTR_FAN(3),
@@ -1611,7 +1633,7 @@ static struct sensor_device_attribute_2 w83795_fan[] = {
        SENSOR_ATTR_FAN(14),
 };
 
-static struct sensor_device_attribute_2 w83795_temp[] = {
+static const struct sensor_device_attribute_2 w83795_temp[][29] = {
        SENSOR_ATTR_TEMP(1),
        SENSOR_ATTR_TEMP(2),
        SENSOR_ATTR_TEMP(3),
@@ -1620,7 +1642,7 @@ static struct sensor_device_attribute_2 w83795_temp[] = {
        SENSOR_ATTR_TEMP(6),
 };
 
-static struct sensor_device_attribute_2 w83795_dts[] = {
+static const struct sensor_device_attribute_2 w83795_dts[][8] = {
        SENSOR_ATTR_DTS(7),
        SENSOR_ATTR_DTS(8),
        SENSOR_ATTR_DTS(9),
@@ -1631,22 +1653,9 @@ static struct sensor_device_attribute_2 w83795_dts[] = {
        SENSOR_ATTR_DTS(14),
 };
 
-static struct sensor_device_attribute_2 w83795_static[] = {
-       SENSOR_ATTR_FANIN_TARGET(1),
-       SENSOR_ATTR_FANIN_TARGET(2),
-       SENSOR_ATTR_FANIN_TARGET(3),
-       SENSOR_ATTR_FANIN_TARGET(4),
-       SENSOR_ATTR_FANIN_TARGET(5),
-       SENSOR_ATTR_FANIN_TARGET(6),
-       SENSOR_ATTR_FANIN_TARGET(7),
-       SENSOR_ATTR_FANIN_TARGET(8),
+static const struct sensor_device_attribute_2 w83795_pwm[][7] = {
        SENSOR_ATTR_PWM(1),
        SENSOR_ATTR_PWM(2),
-};
-
-/* all registers existed in 795g than 795adg,
- * like PWM3 - PWM8 */
-static struct sensor_device_attribute_2 w83795_left_reg[] = {
        SENSOR_ATTR_PWM(3),
        SENSOR_ATTR_PWM(4),
        SENSOR_ATTR_PWM(5),
@@ -1655,7 +1664,7 @@ static struct sensor_device_attribute_2 w83795_left_reg[] = {
        SENSOR_ATTR_PWM(8),
 };
 
-static struct sensor_device_attribute_2 sda_single_files[] = {
+static const struct sensor_device_attribute_2 sda_single_files[] = {
        SENSOR_ATTR_2(chassis, S_IWUSR | S_IRUGO, show_alarm_beep,
                      store_chassis_clear, ALARM_STATUS, 46),
        SENSOR_ATTR_2(beep_enable, S_IWUSR | S_IRUGO, show_beep_enable,
@@ -1676,69 +1685,238 @@ static struct sensor_device_attribute_2 sda_single_files[] = {
 
 static void w83795_init_client(struct i2c_client *client)
 {
+       u8 config;
+
        if (reset)
                w83795_write(client, W83795_REG_CONFIG, 0x80);
 
-       /* Start monitoring */
-       w83795_write(client, W83795_REG_CONFIG,
-                    w83795_read(client, W83795_REG_CONFIG) | 0x01);
+       /* Start monitoring if needed */
+       config = w83795_read(client, W83795_REG_CONFIG);
+       if (!(config & W83795_REG_CONFIG_START)) {
+               dev_info(&client->dev, "Enabling monitoring operations\n");
+               w83795_write(client, W83795_REG_CONFIG,
+                            config | W83795_REG_CONFIG_START);
+       }
+}
+
+static int w83795_get_device_id(struct i2c_client *client)
+{
+       int device_id;
+
+       device_id = i2c_smbus_read_byte_data(client, W83795_REG_DEVICEID);
+
+       /* Special case for rev. A chips; can't be checked first because later
+          revisions emulate this for compatibility */
+       if (device_id < 0 || (device_id & 0xf0) != 0x50) {
+               int alt_id;
+
+               alt_id = i2c_smbus_read_byte_data(client,
+                                                 W83795_REG_DEVICEID_A);
+               if (alt_id == 0x50)
+                       device_id = alt_id;
+       }
+
+       return device_id;
 }
 
 /* Return 0 if detection is successful, -ENODEV otherwise */
 static int w83795_detect(struct i2c_client *client,
                         struct i2c_board_info *info)
 {
-       u8 tmp, bank;
+       int bank, vendor_id, device_id, expected, i2c_addr, config;
        struct i2c_adapter *adapter = client->adapter;
        unsigned short address = client->addr;
+       const char *chip_name;
 
        if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA))
                return -ENODEV;
        bank = i2c_smbus_read_byte_data(client, W83795_REG_BANKSEL);
+       if (bank < 0 || (bank & 0x7c)) {
+               dev_dbg(&adapter->dev,
+                       "w83795: Detection failed at addr 0x%02hx, check %s\n",
+                       address, "bank");
+               return -ENODEV;
+       }
 
-       tmp = bank & 0x80 ? 0x5c : 0xa3;
        /* Check Nuvoton vendor ID */
-       if (tmp != i2c_smbus_read_byte_data(client,
-                                               W83795_REG_VENDORID)) {
-               pr_debug("w83795: Detection failed at check "
-                        "vendor id\n");
+       vendor_id = i2c_smbus_read_byte_data(client, W83795_REG_VENDORID);
+       expected = bank & 0x80 ? 0x5c : 0xa3;
+       if (vendor_id != expected) {
+               dev_dbg(&adapter->dev,
+                       "w83795: Detection failed at addr 0x%02hx, check %s\n",
+                       address, "vendor id");
+               return -ENODEV;
+       }
+
+       /* Check device ID */
+       device_id = w83795_get_device_id(client) |
+                   (i2c_smbus_read_byte_data(client, W83795_REG_CHIPID) << 8);
+       if ((device_id >> 4) != 0x795) {
+               dev_dbg(&adapter->dev,
+                       "w83795: Detection failed at addr 0x%02hx, check %s\n",
+                       address, "device id\n");
                return -ENODEV;
        }
 
        /* If Nuvoton chip, address of chip and W83795_REG_I2C_ADDR
           should match */
-       if ((bank & 0x07) == 0
-        && (i2c_smbus_read_byte_data(client, W83795_REG_I2C_ADDR) & 0x7f) !=
-           address) {
-               pr_debug("w83795: Detection failed at check "
-                        "i2c addr\n");
-               return -ENODEV;
+       if ((bank & 0x07) == 0) {
+               i2c_addr = i2c_smbus_read_byte_data(client,
+                                                   W83795_REG_I2C_ADDR);
+               if ((i2c_addr & 0x7f) != address) {
+                       dev_dbg(&adapter->dev,
+                               "w83795: Detection failed at addr 0x%02hx, "
+                               "check %s\n", address, "i2c addr");
+                       return -ENODEV;
+               }
        }
 
-       /* Determine the chip type now */
-       if (0x79 != i2c_smbus_read_byte_data(client,
-                                            W83795_REG_CHIPID)) {
-               pr_debug("w83795: Detection failed at check "
-                        "chip id\n");
-               return -ENODEV;
+       /* Check 795 chip type: 795G or 795ADG
+          Usually we don't write to chips during detection, but here we don't
+          quite have the choice; hopefully it's OK, we are about to return
+          success anyway */
+       if ((bank & 0x07) != 0)
+               i2c_smbus_write_byte_data(client, W83795_REG_BANKSEL,
+                                         bank & ~0x07);
+       config = i2c_smbus_read_byte_data(client, W83795_REG_CONFIG);
+       if (config & W83795_REG_CONFIG_CONFIG48)
+               chip_name = "w83795adg";
+       else
+               chip_name = "w83795g";
+
+       strlcpy(info->type, chip_name, I2C_NAME_SIZE);
+       dev_info(&adapter->dev, "Found %s rev. %c at 0x%02hx\n", chip_name,
+                'A' + (device_id & 0xf), address);
+
+       return 0;
+}
+
+static int w83795_handle_files(struct device *dev, int (*fn)(struct device *,
+                              const struct device_attribute *))
+{
+       struct w83795_data *data = dev_get_drvdata(dev);
+       int err, i, j;
+
+       for (i = 0; i < ARRAY_SIZE(w83795_in); i++) {
+               if (!(data->has_in & (1 << i)))
+                       continue;
+               for (j = 0; j < ARRAY_SIZE(w83795_in[0]); j++) {
+                       err = fn(dev, &w83795_in[i][j].dev_attr);
+                       if (err)
+                               return err;
+               }
        }
 
-#if 0
-       /* Check 795 chip type: 795G or 795ADG */
-       if (W83795_REG_CONFIG_CONFIG48 &
-                 w83795_read(client, W83795_REG_CONFIG)) {
-               data->chip_type = w83795adg;
-       } else {
-               data->chip_type = w83795g;
+       for (i = 0; i < ARRAY_SIZE(w83795_fan); i++) {
+               if (!(data->has_fan & (1 << i)))
+                       continue;
+               for (j = 0; j < ARRAY_SIZE(w83795_fan[0]); j++) {
+                       err = fn(dev, &w83795_fan[i][j].dev_attr);
+                       if (err)
+                               return err;
+               }
+       }
+
+       for (i = 0; i < ARRAY_SIZE(sda_single_files); i++) {
+               err = fn(dev, &sda_single_files[i].dev_attr);
+               if (err)
+                       return err;
+       }
+
+       for (i = 0; i < data->has_pwm; i++) {
+               for (j = 0; j < ARRAY_SIZE(w83795_pwm[0]); j++) {
+                       err = fn(dev, &w83795_pwm[i][j].dev_attr);
+                       if (err)
+                               return err;
+               }
        }
-#endif
 
-       /* Fill in the remaining client fields and put into the global list */
-       strlcpy(info->type, "w83795", I2C_NAME_SIZE);
+       for (i = 0; i < ARRAY_SIZE(w83795_temp); i++) {
+               if (!(data->has_temp & (1 << i)))
+                       continue;
+               for (j = 0; j < ARRAY_SIZE(w83795_temp[0]); j++) {
+                       err = fn(dev, &w83795_temp[i][j].dev_attr);
+                       if (err)
+                               return err;
+               }
+       }
 
+       if (data->enable_dts != 0) {
+               for (i = 0; i < ARRAY_SIZE(w83795_dts); i++) {
+                       if (!(data->has_dts & (1 << i)))
+                               continue;
+                       for (j = 0; j < ARRAY_SIZE(w83795_dts[0]); j++) {
+                               err = fn(dev, &w83795_dts[i][j].dev_attr);
+                               if (err)
+                                       return err;
+                       }
+               }
+       }
+
+       return 0;
+}
+
+/* We need a wrapper that fits in w83795_handle_files */
+static int device_remove_file_wrapper(struct device *dev,
+                                     const struct device_attribute *attr)
+{
+       device_remove_file(dev, attr);
        return 0;
 }
 
+static void w83795_check_dynamic_in_limits(struct i2c_client *client)
+{
+       struct w83795_data *data = i2c_get_clientdata(client);
+       u8 vid_ctl;
+       int i, err_max, err_min;
+
+       vid_ctl = w83795_read(client, W83795_REG_VID_CTRL);
+
+       /* Return immediately if VRM isn't configured */
+       if ((vid_ctl & 0x07) == 0x00 || (vid_ctl & 0x07) == 0x07)
+               return;
+
+       data->has_dyn_in = (vid_ctl >> 3) & 0x07;
+       for (i = 0; i < 2; i++) {
+               if (!(data->has_dyn_in & (1 << i)))
+                       continue;
+
+               /* Voltage limits in dynamic mode, switch to read-only */
+               err_max = sysfs_chmod_file(&client->dev.kobj,
+                                          &w83795_in[i][2].dev_attr.attr,
+                                          S_IRUGO);
+               err_min = sysfs_chmod_file(&client->dev.kobj,
+                                          &w83795_in[i][3].dev_attr.attr,
+                                          S_IRUGO);
+               if (err_max || err_min)
+                       dev_warn(&client->dev, "Failed to set in%d limits "
+                                "read-only (%d, %d)\n", i, err_max, err_min);
+               else
+                       dev_info(&client->dev, "in%d limits set dynamically "
+                                "from VID\n", i);
+       }
+}
+
+/* Check pins that can be used for either temperature or voltage monitoring */
+static void w83795_apply_temp_config(struct w83795_data *data, u8 config,
+                                    int temp_chan, int in_chan)
+{
+       /* config is a 2-bit value */
+       switch (config) {
+       case 0x2: /* Voltage monitoring */
+               data->has_in |= 1 << in_chan;
+               break;
+       case 0x1: /* Thermal diode */
+               if (temp_chan >= 4)
+                       break;
+               data->temp_mode |= 1 << temp_chan;
+               /* fall through */
+       case 0x3: /* Thermistor */
+               data->has_temp |= 1 << temp_chan;
+               break;
+       }
+}
+
 static int w83795_probe(struct i2c_client *client,
                        const struct i2c_device_id *id)
 {
@@ -1746,7 +1924,7 @@ static int w83795_probe(struct i2c_client *client,
        u8 tmp;
        struct device *dev = &client->dev;
        struct w83795_data *data;
-       int err = 0;
+       int err;
 
        data = kzalloc(sizeof(struct w83795_data), GFP_KERNEL);
        if (!data) {
@@ -1755,82 +1933,33 @@ static int w83795_probe(struct i2c_client *client,
        }
 
        i2c_set_clientdata(client, data);
+       data->chip_type = id->driver_data;
        data->bank = i2c_smbus_read_byte_data(client, W83795_REG_BANKSEL);
        mutex_init(&data->update_lock);
 
        /* Initialize the chip */
        w83795_init_client(client);
 
-       /* Check 795 chip type: 795G or 795ADG */
-       if (W83795_REG_CONFIG_CONFIG48 &
-                 w83795_read(client, W83795_REG_CONFIG)) {
-               data->chip_type = w83795adg;
-       } else {
-               data->chip_type = w83795g;
-       }
-
-       data->has_in = w83795_read(client, W83795_REG_VOLT_CTRL1);
-       data->has_in |= w83795_read(client, W83795_REG_VOLT_CTRL2) << 8;
-       /* VSEN11-9 not for 795adg */
-       if (data->chip_type == w83795adg)
-               data->has_in &= 0xf8ff;
-       data->has_fan = w83795_read(client, W83795_REG_FANIN_CTRL1);
-       data->has_fan |= w83795_read(client, W83795_REG_FANIN_CTRL2) << 8;
+       /* Check which voltages and fans are present */
+       data->has_in = w83795_read(client, W83795_REG_VOLT_CTRL1)
+                    | (w83795_read(client, W83795_REG_VOLT_CTRL2) << 8);
+       data->has_fan = w83795_read(client, W83795_REG_FANIN_CTRL1)
+                     | (w83795_read(client, W83795_REG_FANIN_CTRL2) << 8);
 
-       /* VDSEN12-17 and TR1-6, TD1-4 use same register */
+       /* Check which analog temperatures and extra voltages are present */
        tmp = w83795_read(client, W83795_REG_TEMP_CTRL1);
        if (tmp & 0x20)
                data->enable_dts = 1;
-       else
-               data->enable_dts = 0;
-       data->has_temp = 0;
-       data->temp_mode = 0;
-       if (tmp & 0x08) {
-               if (tmp & 0x04)
-                       data->has_temp |= 0x20;
-               else
-                       data->has_in |= 0x10000;
-       }
-       if (tmp & 0x02) {
-               if (tmp & 0x01)
-                       data->has_temp |= 0x10;
-               else
-                       data->has_in |= 0x8000;
-       }
+       w83795_apply_temp_config(data, (tmp >> 2) & 0x3, 5, 16);
+       w83795_apply_temp_config(data, tmp & 0x3, 4, 15);
        tmp = w83795_read(client, W83795_REG_TEMP_CTRL2);
-       if (tmp & 0x40) {
-               data->has_temp |= 0x08;
-               if (!(tmp & 0x80))
-                       data->temp_mode |= 0x08;
-       } else if (tmp & 0x80) {
-               data->has_in |= 0x100000;
-       }
-       if (tmp & 0x10) {
-               data->has_temp |= 0x04;
-               if (!(tmp & 0x20))
-                       data->temp_mode |= 0x04;
-       } else if (tmp & 0x20) {
-               data->has_in |= 0x80000;
-       }
-       if (tmp & 0x04) {
-               data->has_temp |= 0x02;
-               if (!(tmp & 0x08))
-                       data->temp_mode |= 0x02;
-       } else if (tmp & 0x08) {
-               data->has_in |= 0x40000;
-       }
-       if (tmp & 0x01) {
-               data->has_temp |= 0x01;
-               if (!(tmp & 0x02))
-                       data->temp_mode |= 0x01;
-       } else if (tmp & 0x02) {
-               data->has_in |= 0x20000;
-       }
+       w83795_apply_temp_config(data, tmp >> 6, 3, 20);
+       w83795_apply_temp_config(data, (tmp >> 4) & 0x3, 2, 19);
+       w83795_apply_temp_config(data, (tmp >> 2) & 0x3, 1, 18);
+       w83795_apply_temp_config(data, tmp & 0x3, 0, 17);
 
        /* Check DTS enable status */
-       if (data->enable_dts == 0) {
-               data->has_dts = 0;
-       } else {
+       if (data->enable_dts) {
                if (1 & w83795_read(client, W83795_REG_DTSC))
                        data->enable_dts |= 2;
                data->has_dts = w83795_read(client, W83795_REG_DTSE);
@@ -1864,11 +1993,11 @@ static int w83795_probe(struct i2c_client *client,
                data->fan_min[i] =
                        w83795_read(client, W83795_REG_FAN_MIN_HL(i)) << 4;
                data->fan_min[i] |=
-                 (w83795_read(client, W83795_REG_FAN_MIN_LSB(i) >>
-                       W83795_REG_FAN_MIN_LSB_SHIFT(i))) & 0x0F;
+                 (w83795_read(client, W83795_REG_FAN_MIN_LSB(i)) >>
+                       W83795_REG_FAN_MIN_LSB_SHIFT(i)) & 0x0F;
                data->fan[i] = w83795_read(client, W83795_REG_FAN(i)) << 4;
                data->fan[i] |=
-                 (w83795_read(client, W83795_REG_VRLSB >> 4)) & 0x0F;
+                 (w83795_read(client, W83795_REG_VRLSB) >> 4) & 0x0F;
        }
 
        /* temperature and limits */
@@ -1919,7 +2048,6 @@ static int w83795_probe(struct i2c_client *client,
                data->has_pwm = 2;
        data->pwm_fcms[0] = w83795_read(client, W83795_REG_FCMS1);
        data->pwm_fcms[1] = w83795_read(client, W83795_REG_FCMS2);
-       /* w83795adg only support pwm2-0 */
        for (i = 0; i < W83795_REG_TEMP_NUM; i++)
                data->pwm_tfmr[i] = w83795_read(client, W83795_REG_TFMR(i));
        data->pwm_fomc = w83795_read(client, W83795_REG_FOMC);
@@ -1970,61 +2098,12 @@ static int w83795_probe(struct i2c_client *client,
        data->beep_enable =
                (w83795_read(client, W83795_REG_BEEP(5)) >> 7) & 0x01;
 
-       /* Register sysfs hooks */
-       for (i = 0; i < ARRAY_SIZE(w83795_in); i++) {
-               if (!(data->has_in & (1 << (i / 6))))
-                       continue;
-               err = device_create_file(dev, &w83795_in[i].dev_attr);
-               if (err)
-                       goto exit_remove;
-       }
-
-       for (i = 0; i < ARRAY_SIZE(w83795_fan); i++) {
-               if (!(data->has_fan & (1 << (i / 5))))
-                       continue;
-               err = device_create_file(dev, &w83795_fan[i].dev_attr);
-               if (err)
-                       goto exit_remove;
-       }
-
-       for (i = 0; i < ARRAY_SIZE(sda_single_files); i++) {
-               err = device_create_file(dev, &sda_single_files[i].dev_attr);
-               if (err)
-                       goto exit_remove;
-       }
-
-       for (i = 0; i < ARRAY_SIZE(w83795_temp); i++) {
-               if (!(data->has_temp & (1 << (i / 29))))
-                       continue;
-               err = device_create_file(dev, &w83795_temp[i].dev_attr);
-               if (err)
-                       goto exit_remove;
-       }
-
-       if (data->enable_dts != 0) {
-               for (i = 0; i < ARRAY_SIZE(w83795_dts); i++) {
-                       if (!(data->has_dts & (1 << (i / 8))))
-                               continue;
-                       err = device_create_file(dev, &w83795_dts[i].dev_attr);
-                       if (err)
-                               goto exit_remove;
-               }
-       }
-
-       if (data->chip_type == w83795g) {
-               for (i = 0; i < ARRAY_SIZE(w83795_left_reg); i++) {
-                       err = device_create_file(dev,
-                                                &w83795_left_reg[i].dev_attr);
-                       if (err)
-                               goto exit_remove;
-               }
-       }
+       err = w83795_handle_files(dev, device_create_file);
+       if (err)
+               goto exit_remove;
 
-       for (i = 0; i < ARRAY_SIZE(w83795_static); i++) {
-               err = device_create_file(dev, &w83795_static[i].dev_attr);
-               if (err)
-                       goto exit_remove;
-       }
+       if (data->chip_type == w83795g)
+               w83795_check_dynamic_in_limits(client);
 
        data->hwmon_dev = hwmon_device_register(dev);
        if (IS_ERR(data->hwmon_dev)) {
@@ -2034,31 +2113,8 @@ static int w83795_probe(struct i2c_client *client,
 
        return 0;
 
-       /* Unregister sysfs hooks */
 exit_remove:
-       for (i = 0; i < ARRAY_SIZE(w83795_in); i++)
-               device_remove_file(dev, &w83795_in[i].dev_attr);
-
-       for (i = 0; i < ARRAY_SIZE(w83795_fan); i++)
-               device_remove_file(dev, &w83795_fan[i].dev_attr);
-
-       for (i = 0; i < ARRAY_SIZE(sda_single_files); i++)
-               device_remove_file(dev, &sda_single_files[i].dev_attr);
-
-       if (data->chip_type == w83795g) {
-               for (i = 0; i < ARRAY_SIZE(w83795_left_reg); i++)
-                       device_remove_file(dev, &w83795_left_reg[i].dev_attr);
-       }
-
-       for (i = 0; i < ARRAY_SIZE(w83795_temp); i++)
-               device_remove_file(dev, &w83795_temp[i].dev_attr);
-
-       for (i = 0; i < ARRAY_SIZE(w83795_dts); i++)
-               device_remove_file(dev, &w83795_dts[i].dev_attr);
-
-       for (i = 0; i < ARRAY_SIZE(w83795_static); i++)
-               device_remove_file(dev, &w83795_static[i].dev_attr);
-
+       w83795_handle_files(dev, device_remove_file_wrapper);
        kfree(data);
 exit:
        return err;
@@ -2067,34 +2123,9 @@ exit:
 static int w83795_remove(struct i2c_client *client)
 {
        struct w83795_data *data = i2c_get_clientdata(client);
-       struct device *dev = &client->dev;
-       int i;
 
        hwmon_device_unregister(data->hwmon_dev);
-
-       for (i = 0; i < ARRAY_SIZE(w83795_in); i++)
-               device_remove_file(dev, &w83795_in[i].dev_attr);
-
-       for (i = 0; i < ARRAY_SIZE(w83795_fan); i++)
-               device_remove_file(dev, &w83795_fan[i].dev_attr);
-
-       for (i = 0; i < ARRAY_SIZE(sda_single_files); i++)
-               device_remove_file(dev, &sda_single_files[i].dev_attr);
-
-       if (data->chip_type == w83795g) {
-               for (i = 0; i < ARRAY_SIZE(w83795_left_reg); i++)
-                       device_remove_file(dev, &w83795_left_reg[i].dev_attr);
-       }
-
-       for (i = 0; i < ARRAY_SIZE(w83795_temp); i++)
-               device_remove_file(dev, &w83795_temp[i].dev_attr);
-
-       for (i = 0; i < ARRAY_SIZE(w83795_dts); i++)
-               device_remove_file(dev, &w83795_dts[i].dev_attr);
-
-       for (i = 0; i < ARRAY_SIZE(w83795_static); i++)
-               device_remove_file(dev, &w83795_static[i].dev_attr);
-
+       w83795_handle_files(&client->dev, device_remove_file_wrapper);
        kfree(data);
 
        return 0;
@@ -2102,7 +2133,8 @@ static int w83795_remove(struct i2c_client *client)
 
 
 static const struct i2c_device_id w83795_id[] = {
-       { "w83795", w83795 },
+       { "w83795g", w83795g },
+       { "w83795adg", w83795adg },
        { }
 };
 MODULE_DEVICE_TABLE(i2c, w83795_id);
@@ -2131,7 +2163,7 @@ static void __exit sensors_w83795_exit(void)
 }
 
 MODULE_AUTHOR("Wei Song");
-MODULE_DESCRIPTION("w83795 driver");
+MODULE_DESCRIPTION("W83795G/ADG hardware monitoring driver");
 MODULE_LICENSE("GPL");
 
 module_init(sensors_w83795_init);