瀏覽代碼

hwmon: (dme1737) Add SCH5127 support

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>
Juerg Haefliger 15 年之前
父節點
當前提交
ea694431f9
共有 2 個文件被更改,包括 266 次插入113 次删除
  1. 41 10
      Documentation/hwmon/dme1737
  2. 225 103
      drivers/hwmon/dme1737.c

+ 41 - 10
Documentation/hwmon/dme1737

@@ -9,11 +9,15 @@ Supported chips:
   * SMSC SCH3112, SCH3114, SCH3116
   * SMSC SCH3112, SCH3114, SCH3116
     Prefix: 'sch311x'
     Prefix: 'sch311x'
     Addresses scanned: none, address read from Super-I/O config space
     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
   * SMSC SCH5027
     Prefix: 'sch5027'
     Prefix: 'sch5027'
     Addresses scanned: I2C 0x2c, 0x2d, 0x2e
     Addresses scanned: I2C 0x2c, 0x2d, 0x2e
     Datasheet: Provided by SMSC upon request and under NDA
     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:
 Authors:
     Juerg Haefliger <juergh@gmail.com>
     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
 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
 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
 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
 the configuration of the chip. The driver will detect which features are
 present during initialization and create the sysfs attributes accordingly.
 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
 	in6: Vbat	(+3.0V)			0V - 4.38V
 
 
 SCH311x:
 SCH311x:
-	in0: +2.5V				0V - 6.64V
+	in0: +2.5V				0V - 3.32V
 	in1: Vccp	(processor core)	0V - 2V
 	in1: Vccp	(processor core)	0V - 2V
 	in2: VCC	(internal +3.3V)	0V - 4.38V
 	in2: VCC	(internal +3.3V)	0V - 4.38V
 	in3: +5V				0V - 6.64V
 	in3: +5V				0V - 6.64V
@@ -93,6 +97,15 @@ SCH5027:
 	in5: VTR	(+3.3V standby)		0V - 4.38V
 	in5: VTR	(+3.3V standby)		0V - 4.38V
 	in6: Vbat	(+3.0V)			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
 Each voltage input has associated min and max limits which trigger an alarm
 when crossed.
 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
 pwm[1-3]_auto_point2_pwm	RO	Auto PWM pwm point. Auto_point2 is the
 					full-speed duty-cycle which is hard-
 					full-speed duty-cycle which is hard-
 					wired to 255 (100% duty-cycle).
 					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

+ 225 - 103
drivers/hwmon/dme1737.c

@@ -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
  * 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
  * 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
  * 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
  * 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 */
 /* Addresses to scan */
 static const unsigned short normal_i2c[] = {0x2c, 0x2d, 0x2e, I2C_CLIENT_END};
 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 DME1737_VERSTEP_MASK	0xf8
 #define SCH311X_DEVICE		0x8c
 #define SCH311X_DEVICE		0x8c
 #define SCH5027_VERSTEP		0x69
 #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 */
 /* Length of ISA address segment */
 #define DME1737_EXTENT	2
 #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 vid;
 	u8 pwm_rr_en;
 	u8 pwm_rr_en;
-	u8 has_pwm;
-	u8 has_fan;
+	u32 has_features;
 
 
 	/* Register values */
 	/* Register values */
 	u16 in[7];
 	u16 in[7];
@@ -224,8 +244,11 @@ static const int IN_NOMINAL_SCH311x[] = {2500, 1500, 3300, 5000, 12000, 3300,
 					 3300};
 					 3300};
 static const int IN_NOMINAL_SCH5027[] = {5000, 2250, 3300, 1125, 1125, 3300,
 static const int IN_NOMINAL_SCH5027[] = {5000, 2250, 3300, 1125, 1125, 3300,
 					 3300};
 					 3300};
+static const int IN_NOMINAL_SCH5127[] = {2500, 2250, 3300, 1125, 1125, 3300,
+					 3300};
 #define IN_NOMINAL(type)	((type) == sch311x ? IN_NOMINAL_SCH311x : \
 #define IN_NOMINAL(type)	((type) == sch311x ? IN_NOMINAL_SCH311x : \
 				 (type) == sch5027 ? IN_NOMINAL_SCH5027 : \
 				 (type) == sch5027 ? IN_NOMINAL_SCH5027 : \
+				 (type) == sch5127 ? IN_NOMINAL_SCH5127 : \
 				 IN_NOMINAL_DME1737)
 				 IN_NOMINAL_DME1737)
 
 
 /* Voltage input
 /* Voltage input
@@ -568,7 +591,7 @@ static struct dme1737_data *dme1737_update_device(struct device *dev)
 
 
 	/* Sample register contents every 1 sec */
 	/* Sample register contents every 1 sec */
 	if (time_after(jiffies, data->last_update + HZ) || !data->valid) {
 	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) &
 			data->vid = dme1737_read(data, DME1737_REG_VID) &
 				0x3f;
 				0x3f;
 		}
 		}
@@ -599,7 +622,7 @@ static struct dme1737_data *dme1737_update_device(struct device *dev)
 					DME1737_REG_TEMP_MIN(ix));
 					DME1737_REG_TEMP_MIN(ix));
 			data->temp_max[ix] = dme1737_read(data,
 			data->temp_max[ix] = dme1737_read(data,
 					DME1737_REG_TEMP_MAX(ix));
 					DME1737_REG_TEMP_MAX(ix));
-			if (data->type != sch5027) {
+			if (data->has_features & HAS_TEMP_OFFSET) {
 				data->temp_offset[ix] = dme1737_read(data,
 				data->temp_offset[ix] = dme1737_read(data,
 						DME1737_REG_TEMP_OFFSET(ix));
 						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++) {
 		for (ix = 0; ix < ARRAY_SIZE(data->fan); ix++) {
 			/* Skip reading registers if optional fans are not
 			/* Skip reading registers if optional fans are not
 			 * present */
 			 * present */
-			if (!(data->has_fan & (1 << ix))) {
+			if (!(data->has_features & HAS_FAN(ix))) {
 				continue;
 				continue;
 			}
 			}
 			data->fan[ix] = dme1737_read(data,
 			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++) {
 		for (ix = 0; ix < ARRAY_SIZE(data->pwm); ix++) {
 			/* Skip reading registers if optional PWMs are not
 			/* Skip reading registers if optional PWMs are not
 			 * present */
 			 * present */
-			if (!(data->has_pwm & (1 << ix))) {
+			if (!(data->has_features & HAS_PWM(ix))) {
 				continue;
 				continue;
 			}
 			}
 			data->pwm[ix] = dme1737_read(data,
 			data->pwm[ix] = dme1737_read(data,
@@ -672,12 +695,24 @@ static struct dme1737_data *dme1737_update_device(struct device *dev)
 
 
 		/* Thermal zone registers */
 		/* Thermal zone registers */
 		for (ix = 0; ix < ARRAY_SIZE(data->zone_low); ix++) {
 		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++) {
 			for (ix = 0; ix < ARRAY_SIZE(data->zone_hyst); ix++) {
 				data->zone_hyst[ix] = dme1737_read(data,
 				data->zone_hyst[ix] = dme1737_read(data,
 						DME1737_REG_ZONE_HYST(ix));
 						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_point2_temp.dev_attr.attr,
 	&sensor_dev_attr_zone2_auto_point3_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_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_temp1_offset.dev_attr.attr,
 	&sensor_dev_attr_temp2_offset.dev_attr.attr,
 	&sensor_dev_attr_temp2_offset.dev_attr.attr,
 	&sensor_dev_attr_temp3_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[] = {
 static struct attribute *dme1737_vid_attr[] = {
 	&dev_attr_vrm.attr,
 	&dev_attr_vrm.attr,
 	&dev_attr_cpu0_vid.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.
 /* The following structs hold the PWM attributes, some of which are optional.
  * Their creation depends on the chip configuration which is determined during
  * Their creation depends on the chip configuration which is determined during
  * module load. */
  * 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_pwm1_auto_pwm_min.dev_attr.attr,
 	&sensor_dev_attr_pwm2_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_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_point1_temp.dev_attr.attr,
 	&sensor_dev_attr_zone2_auto_point2_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_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_point1_temp.dev_attr.attr,
 	&sensor_dev_attr_zone3_auto_point2_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_point3_temp.dev_attr.attr,
 	NULL
 	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;
 	int ix;
 
 
 	for (ix = 0; ix < ARRAY_SIZE(dme1737_fan_group); 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,
 			sysfs_remove_group(&dev->kobj,
 					   &dme1737_fan_group[ix]);
 					   &dme1737_fan_group[ix]);
 		}
 		}
 	}
 	}
 
 
 	for (ix = 0; ix < ARRAY_SIZE(dme1737_pwm_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,
 			sysfs_remove_group(&dev->kobj,
 					   &dme1737_pwm_group[ix]);
 					   &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);
 	sysfs_remove_group(&dev->kobj, &dme1737_group);
 
 
 	if (!data->client) {
 	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,
 	    (err = sysfs_create_group(&dev->kobj,
 				      &dme1737_vid_group))) {
 				      &dme1737_vid_group))) {
 		goto exit_remove;
 		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 */
 	/* Create fan sysfs attributes */
 	for (ix = 0; ix < ARRAY_SIZE(dme1737_fan_group); ix++) {
 	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,
 			if ((err = sysfs_create_group(&dev->kobj,
 						&dme1737_fan_group[ix]))) {
 						&dme1737_fan_group[ix]))) {
 				goto exit_remove;
 				goto exit_remove;
@@ -1960,14 +2041,14 @@ static int dme1737_create_files(struct device *dev)
 
 
 	/* Create PWM sysfs attributes */
 	/* Create PWM sysfs attributes */
 	for (ix = 0; ix < ARRAY_SIZE(dme1737_pwm_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)) {
 			if ((err = sysfs_create_group(&dev->kobj,
 			if ((err = sysfs_create_group(&dev->kobj,
 						&dme1737_pwm_group[ix]))) {
 						&dme1737_pwm_group[ix]))) {
 				goto exit_remove;
 				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,
 		dme1737_chmod_group(dev, &dme1737_zone_chmod_group,
 				    S_IRUGO | S_IWUSR);
 				    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);
 					    S_IRUGO | S_IWUSR);
 		}
 		}
 
 
 		/* Change permissions of PWM sysfs attributes */
 		/* Change permissions of PWM sysfs attributes */
 		for (ix = 0; ix < ARRAY_SIZE(dme1737_pwm_chmod_group); ix++) {
 		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_chmod_group(dev,
 						&dme1737_pwm_chmod_group[ix],
 						&dme1737_pwm_chmod_group[ix],
 						S_IRUGO | S_IWUSR);
 						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 */
 		/* Change permissions of pwm[1-3] if in manual mode */
 		for (ix = 0; ix < 3; ix++) {
 		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)) {
 			    (PWM_EN_FROM_REG(data->pwm_config[ix]) == 1)) {
 				dme1737_chmod_file(dev,
 				dme1737_chmod_file(dev,
 						dme1737_pwm_chmod_attr[ix],
 						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 */
 	if (client) {   /* I2C chip */
 		data->config2 = dme1737_read(data, DME1737_REG_CONFIG2);
 		data->config2 = dme1737_read(data, DME1737_REG_CONFIG2);
 		/* Check if optional fan3 input is enabled */
 		/* Check if optional fan3 input is enabled */
 		if (data->config2 & 0x04) {
 		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
 		/* Fan4 and pwm3 are only available if the client's I2C address
 		 * is the default 0x2e. Otherwise the I/Os associated with
 		 * is the default 0x2e. Otherwise the I/Os associated with
 		 * these functions are used for addr enable/select. */
 		 * these functions are used for addr enable/select. */
 		if (client->addr == 0x2e) {
 		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 "
 			dev_warn(dev, "Failed to query Super-IO for optional "
 				 "features.\n");
 				 "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, "
 	dev_info(dev, "Optional features: pwm3=%s, pwm5=%s, pwm6=%s, "
 		 "fan3=%s, fan4=%s, fan5=%s, fan6=%s.\n",
 		 "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);
 	reg = dme1737_read(data, DME1737_REG_TACH_PWM);
 	/* Inform if fan-to-pwm mapping differs from the default */
 	/* 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++) {
 		for (ix = 0; ix < 3; ix++) {
 			data->pwm_config[ix] = dme1737_read(data,
 			data->pwm_config[ix] = dme1737_read(data,
 						DME1737_REG_PWM_CONFIG(ix));
 						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)) {
 			    (PWM_EN_FROM_REG(data->pwm_config[ix]) == -1)) {
 				dev_info(dev, "Switching pwm%d to "
 				dev_info(dev, "Switching pwm%d to "
 					 "manual mode.\n", ix + 1);
 					 "manual mode.\n", ix + 1);
@@ -2142,7 +2246,7 @@ static int dme1737_init_device(struct device *dev)
 	data->pwm_acz[2] = 4;	/* pwm3 -> zone3 */
 	data->pwm_acz[2] = 4;	/* pwm3 -> zone3 */
 
 
 	/* Set VRM */
 	/* 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);
 	dme1737_sio_enter(sio_cip);
 
 
 	/* Check device ID
 	/* 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;
 		err = -ENODEV;
 		goto exit;
 		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
 	 * are enabled and available. Bits [3:2] of registers 0x43-0x46 are set
 	 * to '10' if the respective feature is enabled. */
 	 * to '10' if the respective feature is enabled. */
 	if ((inb(addr + 0x43) & 0x0c) == 0x08) { /* fan6 */
 	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 &&
 	if (company == DME1737_COMPANY_SMSC &&
 	    verstep == SCH5027_VERSTEP) {
 	    verstep == SCH5027_VERSTEP) {
 		name = "sch5027";
 		name = "sch5027";
-
 	} else if (company == DME1737_COMPANY_SMSC &&
 	} else if (company == DME1737_COMPANY_SMSC &&
 		   (verstep & DME1737_VERSTEP_MASK) == DME1737_VERSTEP) {
 		   (verstep & DME1737_VERSTEP_MASK) == DME1737_VERSTEP) {
 		name = "dme1737";
 		name = "dme1737";
@@ -2329,10 +2432,10 @@ static int __init dme1737_isa_detect(int sio_cip, unsigned short *addr)
 	dme1737_sio_enter(sio_cip);
 	dme1737_sio_enter(sio_cip);
 
 
 	/* Check device ID
 	/* 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;
 		err = -ENODEV;
 		goto exit;
 		goto exit;
 	}
 	}
@@ -2424,23 +2527,42 @@ static int __devinit dme1737_isa_probe(struct platform_device *pdev)
 	platform_set_drvdata(pdev, data);
 	platform_set_drvdata(pdev, data);
 
 
 	/* Skip chip detection if module is loaded with force_id parameter */
 	/* 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);
 		company = dme1737_read(data, DME1737_REG_COMPANY);
 		device = dme1737_read(data, DME1737_REG_DEVICE);
 		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;
 			err = -ENODEV;
 			goto exit_kfree;
 			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 */
 	/* Initialize the chip */
 	if ((err = dme1737_init_device(dev))) {
 	if ((err = dme1737_init_device(dev))) {