[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 added fan/pwm control to the f71882fg driver in
linux-2.6.25.4. There's no hardware speed control yet, I intend
to add that later. Since this chip is able to work in RPM and
PWM controlled mode I needed to extend the possible input for
/sys/class/hwmon/hwmon*/pwm*_enable, where the extra value control PWM
vs. RPM mode.

This also raises a question. RPM control seems to be the most appropriate
version of fan control, but I need to add a maximum expected RPM value
to the driver. I was thinking about /sys/class/hwmon/hwmon./fan*_max,
but I would like like some feedback on that.

Please CC me when reacting to this post, since I'm not on the list.

Kind regards,

Mark van Doesburg.


--- /tmp/linux-2.6.25.4/drivers/hwmon/f71882fg.c	2008-05-15 17:00:12.000000000 +0200
+++ drivers/hwmon/f71882fg.c	2008-06-11 06:59:42.000000000 +0200
@@ -57,6 +57,7 @@
 #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_STATUS		0x92
 #define F71882FG_REG_FAN_BEEP		0x93
 
@@ -70,6 +71,11 @@
 #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_START		0x01
 
 #define FAN_MIN_DETECT			366 /* Lowest detectable fanspeed */
@@ -88,6 +94,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 +111,7 @@
 	u8	in_status;
 	u8	in_beep;
 	u16	fan[4];
+	u16	fan_target[4];
 	u8	fan_status;
 	u8	fan_beep;
 	u8	temp[3];
@@ -114,11 +122,15 @@
 	u8	temp_status;
 	u8	temp_beep;
 	u8	temp_diode_open;
+	u8	pwm[4];
+	u8	pwm_enable;
+	u8	pwm_type;
 };
 
 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 +148,10 @@
 /* Sysfs Fan */
 static ssize_t show_fan(struct device *dev, struct device_attribute *devattr,
 	char *buf);
+static ssize_t show_fan_target(struct device *dev, struct device_attribute
+	*devattr, char *buf);
+static ssize_t store_fan_target(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 +189,15 @@
 static ssize_t show_name(struct device *dev, struct device_attribute *devattr,
 	char *buf);
 
+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 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 +277,41 @@
 static struct sensor_device_attribute f71882fg_fan_attr[] =
 {
 	SENSOR_ATTR(fan1_input, S_IRUGO, show_fan, NULL, 0),
+	SENSOR_ATTR(fan1_target, S_IRUGO|S_IWUSR, show_fan_target,
+		store_fan_target, 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_target, S_IRUGO|S_IWUSR, show_fan_target,
+		store_fan_target, 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_target, S_IRUGO|S_IWUSR, show_fan_target,
+		store_fan_target, 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_target, S_IRUGO|S_IWUSR, show_fan_target,
+		store_fan_target, 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(pwm2, S_IRUGO|S_IWUSR, show_pwm, store_pwm, 1),
+	SENSOR_ATTR(pwm3, S_IRUGO|S_IWUSR, show_pwm, store_pwm, 2),
+	SENSOR_ATTR(pwm4, S_IRUGO|S_IWUSR, show_pwm, store_pwm, 3),
+	SENSOR_ATTR(pwm1_enable, S_IRUGO|S_IWUSR, show_pwm_enable, 
+		store_pwm_enable, 0),
+	SENSOR_ATTR(pwm2_enable, S_IRUGO|S_IWUSR, show_pwm_enable, 
+		store_pwm_enable, 1), 
+	SENSOR_ATTR(pwm3_enable, S_IRUGO|S_IWUSR, show_pwm_enable, 
+		store_pwm_enable, 2),
+	SENSOR_ATTR(pwm4_enable, S_IRUGO|S_IWUSR, show_pwm_enable, 
+		store_pwm_enable, 3)
 };
 
 
@@ -310,6 +355,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 +388,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 +458,18 @@
 
 		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);
+		for (nr = 0; nr < 4; nr++) {
 			data->fan[nr] = f71882fg_read16(data,
 						F71882FG_REG_FAN(nr));
+			data->fan_target[nr] = f71882fg_read16(data,
+						F71882FG_REG_FAN_TARGET(nr));
+			data->pwm[nr] = f71882fg_read8(data,
+						F71882FG_REG_PWM(nr));
+                }
 
 		data->in_status = f71882fg_read8(data,
 						F71882FG_REG_IN_STATUS);
@@ -432,6 +500,30 @@
 	return sprintf(buf, "%d\n", speed);
 }
 
+static ssize_t show_fan_target(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_target[nr]);
+	return sprintf(buf, "%d\n", speed);
+}
+
+static ssize_t store_fan_target(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);
+	val=fan_to_reg(val);
+
+	mutex_lock(&data->update_lock);
+	f71882fg_write16(data, F71882FG_REG_FAN_TARGET(nr), 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 +831,73 @@
 		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 nr = to_sensor_dev_attr(devattr)->index;
+	return sprintf(buf, "%d\n", data->pwm[nr]);
+}
+
+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 = simple_strtoul(buf, NULL, 10);
+
+	mutex_lock(&data->update_lock);
+	f71882fg_write8(data, F71882FG_REG_PWM(nr), val);
+	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;
+
+	switch(3&(data->pwm_enable>>(2*nr))) {
+	case 0: result=2; break; // Temperature controlled (RPM mode)
+	case 1: result=3; break; // Temperature controlled (DUTY CYCLE mode)
+	case 2: result=4; break; // Manual mode (RPM control)
+	default: result=1; break; // Manual mode (DUTY CYCLE)
+	}
+
+	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 reg;
+	int nr = to_sensor_dev_attr(devattr)->index;
+	int val = simple_strtoul(buf, NULL, 10);
+
+	switch(val) {
+	case 1: reg=3; break; // Manual mode (DUTY CYCLE)
+	case 2: reg=0; break; // Temperature controlled (RPM mode)
+	case 3: reg=1; break; // Temperature controlled (DUTY CYCLE mode)
+	case 4: reg=2; break; // Manual mode (RPM control)
+	default: val=0; reg=3; break; // Manual mode 100% duty cycle
+	}
+
+	mutex_lock(&data->update_lock);
+	data->pwm_enable&=~(3<<(2*nr));
+	data->pwm_enable|=reg<<(2*nr);
+	f71882fg_write8(data, F71882FG_REG_PWM_ENABLE, data->pwm_enable);
+	if(!val)
+		f71882fg_write8(data, F71882FG_REG_PWM(nr), 255);
+	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