OMAP3: rework of the ASM sleep code execution paths
[firefly-linux-kernel-4.4.55.git] / drivers / hwmon / w83795.c
index 1d840aa837823a9b0ee6e6f2fe84f7af950d26a8..cdbc7448491e127d8c11e599b57314625010b261 100644 (file)
@@ -165,10 +165,14 @@ static const u8 IN_LSB_SHIFT_IDX[][2] = {
 
 #define W83795_REG_VID_CTRL            0x6A
 
+#define W83795_REG_ALARM_CTRL          0x40
+#define ALARM_CTRL_RTSACS              (1 << 7)
 #define W83795_REG_ALARM(index)                (0x41 + (index))
+#define W83795_REG_CLR_CHASSIS         0x4D
 #define W83795_REG_BEEP(index)         (0x50 + (index))
 
-#define W83795_REG_CLR_CHASSIS         0x4D
+#define W83795_REG_OVT_CFG             0x58
+#define OVT_CFG_SEL                    (1 << 7)
 
 
 #define W83795_REG_FCMS1               0x201
@@ -178,6 +182,14 @@ static const u8 IN_LSB_SHIFT_IDX[][2] = {
 
 #define W83795_REG_TSS(index)          (0x209 + (index))
 
+#define TSS_MAP_RESERVED               0xff
+static const u8 tss_map[4][6] = {
+       { 0,  1,  2,  3,  4,  5},
+       { 6,  7,  8,  9,  0,  1},
+       {10, 11, 12, 13,  2,  3},
+       { 4,  5,  4,  5, TSS_MAP_RESERVED, TSS_MAP_RESERVED},
+};
+
 #define PWM_OUTPUT                     0
 #define PWM_FREQ                       1
 #define PWM_START                      2
@@ -369,6 +381,7 @@ struct w83795_data {
        u8 setup_pwm[3];        /* Register value */
 
        u8 alarms[6];           /* Register value */
+       u8 enable_beep;
        u8 beeps[6];            /* Register value */
 
        char valid;
@@ -499,8 +512,11 @@ static void w83795_update_limits(struct i2c_client *client)
        }
 
        /* Read beep settings */
-       for (i = 0; i < ARRAY_SIZE(data->beeps); i++)
-               data->beeps[i] = w83795_read(client, W83795_REG_BEEP(i));
+       if (data->enable_beep) {
+               for (i = 0; i < ARRAY_SIZE(data->beeps); i++)
+                       data->beeps[i] =
+                               w83795_read(client, W83795_REG_BEEP(i));
+       }
 
        data->valid_limits = 1;
 }
@@ -577,6 +593,7 @@ static struct w83795_data *w83795_update_device(struct device *dev)
        struct i2c_client *client = to_i2c_client(dev);
        struct w83795_data *data = i2c_get_clientdata(client);
        u16 tmp;
+       u8 intrusion;
        int i;
 
        mutex_lock(&data->update_lock);
@@ -648,9 +665,24 @@ static struct w83795_data *w83795_update_device(struct device *dev)
                    w83795_read(client, W83795_REG_PWM(i, PWM_OUTPUT));
        }
 
-       /* update alarm */
+       /* Update intrusion and alarms
+        * It is important to read intrusion first, because reading from
+        * register SMI STS6 clears the interrupt status temporarily. */
+       tmp = w83795_read(client, W83795_REG_ALARM_CTRL);
+       /* Switch to interrupt status for intrusion if needed */
+       if (tmp & ALARM_CTRL_RTSACS)
+               w83795_write(client, W83795_REG_ALARM_CTRL,
+                            tmp & ~ALARM_CTRL_RTSACS);
+       intrusion = w83795_read(client, W83795_REG_ALARM(5)) & (1 << 6);
+       /* Switch to real-time alarms */
+       w83795_write(client, W83795_REG_ALARM_CTRL, tmp | ALARM_CTRL_RTSACS);
        for (i = 0; i < ARRAY_SIZE(data->alarms); i++)
                data->alarms[i] = w83795_read(client, W83795_REG_ALARM(i));
+       data->alarms[5] |= intrusion;
+       /* Restore original configuration if needed */
+       if (!(tmp & ALARM_CTRL_RTSACS))
+               w83795_write(client, W83795_REG_ALARM_CTRL,
+                            tmp & ~ALARM_CTRL_RTSACS);
 
        data->last_updated = jiffies;
        data->valid = 1;
@@ -730,6 +762,10 @@ store_chassis_clear(struct device *dev,
        val = w83795_read(client, W83795_REG_CLR_CHASSIS);
        val |= 0x80;
        w83795_write(client, W83795_REG_CLR_CHASSIS, val);
+
+       /* Clear status and force cache refresh */
+       w83795_read(client, W83795_REG_ALARM(5));
+       data->valid = 0;
        mutex_unlock(&data->update_lock);
        return count;
 }
@@ -857,20 +893,20 @@ show_pwm_enable(struct device *dev, struct device_attribute *attr, char *buf)
        int index = sensor_attr->index;
        u8 tmp;
 
-       if (1 == (data->pwm_fcms[0] & (1 << index))) {
+       /* Speed cruise mode */
+       if (data->pwm_fcms[0] & (1 << index)) {
                tmp = 2;
                goto out;
        }
+       /* Thermal cruise or SmartFan IV mode */
        for (tmp = 0; tmp < 6; tmp++) {
                if (data->pwm_tfmr[tmp] & (1 << index)) {
                        tmp = 3;
                        goto out;
                }
        }
-       if (data->pwm_fomc & (1 << index))
-               tmp = 0;
-       else
-               tmp = 1;
+       /* Manual mode */
+       tmp = 1;
 
 out:
        return sprintf(buf, "%u\n", tmp);
@@ -890,23 +926,21 @@ store_pwm_enable(struct device *dev, struct device_attribute *attr,
 
        if (strict_strtoul(buf, 10, &val) < 0)
                return -EINVAL;
-       if (val > 2)
+       if (val < 1 || val > 2)
                return -EINVAL;
 
        mutex_lock(&data->update_lock);
        switch (val) {
-       case 0:
        case 1:
+               /* Clear speed cruise mode bits */
                data->pwm_fcms[0] &= ~(1 << index);
                w83795_write(client, W83795_REG_FCMS1, data->pwm_fcms[0]);
+               /* Clear thermal cruise mode bits */
                for (i = 0; i < 6; i++) {
                        data->pwm_tfmr[i] &= ~(1 << index);
                        w83795_write(client, W83795_REG_TFMR(i),
                                data->pwm_tfmr[i]);
                }
-               data->pwm_fomc |= 1 << index;
-               data->pwm_fomc ^= val << index;
-               w83795_write(client, W83795_REG_FOMC, data->pwm_fomc);
                break;
        case 2:
                data->pwm_fcms[0] |= (1 << index);
@@ -917,6 +951,42 @@ store_pwm_enable(struct device *dev, struct device_attribute *attr,
        return count;
 }
 
+static ssize_t
+show_pwm_mode(struct device *dev, struct device_attribute *attr, char *buf)
+{
+       struct w83795_data *data = w83795_update_pwm_config(dev);
+       int index = to_sensor_dev_attr_2(attr)->index;
+       unsigned int mode;
+
+       if (data->pwm_fomc & (1 << index))
+               mode = 0;       /* DC */
+       else
+               mode = 1;       /* PWM */
+
+       return sprintf(buf, "%u\n", mode);
+}
+
+/*
+ * Check whether a given temperature source can ever be useful.
+ * Returns the number of selectable temperature channels which are
+ * enabled.
+ */
+static int w83795_tss_useful(const struct w83795_data *data, int tsrc)
+{
+       int useful = 0, i;
+
+       for (i = 0; i < 4; i++) {
+               if (tss_map[i][tsrc] == TSS_MAP_RESERVED)
+                       continue;
+               if (tss_map[i][tsrc] < 6)       /* Analog */
+                       useful += (data->has_temp >> tss_map[i][tsrc]) & 1;
+               else                            /* Digital */
+                       useful += (data->has_dts >> (tss_map[i][tsrc] - 6)) & 1;
+       }
+
+       return useful;
+}
+
 static ssize_t
 show_temp_src(struct device *dev, struct device_attribute *attr, char *buf)
 {
@@ -924,17 +994,18 @@ show_temp_src(struct device *dev, struct device_attribute *attr, char *buf)
            to_sensor_dev_attr_2(attr);
        struct w83795_data *data = w83795_update_pwm_config(dev);
        int index = sensor_attr->index;
-       u8 val = index / 2;
-       u8 tmp = data->temp_src[val];
+       u8 tmp = data->temp_src[index / 2];
 
        if (index & 1)
-               val = 4;
+               tmp >>= 4;      /* Pick high nibble */
        else
-               val = 0;
-       tmp >>= val;
-       tmp &= 0x0f;
+               tmp &= 0x0f;    /* Pick low nibble */
 
-       return sprintf(buf, "%u\n", tmp);
+       /* Look-up the actual temperature channel number */
+       if (tmp >= 4 || tss_map[tmp][index] == TSS_MAP_RESERVED)
+               return -EINVAL;         /* Shouldn't happen */
+
+       return sprintf(buf, "%u\n", (unsigned int)tss_map[tmp][index] + 1);
 }
 
 static ssize_t
@@ -946,12 +1017,21 @@ store_temp_src(struct device *dev, struct device_attribute *attr,
        struct sensor_device_attribute_2 *sensor_attr =
            to_sensor_dev_attr_2(attr);
        int index = sensor_attr->index;
-       unsigned long tmp;
+       int tmp;
+       unsigned long channel;
        u8 val = index / 2;
 
-       if (strict_strtoul(buf, 10, &tmp) < 0)
+       if (strict_strtoul(buf, 10, &channel) < 0 ||
+           channel < 1 || channel > 14)
+               return -EINVAL;
+
+       /* Check if request can be fulfilled */
+       for (tmp = 0; tmp < 4; tmp++) {
+               if (tss_map[tmp][index] == channel - 1)
+                       break;
+       }
+       if (tmp == 4)   /* No match */
                return -EINVAL;
-       tmp = SENSORS_LIMIT(tmp, 0, 15);
 
        mutex_lock(&data->update_lock);
        if (index & 1) {
@@ -1515,7 +1595,7 @@ 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
+/* Don't change the attribute order, _max, _min and _beep 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,        \
@@ -1530,6 +1610,8 @@ store_sf_setup(struct device *dev, struct device_attribute *attr,
                show_alarm_beep, store_beep, BEEP_ENABLE,               \
                index + ((index > 14) ? 1 : 0)) }
 
+/* Don't change the attribute order, _beep is accessed by index
+ * somewhere else in the code */
 #define SENSOR_ATTR_FAN(index) {                                       \
        SENSOR_ATTR_2(fan##index##_input, S_IRUGO, show_fan,            \
                NULL, FAN_INPUT, index - 1), \
@@ -1553,9 +1635,13 @@ store_sf_setup(struct device *dev, struct device_attribute *attr,
                show_pwm, store_pwm, PWM_FREQ, index - 1),       \
        SENSOR_ATTR_2(pwm##index##_enable, S_IWUSR | S_IRUGO,           \
                show_pwm_enable, store_pwm_enable, NOT_USED, index - 1), \
+       SENSOR_ATTR_2(pwm##index##_mode, S_IRUGO,                       \
+               show_pwm_mode, NULL, NOT_USED, index - 1),              \
        SENSOR_ATTR_2(fan##index##_target, S_IWUSR | S_IRUGO, \
                show_fanin, store_fanin, FANIN_TARGET, index - 1) }
 
+/* Don't change the attribute order, _beep is accessed by index
+ * somewhere else in the code */
 #define SENSOR_ATTR_DTS(index) {                                       \
        SENSOR_ATTR_2(temp##index##_type, S_IRUGO ,             \
                show_dts_mode, NULL, NOT_USED, index - 7),      \
@@ -1574,6 +1660,8 @@ store_sf_setup(struct device *dev, struct device_attribute *attr,
        SENSOR_ATTR_2(temp##index##_beep, S_IWUSR | S_IRUGO,            \
                show_alarm_beep, store_beep, BEEP_ENABLE, index + 17) }
 
+/* Don't change the attribute order, _beep is accessed by index
+ * somewhere else in the code */
 #define SENSOR_ATTR_TEMP(index) {                                      \
        SENSOR_ATTR_2(temp##index##_type, S_IRUGO | (index < 4 ? S_IWUSR : 0), \
                show_temp_mode, store_temp_mode, NOT_USED, index - 1),  \
@@ -1593,8 +1681,6 @@ store_sf_setup(struct device *dev, struct device_attribute *attr,
        SENSOR_ATTR_2(temp##index##_beep, S_IWUSR | S_IRUGO,            \
                show_alarm_beep, store_beep, BEEP_ENABLE,               \
                index + (index > 4 ? 11 : 17)),                         \
-       SENSOR_ATTR_2(temp##index##_source_sel, S_IWUSR | S_IRUGO,      \
-               show_temp_src, store_temp_src, NOT_USED, index - 1),    \
        SENSOR_ATTR_2(temp##index##_pwm_enable, S_IWUSR | S_IRUGO,      \
                show_temp_pwm_enable, store_temp_pwm_enable,            \
                TEMP_PWM_ENABLE, index - 1),                            \
@@ -1680,7 +1766,7 @@ static const struct sensor_device_attribute_2 w83795_fan[][4] = {
        SENSOR_ATTR_FAN(14),
 };
 
-static const struct sensor_device_attribute_2 w83795_temp[][29] = {
+static const struct sensor_device_attribute_2 w83795_temp[][28] = {
        SENSOR_ATTR_TEMP(1),
        SENSOR_ATTR_TEMP(2),
        SENSOR_ATTR_TEMP(3),
@@ -1700,7 +1786,7 @@ static const struct sensor_device_attribute_2 w83795_dts[][8] = {
        SENSOR_ATTR_DTS(14),
 };
 
-static const struct sensor_device_attribute_2 w83795_pwm[][7] = {
+static const struct sensor_device_attribute_2 w83795_pwm[][8] = {
        SENSOR_ATTR_PWM(1),
        SENSOR_ATTR_PWM(2),
        SENSOR_ATTR_PWM(3),
@@ -1711,13 +1797,24 @@ static const struct sensor_device_attribute_2 w83795_pwm[][7] = {
        SENSOR_ATTR_PWM(8),
 };
 
+static const struct sensor_device_attribute_2 w83795_tss[6] = {
+       SENSOR_ATTR_2(temp1_source_sel, S_IWUSR | S_IRUGO,
+                     show_temp_src, store_temp_src, NOT_USED, 0),
+       SENSOR_ATTR_2(temp2_source_sel, S_IWUSR | S_IRUGO,
+                     show_temp_src, store_temp_src, NOT_USED, 1),
+       SENSOR_ATTR_2(temp3_source_sel, S_IWUSR | S_IRUGO,
+                     show_temp_src, store_temp_src, NOT_USED, 2),
+       SENSOR_ATTR_2(temp4_source_sel, S_IWUSR | S_IRUGO,
+                     show_temp_src, store_temp_src, NOT_USED, 3),
+       SENSOR_ATTR_2(temp5_source_sel, S_IWUSR | S_IRUGO,
+                     show_temp_src, store_temp_src, NOT_USED, 4),
+       SENSOR_ATTR_2(temp6_source_sel, S_IWUSR | S_IRUGO,
+                     show_temp_src, store_temp_src, NOT_USED, 5),
+};
+
 static const struct sensor_device_attribute_2 sda_single_files[] = {
        SENSOR_ATTR_2(intrusion0_alarm, S_IWUSR | S_IRUGO, show_alarm_beep,
                      store_chassis_clear, ALARM_STATUS, 46),
-       SENSOR_ATTR_2(intrusion0_beep, S_IWUSR | S_IRUGO, show_alarm_beep,
-                     store_beep, BEEP_ENABLE, 46),
-       SENSOR_ATTR_2(beep_enable, S_IWUSR | S_IRUGO, show_alarm_beep,
-                     store_beep, BEEP_ENABLE, 47),
 #ifdef CONFIG_SENSORS_W83795_FANCTRL
        SENSOR_ATTR_2(speed_cruise_tolerance, S_IWUSR | S_IRUGO, show_fanin,
                store_fanin, FANIN_TOL, NOT_USED),
@@ -1730,6 +1827,13 @@ static const struct sensor_device_attribute_2 sda_single_files[] = {
 #endif
 };
 
+static const struct sensor_device_attribute_2 sda_beep_files[] = {
+       SENSOR_ATTR_2(intrusion0_beep, S_IWUSR | S_IRUGO, show_alarm_beep,
+                     store_beep, BEEP_ENABLE, 46),
+       SENSOR_ATTR_2(beep_enable, S_IWUSR | S_IRUGO, show_alarm_beep,
+                     store_beep, BEEP_ENABLE, 47),
+};
+
 /*
  * Driver interface
  */
@@ -1859,6 +1963,8 @@ static int w83795_handle_files(struct device *dev, int (*fn)(struct device *,
                if (!(data->has_in & (1 << i)))
                        continue;
                for (j = 0; j < ARRAY_SIZE(w83795_in[0]); j++) {
+                       if (j == 4 && !data->enable_beep)
+                               continue;
                        err = fn(dev, &w83795_in[i][j].dev_attr);
                        if (err)
                                return err;
@@ -1869,18 +1975,37 @@ static int w83795_handle_files(struct device *dev, int (*fn)(struct device *,
                if (!(data->has_fan & (1 << i)))
                        continue;
                for (j = 0; j < ARRAY_SIZE(w83795_fan[0]); j++) {
+                       if (j == 3 && !data->enable_beep)
+                               continue;
                        err = fn(dev, &w83795_fan[i][j].dev_attr);
                        if (err)
                                return err;
                }
        }
 
+       for (i = 0; i < ARRAY_SIZE(w83795_tss); i++) {
+               j = w83795_tss_useful(data, i);
+               if (!j)
+                       continue;
+               err = fn(dev, &w83795_tss[i].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;
        }
 
+       if (data->enable_beep) {
+               for (i = 0; i < ARRAY_SIZE(sda_beep_files); i++) {
+                       err = fn(dev, &sda_beep_files[i].dev_attr);
+                       if (err)
+                               return err;
+               }
+       }
+
 #ifdef CONFIG_SENSORS_W83795_FANCTRL
        for (i = 0; i < data->has_pwm; i++) {
                for (j = 0; j < ARRAY_SIZE(w83795_pwm[0]); j++) {
@@ -1899,6 +2024,8 @@ static int w83795_handle_files(struct device *dev, int (*fn)(struct device *,
 #else
                for (j = 0; j < 8; j++) {
 #endif
+                       if (j == 7 && !data->enable_beep)
+                               continue;
                        err = fn(dev, &w83795_temp[i][j].dev_attr);
                        if (err)
                                return err;
@@ -1910,6 +2037,8 @@ static int w83795_handle_files(struct device *dev, int (*fn)(struct device *,
                        if (!(data->has_dts & (1 << i)))
                                continue;
                        for (j = 0; j < ARRAY_SIZE(w83795_dts[0]); j++) {
+                               if (j == 7 && !data->enable_beep)
+                                       continue;
                                err = fn(dev, &w83795_dts[i][j].dev_attr);
                                if (err)
                                        return err;
@@ -2049,6 +2178,18 @@ static int w83795_probe(struct i2c_client *client,
        else
                data->has_pwm = 2;
 
+       /* Check if BEEP pin is available */
+       if (data->chip_type == w83795g) {
+               /* The W83795G has a dedicated BEEP pin */
+               data->enable_beep = 1;
+       } else {
+               /* The W83795ADG has a shared pin for OVT# and BEEP, so you
+                * can't have both */
+               tmp = w83795_read(client, W83795_REG_OVT_CFG);
+               if ((tmp & OVT_CFG_SEL) == 0)
+                       data->enable_beep = 1;
+       }
+
        err = w83795_handle_files(dev, device_create_file);
        if (err)
                goto exit_remove;