]> git.sur5r.net Git - groeck-it87/commitdiff
Disable SMBus access while accessing Enviromnental Controller registers
authorGuenter Roeck <linux@roeck-us.net>
Sat, 30 Sep 2017 04:52:09 +0000 (21:52 -0700)
committerGuenter Roeck <linux@roeck-us.net>
Sat, 30 Sep 2017 16:23:47 +0000 (09:23 -0700)
Some chips support Environmental Controller access through SMBus.
On those chips, it is possible that an Embedded Controller accesses
Environmental Controller chip registers at any time. There is no real
means for synchronization. On banked chips, this can and will result in
access errors with unpredictable result.

Disable SMBus access while reading or writing environmental controller
registers to work around the problem.

Signed-off-by: Guenter Roeck <linux@roeck-us.net>
it87.c

diff --git a/it87.c b/it87.c
index 87ee0882127e81190ea36764747da002162e6bf0..533d243a80d363b884062c29c15e74855bd011c5 100644 (file)
--- a/it87.c
+++ b/it87.c
@@ -146,13 +146,10 @@ static inline int superio_enter(int ioreg)
         * Try to reserve ioreg and ioreg + 1 for exclusive access.
         */
        if (!request_muxed_region(ioreg, 2, DRVNAME))
-               goto error;
+               return -EBUSY;
 
        __superio_enter(ioreg);
        return 0;
-
-error:
-       return -EBUSY;
 }
 
 static inline void superio_exit(int ioreg, bool doexit)
@@ -193,10 +190,13 @@ static inline void superio_exit(int ioreg, bool doexit)
 #define IT8655E_DEVID 0x8655
 #define IT8665E_DEVID 0x8665
 #define IT8686E_DEVID 0x8686
-#define IT87_ACT_REG  0x30
-#define IT87_BASE_REG 0x60
 
-/* Logical device 7 registers (IT8712F and later) */
+/* Logical device 4 (Environmental Monitor) registers */
+#define IT87_ACT_REG           0x30
+#define IT87_BASE_REG          0x60
+#define IT87_SPECIAL_CFG_REG   0xf3    /* special configuration register */
+
+/* Logical device 7 (GPIO) registers (IT8712F and later) */
 #define IT87_SIO_GPIO1_REG     0x25
 #define IT87_SIO_GPIO2_REG     0x26
 #define IT87_SIO_GPIO3_REG     0x27
@@ -335,6 +335,8 @@ struct it87_devices {
        u8 num_temp_map;        /* Number of temperature sources for pwm */
        u8 peci_mask;
        u8 old_peci_mask;
+       u8 smbus_bitmap;        /* SMBus enable bits in extra config register */
+       u8 ec_special_config;
 };
 
 #define FEAT_12MV_ADC          BIT(0)
@@ -618,6 +620,7 @@ static const struct it87_devices it87_devices[] = {
                .num_temp_limit = 6,
                .num_temp_offset = 6,
                .num_temp_map = 6,
+               .smbus_bitmap = BIT(1) | BIT(2),
        },
        [it8628] = {
                .name = "it8628",
@@ -641,6 +644,7 @@ static const struct it87_devices it87_devices[] = {
                .num_temp_limit = 6,
                .num_temp_offset = 6,
                .num_temp_map = 6,
+               .smbus_bitmap = BIT(2),
        },
        [it8665] = {
                .name = "it8665",
@@ -652,6 +656,7 @@ static const struct it87_devices it87_devices[] = {
                .num_temp_limit = 6,
                .num_temp_offset = 6,
                .num_temp_map = 6,
+               .smbus_bitmap = BIT(2),
        },
        [it8686] = {
                .name = "it8686",
@@ -663,6 +668,7 @@ static const struct it87_devices it87_devices[] = {
                .num_temp_limit = 6,
                .num_temp_offset = 6,
                .num_temp_map = 7,
+               .smbus_bitmap = BIT(1) | BIT(2),
        },
 };
 
@@ -703,6 +709,8 @@ static const struct it87_devices it87_devices[] = {
 
 struct it87_sio_data {
        enum chips type;
+       u8 sioaddr;
+       u8 doexit;
        /* Values read from Super-I/O config space */
        u8 revision;
        u8 vid_value;
@@ -714,6 +722,8 @@ struct it87_sio_data {
        u8 skip_fan;
        u8 skip_pwm;
        u8 skip_temp;
+       u8 smbus_bitmap;
+       u8 ec_special_config;
 };
 
 /*
@@ -727,6 +737,11 @@ struct it87_data {
        u8 peci_mask;
        u8 old_peci_mask;
 
+       u8 smbus_bitmap;        /* !=0 if SMBus needs to be disabled */
+       u8 ec_special_config;   /* EC special config register restore value */
+       u8 sioaddr;             /* SIO port address */
+       bool doexit;            /* true if exit from sio config is ok */
+
        const u8 *REG_FAN;
        const u8 *REG_FANX;
        const u8 *REG_FAN_MIN;
@@ -910,6 +925,39 @@ static const unsigned int pwm_freq[8] = {
        750000,
 };
 
+static int smbus_disable(struct it87_data *data)
+{
+       int err;
+
+       if (data->smbus_bitmap) {
+               err = superio_enter(data->sioaddr);
+               if (err)
+                       return err;
+               superio_select(data->sioaddr, PME);
+               superio_outb(data->sioaddr, IT87_SPECIAL_CFG_REG,
+                            data->ec_special_config & ~data->smbus_bitmap);
+               superio_exit(data->sioaddr, data->doexit);
+       }
+       return 0;
+}
+
+static int smbus_enable(struct it87_data *data)
+{
+       int err;
+
+       if (data->smbus_bitmap) {
+               err = superio_enter(data->sioaddr);
+               if (err)
+                       return err;
+
+               superio_select(data->sioaddr, PME);
+               superio_outb(data->sioaddr, IT87_SPECIAL_CFG_REG,
+                            data->ec_special_config);
+               superio_exit(data->sioaddr, data->doexit);
+       }
+       return 0;
+}
+
 static int _it87_read_value(struct it87_data *data, u8 reg)
 {
        outb_p(reg, data->addr + IT87_ADDR_REG_OFFSET);
@@ -941,6 +989,7 @@ static u8 it87_set_bank(struct it87_data *data, u8 bank)
 
 /*
  * Must be called with data->update_lock held, except during initialization.
+ * Must be called with SMBus accesses disabled.
  * We ignore the IT87 BUSY flag at this moment - it could lead to deadlocks,
  * would slow down the IT87 access and should not be necessary.
  */
@@ -958,6 +1007,7 @@ static int it87_read_value(struct it87_data *data, u16 reg)
 
 /*
  * Must be called with data->update_lock held, except during initialization.
+ * Must be called with SMBus accesses disabled
  * We ignore the IT87 BUSY flag at this moment - it could lead to deadlocks,
  * would slow down the IT87 access and should not be necessary.
  */
@@ -1023,12 +1073,32 @@ static void it87_update_pwm_ctrl(struct it87_data *data, int nr)
        }
 }
 
+static int it87_lock(struct it87_data *data)
+{
+       int err;
+
+       mutex_lock(&data->update_lock);
+       err = smbus_disable(data);
+       if (err)
+               mutex_unlock(&data->update_lock);
+       return err;
+}
+
+static void it87_unlock(struct it87_data *data)
+{
+       smbus_enable(data);
+       mutex_unlock(&data->update_lock);
+}
+
 static struct it87_data *it87_update_device(struct device *dev)
 {
        struct it87_data *data = dev_get_drvdata(dev);
+       int err;
        int i;
 
-       mutex_lock(&data->update_lock);
+       err = it87_lock(data);
+       if (err)
+               return ERR_PTR(err);
 
        if (time_after(jiffies, data->last_updated + HZ + HZ / 2) ||
            !data->valid) {
@@ -1135,9 +1205,7 @@ static struct it87_data *it87_update_device(struct device *dev)
                data->last_updated = jiffies;
                data->valid = 1;
        }
-
-       mutex_unlock(&data->update_lock);
-
+       it87_unlock(data);
        return data;
 }
 
@@ -1149,6 +1217,9 @@ static ssize_t show_in(struct device *dev, struct device_attribute *attr,
        int index = sattr->index;
        int nr = sattr->nr;
 
+       if (IS_ERR(data))
+               return PTR_ERR(data);
+
        return sprintf(buf, "%d\n", in_from_reg(data, nr, data->in[nr][index]));
 }
 
@@ -1160,17 +1231,21 @@ static ssize_t set_in(struct device *dev, struct device_attribute *attr,
        int index = sattr->index;
        int nr = sattr->nr;
        unsigned long val;
+       int err;
 
        if (kstrtoul(buf, 10, &val) < 0)
                return -EINVAL;
 
-       mutex_lock(&data->update_lock);
+       err = it87_lock(data);
+       if (err)
+               return err;
+
        data->in[nr][index] = in_to_reg(data, nr, val);
        it87_write_value(data,
                         index == 1 ? IT87_REG_VIN_MIN(nr)
                                    : IT87_REG_VIN_MAX(nr),
                         data->in[nr][index]);
-       mutex_unlock(&data->update_lock);
+       it87_unlock(data);
        return count;
 }
 
@@ -1237,6 +1312,9 @@ static ssize_t show_temp(struct device *dev, struct device_attribute *attr,
        int index = sattr->index;
        struct it87_data *data = it87_update_device(dev);
 
+       if (IS_ERR(data))
+               return PTR_ERR(data);
+
        return sprintf(buf, "%d\n", TEMP_FROM_REG(data->temp[nr][index]));
 }
 
@@ -1249,11 +1327,14 @@ static ssize_t set_temp(struct device *dev, struct device_attribute *attr,
        struct it87_data *data = dev_get_drvdata(dev);
        long val;
        u8 reg, regval;
+       int err;
 
        if (kstrtol(buf, 10, &val) < 0)
                return -EINVAL;
 
-       mutex_lock(&data->update_lock);
+       err = it87_lock(data);
+       if (err)
+               return err;
 
        switch (index) {
        default:
@@ -1276,7 +1357,7 @@ static ssize_t set_temp(struct device *dev, struct device_attribute *attr,
 
        data->temp[nr][index] = TEMP_TO_REG(val);
        it87_write_value(data, reg, data->temp[nr][index]);
-       mutex_unlock(&data->update_lock);
+       it87_unlock(data);
        return count;
 }
 
@@ -1397,8 +1478,12 @@ static ssize_t show_temp_type(struct device *dev, struct device_attribute *attr,
 {
        struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr);
        struct it87_data *data = it87_update_device(dev);
-       int type = get_temp_type(data, sensor_attr->index);
+       int type;
+
+       if (IS_ERR(data))
+               return PTR_ERR(data);
 
+       type = get_temp_type(data, sensor_attr->index);
        return sprintf(buf, "%d\n", type);
 }
 
@@ -1411,10 +1496,15 @@ static ssize_t set_temp_type(struct device *dev, struct device_attribute *attr,
        struct it87_data *data = dev_get_drvdata(dev);
        long val;
        u8 reg, extra;
+       int err;
 
        if (kstrtol(buf, 10, &val) < 0)
                return -EINVAL;
 
+       err = it87_lock(data);
+       if (err)
+               return err;
+
        reg = it87_read_value(data, IT87_REG_TEMP_ENABLE);
        reg &= ~(1 << nr);
        reg &= ~(8 << nr);
@@ -1437,17 +1527,19 @@ static ssize_t set_temp_type(struct device *dev, struct device_attribute *attr,
                reg |= (nr + 1) << 6;
        else if (has_temp_old_peci(data, nr) && val == 6)
                extra |= 0x80;
-       else if (val != 0)
-               return -EINVAL;
+       else if (val != 0) {
+               count = -EINVAL;
+               goto unlock;
+       }
 
-       mutex_lock(&data->update_lock);
        data->sensor = reg;
        data->extra = extra;
        it87_write_value(data, IT87_REG_TEMP_ENABLE, data->sensor);
        if (has_temp_old_peci(data, nr))
                it87_write_value(data, IT87_REG_TEMP_EXTRA, data->extra);
        data->valid = 0;        /* Force cache refresh */
-       mutex_unlock(&data->update_lock);
+unlock:
+       it87_unlock(data);
        return count;
 }
 
@@ -1489,6 +1581,9 @@ static ssize_t show_fan(struct device *dev, struct device_attribute *attr,
        int speed;
        struct it87_data *data = it87_update_device(dev);
 
+       if (IS_ERR(data))
+               return PTR_ERR(data);
+
        speed = has_16bit_fans(data) ?
                FAN16_FROM_REG(data->fan[nr][index]) :
                FAN_FROM_REG(data->fan[nr][index],
@@ -1503,6 +1598,9 @@ static ssize_t show_fan_div(struct device *dev, struct device_attribute *attr,
        struct it87_data *data = it87_update_device(dev);
        int nr = sensor_attr->index;
 
+       if (IS_ERR(data))
+               return PTR_ERR(data);
+
        return sprintf(buf, "%lu\n", DIV_FROM_REG(data->fan_div[nr]));
 }
 
@@ -1513,6 +1611,9 @@ static ssize_t show_pwm_enable(struct device *dev,
        struct it87_data *data = it87_update_device(dev);
        int nr = sensor_attr->index;
 
+       if (IS_ERR(data))
+               return PTR_ERR(data);
+
        return sprintf(buf, "%d\n", pwm_mode(data, nr));
 }
 
@@ -1523,6 +1624,9 @@ static ssize_t show_pwm(struct device *dev, struct device_attribute *attr,
        struct it87_data *data = it87_update_device(dev);
        int nr = sensor_attr->index;
 
+       if (IS_ERR(data))
+               return PTR_ERR(data);
+
        return sprintf(buf, "%d\n",
                       pwm_from_reg(data, data->pwm_duty[nr]));
 }
@@ -1536,6 +1640,9 @@ static ssize_t show_pwm_freq(struct device *dev, struct device_attribute *attr,
        unsigned int freq;
        int index;
 
+       if (IS_ERR(data))
+               return PTR_ERR(data);
+
        if (has_pwm_freq2(data) && nr == 1)
                index = (data->extra >> 4) & 0x07;
        else
@@ -1555,12 +1662,15 @@ static ssize_t set_fan(struct device *dev, struct device_attribute *attr,
 
        struct it87_data *data = dev_get_drvdata(dev);
        long val;
+       int err;
        u8 reg;
 
        if (kstrtol(buf, 10, &val) < 0)
                return -EINVAL;
 
-       mutex_lock(&data->update_lock);
+       err = it87_lock(data);
+       if (err)
+               return err;
 
        if (has_16bit_fans(data)) {
                data->fan[nr][index] = FAN16_TO_REG(val);
@@ -1586,8 +1696,7 @@ static ssize_t set_fan(struct device *dev, struct device_attribute *attr,
                it87_write_value(data, data->REG_FAN_MIN[nr],
                                 data->fan[nr][index]);
        }
-
-       mutex_unlock(&data->update_lock);
+       it87_unlock(data);
        return count;
 }
 
@@ -1598,13 +1707,16 @@ static ssize_t set_fan_div(struct device *dev, struct device_attribute *attr,
        struct it87_data *data = dev_get_drvdata(dev);
        int nr = sensor_attr->index;
        unsigned long val;
-       int min;
+       int min, err;
        u8 old;
 
        if (kstrtoul(buf, 10, &val) < 0)
                return -EINVAL;
 
-       mutex_lock(&data->update_lock);
+       err = it87_lock(data);
+       if (err)
+               return err;
+
        old = it87_read_value(data, IT87_REG_FAN_DIV);
 
        /* Save fan min limit */
@@ -1631,8 +1743,7 @@ static ssize_t set_fan_div(struct device *dev, struct device_attribute *attr,
        /* Restore fan min limit */
        data->fan[nr][1] = FAN_TO_REG(min, DIV_FROM_REG(data->fan_div[nr]));
        it87_write_value(data, data->REG_FAN_MIN[nr], data->fan[nr][1]);
-
-       mutex_unlock(&data->update_lock);
+       it87_unlock(data);
        return count;
 }
 
@@ -1673,6 +1784,7 @@ static ssize_t set_pwm_enable(struct device *dev, struct device_attribute *attr,
        struct it87_data *data = dev_get_drvdata(dev);
        int nr = sensor_attr->index;
        long val;
+       int err;
 
        if (kstrtol(buf, 10, &val) < 0 || val < 0 || val > 2)
                return -EINVAL;
@@ -1683,7 +1795,10 @@ static ssize_t set_pwm_enable(struct device *dev, struct device_attribute *attr,
                        return -EINVAL;
        }
 
-       mutex_lock(&data->update_lock);
+       err = it87_lock(data);
+       if (err)
+               return err;;
+
        it87_update_pwm_ctrl(data, nr);
 
        if (val == 0) {
@@ -1737,8 +1852,7 @@ static ssize_t set_pwm_enable(struct device *dev, struct device_attribute *attr,
                                         data->fan_main_ctrl);
                }
        }
-
-       mutex_unlock(&data->update_lock);
+       it87_unlock(data);
        return count;
 }
 
@@ -1749,11 +1863,15 @@ static ssize_t set_pwm(struct device *dev, struct device_attribute *attr,
        struct it87_data *data = dev_get_drvdata(dev);
        int nr = sensor_attr->index;
        long val;
+       int err;
 
        if (kstrtol(buf, 10, &val) < 0 || val < 0 || val > 255)
                return -EINVAL;
 
-       mutex_lock(&data->update_lock);
+       err = it87_lock(data);
+       if (err)
+               return err;
+
        it87_update_pwm_ctrl(data, nr);
        if (has_newer_autopwm(data)) {
                /*
@@ -1761,8 +1879,8 @@ static ssize_t set_pwm(struct device *dev, struct device_attribute *attr,
                 * is read-only so we can't write the value.
                 */
                if (data->pwm_ctrl[nr] & 0x80) {
-                       mutex_unlock(&data->update_lock);
-                       return -EBUSY;
+                       count = -EBUSY;
+                       goto unlock;
                }
                data->pwm_duty[nr] = pwm_to_reg(data, val);
                it87_write_value(data, IT87_REG_PWM_DUTY[nr],
@@ -1779,7 +1897,8 @@ static ssize_t set_pwm(struct device *dev, struct device_attribute *attr,
                                         data->pwm_ctrl[nr]);
                }
        }
-       mutex_unlock(&data->update_lock);
+unlock:
+       it87_unlock(data);
        return count;
 }
 
@@ -1790,6 +1909,7 @@ static ssize_t set_pwm_freq(struct device *dev, struct device_attribute *attr,
        struct it87_data *data = dev_get_drvdata(dev);
        int nr = sensor_attr->index;
        unsigned long val;
+       int err;
        int i;
 
        if (kstrtoul(buf, 10, &val) < 0)
@@ -1804,7 +1924,10 @@ static ssize_t set_pwm_freq(struct device *dev, struct device_attribute *attr,
                        break;
        }
 
-       mutex_lock(&data->update_lock);
+       err = it87_lock(data);
+       if (err)
+               return err;
+
        if (nr == 0) {
                data->fan_ctl = it87_read_value(data, IT87_REG_FAN_CTL) & 0x8f;
                data->fan_ctl |= i << 4;
@@ -1814,8 +1937,7 @@ static ssize_t set_pwm_freq(struct device *dev, struct device_attribute *attr,
                data->extra |= i << 4;
                it87_write_value(data, IT87_REG_TEMP_EXTRA, data->extra);
        }
-       mutex_unlock(&data->update_lock);
-
+       it87_unlock(data);
        return count;
 }
 
@@ -1826,6 +1948,9 @@ static ssize_t show_pwm_temp_map(struct device *dev,
        struct it87_data *data = it87_update_device(dev);
        int nr = sensor_attr->index;
 
+       if (IS_ERR(data))
+               return PTR_ERR(data);
+
        return sprintf(buf, "%d\n", data->pwm_temp_map[nr] + 1);
 }
 
@@ -1837,6 +1962,7 @@ static ssize_t set_pwm_temp_map(struct device *dev,
        struct it87_data *data = dev_get_drvdata(dev);
        int nr = sensor_attr->index;
        unsigned long val;
+       int err;
        u8 map;
 
        if (kstrtoul(buf, 10, &val) < 0)
@@ -1847,7 +1973,10 @@ static ssize_t set_pwm_temp_map(struct device *dev,
 
        map = val - 1;
 
-       mutex_lock(&data->update_lock);
+       err = it87_lock(data);
+       if (err)
+               return err;
+
        it87_update_pwm_ctrl(data, nr);
        data->pwm_temp_map[nr] = map;
        /*
@@ -1858,7 +1987,7 @@ static ssize_t set_pwm_temp_map(struct device *dev,
                data->pwm_ctrl[nr] = temp_map_to_reg(data, nr, map);
                it87_write_value(data, data->REG_PWM[nr], data->pwm_ctrl[nr]);
        }
-       mutex_unlock(&data->update_lock);
+       it87_unlock(data);
        return count;
 }
 
@@ -1871,6 +2000,9 @@ static ssize_t show_auto_pwm(struct device *dev, struct device_attribute *attr,
        int nr = sensor_attr->nr;
        int point = sensor_attr->index;
 
+       if (IS_ERR(data))
+               return PTR_ERR(data);
+
        return sprintf(buf, "%d\n",
                       pwm_from_reg(data, data->auto_pwm[nr][point]));
 }
@@ -1885,18 +2017,22 @@ static ssize_t set_auto_pwm(struct device *dev, struct device_attribute *attr,
        int point = sensor_attr->index;
        int regaddr;
        long val;
+       int err;
 
        if (kstrtol(buf, 10, &val) < 0 || val < 0 || val > 255)
                return -EINVAL;
 
-       mutex_lock(&data->update_lock);
+       err = it87_lock(data);
+       if (err)
+               return err;
+
        data->auto_pwm[nr][point] = pwm_to_reg(data, val);
        if (has_newer_autopwm(data))
                regaddr = IT87_REG_AUTO_TEMP(nr, 3);
        else
                regaddr = IT87_REG_AUTO_PWM(nr, point);
        it87_write_value(data, regaddr, data->auto_pwm[nr][point]);
-       mutex_unlock(&data->update_lock);
+       it87_unlock(data);
        return count;
 }
 
@@ -1907,6 +2043,9 @@ static ssize_t show_auto_pwm_slope(struct device *dev,
        struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr);
        int nr = sensor_attr->index;
 
+       if (IS_ERR(data))
+               return PTR_ERR(data);
+
        return sprintf(buf, "%d\n", data->auto_pwm[nr][1] & 0x7f);
 }
 
@@ -1918,15 +2057,19 @@ static ssize_t set_auto_pwm_slope(struct device *dev,
        struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr);
        int nr = sensor_attr->index;
        unsigned long val;
+       int err;
 
        if (kstrtoul(buf, 10, &val) < 0 || val > 127)
                return -EINVAL;
 
-       mutex_lock(&data->update_lock);
+       err = it87_lock(data);
+       if (err)
+               return err;
+
        data->auto_pwm[nr][1] = (data->auto_pwm[nr][1] & 0x80) | val;
        it87_write_value(data, IT87_REG_AUTO_TEMP(nr, 4),
                         data->auto_pwm[nr][1]);
-       mutex_unlock(&data->update_lock);
+       it87_unlock(data);
        return count;
 }
 
@@ -1940,6 +2083,9 @@ static ssize_t show_auto_temp(struct device *dev, struct device_attribute *attr,
        int point = sensor_attr->index;
        int reg;
 
+       if (IS_ERR(data))
+               return PTR_ERR(data);
+
        if (has_old_autopwm(data) || point)
                reg = data->auto_temp[nr][point];
        else
@@ -1958,11 +2104,15 @@ static ssize_t set_auto_temp(struct device *dev, struct device_attribute *attr,
        int point = sensor_attr->index;
        long val;
        int reg;
+       int err;
 
        if (kstrtol(buf, 10, &val) < 0 || val < -128000 || val > 127000)
                return -EINVAL;
 
-       mutex_lock(&data->update_lock);
+       err = it87_lock(data);
+       if (err)
+               return err;
+
        if (has_newer_autopwm(data) && !point) {
                reg = data->auto_temp[nr][1] - TEMP_TO_REG(val);
                reg = clamp_val(reg, 0, 0x1f) | (data->auto_temp[nr][0] & 0xe0);
@@ -1975,7 +2125,7 @@ static ssize_t set_auto_temp(struct device *dev, struct device_attribute *attr,
                        point--;
                it87_write_value(data, IT87_REG_AUTO_TEMP(nr, point), reg);
        }
-       mutex_unlock(&data->update_lock);
+       it87_unlock(data);
        return count;
 }
 
@@ -2160,6 +2310,9 @@ static ssize_t show_alarms(struct device *dev, struct device_attribute *attr,
 {
        struct it87_data *data = it87_update_device(dev);
 
+       if (IS_ERR(data))
+               return PTR_ERR(data);
+
        return sprintf(buf, "%u\n", data->alarms);
 }
 static DEVICE_ATTR(alarms, S_IRUGO, show_alarms, NULL);
@@ -2170,6 +2323,9 @@ static ssize_t show_alarm(struct device *dev, struct device_attribute *attr,
        struct it87_data *data = it87_update_device(dev);
        int bitnr = to_sensor_dev_attr(attr)->index;
 
+       if (IS_ERR(data))
+               return PTR_ERR(data);
+
        return sprintf(buf, "%u\n", (data->alarms >> bitnr) & 1);
 }
 
@@ -2178,24 +2334,22 @@ static ssize_t clear_intrusion(struct device *dev,
                               size_t count)
 {
        struct it87_data *data = dev_get_drvdata(dev);
-       int config;
+       int err, config;
        long val;
 
        if (kstrtol(buf, 10, &val) < 0 || val != 0)
                return -EINVAL;
 
-       mutex_lock(&data->update_lock);
-       config = it87_read_value(data, IT87_REG_CONFIG);
-       if (config < 0) {
-               count = config;
-       } else {
-               config |= BIT(5);
-               it87_write_value(data, IT87_REG_CONFIG, config);
-               /* Invalidate cache to force re-read */
-               data->valid = 0;
-       }
-       mutex_unlock(&data->update_lock);
+       err = it87_lock(data);
+       if (err)
+               return err;
 
+       config = it87_read_value(data, IT87_REG_CONFIG);
+       config |= BIT(5);
+       it87_write_value(data, IT87_REG_CONFIG, config);
+       /* Invalidate cache to force re-read */
+       data->valid = 0;
+       it87_unlock(data);
        return count;
 }
 
@@ -2228,6 +2382,9 @@ static ssize_t show_beep(struct device *dev, struct device_attribute *attr,
        struct it87_data *data = it87_update_device(dev);
        int bitnr = to_sensor_dev_attr(attr)->index;
 
+       if (IS_ERR(data))
+               return PTR_ERR(data);
+
        return sprintf(buf, "%u\n", (data->beeps >> bitnr) & 1);
 }
 
@@ -2237,18 +2394,22 @@ static ssize_t set_beep(struct device *dev, struct device_attribute *attr,
        int bitnr = to_sensor_dev_attr(attr)->index;
        struct it87_data *data = dev_get_drvdata(dev);
        long val;
+       int err;
 
        if (kstrtol(buf, 10, &val) < 0 || (val != 0 && val != 1))
                return -EINVAL;
 
-       mutex_lock(&data->update_lock);
+       err = it87_lock(data);
+       if (err)
+               return err;
+
        data->beeps = it87_read_value(data, IT87_REG_BEEP_ENABLE);
        if (val)
                data->beeps |= BIT(bitnr);
        else
                data->beeps &= ~BIT(bitnr);
        it87_write_value(data, IT87_REG_BEEP_ENABLE, data->beeps);
-       mutex_unlock(&data->update_lock);
+       it87_unlock(data);
        return count;
 }
 
@@ -2304,6 +2465,9 @@ static ssize_t show_vid_reg(struct device *dev, struct device_attribute *attr,
 {
        struct it87_data *data = it87_update_device(dev);
 
+       if (IS_ERR(data))
+               return PTR_ERR(data);
+
        return sprintf(buf, "%ld\n", (long)vid_from_reg(data->vid, data->vrm));
 }
 static DEVICE_ATTR(cpu0_vid, S_IRUGO, show_vid_reg, NULL);
@@ -2789,6 +2953,8 @@ static int __init it87_find(int sioaddr, unsigned short *address,
        if (err)
                return err;
 
+       sio_data->sioaddr = sioaddr;
+
        err = -ENODEV;
        chip_type = superio_inw(sioaddr, DEVID);
        if (chip_type == 0xffff)
@@ -2905,6 +3071,8 @@ static int __init it87_find(int sioaddr, unsigned short *address,
                goto exit;
        }
 
+       sio_data->doexit = doexit;
+
        err = 0;
        sio_data->revision = superio_inb(sioaddr, DEVREV) & 0x0f;
        pr_info("Found IT%04x%s chip at 0x%x, revision %d\n", chip_type,
@@ -3387,6 +3555,15 @@ static int __init it87_find(int sioaddr, unsigned short *address,
        if (sio_data->beep_pin)
                pr_info("Beeping is supported\n");
 
+       if (config->smbus_bitmap) {
+               u8 reg;
+
+               superio_select(sioaddr, PME);
+               reg = superio_inb(sioaddr, IT87_SPECIAL_CFG_REG);
+               sio_data->ec_special_config = reg;
+               sio_data->smbus_bitmap = reg & config->smbus_bitmap;
+       }
+
 exit:
        superio_exit(sioaddr, doexit);
        return err;
@@ -3664,6 +3841,7 @@ static int it87_probe(struct platform_device *pdev)
        struct it87_sio_data *sio_data = dev_get_platdata(dev);
        int enable_pwm_interface;
        struct device *hwmon_dev;
+       int err;
 
        res = platform_get_resource(pdev, IORESOURCE_IO, 0);
        if (!devm_request_region(&pdev->dev, res->start, IT87_EC_EXTENT,
@@ -3680,6 +3858,10 @@ static int it87_probe(struct platform_device *pdev)
 
        data->addr = res->start;
        data->type = sio_data->type;
+       data->sioaddr = sio_data->sioaddr;
+       data->smbus_bitmap = sio_data->smbus_bitmap;
+       data->ec_special_config = sio_data->ec_special_config;
+       data->doexit = sio_data->doexit;
        data->features = it87_devices[sio_data->type].features;
        data->num_temp_limit = it87_devices[sio_data->type].num_temp_limit;
        data->num_temp_offset = it87_devices[sio_data->type].num_temp_offset;
@@ -3710,11 +3892,6 @@ static int it87_probe(struct platform_device *pdev)
                break;
        }
 
-       /* Now, we do the remaining detection. */
-       if ((it87_read_value(data, IT87_REG_CONFIG) & 0x80) ||
-           it87_read_value(data, IT87_REG_CHIPID) != 0x90)
-               return -ENODEV;
-
        platform_set_drvdata(pdev, data);
 
        mutex_init(&data->update_lock);
@@ -3722,6 +3899,17 @@ static int it87_probe(struct platform_device *pdev)
        /* Initialize register pointers */
        it87_init_regs(pdev);
 
+       err = smbus_disable(data);
+       if (err)
+               return err;
+
+       /* Now, we do the remaining detection. */
+       if ((it87_read_value(data, IT87_REG_CONFIG) & 0x80) ||
+           it87_read_value(data, IT87_REG_CHIPID) != 0x90) {
+               smbus_enable(data);
+               return -ENODEV;
+       }
+
        /* Check PWM configuration */
        enable_pwm_interface = it87_check_pwm(dev);
 
@@ -3778,6 +3966,8 @@ static int it87_probe(struct platform_device *pdev)
        /* Initialize the IT87 chip */
        it87_init_device(pdev);
 
+       smbus_enable(data);
+
        if (!sio_data->skip_vid) {
                data->has_vid = true;
                data->vrm = vid_which_vrm();