hwmon: (dme1737) Add SCH5127 support
authorJuerg Haefliger <juergh@gmail.com>
Thu, 27 May 2010 17:59:01 +0000 (19:59 +0200)
committerJean Delvare <khali@linux-fr.org>
Thu, 27 May 2010 17:59:01 +0000 (19:59 +0200)
Add support for the hardware monitoring capabilities of the SCH5127
chip to the dme1737 driver.

Signed-off-by: Juerg Haefliger <juergh@gmail.com>
Signed-off-by: Jean Delvare <khali@linux-fr.org>
Tested-by: Jeff Rickman <jrickman@myamigos.us>
Documentation/hwmon/dme1737
drivers/hwmon/dme1737.c

index 001d2e7..fc5df76 100644 (file)
@@ -9,11 +9,15 @@ Supported chips:
   * SMSC SCH3112, SCH3114, SCH3116
     Prefix: 'sch311x'
     Addresses scanned: none, address read from Super-I/O config space
   * SMSC SCH3112, SCH3114, SCH3116
     Prefix: 'sch311x'
     Addresses scanned: none, address read from Super-I/O config space
-    Datasheet: http://www.nuhorizons.com/FeaturedProducts/Volume1/SMSC/311x.pdf
+    Datasheet: Available on the Internet
   * SMSC SCH5027
     Prefix: 'sch5027'
     Addresses scanned: I2C 0x2c, 0x2d, 0x2e
     Datasheet: Provided by SMSC upon request and under NDA
   * SMSC SCH5027
     Prefix: 'sch5027'
     Addresses scanned: I2C 0x2c, 0x2d, 0x2e
     Datasheet: Provided by SMSC upon request and under NDA
+  * SMSC SCH5127
+    Prefix: 'sch5127'
+    Addresses scanned: none, address read from Super-I/O config space
+    Datasheet: Provided by SMSC upon request and under NDA
 
 Authors:
     Juerg Haefliger <juergh@gmail.com>
 
 Authors:
     Juerg Haefliger <juergh@gmail.com>
@@ -36,8 +40,8 @@ Description
 -----------
 
 This driver implements support for the hardware monitoring capabilities of the
 -----------
 
 This driver implements support for the hardware monitoring capabilities of the
-SMSC DME1737 and Asus A8000 (which are the same), SMSC SCH5027, and SMSC
-SCH311x Super-I/O chips. These chips feature monitoring of 3 temp sensors
+SMSC DME1737 and Asus A8000 (which are the same), SMSC SCH5027, SCH311x,
+and SCH5127 Super-I/O chips. These chips feature monitoring of 3 temp sensors
 temp[1-3] (2 remote diodes and 1 internal), 7 voltages in[0-6] (6 external and
 1 internal) and up to 6 fan speeds fan[1-6]. Additionally, the chips implement
 up to 5 PWM outputs pwm[1-3,5-6] for controlling fan speeds both manually and
 temp[1-3] (2 remote diodes and 1 internal), 7 voltages in[0-6] (6 external and
 1 internal) and up to 6 fan speeds fan[1-6]. Additionally, the chips implement
 up to 5 PWM outputs pwm[1-3,5-6] for controlling fan speeds both manually and
@@ -48,14 +52,14 @@ Fan[3-6] and pwm[3,5-6] are optional features and their availability depends on
 the configuration of the chip. The driver will detect which features are
 present during initialization and create the sysfs attributes accordingly.
 
 the configuration of the chip. The driver will detect which features are
 present during initialization and create the sysfs attributes accordingly.
 
-For the SCH311x, fan[1-3] and pwm[1-3] are always present and fan[4-6] and
-pwm[5-6] don't exist.
+For the SCH311x and SCH5127, fan[1-3] and pwm[1-3] are always present and
+fan[4-6] and pwm[5-6] don't exist.
 
 The hardware monitoring features of the DME1737, A8000, and SCH5027 are only
 
 The hardware monitoring features of the DME1737, A8000, and SCH5027 are only
-accessible via SMBus, while the SCH311x only provides access via the ISA bus.
-The driver will therefore register itself as an I2C client driver if it detects
-a DME1737, A8000, or SCH5027 and as a platform driver if it detects a SCH311x
-chip.
+accessible via SMBus, while the SCH311x and SCH5127 only provide access via
+the ISA bus. The driver will therefore register itself as an I2C client driver
+if it detects a DME1737, A8000, or SCH5027 and as a platform driver if it
+detects a SCH311x or SCH5127 chip.
 
 
 Voltage Monitoring
 
 
 Voltage Monitoring
@@ -76,7 +80,7 @@ DME1737, A8000:
        in6: Vbat       (+3.0V)                 0V - 4.38V
 
 SCH311x:
        in6: Vbat       (+3.0V)                 0V - 4.38V
 
 SCH311x:
-       in0: +2.5V                              0V - 6.64V
+       in0: +2.5V                              0V - 3.32V
        in1: Vccp       (processor core)        0V - 2V
        in2: VCC        (internal +3.3V)        0V - 4.38V
        in3: +5V                                0V - 6.64V
        in1: Vccp       (processor core)        0V - 2V
        in2: VCC        (internal +3.3V)        0V - 4.38V
        in3: +5V                                0V - 6.64V
@@ -93,6 +97,15 @@ SCH5027:
        in5: VTR        (+3.3V standby)         0V - 4.38V
        in6: Vbat       (+3.0V)                 0V - 4.38V
 
        in5: VTR        (+3.3V standby)         0V - 4.38V
        in6: Vbat       (+3.0V)                 0V - 4.38V
 
+SCH5127:
+       in0: +2.5                               0V - 3.32V
+       in1: Vccp       (processor core)        0V - 3V
+       in2: VCC        (internal +3.3V)        0V - 4.38V
+       in3: V2_IN                              0V - 1.5V
+       in4: V1_IN                              0V - 1.5V
+       in5: VTR        (+3.3V standby)         0V - 4.38V
+       in6: Vbat       (+3.0V)                 0V - 4.38V
+
 Each voltage input has associated min and max limits which trigger an alarm
 when crossed.
 
 Each voltage input has associated min and max limits which trigger an alarm
 when crossed.
 
@@ -293,3 +306,21 @@ pwm[1-3]_auto_point1_pwm   RW      Auto PWM pwm point. Auto_point1 is the
 pwm[1-3]_auto_point2_pwm       RO      Auto PWM pwm point. Auto_point2 is the
                                        full-speed duty-cycle which is hard-
                                        wired to 255 (100% duty-cycle).
 pwm[1-3]_auto_point2_pwm       RO      Auto PWM pwm point. Auto_point2 is the
                                        full-speed duty-cycle which is hard-
                                        wired to 255 (100% duty-cycle).
+
+Chip Differences
+----------------
+
+Feature                        dme1737 sch311x sch5027 sch5127
+-------------------------------------------------------
+temp[1-3]_offset       yes     yes
+vid                    yes
+zone3                  yes     yes     yes
+zone[1-3]_hyst         yes     yes
+pwm min/off            yes     yes
+fan3                   opt     yes     opt     yes
+pwm3                   opt     yes     opt     yes
+fan4                   opt             opt
+fan5                   opt             opt
+pwm5                   opt             opt
+fan6                   opt             opt
+pwm6                   opt             opt
index 823dd28..980c17d 100644 (file)
@@ -1,12 +1,14 @@
 /*
 /*
- * dme1737.c - Driver for the SMSC DME1737, Asus A8000, SMSC SCH311x and
- *             SCH5027 Super-I/O chips integrated hardware monitoring features.
- * Copyright (c) 2007, 2008 Juerg Haefliger <juergh@gmail.com>
+ * dme1737.c - Driver for the SMSC DME1737, Asus A8000, SMSC SCH311x, SCH5027,
+ *             and SCH5127 Super-I/O chips integrated hardware monitoring
+ *             features.
+ * Copyright (c) 2007, 2008, 2009, 2010 Juerg Haefliger <juergh@gmail.com>
  *
  * This driver is an I2C/ISA hybrid, meaning that it uses the I2C bus to access
  * the chip registers if a DME1737, A8000, or SCH5027 is found and the ISA bus
  *
  * This driver is an I2C/ISA hybrid, meaning that it uses the I2C bus to access
  * the chip registers if a DME1737, A8000, or SCH5027 is found and the ISA bus
- * if a SCH311x chip is found. Both types of chips have very similar hardware
- * monitoring capabilities but differ in the way they can be accessed.
+ * if a SCH311x or SCH5127 chip is found. Both types of chips have very
+ * similar hardware monitoring capabilities but differ in the way they can be
+ * accessed.
  *
  * 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
  *
  * 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
@@ -57,7 +59,7 @@ MODULE_PARM_DESC(probe_all_addr, "Include probing of non-standard LPC "
 /* Addresses to scan */
 static const unsigned short normal_i2c[] = {0x2c, 0x2d, 0x2e, I2C_CLIENT_END};
 
 /* Addresses to scan */
 static const unsigned short normal_i2c[] = {0x2c, 0x2d, 0x2e, I2C_CLIENT_END};
 
-enum chips { dme1737, sch5027, sch311x };
+enum chips { dme1737, sch5027, sch311x, sch5127 };
 
 /* ---------------------------------------------------------------------
  * Registers
 
 /* ---------------------------------------------------------------------
  * Registers
@@ -164,10 +166,29 @@ static const u8 DME1737_BIT_ALARM_FAN[] = {10, 11, 12, 13, 22, 23};
 #define DME1737_VERSTEP_MASK   0xf8
 #define SCH311X_DEVICE         0x8c
 #define SCH5027_VERSTEP                0x69
 #define DME1737_VERSTEP_MASK   0xf8
 #define SCH311X_DEVICE         0x8c
 #define SCH5027_VERSTEP                0x69
+#define SCH5127_DEVICE         0x8e
+
+/* Device ID values (global configuration register index 0x20) */
+#define DME1737_ID_1   0x77
+#define DME1737_ID_2   0x78
+#define SCH3112_ID     0x7c
+#define SCH3114_ID     0x7d
+#define SCH3116_ID     0x7f
+#define SCH5027_ID     0x89
+#define SCH5127_ID     0x86
 
 /* Length of ISA address segment */
 #define DME1737_EXTENT 2
 
 
 /* Length of ISA address segment */
 #define DME1737_EXTENT 2
 
+/* chip-dependent features */
+#define HAS_TEMP_OFFSET                (1 << 0)                /* bit 0 */
+#define HAS_VID                        (1 << 1)                /* bit 1 */
+#define HAS_ZONE3              (1 << 2)                /* bit 2 */
+#define HAS_ZONE_HYST          (1 << 3)                /* bit 3 */
+#define HAS_PWM_MIN            (1 << 4)                /* bit 4 */
+#define HAS_FAN(ix)            (1 << ((ix) + 5))       /* bits 5-10 */
+#define HAS_PWM(ix)            (1 << ((ix) + 11))      /* bits 11-16 */
+
 /* ---------------------------------------------------------------------
  * Data structures and manipulation thereof
  * --------------------------------------------------------------------- */
 /* ---------------------------------------------------------------------
  * Data structures and manipulation thereof
  * --------------------------------------------------------------------- */
@@ -187,8 +208,7 @@ struct dme1737_data {
 
        u8 vid;
        u8 pwm_rr_en;
 
        u8 vid;
        u8 pwm_rr_en;
-       u8 has_pwm;
-       u8 has_fan;
+       u32 has_features;
 
        /* Register values */
        u16 in[7];
 
        /* Register values */
        u16 in[7];
@@ -224,8 +244,11 @@ static const int IN_NOMINAL_SCH311x[] = {2500, 1500, 3300, 5000, 12000, 3300,
                                         3300};
 static const int IN_NOMINAL_SCH5027[] = {5000, 2250, 3300, 1125, 1125, 3300,
                                         3300};
                                         3300};
 static const int IN_NOMINAL_SCH5027[] = {5000, 2250, 3300, 1125, 1125, 3300,
                                         3300};
+static const int IN_NOMINAL_SCH5127[] = {2500, 2250, 3300, 1125, 1125, 3300,
+                                        3300};
 #define IN_NOMINAL(type)       ((type) == sch311x ? IN_NOMINAL_SCH311x : \
                                 (type) == sch5027 ? IN_NOMINAL_SCH5027 : \
 #define IN_NOMINAL(type)       ((type) == sch311x ? IN_NOMINAL_SCH311x : \
                                 (type) == sch5027 ? IN_NOMINAL_SCH5027 : \
+                                (type) == sch5127 ? IN_NOMINAL_SCH5127 : \
                                 IN_NOMINAL_DME1737)
 
 /* Voltage input
                                 IN_NOMINAL_DME1737)
 
 /* Voltage input
@@ -568,7 +591,7 @@ static struct dme1737_data *dme1737_update_device(struct device *dev)
 
        /* Sample register contents every 1 sec */
        if (time_after(jiffies, data->last_update + HZ) || !data->valid) {
 
        /* Sample register contents every 1 sec */
        if (time_after(jiffies, data->last_update + HZ) || !data->valid) {
-               if (data->type == dme1737) {
+               if (data->has_features & HAS_VID) {
                        data->vid = dme1737_read(data, DME1737_REG_VID) &
                                0x3f;
                }
                        data->vid = dme1737_read(data, DME1737_REG_VID) &
                                0x3f;
                }
@@ -599,7 +622,7 @@ static struct dme1737_data *dme1737_update_device(struct device *dev)
                                        DME1737_REG_TEMP_MIN(ix));
                        data->temp_max[ix] = dme1737_read(data,
                                        DME1737_REG_TEMP_MAX(ix));
                                        DME1737_REG_TEMP_MIN(ix));
                        data->temp_max[ix] = dme1737_read(data,
                                        DME1737_REG_TEMP_MAX(ix));
-                       if (data->type != sch5027) {
+                       if (data->has_features & HAS_TEMP_OFFSET) {
                                data->temp_offset[ix] = dme1737_read(data,
                                                DME1737_REG_TEMP_OFFSET(ix));
                        }
                                data->temp_offset[ix] = dme1737_read(data,
                                                DME1737_REG_TEMP_OFFSET(ix));
                        }
@@ -626,7 +649,7 @@ static struct dme1737_data *dme1737_update_device(struct device *dev)
                for (ix = 0; ix < ARRAY_SIZE(data->fan); ix++) {
                        /* Skip reading registers if optional fans are not
                         * present */
                for (ix = 0; ix < ARRAY_SIZE(data->fan); ix++) {
                        /* Skip reading registers if optional fans are not
                         * present */
-                       if (!(data->has_fan & (1 << ix))) {
+                       if (!(data->has_features & HAS_FAN(ix))) {
                                continue;
                        }
                        data->fan[ix] = dme1737_read(data,
                                continue;
                        }
                        data->fan[ix] = dme1737_read(data,
@@ -650,7 +673,7 @@ static struct dme1737_data *dme1737_update_device(struct device *dev)
                for (ix = 0; ix < ARRAY_SIZE(data->pwm); ix++) {
                        /* Skip reading registers if optional PWMs are not
                         * present */
                for (ix = 0; ix < ARRAY_SIZE(data->pwm); ix++) {
                        /* Skip reading registers if optional PWMs are not
                         * present */
-                       if (!(data->has_pwm & (1 << ix))) {
+                       if (!(data->has_features & HAS_PWM(ix))) {
                                continue;
                        }
                        data->pwm[ix] = dme1737_read(data,
                                continue;
                        }
                        data->pwm[ix] = dme1737_read(data,
@@ -672,12 +695,24 @@ static struct dme1737_data *dme1737_update_device(struct device *dev)
 
                /* Thermal zone registers */
                for (ix = 0; ix < ARRAY_SIZE(data->zone_low); ix++) {
 
                /* Thermal zone registers */
                for (ix = 0; ix < ARRAY_SIZE(data->zone_low); ix++) {
-                       data->zone_low[ix] = dme1737_read(data,
-                                       DME1737_REG_ZONE_LOW(ix));
-                       data->zone_abs[ix] = dme1737_read(data,
-                                       DME1737_REG_ZONE_ABS(ix));
+                       /* Skip reading registers if zone3 is not present */
+                       if ((ix == 2) && !(data->has_features & HAS_ZONE3)) {
+                               continue;
+                       }
+                       /* sch5127 zone2 registers are special */
+                       if ((ix == 1) && (data->type == sch5127)) {
+                               data->zone_low[1] = dme1737_read(data,
+                                               DME1737_REG_ZONE_LOW(2));
+                               data->zone_abs[1] = dme1737_read(data,
+                                               DME1737_REG_ZONE_ABS(2));
+                       } else {
+                               data->zone_low[ix] = dme1737_read(data,
+                                               DME1737_REG_ZONE_LOW(ix));
+                               data->zone_abs[ix] = dme1737_read(data,
+                                               DME1737_REG_ZONE_ABS(ix));
+                       }
                }
                }
-               if (data->type != sch5027) {
+               if (data->has_features & HAS_ZONE_HYST) {
                        for (ix = 0; ix < ARRAY_SIZE(data->zone_hyst); ix++) {
                                data->zone_hyst[ix] = dme1737_read(data,
                                                DME1737_REG_ZONE_HYST(ix));
                        for (ix = 0; ix < ARRAY_SIZE(data->zone_hyst); ix++) {
                                data->zone_hyst[ix] = dme1737_read(data,
                                                DME1737_REG_ZONE_HYST(ix));
@@ -1594,10 +1629,6 @@ static struct attribute *dme1737_attr[] ={
        &sensor_dev_attr_zone2_auto_point2_temp.dev_attr.attr,
        &sensor_dev_attr_zone2_auto_point3_temp.dev_attr.attr,
        &sensor_dev_attr_zone2_auto_channels_temp.dev_attr.attr,
        &sensor_dev_attr_zone2_auto_point2_temp.dev_attr.attr,
        &sensor_dev_attr_zone2_auto_point3_temp.dev_attr.attr,
        &sensor_dev_attr_zone2_auto_channels_temp.dev_attr.attr,
-       &sensor_dev_attr_zone3_auto_point1_temp.dev_attr.attr,
-       &sensor_dev_attr_zone3_auto_point2_temp.dev_attr.attr,
-       &sensor_dev_attr_zone3_auto_point3_temp.dev_attr.attr,
-       &sensor_dev_attr_zone3_auto_channels_temp.dev_attr.attr,
        NULL
 };
 
        NULL
 };
 
@@ -1605,27 +1636,23 @@ static const struct attribute_group dme1737_group = {
        .attrs = dme1737_attr,
 };
 
        .attrs = dme1737_attr,
 };
 
-/* The following struct holds misc attributes, which are not available in all
- * chips. Their creation depends on the chip type which is determined during
- * module load. */
-static struct attribute *dme1737_misc_attr[] = {
-       /* Temperatures */
+/* The following struct holds temp offset attributes, which are not available
+ * in all chips. The following chips support them:
+ * DME1737, SCH311x */
+static struct attribute *dme1737_temp_offset_attr[] = {
        &sensor_dev_attr_temp1_offset.dev_attr.attr,
        &sensor_dev_attr_temp2_offset.dev_attr.attr,
        &sensor_dev_attr_temp3_offset.dev_attr.attr,
        &sensor_dev_attr_temp1_offset.dev_attr.attr,
        &sensor_dev_attr_temp2_offset.dev_attr.attr,
        &sensor_dev_attr_temp3_offset.dev_attr.attr,
-       /* Zones */
-       &sensor_dev_attr_zone1_auto_point1_temp_hyst.dev_attr.attr,
-       &sensor_dev_attr_zone2_auto_point1_temp_hyst.dev_attr.attr,
-       &sensor_dev_attr_zone3_auto_point1_temp_hyst.dev_attr.attr,
        NULL
 };
 
        NULL
 };
 
-static const struct attribute_group dme1737_misc_group = {
-       .attrs = dme1737_misc_attr,
+static const struct attribute_group dme1737_temp_offset_group = {
+       .attrs = dme1737_temp_offset_attr,
 };
 
 };
 
-/* The following struct holds VID-related attributes. Their creation
-   depends on the chip type which is determined during module load. */
+/* The following struct holds VID related attributes, which are not available
+ * in all chips. The following chips support them:
+ * DME1737 */
 static struct attribute *dme1737_vid_attr[] = {
        &dev_attr_vrm.attr,
        &dev_attr_cpu0_vid.attr,
 static struct attribute *dme1737_vid_attr[] = {
        &dev_attr_vrm.attr,
        &dev_attr_cpu0_vid.attr,
@@ -1636,6 +1663,36 @@ static const struct attribute_group dme1737_vid_group = {
        .attrs = dme1737_vid_attr,
 };
 
        .attrs = dme1737_vid_attr,
 };
 
+/* The following struct holds temp zone 3 related attributes, which are not
+ * available in all chips. The following chips support them:
+ * DME1737, SCH311x, SCH5027 */
+static struct attribute *dme1737_zone3_attr[] = {
+       &sensor_dev_attr_zone3_auto_point1_temp.dev_attr.attr,
+       &sensor_dev_attr_zone3_auto_point2_temp.dev_attr.attr,
+       &sensor_dev_attr_zone3_auto_point3_temp.dev_attr.attr,
+       &sensor_dev_attr_zone3_auto_channels_temp.dev_attr.attr,
+       NULL
+};
+
+static const struct attribute_group dme1737_zone3_group = {
+       .attrs = dme1737_zone3_attr,
+};
+
+
+/* The following struct holds temp zone hysteresis  related attributes, which
+ * are not available in all chips. The following chips support them:
+ * DME1737, SCH311x */
+static struct attribute *dme1737_zone_hyst_attr[] = {
+       &sensor_dev_attr_zone1_auto_point1_temp_hyst.dev_attr.attr,
+       &sensor_dev_attr_zone2_auto_point1_temp_hyst.dev_attr.attr,
+       &sensor_dev_attr_zone3_auto_point1_temp_hyst.dev_attr.attr,
+       NULL
+};
+
+static const struct attribute_group dme1737_zone_hyst_group = {
+       .attrs = dme1737_zone_hyst_attr,
+};
+
 /* The following structs hold the PWM attributes, some of which are optional.
  * Their creation depends on the chip configuration which is determined during
  * module load. */
 /* The following structs hold the PWM attributes, some of which are optional.
  * Their creation depends on the chip configuration which is determined during
  * module load. */
@@ -1691,10 +1748,10 @@ static const struct attribute_group dme1737_pwm_group[] = {
        { .attrs = dme1737_pwm6_attr },
 };
 
        { .attrs = dme1737_pwm6_attr },
 };
 
-/* The following struct holds misc PWM attributes, which are not available in
- * all chips. Their creation depends on the chip type which is determined
+/* The following struct holds auto PWM min attributes, which are not available
+ * in all chips. Their creation depends on the chip type which is determined
  * during module load. */
  * during module load. */
-static struct attribute *dme1737_pwm_misc_attr[] = {
+static struct attribute *dme1737_auto_pwm_min_attr[] = {
        &sensor_dev_attr_pwm1_auto_pwm_min.dev_attr.attr,
        &sensor_dev_attr_pwm2_auto_pwm_min.dev_attr.attr,
        &sensor_dev_attr_pwm3_auto_pwm_min.dev_attr.attr,
        &sensor_dev_attr_pwm1_auto_pwm_min.dev_attr.attr,
        &sensor_dev_attr_pwm2_auto_pwm_min.dev_attr.attr,
        &sensor_dev_attr_pwm3_auto_pwm_min.dev_attr.attr,
@@ -1764,14 +1821,25 @@ static struct attribute *dme1737_zone_chmod_attr[] = {
        &sensor_dev_attr_zone2_auto_point1_temp.dev_attr.attr,
        &sensor_dev_attr_zone2_auto_point2_temp.dev_attr.attr,
        &sensor_dev_attr_zone2_auto_point3_temp.dev_attr.attr,
        &sensor_dev_attr_zone2_auto_point1_temp.dev_attr.attr,
        &sensor_dev_attr_zone2_auto_point2_temp.dev_attr.attr,
        &sensor_dev_attr_zone2_auto_point3_temp.dev_attr.attr,
+       NULL
+};
+
+static const struct attribute_group dme1737_zone_chmod_group = {
+       .attrs = dme1737_zone_chmod_attr,
+};
+
+
+/* The permissions of the following zone 3 attributes are changed to read-
+ * writeable if the chip is *not* locked. Otherwise they stay read-only. */
+static struct attribute *dme1737_zone3_chmod_attr[] = {
        &sensor_dev_attr_zone3_auto_point1_temp.dev_attr.attr,
        &sensor_dev_attr_zone3_auto_point2_temp.dev_attr.attr,
        &sensor_dev_attr_zone3_auto_point3_temp.dev_attr.attr,
        NULL
 };
 
        &sensor_dev_attr_zone3_auto_point1_temp.dev_attr.attr,
        &sensor_dev_attr_zone3_auto_point2_temp.dev_attr.attr,
        &sensor_dev_attr_zone3_auto_point3_temp.dev_attr.attr,
        NULL
 };
 
-static const struct attribute_group dme1737_zone_chmod_group = {
-       .attrs = dme1737_zone_chmod_attr,
+static const struct attribute_group dme1737_zone3_chmod_group = {
+       .attrs = dme1737_zone3_chmod_attr,
 };
 
 /* The permissions of the following PWM attributes are changed to read-
 };
 
 /* The permissions of the following PWM attributes are changed to read-
@@ -1887,30 +1955,35 @@ static void dme1737_remove_files(struct device *dev)
        int ix;
 
        for (ix = 0; ix < ARRAY_SIZE(dme1737_fan_group); ix++) {
        int ix;
 
        for (ix = 0; ix < ARRAY_SIZE(dme1737_fan_group); ix++) {
-               if (data->has_fan & (1 << ix)) {
+               if (data->has_features & HAS_FAN(ix)) {
                        sysfs_remove_group(&dev->kobj,
                                           &dme1737_fan_group[ix]);
                }
        }
 
        for (ix = 0; ix < ARRAY_SIZE(dme1737_pwm_group); ix++) {
                        sysfs_remove_group(&dev->kobj,
                                           &dme1737_fan_group[ix]);
                }
        }
 
        for (ix = 0; ix < ARRAY_SIZE(dme1737_pwm_group); ix++) {
-               if (data->has_pwm & (1 << ix)) {
+               if (data->has_features & HAS_PWM(ix)) {
                        sysfs_remove_group(&dev->kobj,
                                           &dme1737_pwm_group[ix]);
                        sysfs_remove_group(&dev->kobj,
                                           &dme1737_pwm_group[ix]);
-                       if (data->type != sch5027 && ix < 3) {
+                       if ((data->has_features & HAS_PWM_MIN) && ix < 3) {
                                sysfs_remove_file(&dev->kobj,
                                sysfs_remove_file(&dev->kobj,
-                                                 dme1737_pwm_misc_attr[ix]);
+                                               dme1737_auto_pwm_min_attr[ix]);
                        }
                }
        }
 
                        }
                }
        }
 
-       if (data->type != sch5027) {
-               sysfs_remove_group(&dev->kobj, &dme1737_misc_group);
+       if (data->has_features & HAS_TEMP_OFFSET) {
+               sysfs_remove_group(&dev->kobj, &dme1737_temp_offset_group);
        }
        }
-       if (data->type == dme1737) {
+       if (data->has_features & HAS_VID) {
                sysfs_remove_group(&dev->kobj, &dme1737_vid_group);
        }
                sysfs_remove_group(&dev->kobj, &dme1737_vid_group);
        }
-
+       if (data->has_features & HAS_ZONE3) {
+               sysfs_remove_group(&dev->kobj, &dme1737_zone3_group);
+       }
+       if (data->has_features & HAS_ZONE_HYST) {
+               sysfs_remove_group(&dev->kobj, &dme1737_zone_hyst_group);
+       }
        sysfs_remove_group(&dev->kobj, &dme1737_group);
 
        if (!data->client) {
        sysfs_remove_group(&dev->kobj, &dme1737_group);
 
        if (!data->client) {
@@ -1934,23 +2007,31 @@ static int dme1737_create_files(struct device *dev)
                goto exit_remove;
        }
 
                goto exit_remove;
        }
 
-       /* Create misc sysfs attributes */
-       if ((data->type != sch5027) &&
+       /* Create chip-dependent sysfs attributes */
+       if ((data->has_features & HAS_TEMP_OFFSET) &&
            (err = sysfs_create_group(&dev->kobj,
            (err = sysfs_create_group(&dev->kobj,
-                                     &dme1737_misc_group))) {
+                                     &dme1737_temp_offset_group))) {
                goto exit_remove;
        }
                goto exit_remove;
        }
-
-       /* Create VID-related sysfs attributes */
-       if ((data->type == dme1737) &&
+       if ((data->has_features & HAS_VID) &&
            (err = sysfs_create_group(&dev->kobj,
                                      &dme1737_vid_group))) {
                goto exit_remove;
        }
            (err = sysfs_create_group(&dev->kobj,
                                      &dme1737_vid_group))) {
                goto exit_remove;
        }
+       if ((data->has_features & HAS_ZONE3) &&
+           (err = sysfs_create_group(&dev->kobj,
+                                     &dme1737_zone3_group))) {
+               goto exit_remove;
+       }
+       if ((data->has_features & HAS_ZONE_HYST) &&
+           (err = sysfs_create_group(&dev->kobj,
+                                     &dme1737_zone_hyst_group))) {
+               goto exit_remove;
+       }
 
        /* Create fan sysfs attributes */
        for (ix = 0; ix < ARRAY_SIZE(dme1737_fan_group); ix++) {
 
        /* Create fan sysfs attributes */
        for (ix = 0; ix < ARRAY_SIZE(dme1737_fan_group); ix++) {
-               if (data->has_fan & (1 << ix)) {
+               if (data->has_features & HAS_FAN(ix)) {
                        if ((err = sysfs_create_group(&dev->kobj,
                                                &dme1737_fan_group[ix]))) {
                                goto exit_remove;
                        if ((err = sysfs_create_group(&dev->kobj,
                                                &dme1737_fan_group[ix]))) {
                                goto exit_remove;
@@ -1960,14 +2041,14 @@ static int dme1737_create_files(struct device *dev)
 
        /* Create PWM sysfs attributes */
        for (ix = 0; ix < ARRAY_SIZE(dme1737_pwm_group); ix++) {
 
        /* Create PWM sysfs attributes */
        for (ix = 0; ix < ARRAY_SIZE(dme1737_pwm_group); ix++) {
-               if (data->has_pwm & (1 << ix)) {
+               if (data->has_features & HAS_PWM(ix)) {
                        if ((err = sysfs_create_group(&dev->kobj,
                                                &dme1737_pwm_group[ix]))) {
                                goto exit_remove;
                        }
                        if ((err = sysfs_create_group(&dev->kobj,
                                                &dme1737_pwm_group[ix]))) {
                                goto exit_remove;
                        }
-                       if (data->type != sch5027 && ix < 3 &&
+                       if ((data->has_features & HAS_PWM_MIN) && ix < 3 &&
                            (err = sysfs_create_file(&dev->kobj,
                            (err = sysfs_create_file(&dev->kobj,
-                                               dme1737_pwm_misc_attr[ix]))) {
+                                       dme1737_auto_pwm_min_attr[ix]))) {
                                goto exit_remove;
                        }
                }
                                goto exit_remove;
                        }
                }
@@ -1983,21 +2064,30 @@ static int dme1737_create_files(struct device *dev)
                dme1737_chmod_group(dev, &dme1737_zone_chmod_group,
                                    S_IRUGO | S_IWUSR);
 
                dme1737_chmod_group(dev, &dme1737_zone_chmod_group,
                                    S_IRUGO | S_IWUSR);
 
-               /* Change permissions of misc sysfs attributes */
-               if (data->type != sch5027) {
-                       dme1737_chmod_group(dev, &dme1737_misc_group,
+               /* Change permissions of chip-dependent sysfs attributes */
+               if (data->has_features & HAS_TEMP_OFFSET) {
+                       dme1737_chmod_group(dev, &dme1737_temp_offset_group,
+                                           S_IRUGO | S_IWUSR);
+               }
+               if (data->has_features & HAS_ZONE3) {
+                       dme1737_chmod_group(dev, &dme1737_zone3_chmod_group,
+                                           S_IRUGO | S_IWUSR);
+               }
+               if (data->has_features & HAS_ZONE_HYST) {
+                       dme1737_chmod_group(dev, &dme1737_zone_hyst_group,
                                            S_IRUGO | S_IWUSR);
                }
 
                /* Change permissions of PWM sysfs attributes */
                for (ix = 0; ix < ARRAY_SIZE(dme1737_pwm_chmod_group); ix++) {
                                            S_IRUGO | S_IWUSR);
                }
 
                /* Change permissions of PWM sysfs attributes */
                for (ix = 0; ix < ARRAY_SIZE(dme1737_pwm_chmod_group); ix++) {
-                       if (data->has_pwm & (1 << ix)) {
+                       if (data->has_features & HAS_PWM(ix)) {
                                dme1737_chmod_group(dev,
                                                &dme1737_pwm_chmod_group[ix],
                                                S_IRUGO | S_IWUSR);
                                dme1737_chmod_group(dev,
                                                &dme1737_pwm_chmod_group[ix],
                                                S_IRUGO | S_IWUSR);
-                               if (data->type != sch5027 && ix < 3) {
+                               if ((data->has_features & HAS_PWM_MIN) &&
+                                   ix < 3) {
                                        dme1737_chmod_file(dev,
                                        dme1737_chmod_file(dev,
-                                               dme1737_pwm_misc_attr[ix],
+                                               dme1737_auto_pwm_min_attr[ix],
                                                S_IRUGO | S_IWUSR);
                                }
                        }
                                                S_IRUGO | S_IWUSR);
                                }
                        }
@@ -2005,7 +2095,7 @@ static int dme1737_create_files(struct device *dev)
 
                /* Change permissions of pwm[1-3] if in manual mode */
                for (ix = 0; ix < 3; ix++) {
 
                /* Change permissions of pwm[1-3] if in manual mode */
                for (ix = 0; ix < 3; ix++) {
-                       if ((data->has_pwm & (1 << ix)) &&
+                       if ((data->has_features & HAS_PWM(ix)) &&
                            (PWM_EN_FROM_REG(data->pwm_config[ix]) == 1)) {
                                dme1737_chmod_file(dev,
                                                dme1737_pwm_chmod_attr[ix],
                            (PWM_EN_FROM_REG(data->pwm_config[ix]) == 1)) {
                                dme1737_chmod_file(dev,
                                                dme1737_pwm_chmod_attr[ix],
@@ -2052,20 +2142,20 @@ static int dme1737_init_device(struct device *dev)
                return -EFAULT;
        }
 
                return -EFAULT;
        }
 
-       /* Determine which optional fan and pwm features are enabled/present */
+       /* Determine which optional fan and pwm features are enabled (only
+        * valid for I2C devices) */
        if (client) {   /* I2C chip */
                data->config2 = dme1737_read(data, DME1737_REG_CONFIG2);
                /* Check if optional fan3 input is enabled */
                if (data->config2 & 0x04) {
        if (client) {   /* I2C chip */
                data->config2 = dme1737_read(data, DME1737_REG_CONFIG2);
                /* Check if optional fan3 input is enabled */
                if (data->config2 & 0x04) {
-                       data->has_fan |= (1 << 2);
+                       data->has_features |= HAS_FAN(2);
                }
 
                /* Fan4 and pwm3 are only available if the client's I2C address
                 * is the default 0x2e. Otherwise the I/Os associated with
                 * these functions are used for addr enable/select. */
                if (client->addr == 0x2e) {
                }
 
                /* Fan4 and pwm3 are only available if the client's I2C address
                 * is the default 0x2e. Otherwise the I/Os associated with
                 * these functions are used for addr enable/select. */
                if (client->addr == 0x2e) {
-                       data->has_fan |= (1 << 3);
-                       data->has_pwm |= (1 << 2);
+                       data->has_features |= HAS_FAN(3) | HAS_PWM(2);
                }
 
                /* Determine which of the optional fan[5-6] and pwm[5-6]
                }
 
                /* Determine which of the optional fan[5-6] and pwm[5-6]
@@ -2077,26 +2167,40 @@ static int dme1737_init_device(struct device *dev)
                        dev_warn(dev, "Failed to query Super-IO for optional "
                                 "features.\n");
                }
                        dev_warn(dev, "Failed to query Super-IO for optional "
                                 "features.\n");
                }
-       } else {   /* ISA chip */
-               /* Fan3 and pwm3 are always available. Fan[4-5] and pwm[5-6]
-                * don't exist in the ISA chip. */
-               data->has_fan |= (1 << 2);
-               data->has_pwm |= (1 << 2);
        }
 
        }
 
-       /* Fan1, fan2, pwm1, and pwm2 are always present */
-       data->has_fan |= 0x03;
-       data->has_pwm |= 0x03;
+       /* Fan[1-2] and pwm[1-2] are present in all chips */
+       data->has_features |= HAS_FAN(0) | HAS_FAN(1) | HAS_PWM(0) | HAS_PWM(1);
+
+       /* Chip-dependent features */
+       switch (data->type) {
+       case dme1737:
+               data->has_features |= HAS_TEMP_OFFSET | HAS_VID | HAS_ZONE3 |
+                       HAS_ZONE_HYST | HAS_PWM_MIN;
+               break;
+       case sch311x:
+               data->has_features |= HAS_TEMP_OFFSET | HAS_ZONE3 |
+                       HAS_ZONE_HYST | HAS_PWM_MIN | HAS_FAN(2) | HAS_PWM(2);
+               break;
+       case sch5027:
+               data->has_features |= HAS_ZONE3;
+               break;
+       case sch5127:
+               data->has_features |= HAS_FAN(2) | HAS_PWM(2);
+               break;
+       default:
+               break;
+       }
 
        dev_info(dev, "Optional features: pwm3=%s, pwm5=%s, pwm6=%s, "
                 "fan3=%s, fan4=%s, fan5=%s, fan6=%s.\n",
 
        dev_info(dev, "Optional features: pwm3=%s, pwm5=%s, pwm6=%s, "
                 "fan3=%s, fan4=%s, fan5=%s, fan6=%s.\n",
-                (data->has_pwm & (1 << 2)) ? "yes" : "no",
-                (data->has_pwm & (1 << 4)) ? "yes" : "no",
-                (data->has_pwm & (1 << 5)) ? "yes" : "no",
-                (data->has_fan & (1 << 2)) ? "yes" : "no",
-                (data->has_fan & (1 << 3)) ? "yes" : "no",
-                (data->has_fan & (1 << 4)) ? "yes" : "no",
-                (data->has_fan & (1 << 5)) ? "yes" : "no");
+                (data->has_features & HAS_PWM(2)) ? "yes" : "no",
+                (data->has_features & HAS_PWM(4)) ? "yes" : "no",
+                (data->has_features & HAS_PWM(5)) ? "yes" : "no",
+                (data->has_features & HAS_FAN(2)) ? "yes" : "no",
+                (data->has_features & HAS_FAN(3)) ? "yes" : "no",
+                (data->has_features & HAS_FAN(4)) ? "yes" : "no",
+                (data->has_features & HAS_FAN(5)) ? "yes" : "no");
 
        reg = dme1737_read(data, DME1737_REG_TACH_PWM);
        /* Inform if fan-to-pwm mapping differs from the default */
 
        reg = dme1737_read(data, DME1737_REG_TACH_PWM);
        /* Inform if fan-to-pwm mapping differs from the default */
@@ -2122,7 +2226,7 @@ static int dme1737_init_device(struct device *dev)
                for (ix = 0; ix < 3; ix++) {
                        data->pwm_config[ix] = dme1737_read(data,
                                                DME1737_REG_PWM_CONFIG(ix));
                for (ix = 0; ix < 3; ix++) {
                        data->pwm_config[ix] = dme1737_read(data,
                                                DME1737_REG_PWM_CONFIG(ix));
-                       if ((data->has_pwm & (1 << ix)) &&
+                       if ((data->has_features & HAS_PWM(ix)) &&
                            (PWM_EN_FROM_REG(data->pwm_config[ix]) == -1)) {
                                dev_info(dev, "Switching pwm%d to "
                                         "manual mode.\n", ix + 1);
                            (PWM_EN_FROM_REG(data->pwm_config[ix]) == -1)) {
                                dev_info(dev, "Switching pwm%d to "
                                         "manual mode.\n", ix + 1);
@@ -2142,7 +2246,7 @@ static int dme1737_init_device(struct device *dev)
        data->pwm_acz[2] = 4;   /* pwm3 -> zone3 */
 
        /* Set VRM */
        data->pwm_acz[2] = 4;   /* pwm3 -> zone3 */
 
        /* Set VRM */
-       if (data->type == dme1737) {
+       if (data->has_features & HAS_VID) {
                data->vrm = vid_which_vrm();
        }
 
                data->vrm = vid_which_vrm();
        }
 
@@ -2163,10 +2267,10 @@ static int dme1737_i2c_get_features(int sio_cip, struct dme1737_data *data)
        dme1737_sio_enter(sio_cip);
 
        /* Check device ID
        dme1737_sio_enter(sio_cip);
 
        /* Check device ID
-        * The DME1737 can return either 0x78 or 0x77 as its device ID.
-        * The SCH5027 returns 0x89 as its device ID. */
+        * We currently know about two kinds of DME1737 and SCH5027. */
        reg = force_id ? force_id : dme1737_sio_inb(sio_cip, 0x20);
        reg = force_id ? force_id : dme1737_sio_inb(sio_cip, 0x20);
-       if (!(reg == 0x77 || reg == 0x78 || reg == 0x89)) {
+       if (!(reg == DME1737_ID_1 || reg == DME1737_ID_2 ||
+             reg == SCH5027_ID)) {
                err = -ENODEV;
                goto exit;
        }
                err = -ENODEV;
                goto exit;
        }
@@ -2185,16 +2289,16 @@ static int dme1737_i2c_get_features(int sio_cip, struct dme1737_data *data)
         * are enabled and available. Bits [3:2] of registers 0x43-0x46 are set
         * to '10' if the respective feature is enabled. */
        if ((inb(addr + 0x43) & 0x0c) == 0x08) { /* fan6 */
         * are enabled and available. Bits [3:2] of registers 0x43-0x46 are set
         * to '10' if the respective feature is enabled. */
        if ((inb(addr + 0x43) & 0x0c) == 0x08) { /* fan6 */
-               data->has_fan |= (1 << 5);
+               data->has_features |= HAS_FAN(5);
        }
        if ((inb(addr + 0x44) & 0x0c) == 0x08) { /* pwm6 */
        }
        if ((inb(addr + 0x44) & 0x0c) == 0x08) { /* pwm6 */
-               data->has_pwm |= (1 << 5);
+               data->has_features |= HAS_PWM(5);
        }
        if ((inb(addr + 0x45) & 0x0c) == 0x08) { /* fan5 */
        }
        if ((inb(addr + 0x45) & 0x0c) == 0x08) { /* fan5 */
-               data->has_fan |= (1 << 4);
+               data->has_features |= HAS_FAN(4);
        }
        if ((inb(addr + 0x46) & 0x0c) == 0x08) { /* pwm5 */
        }
        if ((inb(addr + 0x46) & 0x0c) == 0x08) { /* pwm5 */
-               data->has_pwm |= (1 << 4);
+               data->has_features |= HAS_PWM(4);
        }
 
 exit:
        }
 
 exit:
@@ -2222,7 +2326,6 @@ static int dme1737_i2c_detect(struct i2c_client *client,
        if (company == DME1737_COMPANY_SMSC &&
            verstep == SCH5027_VERSTEP) {
                name = "sch5027";
        if (company == DME1737_COMPANY_SMSC &&
            verstep == SCH5027_VERSTEP) {
                name = "sch5027";
-
        } else if (company == DME1737_COMPANY_SMSC &&
                   (verstep & DME1737_VERSTEP_MASK) == DME1737_VERSTEP) {
                name = "dme1737";
        } else if (company == DME1737_COMPANY_SMSC &&
                   (verstep & DME1737_VERSTEP_MASK) == DME1737_VERSTEP) {
                name = "dme1737";
@@ -2329,10 +2432,10 @@ static int __init dme1737_isa_detect(int sio_cip, unsigned short *addr)
        dme1737_sio_enter(sio_cip);
 
        /* Check device ID
        dme1737_sio_enter(sio_cip);
 
        /* Check device ID
-        * We currently know about SCH3112 (0x7c), SCH3114 (0x7d), and
-        * SCH3116 (0x7f). */
+        * We currently know about SCH3112, SCH3114, SCH3116, and SCH5127 */
        reg = force_id ? force_id : dme1737_sio_inb(sio_cip, 0x20);
        reg = force_id ? force_id : dme1737_sio_inb(sio_cip, 0x20);
-       if (!(reg == 0x7c || reg == 0x7d || reg == 0x7f)) {
+       if (!(reg == SCH3112_ID || reg == SCH3114_ID || reg == SCH3116_ID ||
+             reg == SCH5127_ID)) {
                err = -ENODEV;
                goto exit;
        }
                err = -ENODEV;
                goto exit;
        }
@@ -2424,23 +2527,42 @@ static int __devinit dme1737_isa_probe(struct platform_device *pdev)
        platform_set_drvdata(pdev, data);
 
        /* Skip chip detection if module is loaded with force_id parameter */
        platform_set_drvdata(pdev, data);
 
        /* Skip chip detection if module is loaded with force_id parameter */
-       if (!force_id) {
+       switch (force_id) {
+       case SCH3112_ID:
+       case SCH3114_ID:
+       case SCH3116_ID:
+               data->type = sch311x;
+               break;
+       case SCH5127_ID:
+               data->type = sch5127;
+               break;
+       default:
                company = dme1737_read(data, DME1737_REG_COMPANY);
                device = dme1737_read(data, DME1737_REG_DEVICE);
 
                company = dme1737_read(data, DME1737_REG_COMPANY);
                device = dme1737_read(data, DME1737_REG_DEVICE);
 
-               if (!((company == DME1737_COMPANY_SMSC) &&
-                     (device == SCH311X_DEVICE))) {
+               if ((company == DME1737_COMPANY_SMSC) &&
+                   (device == SCH311X_DEVICE)) {
+                       data->type = sch311x;
+               } else if ((company == DME1737_COMPANY_SMSC) &&
+                          (device == SCH5127_DEVICE)) {
+                       data->type = sch5127;
+               } else {
                        err = -ENODEV;
                        goto exit_kfree;
                }
        }
                        err = -ENODEV;
                        goto exit_kfree;
                }
        }
-       data->type = sch311x;
 
 
-       /* Fill in the remaining client fields and initialize the mutex */
-       data->name = "sch311x";
+       if (data->type == sch5127) {
+               data->name = "sch5127";
+       } else {
+               data->name = "sch311x";
+       }
+
+       /* Initialize the mutex */
        mutex_init(&data->update_lock);
 
        mutex_init(&data->update_lock);
 
-       dev_info(dev, "Found a SCH311x chip at 0x%04x\n", data->addr);
+       dev_info(dev, "Found a %s chip at 0x%04x\n",
+                data->type == sch5127 ? "SCH5127" : "SCH311x", data->addr);
 
        /* Initialize the chip */
        if ((err = dme1737_init_device(dev))) {
 
        /* Initialize the chip */
        if ((err = dme1737_init_device(dev))) {