[lm-sensors] [PATCH 2.6.25.4] f71882.c driver, Added PWM/FAN control.
Mark van Doesburg
mark.vandoesburg at hetnet.nl
Wed Jun 11 07:20:13 CEST 2008
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)
{
More information about the lm-sensors
mailing list