[PATCH 2.6.25.4] f71882.c driver, Added PWM/FAN control.

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Hello Hans,

I've finisched the two patches, so here they are.

Regards,

Mark.


--- /tmp/linux-2.6.25.4/drivers/hwmon/f71882fg.c	2008-05-15 17:00:12.000000000 +0200
+++ drivers/hwmon/f71882fg.c	2008-06-30 21:02:45.000000000 +0200
@@ -194,79 +194,79 @@
 	__ATTR( name, S_IRUGO, show_name, NULL ),
 };
 
-static struct sensor_device_attribute f71882fg_in_temp_attr[] =
+static struct sensor_device_attribute_2 f71882fg_in_temp_attr[] =
 {
-	SENSOR_ATTR(in0_input, S_IRUGO, show_in, NULL, 0),
-	SENSOR_ATTR(in1_input, S_IRUGO, show_in, NULL, 1),
-	SENSOR_ATTR(in1_max, S_IRUGO|S_IWUSR, show_in_max, store_in_max, 1),
-	SENSOR_ATTR(in1_beep, S_IRUGO|S_IWUSR, show_in_beep, store_in_beep, 1),
-	SENSOR_ATTR(in1_alarm, S_IRUGO, show_in_alarm, NULL, 1),
-	SENSOR_ATTR(in2_input, S_IRUGO, show_in, NULL, 2),
-	SENSOR_ATTR(in3_input, S_IRUGO, show_in, NULL, 3),
-	SENSOR_ATTR(in4_input, S_IRUGO, show_in, NULL, 4),
-	SENSOR_ATTR(in5_input, S_IRUGO, show_in, NULL, 5),
-	SENSOR_ATTR(in6_input, S_IRUGO, show_in, NULL, 6),
-	SENSOR_ATTR(in7_input, S_IRUGO, show_in, NULL, 7),
-	SENSOR_ATTR(in8_input, S_IRUGO, show_in, NULL, 8),
-	SENSOR_ATTR(temp1_input, S_IRUGO, show_temp, NULL, 0),
-	SENSOR_ATTR(temp1_max, S_IRUGO|S_IWUSR, show_temp_max,
-		store_temp_max, 0),
-	SENSOR_ATTR(temp1_max_hyst, S_IRUGO|S_IWUSR, show_temp_max_hyst,
-		store_temp_max_hyst, 0),
-	SENSOR_ATTR(temp1_crit, S_IRUGO|S_IWUSR, show_temp_crit,
-		store_temp_crit, 0),
-	SENSOR_ATTR(temp1_crit_hyst, S_IRUGO, show_temp_crit_hyst, NULL, 0),
-	SENSOR_ATTR(temp1_type, S_IRUGO, show_temp_type, NULL, 0),
-	SENSOR_ATTR(temp1_beep, S_IRUGO|S_IWUSR, show_temp_beep,
-		store_temp_beep, 0),
-	SENSOR_ATTR(temp1_alarm, S_IRUGO, show_temp_alarm, NULL, 0),
-	SENSOR_ATTR(temp1_fault, S_IRUGO, show_temp_fault, NULL, 0),
-	SENSOR_ATTR(temp2_input, S_IRUGO, show_temp, NULL, 1),
-	SENSOR_ATTR(temp2_max, S_IRUGO|S_IWUSR, show_temp_max,
-		store_temp_max, 1),
-	SENSOR_ATTR(temp2_max_hyst, S_IRUGO|S_IWUSR, show_temp_max_hyst,
-		store_temp_max_hyst, 1),
-	SENSOR_ATTR(temp2_crit, S_IRUGO|S_IWUSR, show_temp_crit,
-		store_temp_crit, 1),
-	SENSOR_ATTR(temp2_crit_hyst, S_IRUGO, show_temp_crit_hyst, NULL, 1),
-	SENSOR_ATTR(temp2_type, S_IRUGO, show_temp_type, NULL, 1),
-	SENSOR_ATTR(temp2_beep, S_IRUGO|S_IWUSR, show_temp_beep,
-		store_temp_beep, 1),
-	SENSOR_ATTR(temp2_alarm, S_IRUGO, show_temp_alarm, NULL, 1),
-	SENSOR_ATTR(temp2_fault, S_IRUGO, show_temp_fault, NULL, 1),
-	SENSOR_ATTR(temp3_input, S_IRUGO, show_temp, NULL, 2),
-	SENSOR_ATTR(temp3_max, S_IRUGO|S_IWUSR, show_temp_max,
-		store_temp_max, 2),
-	SENSOR_ATTR(temp3_max_hyst, S_IRUGO|S_IWUSR, show_temp_max_hyst,
-		store_temp_max_hyst, 2),
-	SENSOR_ATTR(temp3_crit, S_IRUGO|S_IWUSR, show_temp_crit,
-		store_temp_crit, 2),
-	SENSOR_ATTR(temp3_crit_hyst, S_IRUGO, show_temp_crit_hyst, NULL, 2),
-	SENSOR_ATTR(temp3_type, S_IRUGO, show_temp_type, NULL, 2),
-	SENSOR_ATTR(temp3_beep, S_IRUGO|S_IWUSR, show_temp_beep,
-		store_temp_beep, 2),
-	SENSOR_ATTR(temp3_alarm, S_IRUGO, show_temp_alarm, NULL, 2),
-	SENSOR_ATTR(temp3_fault, S_IRUGO, show_temp_fault, NULL, 2)
+	SENSOR_ATTR_2(in0_input, S_IRUGO, show_in, NULL, 0, 0),
+	SENSOR_ATTR_2(in1_input, S_IRUGO, show_in, NULL, 1, 0),
+	SENSOR_ATTR_2(in1_max, S_IRUGO|S_IWUSR, show_in_max, store_in_max, 1, 0),
+	SENSOR_ATTR_2(in1_beep, S_IRUGO|S_IWUSR, show_in_beep, store_in_beep, 1, 0),
+	SENSOR_ATTR_2(in1_alarm, S_IRUGO, show_in_alarm, NULL, 1, 0),
+	SENSOR_ATTR_2(in2_input, S_IRUGO, show_in, NULL, 2, 0),
+	SENSOR_ATTR_2(in3_input, S_IRUGO, show_in, NULL, 3, 0),
+	SENSOR_ATTR_2(in4_input, S_IRUGO, show_in, NULL, 4, 0),
+	SENSOR_ATTR_2(in5_input, S_IRUGO, show_in, NULL, 5, 0),
+	SENSOR_ATTR_2(in6_input, S_IRUGO, show_in, NULL, 6, 0),
+	SENSOR_ATTR_2(in7_input, S_IRUGO, show_in, NULL, 7, 0),
+	SENSOR_ATTR_2(in8_input, S_IRUGO, show_in, NULL, 8, 0),
+	SENSOR_ATTR_2(temp1_input, S_IRUGO, show_temp, NULL, 0, 0),
+	SENSOR_ATTR_2(temp1_max, S_IRUGO|S_IWUSR, show_temp_max,
+		store_temp_max, 0, 0),
+	SENSOR_ATTR_2(temp1_max_hyst, S_IRUGO|S_IWUSR, show_temp_max_hyst,
+		store_temp_max_hyst, 0, 0),
+	SENSOR_ATTR_2(temp1_crit, S_IRUGO|S_IWUSR, show_temp_crit,
+		store_temp_crit, 0, 0),
+	SENSOR_ATTR_2(temp1_crit_hyst, S_IRUGO, show_temp_crit_hyst, NULL, 0, 0),
+	SENSOR_ATTR_2(temp1_type, S_IRUGO, show_temp_type, NULL, 0, 0),
+	SENSOR_ATTR_2(temp1_beep, S_IRUGO|S_IWUSR, show_temp_beep,
+		store_temp_beep, 0, 0),
+	SENSOR_ATTR_2(temp1_alarm, S_IRUGO, show_temp_alarm, NULL, 0, 0),
+	SENSOR_ATTR_2(temp1_fault, S_IRUGO, show_temp_fault, NULL, 0, 0),
+	SENSOR_ATTR_2(temp2_input, S_IRUGO, show_temp, NULL, 1, 0),
+	SENSOR_ATTR_2(temp2_max, S_IRUGO|S_IWUSR, show_temp_max,
+		store_temp_max, 1, 0),
+	SENSOR_ATTR_2(temp2_max_hyst, S_IRUGO|S_IWUSR, show_temp_max_hyst,
+		store_temp_max_hyst, 1, 0),
+	SENSOR_ATTR_2(temp2_crit, S_IRUGO|S_IWUSR, show_temp_crit,
+		store_temp_crit, 1, 0),
+	SENSOR_ATTR_2(temp2_crit_hyst, S_IRUGO, show_temp_crit_hyst, NULL, 1, 0),
+	SENSOR_ATTR_2(temp2_type, S_IRUGO, show_temp_type, NULL, 1, 0),
+	SENSOR_ATTR_2(temp2_beep, S_IRUGO|S_IWUSR, show_temp_beep,
+		store_temp_beep, 1, 0),
+	SENSOR_ATTR_2(temp2_alarm, S_IRUGO, show_temp_alarm, NULL, 1, 0),
+	SENSOR_ATTR_2(temp2_fault, S_IRUGO, show_temp_fault, NULL, 1, 0),
+	SENSOR_ATTR_2(temp3_input, S_IRUGO, show_temp, NULL, 2, 0),
+	SENSOR_ATTR_2(temp3_max, S_IRUGO|S_IWUSR, show_temp_max,
+		store_temp_max, 2, 0),
+	SENSOR_ATTR_2(temp3_max_hyst, S_IRUGO|S_IWUSR, show_temp_max_hyst,
+		store_temp_max_hyst, 2, 0),
+	SENSOR_ATTR_2(temp3_crit, S_IRUGO|S_IWUSR, show_temp_crit,
+		store_temp_crit, 2, 0),
+	SENSOR_ATTR_2(temp3_crit_hyst, S_IRUGO, show_temp_crit_hyst, NULL, 2, 0),
+	SENSOR_ATTR_2(temp3_type, S_IRUGO, show_temp_type, NULL, 2, 0),
+	SENSOR_ATTR_2(temp3_beep, S_IRUGO|S_IWUSR, show_temp_beep,
+		store_temp_beep, 2, 0),
+	SENSOR_ATTR_2(temp3_alarm, S_IRUGO, show_temp_alarm, NULL, 2, 0),
+	SENSOR_ATTR_2(temp3_fault, S_IRUGO, show_temp_fault, NULL, 2, 0)
 };
 
-static struct sensor_device_attribute f71882fg_fan_attr[] =
+static struct sensor_device_attribute_2 f71882fg_fan_attr[] =
 {
-	SENSOR_ATTR(fan1_input, S_IRUGO, show_fan, NULL, 0),
-	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(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(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(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_2(fan1_input, S_IRUGO, show_fan, NULL, 0, 0),
+	SENSOR_ATTR_2(fan1_beep, S_IRUGO|S_IWUSR, show_fan_beep,
+		store_fan_beep, 0, 0),
+	SENSOR_ATTR_2(fan1_alarm, S_IRUGO, show_fan_alarm, NULL, 0, 0),
+	SENSOR_ATTR_2(fan2_input, S_IRUGO, show_fan, NULL, 1, 0),
+	SENSOR_ATTR_2(fan2_beep, S_IRUGO|S_IWUSR, show_fan_beep,
+		store_fan_beep, 1, 0),
+	SENSOR_ATTR_2(fan2_alarm, S_IRUGO, show_fan_alarm, NULL, 1, 0),
+	SENSOR_ATTR_2(fan3_input, S_IRUGO, show_fan, NULL, 2, 0),
+	SENSOR_ATTR_2(fan3_beep, S_IRUGO|S_IWUSR, show_fan_beep,
+		store_fan_beep, 2, 0),
+	SENSOR_ATTR_2(fan3_alarm, S_IRUGO, show_fan_alarm, NULL, 2, 0),
+	SENSOR_ATTR_2(fan4_input, S_IRUGO, show_fan, NULL, 3, 0),
+	SENSOR_ATTR_2(fan4_beep, S_IRUGO|S_IWUSR, show_fan_beep,
+		store_fan_beep, 3, 0),
+	SENSOR_ATTR_2(fan4_alarm, S_IRUGO, show_fan_alarm, NULL, 3, 0)
 };
 
 
@@ -423,7 +423,7 @@
 	char *buf)
 {
 	struct f71882fg_data *data = f71882fg_update_device(dev);
-	int nr = to_sensor_dev_attr(devattr)->index;
+	int nr = to_sensor_dev_attr_2(devattr)->index;
 	int speed = fan_from_reg(data->fan[nr]);
 
 	if (speed == FAN_MIN_DETECT)
@@ -436,7 +436,7 @@
 	*devattr, char *buf)
 {
 	struct f71882fg_data *data = f71882fg_update_device(dev);
-	int nr = to_sensor_dev_attr(devattr)->index;
+	int nr = to_sensor_dev_attr_2(devattr)->index;
 
 	if (data->fan_beep & (1 << nr))
 		return sprintf(buf, "1\n");
@@ -448,7 +448,7 @@
 	*devattr, const char *buf, size_t count)
 {
 	struct f71882fg_data *data = dev_get_drvdata(dev);
-	int nr = to_sensor_dev_attr(devattr)->index;
+	int nr = to_sensor_dev_attr_2(devattr)->index;
 	int val = simple_strtoul(buf, NULL, 10);
 
 	mutex_lock(&data->update_lock);
@@ -467,7 +467,7 @@
 	*devattr, char *buf)
 {
 	struct f71882fg_data *data = f71882fg_update_device(dev);
-	int nr = to_sensor_dev_attr(devattr)->index;
+	int nr = to_sensor_dev_attr_2(devattr)->index;
 
 	if (data->fan_status & (1 << nr))
 		return sprintf(buf, "1\n");
@@ -479,7 +479,7 @@
 	char *buf)
 {
 	struct f71882fg_data *data = f71882fg_update_device(dev);
-	int nr = to_sensor_dev_attr(devattr)->index;
+	int nr = to_sensor_dev_attr_2(devattr)->index;
 
 	return sprintf(buf, "%d\n", data->in[nr] * 8);
 }
@@ -513,7 +513,7 @@
 	*devattr, char *buf)
 {
 	struct f71882fg_data *data = f71882fg_update_device(dev);
-	int nr = to_sensor_dev_attr(devattr)->index;
+	int nr = to_sensor_dev_attr_2(devattr)->index;
 
 	if (data->in_beep & (1 << nr))
 		return sprintf(buf, "1\n");
@@ -525,7 +525,7 @@
 	*devattr, const char *buf, size_t count)
 {
 	struct f71882fg_data *data = dev_get_drvdata(dev);
-	int nr = to_sensor_dev_attr(devattr)->index;
+	int nr = to_sensor_dev_attr_2(devattr)->index;
 	int val = simple_strtoul(buf, NULL, 10);
 
 	mutex_lock(&data->update_lock);
@@ -544,7 +544,7 @@
 	*devattr, char *buf)
 {
 	struct f71882fg_data *data = f71882fg_update_device(dev);
-	int nr = to_sensor_dev_attr(devattr)->index;
+	int nr = to_sensor_dev_attr_2(devattr)->index;
 
 	if (data->in_status & (1 << nr))
 		return sprintf(buf, "1\n");
@@ -556,7 +556,7 @@
 	char *buf)
 {
 	struct f71882fg_data *data = f71882fg_update_device(dev);
-	int nr = to_sensor_dev_attr(devattr)->index;
+	int nr = to_sensor_dev_attr_2(devattr)->index;
 
 	return sprintf(buf, "%d\n", data->temp[nr] * 1000);
 }
@@ -565,7 +565,7 @@
 	*devattr, char *buf)
 {
 	struct f71882fg_data *data = f71882fg_update_device(dev);
-	int nr = to_sensor_dev_attr(devattr)->index;
+	int nr = to_sensor_dev_attr_2(devattr)->index;
 
 	return sprintf(buf, "%d\n", data->temp_high[nr] * 1000);
 }
@@ -574,7 +574,7 @@
 	*devattr, const char *buf, size_t count)
 {
 	struct f71882fg_data *data = dev_get_drvdata(dev);
-	int nr = to_sensor_dev_attr(devattr)->index;
+	int nr = to_sensor_dev_attr_2(devattr)->index;
 	int val = simple_strtoul(buf, NULL, 10) / 1000;
 
 	if (val > 255)
@@ -592,7 +592,7 @@
 	*devattr, char *buf)
 {
 	struct f71882fg_data *data = f71882fg_update_device(dev);
-	int nr = to_sensor_dev_attr(devattr)->index;
+	int nr = to_sensor_dev_attr_2(devattr)->index;
 
 	return sprintf(buf, "%d\n",
 		(data->temp_high[nr] - data->temp_hyst[nr]) * 1000);
@@ -602,7 +602,7 @@
 	*devattr, const char *buf, size_t count)
 {
 	struct f71882fg_data *data = dev_get_drvdata(dev);
-	int nr = to_sensor_dev_attr(devattr)->index;
+	int nr = to_sensor_dev_attr_2(devattr)->index;
 	int val = simple_strtoul(buf, NULL, 10) / 1000;
 	ssize_t ret = count;
 
@@ -642,7 +642,7 @@
 	*devattr, char *buf)
 {
 	struct f71882fg_data *data = f71882fg_update_device(dev);
-	int nr = to_sensor_dev_attr(devattr)->index;
+	int nr = to_sensor_dev_attr_2(devattr)->index;
 
 	return sprintf(buf, "%d\n", data->temp_ovt[nr] * 1000);
 }
@@ -651,7 +651,7 @@
 	*devattr, const char *buf, size_t count)
 {
 	struct f71882fg_data *data = dev_get_drvdata(dev);
-	int nr = to_sensor_dev_attr(devattr)->index;
+	int nr = to_sensor_dev_attr_2(devattr)->index;
 	int val = simple_strtoul(buf, NULL, 10) / 1000;
 
 	if (val > 255)
@@ -669,7 +669,7 @@
 	*devattr, char *buf)
 {
 	struct f71882fg_data *data = f71882fg_update_device(dev);
-	int nr = to_sensor_dev_attr(devattr)->index;
+	int nr = to_sensor_dev_attr_2(devattr)->index;
 
 	return sprintf(buf, "%d\n",
 		(data->temp_ovt[nr] - data->temp_hyst[nr]) * 1000);
@@ -679,7 +679,7 @@
 	*devattr, char *buf)
 {
 	struct f71882fg_data *data = f71882fg_update_device(dev);
-	int nr = to_sensor_dev_attr(devattr)->index;
+	int nr = to_sensor_dev_attr_2(devattr)->index;
 
 	return sprintf(buf, "%d\n", data->temp_type[nr]);
 }
@@ -688,7 +688,7 @@
 	*devattr, char *buf)
 {
 	struct f71882fg_data *data = f71882fg_update_device(dev);
-	int nr = to_sensor_dev_attr(devattr)->index;
+	int nr = to_sensor_dev_attr_2(devattr)->index;
 
 	if (data->temp_beep & (1 << (nr + 1)))
 		return sprintf(buf, "1\n");
@@ -700,7 +700,7 @@
 	*devattr, const char *buf, size_t count)
 {
 	struct f71882fg_data *data = dev_get_drvdata(dev);
-	int nr = to_sensor_dev_attr(devattr)->index;
+	int nr = to_sensor_dev_attr_2(devattr)->index;
 	int val = simple_strtoul(buf, NULL, 10);
 
 	mutex_lock(&data->update_lock);
@@ -719,7 +719,7 @@
 	*devattr, char *buf)
 {
 	struct f71882fg_data *data = f71882fg_update_device(dev);
-	int nr = to_sensor_dev_attr(devattr)->index;
+	int nr = to_sensor_dev_attr_2(devattr)->index;
 
 	if (data->temp_status & (1 << (nr + 1)))
 		return sprintf(buf, "1\n");
@@ -731,7 +731,7 @@
 	*devattr, char *buf)
 {
 	struct f71882fg_data *data = f71882fg_update_device(dev);
-	int nr = to_sensor_dev_attr(devattr)->index;
+	int nr = to_sensor_dev_attr_2(devattr)->index;
 
 	if (data->temp_diode_open & (1 << (nr + 1)))
 		return sprintf(buf, "1\n");


--- attr2	2008-06-30 21:02:45.000000000 +0200
+++ f71882fg.c	2008-06-30 21:12:57.000000000 +0200
@@ -57,6 +57,8 @@
 #define F71882FG_REG_IN1_HIGH		0x32
 
 #define F71882FG_REG_FAN(nr)		(0xA0 + (16 * (nr)))
+#define F71882FG_REG_FAN_TARGET(nr)	(0xA2 + (16 * (nr)))
+#define F71882FG_REG_FAN_FULL_SPEED(nr)	(0xA4 + (16 * (nr)))
 #define F71882FG_REG_FAN_STATUS		0x92
 #define F71882FG_REG_FAN_BEEP		0x93
 
@@ -70,6 +72,18 @@
 #define F71882FG_REG_TEMP_TYPE		0x6B
 #define F71882FG_REG_TEMP_DIODE_OPEN	0x6F
 
+
+#define F71882FG_REG_PWM(nr)            (0xA3 + (16 * (nr)))
+#define F71882FG_REG_PWM_TYPE		0x94
+#define F71882FG_REG_PWM_ENABLE         0x96
+
+#define F71882FG_REG_FAN_HYST0		0x98
+#define F71882FG_REG_FAN_HYST1		0x99
+
+#define F71882FG_REG_POINT_PWM(pwm,point)	(0xAA + point + (16 * (pwm)))
+#define F71882FG_REG_POINT_TEMP(pwm,point)	(0xA6 + point + (16 * (pwm)))
+#define F71882FG_REG_POINT_MAPPING(nr)		(0xAF + 16*(nr))
+
 #define	F71882FG_REG_START		0x01
 
 #define FAN_MIN_DETECT			366 /* Lowest detectable fanspeed */
@@ -78,6 +92,12 @@
 module_param(force_id, ushort, 0);
 MODULE_PARM_DESC(force_id, "Override the detected device ID");
 
+static int fan_mode[4]={0, 0, 0, 0};
+module_param_array(fan_mode, int, NULL, 0);
+MODULE_PARM_DESC(fan_mode, 
+	"List of fan modes (for four fans) (0=don't change, 1=pwm, 2=rpm");
+
+
 static struct platform_device *f71882fg_pdev = NULL;
 
 /* Super-I/O Function prototypes */
@@ -88,6 +108,7 @@
 static inline void superio_exit(int base);
 
 static inline u16 fan_from_reg ( u16 reg );
+static inline u16 fan_to_reg ( u16 reg );
 
 struct f71882fg_data {
 	unsigned short addr;
@@ -104,6 +125,8 @@
 	u8	in_status;
 	u8	in_beep;
 	u16	fan[4];
+	u16	fan_target[4];
+	u16	fan_full_speed[4];
 	u8	fan_status;
 	u8	fan_beep;
 	u8	temp[3];
@@ -114,11 +137,19 @@
 	u8	temp_status;
 	u8	temp_beep;
 	u8	temp_diode_open;
+	u8	pwm[4];
+	u8	pwm_enable;
+	u8	pwm_type;
+	u8	pwm_auto_point_hyst[2];
+	u8	pwm_auto_point_mapping[4];
+	u8	pwm_auto_point_pwm[4][5];
+	u8	pwm_auto_point_temp[4][4];
 };
 
 static u8 f71882fg_read8(struct f71882fg_data *data, u8 reg);
 static u16 f71882fg_read16(struct f71882fg_data *data, u8 reg);
 static void f71882fg_write8(struct f71882fg_data *data, u8 reg, u8 val);
+static void f71882fg_write16(struct f71882fg_data *data, u8 reg, u16 val);
 
 /* Sysfs in*/
 static ssize_t show_in(struct device *dev, struct device_attribute *devattr,
@@ -136,6 +167,10 @@
 /* Sysfs Fan */
 static ssize_t show_fan(struct device *dev, struct device_attribute *devattr,
 	char *buf);
+static ssize_t show_fan_full_speed(struct device *dev, 
+	struct device_attribute *devattr, char *buf);
+static ssize_t store_fan_full_speed(struct device *dev, 
+	struct device_attribute *devattr, const char *buf, size_t count);
 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
@@ -173,6 +208,36 @@
 static ssize_t show_name(struct device *dev, struct device_attribute *devattr,
 	char *buf);
 
+/* PWM and Auto point control */
+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);
+static ssize_t show_pwm_enable(struct device *dev, 
+	struct device_attribute *devattr, char *buf);
+static ssize_t store_pwm_enable(struct device *dev, struct device_attribute
+	*devattr, const char *buf, size_t count);
+static ssize_t show_pwm_interpolate(struct device *dev, 
+	struct device_attribute *devattr, char *buf);
+static ssize_t store_pwm_interpolate(struct device *dev, 
+	struct device_attribute *devattr, const char *buf, size_t count);
+static ssize_t show_pwm_auto_point_channel(struct device *dev, 
+	struct device_attribute *devattr, char *buf);
+static ssize_t store_pwm_auto_point_channel(struct device *dev, 
+	struct device_attribute *devattr, const char *buf, size_t count);
+static ssize_t show_pwm_auto_point_temp_hyst(struct device *dev, 
+	struct device_attribute *devattr, char *buf);
+static ssize_t store_pwm_auto_point_temp_hyst(struct device *dev, 
+	struct device_attribute *devattr, const char *buf, size_t count);
+static ssize_t show_pwm_auto_point_pwm(struct device *dev, 
+	struct device_attribute *devattr, char *buf);
+static ssize_t store_pwm_auto_point_pwm(struct device *dev, 
+	struct device_attribute *devattr, const char *buf, size_t count);
+static ssize_t show_pwm_auto_point_temp(struct device *dev, 
+	struct device_attribute *devattr, char *buf);
+static ssize_t store_pwm_auto_point_temp(struct device *dev, 
+	struct device_attribute *devattr, const char *buf, size_t count);
+
 static int __devinit f71882fg_probe(struct platform_device * pdev);
 static int __devexit f71882fg_remove(struct platform_device *pdev);
 static int __init f71882fg_init(void);
@@ -252,21 +317,181 @@
 static struct sensor_device_attribute_2 f71882fg_fan_attr[] =
 {
 	SENSOR_ATTR_2(fan1_input, S_IRUGO, show_fan, NULL, 0, 0),
+	SENSOR_ATTR_2(fan1_full_speed, S_IRUGO|S_IWUSR, show_fan_full_speed, 
+		store_fan_full_speed, 0, 0),
 	SENSOR_ATTR_2(fan1_beep, S_IRUGO|S_IWUSR, show_fan_beep,
 		store_fan_beep, 0, 0),
 	SENSOR_ATTR_2(fan1_alarm, S_IRUGO, show_fan_alarm, NULL, 0, 0),
 	SENSOR_ATTR_2(fan2_input, S_IRUGO, show_fan, NULL, 1, 0),
+	SENSOR_ATTR_2(fan2_full_speed, S_IRUGO|S_IWUSR, show_fan_full_speed, 
+		store_fan_full_speed, 1, 0),
 	SENSOR_ATTR_2(fan2_beep, S_IRUGO|S_IWUSR, show_fan_beep,
 		store_fan_beep, 1, 0),
 	SENSOR_ATTR_2(fan2_alarm, S_IRUGO, show_fan_alarm, NULL, 1, 0),
 	SENSOR_ATTR_2(fan3_input, S_IRUGO, show_fan, NULL, 2, 0),
+	SENSOR_ATTR_2(fan3_full_speed, S_IRUGO|S_IWUSR, show_fan_full_speed, 
+		store_fan_full_speed, 2, 0),
 	SENSOR_ATTR_2(fan3_beep, S_IRUGO|S_IWUSR, show_fan_beep,
 		store_fan_beep, 2, 0),
 	SENSOR_ATTR_2(fan3_alarm, S_IRUGO, show_fan_alarm, NULL, 2, 0),
 	SENSOR_ATTR_2(fan4_input, S_IRUGO, show_fan, NULL, 3, 0),
+	SENSOR_ATTR_2(fan4_full_speed, S_IRUGO|S_IWUSR, show_fan_full_speed, 
+		store_fan_full_speed, 3, 0),
 	SENSOR_ATTR_2(fan4_beep, S_IRUGO|S_IWUSR, show_fan_beep,
 		store_fan_beep, 3, 0),
-	SENSOR_ATTR_2(fan4_alarm, S_IRUGO, show_fan_alarm, NULL, 3, 0)
+	SENSOR_ATTR_2(fan4_alarm, S_IRUGO, show_fan_alarm, NULL, 3, 0),
+
+	SENSOR_ATTR_2(pwm1, S_IRUGO|S_IWUSR, show_pwm, store_pwm, 0, 0),
+	SENSOR_ATTR_2(pwm1_enable, S_IRUGO|S_IWUSR, show_pwm_enable, 
+		store_pwm_enable, 0, 0),
+	SENSOR_ATTR_2(pwm1_interpolate, S_IRUGO|S_IWUSR, 
+		show_pwm_interpolate, store_pwm_interpolate, 0, 0),
+	SENSOR_ATTR_2(pwm1_auto_point1_temp_hyst, S_IRUGO|S_IWUSR, 
+		show_pwm_auto_point_temp_hyst, store_pwm_auto_point_temp_hyst, 
+		0, 0),
+	SENSOR_ATTR_2(pwm1_auto_channels_temp, S_IRUGO|S_IWUSR, 
+		show_pwm_auto_point_channel, store_pwm_auto_point_channel, 0, 0),
+	SENSOR_ATTR_2(pwm1_auto_point1_pwm, S_IRUGO|S_IWUSR, 
+		show_pwm_auto_point_pwm, store_pwm_auto_point_pwm, 
+		0, 0),
+	SENSOR_ATTR_2(pwm1_auto_point2_pwm, S_IRUGO|S_IWUSR, 
+		show_pwm_auto_point_pwm, store_pwm_auto_point_pwm, 
+		0, 1),
+	SENSOR_ATTR_2(pwm1_auto_point3_pwm, S_IRUGO|S_IWUSR, 
+		show_pwm_auto_point_pwm, store_pwm_auto_point_pwm, 
+		0, 2),
+	SENSOR_ATTR_2(pwm1_auto_point4_pwm, S_IRUGO|S_IWUSR, 
+		show_pwm_auto_point_pwm, store_pwm_auto_point_pwm, 
+		0, 3),
+	SENSOR_ATTR_2(pwm1_auto_point5_pwm, S_IRUGO|S_IWUSR, 
+		show_pwm_auto_point_pwm, store_pwm_auto_point_pwm, 
+		0, 4),
+	SENSOR_ATTR_2(pwm1_auto_point1_temp, S_IRUGO|S_IWUSR, 
+		show_pwm_auto_point_temp, store_pwm_auto_point_temp, 
+		0, 0),
+	SENSOR_ATTR_2(pwm1_auto_point2_temp, S_IRUGO|S_IWUSR, 
+		show_pwm_auto_point_temp, store_pwm_auto_point_temp, 
+		0, 1),
+	SENSOR_ATTR_2(pwm1_auto_point3_temp, S_IRUGO|S_IWUSR, 
+		show_pwm_auto_point_temp, store_pwm_auto_point_temp, 
+		0, 2),
+	SENSOR_ATTR_2(pwm1_auto_point4_temp, S_IRUGO|S_IWUSR, 
+		show_pwm_auto_point_temp, store_pwm_auto_point_temp, 
+		0, 3),
+
+	SENSOR_ATTR_2(pwm2, S_IRUGO|S_IWUSR, show_pwm, store_pwm, 1, 0),
+	SENSOR_ATTR_2(pwm2_enable, S_IRUGO|S_IWUSR, show_pwm_enable, 
+		store_pwm_enable, 1, 0),
+	SENSOR_ATTR_2(pwm2_interpolate, S_IRUGO|S_IWUSR, 
+		show_pwm_interpolate, store_pwm_interpolate, 1, 0),
+	SENSOR_ATTR_2(pwm2_auto_point1_temp_hyst, S_IRUGO|S_IWUSR, 
+		show_pwm_auto_point_temp_hyst, store_pwm_auto_point_temp_hyst, 
+		1, 0),
+	SENSOR_ATTR_2(pwm2_auto_channels_temp, S_IRUGO|S_IWUSR, 
+		show_pwm_auto_point_channel, store_pwm_auto_point_channel, 1, 0),
+	SENSOR_ATTR_2(pwm2_auto_point1_pwm, S_IRUGO|S_IWUSR, 
+		show_pwm_auto_point_pwm, store_pwm_auto_point_pwm, 
+		1, 0),
+	SENSOR_ATTR_2(pwm2_auto_point2_pwm, S_IRUGO|S_IWUSR, 
+		show_pwm_auto_point_pwm, store_pwm_auto_point_pwm, 
+		1, 1),
+	SENSOR_ATTR_2(pwm2_auto_point3_pwm, S_IRUGO|S_IWUSR, 
+		show_pwm_auto_point_pwm, store_pwm_auto_point_pwm, 
+		1, 2),
+	SENSOR_ATTR_2(pwm2_auto_point4_pwm, S_IRUGO|S_IWUSR, 
+		show_pwm_auto_point_pwm, store_pwm_auto_point_pwm, 
+		1, 3),
+	SENSOR_ATTR_2(pwm2_auto_point5_pwm, S_IRUGO|S_IWUSR, 
+		show_pwm_auto_point_pwm, store_pwm_auto_point_pwm, 
+		1, 4),
+	SENSOR_ATTR_2(pwm2_auto_point1_temp, S_IRUGO|S_IWUSR, 
+		show_pwm_auto_point_temp, store_pwm_auto_point_temp, 
+		1, 0),
+	SENSOR_ATTR_2(pwm2_auto_point2_temp, S_IRUGO|S_IWUSR, 
+		show_pwm_auto_point_temp, store_pwm_auto_point_temp, 
+		1, 1),
+	SENSOR_ATTR_2(pwm2_auto_point3_temp, S_IRUGO|S_IWUSR, 
+		show_pwm_auto_point_temp, store_pwm_auto_point_temp, 
+		1, 2),
+	SENSOR_ATTR_2(pwm2_auto_point4_temp, S_IRUGO|S_IWUSR, 
+		show_pwm_auto_point_temp, store_pwm_auto_point_temp, 
+		1, 3),
+
+	SENSOR_ATTR_2(pwm3, S_IRUGO|S_IWUSR, show_pwm, store_pwm, 2, 0),
+	SENSOR_ATTR_2(pwm3_enable, S_IRUGO|S_IWUSR, show_pwm_enable, 
+		store_pwm_enable, 2, 0),
+	SENSOR_ATTR_2(pwm3_interpolate, S_IRUGO|S_IWUSR, 
+		show_pwm_interpolate, store_pwm_interpolate, 2, 0),
+	SENSOR_ATTR_2(pwm3_auto_point1_temp_hyst, S_IRUGO|S_IWUSR, 
+		show_pwm_auto_point_temp_hyst, store_pwm_auto_point_temp_hyst, 
+		2, 0),
+	SENSOR_ATTR_2(pwm3_auto_channels_temp, S_IRUGO|S_IWUSR, 
+		show_pwm_auto_point_channel, store_pwm_auto_point_channel, 2, 0),
+	SENSOR_ATTR_2(pwm3_auto_point1_pwm, S_IRUGO|S_IWUSR, 
+		show_pwm_auto_point_pwm, store_pwm_auto_point_pwm, 
+		2, 0),
+	SENSOR_ATTR_2(pwm3_auto_point2_pwm, S_IRUGO|S_IWUSR, 
+		show_pwm_auto_point_pwm, store_pwm_auto_point_pwm, 
+		2, 1),
+	SENSOR_ATTR_2(pwm3_auto_point3_pwm, S_IRUGO|S_IWUSR, 
+		show_pwm_auto_point_pwm, store_pwm_auto_point_pwm, 
+		2, 2),
+	SENSOR_ATTR_2(pwm3_auto_point4_pwm, S_IRUGO|S_IWUSR, 
+		show_pwm_auto_point_pwm, store_pwm_auto_point_pwm, 
+		2, 3),
+	SENSOR_ATTR_2(pwm3_auto_point5_pwm, S_IRUGO|S_IWUSR, 
+		show_pwm_auto_point_pwm, store_pwm_auto_point_pwm, 
+		2, 4),
+	SENSOR_ATTR_2(pwm3_auto_point1_temp, S_IRUGO|S_IWUSR, 
+		show_pwm_auto_point_temp, store_pwm_auto_point_temp, 
+		2, 0),
+	SENSOR_ATTR_2(pwm3_auto_point2_temp, S_IRUGO|S_IWUSR, 
+		show_pwm_auto_point_temp, store_pwm_auto_point_temp, 
+		2, 1),
+	SENSOR_ATTR_2(pwm3_auto_point3_temp, S_IRUGO|S_IWUSR, 
+		show_pwm_auto_point_temp, store_pwm_auto_point_temp, 
+		2, 2),
+	SENSOR_ATTR_2(pwm3_auto_point4_temp, S_IRUGO|S_IWUSR, 
+		show_pwm_auto_point_temp, store_pwm_auto_point_temp, 
+		2, 3),
+
+	SENSOR_ATTR_2(pwm4, S_IRUGO|S_IWUSR, show_pwm, store_pwm, 3, 0),
+	SENSOR_ATTR_2(pwm4_enable, S_IRUGO|S_IWUSR, show_pwm_enable, 
+		store_pwm_enable, 3, 0),
+	SENSOR_ATTR_2(pwm4_interpolate, S_IRUGO|S_IWUSR, 
+		show_pwm_interpolate, store_pwm_interpolate, 3, 0),
+	SENSOR_ATTR_2(pwm4_auto_point1_temp_hyst, S_IRUGO|S_IWUSR, 
+		show_pwm_auto_point_temp_hyst, store_pwm_auto_point_temp_hyst, 
+		3, 0),
+	SENSOR_ATTR_2(pwm4_auto_channels_temp, S_IRUGO|S_IWUSR, 
+		show_pwm_auto_point_channel, store_pwm_auto_point_channel, 3, 0),
+	SENSOR_ATTR_2(pwm4_auto_point1_pwm, S_IRUGO|S_IWUSR, 
+		show_pwm_auto_point_pwm, store_pwm_auto_point_pwm, 
+		3, 0),
+	SENSOR_ATTR_2(pwm4_auto_point2_pwm, S_IRUGO|S_IWUSR, 
+		show_pwm_auto_point_pwm, store_pwm_auto_point_pwm, 
+		3, 1),
+	SENSOR_ATTR_2(pwm4_auto_point3_pwm, S_IRUGO|S_IWUSR, 
+		show_pwm_auto_point_pwm, store_pwm_auto_point_pwm, 
+		3, 2),
+	SENSOR_ATTR_2(pwm4_auto_point4_pwm, S_IRUGO|S_IWUSR, 
+		show_pwm_auto_point_pwm, store_pwm_auto_point_pwm, 
+		3, 3),
+	SENSOR_ATTR_2(pwm4_auto_point5_pwm, S_IRUGO|S_IWUSR, 
+		show_pwm_auto_point_pwm, store_pwm_auto_point_pwm, 
+		3, 4),
+	SENSOR_ATTR_2(pwm4_auto_point1_temp, S_IRUGO|S_IWUSR, 
+		show_pwm_auto_point_temp, store_pwm_auto_point_temp, 
+		3, 0),
+	SENSOR_ATTR_2(pwm4_auto_point2_temp, S_IRUGO|S_IWUSR, 
+		show_pwm_auto_point_temp, store_pwm_auto_point_temp, 
+		3, 1),
+	SENSOR_ATTR_2(pwm4_auto_point3_temp, S_IRUGO|S_IWUSR, 
+		show_pwm_auto_point_temp, store_pwm_auto_point_temp, 
+		3, 2),
+	SENSOR_ATTR_2(pwm4_auto_point4_temp, S_IRUGO|S_IWUSR, 
+		show_pwm_auto_point_temp, store_pwm_auto_point_temp, 
+		3, 3)
 };
 
 
@@ -310,6 +535,11 @@
 	return reg ? (1500000 / reg) : 0;
 }
 
+static inline u16 fan_to_reg(u16 fan)
+{
+	return fan ? (1500000/fan) : 0;
+}
+
 static u8 f71882fg_read8(struct f71882fg_data *data, u8 reg)
 {
 	u8 val;
@@ -338,6 +568,15 @@
 	outb(val, data->addr + DATA_REG_OFFSET);
 }
 
+static void f71882fg_write16(struct f71882fg_data *data, u8 reg, u16 val)
+{
+	outb(reg++, data->addr + ADDR_REG_OFFSET);
+	outb(val>>8, data->addr + DATA_REG_OFFSET);
+	outb(reg, data->addr + ADDR_REG_OFFSET);
+	outb(val&255, data->addr + DATA_REG_OFFSET);
+}
+
+
 static struct f71882fg_data *f71882fg_update_device(struct device * dev)
 {
 	struct f71882fg_data *data = dev_get_drvdata(dev);
@@ -399,9 +638,37 @@
 
 		data->fan_status = f71882fg_read8(data,
 						F71882FG_REG_FAN_STATUS);
-		for (nr = 0; nr < 4; nr++)
+		data->pwm_type = f71882fg_read8(data,
+						F71882FG_REG_PWM_TYPE);
+		data->pwm_enable = f71882fg_read8(data,
+						F71882FG_REG_PWM_ENABLE);
+		data->pwm_auto_point_hyst[0] = f71882fg_read8(data,
+						F71882FG_REG_FAN_HYST0);
+		data->pwm_auto_point_hyst[1] = f71882fg_read8(data,
+						F71882FG_REG_FAN_HYST1);
+		for (nr = 0; nr < 4; nr++) {
+			int point;
 			data->fan[nr] = f71882fg_read16(data,
-						F71882FG_REG_FAN(nr));
+				F71882FG_REG_FAN(nr));
+			data->fan_target[nr] = f71882fg_read16(data,
+				F71882FG_REG_FAN_TARGET(nr));
+			data->fan_full_speed[nr] = f71882fg_read16(data,
+				F71882FG_REG_FAN_FULL_SPEED(nr));
+			data->pwm[nr] = f71882fg_read8(data,
+				F71882FG_REG_PWM(nr));
+			data->pwm_auto_point_mapping[nr] = f71882fg_read8(data,
+				F71882FG_REG_POINT_MAPPING(nr));
+			for(point=0;point<5;point++) {
+				data->pwm_auto_point_pwm[nr][point]=
+					f71882fg_read8(data,
+						F71882FG_REG_POINT_PWM(nr,
+							point));
+				data->pwm_auto_point_temp[nr][point]=
+					f71882fg_read8(data,
+						F71882FG_REG_POINT_TEMP(nr,
+							point));
+			}
+                }
 
 		data->in_status = f71882fg_read8(data,
 						F71882FG_REG_IN_STATUS);
@@ -432,6 +699,34 @@
 	return sprintf(buf, "%d\n", speed);
 }
 
+static ssize_t show_fan_full_speed(struct device *dev, 
+	struct device_attribute *devattr, char *buf)
+{
+	struct f71882fg_data *data = f71882fg_update_device(dev);
+	int nr = to_sensor_dev_attr_2(devattr)->index;
+	int speed = fan_from_reg(data->fan_full_speed[nr]);
+	return sprintf(buf, "%d\n", speed);
+}
+
+static ssize_t store_fan_full_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_2(devattr)->index;
+	int val = simple_strtoul(buf, NULL, 10);
+
+	mutex_lock(&data->update_lock);
+	if(data->pwm_enable&(1<<(2*nr)))
+		count=-EINVAL;
+	else
+		f71882fg_write16(data, F71882FG_REG_FAN_FULL_SPEED(nr), 
+			fan_to_reg(val));
+	mutex_unlock(&data->update_lock);
+
+	return count;
+}
+
+
 static ssize_t show_fan_beep(struct device *dev, struct device_attribute
 	*devattr, char *buf)
 {
@@ -739,6 +1034,276 @@
 		return sprintf(buf, "0\n");
 }
 
+static ssize_t show_pwm(struct device *dev, struct device_attribute *devattr,
+	char *buf)
+{
+	struct f71882fg_data *data = f71882fg_update_device(dev);
+	int val, nr = to_sensor_dev_attr_2(devattr)->index;
+	if(data->pwm_enable&(1<<(2*nr)))
+		// PWM mode
+		val=data->pwm[nr];
+	else
+		// RPM mode
+		val=255*fan_from_reg(data->fan_target[nr])
+			/fan_from_reg(data->fan_full_speed[nr]);
+	return sprintf(buf, "%d\n", val);
+}
+
+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);
+	struct f71882fg_data *data = f71882fg_update_device(dev);
+	int nr = to_sensor_dev_attr_2(devattr)->index;
+	int val = simple_strtoul(buf, NULL, 10);
+
+	mutex_lock(&data->update_lock);
+	if(data->pwm_enable&(1<<(2*nr)))
+		// PWM mode
+		f71882fg_write8(data, F71882FG_REG_PWM(nr), val);
+	else {
+		// RPM mode
+		int target=val*fan_from_reg(data->fan_full_speed[nr])/255;
+		f71882fg_write16(data, F71882FG_REG_FAN_TARGET(nr), 
+			fan_to_reg(target));
+	}
+	mutex_unlock(&data->update_lock);
+
+	return count;
+}
+
+static ssize_t show_pwm_enable(struct device *dev, 
+	struct device_attribute *devattr,
+	char *buf)
+{
+	int result;
+	struct f71882fg_data *data = f71882fg_update_device(dev);
+	int nr = to_sensor_dev_attr_2(devattr)->index;
+
+	if(2&(data->pwm_enable>>(2*nr)))
+		result=1;
+	else
+		result=2;
+
+	return sprintf(buf, "%d\n", result);
+}
+
+static ssize_t store_pwm_enable(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_2(devattr)->index;
+	int val = simple_strtoul(buf, NULL, 10);
+	if(val<0 || val>2)
+		return -EINVAL;
+
+	mutex_lock(&data->update_lock);
+	switch(val) {
+	case 1: data->pwm_enable|=2<<(2*nr); break;	// Manual
+	case 2: data->pwm_enable&=~(2<<(2*nr)); break;	// Temperature ctrl
+	}
+	switch(fan_mode[nr]) {
+	case 1: data->pwm_enable|=1<<(2*nr); break;	// RPM mode
+	case 2: data->pwm_enable&=~(1<<(2*nr)); break;	// Duty cycle mode
+	}
+	f71882fg_write8(data, F71882FG_REG_PWM_ENABLE, data->pwm_enable);
+	mutex_unlock(&data->update_lock);
+	if(!val)
+		store_pwm(dev,devattr,"255",4);
+
+	return count;
+}
+
+static ssize_t show_pwm_auto_point_pwm(struct device *dev, 
+	struct device_attribute *devattr,
+	char *buf)
+{
+	int result;
+	struct f71882fg_data *data = f71882fg_update_device(dev);
+	int pwm = to_sensor_dev_attr_2(devattr)->index;
+	int point = to_sensor_dev_attr_2(devattr)->nr;
+
+	if(data->pwm_enable&(1<<(2*pwm))) {
+		// PWM mode
+		result=data->pwm_auto_point_pwm[pwm][point];
+	} else {
+		// RPM mode
+		result=32*255/(32+data->pwm_auto_point_pwm[pwm][point]);
+	}
+
+	return sprintf(buf, "%d\n", result);
+}
+
+static ssize_t store_pwm_auto_point_pwm(struct device *dev, 
+	struct device_attribute *devattr, 
+	const char *buf, size_t count)
+{
+	// struct f71882fg_data *data = dev_get_drvdata(dev);
+	struct f71882fg_data *data = f71882fg_update_device(dev);
+	int pwm = to_sensor_dev_attr_2(devattr)->index;
+	int point = to_sensor_dev_attr_2(devattr)->nr;
+	int val = simple_strtoul(buf, NULL, 10);
+
+	mutex_lock(&data->update_lock);
+	if(data->pwm_enable&(1<<(2*pwm))) {
+		// PWM mode
+		f71882fg_write8(data, F71882FG_REG_POINT_PWM(pwm,point), val);
+	} else {
+		// RPM mode
+		if(val<29)	// Prevent negative numbers
+			val=255;
+		else
+			val=(255-val)*32/val;
+		f71882fg_write8(data, F71882FG_REG_POINT_PWM(pwm,point),val);
+	}
+	mutex_unlock(&data->update_lock);
+
+	return count;
+}
+
+static ssize_t show_pwm_auto_point_temp_hyst(struct device *dev, 
+	struct device_attribute *devattr,
+	char *buf)
+{
+	int result;
+	struct f71882fg_data *data = f71882fg_update_device(dev);
+	int nr = to_sensor_dev_attr_2(devattr)->index;
+
+	switch(nr) {
+	case 0: result=data->pwm_auto_point_hyst[0]&15; break;
+	case 1: result=data->pwm_auto_point_hyst[0]>>4; break;
+	case 2: result=data->pwm_auto_point_hyst[1]&15; break;
+	case 3: result=data->pwm_auto_point_hyst[1]>>4; break;
+	default: result=-1;
+	}
+
+	return sprintf(buf, "%d\n", result);
+}
+
+static ssize_t store_pwm_auto_point_temp_hyst(struct device *dev, 
+	struct device_attribute *devattr, 
+	const char *buf, size_t count)
+{
+	// struct f71882fg_data *data = dev_get_drvdata(dev);
+	struct f71882fg_data *data = f71882fg_update_device(dev);
+	int nr = to_sensor_dev_attr_2(devattr)->index;
+	int val = simple_strtoul(buf, NULL, 10);
+	val&=15;
+	switch(nr) {
+	case 0: val=(data->pwm_auto_point_hyst[0]&0xf0)|val; break;
+	case 1: val=(data->pwm_auto_point_hyst[0]&0x0f)|(val<<4); break;
+	case 2: val=(data->pwm_auto_point_hyst[1]&0xf0)|val; break;
+	case 3: val=(data->pwm_auto_point_hyst[1]&0x0f)|(val<<4); break;
+	}
+
+	mutex_lock(&data->update_lock);
+	if(nr==0 || nr==1)
+		f71882fg_write8(data, F71882FG_REG_FAN_HYST0, val); 
+	else
+		f71882fg_write8(data, F71882FG_REG_FAN_HYST1, val); 
+	mutex_unlock(&data->update_lock);
+
+	return count;
+}
+
+static ssize_t show_pwm_interpolate(struct device *dev, 
+	struct device_attribute *devattr,
+	char *buf)
+{
+	int result;
+	struct f71882fg_data *data = f71882fg_update_device(dev);
+	int nr = to_sensor_dev_attr_2(devattr)->index;
+
+	result=1&(data->pwm_auto_point_mapping[nr]>>4);
+
+	return sprintf(buf, "%d\n", result);
+}
+
+static ssize_t store_pwm_interpolate(struct device *dev, 
+	struct device_attribute *devattr, 
+	const char *buf, size_t count)
+{
+	// struct f71882fg_data *data = dev_get_drvdata(dev);
+	struct f71882fg_data *data = f71882fg_update_device(dev);
+	int nr = to_sensor_dev_attr_2(devattr)->index;
+	int val = simple_strtoul(buf, NULL, 10);
+	if(val)
+		val=data->pwm_auto_point_mapping[nr]|(1<<4);
+	else
+		val=data->pwm_auto_point_mapping[nr]&(~(1<<4));
+	mutex_lock(&data->update_lock);
+	f71882fg_write8(data, F71882FG_REG_POINT_MAPPING(nr), val); 
+	mutex_unlock(&data->update_lock);
+
+	return count;
+}
+static ssize_t show_pwm_auto_point_channel(struct device *dev, 
+	struct device_attribute *devattr,
+	char *buf)
+{
+	int result;
+	struct f71882fg_data *data = f71882fg_update_device(dev);
+	int nr = to_sensor_dev_attr_2(devattr)->index;
+
+	result=1<<(data->pwm_auto_point_mapping[nr]&3);
+	result>>=1;
+
+	return sprintf(buf, "%d\n", result);
+}
+
+static ssize_t store_pwm_auto_point_channel(struct device *dev, 
+	struct device_attribute *devattr, 
+	const char *buf, size_t count)
+{
+	// struct f71882fg_data *data = dev_get_drvdata(dev);
+	struct f71882fg_data *data = f71882fg_update_device(dev);
+	int nr = to_sensor_dev_attr_2(devattr)->index;
+	int val = simple_strtoul(buf, NULL, 10);
+	switch(val) {
+	case 1: val=1; break;
+	case 2: val=2; break;
+	case 4: val=3; break;
+	default: return -EINVAL;
+	}
+	val|=data->pwm_auto_point_mapping[nr]&0xfc;
+	mutex_lock(&data->update_lock);
+	f71882fg_write8(data, F71882FG_REG_POINT_MAPPING(nr), val); 
+	mutex_unlock(&data->update_lock);
+
+	return count;
+}
+
+static ssize_t show_pwm_auto_point_temp(struct device *dev, 
+	struct device_attribute *devattr,
+	char *buf)
+{
+	int result;
+	struct f71882fg_data *data = f71882fg_update_device(dev);
+	int pwm = to_sensor_dev_attr_2(devattr)->index;
+	int point = to_sensor_dev_attr_2(devattr)->nr;
+
+	result=data->pwm_auto_point_temp[pwm][point];
+	return sprintf(buf, "%d\n", result);
+}
+
+static ssize_t store_pwm_auto_point_temp(struct device *dev, 
+	struct device_attribute *devattr, 
+	const char *buf, size_t count)
+{
+	// struct f71882fg_data *data = dev_get_drvdata(dev);
+	struct f71882fg_data *data = f71882fg_update_device(dev);
+	int pwm = to_sensor_dev_attr_2(devattr)->index;
+	int point = to_sensor_dev_attr_2(devattr)->nr;
+	int val = simple_strtoul(buf, NULL, 10);
+
+	mutex_lock(&data->update_lock);
+	f71882fg_write8(data, F71882FG_REG_POINT_TEMP(pwm,point), val);
+	mutex_unlock(&data->update_lock);
+
+	return count;
+}
+
+
 static ssize_t show_name(struct device *dev, struct device_attribute *devattr,
 	char *buf)
 {





[Index of Archives]     [Linux Kernel]     [Linux Hardware Monitoring]     [Linux USB Devel]     [Linux Audio Users]     [Linux Kernel]     [Linux SCSI]     [Yosemite Backpacking]

  Powered by Linux