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

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

 



Hi,

I've finally added some missing features to the driver. There are still
some things unimplemented, such as:

	1. Changing the fan target reached timeout.
	2. Something to set FAN?_RATE_SEL.  
	3. FAN?_STOP_DUTY
	4. FAN?_JUMP_HIGH_EN and FAN?_JUMP_LOW_EN 

Mostly because I don't need them, and there is no default name for
these features.

I did add one entry to set/reset the interpolation mode of the auto_point
follower, to make the pwm control act as a P-regulator. Simply because
I like it, it prevents rapid speedups and slowdowns of the fan.

Since the auto_point follower is able to track only one temperature at
a time, I gave a preference to the lowest numbered one. Maybe returning
an error would be better if the user tries to set an impossible value ?

regards,

Mark van Doesburg


--- /tmp/linux-2.6.25.4/drivers/hwmon/f71882fg.c	2008-05-15 17:00:12.000000000 +0200
+++ ./f71882fg.c	2008-06-25 07:43:52.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,14 +72,36 @@
 #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 */
 
+#define AUTO_POINT(pwm,point) ((pwm<<4)|(point))
+#define AUTO_POINT_PWM(val) ((val)>>4)
+#define AUTO_POINT_POINT(val) ((val)&15)
+
 static unsigned short force_id;
 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 +112,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 +129,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 +141,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][4];
+	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 +171,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 +212,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 +321,181 @@
 static struct sensor_device_attribute f71882fg_fan_attr[] =
 {
 	SENSOR_ATTR(fan1_input, S_IRUGO, show_fan, NULL, 0),
+	SENSOR_ATTR(fan1_full_speed, S_IRUGO|S_IWUSR, show_fan_full_speed, 
+		store_fan_full_speed, 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_full_speed, S_IRUGO|S_IWUSR, show_fan_full_speed, 
+		store_fan_full_speed, 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_full_speed, S_IRUGO|S_IWUSR, show_fan_full_speed, 
+		store_fan_full_speed, 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_full_speed, S_IRUGO|S_IWUSR, show_fan_full_speed, 
+		store_fan_full_speed, 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(pwm1, S_IRUGO|S_IWUSR, show_pwm, store_pwm, 0),
+	SENSOR_ATTR(pwm1_enable, S_IRUGO|S_IWUSR, show_pwm_enable, 
+		store_pwm_enable, 0),
+	SENSOR_ATTR(pwm1_interpolate, S_IRUGO|S_IWUSR, 
+		show_pwm_interpolate, store_pwm_interpolate, 0),
+	SENSOR_ATTR(pwm1_auto_point1_temp_hyst, S_IRUGO|S_IWUSR, 
+		show_pwm_auto_point_temp_hyst, store_pwm_auto_point_temp_hyst, 
+		0),
+	SENSOR_ATTR(pwm1_auto_channels_temp, S_IRUGO|S_IWUSR, 
+		show_pwm_auto_point_channel, store_pwm_auto_point_channel, 0),
+	SENSOR_ATTR(pwm1_auto_point1_pwm, S_IRUGO|S_IWUSR, 
+		show_pwm_auto_point_pwm, store_pwm_auto_point_pwm, 
+		AUTO_POINT(0,0)),
+	SENSOR_ATTR(pwm1_auto_point2_pwm, S_IRUGO|S_IWUSR, 
+		show_pwm_auto_point_pwm, store_pwm_auto_point_pwm, 
+		AUTO_POINT(0,1)),
+	SENSOR_ATTR(pwm1_auto_point3_pwm, S_IRUGO|S_IWUSR, 
+		show_pwm_auto_point_pwm, store_pwm_auto_point_pwm, 
+		AUTO_POINT(0,2)),
+	SENSOR_ATTR(pwm1_auto_point4_pwm, S_IRUGO|S_IWUSR, 
+		show_pwm_auto_point_pwm, store_pwm_auto_point_pwm, 
+		AUTO_POINT(0,3)),
+	SENSOR_ATTR(pwm1_auto_point5_pwm, S_IRUGO|S_IWUSR, 
+		show_pwm_auto_point_pwm, store_pwm_auto_point_pwm, 
+		AUTO_POINT(0,4)),
+	SENSOR_ATTR(pwm1_auto_point1_temp, S_IRUGO|S_IWUSR, 
+		show_pwm_auto_point_temp, store_pwm_auto_point_temp, 
+		AUTO_POINT(0,0)),
+	SENSOR_ATTR(pwm1_auto_point2_temp, S_IRUGO|S_IWUSR, 
+		show_pwm_auto_point_temp, store_pwm_auto_point_temp, 
+		AUTO_POINT(0,1)),
+	SENSOR_ATTR(pwm1_auto_point3_temp, S_IRUGO|S_IWUSR, 
+		show_pwm_auto_point_temp, store_pwm_auto_point_temp, 
+		AUTO_POINT(0,2)),
+	SENSOR_ATTR(pwm1_auto_point4_temp, S_IRUGO|S_IWUSR, 
+		show_pwm_auto_point_temp, store_pwm_auto_point_temp, 
+		AUTO_POINT(0,3)),
+
+	SENSOR_ATTR(pwm2, S_IRUGO|S_IWUSR, show_pwm, store_pwm, 1),
+	SENSOR_ATTR(pwm2_enable, S_IRUGO|S_IWUSR, show_pwm_enable, 
+		store_pwm_enable, 1),
+	SENSOR_ATTR(pwm2_interpolate, S_IRUGO|S_IWUSR, 
+		show_pwm_interpolate, store_pwm_interpolate, 1),
+	SENSOR_ATTR(pwm2_auto_point1_temp_hyst, S_IRUGO|S_IWUSR, 
+		show_pwm_auto_point_temp_hyst, store_pwm_auto_point_temp_hyst, 
+		1),
+	SENSOR_ATTR(pwm2_auto_channels_temp, S_IRUGO|S_IWUSR, 
+		show_pwm_auto_point_channel, store_pwm_auto_point_channel, 1),
+	SENSOR_ATTR(pwm2_auto_point1_pwm, S_IRUGO|S_IWUSR, 
+		show_pwm_auto_point_pwm, store_pwm_auto_point_pwm, 
+		AUTO_POINT(1,0)),
+	SENSOR_ATTR(pwm2_auto_point2_pwm, S_IRUGO|S_IWUSR, 
+		show_pwm_auto_point_pwm, store_pwm_auto_point_pwm, 
+		AUTO_POINT(1,1)),
+	SENSOR_ATTR(pwm2_auto_point3_pwm, S_IRUGO|S_IWUSR, 
+		show_pwm_auto_point_pwm, store_pwm_auto_point_pwm, 
+		AUTO_POINT(1,2)),
+	SENSOR_ATTR(pwm2_auto_point4_pwm, S_IRUGO|S_IWUSR, 
+		show_pwm_auto_point_pwm, store_pwm_auto_point_pwm, 
+		AUTO_POINT(1,3)),
+	SENSOR_ATTR(pwm2_auto_point5_pwm, S_IRUGO|S_IWUSR, 
+		show_pwm_auto_point_pwm, store_pwm_auto_point_pwm, 
+		AUTO_POINT(1,4)),
+	SENSOR_ATTR(pwm2_auto_point1_temp, S_IRUGO|S_IWUSR, 
+		show_pwm_auto_point_temp, store_pwm_auto_point_temp, 
+		AUTO_POINT(1,0)),
+	SENSOR_ATTR(pwm2_auto_point2_temp, S_IRUGO|S_IWUSR, 
+		show_pwm_auto_point_temp, store_pwm_auto_point_temp, 
+		AUTO_POINT(1,1)),
+	SENSOR_ATTR(pwm2_auto_point3_temp, S_IRUGO|S_IWUSR, 
+		show_pwm_auto_point_temp, store_pwm_auto_point_temp, 
+		AUTO_POINT(1,2)),
+	SENSOR_ATTR(pwm2_auto_point4_temp, S_IRUGO|S_IWUSR, 
+		show_pwm_auto_point_temp, store_pwm_auto_point_temp, 
+		AUTO_POINT(1,3)),
+
+	SENSOR_ATTR(pwm3, S_IRUGO|S_IWUSR, show_pwm, store_pwm, 2),
+	SENSOR_ATTR(pwm3_enable, S_IRUGO|S_IWUSR, show_pwm_enable, 
+		store_pwm_enable, 2),
+	SENSOR_ATTR(pwm3_interpolate, S_IRUGO|S_IWUSR, 
+		show_pwm_interpolate, store_pwm_interpolate, 2),
+	SENSOR_ATTR(pwm3_auto_point1_temp_hyst, S_IRUGO|S_IWUSR, 
+		show_pwm_auto_point_temp_hyst, store_pwm_auto_point_temp_hyst, 
+		2),
+	SENSOR_ATTR(pwm3_auto_channels_temp, S_IRUGO|S_IWUSR, 
+		show_pwm_auto_point_channel, store_pwm_auto_point_channel, 2),
+	SENSOR_ATTR(pwm3_auto_point1_pwm, S_IRUGO|S_IWUSR, 
+		show_pwm_auto_point_pwm, store_pwm_auto_point_pwm, 
+		AUTO_POINT(2,0)),
+	SENSOR_ATTR(pwm3_auto_point2_pwm, S_IRUGO|S_IWUSR, 
+		show_pwm_auto_point_pwm, store_pwm_auto_point_pwm, 
+		AUTO_POINT(2,1)),
+	SENSOR_ATTR(pwm3_auto_point3_pwm, S_IRUGO|S_IWUSR, 
+		show_pwm_auto_point_pwm, store_pwm_auto_point_pwm, 
+		AUTO_POINT(2,2)),
+	SENSOR_ATTR(pwm3_auto_point4_pwm, S_IRUGO|S_IWUSR, 
+		show_pwm_auto_point_pwm, store_pwm_auto_point_pwm, 
+		AUTO_POINT(2,3)),
+	SENSOR_ATTR(pwm3_auto_point5_pwm, S_IRUGO|S_IWUSR, 
+		show_pwm_auto_point_pwm, store_pwm_auto_point_pwm, 
+		AUTO_POINT(2,4)),
+	SENSOR_ATTR(pwm3_auto_point1_temp, S_IRUGO|S_IWUSR, 
+		show_pwm_auto_point_temp, store_pwm_auto_point_temp, 
+		AUTO_POINT(2,0)),
+	SENSOR_ATTR(pwm3_auto_point2_temp, S_IRUGO|S_IWUSR, 
+		show_pwm_auto_point_temp, store_pwm_auto_point_temp, 
+		AUTO_POINT(2,1)),
+	SENSOR_ATTR(pwm3_auto_point3_temp, S_IRUGO|S_IWUSR, 
+		show_pwm_auto_point_temp, store_pwm_auto_point_temp, 
+		AUTO_POINT(2,2)),
+	SENSOR_ATTR(pwm3_auto_point4_temp, S_IRUGO|S_IWUSR, 
+		show_pwm_auto_point_temp, store_pwm_auto_point_temp, 
+		AUTO_POINT(2,3)),
+
+	SENSOR_ATTR(pwm4, S_IRUGO|S_IWUSR, show_pwm, store_pwm, 3),
+	SENSOR_ATTR(pwm4_enable, S_IRUGO|S_IWUSR, show_pwm_enable, 
+		store_pwm_enable, 3),
+	SENSOR_ATTR(pwm4_interpolate, S_IRUGO|S_IWUSR, 
+		show_pwm_interpolate, store_pwm_interpolate, 3),
+	SENSOR_ATTR(pwm4_auto_point1_temp_hyst, S_IRUGO|S_IWUSR, 
+		show_pwm_auto_point_temp_hyst, store_pwm_auto_point_temp_hyst, 
+		3),
+	SENSOR_ATTR(pwm4_auto_channels_temp, S_IRUGO|S_IWUSR, 
+		show_pwm_auto_point_channel, store_pwm_auto_point_channel, 3),
+	SENSOR_ATTR(pwm4_auto_point1_pwm, S_IRUGO|S_IWUSR, 
+		show_pwm_auto_point_pwm, store_pwm_auto_point_pwm, 
+		AUTO_POINT(3,0)),
+	SENSOR_ATTR(pwm4_auto_point2_pwm, S_IRUGO|S_IWUSR, 
+		show_pwm_auto_point_pwm, store_pwm_auto_point_pwm, 
+		AUTO_POINT(3,1)),
+	SENSOR_ATTR(pwm4_auto_point3_pwm, S_IRUGO|S_IWUSR, 
+		show_pwm_auto_point_pwm, store_pwm_auto_point_pwm, 
+		AUTO_POINT(3,2)),
+	SENSOR_ATTR(pwm4_auto_point4_pwm, S_IRUGO|S_IWUSR, 
+		show_pwm_auto_point_pwm, store_pwm_auto_point_pwm, 
+		AUTO_POINT(3,3)),
+	SENSOR_ATTR(pwm4_auto_point5_pwm, S_IRUGO|S_IWUSR, 
+		show_pwm_auto_point_pwm, store_pwm_auto_point_pwm, 
+		AUTO_POINT(3,4)),
+	SENSOR_ATTR(pwm4_auto_point1_temp, S_IRUGO|S_IWUSR, 
+		show_pwm_auto_point_temp, store_pwm_auto_point_temp, 
+		AUTO_POINT(3,0)),
+	SENSOR_ATTR(pwm4_auto_point2_temp, S_IRUGO|S_IWUSR, 
+		show_pwm_auto_point_temp, store_pwm_auto_point_temp, 
+		AUTO_POINT(3,1)),
+	SENSOR_ATTR(pwm4_auto_point3_temp, S_IRUGO|S_IWUSR, 
+		show_pwm_auto_point_temp, store_pwm_auto_point_temp, 
+		AUTO_POINT(3,2)),
+	SENSOR_ATTR(pwm4_auto_point4_temp, S_IRUGO|S_IWUSR, 
+		show_pwm_auto_point_temp, store_pwm_auto_point_temp, 
+		AUTO_POINT(3,3))
 };
 
 
@@ -310,6 +539,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 +572,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 +642,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<4;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 +703,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(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(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 +1038,291 @@
 		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(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(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(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(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, point, pwm;
+	struct f71882fg_data *data = f71882fg_update_device(dev);
+	int nr = to_sensor_dev_attr(devattr)->index;
+
+	pwm=AUTO_POINT_PWM(nr);
+	point=AUTO_POINT_POINT(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);
+	int point, pwm;
+	struct f71882fg_data *data = f71882fg_update_device(dev);
+	int nr = to_sensor_dev_attr(devattr)->index;
+	int val = simple_strtoul(buf, NULL, 10);
+
+	pwm=AUTO_POINT_PWM(nr);
+	point=AUTO_POINT_POINT(nr);
+
+	mutex_lock(&data->update_lock);
+	if(data->pwm_enable&(1<<(2*pwm))) {
+		// PWM mode
+		f71882fg_write8(data, F71882FG_REG_POINT_PWM(pwm,point), val);
+		printk(KERN_INFO "nr=%d writing %d to 0x%x\n",
+			nr, val, F71882FG_REG_POINT_PWM(pwm,point));
+	} 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);
+		printk(KERN_INFO "RPM nr=%d writing %d to 0x%x\n",
+			nr, val, F71882FG_REG_POINT_PWM(pwm,point));
+	}
+	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(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(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(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(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(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(devattr)->index;
+	int val = simple_strtoul(buf, NULL, 10);
+	if(val&1)
+		val=1;
+	else if(val&2)
+		val=2;
+	else
+		val=3;
+	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, point, pwm;
+	struct f71882fg_data *data = f71882fg_update_device(dev);
+	int nr = to_sensor_dev_attr(devattr)->index;
+
+	pwm=AUTO_POINT_PWM(nr);
+	point=AUTO_POINT_POINT(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);
+	int point, pwm;
+	struct f71882fg_data *data = f71882fg_update_device(dev);
+	int nr = to_sensor_dev_attr(devattr)->index;
+	int val = simple_strtoul(buf, NULL, 10);
+
+	pwm=AUTO_POINT_PWM(nr);
+	point=AUTO_POINT_POINT(nr);
+
+	mutex_lock(&data->update_lock);
+	printk(KERN_INFO "writing %x\n",F71882FG_REG_POINT_TEMP(pwm,point));
+	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