浏览代码

hwmon: (dme1737) Add support for the SMSC SCH5027

Add support for the SCH5027. The differences to the DME1737 are:
- No support for programmable temp offsets
- In auto mode, PWM outputs stay on min value if temp goes below low threshold
  and can't be programmed to fully turn off
- Different voltage scaling
- No VID input

Signed-off-by: Juerg Haefliger <juergh@gmail.com>
Signed-off-by: Jean Delvare <khali@linux-fr.org>
Juerg Haefliger 16 年之前
父节点
当前提交
549edb8332
共有 3 个文件被更改,包括 169 次插入81 次删除
  1. 37 16
      Documentation/hwmon/dme1737
  2. 2 2
      drivers/hwmon/Kconfig
  3. 130 63
      drivers/hwmon/dme1737.c

+ 37 - 16
Documentation/hwmon/dme1737

@@ -10,6 +10,10 @@ Supported chips:
     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: http://www.nuhorizons.com/FeaturedProducts/Volume1/SMSC/311x.pdf
+  * SMSC SCH5027
+    Prefix: 'sch5027'
+    Addresses scanned: I2C 0x2c, 0x2d, 0x2e
+    Datasheet: Provided by SMSC upon request and under NDA
 
 
 Authors:
 Authors:
     Juerg Haefliger <juergh@gmail.com>
     Juerg Haefliger <juergh@gmail.com>
@@ -27,33 +31,31 @@ Module Parameters
 			following boards:
 			following boards:
 			- VIA EPIA SN18000
 			- VIA EPIA SN18000
 
 
-Note that there is no need to use this parameter if the driver loads without
-complaining. The driver will say so if it is necessary.
-
 
 
 Description
 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) and SMSC SCH311x 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
+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
+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
 automatically.
 automatically.
 
 
-For the DME1737 and A8000, fan[1-2] and pwm[1-2] are always present. 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.
+For the DME1737, A8000 and SCH5027, fan[1-2] and pwm[1-2] are always present.
+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.
 
 
 For the SCH311x, fan[1-3] and pwm[1-3] are always present and fan[4-6] and
 For the SCH311x, fan[1-3] and pwm[1-3] are always present and fan[4-6] and
 pwm[5-6] don't exist.
 pwm[5-6] don't exist.
 
 
-The hardware monitoring features of the DME1737 and A8000 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
-or A8000 and as a platform driver if it detects a SCH311x chip.
+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.
 
 
 
 
 Voltage Monitoring
 Voltage Monitoring
@@ -64,6 +66,7 @@ scaling resistors. The values returned by the driver therefore reflect true
 millivolts and don't need scaling. The voltage inputs are mapped as follows
 millivolts and don't need scaling. The voltage inputs are mapped as follows
 (the last column indicates the input ranges):
 (the last column indicates the input ranges):
 
 
+DME1737, A8000:
 	in0: +5VTR	(+5V standby)		0V - 6.64V
 	in0: +5VTR	(+5V standby)		0V - 6.64V
 	in1: Vccp	(processor core)	0V - 3V
 	in1: Vccp	(processor core)	0V - 3V
 	in2: VCC	(internal +3.3V)	0V - 4.38V
 	in2: VCC	(internal +3.3V)	0V - 4.38V
@@ -72,6 +75,24 @@ millivolts and don't need scaling. The voltage inputs are mapped as follows
 	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
 
 
+SCH311x:
+	in0: +2.5V				0V - 6.64V
+	in1: Vccp	(processor core)	0V - 2V
+	in2: VCC	(internal +3.3V)	0V - 4.38V
+	in3: +5V				0V - 6.64V
+	in4: +12V				0V - 16V
+	in5: VTR	(+3.3V standby)		0V - 4.38V
+	in6: Vbat	(+3.0V)			0V - 4.38V
+
+SCH5027:
+	in0: +5VTR	(+5V standby)		0V - 6.64V
+	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.
 
 

+ 2 - 2
drivers/hwmon/Kconfig

@@ -575,8 +575,8 @@ config SENSORS_DME1737
 	select HWMON_VID
 	select HWMON_VID
 	help
 	help
 	  If you say yes here you get support for the hardware monitoring
 	  If you say yes here you get support for the hardware monitoring
-	  and fan control features of the SMSC DME1737 (and compatibles
-	  like the Asus A8000) and SCH311x Super-I/O chips.
+	  and fan control features of the SMSC DME1737, SCH311x, SCH5027, and
+	  Asus A8000 Super-I/O chips.
 
 
 	  This driver can also be built as a module.  If so, the module
 	  This driver can also be built as a module.  If so, the module
 	  will be called dme1737.
 	  will be called dme1737.

+ 130 - 63
drivers/hwmon/dme1737.c

@@ -1,11 +1,11 @@
 /*
 /*
- * dme1737.c - Driver for the SMSC DME1737, Asus A8000, and SMSC SCH311x
- *             Super-I/O chips integrated hardware monitoring features.
- * Copyright (c) 2007 Juerg Haefliger <juergh@gmail.com>
+ * 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>
  *
  *
  * 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 (or A8000) is found and the ISA bus if a
- * SCH311x chip is found. Both types of chips have very similar hardware
+ * 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.
  * 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
@@ -57,7 +57,10 @@ MODULE_PARM_DESC(probe_all_addr, "Include probing of non-standard LPC "
 static const unsigned short normal_i2c[] = {0x2c, 0x2d, 0x2e, I2C_CLIENT_END};
 static const unsigned short normal_i2c[] = {0x2c, 0x2d, 0x2e, I2C_CLIENT_END};
 
 
 /* Insmod parameters */
 /* Insmod parameters */
-I2C_CLIENT_INSMOD_1(dme1737);
+I2C_CLIENT_INSMOD_2(dme1737, sch5027);
+
+/* ISA chip types */
+enum isa_chips { sch311x = sch5027 + 1 };
 
 
 /* ---------------------------------------------------------------------
 /* ---------------------------------------------------------------------
  * Registers
  * Registers
@@ -163,6 +166,7 @@ static const u8 DME1737_BIT_ALARM_FAN[] = {10, 11, 12, 13, 22, 23};
 #define DME1737_VERSTEP		0x88
 #define DME1737_VERSTEP		0x88
 #define DME1737_VERSTEP_MASK	0xf8
 #define DME1737_VERSTEP_MASK	0xf8
 #define SCH311X_DEVICE		0x8c
 #define SCH311X_DEVICE		0x8c
+#define SCH5027_VERSTEP		0x69
 
 
 /* Length of ISA address segment */
 /* Length of ISA address segment */
 #define DME1737_EXTENT	2
 #define DME1737_EXTENT	2
@@ -182,6 +186,7 @@ struct dme1737_data {
 	unsigned long last_update;	/* in jiffies */
 	unsigned long last_update;	/* in jiffies */
 	unsigned long last_vbat;	/* in jiffies */
 	unsigned long last_vbat;	/* in jiffies */
 	enum chips type;
 	enum chips type;
+	const int *in_nominal;		/* pointer to IN_NOMINAL array */
 
 
 	u8 vid;
 	u8 vid;
 	u8 pwm_rr_en;
 	u8 pwm_rr_en;
@@ -220,23 +225,23 @@ static const int IN_NOMINAL_DME1737[] = {5000, 2250, 3300, 5000, 12000, 3300,
 					 3300};
 					 3300};
 static const int IN_NOMINAL_SCH311x[] = {2500, 1500, 3300, 5000, 12000, 3300,
 static const int IN_NOMINAL_SCH311x[] = {2500, 1500, 3300, 5000, 12000, 3300,
 					 3300};
 					 3300};
-#define IN_NOMINAL(ix, type)	(((type) == dme1737) ? \
-				IN_NOMINAL_DME1737[(ix)] : \
-				IN_NOMINAL_SCH311x[(ix)])
+static const int IN_NOMINAL_SCH5027[] = {5000, 2250, 3300, 1125, 1125, 3300,
+					 3300};
+#define IN_NOMINAL(type)	((type) == sch311x ? IN_NOMINAL_SCH311x : \
+				 (type) == sch5027 ? IN_NOMINAL_SCH5027 : \
+				 IN_NOMINAL_DME1737)
 
 
 /* Voltage input
 /* Voltage input
  * Voltage inputs have 16 bits resolution, limit values have 8 bits
  * Voltage inputs have 16 bits resolution, limit values have 8 bits
  * resolution. */
  * resolution. */
-static inline int IN_FROM_REG(int reg, int ix, int res, int type)
+static inline int IN_FROM_REG(int reg, int nominal, int res)
 {
 {
-	return (reg * IN_NOMINAL(ix, type) + (3 << (res - 3))) /
-		(3 << (res - 2));
+	return (reg * nominal + (3 << (res - 3))) / (3 << (res - 2));
 }
 }
 
 
-static inline int IN_TO_REG(int val, int ix, int type)
+static inline int IN_TO_REG(int val, int nominal)
 {
 {
-	return SENSORS_LIMIT((val * 192 + IN_NOMINAL(ix, type) / 2) /
-			     IN_NOMINAL(ix, type), 0, 255);
+	return SENSORS_LIMIT((val * 192 + nominal / 2) / nominal, 0, 255);
 }
 }
 
 
 /* Temperature input
 /* Temperature input
@@ -565,7 +570,10 @@ 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) {
-		data->vid = dme1737_read(client, DME1737_REG_VID) & 0x3f;
+		if (data->type != sch5027) {
+			data->vid = dme1737_read(client, DME1737_REG_VID) &
+				0x3f;
+		}
 
 
 		/* In (voltage) registers */
 		/* In (voltage) registers */
 		for (ix = 0; ix < ARRAY_SIZE(data->in); ix++) {
 		for (ix = 0; ix < ARRAY_SIZE(data->in); ix++) {
@@ -593,8 +601,10 @@ 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(client,
 			data->temp_max[ix] = dme1737_read(client,
 					DME1737_REG_TEMP_MAX(ix));
 					DME1737_REG_TEMP_MAX(ix));
-			data->temp_offset[ix] = dme1737_read(client,
-					DME1737_REG_TEMP_OFFSET(ix));
+			if (data->type != sch5027) {
+				data->temp_offset[ix] = dme1737_read(client,
+						DME1737_REG_TEMP_OFFSET(ix));
+			}
 		}
 		}
 
 
 		/* In and temp LSB registers
 		/* In and temp LSB registers
@@ -669,9 +679,11 @@ static struct dme1737_data *dme1737_update_device(struct device *dev)
 			data->zone_abs[ix] = dme1737_read(client,
 			data->zone_abs[ix] = dme1737_read(client,
 					DME1737_REG_ZONE_ABS(ix));
 					DME1737_REG_ZONE_ABS(ix));
 		}
 		}
-		for (ix = 0; ix < ARRAY_SIZE(data->zone_hyst); ix++) {
-			data->zone_hyst[ix] = dme1737_read(client,
+		if (data->type != sch5027) {
+			for (ix = 0; ix < ARRAY_SIZE(data->zone_hyst); ix++) {
+				data->zone_hyst[ix] = dme1737_read(client,
 						DME1737_REG_ZONE_HYST(ix));
 						DME1737_REG_ZONE_HYST(ix));
+			}
 		}
 		}
 
 
 		/* Alarm registers */
 		/* Alarm registers */
@@ -735,13 +747,13 @@ static ssize_t show_in(struct device *dev, struct device_attribute *attr,
 
 
 	switch (fn) {
 	switch (fn) {
 	case SYS_IN_INPUT:
 	case SYS_IN_INPUT:
-		res = IN_FROM_REG(data->in[ix], ix, 16, data->type);
+		res = IN_FROM_REG(data->in[ix], data->in_nominal[ix], 16);
 		break;
 		break;
 	case SYS_IN_MIN:
 	case SYS_IN_MIN:
-		res = IN_FROM_REG(data->in_min[ix], ix, 8, data->type);
+		res = IN_FROM_REG(data->in_min[ix], data->in_nominal[ix], 8);
 		break;
 		break;
 	case SYS_IN_MAX:
 	case SYS_IN_MAX:
-		res = IN_FROM_REG(data->in_max[ix], ix, 8, data->type);
+		res = IN_FROM_REG(data->in_max[ix], data->in_nominal[ix], 8);
 		break;
 		break;
 	case SYS_IN_ALARM:
 	case SYS_IN_ALARM:
 		res = (data->alarms >> DME1737_BIT_ALARM_IN[ix]) & 0x01;
 		res = (data->alarms >> DME1737_BIT_ALARM_IN[ix]) & 0x01;
@@ -768,12 +780,12 @@ static ssize_t set_in(struct device *dev, struct device_attribute *attr,
 	mutex_lock(&data->update_lock);
 	mutex_lock(&data->update_lock);
 	switch (fn) {
 	switch (fn) {
 	case SYS_IN_MIN:
 	case SYS_IN_MIN:
-		data->in_min[ix] = IN_TO_REG(val, ix, data->type);
+		data->in_min[ix] = IN_TO_REG(val, data->in_nominal[ix]);
 		dme1737_write(client, DME1737_REG_IN_MIN(ix),
 		dme1737_write(client, DME1737_REG_IN_MIN(ix),
 			      data->in_min[ix]);
 			      data->in_min[ix]);
 		break;
 		break;
 	case SYS_IN_MAX:
 	case SYS_IN_MAX:
-		data->in_max[ix] = IN_TO_REG(val, ix, data->type);
+		data->in_max[ix] = IN_TO_REG(val, data->in_nominal[ix]);
 		dme1737_write(client, DME1737_REG_IN_MAX(ix),
 		dme1737_write(client, DME1737_REG_IN_MAX(ix),
 			      data->in_max[ix]);
 			      data->in_max[ix]);
 		break;
 		break;
@@ -1570,43 +1582,56 @@ static struct attribute *dme1737_attr[] ={
 	&sensor_dev_attr_temp1_max.dev_attr.attr,
 	&sensor_dev_attr_temp1_max.dev_attr.attr,
 	&sensor_dev_attr_temp1_alarm.dev_attr.attr,
 	&sensor_dev_attr_temp1_alarm.dev_attr.attr,
 	&sensor_dev_attr_temp1_fault.dev_attr.attr,
 	&sensor_dev_attr_temp1_fault.dev_attr.attr,
-	&sensor_dev_attr_temp1_offset.dev_attr.attr,
 	&sensor_dev_attr_temp2_input.dev_attr.attr,
 	&sensor_dev_attr_temp2_input.dev_attr.attr,
 	&sensor_dev_attr_temp2_min.dev_attr.attr,
 	&sensor_dev_attr_temp2_min.dev_attr.attr,
 	&sensor_dev_attr_temp2_max.dev_attr.attr,
 	&sensor_dev_attr_temp2_max.dev_attr.attr,
 	&sensor_dev_attr_temp2_alarm.dev_attr.attr,
 	&sensor_dev_attr_temp2_alarm.dev_attr.attr,
 	&sensor_dev_attr_temp2_fault.dev_attr.attr,
 	&sensor_dev_attr_temp2_fault.dev_attr.attr,
-	&sensor_dev_attr_temp2_offset.dev_attr.attr,
 	&sensor_dev_attr_temp3_input.dev_attr.attr,
 	&sensor_dev_attr_temp3_input.dev_attr.attr,
 	&sensor_dev_attr_temp3_min.dev_attr.attr,
 	&sensor_dev_attr_temp3_min.dev_attr.attr,
 	&sensor_dev_attr_temp3_max.dev_attr.attr,
 	&sensor_dev_attr_temp3_max.dev_attr.attr,
 	&sensor_dev_attr_temp3_alarm.dev_attr.attr,
 	&sensor_dev_attr_temp3_alarm.dev_attr.attr,
 	&sensor_dev_attr_temp3_fault.dev_attr.attr,
 	&sensor_dev_attr_temp3_fault.dev_attr.attr,
-	&sensor_dev_attr_temp3_offset.dev_attr.attr,
 	/* Zones */
 	/* Zones */
-	&sensor_dev_attr_zone1_auto_point1_temp_hyst.dev_attr.attr,
 	&sensor_dev_attr_zone1_auto_point1_temp.dev_attr.attr,
 	&sensor_dev_attr_zone1_auto_point1_temp.dev_attr.attr,
 	&sensor_dev_attr_zone1_auto_point2_temp.dev_attr.attr,
 	&sensor_dev_attr_zone1_auto_point2_temp.dev_attr.attr,
 	&sensor_dev_attr_zone1_auto_point3_temp.dev_attr.attr,
 	&sensor_dev_attr_zone1_auto_point3_temp.dev_attr.attr,
 	&sensor_dev_attr_zone1_auto_channels_temp.dev_attr.attr,
 	&sensor_dev_attr_zone1_auto_channels_temp.dev_attr.attr,
-	&sensor_dev_attr_zone2_auto_point1_temp_hyst.dev_attr.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,
 	&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_hyst.dev_attr.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,
 	&sensor_dev_attr_zone3_auto_channels_temp.dev_attr.attr,
 	&sensor_dev_attr_zone3_auto_channels_temp.dev_attr.attr,
+	NULL
+};
+
+static const struct attribute_group dme1737_group = {
+	.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 */
+	&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,
 	/* Misc */
 	/* Misc */
 	&dev_attr_vrm.attr,
 	&dev_attr_vrm.attr,
 	&dev_attr_cpu0_vid.attr,
 	&dev_attr_cpu0_vid.attr,
 	NULL
 	NULL
 };
 };
 
 
-static const struct attribute_group dme1737_group = {
-	.attrs = dme1737_attr,
+static const struct attribute_group dme1737_misc_group = {
+	.attrs = dme1737_misc_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.
@@ -1618,7 +1643,6 @@ static struct attribute *dme1737_pwm1_attr[] = {
 	&sensor_dev_attr_pwm1_enable.dev_attr.attr,
 	&sensor_dev_attr_pwm1_enable.dev_attr.attr,
 	&sensor_dev_attr_pwm1_ramp_rate.dev_attr.attr,
 	&sensor_dev_attr_pwm1_ramp_rate.dev_attr.attr,
 	&sensor_dev_attr_pwm1_auto_channels_zone.dev_attr.attr,
 	&sensor_dev_attr_pwm1_auto_channels_zone.dev_attr.attr,
-	&sensor_dev_attr_pwm1_auto_pwm_min.dev_attr.attr,
 	&sensor_dev_attr_pwm1_auto_point1_pwm.dev_attr.attr,
 	&sensor_dev_attr_pwm1_auto_point1_pwm.dev_attr.attr,
 	&sensor_dev_attr_pwm1_auto_point2_pwm.dev_attr.attr,
 	&sensor_dev_attr_pwm1_auto_point2_pwm.dev_attr.attr,
 	NULL
 	NULL
@@ -1629,7 +1653,6 @@ static struct attribute *dme1737_pwm2_attr[] = {
 	&sensor_dev_attr_pwm2_enable.dev_attr.attr,
 	&sensor_dev_attr_pwm2_enable.dev_attr.attr,
 	&sensor_dev_attr_pwm2_ramp_rate.dev_attr.attr,
 	&sensor_dev_attr_pwm2_ramp_rate.dev_attr.attr,
 	&sensor_dev_attr_pwm2_auto_channels_zone.dev_attr.attr,
 	&sensor_dev_attr_pwm2_auto_channels_zone.dev_attr.attr,
-	&sensor_dev_attr_pwm2_auto_pwm_min.dev_attr.attr,
 	&sensor_dev_attr_pwm2_auto_point1_pwm.dev_attr.attr,
 	&sensor_dev_attr_pwm2_auto_point1_pwm.dev_attr.attr,
 	&sensor_dev_attr_pwm2_auto_point2_pwm.dev_attr.attr,
 	&sensor_dev_attr_pwm2_auto_point2_pwm.dev_attr.attr,
 	NULL
 	NULL
@@ -1640,7 +1663,6 @@ static struct attribute *dme1737_pwm3_attr[] = {
 	&sensor_dev_attr_pwm3_enable.dev_attr.attr,
 	&sensor_dev_attr_pwm3_enable.dev_attr.attr,
 	&sensor_dev_attr_pwm3_ramp_rate.dev_attr.attr,
 	&sensor_dev_attr_pwm3_ramp_rate.dev_attr.attr,
 	&sensor_dev_attr_pwm3_auto_channels_zone.dev_attr.attr,
 	&sensor_dev_attr_pwm3_auto_channels_zone.dev_attr.attr,
-	&sensor_dev_attr_pwm3_auto_pwm_min.dev_attr.attr,
 	&sensor_dev_attr_pwm3_auto_point1_pwm.dev_attr.attr,
 	&sensor_dev_attr_pwm3_auto_point1_pwm.dev_attr.attr,
 	&sensor_dev_attr_pwm3_auto_point2_pwm.dev_attr.attr,
 	&sensor_dev_attr_pwm3_auto_point2_pwm.dev_attr.attr,
 	NULL
 	NULL
@@ -1667,6 +1689,15 @@ 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
+ * during module load. */
+static struct attribute *dme1737_pwm_misc_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,
+};
+
 /* The following structs hold the fan attributes, some of which are optional.
 /* The following structs hold the fan 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. */
@@ -1722,31 +1753,23 @@ static const struct attribute_group dme1737_fan_group[] = {
 	{ .attrs = dme1737_fan6_attr },
 	{ .attrs = dme1737_fan6_attr },
 };
 };
 
 
-/* The permissions of all of the following attributes are changed to read-
+/* The permissions of the following zone attributes are changed to read-
  * writeable if the chip is *not* locked. Otherwise they stay read-only. */
  * writeable if the chip is *not* locked. Otherwise they stay read-only. */
-static struct attribute *dme1737_misc_chmod_attr[] = {
-	/* Temperatures */
-	&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,
+static struct attribute *dme1737_zone_chmod_attr[] = {
 	&sensor_dev_attr_zone1_auto_point1_temp.dev_attr.attr,
 	&sensor_dev_attr_zone1_auto_point1_temp.dev_attr.attr,
 	&sensor_dev_attr_zone1_auto_point2_temp.dev_attr.attr,
 	&sensor_dev_attr_zone1_auto_point2_temp.dev_attr.attr,
 	&sensor_dev_attr_zone1_auto_point3_temp.dev_attr.attr,
 	&sensor_dev_attr_zone1_auto_point3_temp.dev_attr.attr,
-	&sensor_dev_attr_zone2_auto_point1_temp_hyst.dev_attr.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,
-	&sensor_dev_attr_zone3_auto_point1_temp_hyst.dev_attr.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_misc_chmod_group = {
-	.attrs = dme1737_misc_chmod_attr,
+static const struct attribute_group dme1737_zone_chmod_group = {
+	.attrs = dme1737_zone_chmod_attr,
 };
 };
 
 
 /* The permissions of the following PWM attributes are changed to read-
 /* The permissions of the following PWM attributes are changed to read-
@@ -1757,7 +1780,6 @@ static struct attribute *dme1737_pwm1_chmod_attr[] = {
 	&sensor_dev_attr_pwm1_enable.dev_attr.attr,
 	&sensor_dev_attr_pwm1_enable.dev_attr.attr,
 	&sensor_dev_attr_pwm1_ramp_rate.dev_attr.attr,
 	&sensor_dev_attr_pwm1_ramp_rate.dev_attr.attr,
 	&sensor_dev_attr_pwm1_auto_channels_zone.dev_attr.attr,
 	&sensor_dev_attr_pwm1_auto_channels_zone.dev_attr.attr,
-	&sensor_dev_attr_pwm1_auto_pwm_min.dev_attr.attr,
 	&sensor_dev_attr_pwm1_auto_point1_pwm.dev_attr.attr,
 	&sensor_dev_attr_pwm1_auto_point1_pwm.dev_attr.attr,
 	NULL
 	NULL
 };
 };
@@ -1766,7 +1788,6 @@ static struct attribute *dme1737_pwm2_chmod_attr[] = {
 	&sensor_dev_attr_pwm2_enable.dev_attr.attr,
 	&sensor_dev_attr_pwm2_enable.dev_attr.attr,
 	&sensor_dev_attr_pwm2_ramp_rate.dev_attr.attr,
 	&sensor_dev_attr_pwm2_ramp_rate.dev_attr.attr,
 	&sensor_dev_attr_pwm2_auto_channels_zone.dev_attr.attr,
 	&sensor_dev_attr_pwm2_auto_channels_zone.dev_attr.attr,
-	&sensor_dev_attr_pwm2_auto_pwm_min.dev_attr.attr,
 	&sensor_dev_attr_pwm2_auto_point1_pwm.dev_attr.attr,
 	&sensor_dev_attr_pwm2_auto_point1_pwm.dev_attr.attr,
 	NULL
 	NULL
 };
 };
@@ -1775,7 +1796,6 @@ static struct attribute *dme1737_pwm3_chmod_attr[] = {
 	&sensor_dev_attr_pwm3_enable.dev_attr.attr,
 	&sensor_dev_attr_pwm3_enable.dev_attr.attr,
 	&sensor_dev_attr_pwm3_ramp_rate.dev_attr.attr,
 	&sensor_dev_attr_pwm3_ramp_rate.dev_attr.attr,
 	&sensor_dev_attr_pwm3_auto_channels_zone.dev_attr.attr,
 	&sensor_dev_attr_pwm3_auto_channels_zone.dev_attr.attr,
-	&sensor_dev_attr_pwm3_auto_pwm_min.dev_attr.attr,
 	&sensor_dev_attr_pwm3_auto_point1_pwm.dev_attr.attr,
 	&sensor_dev_attr_pwm3_auto_point1_pwm.dev_attr.attr,
 	NULL
 	NULL
 };
 };
@@ -1875,9 +1895,17 @@ static void dme1737_remove_files(struct device *dev)
 		if (data->has_pwm & (1 << ix)) {
 		if (data->has_pwm & (1 << 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) {
+				sysfs_remove_file(&dev->kobj,
+						  dme1737_pwm_misc_attr[ix]);
+			}
 		}
 		}
 	}
 	}
 
 
+	if (data->type != sch5027) {
+		sysfs_remove_group(&dev->kobj, &dme1737_misc_group);
+	}
+
 	sysfs_remove_group(&dev->kobj, &dme1737_group);
 	sysfs_remove_group(&dev->kobj, &dme1737_group);
 
 
 	if (!data->client.driver) {
 	if (!data->client.driver) {
@@ -1901,6 +1929,13 @@ static int dme1737_create_files(struct device *dev)
 		goto exit_remove;
 		goto exit_remove;
 	}
 	}
 
 
+	/* Create misc sysfs attributes */
+	if ((data->type != sch5027) &&
+	    (err = sysfs_create_group(&dev->kobj,
+				      &dme1737_misc_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_fan & (1 << ix)) {
@@ -1918,6 +1953,11 @@ static int dme1737_create_files(struct device *dev)
 						&dme1737_pwm_group[ix]))) {
 						&dme1737_pwm_group[ix]))) {
 				goto exit_remove;
 				goto exit_remove;
 			}
 			}
+			if (data->type != sch5027 && ix < 3 &&
+			    (err = sysfs_create_file(&dev->kobj,
+						dme1737_pwm_misc_attr[ix]))) {
+				goto exit_remove;
+			}
 		}
 		}
 	}
 	}
 
 
@@ -1927,16 +1967,27 @@ static int dme1737_create_files(struct device *dev)
 		dev_info(dev, "Device is locked. Some attributes "
 		dev_info(dev, "Device is locked. Some attributes "
 			 "will be read-only.\n");
 			 "will be read-only.\n");
 	} else {
 	} else {
-		/* Change permissions of standard sysfs attributes */
-		dme1737_chmod_group(dev, &dme1737_misc_chmod_group,
+		/* Change permissions of zone sysfs attributes */
+		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,
+					    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_pwm & (1 << 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) {
+					dme1737_chmod_file(dev,
+						dme1737_pwm_misc_attr[ix],
+						S_IRUGO | S_IWUSR);
+				}
 			}
 			}
 		}
 		}
 
 
@@ -1966,6 +2017,9 @@ static int dme1737_init_device(struct device *dev)
 	int ix;
 	int ix;
 	u8 reg;
 	u8 reg;
 
 
+	/* Point to the right nominal voltages array */
+	data->in_nominal = IN_NOMINAL(data->type);
+
 	data->config = dme1737_read(client, DME1737_REG_CONFIG);
 	data->config = dme1737_read(client, DME1737_REG_CONFIG);
 	/* Inform if part is not monitoring/started */
 	/* Inform if part is not monitoring/started */
 	if (!(data->config & 0x01)) {
 	if (!(data->config & 0x01)) {
@@ -2076,7 +2130,9 @@ 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 */
-	data->vrm = vid_which_vrm();
+	if (data->type != sch5027) {
+		data->vrm = vid_which_vrm();
+	}
 
 
 	return 0;
 	return 0;
 }
 }
@@ -2095,9 +2151,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 DME1737 can return either 0x78 or 0x77 as its device ID.
+	 * The SCH5027 returns 0x89 as its device ID. */
 	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)) {
+	if (!(reg == 0x77 || reg == 0x78 || reg == 0x89)) {
 		err = -ENODEV;
 		err = -ENODEV;
 		goto exit;
 		goto exit;
 	}
 	}
@@ -2166,15 +2223,24 @@ static int dme1737_i2c_detect(struct i2c_adapter *adapter, int address,
 		company = dme1737_read(client, DME1737_REG_COMPANY);
 		company = dme1737_read(client, DME1737_REG_COMPANY);
 		verstep = dme1737_read(client, DME1737_REG_VERSTEP);
 		verstep = dme1737_read(client, DME1737_REG_VERSTEP);
 
 
-		if (!((company == DME1737_COMPANY_SMSC) &&
-		      ((verstep & DME1737_VERSTEP_MASK) == DME1737_VERSTEP))) {
+		if (company == DME1737_COMPANY_SMSC &&
+		    (verstep & DME1737_VERSTEP_MASK) == DME1737_VERSTEP) {
+			kind = dme1737;
+		} else if (company == DME1737_COMPANY_SMSC &&
+			   verstep == SCH5027_VERSTEP) {
+			kind = sch5027;
+		} else {
 			err = -ENODEV;
 			err = -ENODEV;
 			goto exit_kfree;
 			goto exit_kfree;
 		}
 		}
 	}
 	}
 
 
-	kind = dme1737;
-	name = "dme1737";
+	if (kind == sch5027) {
+		name = "sch5027";
+	} else {
+		kind = dme1737;
+		name = "dme1737";
+	}
 	data->type = kind;
 	data->type = kind;
 
 
 	/* Fill in the remaining client fields and put it into the global
 	/* Fill in the remaining client fields and put it into the global
@@ -2187,8 +2253,9 @@ static int dme1737_i2c_detect(struct i2c_adapter *adapter, int address,
 		goto exit_kfree;
 		goto exit_kfree;
 	}
 	}
 
 
-	dev_info(dev, "Found a DME1737 chip at 0x%02x (rev 0x%02x).\n",
-		 client->addr, verstep);
+	dev_info(dev, "Found a %s chip at 0x%02x (rev 0x%02x).\n",
+		 kind == sch5027 ? "SCH5027" : "DME1737", client->addr,
+		 verstep);
 
 
 	/* Initialize the DME1737 chip */
 	/* Initialize the DME1737 chip */
 	if ((err = dme1737_init_device(dev))) {
 	if ((err = dme1737_init_device(dev))) {
@@ -2371,7 +2438,7 @@ static int __devinit dme1737_isa_probe(struct platform_device *pdev)
 			goto exit_kfree;
 			goto exit_kfree;
 		}
 		}
 	}
 	}
-	data->type = -1;
+	data->type = sch311x;
 
 
 	/* Fill in the remaining client fields and initialize the mutex */
 	/* Fill in the remaining client fields and initialize the mutex */
 	strlcpy(client->name, "sch311x", I2C_NAME_SIZE);
 	strlcpy(client->name, "sch311x", I2C_NAME_SIZE);