it87 pwm patch for 2.6.6

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

 



Hello,

Attached is two incremental patches against drivers/i2c/it87.c, adding
more support to the it87 fan controller driver in 2.6.6-bk2. Attached is
also a small ruby script I use on my computer, just as an example.

it87_cleanups_jm1.patch:
a rename to reflect the datasheet name better, removal of a old comment
that doesn't apply anymore to the 2.6.x line

it87_manual_and_auto_pwm_jm1.patch:
manual and auto pwm support in one largish patch (I started to split
this one into two separate patches, but it was too much work and I got
lazy..).

The work is based on Takeru KOMORIYA' patch:
http://archives.andrew.net.au/lm-sensors/msg07002.html

The auto fan speed API is written to match these messages:
http://archives.andrew.net.au/lm-sensors/msg07517.html
http://archives.andrew.net.au/lm-sensors/msg07519.html

The following two pecularities of the it87 chip didn't exactly match
that API suggestion:

1. If the temperature of any programmed channel goes above
fanX_auto_temp_max, all fans in sg-automatic mode turn on at maximum
speed until the temperture comes down.

2. The fanX_auto_temp_channel variables are bitfields, but they can only
have one bit on (i.e., the possible values are 1, 2 and 4).

Some other observations:

1. I wrote this reading the it8705f datasheet. I got the impression the
other it87* chips work in the same way, but I haven't actually checked
this.

2. The fan controllers can be in three different states, changes in
settings effecting other states than the current one are rejected. An
alternative would be to save them and write them to the it87 after a
state change.

3. There are a lot of tunable settings files... The sysfs directory
becomes quite cluttered. And the it87.c contains a lot of adminstrative
overhead to set up all those files.

4. The fanX_auto_temp_* variables need to be mangled just like
fanX_input (i.e., if you have a compute line in your sensors.conf, you
need to do the same computation to values written to these files).


-------------- next part --------------
--- it87.c.orig	2004-05-16 22:13:32.000000000 +0300
+++ it87.c	2004-05-16 22:36:44.000000000 +0300
@@ -128,7 +128,7 @@
 
 #define IT87_REG_FAN(nr)       (0x0d + (nr))
 #define IT87_REG_FAN_MIN(nr)   (0x10 + (nr))
-#define IT87_REG_FAN_CTRL      0x13
+#define IT87_REG_FAN_MAIN_CTRL 0x13
 
 #define IT87_REG_VIN(nr)       (0x20 + (nr))
 #define IT87_REG_TEMP(nr)      (0x29 + (nr))
@@ -159,9 +159,9 @@
 
 #define FAN_FROM_REG(val,div) ((val)==0?-1:(val)==255?0:1350000/((val)*(div)))
 
-#define TEMP_TO_REG(val) (SENSORS_LIMIT(((val)<0?(((val)-5)/10):\
-					((val)+5)/10),0,255))
-#define TEMP_FROM_REG(val) (((val)>0x80?(val)-0x100:(val))*10)
+#define TEMP_TO_REG(val) (SENSORS_LIMIT(((val)<0?(((val)-5)/(10*100)):\
+					((val)+5)/(10*100)),0,255))
+#define TEMP_FROM_REG(val) (((val)>0x80?(val)-0x100:(val))*10*100)
 
 #define VID_FROM_REG(val) ((val)==0x1f?0:(val)>=0x10?510-(val)*10:\
 				205-(val)*5)
@@ -325,24 +325,24 @@
 static ssize_t show_temp(struct device *dev, char *buf, int nr)
 {
 	struct it87_data *data = it87_update_device(dev);
-	return sprintf(buf, "%d\n", TEMP_FROM_REG(data->temp[nr])*100 );
+	return sprintf(buf, "%d\n", TEMP_FROM_REG(data->temp[nr]));
 }
 static ssize_t show_temp_max(struct device *dev, char *buf, int nr)
 {
 	struct it87_data *data = it87_update_device(dev);
-	return sprintf(buf, "%d\n", TEMP_FROM_REG(data->temp_high[nr])*100);
+	return sprintf(buf, "%d\n", TEMP_FROM_REG(data->temp_high[nr]));
 }
 static ssize_t show_temp_min(struct device *dev, char *buf, int nr)
 {
 	struct it87_data *data = it87_update_device(dev);
-	return sprintf(buf, "%d\n", TEMP_FROM_REG(data->temp_low[nr])*100);
+	return sprintf(buf, "%d\n", TEMP_FROM_REG(data->temp_low[nr]));
 }
 static ssize_t set_temp_max(struct device *dev, const char *buf, 
 		size_t count, int nr)
 {
 	struct i2c_client *client = to_i2c_client(dev);
 	struct it87_data *data = i2c_get_clientdata(client);
-	int val = simple_strtol(buf, NULL, 10)/100;
+	int val = simple_strtol(buf, NULL, 10);
 	data->temp_high[nr] = TEMP_TO_REG(val);
 	it87_write_value(client, IT87_REG_TEMP_HIGH(nr), data->temp_high[nr]);
 	return count;
@@ -352,7 +352,7 @@
 {
 	struct i2c_client *client = to_i2c_client(dev);
 	struct it87_data *data = i2c_get_clientdata(client);
-	int val = simple_strtol(buf, NULL, 10)/100;
+	int val = simple_strtol(buf, NULL, 10);
 	data->temp_low[nr] = TEMP_TO_REG(val);
 	it87_write_value(client, IT87_REG_TEMP_LOW(nr), data->temp_low[nr]);
 	return count;
@@ -773,9 +773,7 @@
    We don't want to lock the whole ISA bus, so we lock each client
    separately.
    We ignore the IT87 BUSY flag at this moment - it could lead to deadlocks,
-   would slow down the IT87 access and should not be necessary. 
-   There are some ugly typecasts here, but the good new is - they should
-   nowhere else be necessary! */
+   would slow down the IT87 access and should not be necessary. */
 static int it87_read_value(struct i2c_client *client, u8 reg)
 {
 	struct it87_data *data = i2c_get_clientdata(client);
@@ -795,9 +793,7 @@
    We don't want to lock the whole ISA bus, so we lock each client
    separately.
    We ignore the IT87 BUSY flag at this moment - it could lead to deadlocks,
-   would slow down the IT87 access and should not be necessary. 
-   There are some ugly typecasts here, but the good new is - they should
-   nowhere else be necessary! */
+   would slow down the IT87 access and should not be necessary. */
 static int it87_write_value(struct i2c_client *client, u8 reg, u8 value)
 {
 	struct it87_data *data = i2c_get_clientdata(client);
@@ -840,11 +836,11 @@
 	}
 
 	/* Check if tachometers are reset manually or by some reason */
-	tmp = it87_read_value(client, IT87_REG_FAN_CTRL);
+	tmp = it87_read_value(client, IT87_REG_FAN_MAIN_CTRL);
 	if ((tmp & 0x70) == 0) {
 		/* Enable all fan tachometers */
 		tmp = (tmp & 0x8f) | 0x70;
-		it87_write_value(client, IT87_REG_FAN_CTRL, tmp);
+		it87_write_value(client, IT87_REG_FAN_MAIN_CTRL, tmp);
 	}
 
 	/* Start monitoring */
-------------- next part --------------
--- it87.c.orig	2004-05-16 23:10:31.000000000 +0300
+++ it87.c	2004-05-16 23:14:44.000000000 +0300
@@ -129,6 +129,8 @@
 #define IT87_REG_FAN(nr)       (0x0d + (nr))
 #define IT87_REG_FAN_MIN(nr)   (0x10 + (nr))
 #define IT87_REG_FAN_MAIN_CTRL 0x13
+#define IT87_REG_FAN_CTL       0x14
+#define IT87_REG_PWM(nr)       (0x15 + (nr))
 
 #define IT87_REG_VIN(nr)       (0x20 + (nr))
 #define IT87_REG_TEMP(nr)      (0x29 + (nr))
@@ -145,6 +147,17 @@
 
 #define IT87_REG_CHIPID        0x58
 
+#define IT87_REG_OFF_TEMP(nr)  ((nr==2)?0x70:((nr==1)?0x68:0x60))
+#define IT87_REG_LOW_TEMP(nr)  ((nr==2)?0x71:((nr==1)?0x69:0x61))
+#define IT87_REG_MED_TEMP(nr)  ((nr==2)?0x72:((nr==1)?0x6a:0x62))
+#define IT87_REG_HI_TEMP(nr)   ((nr==2)?0x73:((nr==1)?0x6b:0x63))
+#define IT87_REG_FULL_TEMP(nr) ((nr==2)?0x74:((nr==1)?0x6c:0x64))
+
+#define IT87_REG_LOW_PWM(nr)   ((nr==2)?0x75:((nr==1)?0x6d:0x65))
+#define IT87_REG_MED_PWM(nr)   ((nr==2)?0x76:((nr==1)?0x6e:0x66))
+#define IT87_REG_HI_PWM(nr)    ((nr==2)?0x77:((nr==1)?0x6f:0x67))
+								 
+
 #define IN_TO_REG(val)  (SENSORS_LIMIT((((val) * 10 + 8)/16),0,255))
 #define IN_FROM_REG(val) (((val) *  16) / 10)
 
@@ -159,6 +172,10 @@
 
 #define FAN_FROM_REG(val,div) ((val)==0?-1:(val)==255?0:1350000/((val)*(div)))
 
+#define PWM_TO_REG(val) ((val) >> 1)
+
+#define PWM_FROM_REG(val) (((val)&0x7f) << 1)
+
 #define TEMP_TO_REG(val) (SENSORS_LIMIT(((val)<0?(((val)-5)/(10*100)):\
 					((val)+5)/(10*100)),0,255))
 #define TEMP_FROM_REG(val) (((val)>0x80?(val)-0x100:(val))*10*100)
@@ -195,6 +212,11 @@
 	u8 in_min[9];		/* Register value */
 	u8 fan[3];		/* Register value */
 	u8 fan_min[3];		/* Register value */
+	u8 pwm_mode[3];		/* PWM bit 7 (0 = software, 1 = automatic) */
+	u8 pwm_tmpin[3];	/* PWM bits 1-0 when bit 7 == 1*/
+	u8 pwm_value[3];	/* PWM bits 6-0 when bit 7 == 0 */
+	u8 fan_main_ctrl;	/* Register value */
+	u8 fan_ctl;		/* Register value */
 	u8 temp[3];		/* Register value */
 	u8 temp_high[3];	/* Register value */
 	u8 temp_low[3];		/* Register value */
@@ -204,6 +226,24 @@
 	u32 alarms;		/* Register encoding, combined */
 };
 
+/* Modes that the it87's fan controllers can be in. */
+enum it87_pwm_modes {
+	PWM_OFF,	/* pwm settings disabled, only on/off available */
+	PWM_SOFTWARE,	/* manual pwm enabled for userspace software to adjust */
+	PWM_AUTOMATIC	/* automatic pwm, it87 adjusts speed according to settings */
+};
+
+static inline enum it87_pwm_modes it87_pwm_mode(struct it87_data *data, int nr) {
+	if (data->fan_main_ctrl & (1 << nr)) {
+		if (data->pwm_mode[nr] == 0)
+			return PWM_SOFTWARE;
+		else
+			return PWM_AUTOMATIC;
+	} else {
+		return PWM_OFF;
+	}
+}
+
 
 static int it87_attach_adapter(struct i2c_adapter *adapter);
 static int it87_find(int *address);
@@ -455,6 +495,83 @@
 	struct it87_data *data = it87_update_device(dev);
 	return sprintf(buf,"%d\n", DIV_FROM_REG(data->fan_div[nr]) );
 }
+static ssize_t show_fan_pwm(struct device *dev, char *buf, int nr)
+{
+	struct it87_data *data = it87_update_device(dev);
+	return sprintf(buf,"%d\n", PWM_FROM_REG(data->pwm_value[nr]));
+}
+static ssize_t show_fan_pwm_enable (struct device *dev, char *buf, int nr)
+{
+	struct it87_data *data = it87_update_device(dev);
+	return sprintf(buf, "%d\n", (data->fan_main_ctrl & (1 << nr)) != 0);
+}
+static ssize_t show_fan_enable (struct device *dev, char *buf, int nr)
+{
+	struct it87_data *data = it87_update_device(dev);
+	return sprintf(buf, "%d\n", (data->fan_ctl & (1 << nr)) != 0);
+}
+static ssize_t show_fan_auto_enable (struct device *dev, char *buf, int nr)
+{
+	struct it87_data *data = it87_update_device(dev);
+	return sprintf(buf, "%d\n", data->pwm_mode[nr]);
+}
+static ssize_t show_fan_auto_temp_channel (struct device *dev, char *buf, int nr)
+{
+	struct it87_data *data = it87_update_device(dev);
+	return sprintf(buf, "%d\n", data->pwm_tmpin[nr] ? (data->pwm_tmpin[nr] << 1) : 1 );
+}
+static ssize_t show_fan_auto_temp_off (struct device *dev, char *buf, int nr)
+{
+	struct i2c_client *client = to_i2c_client(dev);
+	u8 temp = it87_read_value(client, IT87_REG_OFF_TEMP(nr));
+	return sprintf(buf, "%d\n", TEMP_FROM_REG(temp));
+}
+static ssize_t show_fan_auto_temp_min (struct device *dev, char *buf, int nr)
+{
+	struct i2c_client *client = to_i2c_client(dev);
+	u8 temp = it87_read_value(client, IT87_REG_LOW_TEMP(nr));
+	return sprintf(buf, "%d\n", TEMP_FROM_REG(temp));
+}
+static ssize_t show_fan_auto_temp_low (struct device *dev, char *buf, int nr)
+{
+	struct i2c_client *client = to_i2c_client(dev);
+	u8 temp = it87_read_value(client, IT87_REG_MED_TEMP(nr));
+	return sprintf(buf, "%d\n", TEMP_FROM_REG(temp));
+}
+static ssize_t show_fan_auto_temp_medium (struct device *dev, char *buf, int nr)
+{
+	struct i2c_client *client = to_i2c_client(dev);
+	u8 temp = it87_read_value(client, IT87_REG_HI_TEMP(nr));
+	return sprintf(buf, "%d\n", TEMP_FROM_REG(temp));
+}
+static ssize_t show_fan_auto_temp_max (struct device *dev, char *buf, int nr)
+{
+	struct i2c_client *client = to_i2c_client(dev);
+	u8 temp = it87_read_value(client, IT87_REG_FULL_TEMP(nr));
+	return sprintf(buf, "%d\n", TEMP_FROM_REG(temp));
+}
+static ssize_t show_fan_auto_pwm_min (struct device *dev, char *buf, int nr)
+{
+	struct i2c_client *client = to_i2c_client(dev);
+	u8 temp = it87_read_value(client, IT87_REG_LOW_PWM(nr));
+	return sprintf(buf, "%d\n", temp);
+}
+static ssize_t show_fan_auto_pwm_low (struct device *dev, char *buf, int nr)
+{
+	struct i2c_client *client = to_i2c_client(dev);
+	u8 temp = it87_read_value(client, IT87_REG_MED_PWM(nr));
+	return sprintf(buf, "%d\n", temp);
+}
+static ssize_t show_fan_auto_pwm_medium (struct device *dev, char *buf, int nr)
+{
+	struct i2c_client *client = to_i2c_client(dev);
+	u8 temp = it87_read_value(client, IT87_REG_HI_PWM(nr));
+	return sprintf(buf, "%d\n", temp);
+}
+static ssize_t show_fan_auto_pwm_max (struct device *dev, char *buf, int nr)
+{
+	return sprintf(buf, "%d\n", 255);
+}
 static ssize_t set_fan_min(struct device *dev, const char *buf, 
 		size_t count, int nr)
 {
@@ -501,6 +618,175 @@
 	}
 	return count;
 }
+static ssize_t set_fan_pwm(struct device *dev, const char *buf,
+		 size_t count, int nr)
+{
+	struct i2c_client *client = to_i2c_client(dev);
+	struct it87_data *data = i2c_get_clientdata(client);
+	int val = simple_strtol(buf, NULL, 10);
+
+	if (it87_pwm_mode(data, nr) != PWM_SOFTWARE)
+		return -1;
+	if (val < 0 || val > 255)
+		return -1;
+	data->pwm_value[nr] = PWM_TO_REG(val);
+	it87_write_value(client, IT87_REG_PWM(nr), data->pwm_value[nr]);
+	return count;
+}
+static ssize_t set_fan_pwm_enable (struct device *dev, const char *buf,
+		size_t count, int nr)
+{
+	struct i2c_client *client = to_i2c_client(dev);
+	struct it87_data *data = i2c_get_clientdata(client);
+	int val = simple_strtol(buf, NULL, 10);
+
+	if (val == 1)
+		data->fan_main_ctrl |= 1 << nr;
+	else if (val == 0)
+		data->fan_main_ctrl &= ~(1 << nr);
+	else
+		return -1;
+	data->fan_main_ctrl |= 0x70;
+	it87_write_value(client, IT87_REG_FAN_MAIN_CTRL, data->fan_main_ctrl);
+	return count;
+}
+static ssize_t set_fan_enable (struct device *dev, const char *buf,
+		size_t count, int nr)
+{
+	struct i2c_client *client = to_i2c_client(dev);
+	struct it87_data *data = i2c_get_clientdata(client);
+	int val = simple_strtol(buf, NULL, 10);
+
+	if (it87_pwm_mode(data, nr) != PWM_OFF)
+		return -1;
+	if (val == 1)
+		data->fan_ctl |= 1 << nr;
+	else if (val == 0)
+		data->fan_ctl &= ~(1 << nr);
+	else
+		return -1;
+	it87_write_value(client, IT87_REG_FAN_CTL, (1 << 7) | data->fan_ctl);
+	return count;
+}
+static ssize_t set_fan_auto_enable (struct device *dev, const char *buf,
+		size_t count, int nr)
+{
+	struct i2c_client *client = to_i2c_client(dev);
+	struct it87_data *data = i2c_get_clientdata(client);
+	int val = simple_strtol(buf, NULL, 10);
+	u8 lowbits;
+
+	if (it87_pwm_mode(data, nr) == PWM_OFF)
+		return -1;
+
+	if (val == 1) {
+		data->pwm_mode[nr] = 1;
+		lowbits = data->pwm_tmpin[nr];
+	} else if (val == 0) {
+		data->pwm_mode[nr] = 0;
+		lowbits = data->pwm_value[nr];
+	} else
+		return -1;
+	it87_write_value(client, IT87_REG_PWM(nr),
+			data->pwm_mode[nr] << 7 | lowbits);
+	return count;
+}
+static ssize_t set_fan_auto_temp_channel (struct device *dev, const char *buf,
+		size_t count, int nr)
+{
+	struct i2c_client *client = to_i2c_client(dev);
+	struct it87_data *data = i2c_get_clientdata(client);
+	int val = simple_strtol(buf, NULL, 10);
+
+	if (it87_pwm_mode(data, nr) != PWM_AUTOMATIC)
+		return -1;
+	if (!(val == 1 || val == 2 || val == 4))
+		return -1;
+	data->pwm_tmpin[nr] = val >> 1;
+	it87_write_value(client, IT87_REG_PWM(nr), 1 << 7 | data->pwm_tmpin[nr]);
+	return count;
+}
+static ssize_t set_fan_auto_temp_off (struct device *dev, const char *buf,
+		size_t count, int nr)
+{
+	struct i2c_client *client = to_i2c_client(dev);
+	int val = simple_strtol(buf, NULL, 10);
+
+	it87_write_value(client, IT87_REG_OFF_TEMP(nr), TEMP_TO_REG(val));
+	return count;
+}
+static ssize_t set_fan_auto_temp_min (struct device *dev, const char *buf,
+		size_t count, int nr)
+{
+	struct i2c_client *client = to_i2c_client(dev);
+	int val = simple_strtol(buf, NULL, 10);
+
+	it87_write_value(client, IT87_REG_LOW_TEMP(nr), TEMP_TO_REG(val));
+	return count;
+}
+static ssize_t set_fan_auto_temp_low (struct device *dev, const char *buf,
+		size_t count, int nr)
+{
+	struct i2c_client *client = to_i2c_client(dev);
+	int val = simple_strtol(buf, NULL, 10);
+
+	it87_write_value(client, IT87_REG_MED_TEMP(nr), TEMP_TO_REG(val));
+	return count;
+}
+static ssize_t set_fan_auto_temp_medium (struct device *dev, const char *buf,
+		size_t count, int nr)
+{
+	struct i2c_client *client = to_i2c_client(dev);
+	int val = simple_strtol(buf, NULL, 10);
+
+	it87_write_value(client, IT87_REG_HI_TEMP(nr), TEMP_TO_REG(val));
+	return count;
+}
+static ssize_t set_fan_auto_temp_max (struct device *dev, const char *buf,
+		size_t count, int nr)
+{
+	struct i2c_client *client = to_i2c_client(dev);
+	int val = simple_strtol(buf, NULL, 10);
+
+	it87_write_value(client, IT87_REG_FULL_TEMP(nr), TEMP_TO_REG(val));
+	return count;
+}
+static ssize_t set_fan_auto_pwm_min (struct device *dev, const char *buf,
+		size_t count, int nr)
+{
+	struct i2c_client *client = to_i2c_client(dev);
+	int val = simple_strtol(buf, NULL, 10);
+
+	it87_write_value(client, IT87_REG_LOW_PWM(nr), val);
+	return count;
+}
+static ssize_t set_fan_auto_pwm_low (struct device *dev, const char *buf,
+		size_t count, int nr)
+{
+	struct i2c_client *client = to_i2c_client(dev);
+	int val = simple_strtol(buf, NULL, 10);
+
+	it87_write_value(client, IT87_REG_MED_PWM(nr), val);
+	return count;
+}
+static ssize_t set_fan_auto_pwm_medium (struct device *dev, const char *buf,
+		size_t count, int nr)
+{
+	struct i2c_client *client = to_i2c_client(dev);
+	int val = simple_strtol(buf, NULL, 10);
+
+	it87_write_value(client, IT87_REG_HI_PWM(nr), val);
+	return count;
+}
+static ssize_t set_fan_auto_pwm_max (struct device *dev, const char *buf,
+		size_t count, int nr)
+{
+	int val = simple_strtol(buf, NULL, 10);
+
+	if (val != 255)
+		return -1;
+	return count;
+}
 
 #define show_fan_offset(offset)						\
 static ssize_t show_fan_##offset (struct device *dev, char *buf)	\
@@ -515,6 +801,75 @@
 {									\
 	return show_fan_div(dev, buf, 0x##offset - 1);			\
 }									\
+static ssize_t show_fan_##offset##_pwm (struct device *dev, char *buf)	\
+{									\
+	return show_fan_pwm(dev, buf, 0x##offset - 1);			\
+}									\
+static ssize_t show_fan_##offset##_pwm_enable (struct device *dev,	\
+		char *buf) 						\
+{									\
+	return show_fan_pwm_enable(dev, buf, 0x##offset - 1);		\
+}									\
+static ssize_t show_fan_##offset##_enable (struct device *dev,		\
+		char *buf) 						\
+{									\
+	return show_fan_enable(dev, buf, 0x##offset - 1);		\
+}									\
+static ssize_t show_fan_##offset##_auto_enable (struct device *dev,	\
+		char *buf) 						\
+{									\
+	return show_fan_auto_enable(dev, buf, 0x##offset - 1);		\
+}									\
+static ssize_t show_fan_##offset##_auto_temp_channel (			\
+		struct device *dev, char *buf) 				\
+{									\
+	return show_fan_auto_temp_channel(dev, buf, 0x##offset - 1);	\
+}									\
+static ssize_t show_fan_##offset##_auto_temp_off (			\
+		struct device *dev, char *buf) 				\
+{									\
+	return show_fan_auto_temp_off(dev, buf, 0x##offset - 1);	\
+}									\
+static ssize_t show_fan_##offset##_auto_temp_min (			\
+		struct device *dev, char *buf) 				\
+{									\
+	return show_fan_auto_temp_min(dev, buf, 0x##offset - 1);	\
+}									\
+static ssize_t show_fan_##offset##_auto_temp_low (			\
+		struct device *dev, char *buf) 				\
+{									\
+	return show_fan_auto_temp_low(dev, buf, 0x##offset - 1);	\
+}									\
+static ssize_t show_fan_##offset##_auto_temp_medium (			\
+		struct device *dev, char *buf) 				\
+{									\
+	return show_fan_auto_temp_medium(dev, buf, 0x##offset - 1);	\
+}									\
+static ssize_t show_fan_##offset##_auto_temp_max (			\
+		struct device *dev, char *buf) 				\
+{									\
+	return show_fan_auto_temp_max(dev, buf, 0x##offset - 1);	\
+}									\
+static ssize_t show_fan_##offset##_auto_pwm_min (			\
+		struct device *dev, char *buf) 				\
+{									\
+	return show_fan_auto_pwm_min(dev, buf, 0x##offset - 1);		\
+}									\
+static ssize_t show_fan_##offset##_auto_pwm_low (			\
+		struct device *dev, char *buf) 				\
+{									\
+	return show_fan_auto_pwm_low(dev, buf, 0x##offset - 1);		\
+}									\
+static ssize_t show_fan_##offset##_auto_pwm_medium (			\
+		struct device *dev, char *buf) 				\
+{									\
+	return show_fan_auto_pwm_medium(dev, buf, 0x##offset - 1);	\
+}									\
+static ssize_t show_fan_##offset##_auto_pwm_max (			\
+		struct device *dev, char *buf) 				\
+{									\
+	return show_fan_auto_pwm_max(dev, buf, 0x##offset - 1);		\
+}									\
 static ssize_t set_fan_##offset##_min (struct device *dev, 		\
 	const char *buf, size_t count) 					\
 {									\
@@ -525,11 +880,122 @@
 {									\
 	return set_fan_div(dev, buf, count, 0x##offset - 1);		\
 }									\
+static ssize_t set_fan_##offset##_pwm (struct device *dev,		\
+		const char *buf, size_t count)				\
+{									\
+	return set_fan_pwm(dev, buf, count, 0x##offset - 1);		\
+}									\
+static ssize_t set_fan_##offset##_pwm_enable (struct device *dev,	\
+		const char *buf, size_t count)				\
+{									\
+	return set_fan_pwm_enable(dev, buf, count, 0x##offset - 1);	\
+}									\
+static ssize_t set_fan_##offset##_enable (struct device *dev,		\
+		const char *buf, size_t count)				\
+{									\
+	return set_fan_enable(dev, buf, count, 0x##offset - 1);		\
+}									\
+static ssize_t set_fan_##offset##_auto_enable (struct device *dev,	\
+		const char *buf, size_t count)				\
+{									\
+	return set_fan_auto_enable(dev, buf, count, 0x##offset - 1);	\
+}									\
+static ssize_t set_fan_##offset##_auto_temp_channel (			\
+		struct device *dev, const char *buf, size_t count)	\
+{									\
+	return set_fan_auto_temp_channel(dev, buf, count,		\
+			0x##offset - 1);				\
+}									\
+static ssize_t set_fan_##offset##_auto_temp_off (			\
+		struct device *dev, const char *buf, size_t count)	\
+{									\
+	return set_fan_auto_temp_off(dev, buf, count, 0x##offset - 1);	\
+}									\
+static ssize_t set_fan_##offset##_auto_temp_min (			\
+		struct device *dev, const char *buf, size_t count)	\
+{									\
+	return set_fan_auto_temp_min(dev, buf, count, 0x##offset - 1);	\
+}									\
+static ssize_t set_fan_##offset##_auto_temp_low (			\
+		struct device *dev, const char *buf, size_t count)	\
+{									\
+	return set_fan_auto_temp_low(dev, buf, count, 0x##offset - 1);	\
+}									\
+static ssize_t set_fan_##offset##_auto_temp_medium (			\
+		struct device *dev, const char *buf, size_t count)	\
+{									\
+	return set_fan_auto_temp_medium(dev, buf, count, 0x##offset - 1);	\
+}									\
+static ssize_t set_fan_##offset##_auto_temp_max (			\
+		struct device *dev, const char *buf, size_t count)	\
+{									\
+	return set_fan_auto_temp_max(dev, buf, count, 0x##offset - 1);	\
+}									\
+static ssize_t set_fan_##offset##_auto_pwm_min (			\
+		struct device *dev, const char *buf, size_t count)	\
+{									\
+	return set_fan_auto_pwm_min(dev, buf, count, 0x##offset - 1);	\
+}									\
+static ssize_t set_fan_##offset##_auto_pwm_low (			\
+		struct device *dev, const char *buf, size_t count)	\
+{									\
+	return set_fan_auto_pwm_low(dev, buf, count, 0x##offset - 1);	\
+}									\
+static ssize_t set_fan_##offset##_auto_pwm_medium (			\
+		struct device *dev, const char *buf, size_t count)	\
+{									\
+	return set_fan_auto_pwm_medium(dev, buf, count, 0x##offset - 1);	\
+}									\
+static ssize_t set_fan_##offset##_auto_pwm_max (			\
+		struct device *dev, const char *buf, size_t count)	\
+{									\
+	return set_fan_auto_pwm_max(dev, buf, count, 0x##offset - 1);	\
+}									\
 static DEVICE_ATTR(fan##offset##_input, S_IRUGO, show_fan_##offset, NULL) \
 static DEVICE_ATTR(fan##offset##_min, S_IRUGO | S_IWUSR, 		\
 		show_fan_##offset##_min, set_fan_##offset##_min) 	\
 static DEVICE_ATTR(fan##offset##_div, S_IRUGO | S_IWUSR, 		\
-		show_fan_##offset##_div, set_fan_##offset##_div)
+		show_fan_##offset##_div, set_fan_##offset##_div)	\
+static DEVICE_ATTR(fan##offset##_pwm, S_IRUGO | S_IWUSR,		\
+		show_fan_##offset##_pwm, set_fan_##offset##_pwm)	\
+static DEVICE_ATTR(fan##offset##_pwm_enable, S_IRUGO | S_IWUSR,	\
+		show_fan_##offset##_pwm_enable,				\
+		set_fan_##offset##_pwm_enable)				\
+static DEVICE_ATTR(fan##offset##_enable, S_IRUGO | S_IWUSR,		\
+		show_fan_##offset##_enable, set_fan_##offset##_enable)	\
+static DEVICE_ATTR(fan##offset##_auto_enable, S_IRUGO | S_IWUSR,	\
+		show_fan_##offset##_auto_enable,			\
+		set_fan_##offset##_auto_enable) 			\
+static DEVICE_ATTR(fan##offset##_auto_temp_channel, S_IRUGO | S_IWUSR,	\
+		show_fan_##offset##_auto_temp_channel,			\
+		set_fan_##offset##_auto_temp_channel)			\
+static DEVICE_ATTR(fan##offset##_auto_temp_off, S_IRUGO | S_IWUSR,	\
+		show_fan_##offset##_auto_temp_off,			\
+		set_fan_##offset##_auto_temp_off)			\
+static DEVICE_ATTR(fan##offset##_auto_temp_min, S_IRUGO | S_IWUSR,	\
+		show_fan_##offset##_auto_temp_min,			\
+		set_fan_##offset##_auto_temp_min)			\
+static DEVICE_ATTR(fan##offset##_auto_temp_low, S_IRUGO | S_IWUSR,	\
+		show_fan_##offset##_auto_temp_low,			\
+		set_fan_##offset##_auto_temp_low)			\
+static DEVICE_ATTR(fan##offset##_auto_temp_medium, S_IRUGO | S_IWUSR,	\
+		show_fan_##offset##_auto_temp_medium,			\
+		set_fan_##offset##_auto_temp_medium)			\
+static DEVICE_ATTR(fan##offset##_auto_temp_max, S_IRUGO | S_IWUSR,	\
+		show_fan_##offset##_auto_temp_max,			\
+		set_fan_##offset##_auto_temp_max)			\
+static DEVICE_ATTR(fan##offset##_auto_pwm_min, S_IRUGO | S_IWUSR,	\
+		show_fan_##offset##_auto_pwm_min,			\
+		set_fan_##offset##_auto_pwm_min)			\
+static DEVICE_ATTR(fan##offset##_auto_pwm_low, S_IRUGO | S_IWUSR,	\
+		show_fan_##offset##_auto_pwm_low,			\
+		set_fan_##offset##_auto_pwm_low)			\
+static DEVICE_ATTR(fan##offset##_auto_pwm_medium, S_IRUGO | S_IWUSR,	\
+		show_fan_##offset##_auto_pwm_medium,			\
+		set_fan_##offset##_auto_pwm_medium)			\
+static DEVICE_ATTR(fan##offset##_auto_pwm_max, S_IRUGO | S_IWUSR,	\
+		show_fan_##offset##_auto_pwm_max,			\
+		set_fan_##offset##_auto_pwm_max)
 
 show_fan_offset(1);
 show_fan_offset(2);
@@ -739,6 +1205,48 @@
 	device_create_file(&new_client->dev, &dev_attr_fan1_div);
 	device_create_file(&new_client->dev, &dev_attr_fan2_div);
 	device_create_file(&new_client->dev, &dev_attr_fan3_div);
+	device_create_file(&new_client->dev, &dev_attr_fan1_pwm);
+	device_create_file(&new_client->dev, &dev_attr_fan2_pwm);
+	device_create_file(&new_client->dev, &dev_attr_fan3_pwm);
+	device_create_file(&new_client->dev, &dev_attr_fan1_pwm_enable);
+	device_create_file(&new_client->dev, &dev_attr_fan2_pwm_enable);
+	device_create_file(&new_client->dev, &dev_attr_fan3_pwm_enable);
+	device_create_file(&new_client->dev, &dev_attr_fan1_enable);
+	device_create_file(&new_client->dev, &dev_attr_fan2_enable);
+	device_create_file(&new_client->dev, &dev_attr_fan3_enable);
+	device_create_file(&new_client->dev, &dev_attr_fan1_auto_enable);
+	device_create_file(&new_client->dev, &dev_attr_fan2_auto_enable);
+	device_create_file(&new_client->dev, &dev_attr_fan3_auto_enable);
+	device_create_file(&new_client->dev, &dev_attr_fan1_auto_temp_channel);
+	device_create_file(&new_client->dev, &dev_attr_fan2_auto_temp_channel);
+	device_create_file(&new_client->dev, &dev_attr_fan3_auto_temp_channel);
+	device_create_file(&new_client->dev, &dev_attr_fan1_auto_temp_off);
+	device_create_file(&new_client->dev, &dev_attr_fan2_auto_temp_off);
+	device_create_file(&new_client->dev, &dev_attr_fan3_auto_temp_off);
+	device_create_file(&new_client->dev, &dev_attr_fan1_auto_temp_min);
+	device_create_file(&new_client->dev, &dev_attr_fan2_auto_temp_min);
+	device_create_file(&new_client->dev, &dev_attr_fan3_auto_temp_min);
+	device_create_file(&new_client->dev, &dev_attr_fan1_auto_temp_low);
+	device_create_file(&new_client->dev, &dev_attr_fan2_auto_temp_low);
+	device_create_file(&new_client->dev, &dev_attr_fan3_auto_temp_low);
+	device_create_file(&new_client->dev, &dev_attr_fan1_auto_temp_medium);
+	device_create_file(&new_client->dev, &dev_attr_fan2_auto_temp_medium);
+	device_create_file(&new_client->dev, &dev_attr_fan3_auto_temp_medium);
+	device_create_file(&new_client->dev, &dev_attr_fan1_auto_temp_max);
+	device_create_file(&new_client->dev, &dev_attr_fan2_auto_temp_max);
+	device_create_file(&new_client->dev, &dev_attr_fan3_auto_temp_max);
+	device_create_file(&new_client->dev, &dev_attr_fan1_auto_pwm_min);
+	device_create_file(&new_client->dev, &dev_attr_fan2_auto_pwm_min);
+	device_create_file(&new_client->dev, &dev_attr_fan3_auto_pwm_min);
+	device_create_file(&new_client->dev, &dev_attr_fan1_auto_pwm_low);
+	device_create_file(&new_client->dev, &dev_attr_fan2_auto_pwm_low);
+	device_create_file(&new_client->dev, &dev_attr_fan3_auto_pwm_low);
+	device_create_file(&new_client->dev, &dev_attr_fan1_auto_pwm_medium);
+	device_create_file(&new_client->dev, &dev_attr_fan2_auto_pwm_medium);
+	device_create_file(&new_client->dev, &dev_attr_fan3_auto_pwm_medium);
+	device_create_file(&new_client->dev, &dev_attr_fan1_auto_pwm_max);
+	device_create_file(&new_client->dev, &dev_attr_fan2_auto_pwm_max);
+	device_create_file(&new_client->dev, &dev_attr_fan3_auto_pwm_max);
 	device_create_file(&new_client->dev, &dev_attr_alarms);
 
 	return 0;
@@ -843,6 +1351,14 @@
 		it87_write_value(client, IT87_REG_FAN_MAIN_CTRL, tmp);
 	}
 
+	/* Check poloarity of FAN_CTL3-1 registers (which are low active after a reset) */
+	tmp = it87_read_value(client, IT87_REG_FAN_CTL);
+	if ((tmp & (1 << 7)) == 0) {
+		/* Set polarity of FAN_CTL3-1 to active high */
+		tmp |= (1 << 7) | 0x7;
+		it87_write_value(client, IT87_REG_FAN_CTL, tmp);
+	}
+
 	/* Start monitoring */
 	it87_write_value(client, IT87_REG_CONFIG,
 			 (it87_read_value(client, IT87_REG_CONFIG) & 0x36)
@@ -904,6 +1420,18 @@
 		data->fan_div[1] = (i >> 3) & 0x07;
 		data->fan_div[2] = (i & 0x40) ? 3 : 1;
 
+		for (i = 0; i < 3; i++) {
+			u8 pwm;
+			pwm = it87_read_value(client, IT87_REG_PWM(i));
+			data->pwm_mode[i] = (pwm >> 7);
+			if (it87_pwm_mode(data, i) == PWM_AUTOMATIC)
+				data->pwm_tmpin[i] = pwm & 0x03;
+			else if (it87_pwm_mode(data, i) == PWM_SOFTWARE)
+				data->pwm_value[i] = pwm & 0x7f;
+		}
+		data->fan_main_ctrl = it87_read_value(client, IT87_REG_FAN_MAIN_CTRL);
+		data->fan_ctl = it87_read_value(client, IT87_REG_FAN_CTL);
+
 		data->alarms =
 			it87_read_value(client, IT87_REG_ALARM1) |
 			(it87_read_value(client, IT87_REG_ALARM2) << 8) |
-------------- next part --------------
#!/usr/bin/ruby

def set_value(file, val)
	Kernel.system("echo #{val} > #{file}")
end

def convert_cpu_temp(temp)
	((3*temp-128)*1000).to_i
end
def convert_mb_temp(temp)
	((temp)*1000).to_i
end

devdir="/sys/bus/i2c/devices/0-0290"

begin
	f = File.new(devdir, "r")
	f.close
rescue Errno::ENOENT
	print "no driver module loaded?\n"
	exit
end

Dir.chdir(devdir)


set_value("fan1_pwm_enable", 1)
set_value("fan1_auto_enable", 1)
set_value("fan1_auto_temp_channel", 1)

set_value("fan1_auto_temp_off", convert_cpu_temp(53))
set_value("fan1_auto_temp_min", convert_cpu_temp(55))
set_value("fan1_auto_pwm_min", 205)
set_value("fan1_auto_temp_low", convert_cpu_temp(58))
set_value("fan1_auto_pwm_low", 220)
set_value("fan1_auto_temp_medium", convert_cpu_temp(60))
set_value("fan1_auto_pwm_medium", 245)
set_value("fan1_auto_temp_max", convert_cpu_temp(62))



set_value("fan2_pwm_enable", 1)
set_value("fan2_auto_enable", 1)
set_value("fan2_auto_temp_channel", 2)

offtemp = 37 # reach => off
ontemp = 42 # cross => on
panictemp = 45
set_value("fan2_auto_temp_off", convert_mb_temp(offtemp))
set_value("fan2_auto_temp_min", convert_mb_temp(ontemp))
set_value("fan2_auto_pwm_min", 255)
set_value("fan2_auto_temp_low", convert_mb_temp(ontemp))
set_value("fan2_auto_pwm_low", 255)
set_value("fan2_auto_temp_medium", convert_mb_temp(ontemp))
set_value("fan2_auto_pwm_medium", 255)
set_value("fan2_auto_temp_max", convert_mb_temp(panictemp))




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

  Powered by Linux