--- /home/nicos/test/linux-2.6.24.5/drivers/hwmon/f71882fg.c	2008-04-19 03:53:39.000000000 +0200
+++ drivers/hwmon/f71882fg.c	2008-06-05 00:07:08.000000000 +0200
@@ -57,8 +57,11 @@
 #define F71882FG_REG_IN1_HIGH		0x32
 
 #define F71882FG_REG_FAN(nr)		(0xA0 + (16 * (nr)))
+#define F71882FG_REG_FAN_SPEED(nr)	(0xA2 + (16 * (nr)))
 #define F71882FG_REG_FAN_STATUS		0x92
 #define F71882FG_REG_FAN_BEEP		0x93
+#define F71882FG_REG_FAN_TYPE		0x94
+#define F71882FG_REG_FAN_MODE		0x96
 
 #define F71882FG_REG_TEMP(nr)		(0x72 + 2 * (nr))
 #define F71882FG_REG_TEMP_OVT(nr)	(0x82 + 2 * (nr))
@@ -102,6 +105,9 @@
 	u16	fan[4];
 	u8	fan_status;
 	u8	fan_beep;
+	u8	fan_type;
+	u8	fan_mode;
+	u16	fan_speed[4];
 	u8	temp[3];
 	u8	temp_ovt[3];
 	u8	temp_high[3];
@@ -132,12 +138,25 @@
 /* Sysfs Fan */
 static ssize_t show_fan(struct device *dev, struct device_attribute *devattr,
 	char *buf);
+static ssize_t show_fan_type(struct device *dev, struct device_attribute
+	*devattr, char *buf);
+static ssize_t show_fan_mode(struct device *dev, struct device_attribute
+	*devattr, char *buf);
 static ssize_t show_fan_beep(struct device *dev, struct device_attribute
 	*devattr, char *buf);
 static ssize_t store_fan_beep(struct device *dev, struct device_attribute
 	*devattr, const char *buf, size_t count);
+static ssize_t show_fan_speed(struct device *dev, struct device_attribute
+	*devattr, char *buf);
+static ssize_t store_fan_speed(struct device *dev, struct device_attribute
+	*devattr, const char *buf, size_t count);
 static ssize_t show_fan_alarm(struct device *dev, struct device_attribute
 	*devattr, char *buf);
+/* Sysfs Fan PWM */
+static ssize_t show_pwm(struct device *dev, struct device_attribute
+	*devattr, char *buf);
+static ssize_t store_pwm(struct device *dev, struct device_attribute
+	*devattr, const char *buf, size_t count);
 /* Sysfs Temp */
 static ssize_t show_temp(struct device *dev, struct device_attribute
 	*devattr, char *buf);
@@ -251,18 +270,38 @@
 	SENSOR_ATTR(fan1_beep, S_IRUGO|S_IWUSR, show_fan_beep,
 		store_fan_beep, 0),
 	SENSOR_ATTR(fan1_alarm, S_IRUGO, show_fan_alarm, NULL, 0),
+	SENSOR_ATTR(fan1_type, S_IRUGO, show_fan_type, NULL, 0),
+	SENSOR_ATTR(fan1_mode, S_IRUGO, show_fan_mode, NULL, 0),
+	SENSOR_ATTR(fan1_speed, S_IRUGO|S_IWUSR, show_fan_speed,
+		store_fan_speed, 0),
+	SENSOR_ATTR(pwm1, S_IRUGO|S_IWUSR, show_pwm, store_pwm, 0),
 	SENSOR_ATTR(fan2_input, S_IRUGO, show_fan, NULL, 1),
 	SENSOR_ATTR(fan2_beep, S_IRUGO|S_IWUSR, show_fan_beep,
 		store_fan_beep, 1),
 	SENSOR_ATTR(fan2_alarm, S_IRUGO, show_fan_alarm, NULL, 1),
+	SENSOR_ATTR(fan2_type, S_IRUGO, show_fan_type, NULL, 1),
+	SENSOR_ATTR(fan2_mode, S_IRUGO, show_fan_mode, NULL, 1),
+	SENSOR_ATTR(fan2_speed, S_IRUGO|S_IWUSR, show_fan_speed,
+		store_fan_speed, 1),
+	SENSOR_ATTR(pwm2, S_IRUGO|S_IWUSR, show_pwm, store_pwm, 1),
 	SENSOR_ATTR(fan3_input, S_IRUGO, show_fan, NULL, 2),
 	SENSOR_ATTR(fan3_beep, S_IRUGO|S_IWUSR, show_fan_beep,
 		store_fan_beep, 2),
 	SENSOR_ATTR(fan3_alarm, S_IRUGO, show_fan_alarm, NULL, 2),
+	SENSOR_ATTR(fan3_type, S_IRUGO, show_fan_type, NULL, 2),
+	SENSOR_ATTR(fan3_mode, S_IRUGO, show_fan_mode, NULL, 2),
+	SENSOR_ATTR(fan3_speed, S_IRUGO|S_IWUSR, show_fan_speed,
+		store_fan_speed, 2),
+	SENSOR_ATTR(pwm3, S_IRUGO|S_IWUSR, show_pwm, store_pwm, 2),
 	SENSOR_ATTR(fan4_input, S_IRUGO, show_fan, NULL, 3),
 	SENSOR_ATTR(fan4_beep, S_IRUGO|S_IWUSR, show_fan_beep,
 		store_fan_beep, 3),
-	SENSOR_ATTR(fan4_alarm, S_IRUGO, show_fan_alarm, NULL, 3)
+	SENSOR_ATTR(fan4_alarm, S_IRUGO, show_fan_alarm, NULL, 3),
+	SENSOR_ATTR(fan4_type, S_IRUGO, show_fan_type, NULL, 3),
+	SENSOR_ATTR(fan4_mode, S_IRUGO, show_fan_mode, NULL, 3),
+	SENSOR_ATTR(fan4_speed, S_IRUGO|S_IWUSR, show_fan_speed,
+		store_fan_speed, 3),
+	SENSOR_ATTR(pwm4, S_IRUGO|S_IWUSR, show_pwm, store_pwm, 3),
 };
 
 
@@ -379,6 +418,8 @@
 		data->temp_beep = f71882fg_read8(data, F71882FG_REG_TEMP_BEEP);
 
 		data->fan_beep = f71882fg_read8(data, F71882FG_REG_FAN_BEEP);
+		data->fan_type = f71882fg_read8(data, F71882FG_REG_FAN_TYPE);
+		data->fan_mode = f71882fg_read8(data, F71882FG_REG_FAN_MODE);
 
 		data->last_limits = jiffies;
 	}
@@ -395,9 +436,12 @@
 
 		data->fan_status = f71882fg_read8(data,
 						F71882FG_REG_FAN_STATUS);
-		for (nr = 0; nr < 4; nr++)
+		for (nr = 0; nr < 4; nr++) {
 			data->fan[nr] = f71882fg_read16(data,
 						F71882FG_REG_FAN(nr));
+			data->fan_speed[nr] = f71882fg_read16(data,
+						F71882FG_REG_FAN_SPEED(nr));
+		}
 
 		data->in_status = f71882fg_read8(data,
 						F71882FG_REG_IN_STATUS);
@@ -471,6 +515,130 @@
 		return sprintf(buf, "0\n");
 }
 
+static ssize_t show_fan_type(struct device *dev, struct device_attribute
+	*devattr, char *buf)
+{
+	struct f71882fg_data *data = f71882fg_update_device(dev);
+	int nr = to_sensor_dev_attr(devattr)->index;
+
+	/* 2-bit values */
+	switch(data->fan_type & (3 << (2*nr)) >> (2*nr)) {
+	  case 0:
+		return sprintf(buf, "0 PWM (pushpull)\n");
+	  case 1:
+		return sprintf(buf, "1 linear\n");
+	  case 2:
+		return sprintf(buf, "2 PWM (open drain, 4-wire)\n");
+	  default:
+		return sprintf(buf, "3 invalid\n");
+	}
+}
+
+static ssize_t show_fan_mode(struct device *dev, struct device_attribute
+	*devattr, char *buf)
+{
+	struct f71882fg_data *data = f71882fg_update_device(dev);
+	int nr = to_sensor_dev_attr(devattr)->index;
+
+	/* 2-bit values */
+	switch(data->fan_mode & (3 << (2*nr)) >> (2*nr)) {
+	  case 0:
+		return sprintf(buf, "0 auto (RPM)\n");
+	  case 1:
+		return sprintf(buf, "1 auto (PWM)\n");
+	  case 2:
+		return sprintf(buf, "2 manual (RPM)\n");
+	  default:
+		return sprintf(buf, "3 manual (PWM)\n");
+	}
+}
+
+static ssize_t show_fan_speed(struct device *dev, struct device_attribute
+	*devattr, char *buf)
+{
+	struct f71882fg_data *data = f71882fg_update_device(dev);
+	int nr = to_sensor_dev_attr(devattr)->index;
+
+	/* 2-bit values */
+	return sprintf(buf, "%hu\n", data->fan_speed[nr]);
+}
+
+static ssize_t show_pwm(struct device *dev, struct device_attribute *devattr,
+	char *buf)
+{
+	struct f71882fg_data *data = f71882fg_update_device(dev);
+	int nr = to_sensor_dev_attr(devattr)->index;
+	int speed = fan_from_reg(data->fan[nr]);
+
+	u8 fan_mode = (data->fan_mode & (3 << (2*nr))) >> 2*nr;
+
+	if (speed == FAN_MIN_DETECT)
+		speed = 0;
+
+	if(fan_mode % 2 == 0) {
+		/* no PWM */
+		return sprintf(buf, "0\n");
+	} else {
+		return sprintf(buf, "%hu\n", data->fan_speed[nr]);
+	}
+}
+
+static ssize_t store_fan_speed(struct device *dev, struct device_attribute
+	*devattr, const char *buf, size_t count)
+{
+	struct f71882fg_data *data = dev_get_drvdata(dev);
+	int nr = to_sensor_dev_attr(devattr)->index;
+	int val = (u16)simple_strtoul(buf, NULL, 10);
+	u8 msb, lsb;
+
+	u8 fan_mode = (data->fan_mode & (3 << (2*nr))) >> 2*nr;
+	if(fan_mode <= 1) {
+		/* auto mode, register is read-only */
+		printk(KERN_INFO DRVNAME ": not setting speed of fan #%d: fan mode is auto\n", nr+1);
+		return count;
+	}
+
+	mutex_lock(&data->update_lock);
+	data->fan_speed[nr] = val;
+	msb = (u8)(data->fan_speed[nr] >> 8);
+	lsb = (u8)(data->fan_speed[nr] & 0xff);
+	if(fan_mode % 2 == 0) {
+		/* only write MSB in RPM mode */
+		printk(KERN_INFO DRVNAME ": set speed of fan #%d: msb=%d\n", nr+1, (int)lsb);
+		f71882fg_write8(data, F71882FG_REG_FAN_SPEED(nr), msb);
+	}
+	printk(KERN_INFO DRVNAME ": set speed of fan #%d: lsb=%d\n", nr+1, (int)lsb);
+	f71882fg_write8(data, F71882FG_REG_FAN_SPEED(nr)+1, lsb);
+	mutex_unlock(&data->update_lock);
+
+	return count;
+}
+
+static ssize_t store_pwm(struct device *dev, struct device_attribute
+	*devattr, const char *buf, size_t count)
+{
+	struct f71882fg_data *data = dev_get_drvdata(dev);
+	int nr = to_sensor_dev_attr(devattr)->index;
+	int val = (u16)simple_strtoul(buf, NULL, 10);
+	u8 lsb;
+
+	u8 fan_mode = (data->fan_mode & (3 << (2*nr))) >> 2*nr;
+
+	if(fan_mode != 3) {
+		/* no manual PWM */
+		printk(KERN_INFO DRVNAME ": not setting speed of fan #%d: fan mode is auto or RPM\n", nr+1);
+		return count;
+	}
+
+	mutex_lock(&data->update_lock);
+	data->fan_speed[nr] = val;
+	lsb = (u8)(data->fan_speed[nr] & 0xff);
+	f71882fg_write8(data, F71882FG_REG_FAN_SPEED(nr)+1, lsb);
+	mutex_unlock(&data->update_lock);
+
+	return count;
+}
+
 static ssize_t show_in(struct device *dev, struct device_attribute *devattr,
 	char *buf)
 {

