]> git.sur5r.net Git - groeck-it87/blobdiff - it87.c
Add feature flags for fan count and 16-bit fan configuration
[groeck-it87] / it87.c
diff --git a/it87.c b/it87.c
index 7db581c7b3a97de5ffa01e3326421ea988f4e3eb..ce689ae7698debe468a41f9f0402655db35d07b4 100644 (file)
--- a/it87.c
+++ b/it87.c
@@ -10,7 +10,9 @@
  *  This driver supports only the Environment Controller in the IT8705F and
  *  similar parts.  The other devices are supported by different drivers.
  *
- *  Supports: IT8705F  Super I/O chip w/LPC interface
+ *  Supports: IT8603E  Super I/O chip w/LPC interface
+ *            IT8623E  Super I/O chip w/LPC interface
+ *            IT8705F  Super I/O chip w/LPC interface
  *            IT8712F  Super I/O chip w/LPC interface
  *            IT8716F  Super I/O chip w/LPC interface
  *            IT8718F  Super I/O chip w/LPC interface
  *            IT8726F  Super I/O chip w/LPC interface
  *            IT8728F  Super I/O chip w/LPC interface
  *            IT8758E  Super I/O chip w/LPC interface
+ *            IT8771E  Super I/O chip w/LPC interface
+ *            IT8772E  Super I/O chip w/LPC interface
+ *            IT8781F  Super I/O chip w/LPC interface
  *            IT8782F  Super I/O chip w/LPC interface
  *            IT8783E/F Super I/O chip w/LPC interface
  *            Sis950   A clone of the IT8705F
  *
  *  Copyright (C) 2001 Chris Gauthron
- *  Copyright (C) 2005-2010 Jean Delvare <khali@linux-fr.org>
+ *  Copyright (C) 2005-2010 Jean Delvare <jdelvare@suse.de>
  *
  *  This program is free software; you can redistribute it and/or modify
  *  it under the terms of the GNU General Public License as published by
 #include <linux/dmi.h>
 #include <linux/acpi.h>
 #include <linux/io.h>
-#include <linux/version.h>
 #include "compat.h"
 
 #define DRVNAME "it87"
 
-enum chips { it87, it8712, it8716, it8718, it8720, it8721, it8728, it8782,
-            it8783 };
+enum chips { it87, it8712, it8716, it8718, it8720, it8721, it8728, it8771,
+            it8772, it8781, it8782, it8783, it8603 };
 
 static unsigned short force_id;
 module_param(force_id, ushort, 0);
@@ -142,8 +146,13 @@ static inline void superio_exit(void)
 #define IT8721F_DEVID 0x8721
 #define IT8726F_DEVID 0x8726
 #define IT8728F_DEVID 0x8728
+#define IT8771E_DEVID 0x8771
+#define IT8772E_DEVID 0x8772
+#define IT8781F_DEVID 0x8781
 #define IT8782F_DEVID 0x8782
 #define IT8783E_DEVID 0x8783
+#define IT8603E_DEVID 0x8603
+#define IT8623E_DEVID 0x8623
 #define IT87_ACT_REG  0x30
 #define IT87_BASE_REG 0x60
 
@@ -205,6 +214,8 @@ static const u8 IT87_REG_FAN[]              = { 0x0d, 0x0e, 0x0f, 0x80, 0x82 };
 static const u8 IT87_REG_FAN_MIN[]     = { 0x10, 0x11, 0x12, 0x84, 0x86 };
 static const u8 IT87_REG_FANX[]                = { 0x18, 0x19, 0x1a, 0x81, 0x83 };
 static const u8 IT87_REG_FANX_MIN[]    = { 0x1b, 0x1c, 0x1d, 0x85, 0x87 };
+static const u8 IT87_REG_TEMP_OFFSET[] = { 0x56, 0x57, 0x59 };
+
 #define IT87_REG_FAN_MAIN_CTRL 0x13
 #define IT87_REG_FAN_CTL       0x14
 #define IT87_REG_PWM(nr)       (0x15 + (nr))
@@ -218,8 +229,6 @@ static const u8 IT87_REG_FANX_MIN[] = { 0x1b, 0x1c, 0x1d, 0x85, 0x87 };
 #define IT87_REG_TEMP_HIGH(nr) (0x40 + (nr) * 2)
 #define IT87_REG_TEMP_LOW(nr)  (0x41 + (nr) * 2)
 
-#define IT87_REG_TEMP_OFFSET(nr)       (0x55 + (1 << ((nr) - 1)))
-
 #define IT87_REG_VIN_ENABLE    0x50
 #define IT87_REG_TEMP_ENABLE   0x51
 #define IT87_REG_TEMP_EXTRA    0x55
@@ -231,58 +240,123 @@ static const u8 IT87_REG_FANX_MIN[]      = { 0x1b, 0x1c, 0x1d, 0x85, 0x87 };
 #define IT87_REG_AUTO_PWM(nr, i)  (0x65 + (nr) * 8 + (i))
 
 struct it87_devices {
-       const char * const name;
+       const char *name;
        u16 features;
+       u8 peci_mask;
+       u8 old_peci_mask;
 };
 
-#define        FEAT_12MV_ADC           (1 << 0)
-#define        FEAT_NEWER_AUTOPWM      (1 << 1)
-#define        FEAT_OLD_AUTOPWM        (1 << 2)
-#define        FEAT_16BIT_FAN          (1 << 3)
+#define FEAT_12MV_ADC          (1 << 0)
+#define FEAT_NEWER_AUTOPWM     (1 << 1)
+#define FEAT_OLD_AUTOPWM       (1 << 2)
+#define FEAT_16BIT_FANS                (1 << 3)
+#define FEAT_TEMP_OFFSET       (1 << 4)
+#define FEAT_TEMP_PECI         (1 << 5)
+#define FEAT_TEMP_OLD_PECI     (1 << 6)
+#define FEAT_FAN16_CONFIG      (1 << 7)        /* Need to enable 16-bit fans */
+#define FEAT_FIVE_FANS         (1 << 8)        /* Supports five fans */
 
 static const struct it87_devices it87_devices[] = {
        [it87] = {
                .name = "it87",
-               .features = FEAT_OLD_AUTOPWM,   /* may need to override */
+               .features = FEAT_OLD_AUTOPWM | FEAT_FAN16_CONFIG,
+                                               /* may need to overwrite */
        },
        [it8712] = {
                .name = "it8712",
-               .features = FEAT_OLD_AUTOPWM,   /* may need to overwrite */
+               .features = FEAT_OLD_AUTOPWM | FEAT_FAN16_CONFIG,
+                                               /* may need to overwrite */
        },
        [it8716] = {
                .name = "it8716",
-               .features = FEAT_16BIT_FAN,
+               .features = FEAT_16BIT_FANS | FEAT_TEMP_OFFSET
+                 | FEAT_FAN16_CONFIG | FEAT_FIVE_FANS,
        },
        [it8718] = {
                .name = "it8718",
-               .features = FEAT_16BIT_FAN,
+               .features = FEAT_16BIT_FANS | FEAT_TEMP_OFFSET
+                 | FEAT_TEMP_OLD_PECI | FEAT_FAN16_CONFIG | FEAT_FIVE_FANS,
+               .old_peci_mask = 0x4,
        },
        [it8720] = {
                .name = "it8720",
-               .features = FEAT_16BIT_FAN,
+               .features = FEAT_16BIT_FANS | FEAT_TEMP_OFFSET
+                 | FEAT_TEMP_OLD_PECI | FEAT_FAN16_CONFIG | FEAT_FIVE_FANS,
+               .old_peci_mask = 0x4,
        },
        [it8721] = {
                .name = "it8721",
-               .features = FEAT_NEWER_AUTOPWM | FEAT_12MV_ADC | FEAT_16BIT_FAN,
+               .features = FEAT_NEWER_AUTOPWM | FEAT_12MV_ADC | FEAT_16BIT_FANS
+                 | FEAT_TEMP_OFFSET | FEAT_TEMP_OLD_PECI | FEAT_TEMP_PECI
+                 | FEAT_FAN16_CONFIG | FEAT_FIVE_FANS,
+               .peci_mask = 0x05,
+               .old_peci_mask = 0x02,  /* Actually reports PCH */
        },
        [it8728] = {
                .name = "it8728",
-               .features = FEAT_NEWER_AUTOPWM | FEAT_12MV_ADC | FEAT_16BIT_FAN,
+               .features = FEAT_NEWER_AUTOPWM | FEAT_12MV_ADC | FEAT_16BIT_FANS
+                 | FEAT_TEMP_OFFSET | FEAT_TEMP_PECI | FEAT_FIVE_FANS,
+               .peci_mask = 0x07,
+       },
+       [it8771] = {
+               .name = "it8771",
+               .features = FEAT_NEWER_AUTOPWM | FEAT_12MV_ADC | FEAT_16BIT_FANS
+                 | FEAT_TEMP_OFFSET | FEAT_TEMP_PECI,
+                               /* PECI: guesswork */
+                               /* 12mV ADC (OHM) */
+                               /* 16 bit fans (OHM) */
+                               /* three fans, always 16 bit (guesswork) */
+               .peci_mask = 0x07,
+       },
+       [it8772] = {
+               .name = "it8772",
+               .features = FEAT_NEWER_AUTOPWM | FEAT_12MV_ADC | FEAT_16BIT_FANS
+                 | FEAT_TEMP_OFFSET | FEAT_TEMP_PECI,
+                               /* PECI (coreboot) */
+                               /* 12mV ADC (HWSensors4, OHM) */
+                               /* 16 bit fans (HWSensors4, OHM) */
+                               /* three fans, always 16 bit (datasheet) */
+               .peci_mask = 0x07,
+       },
+       [it8781] = {
+               .name = "it8781",
+               .features = FEAT_16BIT_FANS | FEAT_TEMP_OFFSET
+                 | FEAT_TEMP_OLD_PECI | FEAT_FAN16_CONFIG,
+               .old_peci_mask = 0x4,
        },
        [it8782] = {
                .name = "it8782",
-               .features = FEAT_16BIT_FAN,
+               .features = FEAT_16BIT_FANS | FEAT_TEMP_OFFSET
+                 | FEAT_TEMP_OLD_PECI | FEAT_FAN16_CONFIG,
+               .old_peci_mask = 0x4,
        },
        [it8783] = {
                .name = "it8783",
-               .features = FEAT_16BIT_FAN,
+               .features = FEAT_16BIT_FANS | FEAT_TEMP_OFFSET
+                 | FEAT_TEMP_OLD_PECI | FEAT_FAN16_CONFIG,
+               .old_peci_mask = 0x4,
+       },
+       [it8603] = {
+               .name = "it8603",
+               .features = FEAT_NEWER_AUTOPWM | FEAT_12MV_ADC | FEAT_16BIT_FANS
+                 | FEAT_TEMP_OFFSET | FEAT_TEMP_PECI,
+               .peci_mask = 0x07,
        },
 };
 
-#define has_16bit_fans(data)   ((data)->features & FEAT_16BIT_FAN)
+#define has_16bit_fans(data)   ((data)->features & FEAT_16BIT_FANS)
 #define has_12mv_adc(data)     ((data)->features & FEAT_12MV_ADC)
 #define has_newer_autopwm(data)        ((data)->features & FEAT_NEWER_AUTOPWM)
 #define has_old_autopwm(data)  ((data)->features & FEAT_OLD_AUTOPWM)
+#define has_temp_offset(data)  ((data)->features & FEAT_TEMP_OFFSET)
+#define has_temp_peci(data, nr)        (((data)->features & FEAT_TEMP_PECI) && \
+                                ((data)->peci_mask & (1 << nr)))
+#define has_temp_old_peci(data, nr) \
+                               (((data)->features & FEAT_TEMP_OLD_PECI) && \
+                                ((data)->old_peci_mask & (1 << nr)))
+#define has_fan16_config(data) (has_16bit_fans(data) && \
+                                ((data)->features & FEAT_FAN16_CONFIG))
+#define has_five_fans(data)    ((data)->features & FEAT_FIVE_FANS)
 
 struct it87_sio_data {
        enum chips type;
@@ -306,7 +380,9 @@ struct it87_sio_data {
 struct it87_data {
        struct device *hwmon_dev;
        enum chips type;
-       u32 features;
+       u16 features;
+       u8 peci_mask;
+       u8 old_peci_mask;
 
        unsigned short addr;
        const char *name;
@@ -315,12 +391,13 @@ struct it87_data {
        unsigned long last_updated;     /* In jiffies */
 
        u16 in_scaled;          /* Internal voltage sensors are scaled */
-       u8 in[9][3];            /* [nr][0]=in, [1]=min, [2]=max */
+       u8 in[10][3];           /* [nr][0]=in, [1]=min, [2]=max */
        u8 has_fan;             /* Bitfield, fans enabled */
        u16 fan[5][2];          /* Register values, [nr][0]=fan, [1]=min */
        u8 has_temp;            /* Bitfield, temp sensors enabled */
        s8 temp[3][4];          /* [nr][0]=temp, [1]=min, [2]=max, [3]=offset */
-       u8 sensor;              /* Register value */
+       u8 sensor;              /* Register value (IT87_REG_TEMP_ENABLE) */
+       u8 extra;               /* Register value (IT87_REG_TEMP_EXTRA) */
        u8 fan_div[3];          /* Register encoding, shifted right */
        u8 vid;                 /* Register encoding, combined */
        u8 vrm;
@@ -359,7 +436,7 @@ static int adc_lsb(const struct it87_data *data, int nr)
 static u8 in_to_reg(const struct it87_data *data, int nr, long val)
 {
        val = DIV_ROUND_CLOSEST(val, adc_lsb(data, nr));
-       return SENSORS_LIMIT(val, 0, 255);
+       return clamp_val(val, 0, 255);
 }
 
 static int in_from_reg(const struct it87_data *data, int nr, int val)
@@ -371,16 +448,15 @@ static inline u8 FAN_TO_REG(long rpm, int div)
 {
        if (rpm == 0)
                return 255;
-       rpm = SENSORS_LIMIT(rpm, 1, 1000000);
-       return SENSORS_LIMIT((1350000 + rpm * div / 2) / (rpm * div), 1,
-                            254);
+       rpm = clamp_val(rpm, 1, 1000000);
+       return clamp_val((1350000 + rpm * div / 2) / (rpm * div), 1, 254);
 }
 
 static inline u16 FAN16_TO_REG(long rpm)
 {
        if (rpm == 0)
                return 0xffff;
-       return SENSORS_LIMIT((1350000 + rpm) / (rpm * 2), 1, 0xfffe);
+       return clamp_val((1350000 + rpm) / (rpm * 2), 1, 0xfffe);
 }
 
 #define FAN_FROM_REG(val, div) ((val) == 0 ? -1 : (val) == 255 ? 0 : \
@@ -389,8 +465,8 @@ static inline u16 FAN16_TO_REG(long rpm)
 #define FAN16_FROM_REG(val) ((val) == 0 ? -1 : (val) == 0xffff ? 0 : \
                             1350000 / ((val) * 2))
 
-#define TEMP_TO_REG(val) (SENSORS_LIMIT(((val) < 0 ? (((val) - 500) / 1000) : \
-                                       ((val) + 500) / 1000), -128, 127))
+#define TEMP_TO_REG(val) (clamp_val(((val) < 0 ? (((val) - 500) / 1000) : \
+                                   ((val) + 500) / 1000), -128, 127))
 #define TEMP_FROM_REG(val) ((val) * 1000)
 
 static u8 pwm_to_reg(const struct it87_data *data, long val)
@@ -431,7 +507,7 @@ static const unsigned int pwm_freq[8] = {
 };
 
 static int it87_probe(struct platform_device *pdev);
-static int __devexit it87_remove(struct platform_device *pdev);
+static int it87_remove(struct platform_device *pdev);
 
 static int it87_read_value(struct it87_data *data, u8 reg);
 static void it87_write_value(struct it87_data *data, u8 reg, u8 value);
@@ -442,11 +518,10 @@ static void it87_init_device(struct platform_device *pdev);
 
 static struct platform_driver it87_driver = {
        .driver = {
-               .owner  = THIS_MODULE,
                .name   = DRVNAME,
        },
        .probe  = it87_probe,
-       .remove = __devexit_p(it87_remove),
+       .remove = it87_remove,
 };
 
 static ssize_t show_in(struct device *dev, struct device_attribute *attr,
@@ -483,41 +558,56 @@ static ssize_t set_in(struct device *dev, struct device_attribute *attr,
        return count;
 }
 
-#define S_IRUGOWU      (S_IRUGO | S_IWUSR)
-
 static SENSOR_DEVICE_ATTR_2(in0_input, S_IRUGO, show_in, NULL, 0, 0);
-static SENSOR_DEVICE_ATTR_2(in0_min, S_IRUGOWU, show_in, set_in, 0, 1);
-static SENSOR_DEVICE_ATTR_2(in0_max, S_IRUGOWU, show_in, set_in, 0, 2);
+static SENSOR_DEVICE_ATTR_2(in0_min, S_IRUGO | S_IWUSR, show_in, set_in,
+                           0, 1);
+static SENSOR_DEVICE_ATTR_2(in0_max, S_IRUGO | S_IWUSR, show_in, set_in,
+                           0, 2);
 
 static SENSOR_DEVICE_ATTR_2(in1_input, S_IRUGO, show_in, NULL, 1, 0);
-static SENSOR_DEVICE_ATTR_2(in1_min, S_IRUGOWU, show_in, set_in, 1, 1);
-static SENSOR_DEVICE_ATTR_2(in1_max, S_IRUGOWU, show_in, set_in, 1, 2);
+static SENSOR_DEVICE_ATTR_2(in1_min, S_IRUGO | S_IWUSR, show_in, set_in,
+                           1, 1);
+static SENSOR_DEVICE_ATTR_2(in1_max, S_IRUGO | S_IWUSR, show_in, set_in,
+                           1, 2);
 
 static SENSOR_DEVICE_ATTR_2(in2_input, S_IRUGO, show_in, NULL, 2, 0);
-static SENSOR_DEVICE_ATTR_2(in2_min, S_IRUGOWU, show_in, set_in, 2, 1);
-static SENSOR_DEVICE_ATTR_2(in2_max, S_IRUGOWU, show_in, set_in, 2, 2);
+static SENSOR_DEVICE_ATTR_2(in2_min, S_IRUGO | S_IWUSR, show_in, set_in,
+                           2, 1);
+static SENSOR_DEVICE_ATTR_2(in2_max, S_IRUGO | S_IWUSR, show_in, set_in,
+                           2, 2);
 
 static SENSOR_DEVICE_ATTR_2(in3_input, S_IRUGO, show_in, NULL, 3, 0);
-static SENSOR_DEVICE_ATTR_2(in3_min, S_IRUGOWU, show_in, set_in, 3, 1);
-static SENSOR_DEVICE_ATTR_2(in3_max, S_IRUGOWU, show_in, set_in, 3, 2);
+static SENSOR_DEVICE_ATTR_2(in3_min, S_IRUGO | S_IWUSR, show_in, set_in,
+                           3, 1);
+static SENSOR_DEVICE_ATTR_2(in3_max, S_IRUGO | S_IWUSR, show_in, set_in,
+                           3, 2);
 
 static SENSOR_DEVICE_ATTR_2(in4_input, S_IRUGO, show_in, NULL, 4, 0);
-static SENSOR_DEVICE_ATTR_2(in4_min, S_IRUGOWU, show_in, set_in, 4, 1);
-static SENSOR_DEVICE_ATTR_2(in4_max, S_IRUGOWU, show_in, set_in, 4, 2);
+static SENSOR_DEVICE_ATTR_2(in4_min, S_IRUGO | S_IWUSR, show_in, set_in,
+                           4, 1);
+static SENSOR_DEVICE_ATTR_2(in4_max, S_IRUGO | S_IWUSR, show_in, set_in,
+                           4, 2);
 
 static SENSOR_DEVICE_ATTR_2(in5_input, S_IRUGO, show_in, NULL, 5, 0);
-static SENSOR_DEVICE_ATTR_2(in5_min, S_IRUGOWU, show_in, set_in, 5, 1);
-static SENSOR_DEVICE_ATTR_2(in5_max, S_IRUGOWU, show_in, set_in, 5, 2);
+static SENSOR_DEVICE_ATTR_2(in5_min, S_IRUGO | S_IWUSR, show_in, set_in,
+                           5, 1);
+static SENSOR_DEVICE_ATTR_2(in5_max, S_IRUGO | S_IWUSR, show_in, set_in,
+                           5, 2);
 
 static SENSOR_DEVICE_ATTR_2(in6_input, S_IRUGO, show_in, NULL, 6, 0);
-static SENSOR_DEVICE_ATTR_2(in6_min, S_IRUGOWU, show_in, set_in, 6, 1);
-static SENSOR_DEVICE_ATTR_2(in6_max, S_IRUGOWU, show_in, set_in, 6, 2);
+static SENSOR_DEVICE_ATTR_2(in6_min, S_IRUGO | S_IWUSR, show_in, set_in,
+                           6, 1);
+static SENSOR_DEVICE_ATTR_2(in6_max, S_IRUGO | S_IWUSR, show_in, set_in,
+                           6, 2);
 
 static SENSOR_DEVICE_ATTR_2(in7_input, S_IRUGO, show_in, NULL, 7, 0);
-static SENSOR_DEVICE_ATTR_2(in7_min, S_IRUGOWU, show_in, set_in, 7, 1);
-static SENSOR_DEVICE_ATTR_2(in7_max, S_IRUGOWU, show_in, set_in, 7, 2);
+static SENSOR_DEVICE_ATTR_2(in7_min, S_IRUGO | S_IWUSR, show_in, set_in,
+                           7, 1);
+static SENSOR_DEVICE_ATTR_2(in7_max, S_IRUGO | S_IWUSR, show_in, set_in,
+                           7, 2);
 
 static SENSOR_DEVICE_ATTR_2(in8_input, S_IRUGO, show_in, NULL, 8, 0);
+static SENSOR_DEVICE_ATTR_2(in9_input, S_IRUGO, show_in, NULL, 9, 0);
 
 /* 3 temperatures */
 static ssize_t show_temp(struct device *dev, struct device_attribute *attr,
@@ -539,43 +629,72 @@ static ssize_t set_temp(struct device *dev, struct device_attribute *attr,
        int index = sattr->index;
        struct it87_data *data = dev_get_drvdata(dev);
        long val;
+       u8 reg, regval;
 
        if (kstrtol(buf, 10, &val) < 0)
                return -EINVAL;
 
        mutex_lock(&data->update_lock);
+
+       switch (index) {
+       default:
+       case 1:
+               reg = IT87_REG_TEMP_LOW(nr);
+               break;
+       case 2:
+               reg = IT87_REG_TEMP_HIGH(nr);
+               break;
+       case 3:
+               regval = it87_read_value(data, IT87_REG_BEEP_ENABLE);
+               if (!(regval & 0x80)) {
+                       regval |= 0x80;
+                       it87_write_value(data, IT87_REG_BEEP_ENABLE, regval);
+               }
+               data->valid = 0;
+               reg = IT87_REG_TEMP_OFFSET[nr];
+               break;
+       }
+
        data->temp[nr][index] = TEMP_TO_REG(val);
-       it87_write_value(data,
-                        index == 1 ? IT87_REG_TEMP_LOW(nr)
-                                   : index == 2 ? IT87_REG_TEMP_HIGH(nr)
-                                                : IT87_REG_TEMP_OFFSET(nr),
-                        data->temp[nr][index]);
-       data->valid = 0;
+       it87_write_value(data, reg, data->temp[nr][index]);
        mutex_unlock(&data->update_lock);
        return count;
 }
 
 static SENSOR_DEVICE_ATTR_2(temp1_input, S_IRUGO, show_temp, NULL, 0, 0);
-static SENSOR_DEVICE_ATTR_2(temp1_min, S_IRUGOWU, show_temp, set_temp, 0, 1);
-static SENSOR_DEVICE_ATTR_2(temp1_max, S_IRUGOWU, show_temp, set_temp, 0, 2);
-static SENSOR_DEVICE_ATTR_2(temp1_offset, S_IRUGOWU, show_temp, set_temp, 0, 3);
+static SENSOR_DEVICE_ATTR_2(temp1_min, S_IRUGO | S_IWUSR, show_temp, set_temp,
+                           0, 1);
+static SENSOR_DEVICE_ATTR_2(temp1_max, S_IRUGO | S_IWUSR, show_temp, set_temp,
+                           0, 2);
+static SENSOR_DEVICE_ATTR_2(temp1_offset, S_IRUGO | S_IWUSR, show_temp,
+                           set_temp, 0, 3);
 static SENSOR_DEVICE_ATTR_2(temp2_input, S_IRUGO, show_temp, NULL, 1, 0);
-static SENSOR_DEVICE_ATTR_2(temp2_min, S_IRUGOWU, show_temp, set_temp, 1, 1);
-static SENSOR_DEVICE_ATTR_2(temp2_max, S_IRUGOWU, show_temp, set_temp, 1, 2);
-static SENSOR_DEVICE_ATTR_2(temp2_offset, S_IRUGOWU, show_temp, set_temp, 1, 3);
+static SENSOR_DEVICE_ATTR_2(temp2_min, S_IRUGO | S_IWUSR, show_temp, set_temp,
+                           1, 1);
+static SENSOR_DEVICE_ATTR_2(temp2_max, S_IRUGO | S_IWUSR, show_temp, set_temp,
+                           1, 2);
+static SENSOR_DEVICE_ATTR_2(temp2_offset, S_IRUGO | S_IWUSR, show_temp,
+                           set_temp, 1, 3);
 static SENSOR_DEVICE_ATTR_2(temp3_input, S_IRUGO, show_temp, NULL, 2, 0);
-static SENSOR_DEVICE_ATTR_2(temp3_min, S_IRUGOWU, show_temp, set_temp, 2, 1);
-static SENSOR_DEVICE_ATTR_2(temp3_max, S_IRUGOWU, show_temp, set_temp, 2, 2);
-static SENSOR_DEVICE_ATTR_2(temp3_offset, S_IRUGOWU, show_temp, set_temp, 2, 3);
-
-static ssize_t show_type(struct device *dev, struct device_attribute *attr,
-                        char *buf)
+static SENSOR_DEVICE_ATTR_2(temp3_min, S_IRUGO | S_IWUSR, show_temp, set_temp,
+                           2, 1);
+static SENSOR_DEVICE_ATTR_2(temp3_max, S_IRUGO | S_IWUSR, show_temp, set_temp,
+                           2, 2);
+static SENSOR_DEVICE_ATTR_2(temp3_offset, S_IRUGO | S_IWUSR, show_temp,
+                           set_temp, 2, 3);
+
+static ssize_t show_temp_type(struct device *dev, struct device_attribute *attr,
+                             char *buf)
 {
        struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr);
        int nr = sensor_attr->index;
        struct it87_data *data = it87_update_device(dev);
        u8 reg = data->sensor;      /* In case value is updated while used */
+       u8 extra = data->extra;
 
+       if ((has_temp_peci(data, nr) && (reg >> 6 == nr + 1))
+           || (has_temp_old_peci(data, nr) && (extra & 0x80)))
+               return sprintf(buf, "6\n");  /* Intel PECI */
        if (reg & (1 << nr))
                return sprintf(buf, "3\n");  /* thermal diode */
        if (reg & (8 << nr))
@@ -583,15 +702,15 @@ static ssize_t show_type(struct device *dev, struct device_attribute *attr,
        return sprintf(buf, "0\n");      /* disabled */
 }
 
-static ssize_t set_type(struct device *dev, struct device_attribute *attr,
-                       const char *buf, size_t count)
+static ssize_t set_temp_type(struct device *dev, struct device_attribute *attr,
+                            const char *buf, size_t count)
 {
        struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr);
        int nr = sensor_attr->index;
 
        struct it87_data *data = dev_get_drvdata(dev);
        long val;
-       u8 reg;
+       u8 reg, extra;
 
        if (kstrtol(buf, 10, &val) < 0)
                return -EINVAL;
@@ -599,30 +718,45 @@ static ssize_t set_type(struct device *dev, struct device_attribute *attr,
        reg = it87_read_value(data, IT87_REG_TEMP_ENABLE);
        reg &= ~(1 << nr);
        reg &= ~(8 << nr);
+       if (has_temp_peci(data, nr) && (reg >> 6 == nr + 1 || val == 6))
+               reg &= 0x3f;
+       extra = it87_read_value(data, IT87_REG_TEMP_EXTRA);
+       if (has_temp_old_peci(data, nr) && ((extra & 0x80) || val == 6))
+               extra &= 0x7f;
        if (val == 2) { /* backwards compatibility */
                dev_warn(dev,
                         "Sensor type 2 is deprecated, please use 4 instead\n");
                val = 4;
        }
-       /* 3 = thermal diode; 4 = thermistor; 0 = disabled */
+       /* 3 = thermal diode; 4 = thermistor; 6 = Intel PECI; 0 = disabled */
        if (val == 3)
                reg |= 1 << nr;
        else if (val == 4)
                reg |= 8 << nr;
+       else if (has_temp_peci(data, nr) && val == 6)
+               reg |= (nr + 1) << 6;
+       else if (has_temp_old_peci(data, nr) && val == 6)
+               extra |= 0x80;
        else if (val != 0)
                return -EINVAL;
 
        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);
        return count;
 }
 
-static SENSOR_DEVICE_ATTR(temp1_type, S_IRUGOWU, show_type, set_type, 0);
-static SENSOR_DEVICE_ATTR(temp2_type, S_IRUGOWU, show_type, set_type, 1);
-static SENSOR_DEVICE_ATTR(temp3_type, S_IRUGOWU, show_type, set_type, 2);
+static SENSOR_DEVICE_ATTR(temp1_type, S_IRUGO | S_IWUSR, show_temp_type,
+                         set_temp_type, 0);
+static SENSOR_DEVICE_ATTR(temp2_type, S_IRUGO | S_IWUSR, show_temp_type,
+                         set_temp_type, 1);
+static SENSOR_DEVICE_ATTR(temp3_type, S_IRUGO | S_IWUSR, show_temp_type,
+                         set_temp_type, 2);
 
 /* 3 Fans */
 
@@ -630,7 +764,7 @@ static int pwm_mode(const struct it87_data *data, int nr)
 {
        int ctrl = data->fan_main_ctrl & (1 << nr);
 
-       if (ctrl == 0)                                  /* Full speed */
+       if (ctrl == 0 && data->type != it8603)          /* Full speed */
                return 0;
        if (data->pwm_ctrl[nr] & 0x80)                  /* Automatic mode */
                return 2;
@@ -707,7 +841,13 @@ static ssize_t set_fan(struct device *dev, struct device_attribute *attr,
 
        mutex_lock(&data->update_lock);
 
-       if (!has_16bit_fans(data)) {
+       if (has_16bit_fans(data)) {
+               data->fan[nr][index] = FAN16_TO_REG(val);
+               it87_write_value(data, IT87_REG_FAN_MIN[nr],
+                                data->fan[nr][index] & 0xff);
+               it87_write_value(data, IT87_REG_FANX_MIN[nr],
+                                data->fan[nr][index] >> 8);
+       } else {
                reg = it87_read_value(data, IT87_REG_FAN_DIV);
                switch (nr) {
                case 0:
@@ -724,12 +864,6 @@ static ssize_t set_fan(struct device *dev, struct device_attribute *attr,
                  FAN_TO_REG(val, DIV_FROM_REG(data->fan_div[nr]));
                it87_write_value(data, IT87_REG_FAN_MIN[nr],
                                 data->fan[nr][index]);
-       } else {
-               data->fan[nr][index] = FAN16_TO_REG(val);
-               it87_write_value(data, IT87_REG_FAN_MIN[nr],
-                                data->fan[nr][index] & 0xff);
-               it87_write_value(data, IT87_REG_FANX_MIN[nr],
-                                data->fan[nr][index] >> 8);
        }
 
        mutex_unlock(&data->update_lock);
@@ -825,6 +959,10 @@ static ssize_t set_pwm_enable(struct device *dev,
                        return -EINVAL;
        }
 
+       /* IT8603E does not have on/off mode */
+       if (val == 0 && data->type == it8603)
+               return -EINVAL;
+
        mutex_lock(&data->update_lock);
 
        if (val == 0) {
@@ -844,10 +982,13 @@ static ssize_t set_pwm_enable(struct device *dev,
                else                                    /* Automatic mode */
                        data->pwm_ctrl[nr] = 0x80 | data->pwm_temp_map[nr];
                it87_write_value(data, IT87_REG_PWM(nr), data->pwm_ctrl[nr]);
-               /* set SmartGuardian mode */
-               data->fan_main_ctrl |= (1 << nr);
-               it87_write_value(data, IT87_REG_FAN_MAIN_CTRL,
-                                data->fan_main_ctrl);
+
+               if (data->type != it8603) {
+                       /* set SmartGuardian mode */
+                       data->fan_main_ctrl |= (1 << nr);
+                       it87_write_value(data, IT87_REG_FAN_MAIN_CTRL,
+                                        data->fan_main_ctrl);
+               }
        }
 
        mutex_unlock(&data->update_lock);
@@ -1050,96 +1191,104 @@ static ssize_t set_auto_temp(struct device *dev,
 }
 
 static SENSOR_DEVICE_ATTR_2(fan1_input, S_IRUGO, show_fan, NULL, 0, 0);
-static SENSOR_DEVICE_ATTR_2(fan1_min, S_IRUGOWU, show_fan, set_fan, 0, 1);
-static SENSOR_DEVICE_ATTR(fan1_div, S_IRUGOWU, show_fan_div, set_fan_div, 0);
+static SENSOR_DEVICE_ATTR_2(fan1_min, S_IRUGO | S_IWUSR, show_fan, set_fan,
+                           0, 1);
+static SENSOR_DEVICE_ATTR(fan1_div, S_IRUGO | S_IWUSR, show_fan_div,
+                         set_fan_div, 0);
 
 static SENSOR_DEVICE_ATTR_2(fan2_input, S_IRUGO, show_fan, NULL, 1, 0);
-static SENSOR_DEVICE_ATTR_2(fan2_min, S_IRUGOWU, show_fan, set_fan, 1, 1);
-static SENSOR_DEVICE_ATTR(fan2_div, S_IRUGOWU, show_fan_div, set_fan_div, 1);
+static SENSOR_DEVICE_ATTR_2(fan2_min, S_IRUGO | S_IWUSR, show_fan, set_fan,
+                           1, 1);
+static SENSOR_DEVICE_ATTR(fan2_div, S_IRUGO | S_IWUSR, show_fan_div,
+                         set_fan_div, 1);
 
 static SENSOR_DEVICE_ATTR_2(fan3_input, S_IRUGO, show_fan, NULL, 2, 0);
-static SENSOR_DEVICE_ATTR_2(fan3_min, S_IRUGOWU, show_fan, set_fan, 2, 1);
-static SENSOR_DEVICE_ATTR(fan3_div, S_IRUGOWU, show_fan_div, set_fan_div, 2);
+static SENSOR_DEVICE_ATTR_2(fan3_min, S_IRUGO | S_IWUSR, show_fan, set_fan,
+                           2, 1);
+static SENSOR_DEVICE_ATTR(fan3_div, S_IRUGO | S_IWUSR, show_fan_div,
+                         set_fan_div, 2);
 
 static SENSOR_DEVICE_ATTR_2(fan4_input, S_IRUGO, show_fan, NULL, 3, 0);
-static SENSOR_DEVICE_ATTR_2(fan4_min, S_IRUGOWU, show_fan, set_fan, 3, 1);
+static SENSOR_DEVICE_ATTR_2(fan4_min, S_IRUGO | S_IWUSR, show_fan, set_fan,
+                           3, 1);
 
 static SENSOR_DEVICE_ATTR_2(fan5_input, S_IRUGO, show_fan, NULL, 4, 0);
-static SENSOR_DEVICE_ATTR_2(fan5_min, S_IRUGOWU, show_fan, set_fan, 4, 1);
+static SENSOR_DEVICE_ATTR_2(fan5_min, S_IRUGO | S_IWUSR, show_fan, set_fan,
+                           4, 1);
 
-static SENSOR_DEVICE_ATTR(pwm1_enable, S_IRUGOWU,
+static SENSOR_DEVICE_ATTR(pwm1_enable, S_IRUGO | S_IWUSR,
                          show_pwm_enable, set_pwm_enable, 0);
-static SENSOR_DEVICE_ATTR(pwm1, S_IRUGOWU, show_pwm, set_pwm, 0);
-static DEVICE_ATTR(pwm1_freq, S_IRUGOWU, show_pwm_freq, set_pwm_freq);
-static SENSOR_DEVICE_ATTR(pwm1_auto_channels_temp, S_IRUGOWU,
+static SENSOR_DEVICE_ATTR(pwm1, S_IRUGO | S_IWUSR, show_pwm, set_pwm, 0);
+static DEVICE_ATTR(pwm1_freq, S_IRUGO | S_IWUSR, show_pwm_freq, set_pwm_freq);
+static SENSOR_DEVICE_ATTR(pwm1_auto_channels_temp, S_IRUGO | S_IWUSR,
                          show_pwm_temp_map, set_pwm_temp_map, 0);
-static SENSOR_DEVICE_ATTR_2(pwm1_auto_point1_pwm, S_IRUGOWU,
+static SENSOR_DEVICE_ATTR_2(pwm1_auto_point1_pwm, S_IRUGO | S_IWUSR,
                            show_auto_pwm, set_auto_pwm, 0, 0);
-static SENSOR_DEVICE_ATTR_2(pwm1_auto_point2_pwm, S_IRUGOWU,
+static SENSOR_DEVICE_ATTR_2(pwm1_auto_point2_pwm, S_IRUGO | S_IWUSR,
                            show_auto_pwm, set_auto_pwm, 0, 1);
-static SENSOR_DEVICE_ATTR_2(pwm1_auto_point3_pwm, S_IRUGOWU,
+static SENSOR_DEVICE_ATTR_2(pwm1_auto_point3_pwm, S_IRUGO | S_IWUSR,
                            show_auto_pwm, set_auto_pwm, 0, 2);
 static SENSOR_DEVICE_ATTR_2(pwm1_auto_point4_pwm, S_IRUGO,
                            show_auto_pwm, NULL, 0, 3);
-static SENSOR_DEVICE_ATTR_2(pwm1_auto_point1_temp, S_IRUGOWU,
+static SENSOR_DEVICE_ATTR_2(pwm1_auto_point1_temp, S_IRUGO | S_IWUSR,
                            show_auto_temp, set_auto_temp, 0, 1);
-static SENSOR_DEVICE_ATTR_2(pwm1_auto_point1_temp_hyst, S_IRUGOWU,
+static SENSOR_DEVICE_ATTR_2(pwm1_auto_point1_temp_hyst, S_IRUGO | S_IWUSR,
                            show_auto_temp, set_auto_temp, 0, 0);
-static SENSOR_DEVICE_ATTR_2(pwm1_auto_point2_temp, S_IRUGOWU,
+static SENSOR_DEVICE_ATTR_2(pwm1_auto_point2_temp, S_IRUGO | S_IWUSR,
                            show_auto_temp, set_auto_temp, 0, 2);
-static SENSOR_DEVICE_ATTR_2(pwm1_auto_point3_temp, S_IRUGOWU,
+static SENSOR_DEVICE_ATTR_2(pwm1_auto_point3_temp, S_IRUGO | S_IWUSR,
                            show_auto_temp, set_auto_temp, 0, 3);
-static SENSOR_DEVICE_ATTR_2(pwm1_auto_point4_temp, S_IRUGOWU,
+static SENSOR_DEVICE_ATTR_2(pwm1_auto_point4_temp, S_IRUGO | S_IWUSR,
                            show_auto_temp, set_auto_temp, 0, 4);
 
-static SENSOR_DEVICE_ATTR(pwm2_enable, S_IRUGOWU,
+static SENSOR_DEVICE_ATTR(pwm2_enable, S_IRUGO | S_IWUSR,
                          show_pwm_enable, set_pwm_enable, 1);
-static SENSOR_DEVICE_ATTR(pwm2, S_IRUGOWU, show_pwm, set_pwm, 1);
+static SENSOR_DEVICE_ATTR(pwm2, S_IRUGO | S_IWUSR, show_pwm, set_pwm, 1);
 static DEVICE_ATTR(pwm2_freq, S_IRUGO, show_pwm_freq, NULL);
-static SENSOR_DEVICE_ATTR(pwm2_auto_channels_temp, S_IRUGOWU,
+static SENSOR_DEVICE_ATTR(pwm2_auto_channels_temp, S_IRUGO | S_IWUSR,
                          show_pwm_temp_map, set_pwm_temp_map, 1);
-static SENSOR_DEVICE_ATTR_2(pwm2_auto_point1_pwm, S_IRUGOWU,
+static SENSOR_DEVICE_ATTR_2(pwm2_auto_point1_pwm, S_IRUGO | S_IWUSR,
                            show_auto_pwm, set_auto_pwm, 1, 0);
-static SENSOR_DEVICE_ATTR_2(pwm2_auto_point2_pwm, S_IRUGOWU,
+static SENSOR_DEVICE_ATTR_2(pwm2_auto_point2_pwm, S_IRUGO | S_IWUSR,
                            show_auto_pwm, set_auto_pwm, 1, 1);
-static SENSOR_DEVICE_ATTR_2(pwm2_auto_point3_pwm, S_IRUGOWU,
+static SENSOR_DEVICE_ATTR_2(pwm2_auto_point3_pwm, S_IRUGO | S_IWUSR,
                            show_auto_pwm, set_auto_pwm, 1, 2);
 static SENSOR_DEVICE_ATTR_2(pwm2_auto_point4_pwm, S_IRUGO,
                            show_auto_pwm, NULL, 1, 3);
-static SENSOR_DEVICE_ATTR_2(pwm2_auto_point1_temp, S_IRUGOWU,
+static SENSOR_DEVICE_ATTR_2(pwm2_auto_point1_temp, S_IRUGO | S_IWUSR,
                            show_auto_temp, set_auto_temp, 1, 1);
-static SENSOR_DEVICE_ATTR_2(pwm2_auto_point1_temp_hyst, S_IRUGOWU,
+static SENSOR_DEVICE_ATTR_2(pwm2_auto_point1_temp_hyst, S_IRUGO | S_IWUSR,
                            show_auto_temp, set_auto_temp, 1, 0);
-static SENSOR_DEVICE_ATTR_2(pwm2_auto_point2_temp, S_IRUGOWU,
+static SENSOR_DEVICE_ATTR_2(pwm2_auto_point2_temp, S_IRUGO | S_IWUSR,
                            show_auto_temp, set_auto_temp, 1, 2);
-static SENSOR_DEVICE_ATTR_2(pwm2_auto_point3_temp, S_IRUGOWU,
+static SENSOR_DEVICE_ATTR_2(pwm2_auto_point3_temp, S_IRUGO | S_IWUSR,
                            show_auto_temp, set_auto_temp, 1, 3);
-static SENSOR_DEVICE_ATTR_2(pwm2_auto_point4_temp, S_IRUGOWU,
+static SENSOR_DEVICE_ATTR_2(pwm2_auto_point4_temp, S_IRUGO | S_IWUSR,
                            show_auto_temp, set_auto_temp, 1, 4);
 
-static SENSOR_DEVICE_ATTR(pwm3_enable, S_IRUGOWU,
+static SENSOR_DEVICE_ATTR(pwm3_enable, S_IRUGO | S_IWUSR,
                          show_pwm_enable, set_pwm_enable, 2);
-static SENSOR_DEVICE_ATTR(pwm3, S_IRUGOWU, show_pwm, set_pwm, 2);
+static SENSOR_DEVICE_ATTR(pwm3, S_IRUGO | S_IWUSR, show_pwm, set_pwm, 2);
 static DEVICE_ATTR(pwm3_freq, S_IRUGO, show_pwm_freq, NULL);
-static SENSOR_DEVICE_ATTR(pwm3_auto_channels_temp, S_IRUGOWU,
+static SENSOR_DEVICE_ATTR(pwm3_auto_channels_temp, S_IRUGO | S_IWUSR,
                          show_pwm_temp_map, set_pwm_temp_map, 2);
-static SENSOR_DEVICE_ATTR_2(pwm3_auto_point1_pwm, S_IRUGOWU,
+static SENSOR_DEVICE_ATTR_2(pwm3_auto_point1_pwm, S_IRUGO | S_IWUSR,
                            show_auto_pwm, set_auto_pwm, 2, 0);
-static SENSOR_DEVICE_ATTR_2(pwm3_auto_point2_pwm, S_IRUGOWU,
+static SENSOR_DEVICE_ATTR_2(pwm3_auto_point2_pwm, S_IRUGO | S_IWUSR,
                            show_auto_pwm, set_auto_pwm, 2, 1);
-static SENSOR_DEVICE_ATTR_2(pwm3_auto_point3_pwm, S_IRUGOWU,
+static SENSOR_DEVICE_ATTR_2(pwm3_auto_point3_pwm, S_IRUGO | S_IWUSR,
                            show_auto_pwm, set_auto_pwm, 2, 2);
 static SENSOR_DEVICE_ATTR_2(pwm3_auto_point4_pwm, S_IRUGO,
                            show_auto_pwm, NULL, 2, 3);
-static SENSOR_DEVICE_ATTR_2(pwm3_auto_point1_temp, S_IRUGOWU,
+static SENSOR_DEVICE_ATTR_2(pwm3_auto_point1_temp, S_IRUGO | S_IWUSR,
                            show_auto_temp, set_auto_temp, 2, 1);
-static SENSOR_DEVICE_ATTR_2(pwm3_auto_point1_temp_hyst, S_IRUGOWU,
+static SENSOR_DEVICE_ATTR_2(pwm3_auto_point1_temp_hyst, S_IRUGO | S_IWUSR,
                            show_auto_temp, set_auto_temp, 2, 0);
-static SENSOR_DEVICE_ATTR_2(pwm3_auto_point2_temp, S_IRUGOWU,
+static SENSOR_DEVICE_ATTR_2(pwm3_auto_point2_temp, S_IRUGO | S_IWUSR,
                            show_auto_temp, set_auto_temp, 2, 2);
-static SENSOR_DEVICE_ATTR_2(pwm3_auto_point3_temp, S_IRUGOWU,
+static SENSOR_DEVICE_ATTR_2(pwm3_auto_point3_temp, S_IRUGO | S_IWUSR,
                            show_auto_temp, set_auto_temp, 2, 3);
-static SENSOR_DEVICE_ATTR_2(pwm3_auto_point4_temp, S_IRUGOWU,
+static SENSOR_DEVICE_ATTR_2(pwm3_auto_point4_temp, S_IRUGO | S_IWUSR,
                            show_auto_temp, set_auto_temp, 2, 4);
 
 /* Alarms */
@@ -1303,6 +1452,8 @@ static ssize_t show_label(struct device *dev, struct device_attribute *attr,
 static SENSOR_DEVICE_ATTR(in3_label, S_IRUGO, show_label, NULL, 0);
 static SENSOR_DEVICE_ATTR(in7_label, S_IRUGO, show_label, NULL, 1);
 static SENSOR_DEVICE_ATTR(in8_label, S_IRUGO, show_label, NULL, 2);
+/* special AVCC3 IT8603E in9 */
+static SENSOR_DEVICE_ATTR(in9_label, S_IRUGO, show_label, NULL, 0);
 
 static ssize_t show_name(struct device *dev, struct device_attribute
                         *devattr, char *buf)
@@ -1312,7 +1463,7 @@ static ssize_t show_name(struct device *dev, struct device_attribute
 }
 static DEVICE_ATTR(name, S_IRUGO, show_name, NULL);
 
-static struct attribute *it87_attributes_in[9][5] = {
+static struct attribute *it87_attributes_in[10][5] = {
 {
        &sensor_dev_attr_in0_input.dev_attr.attr,
        &sensor_dev_attr_in0_min.dev_attr.attr,
@@ -1364,9 +1515,12 @@ static struct attribute *it87_attributes_in[9][5] = {
 }, {
        &sensor_dev_attr_in8_input.dev_attr.attr,
        NULL
+}, {
+       &sensor_dev_attr_in9_input.dev_attr.attr,
+       NULL
 } };
 
-static const struct attribute_group it87_group_in[9] = {
+static const struct attribute_group it87_group_in[10] = {
        { .attrs = it87_attributes_in[0] },
        { .attrs = it87_attributes_in[1] },
        { .attrs = it87_attributes_in[2] },
@@ -1376,14 +1530,14 @@ static const struct attribute_group it87_group_in[9] = {
        { .attrs = it87_attributes_in[6] },
        { .attrs = it87_attributes_in[7] },
        { .attrs = it87_attributes_in[8] },
+       { .attrs = it87_attributes_in[9] },
 };
 
-static struct attribute *it87_attributes_temp[3][7] = {
+static struct attribute *it87_attributes_temp[3][6] = {
 {
        &sensor_dev_attr_temp1_input.dev_attr.attr,
        &sensor_dev_attr_temp1_max.dev_attr.attr,
        &sensor_dev_attr_temp1_min.dev_attr.attr,
-       &sensor_dev_attr_temp1_offset.dev_attr.attr,
        &sensor_dev_attr_temp1_type.dev_attr.attr,
        &sensor_dev_attr_temp1_alarm.dev_attr.attr,
        NULL
@@ -1391,7 +1545,6 @@ static struct attribute *it87_attributes_temp[3][7] = {
        &sensor_dev_attr_temp2_input.dev_attr.attr,
        &sensor_dev_attr_temp2_max.dev_attr.attr,
        &sensor_dev_attr_temp2_min.dev_attr.attr,
-       &sensor_dev_attr_temp2_offset.dev_attr.attr,
        &sensor_dev_attr_temp2_type.dev_attr.attr,
        &sensor_dev_attr_temp2_alarm.dev_attr.attr,
        NULL
@@ -1399,7 +1552,6 @@ static struct attribute *it87_attributes_temp[3][7] = {
        &sensor_dev_attr_temp3_input.dev_attr.attr,
        &sensor_dev_attr_temp3_max.dev_attr.attr,
        &sensor_dev_attr_temp3_min.dev_attr.attr,
-       &sensor_dev_attr_temp3_offset.dev_attr.attr,
        &sensor_dev_attr_temp3_type.dev_attr.attr,
        &sensor_dev_attr_temp3_alarm.dev_attr.attr,
        NULL
@@ -1411,6 +1563,12 @@ static const struct attribute_group it87_group_temp[3] = {
        { .attrs = it87_attributes_temp[2] },
 };
 
+static struct attribute *it87_attributes_temp_offset[] = {
+       &sensor_dev_attr_temp1_offset.dev_attr.attr,
+       &sensor_dev_attr_temp2_offset.dev_attr.attr,
+       &sensor_dev_attr_temp3_offset.dev_attr.attr,
+};
+
 static struct attribute *it87_attributes[] = {
        &dev_attr_alarms.attr,
        &sensor_dev_attr_intrusion0_alarm.dev_attr.attr,
@@ -1431,7 +1589,8 @@ static struct attribute *it87_attributes_in_beep[] = {
        &sensor_dev_attr_in5_beep.dev_attr.attr,
        &sensor_dev_attr_in6_beep.dev_attr.attr,
        &sensor_dev_attr_in7_beep.dev_attr.attr,
-       NULL
+       NULL,
+       NULL,
 };
 
 static struct attribute *it87_attributes_temp_beep[] = {
@@ -1570,6 +1729,7 @@ static struct attribute *it87_attributes_label[] = {
        &sensor_dev_attr_in3_label.dev_attr.attr,
        &sensor_dev_attr_in7_label.dev_attr.attr,
        &sensor_dev_attr_in8_label.dev_attr.attr,
+       &sensor_dev_attr_in9_label.dev_attr.attr,
        NULL
 };
 
@@ -1615,12 +1775,25 @@ static int __init it87_find(unsigned short *address,
        case IT8728F_DEVID:
                sio_data->type = it8728;
                break;
+       case IT8771E_DEVID:
+               sio_data->type = it8771;
+               break;
+       case IT8772E_DEVID:
+               sio_data->type = it8772;
+               break;
+       case IT8781F_DEVID:
+               sio_data->type = it8781;
+               break;
        case IT8782F_DEVID:
                sio_data->type = it8782;
                break;
        case IT8783E_DEVID:
                sio_data->type = it8783;
                break;
+       case IT8603E_DEVID:
+       case IT8623E_DEVID:
+               sio_data->type = it8603;
+               break;
        case 0xffff:    /* No device at all */
                goto exit;
        default:
@@ -1642,11 +1815,16 @@ static int __init it87_find(unsigned short *address,
 
        err = 0;
        sio_data->revision = superio_inb(DEVREV) & 0x0f;
-       pr_info("Found IT%04xF chip at 0x%x, revision %d\n",
-               chip_type, *address, sio_data->revision);
+       pr_info("Found IT%04x%c chip at 0x%x, revision %d\n", chip_type,
+               chip_type == 0x8771 || chip_type == 0x8772 ||
+               chip_type == 0x8603 ? 'E' : 'F', *address,
+               sio_data->revision);
 
        /* in8 (Vbat) is always internal */
        sio_data->internal = (1 << 2);
+       /* Only the IT8603E has in9 */
+       if (sio_data->type != it8603)
+               sio_data->skip_in |= (1 << 9);
 
        /* Read GPIO config and VID value from LDN 7 (GPIO) */
        if (sio_data->type == it87) {
@@ -1657,7 +1835,7 @@ static int __init it87_find(unsigned short *address,
                superio_select(5);
                sio_data->beep_pin = superio_inb(IT87_SIO_BEEP_PIN_REG) & 0x3f;
        } else if (sio_data->type == it8783) {
-               int reg25, reg27, reg2A, reg2C, regEF;
+               int reg25, reg27, reg2a, reg2c, regef;
 
                sio_data->skip_vid = 1; /* No VID */
 
@@ -1665,15 +1843,15 @@ static int __init it87_find(unsigned short *address,
 
                reg25 = superio_inb(IT87_SIO_GPIO1_REG);
                reg27 = superio_inb(IT87_SIO_GPIO3_REG);
-               reg2A = superio_inb(IT87_SIO_PINX1_REG);
-               reg2C = superio_inb(IT87_SIO_PINX2_REG);
-               regEF = superio_inb(IT87_SIO_SPI_REG);
+               reg2a = superio_inb(IT87_SIO_PINX1_REG);
+               reg2c = superio_inb(IT87_SIO_PINX2_REG);
+               regef = superio_inb(IT87_SIO_SPI_REG);
 
                /* Check if fan3 is there or not */
-               if ((reg27 & (1 << 0)) || !(reg2C & (1 << 2)))
+               if ((reg27 & (1 << 0)) || !(reg2c & (1 << 2)))
                        sio_data->skip_fan |= (1 << 2);
                if ((reg25 & (1 << 4))
-                   || (!(reg2A & (1 << 1)) && (regEF & (1 << 0))))
+                   || (!(reg2a & (1 << 1)) && (regef & (1 << 0))))
                        sio_data->skip_pwm |= (1 << 2);
 
                /* Check if fan2 is there or not */
@@ -1683,7 +1861,7 @@ static int __init it87_find(unsigned short *address,
                        sio_data->skip_pwm |= (1 << 1);
 
                /* VIN5 */
-               if ((reg27 & (1 << 0)) || (reg2C & (1 << 2)))
+               if ((reg27 & (1 << 0)) || (reg2c & (1 << 2)))
                        sio_data->skip_in |= (1 << 5); /* No VIN5 */
 
                /* VIN6 */
@@ -1708,22 +1886,53 @@ static int __init it87_find(unsigned short *address,
                         * not the case, and ask the user to report if the
                         * resulting voltage is sane.
                         */
-                       if (!(reg2C & (1 << 1))) {
-                               reg2C |= (1 << 1);
-                               superio_outb(IT87_SIO_PINX2_REG, reg2C);
+                       if (!(reg2c & (1 << 1))) {
+                               reg2c |= (1 << 1);
+                               superio_outb(IT87_SIO_PINX2_REG, reg2c);
                                pr_notice("Routing internal VCCH5V to in7.\n");
                        }
                        pr_notice("in7 routed to internal voltage divider, with external pin disabled.\n");
                        pr_notice("Please report if it displays a reasonable voltage.\n");
                }
 
-               if (reg2C & (1 << 0))
+               if (reg2c & (1 << 0))
                        sio_data->internal |= (1 << 0);
-               if (reg2C & (1 << 1))
+               if (reg2c & (1 << 1))
                        sio_data->internal |= (1 << 1);
 
                sio_data->beep_pin = superio_inb(IT87_SIO_BEEP_PIN_REG) & 0x3f;
+       } else if (sio_data->type == it8603) {
+               int reg27, reg29;
+
+               sio_data->skip_vid = 1; /* No VID */
+               superio_select(GPIO);
+
+               reg27 = superio_inb(IT87_SIO_GPIO3_REG);
+
+               /* Check if fan3 is there or not */
+               if (reg27 & (1 << 6))
+                       sio_data->skip_pwm |= (1 << 2);
+               if (reg27 & (1 << 7))
+                       sio_data->skip_fan |= (1 << 2);
 
+               /* Check if fan2 is there or not */
+               reg29 = superio_inb(IT87_SIO_GPIO5_REG);
+               if (reg29 & (1 << 1))
+                       sio_data->skip_pwm |= (1 << 1);
+               if (reg29 & (1 << 2))
+                       sio_data->skip_fan |= (1 << 1);
+
+               sio_data->skip_in |= (1 << 5); /* No VIN5 */
+               sio_data->skip_in |= (1 << 6); /* No VIN6 */
+
+               /* no fan4 */
+               sio_data->skip_pwm |= (1 << 3);
+               sio_data->skip_fan |= (1 << 3);
+
+               sio_data->internal |= (1 << 1); /* in7 is VSB */
+               sio_data->internal |= (1 << 3); /* in9 is AVCC */
+
+               sio_data->beep_pin = superio_inb(IT87_SIO_BEEP_PIN_REG) & 0x3f;
        } else {
                int reg;
                bool uart6;
@@ -1732,10 +1941,12 @@ static int __init it87_find(unsigned short *address,
 
                reg = superio_inb(IT87_SIO_GPIO3_REG);
                if (sio_data->type == it8721 || sio_data->type == it8728 ||
-                   sio_data->type == it8782) {
+                   sio_data->type == it8771 || sio_data->type == it8772 ||
+                   sio_data->type == it8781 || sio_data->type == it8782) {
                        /*
-                        * IT8721F/IT8758E, and IT8782F don't have VID pins
-                        * at all, not sure about the IT8728F.
+                        * IT8721F/IT8758E, IT8728F, IT8772F, IT8781F, and
+                        * IT8782F don't have VID pins at all, not sure about
+                        * the IT8771F.
                         */
                        sio_data->skip_vid = 1;
                } else {
@@ -1789,7 +2000,9 @@ static int __init it87_find(unsigned short *address,
                if (reg & (1 << 0))
                        sio_data->internal |= (1 << 0);
                if ((reg & (1 << 1)) || sio_data->type == it8721 ||
-                   sio_data->type == it8728)
+                   sio_data->type == it8728 ||
+                   sio_data->type == it8771 ||
+                   sio_data->type == it8772)
                        sio_data->internal |= (1 << 1);
 
                /*
@@ -1838,11 +2051,11 @@ exit:
 static void it87_remove_files(struct device *dev)
 {
        struct it87_data *data = platform_get_drvdata(pdev);
-       struct it87_sio_data *sio_data = dev->platform_data;
+       struct it87_sio_data *sio_data = dev_get_platdata(dev);
        int i;
 
        sysfs_remove_group(&dev->kobj, &it87_group);
-       for (i = 0; i < 9; i++) {
+       for (i = 0; i < 10; i++) {
                if (sio_data->skip_in & (1 << i))
                        continue;
                sysfs_remove_group(&dev->kobj, &it87_group_in[i]);
@@ -1854,6 +2067,9 @@ static void it87_remove_files(struct device *dev)
                if (!(data->has_temp & (1 << i)))
                        continue;
                sysfs_remove_group(&dev->kobj, &it87_group_temp[i]);
+               if (has_temp_offset(data))
+                       sysfs_remove_file(&dev->kobj,
+                                         it87_attributes_temp_offset[i]);
                if (sio_data->beep_pin)
                        sysfs_remove_file(&dev->kobj,
                                          it87_attributes_temp_beep[i]);
@@ -1882,12 +2098,12 @@ static void it87_remove_files(struct device *dev)
        sysfs_remove_group(&dev->kobj, &it87_group_label);
 }
 
-static int __devinit it87_probe(struct platform_device *pdev)
+static int it87_probe(struct platform_device *pdev)
 {
        struct it87_data *data;
        struct resource *res;
        struct device *dev = &pdev->dev;
-       struct it87_sio_data *sio_data = dev->platform_data;
+       struct it87_sio_data *sio_data = dev_get_platdata(dev);
        int err = 0, i;
        int enable_pwm_interface;
        int fan_beep_need_rw;
@@ -1907,7 +2123,9 @@ static int __devinit it87_probe(struct platform_device *pdev)
 
        data->addr = res->start;
        data->type = sio_data->type;
-       data->features = it87_devices[data->type].features;
+       data->features = it87_devices[sio_data->type].features;
+       data->peci_mask = it87_devices[sio_data->type].peci_mask;
+       data->old_peci_mask = it87_devices[sio_data->type].old_peci_mask;
        data->name = it87_devices[sio_data->type].name;
        /*
         * IT8705F Datasheet 0.4.1, 3h == Version G.
@@ -1918,13 +2136,13 @@ static int __devinit it87_probe(struct platform_device *pdev)
        case it87:
                if (sio_data->revision >= 0x03) {
                        data->features &= ~FEAT_OLD_AUTOPWM;
-                       data->features |= FEAT_16BIT_FAN;
+                       data->features |= FEAT_16BIT_FANS;
                }
                break;
        case it8712:
                if (sio_data->revision >= 0x08) {
                        data->features &= ~FEAT_OLD_AUTOPWM;
-                       data->features |= FEAT_16BIT_FAN;
+                       data->features |= FEAT_16BIT_FANS | FEAT_FIVE_FANS;
                }
                break;
        default:
@@ -1951,7 +2169,10 @@ static int __devinit it87_probe(struct platform_device *pdev)
                        data->in_scaled |= (1 << 7);    /* in7 is VSB */
                if (sio_data->internal & (1 << 2))
                        data->in_scaled |= (1 << 8);    /* in8 is Vbat */
-       } else if (sio_data->type == it8782 || sio_data->type == it8783) {
+               if (sio_data->internal & (1 << 3))
+                       data->in_scaled |= (1 << 9);    /* in9 is AVCC */
+       } else if (sio_data->type == it8781 || sio_data->type == it8782 ||
+                  sio_data->type == it8783) {
                if (sio_data->internal & (1 << 0))
                        data->in_scaled |= (1 << 3);    /* in3 is VCC5V */
                if (sio_data->internal & (1 << 1))
@@ -1973,7 +2194,7 @@ static int __devinit it87_probe(struct platform_device *pdev)
        if (err)
                return err;
 
-       for (i = 0; i < 9; i++) {
+       for (i = 0; i < 10; i++) {
                if (sio_data->skip_in & (1 << i))
                        continue;
                err = sysfs_create_group(&dev->kobj, &it87_group_in[i]);
@@ -1993,6 +2214,12 @@ static int __devinit it87_probe(struct platform_device *pdev)
                err = sysfs_create_group(&dev->kobj, &it87_group_temp[i]);
                if (err)
                        goto error;
+               if (has_temp_offset(data)) {
+                       err = sysfs_create_file(&dev->kobj,
+                                               it87_attributes_temp_offset[i]);
+                       if (err)
+                               goto error;
+               }
                if (sio_data->beep_pin) {
                        err = sysfs_create_file(&dev->kobj,
                                                it87_attributes_temp_beep[i]);
@@ -2067,7 +2294,7 @@ static int __devinit it87_probe(struct platform_device *pdev)
        }
 
        /* Export labels for internal sensors */
-       for (i = 0; i < 3; i++) {
+       for (i = 0; i < 4; i++) {
                if (!(sio_data->internal & (1 << i)))
                        continue;
                err = sysfs_create_file(&dev->kobj,
@@ -2089,7 +2316,7 @@ error:
        return err;
 }
 
-static int __devexit it87_remove(struct platform_device *pdev)
+static int it87_remove(struct platform_device *pdev)
 {
        struct it87_data *data = platform_get_drvdata(pdev);
 
@@ -2122,7 +2349,7 @@ static void it87_write_value(struct it87_data *data, u8 reg, u8 value)
 }
 
 /* Return 1 if and only if the PWM interface is safe to use */
-static int __devinit it87_check_pwm(struct device *dev)
+static int it87_check_pwm(struct device *dev)
 {
        struct it87_data *data = dev_get_drvdata(dev);
        /*
@@ -2179,9 +2406,9 @@ static int __devinit it87_check_pwm(struct device *dev)
 }
 
 /* Called when we have found a new IT87. */
-static void __devinit it87_init_device(struct platform_device *pdev)
+static void it87_init_device(struct platform_device *pdev)
 {
-       struct it87_sio_data *sio_data = pdev->dev.platform_data;
+       struct it87_sio_data *sio_data = dev_get_platdata(&pdev->dev);
        struct it87_data *data = platform_get_drvdata(pdev);
        int tmp, i;
        u8 mask;
@@ -2248,8 +2475,8 @@ static void __devinit it87_init_device(struct platform_device *pdev)
        }
        data->has_fan = (data->fan_main_ctrl >> 4) & 0x07;
 
-       /* Set tachometers to 16-bit mode if needed */
-       if (has_16bit_fans(data)) {
+       /* Set tachometers to 16-bit mode if needed. */
+       if (has_fan16_config(data)) {
                tmp = it87_read_value(data, IT87_REG_FAN_16BIT);
                if (~tmp & 0x07 & data->has_fan) {
                        dev_dbg(&pdev->dev,
@@ -2257,14 +2484,15 @@ static void __devinit it87_init_device(struct platform_device *pdev)
                        it87_write_value(data, IT87_REG_FAN_16BIT,
                                         tmp | 0x07);
                }
-               /* IT8705F, IT8782F, and IT8783E/F only support three fans. */
-               if (data->type != it87 && data->type != it8782 &&
-                   data->type != it8783) {
-                       if (tmp & (1 << 4))
-                               data->has_fan |= (1 << 3); /* fan4 enabled */
-                       if (tmp & (1 << 5))
-                               data->has_fan |= (1 << 4); /* fan5 enabled */
-               }
+       }
+
+       /* Check for additional fans */
+       if (has_five_fans(data)) {
+               tmp = it87_read_value(data, IT87_REG_FAN_16BIT);
+               if (tmp & (1 << 4))
+                       data->has_fan |= (1 << 3); /* fan4 enabled */
+               if (tmp & (1 << 5))
+                       data->has_fan |= (1 << 4); /* fan5 enabled */
        }
 
        /* Fan input pins may be used for alternative functions */
@@ -2329,6 +2557,8 @@ static struct it87_data *it87_update_device(struct device *dev)
                }
                /* in8 (battery) has no limit registers */
                data->in[8][0] = it87_read_value(data, IT87_REG_VIN(8));
+               if (data->type == it8603)
+                       data->in[9][0] = it87_read_value(data, 0x2f);
 
                for (i = 0; i < 5; i++) {
                        /* Skip disabled fans */
@@ -2356,8 +2586,10 @@ static struct it87_data *it87_update_device(struct device *dev)
                                it87_read_value(data, IT87_REG_TEMP_LOW(i));
                        data->temp[i][2] =
                                it87_read_value(data, IT87_REG_TEMP_HIGH(i));
-                       data->temp[i][3] =
-                               it87_read_value(data, IT87_REG_TEMP_OFFSET(i));
+                       if (has_temp_offset(data))
+                               data->temp[i][3] =
+                                 it87_read_value(data,
+                                                 IT87_REG_TEMP_OFFSET[i]);
                }
 
                /* Newer chips don't have clock dividers */
@@ -2381,6 +2613,7 @@ static struct it87_data *it87_update_device(struct device *dev)
                        it87_update_pwm_ctrl(data, i);
 
                data->sensor = it87_read_value(data, IT87_REG_TEMP_ENABLE);
+               data->extra = it87_read_value(data, IT87_REG_TEMP_EXTRA);
                /*
                 * The IT8705F does not have VID capability.
                 * The IT8718F and later don't use IT87_REG_VID for the
@@ -2482,7 +2715,7 @@ static void __exit sm_it87_exit(void)
 }
 
 
-MODULE_AUTHOR("Chris Gauthron, Jean Delvare <khali@linux-fr.org>");
+MODULE_AUTHOR("Chris Gauthron, Jean Delvare <jdelvare@suse.de>");
 MODULE_DESCRIPTION("IT8705F/IT871xF/IT872xF hardware monitoring driver");
 module_param(update_vbat, bool, 0);
 MODULE_PARM_DESC(update_vbat, "Update vbat if set else return powerup value");