hwmon: (w83795) Add support for dynamic in0-2 limits
[cascardo/linux.git] / drivers / hwmon / w83795.c
index b454160..458fb29 100644 (file)
@@ -55,6 +55,7 @@ MODULE_PARM_DESC(reset, "Set to 1 to reset chip, not recommended");
 #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
@@ -271,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;
 }
@@ -315,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 */
@@ -449,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 */
@@ -1334,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:
@@ -1344,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;
        }
@@ -1372,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);
@@ -1449,6 +1468,8 @@ store_sf_setup(struct device *dev, struct device_attribute *attr,
 
 #define NOT_USED                       -1
 
+/* 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), \
@@ -1664,12 +1685,18 @@ static const 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)
@@ -1837,6 +1864,59 @@ static int device_remove_file_wrapper(struct device *dev,
        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)
 {
@@ -1844,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) {
@@ -1860,68 +1940,26 @@ static int w83795_probe(struct i2c_client *client,
        /* Initialize the chip */
        w83795_init_client(client);
 
-       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);
@@ -1955,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 */
@@ -2010,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);
@@ -2065,6 +2102,9 @@ static int w83795_probe(struct i2c_client *client,
        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)) {
                err = PTR_ERR(data->hwmon_dev);