2.6 lm85.c driver update
Quoted 7 lines · Greg KH
Quoted 3 lines · Justin Thiessen
Hi,
Here is a patch to restore fan control to the lm85 driver in the 2.6.X kernel.
Hm, there are a number of rejects in this patch. Care to resync up with the next kernel release and resend this?
Ok, try this:
signed off by: Justin Thiessen <[email protected]>
patch for kernel 2.6.X lm85 driver follows:
----------------------------------------------------
drivers/i2c/chips/lm85.c renamed from drivers/i2c/chips/lm85.c.orig +405 -46
@@ -4,6 +4,7 @@ Copyright (c) 1998, 1999 Frodo Looijaard <[email protected]> Copyright (c) 2002, 2003 Philip Pokorny <[email protected]> Copyright (c) 2003 Margit Schubert-While <[email protected]> + Copyright (c) 2004 Justin Thiessen <[email protected]> Chip details at <http://www.national.com/ds/LM/LM85.pdf>
@@ -29,11 +30,6 @@#include <linux/i2c.h> #include <linux/i2c-sensor.h> #include <linux/i2c-vid.h> -/* -#include <asm/io.h> -*/ - -#undef LM85EXTENDEDFUNC/* Extended functionality */ /* Addresses to scan */ static unsigned short normal_i2c[] = {0x2c ,0x2d ,0x2e ,I2C_CLIENT_END };
@@ -163,8 +159,6 @@ static int lm85_scaling[] = { /* .001 V#define EXT_FROM_REG (val,sensor) (((val)>>(sensor * 2))&0x03) -#ifdef LM85EXTENDEDFUNC /* Extended functionality */ -/* ZONEs have the following parameters: * Limit (low) temp, 1. degC * Hysteresis (below limit), 1. degC (0-15)
@@ -183,20 +177,32 @@ static int lm85_scaling[] = { /* .001 V* Filter constant (or disabled) .1 seconds */ -/* These are the zone temperature range encodings */ -static int lm85_range_map[] = {/* .1 degC */ -20 ,25 ,33 ,40 ,50 ,66 , -80 ,100 ,133 ,160 ,200 ,266 , -320 ,400 ,533 ,800 +/* These are the zone temperature range encodings in .001 degree C */ +static int lm85_range_map[] = { +2000 ,2500 ,3300 ,4000 ,5000 ,6600 , +8000 ,10000 ,13300 ,16000 ,20000 ,26600 , +32000 ,40000 ,53300 ,80000 };static int RANGE_TO_REG (int range ) {int i; -if ( range >= lm85_range_map[15 ] ) {return 15 ; } -for ( i =0 ; i <15 ; ++i ) -if ( range <= lm85_range_map[i] ) -break ; +if ( range < lm85_range_map[0 ] ) { +return 0 ; + }else if ( range > lm85_range_map[15 ] ) { +return 15 ; + }else {/* find closest match */ +for ( i =14 ; i >=0 ; --i ) { +if ( range > lm85_range_map[i] ) {/* range bracketed */ +if ((lm85_range_map[i+1 ] - range) < + (range - lm85_range_map[i])) { + i++; +break ; + } +break ; + } + } + }return ( i &0x0f ); }#define RANGE_FROM_REG (val) (lm85_range_map[(val)&0x0f])
@@ -240,7 +246,7 @@ static int SPINUP_TO_REG( int spinup )/* These are the PWM frequency encodings */ static int lm85_freq_map[] = {/* .1 Hz */ -100 ,150 ,230 ,300 ,380 ,470 ,620 ,980 +100 ,150 ,230 ,300 ,380 ,470 ,620 ,940 };static int FREQ_TO_REG (int freq ) {
@@ -266,13 +272,9 @@ static int FREQ_TO_REG( int freq )* -2 -- PWM responds to manual control */ -#endif /* Extended functionality */ -static int lm85_zone_map[] = {1 ,2 ,3 ,-1 ,0 ,23 ,123 ,-2 };#define ZONE_FROM_REG (val) (lm85_zone_map[((val)>>5)&0x07]) -#ifdef LM85EXTENDEDFUNC /* Extended functionality */ -static int ZONE_TO_REG (int zone ) {int i;
@@ -285,10 +287,8 @@ static int ZONE_TO_REG( int zone )return ( (i &0x07 )<<5 ); } -#endif /* Extended functionality */ - -#define HYST_TO_REG (val) (SENSORS_LIMIT((-(val)+5)/10,0,15)) -#define HYST_FROM_REG (val) (-(val)*10) +#define HYST_TO_REG (val) (SENSORS_LIMIT(((val)+500)/1000,0,15)) +#define HYST_FROM_REG (val) ((val)*1000)#define OFFSET_TO_REG (val) (SENSORS_LIMIT((val)/25,-127,127))#define OFFSET_FROM_REG (val) ((val)*25)
@@ -338,6 +338,14 @@ struct lm85_zone {u8 hyst;/* Low limit hysteresis. (0-15) */ u8 range;/* Temp range, encoded */ s8 critical;/* "All fans ON" temp limit */ +u8 off_desired;/* Actual "off" temperature specified. Preserved +* to prevent "drift" as other autofan control +* values change. +*/ +u8 max_desired;/* Actual "max" temperature specified. Preserved +* to prevent "drift" as other autofan control +* values change. +*/ };struct lm85_autofan {
@@ -437,18 +445,19 @@ static ssize_t set_fan_min(struct device#define show_fan_offset (offset) \ static ssize_t show_fan_##offset (struct device *dev, char *buf) \ { \ - return show_fan(dev, buf, 0x##offset - 1); \ + return show_fan(dev, buf, offset - 1); \ } \ static ssize_t show_fan_##offset##_min (struct device *dev, char *buf) \ { \ - return show_fan_min(dev, buf, 0x##offset - 1); \ + return show_fan_min(dev, buf, offset - 1); \ } \ static ssize_t set_fan_##offset##_min (struct device *dev, \ const char *buf, size_t count) \ { \ - return set_fan_min(dev, buf, count, 0x##offset - 1); \ + return set_fan_min(dev, buf, count, offset - 1); \ } \ -static DEVICE_ATTR(fan##offset##_input, S_IRUGO, show_fan_##offset, NULL);\ +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);
@@ -527,20 +536,21 @@ static ssize_t show_pwm_enable(struct de#define show_pwm_reg (offset) \ static ssize_t show_pwm_##offset (struct device *dev, char *buf) \ { \ - return show_pwm(dev, buf, 0x##offset - 1); \ + return show_pwm(dev, buf, offset - 1); \ } \ static ssize_t set_pwm_##offset (struct device *dev, \ const char *buf, size_t count) \ { \ - return set_pwm(dev, buf, count, 0x##offset - 1); \ + return set_pwm(dev, buf, count, offset - 1); \ } \ static ssize_t show_pwm_enable##offset (struct device *dev, char *buf) \ { \ - return show_pwm_enable(dev, buf, 0x##offset - 1); \ + return show_pwm_enable(dev, buf, offset - 1); \ } \ -static DEVICE_ATTR(fan##offset##_pwm, S_IRUGO | S_IWUSR, \ +static DEVICE_ATTR(fan##offset##_pwm, S_IRUGO | S_IWUSR, \ show_pwm_##offset, set_pwm_##offset); \ -static DEVICE_ATTR(fan##offset##_pwm_enable, S_IRUGO, show_pwm_enable##offset, NULL); +static DEVICE_ATTR(fan##offset##_pwm_enable, S_IRUGO, \ + show_pwm_enable##offset, NULL);show_pwm_reg (1 );show_pwm_reg (2 );
@@ -594,25 +604,25 @@ static ssize_t set_in_max(struct device#define show_in_reg (offset) \ static ssize_t show_in_##offset (struct device *dev, char *buf) \ { \ - return show_in(dev, buf, 0x##offset); \ + return show_in(dev, buf, offset); \ } \ static ssize_t show_in_##offset##_min (struct device *dev, char *buf) \ { \ - return show_in_min(dev, buf, 0x##offset); \ + return show_in_min(dev, buf, offset); \ } \ static ssize_t show_in_##offset##_max (struct device *dev, char *buf) \ { \ - return show_in_max(dev, buf, 0x##offset); \ + return show_in_max(dev, buf, offset); \ } \ static ssize_t set_in_##offset##_min (struct device *dev, \ const char *buf, size_t count) \ { \ - return set_in_min(dev, buf, count, 0x##offset); \ + return set_in_min(dev, buf, count, offset); \ } \ static ssize_t set_in_##offset##_max (struct device *dev, \ const char *buf, size_t count) \ { \ - return set_in_max(dev, buf, count, 0x##offset); \ + return set_in_max(dev, buf, count, offset); \ } \ static DEVICE_ATTR(in##offset##_input, S_IRUGO, show_in_##offset, NULL); \ static DEVICE_ATTR(in##offset##_min, S_IRUGO | S_IWUSR, \
@@ -674,27 +684,28 @@ static ssize_t set_temp_max(struct devic#define show_temp_reg (offset) \ static ssize_t show_temp_##offset (struct device *dev, char *buf) \ { \ - return show_temp(dev, buf, 0x##offset - 1); \ + return show_temp(dev, buf, offset - 1); \ } \ static ssize_t show_temp_##offset##_min (struct device *dev, char *buf) \ { \ - return show_temp_min(dev, buf, 0x##offset - 1); \ + return show_temp_min(dev, buf, offset - 1); \ } \ static ssize_t show_temp_##offset##_max (struct device *dev, char *buf) \ { \ - return show_temp_max(dev, buf, 0x##offset - 1); \ + return show_temp_max(dev, buf, offset - 1); \ } \ static ssize_t set_temp_##offset##_min (struct device *dev, \ const char *buf, size_t count) \ { \ - return set_temp_min(dev, buf, count, 0x##offset - 1); \ + return set_temp_min(dev, buf, count, offset - 1); \ } \ static ssize_t set_temp_##offset##_max (struct device *dev, \ const char *buf, size_t count) \ { \ - return set_temp_max(dev, buf, count, 0x##offset - 1); \ + return set_temp_max(dev, buf, count, offset - 1); \ } \ -static DEVICE_ATTR(temp##offset##_input, S_IRUGO, show_temp_##offset, NULL); \ +static DEVICE_ATTR(temp##offset##_input, S_IRUGO, show_temp_##offset, \ + NULL); \ static DEVICE_ATTR(temp##offset##_min, S_IRUGO | S_IWUSR, \ show_temp_##offset##_min, set_temp_##offset##_min); \ static DEVICE_ATTR(temp##offset##_max, S_IRUGO | S_IWUSR, \
@@ -705,6 +716,330 @@ show_temp_reg(2);show_temp_reg (3 ); +/* Automatic PWM control */ + +static ssize_t show_pwm_auto_channels (struct device *dev,char *buf,int nr) +{ +struct lm85_data *data =lm85_update_device (dev); +return sprintf (buf,"%d\n" ,ZONE_FROM_REG (data->autofan[nr].config)); +} +static ssize_t set_pwm_auto_channels (struct device *dev,const char *buf, +size_t count,int nr) +{ +struct i2c_client *client =to_i2c_client (dev); +struct lm85_data *data =i2c_get_clientdata (client); +int val; + +down (&data->update_lock); + val =simple_strtol (buf,NULL ,10 ); + data->autofan[nr].config = (data->autofan[nr].config & (~0xe0 )) + |ZONE_TO_REG (val) ; +lm85_write_value (client,LM85_REG_AFAN_CONFIG (nr), + data->autofan[nr].config); +up (&data->update_lock); +return count; +} +static ssize_t show_pwm_auto_pwm_min (struct device *dev,char *buf,int nr) +{ +struct lm85_data *data =lm85_update_device (dev); +return sprintf (buf,"%d\n" ,PWM_FROM_REG (data->autofan[nr].min_pwm)); +} +static ssize_t set_pwm_auto_pwm_min (struct device *dev,const char *buf, +size_t count,int nr) +{ +struct i2c_client *client =to_i2c_client (dev); +struct lm85_data *data =i2c_get_clientdata (client); +int val; + +down (&data->update_lock); + val =simple_strtol (buf,NULL ,10 ); + data->autofan[nr].min_pwm =PWM_TO_REG (val); +lm85_write_value (client,LM85_REG_AFAN_MINPWM (nr), + data->autofan[nr].min_pwm); +up (&data->update_lock); +return count; +} +static ssize_t show_pwm_auto_pwm_minctl (struct device *dev,char *buf,int nr) +{ +struct lm85_data *data =lm85_update_device (dev); +return sprintf (buf,"%d\n" , data->autofan[nr].min_off); +} +static ssize_t set_pwm_auto_pwm_minctl (struct device *dev,const char *buf, +size_t count,int nr) +{ +struct i2c_client *client =to_i2c_client (dev); +struct lm85_data *data =i2c_get_clientdata (client); +int val; + +down (&data->update_lock); + val =simple_strtol (buf,NULL ,10 ); + data->autofan[nr].min_off = val; +lm85_write_value (client,LM85_REG_AFAN_SPIKE1 , data->smooth[0 ] + | data->syncpwm3 + | (data->autofan[0 ].min_off ?0x20 :0 ) + | (data->autofan[1 ].min_off ?0x40 :0 ) + | (data->autofan[2 ].min_off ?0x80 :0 ) + ); +up (&data->update_lock); +return count; +} +static ssize_t show_pwm_auto_pwm_freq (struct device *dev,char *buf,int nr) +{ +struct lm85_data *data =lm85_update_device (dev); +return sprintf (buf,"%d\n" ,FREQ_FROM_REG (data->autofan[nr].freq)); +} +static ssize_t set_pwm_auto_pwm_freq (struct device *dev,const char *buf, +size_t count,int nr) +{ +struct i2c_client *client =to_i2c_client (dev); +struct lm85_data *data =i2c_get_clientdata (client); +int val; + +down (&data->update_lock); + val =simple_strtol (buf,NULL ,10 ); + data->autofan[nr].freq =FREQ_TO_REG (val); +lm85_write_value (client,LM85_REG_AFAN_RANGE (nr), + (data->zone[nr].range <<4 ) + | data->autofan[nr].freq + ); +up (&data->update_lock); +return count; +} +#define pwm_auto (offset) \ +static ssize_t show_pwm##offset##_auto_channels (struct device *dev, \ + char *buf) \ +{ \ + return show_pwm_auto_channels(dev, buf, offset - 1); \ +} \ +static ssize_t set_pwm##offset##_auto_channels (struct device *dev, \ + const char *buf, size_t count) \ +{ \ + return set_pwm_auto_channels(dev, buf, count, offset - 1); \ +} \ +static ssize_t show_pwm##offset##_auto_pwm_min (struct device *dev, \ + char *buf) \ +{ \ + return show_pwm_auto_pwm_min(dev, buf, offset - 1); \ +} \ +static ssize_t set_pwm##offset##_auto_pwm_min (struct device *dev, \ + const char *buf, size_t count) \ +{ \ + return set_pwm_auto_pwm_min(dev, buf, count, offset - 1); \ +} \ +static ssize_t show_pwm##offset##_auto_pwm_minctl (struct device *dev, \ + char *buf) \ +{ \ + return show_pwm_auto_pwm_minctl(dev, buf, offset - 1); \ +} \ +static ssize_t set_pwm##offset##_auto_pwm_minctl (struct device *dev, \ + const char *buf, size_t count) \ +{ \ + return set_pwm_auto_pwm_minctl(dev, buf, count, offset - 1);\ +} \ +static ssize_t show_pwm##offset##_auto_pwm_freq (struct device *dev, \ + char *buf) \ +{ \ + return show_pwm_auto_pwm_freq(dev, buf, offset - 1); \ +} \ +static ssize_t set_pwm##offset##_auto_pwm_freq(struct device *dev, \ + const char *buf, size_t count) \ +{ \ + return set_pwm_auto_pwm_freq(dev, buf, count, offset - 1); \ +} \ +static DEVICE_ATTR(pwm##offset##_auto_channels, S_IRUGO | S_IWUSR, \ + show_pwm##offset##_auto_channels, \ + set_pwm##offset##_auto_channels); \ +static DEVICE_ATTR(pwm##offset##_auto_pwm_min, S_IRUGO | S_IWUSR, \ + show_pwm##offset##_auto_pwm_min, \ + set_pwm##offset##_auto_pwm_min); \ +static DEVICE_ATTR(pwm##offset##_auto_pwm_minctl, S_IRUGO | S_IWUSR, \ + show_pwm##offset##_auto_pwm_minctl, \ + set_pwm##offset##_auto_pwm_minctl); \ +static DEVICE_ATTR(pwm##offset##_auto_pwm_freq, S_IRUGO | S_IWUSR, \ + show_pwm##offset##_auto_pwm_freq, \ + set_pwm##offset##_auto_pwm_freq); +pwm_auto (1 ); +pwm_auto (2 ); +pwm_auto (3 ); + +/* Temperature settings for automatic PWM control */ + +static ssize_t show_temp_auto_temp_off (struct device *dev,char *buf,int nr) +{ +struct lm85_data *data =lm85_update_device (dev); +return sprintf (buf,"%d\n" ,TEMP_FROM_REG (data->zone[nr].limit) - +HYST_FROM_REG (data->zone[nr].hyst)); +} +static ssize_t set_temp_auto_temp_off (struct device *dev,const char *buf, +size_t count,int nr) +{ +struct i2c_client *client =to_i2c_client (dev); +struct lm85_data *data =i2c_get_clientdata (client); +int min, val; + +down (&data->update_lock); + val =simple_strtol (buf,NULL ,10 ); + min =TEMP_FROM_REG (data->zone[nr].limit); + data->zone[nr].off_desired =TEMP_TO_REG (val); + data->zone[nr].hyst =HYST_TO_REG (min - val); +if ( nr ==0 || nr ==1 ) { +lm85_write_value (client,LM85_REG_AFAN_HYST1 , + (data->zone[0 ].hyst <<4 ) + | data->zone[1 ].hyst + ); + }else { +lm85_write_value (client,LM85_REG_AFAN_HYST2 , + (data->zone[2 ].hyst <<4 ) + ); + } +up (&data->update_lock); +return count; +} +static ssize_t show_temp_auto_temp_min (struct device *dev,char *buf,int nr) +{ +struct lm85_data *data =lm85_update_device (dev); +return sprintf (buf,"%d\n" ,TEMP_FROM_REG (data->zone[nr].limit) ); +} +static ssize_t set_temp_auto_temp_min (struct device *dev,const char *buf, +size_t count,int nr) +{ +struct i2c_client *client =to_i2c_client (dev); +struct lm85_data *data =i2c_get_clientdata (client); +int val; + +down (&data->update_lock); + val =simple_strtol (buf,NULL ,10 ); + data->zone[nr].limit =TEMP_TO_REG (val); +lm85_write_value (client,LM85_REG_AFAN_LIMIT (nr), + data->zone[nr].limit); + +/* Update temp_auto_max and temp_auto_range */ + data->zone[nr].range =RANGE_TO_REG ( +TEMP_FROM_REG (data->zone[nr].max_desired) - +TEMP_FROM_REG (data->zone[nr].limit)); +lm85_write_value (client,LM85_REG_AFAN_RANGE (nr), + ((data->zone[nr].range &0x0f ) <<4 ) + | (data->autofan[nr].freq &0x07 )); + +/* Update temp_auto_hyst and temp_auto_off */ + data->zone[nr].hyst =HYST_TO_REG (TEMP_FROM_REG ( + data->zone[nr].limit) -TEMP_FROM_REG ( + data->zone[nr].off_desired)); +if ( nr ==0 || nr ==1 ) { +lm85_write_value (client,LM85_REG_AFAN_HYST1 , + (data->zone[0 ].hyst <<4 ) + | data->zone[1 ].hyst + ); + }else { +lm85_write_value (client,LM85_REG_AFAN_HYST2 , + (data->zone[2 ].hyst <<4 ) + ); + } +up (&data->update_lock); +return count; +} +static ssize_t show_temp_auto_temp_max (struct device *dev,char *buf,int nr) +{ +struct lm85_data *data =lm85_update_device (dev); +return sprintf (buf,"%d\n" ,TEMP_FROM_REG (data->zone[nr].limit) + +RANGE_FROM_REG (data->zone[nr].range)); +} +static ssize_t set_temp_auto_temp_max (struct device *dev,const char *buf, +size_t count,int nr) +{ +struct i2c_client *client =to_i2c_client (dev); +struct lm85_data *data =i2c_get_clientdata (client); +int min, val; + +down (&data->update_lock); + min =TEMP_FROM_REG (data->zone[nr].limit); + val =simple_strtol (buf,NULL ,10 ); + data->zone[nr].max_desired =TEMP_TO_REG (val); + data->zone[nr].range =RANGE_TO_REG ( + val - min); +lm85_write_value (client,LM85_REG_AFAN_RANGE (nr), + ((data->zone[nr].range &0x0f ) <<4 ) + | (data->autofan[nr].freq &0x07 )); +up (&data->update_lock); +return count; +} +static ssize_t show_temp_auto_temp_crit (struct device *dev,char *buf,int nr) +{ +struct lm85_data *data =lm85_update_device (dev); +return sprintf (buf,"%d\n" ,TEMP_FROM_REG (data->zone[nr].critical)); +} +static ssize_t set_temp_auto_temp_crit (struct device *dev,const char *buf, +size_t count,int nr) +{ +struct i2c_client *client =to_i2c_client (dev); +struct lm85_data *data =i2c_get_clientdata (client); +int val; + +down (&data->update_lock); + val =simple_strtol (buf,NULL ,10 ); + data->zone[nr].critical =TEMP_TO_REG (val); +lm85_write_value (client,LM85_REG_AFAN_CRITICAL (nr), + data->zone[nr].critical); +up (&data->update_lock); +return count; +} +#define temp_auto (offset) \ +static ssize_t show_temp##offset##_auto_temp_off (struct device *dev, \ + char *buf) \ +{ \ + return show_temp_auto_temp_off(dev, buf, offset - 1); \ +} \ +static ssize_t set_temp##offset##_auto_temp_off (struct device *dev, \ + const char *buf, size_t count) \ +{ \ + return set_temp_auto_temp_off(dev, buf, count, offset - 1); \ +} \ +static ssize_t show_temp##offset##_auto_temp_min (struct device *dev, \ + char *buf) \ +{ \ + return show_temp_auto_temp_min(dev, buf, offset - 1); \ +} \ +static ssize_t set_temp##offset##_auto_temp_min (struct device *dev, \ + const char *buf, size_t count) \ +{ \ + return set_temp_auto_temp_min(dev, buf, count, offset - 1); \ +} \ +static ssize_t show_temp##offset##_auto_temp_max (struct device *dev, \ + char *buf) \ +{ \ + return show_temp_auto_temp_max(dev, buf, offset - 1); \ +} \ +static ssize_t set_temp##offset##_auto_temp_max (struct device *dev, \ + const char *buf, size_t count) \ +{ \ + return set_temp_auto_temp_max(dev, buf, count, offset - 1); \ +} \ +static ssize_t show_temp##offset##_auto_temp_crit (struct device *dev, \ + char *buf) \ +{ \ + return show_temp_auto_temp_crit(dev, buf, offset - 1); \ +} \ +static ssize_t set_temp##offset##_auto_temp_crit (struct device *dev, \ + const char *buf, size_t count) \ +{ \ + return set_temp_auto_temp_crit(dev, buf, count, offset - 1); \ +} \ +static DEVICE_ATTR(temp##offset##_auto_temp_off, S_IRUGO | S_IWUSR, \ + show_temp##offset##_auto_temp_off, \ + set_temp##offset##_auto_temp_off); \ +static DEVICE_ATTR(temp##offset##_auto_temp_min, S_IRUGO | S_IWUSR, \ + show_temp##offset##_auto_temp_min, \ + set_temp##offset##_auto_temp_min); \ +static DEVICE_ATTR(temp##offset##_auto_temp_max, S_IRUGO | S_IWUSR, \ + show_temp##offset##_auto_temp_max, \ + set_temp##offset##_auto_temp_max); \ +static DEVICE_ATTR(temp##offset##_auto_temp_crit, S_IRUGO | S_IWUSR, \ + show_temp##offset##_auto_temp_crit, \ + set_temp##offset##_auto_temp_crit); +temp_auto (1 ); +temp_auto (2 ); +temp_auto (3 ); +int lm85_attach_adapter (struct i2c_adapter *adapter) {return i2c_detect (adapter, &addr_data, lm85_detect);
@@ -876,6 +1211,30 @@ int lm85_detect(struct i2c_adapter *adapdevice_create_file (&new_client->dev, &dev_attr_vrm);device_create_file (&new_client->dev, &dev_attr_cpu0_vid);device_create_file (&new_client->dev, &dev_attr_alarms); +device_create_file (&new_client->dev, &dev_attr_pwm1_auto_channels); +device_create_file (&new_client->dev, &dev_attr_pwm2_auto_channels); +device_create_file (&new_client->dev, &dev_attr_pwm3_auto_channels); +device_create_file (&new_client->dev, &dev_attr_pwm1_auto_pwm_min); +device_create_file (&new_client->dev, &dev_attr_pwm2_auto_pwm_min); +device_create_file (&new_client->dev, &dev_attr_pwm3_auto_pwm_min); +device_create_file (&new_client->dev, &dev_attr_pwm1_auto_pwm_minctl); +device_create_file (&new_client->dev, &dev_attr_pwm2_auto_pwm_minctl); +device_create_file (&new_client->dev, &dev_attr_pwm3_auto_pwm_minctl); +device_create_file (&new_client->dev, &dev_attr_pwm1_auto_pwm_freq); +device_create_file (&new_client->dev, &dev_attr_pwm2_auto_pwm_freq); +device_create_file (&new_client->dev, &dev_attr_pwm3_auto_pwm_freq); +device_create_file (&new_client->dev, &dev_attr_temp1_auto_temp_off); +device_create_file (&new_client->dev, &dev_attr_temp2_auto_temp_off); +device_create_file (&new_client->dev, &dev_attr_temp3_auto_temp_off); +device_create_file (&new_client->dev, &dev_attr_temp1_auto_temp_min); +device_create_file (&new_client->dev, &dev_attr_temp2_auto_temp_min); +device_create_file (&new_client->dev, &dev_attr_temp3_auto_temp_min); +device_create_file (&new_client->dev, &dev_attr_temp1_auto_temp_max); +device_create_file (&new_client->dev, &dev_attr_temp2_auto_temp_max); +device_create_file (&new_client->dev, &dev_attr_temp3_auto_temp_max); +device_create_file (&new_client->dev, &dev_attr_temp1_auto_temp_crit); +device_create_file (&new_client->dev, &dev_attr_temp2_auto_temp_crit); +device_create_file (&new_client->dev, &dev_attr_temp3_auto_temp_crit);return 0 ;
@@ -1159,7 +1518,7 @@ static void __exit sm_lm85_exit(void)* post 2.7.0 CVS changes. */ MODULE_LICENSE ("GPL" ); -MODULE_AUTHOR ("Philip Pokorny <[email protected]>, Margit Schubert-While <[email protected]>" ); +MODULE_AUTHOR ("Philip Pokorny <[email protected]>, Margit Schubert-While <[email protected]>, Justin Thiessen <[email protected]" );MODULE_DESCRIPTION ("LM85-B, LM85-C driver" );module_init (sm_lm85_init);
drivers/i2c/chips/lm85.c
Quoted 12 lines
Quoted 2 lines
Hm, there are a number of rejects in this patch. Care to resync up with the next kernel release and resend this?
Ok, try this:
signed off by: Justin Thiessen <[email protected]>
patch for kernel 2.6.X lm85 driver follows:
----------------------------------------------------drivers/i2c/chips/lm85.c renamed from drivers/i2c/chips/lm85.c.orig +0
-0
Unfortunately this won't be OK either. 2.6.10-rc1 has a lot of i2c changes, including a number of things your patch attempts to fix (macro abuse reported by Mark Hoffman). So it won't apply to Greg's tree.
Please provide your patch against 2.6.10-rc1. Sorry that you always seem to provide your patch against the not-really-last tree ;)
Signature · 2 lines
Jean Delvare http://khali.linux-fr.org/
Greg KH
AppliedQuoted 22 lines · Justin Thiessen
Quoted 10 lines · Jean Delvare
Quoted 10 lines
Quoted 2 lines
Hm, there are a number of rejects in this patch. Care to resync up with the next kernel release and resend this?
Ok, try this:
signed off by: Justin Thiessen <[email protected]>
patch for kernel 2.6.X lm85 driver follows:
----------------------------------------------------Quoted 6 lines
drivers/i2c/chips/lm85.c
Quoted 2 lines
drivers/i2c/chips/lm85.c renamed from drivers/i2c/chips/lm85.c.orig +0
-0Unfortunately this won't be OK either. 2.6.10-rc1 has a lot of i2c changes, including a number of things your patch attempts to fix (macro abuse reported by Mark Hoffman). So it won't apply to Greg's tree.
Aiiiiiiiieeeeeee.
Quoted 7 lines
Quoted 2 lines
Please provide your patch against 2.6.10-rc1. Sorry that you always seem to provide your patch against the not-really-last tree ;)
It's not your fault I expect kernel releases to be rolled out by punchcards and horse-drawn wagon.
Thanks for the heads-up; see the following patch for a new, "improved" version.
Applied, thanks.
But now I get the following build warnings:
drivers/i2c/chips/lm85.c:220: warning: 'SMOOTH_TO_REG' defined but not used drivers/i2c/chips/lm85.c:236: warning: 'SPINUP_TO_REG' defined but not used
Care to send me a patch to fix them up?
thanks,
greg k-h
Quoted 10 lines · Greg KH
Quoted 9 lines · Justin Thiessen
Quoted 8 lines · Jean Delvare
Quoted 8 lines
Quoted 2 lines
Hm, there are a number of rejects in this patch. Care to resync up with the next kernel release and resend this?
Ok, try this:
signed off by: Justin Thiessen <[email protected]>
patch for kernel 2.6.X lm85 driver follows:
<snip>
Quoted 8 lines
Applied, thanks.
But now I get the following build warnings:
drivers/i2c/chips/lm85.c:220: warning: 'SMOOTH_TO_REG' defined but not useddrivers/i2c/chips/lm85.c:236: warning: 'SPINUP_TO_REG' defined but not usedCare to send me a patch to fix them up?
Ok.
Justin Thiessen
Signed off by Justin Thiessen <[email protected]>
-------
drivers/i2c/chips/lm85.c renamed from drivers/i2c/chips/lm85.c.orig +0 -32
@@ -212,38 +212,6 @@ static int RANGE_TO_REG( int range )* MSB (bit 3, value 8). If the enable bit is 0, the encoded value * is ignored, or set to 0. */ -static int lm85_smooth_map[] = {/* .1 sec */ -350 ,176 ,118 ,70 ,44 ,30 ,16 ,8 -/* 35.4 * 1/1, 1/2, 1/3, 1/5, 1/8, 1/12, 1/24, 1/48 */ - }; -static int SMOOTH_TO_REG (int smooth ) -{ -int i; - -if ( smooth <=0 ) {return 0 ; }/* Disabled */ -for ( i =0 ; i <7 ; ++i ) -if ( smooth >= lm85_smooth_map[i] ) -break ; -return ( (i &0x07 ) |0x08 ); -} -#define SMOOTH_FROM_REG (val) ((val)&0x08?lm85_smooth_map[(val)&0x07]:0) - -/* These are the fan spinup delay time encodings */ -static int lm85_spinup_map[] = {/* .1 sec */ -0 ,1 ,2 ,4 ,7 ,10 ,20 ,40 - }; -static int SPINUP_TO_REG (int spinup ) -{ -int i; - -if ( spinup >= lm85_spinup_map[7 ] ) {return 7 ; } -for ( i =0 ; i <7 ; ++i ) -if ( spinup <= lm85_spinup_map[i] ) -break ; -return ( i &0x07 ); -} -#define SPINUP_FROM_REG (val) (lm85_spinup_map[(val)&0x07]) -/* These are the PWM frequency encodings */ static int lm85_freq_map[] = {/* .1 Hz */ 100 ,150 ,230 ,300 ,380 ,470 ,620 ,940
Greg KH
AppliedQuoted 21 lines · Justin Thiessen
Quoted 10 lines · Greg KH
Quoted 9 lines · Justin Thiessen
Quoted 8 lines · Jean Delvare
Quoted 8 lines
Quoted 2 lines
Hm, there are a number of rejects in this patch. Care to resync up with the next kernel release and resend this?
Ok, try this:
signed off by: Justin Thiessen <[email protected]>
patch for kernel 2.6.X lm85 driver follows:
<snip>
Quoted 7 lines
Applied, thanks.
But now I get the following build warnings:
drivers/i2c/chips/lm85.c:220: warning: 'SMOOTH_TO_REG' defined but not useddrivers/i2c/chips/lm85.c:236: warning: 'SPINUP_TO_REG' defined but not used
Quoted 7 lines
Quoted 1 line
Care to send me a patch to fix them up?
Ok.
Justin Thiessen
Signed off by Justin Thiessen <[email protected]>
Applied, thanks.
greg k-h
PATCH 1/1 2.6 lm85.c driver update
Hi,
Here is a patch to restore fan control to the lm85 driver in the 2.6.X kernel.
It will be followed by a patch to libsensors (chips.c and chips.h) which adjusts entries for the lm85 and compatibles (adm1027) accordingly.
Changes to lm85.c:
It adheres as best as possible to the 3rd fan control interface proposal, using interface names like:
tempN_auto_temp_[off|min|max|crit]
pwmN_auto_pwm_[min|freq|minctl]pwmN_auto_channels
Obviously a bit of translation is needed to fit the:
Off --> Min --> Max --> Crit
model to the native lm85
Min/Hyst/Range/Crit model, particularly as the Range is a set of discrete, non-uniformly-spaced temperature values.
Because of the difference in models, every time tempN_auto_temp_min is adjusted, tempN_auto_temp_off and tempN_auto_temp_max must be recalculated, and new values assigned to Hyst and Range. Otherwise tempN_auto_temp_off and tempN_auto_temp_max would change in an unanticipated (to the user) manner, shifting up or down with corresponding changes in tempN_auto_temp_min.
The code therefore maintains state in tempN_auto_temp_off_desired and tempN_auto_temp_max_desired, using these values to recalculate tempN_auto_temp_off and tempN_auto_temp_max whenever tempN_auto_temp_min varies.
The adjusted "RANGE_TO_REG" function now finds the closest available match rather than simply picking the smallest value bigger than that requested.
The last entry in lm85_range_map has been changed from 980 to 940 to match the value listed in the lm85 datasheet.
I have changed the externally-visible magnitudes of the temperatures from .1C to .001C, in accordance with current interface standards.
I would appreciate any feedback people can provide.
Thanks.
Sincerely,
Justin Thiessen
[email protected]
http://www.penguincomputing.com
===============================
drivers/i2c/chips/lm85.c renamed from drivers/i2c/chips/lm85.c.orig +405 -46
@@ -4,6 +4,7 @@ Copyright (c) 1998, 1999 Frodo Looijaard <[email protected]> Copyright (c) 2002, 2003 Philip Pokorny <[email protected]> Copyright (c) 2003 Margit Schubert-While <[email protected]> + Copyright (c) 2004 Justin Thiessen <[email protected]> Chip details at <http://www.national.com/ds/LM/LM85.pdf>
@@ -29,11 +30,6 @@#include <linux/i2c.h> #include <linux/i2c-sensor.h> #include <linux/i2c-vid.h> -/* -#include <asm/io.h> -*/ - -#undef LM85EXTENDEDFUNC/* Extended functionality */ /* Addresses to scan */ static unsigned short normal_i2c[] = {0x2c ,0x2d ,0x2e ,I2C_CLIENT_END };
@@ -163,8 +159,6 @@ static int lm85_scaling[] = { /* .001 V#define EXT_FROM_REG (val,sensor) (((val)>>(sensor * 2))&0x03) -#ifdef LM85EXTENDEDFUNC /* Extended functionality */ -/* ZONEs have the following parameters: * Limit (low) temp, 1. degC * Hysteresis (below limit), 1. degC (0-15)
@@ -183,20 +177,32 @@ static int lm85_scaling[] = { /* .001 V* Filter constant (or disabled) .1 seconds */ -/* These are the zone temperature range encodings */ -static int lm85_range_map[] = {/* .1 degC */ -20 ,25 ,33 ,40 ,50 ,66 , -80 ,100 ,133 ,160 ,200 ,266 , -320 ,400 ,533 ,800 +/* These are the zone temperature range encodings in .001 degree C */ +static int lm85_range_map[] = { +2000 ,2500 ,3300 ,4000 ,5000 ,6600 , +8000 ,10000 ,13300 ,16000 ,20000 ,26600 , +32000 ,40000 ,53300 ,80000 };static int RANGE_TO_REG (int range ) {int i; -if ( range >= lm85_range_map[15 ] ) {return 15 ; } -for ( i =0 ; i <15 ; ++i ) -if ( range <= lm85_range_map[i] ) -break ; +if ( range < lm85_range_map[0 ] ) { +return 0 ; + }else if ( range > lm85_range_map[15 ] ) { +return 15 ; + }else {/* find closest match */ +for ( i =14 ; i >=0 ; --i ) { +if ( range > lm85_range_map[i] ) {/* range bracketed */ +if ((lm85_range_map[i+1 ] - range) < + (range - lm85_range_map[i])) { + i++; +break ; + } +break ; + } + } + }return ( i &0x0f ); }#define RANGE_FROM_REG (val) (lm85_range_map[(val)&0x0f])
@@ -240,7 +246,7 @@ static int SPINUP_TO_REG( int spinup )/* These are the PWM frequency encodings */ static int lm85_freq_map[] = {/* .1 Hz */ -100 ,150 ,230 ,300 ,380 ,470 ,620 ,980 +100 ,150 ,230 ,300 ,380 ,470 ,620 ,940 };static int FREQ_TO_REG (int freq ) {
@@ -266,13 +272,9 @@ static int FREQ_TO_REG( int freq )* -2 -- PWM responds to manual control */ -#endif /* Extended functionality */ -static int lm85_zone_map[] = {1 ,2 ,3 ,-1 ,0 ,23 ,123 ,-2 };#define ZONE_FROM_REG (val) (lm85_zone_map[((val)>>5)&0x07]) -#ifdef LM85EXTENDEDFUNC /* Extended functionality */ -static int ZONE_TO_REG (int zone ) {int i;
@@ -285,10 +287,8 @@ static int ZONE_TO_REG( int zone )return ( (i &0x07 )<<5 ); } -#endif /* Extended functionality */ - -#define HYST_TO_REG (val) (SENSORS_LIMIT((-(val)+5)/10,0,15)) -#define HYST_FROM_REG (val) (-(val)*10) +#define HYST_TO_REG (val) (SENSORS_LIMIT(((val)+500)/1000,0,15)) +#define HYST_FROM_REG (val) ((val)*1000)#define OFFSET_TO_REG (val) (SENSORS_LIMIT((val)/25,-127,127))#define OFFSET_FROM_REG (val) ((val)*25)
@@ -341,6 +341,14 @@ struct lm85_zone {u8 hyst;/* Low limit hysteresis. (0-15) */ u8 range;/* Temp range, encoded */ s8 critical;/* "All fans ON" temp limit */ +u8 off_desired;/* Actual "off" temperature specified. Preserved +* to prevent "drift" as other autofan control +* values change. +*/ +u8 max_desired;/* Actual "max" temperature specified. Preserved +* to prevent "drift" as other autofan control +* values change. +*/ };struct lm85_autofan {
@@ -439,18 +447,19 @@ static ssize_t set_fan_min(struct device#define show_fan_offset (offset) \ static ssize_t show_fan_##offset (struct device *dev, char *buf) \ { \ - return show_fan(dev, buf, 0x##offset - 1); \ + return show_fan(dev, buf, offset - 1); \ } \ static ssize_t show_fan_##offset##_min (struct device *dev, char *buf) \ { \ - return show_fan_min(dev, buf, 0x##offset - 1); \ + return show_fan_min(dev, buf, offset - 1); \ } \ static ssize_t set_fan_##offset##_min (struct device *dev, \ const char *buf, size_t count) \ { \ - return set_fan_min(dev, buf, count, 0x##offset - 1); \ + return set_fan_min(dev, buf, count, offset - 1); \ } \ -static DEVICE_ATTR(fan##offset##_input, S_IRUGO, show_fan_##offset, NULL) \ +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)
@@ -529,20 +538,21 @@ static ssize_t show_pwm_enable(struct de#define show_pwm_reg (offset) \ static ssize_t show_pwm_##offset (struct device *dev, char *buf) \ { \ - return show_pwm(dev, buf, 0x##offset - 1); \ + return show_pwm(dev, buf, offset - 1); \ } \ static ssize_t set_pwm_##offset (struct device *dev, \ const char *buf, size_t count) \ { \ - return set_pwm(dev, buf, count, 0x##offset - 1); \ + return set_pwm(dev, buf, count, offset - 1); \ } \ static ssize_t show_pwm_enable##offset (struct device *dev, char *buf) \ { \ - return show_pwm_enable(dev, buf, 0x##offset - 1); \ + return show_pwm_enable(dev, buf, offset - 1); \ } \ -static DEVICE_ATTR(fan##offset##_pwm, S_IRUGO | S_IWUSR, \ +static DEVICE_ATTR(fan##offset##_pwm, S_IRUGO | S_IWUSR, \ show_pwm_##offset, set_pwm_##offset) \ -static DEVICE_ATTR(fan##offset##_pwm_enable, S_IRUGO, show_pwm_enable##offset, NULL) +static DEVICE_ATTR(fan##offset##_pwm_enable, S_IRUGO, \ + show_pwm_enable##offset, NULL)show_pwm_reg (1 );show_pwm_reg (2 );
@@ -596,25 +606,25 @@ static ssize_t set_in_max(struct device#define show_in_reg (offset) \ static ssize_t show_in_##offset (struct device *dev, char *buf) \ { \ - return show_in(dev, buf, 0x##offset); \ + return show_in(dev, buf, offset); \ } \ static ssize_t show_in_##offset##_min (struct device *dev, char *buf) \ { \ - return show_in_min(dev, buf, 0x##offset); \ + return show_in_min(dev, buf, offset); \ } \ static ssize_t show_in_##offset##_max (struct device *dev, char *buf) \ { \ - return show_in_max(dev, buf, 0x##offset); \ + return show_in_max(dev, buf, offset); \ } \ static ssize_t set_in_##offset##_min (struct device *dev, \ const char *buf, size_t count) \ { \ - return set_in_min(dev, buf, count, 0x##offset); \ + return set_in_min(dev, buf, count, offset); \ } \ static ssize_t set_in_##offset##_max (struct device *dev, \ const char *buf, size_t count) \ { \ - return set_in_max(dev, buf, count, 0x##offset); \ + return set_in_max(dev, buf, count, offset); \ } \ static DEVICE_ATTR(in##offset##_input, S_IRUGO, show_in_##offset, NULL) \ static DEVICE_ATTR(in##offset##_min, S_IRUGO | S_IWUSR, \
@@ -676,27 +686,28 @@ static ssize_t set_temp_max(struct devic#define show_temp_reg (offset) \ static ssize_t show_temp_##offset (struct device *dev, char *buf) \ { \ - return show_temp(dev, buf, 0x##offset - 1); \ + return show_temp(dev, buf, offset - 1); \ } \ static ssize_t show_temp_##offset##_min (struct device *dev, char *buf) \ { \ - return show_temp_min(dev, buf, 0x##offset - 1); \ + return show_temp_min(dev, buf, offset - 1); \ } \ static ssize_t show_temp_##offset##_max (struct device *dev, char *buf) \ { \ - return show_temp_max(dev, buf, 0x##offset - 1); \ + return show_temp_max(dev, buf, offset - 1); \ } \ static ssize_t set_temp_##offset##_min (struct device *dev, \ const char *buf, size_t count) \ { \ - return set_temp_min(dev, buf, count, 0x##offset - 1); \ + return set_temp_min(dev, buf, count, offset - 1); \ } \ static ssize_t set_temp_##offset##_max (struct device *dev, \ const char *buf, size_t count) \ { \ - return set_temp_max(dev, buf, count, 0x##offset - 1); \ + return set_temp_max(dev, buf, count, offset - 1); \ } \ -static DEVICE_ATTR(temp##offset##_input, S_IRUGO, show_temp_##offset, NULL) \ +static DEVICE_ATTR(temp##offset##_input, S_IRUGO, show_temp_##offset, \ + NULL) \ static DEVICE_ATTR(temp##offset##_min, S_IRUGO | S_IWUSR, \ show_temp_##offset##_min, set_temp_##offset##_min) \ static DEVICE_ATTR(temp##offset##_max, S_IRUGO | S_IWUSR, \
@@ -707,6 +718,330 @@ show_temp_reg(2);show_temp_reg (3 ); +/* Automatic PWM control */ + +static ssize_t show_pwm_auto_channels (struct device *dev,char *buf,int nr) +{ +struct lm85_data *data =lm85_update_device (dev); +return sprintf (buf,"%d\n" ,ZONE_FROM_REG (data->autofan[nr].config)); +} +static ssize_t set_pwm_auto_channels (struct device *dev,const char *buf, +size_t count,int nr) +{ +struct i2c_client *client =to_i2c_client (dev); +struct lm85_data *data =i2c_get_clientdata (client); +int val; + +down (&data->update_lock); + val =simple_strtol (buf,NULL ,10 ); + data->autofan[nr].config = (data->autofan[nr].config & (~0xe0 )) + |ZONE_TO_REG (val) ; +lm85_write_value (client,LM85_REG_AFAN_CONFIG (nr), + data->autofan[nr].config); +up (&data->update_lock); +return count; +} +static ssize_t show_pwm_auto_pwm_min (struct device *dev,char *buf,int nr) +{ +struct lm85_data *data =lm85_update_device (dev); +return sprintf (buf,"%d\n" ,PWM_FROM_REG (data->autofan[nr].min_pwm)); +} +static ssize_t set_pwm_auto_pwm_min (struct device *dev,const char *buf, +size_t count,int nr) +{ +struct i2c_client *client =to_i2c_client (dev); +struct lm85_data *data =i2c_get_clientdata (client); +int val; + +down (&data->update_lock); + val =simple_strtol (buf,NULL ,10 ); + data->autofan[nr].min_pwm =PWM_TO_REG (val); +lm85_write_value (client,LM85_REG_AFAN_MINPWM (nr), + data->autofan[nr].min_pwm); +up (&data->update_lock); +return count; +} +static ssize_t show_pwm_auto_pwm_minctl (struct device *dev,char *buf,int nr) +{ +struct lm85_data *data =lm85_update_device (dev); +return sprintf (buf,"%d\n" , data->autofan[nr].min_off); +} +static ssize_t set_pwm_auto_pwm_minctl (struct device *dev,const char *buf, +size_t count,int nr) +{ +struct i2c_client *client =to_i2c_client (dev); +struct lm85_data *data =i2c_get_clientdata (client); +int val; + +down (&data->update_lock); + val =simple_strtol (buf,NULL ,10 ); + data->autofan[nr].min_off = val; +lm85_write_value (client,LM85_REG_AFAN_SPIKE1 , data->smooth[0 ] + | data->syncpwm3 + | (data->autofan[0 ].min_off ?0x20 :0 ) + | (data->autofan[1 ].min_off ?0x40 :0 ) + | (data->autofan[2 ].min_off ?0x80 :0 ) + ); +up (&data->update_lock); +return count; +} +static ssize_t show_pwm_auto_pwm_freq (struct device *dev,char *buf,int nr) +{ +struct lm85_data *data =lm85_update_device (dev); +return sprintf (buf,"%d\n" ,FREQ_FROM_REG (data->autofan[nr].freq)); +} +static ssize_t set_pwm_auto_pwm_freq (struct device *dev,const char *buf, +size_t count,int nr) +{ +struct i2c_client *client =to_i2c_client (dev); +struct lm85_data *data =i2c_get_clientdata (client); +int val; + +down (&data->update_lock); + val =simple_strtol (buf,NULL ,10 ); + data->autofan[nr].freq =FREQ_TO_REG (val); +lm85_write_value (client,LM85_REG_AFAN_RANGE (nr), + (data->zone[nr].range <<4 ) + | data->autofan[nr].freq + ); +up (&data->update_lock); +return count; +} +#define pwm_auto (offset) \ +static ssize_t show_pwm##offset##_auto_channels (struct device *dev, \ + char *buf) \ +{ \ + return show_pwm_auto_channels(dev, buf, offset - 1); \ +} \ +static ssize_t set_pwm##offset##_auto_channels (struct device *dev, \ + const char *buf, size_t count) \ +{ \ + return set_pwm_auto_channels(dev, buf, count, offset - 1); \ +} \ +static ssize_t show_pwm##offset##_auto_pwm_min (struct device *dev, \ + char *buf) \ +{ \ + return show_pwm_auto_pwm_min(dev, buf, offset - 1); \ +} \ +static ssize_t set_pwm##offset##_auto_pwm_min (struct device *dev, \ + const char *buf, size_t count) \ +{ \ + return set_pwm_auto_pwm_min(dev, buf, count, offset - 1); \ +} \ +static ssize_t show_pwm##offset##_auto_pwm_minctl (struct device *dev, \ + char *buf) \ +{ \ + return show_pwm_auto_pwm_minctl(dev, buf, offset - 1); \ +} \ +static ssize_t set_pwm##offset##_auto_pwm_minctl (struct device *dev, \ + const char *buf, size_t count) \ +{ \ + return set_pwm_auto_pwm_minctl(dev, buf, count, offset - 1);\ +} \ +static ssize_t show_pwm##offset##_auto_pwm_freq (struct device *dev, \ + char *buf) \ +{ \ + return show_pwm_auto_pwm_freq(dev, buf, offset - 1); \ +} \ +static ssize_t set_pwm##offset##_auto_pwm_freq(struct device *dev, \ + const char *buf, size_t count) \ +{ \ + return set_pwm_auto_pwm_freq(dev, buf, count, offset - 1); \ +} \ +static DEVICE_ATTR(pwm##offset##_auto_channels, S_IRUGO | S_IWUSR, \ + show_pwm##offset##_auto_channels, \ + set_pwm##offset##_auto_channels) \ +static DEVICE_ATTR(pwm##offset##_auto_pwm_min, S_IRUGO | S_IWUSR, \ + show_pwm##offset##_auto_pwm_min, \ + set_pwm##offset##_auto_pwm_min) \ +static DEVICE_ATTR(pwm##offset##_auto_pwm_minctl, S_IRUGO | S_IWUSR, \ + show_pwm##offset##_auto_pwm_minctl, \ + set_pwm##offset##_auto_pwm_minctl) \ +static DEVICE_ATTR(pwm##offset##_auto_pwm_freq, S_IRUGO | S_IWUSR, \ + show_pwm##offset##_auto_pwm_freq, \ + set_pwm##offset##_auto_pwm_freq) +pwm_auto (1 ); +pwm_auto (2 ); +pwm_auto (3 ); + +/* Temperature settings for automatic PWM control */ + +static ssize_t show_temp_auto_temp_off (struct device *dev,char *buf,int nr) +{ +struct lm85_data *data =lm85_update_device (dev); +return sprintf (buf,"%d\n" ,TEMP_FROM_REG (data->zone[nr].limit) - +HYST_FROM_REG (data->zone[nr].hyst)); +} +static ssize_t set_temp_auto_temp_off (struct device *dev,const char *buf, +size_t count,int nr) +{ +struct i2c_client *client =to_i2c_client (dev); +struct lm85_data *data =i2c_get_clientdata (client); +int min, val; + +down (&data->update_lock); + val =simple_strtol (buf,NULL ,10 ); + min =TEMP_FROM_REG (data->zone[nr].limit); + data->zone[nr].off_desired =TEMP_TO_REG (val); + data->zone[nr].hyst =HYST_TO_REG (min - val); +if ( nr =0 || nr =1 ) { +lm85_write_value (client,LM85_REG_AFAN_HYST1 , + (data->zone[0 ].hyst <<4 ) + | data->zone[1 ].hyst + ); + }else { +lm85_write_value (client,LM85_REG_AFAN_HYST2 , + (data->zone[2 ].hyst <<4 ) + ); + } +up (&data->update_lock); +return count; +} +static ssize_t show_temp_auto_temp_min (struct device *dev,char *buf,int nr) +{ +struct lm85_data *data =lm85_update_device (dev); +return sprintf (buf,"%d\n" ,TEMP_FROM_REG (data->zone[nr].limit) ); +} +static ssize_t set_temp_auto_temp_min (struct device *dev,const char *buf, +size_t count,int nr) +{ +struct i2c_client *client =to_i2c_client (dev); +struct lm85_data *data =i2c_get_clientdata (client); +int val; + +down (&data->update_lock); + val =simple_strtol (buf,NULL ,10 ); + data->zone[nr].limit =TEMP_TO_REG (val); +lm85_write_value (client,LM85_REG_AFAN_LIMIT (nr), + data->zone[nr].limit); + +/* Update temp_auto_max and temp_auto_range */ + data->zone[nr].range =RANGE_TO_REG ( +TEMP_FROM_REG (data->zone[nr].max_desired) - +TEMP_FROM_REG (data->zone[nr].limit)); +lm85_write_value (client,LM85_REG_AFAN_RANGE (nr), + ((data->zone[nr].range &0x0f ) <<4 ) + | (data->autofan[nr].freq &0x07 )); + +/* Update temp_auto_hyst and temp_auto_off */ + data->zone[nr].hyst =HYST_TO_REG (TEMP_FROM_REG ( + data->zone[nr].limit) -TEMP_FROM_REG ( + data->zone[nr].off_desired)); +if ( nr =0 || nr =1 ) { +lm85_write_value (client,LM85_REG_AFAN_HYST1 , + (data->zone[0 ].hyst <<4 ) + | data->zone[1 ].hyst + ); + }else { +lm85_write_value (client,LM85_REG_AFAN_HYST2 , + (data->zone[2 ].hyst <<4 ) + ); + } +up (&data->update_lock); +return count; +} +static ssize_t show_temp_auto_temp_max (struct device *dev,char *buf,int nr) +{ +struct lm85_data *data =lm85_update_device (dev); +return sprintf (buf,"%d\n" ,TEMP_FROM_REG (data->zone[nr].limit) + +RANGE_FROM_REG (data->zone[nr].range)); +} +static ssize_t set_temp_auto_temp_max (struct device *dev,const char *buf, +size_t count,int nr) +{ +struct i2c_client *client =to_i2c_client (dev); +struct lm85_data *data =i2c_get_clientdata (client); +int min, val; + +down (&data->update_lock); + min =TEMP_FROM_REG (data->zone[nr].limit); + val =simple_strtol (buf,NULL ,10 ); + data->zone[nr].max_desired =TEMP_TO_REG (val); + data->zone[nr].range =RANGE_TO_REG ( + val - min); +lm85_write_value (client,LM85_REG_AFAN_RANGE (nr), + ((data->zone[nr].range &0x0f ) <<4 ) + | (data->autofan[nr].freq &0x07 )); +up (&data->update_lock); +return count; +} +static ssize_t show_temp_auto_temp_crit (struct device *dev,char *buf,int nr) +{ +struct lm85_data *data =lm85_update_device (dev); +return sprintf (buf,"%d\n" ,TEMP_FROM_REG (data->zone[nr].critical)); +} +static ssize_t set_temp_auto_temp_crit (struct device *dev,const char *buf, +size_t count,int nr) +{ +struct i2c_client *client =to_i2c_client (dev); +struct lm85_data *data =i2c_get_clientdata (client); +int val; + +down (&data->update_lock); + val =simple_strtol (buf,NULL ,10 ); + data->zone[nr].critical =TEMP_TO_REG (val); +lm85_write_value (client,LM85_REG_AFAN_CRITICAL (nr), + data->zone[nr].critical); +up (&data->update_lock); +return count; +} +#define temp_auto (offset) \ +static ssize_t show_temp##offset##_auto_temp_off (struct device *dev, \ + char *buf) \ +{ \ + return show_temp_auto_temp_off(dev, buf, offset - 1); \ +} \ +static ssize_t set_temp##offset##_auto_temp_off (struct device *dev, \ + const char *buf, size_t count) \ +{ \ + return set_temp_auto_temp_off(dev, buf, count, offset - 1); \ +} \ +static ssize_t show_temp##offset##_auto_temp_min (struct device *dev, \ + char *buf) \ +{ \ + return show_temp_auto_temp_min(dev, buf, offset - 1); \ +} \ +static ssize_t set_temp##offset##_auto_temp_min (struct device *dev, \ + const char *buf, size_t count) \ +{ \ + return set_temp_auto_temp_min(dev, buf, count, offset - 1); \ +} \ +static ssize_t show_temp##offset##_auto_temp_max (struct device *dev, \ + char *buf) \ +{ \ + return show_temp_auto_temp_max(dev, buf, offset - 1); \ +} \ +static ssize_t set_temp##offset##_auto_temp_max (struct device *dev, \ + const char *buf, size_t count) \ +{ \ + return set_temp_auto_temp_max(dev, buf, count, offset - 1); \ +} \ +static ssize_t show_temp##offset##_auto_temp_crit (struct device *dev, \ + char *buf) \ +{ \ + return show_temp_auto_temp_crit(dev, buf, offset - 1); \ +} \ +static ssize_t set_temp##offset##_auto_temp_crit (struct device *dev, \ + const char *buf, size_t count) \ +{ \ + return set_temp_auto_temp_crit(dev, buf, count, offset - 1);\ +} \ +static DEVICE_ATTR(temp##offset##_auto_temp_off, S_IRUGO | S_IWUSR, \ + show_temp##offset##_auto_temp_off, \ + set_temp##offset##_auto_temp_off) \ +static DEVICE_ATTR(temp##offset##_auto_temp_min, S_IRUGO | S_IWUSR, \ + show_temp##offset##_auto_temp_min, \ + set_temp##offset##_auto_temp_min) \ +static DEVICE_ATTR(temp##offset##_auto_temp_max, S_IRUGO | S_IWUSR, \ + show_temp##offset##_auto_temp_max, \ + set_temp##offset##_auto_temp_max) \ +static DEVICE_ATTR(temp##offset##_auto_temp_crit, S_IRUGO | S_IWUSR, \ + show_temp##offset##_auto_temp_crit, \ + set_temp##offset##_auto_temp_crit) +temp_auto (1 ); +temp_auto (2 ); +temp_auto (3 ); +int lm85_attach_adapter (struct i2c_adapter *adapter) {return i2c_detect (adapter, &addr_data, lm85_detect);
@@ -881,6 +1216,30 @@ int lm85_detect(struct i2c_adapter *adapdevice_create_file (&new_client->dev, &dev_attr_vrm);device_create_file (&new_client->dev, &dev_attr_in0_ref);device_create_file (&new_client->dev, &dev_attr_alarms); +device_create_file (&new_client->dev, &dev_attr_pwm1_auto_channels); +device_create_file (&new_client->dev, &dev_attr_pwm2_auto_channels); +device_create_file (&new_client->dev, &dev_attr_pwm3_auto_channels); +device_create_file (&new_client->dev, &dev_attr_pwm1_auto_pwm_min); +device_create_file (&new_client->dev, &dev_attr_pwm2_auto_pwm_min); +device_create_file (&new_client->dev, &dev_attr_pwm3_auto_pwm_min); +device_create_file (&new_client->dev, &dev_attr_pwm1_auto_pwm_minctl); +device_create_file (&new_client->dev, &dev_attr_pwm2_auto_pwm_minctl); +device_create_file (&new_client->dev, &dev_attr_pwm3_auto_pwm_minctl); +device_create_file (&new_client->dev, &dev_attr_pwm1_auto_pwm_freq); +device_create_file (&new_client->dev, &dev_attr_pwm2_auto_pwm_freq); +device_create_file (&new_client->dev, &dev_attr_pwm3_auto_pwm_freq); +device_create_file (&new_client->dev, &dev_attr_temp1_auto_temp_off); +device_create_file (&new_client->dev, &dev_attr_temp2_auto_temp_off); +device_create_file (&new_client->dev, &dev_attr_temp3_auto_temp_off); +device_create_file (&new_client->dev, &dev_attr_temp1_auto_temp_min); +device_create_file (&new_client->dev, &dev_attr_temp2_auto_temp_min); +device_create_file (&new_client->dev, &dev_attr_temp3_auto_temp_min); +device_create_file (&new_client->dev, &dev_attr_temp1_auto_temp_max); +device_create_file (&new_client->dev, &dev_attr_temp2_auto_temp_max); +device_create_file (&new_client->dev, &dev_attr_temp3_auto_temp_max); +device_create_file (&new_client->dev, &dev_attr_temp1_auto_temp_crit); +device_create_file (&new_client->dev, &dev_attr_temp2_auto_temp_crit); +device_create_file (&new_client->dev, &dev_attr_temp3_auto_temp_crit);return 0 ;
@@ -1164,7 +1523,7 @@ static void __exit sm_lm85_exit(void)* post 2.7.0 CVS changes. */ MODULE_LICENSE ("GPL" ); -MODULE_AUTHOR ("Philip Pokorny <[email protected]>, Margit Schubert-While <[email protected]>" ); +MODULE_AUTHOR ("Philip Pokorny <[email protected]>, Margit Schubert-While <[email protected]>, Justin Thiessen <[email protected]" );MODULE_DESCRIPTION ("LM85-B, LM85-C driver" );module_init (sm_lm85_init);
PATCH 1/1 2.6 lm85.c driver update
Quoted 3 lines · Justin Thiessen
Hi,
Here is a patch to restore fan control to the lm85 driver in the 2.6.X kernel.
Hm, there are a number of rejects in this patch. Care to resync up with the next kernel release and resend this?
thanks,
greg k-h
PATCH 1/1 2.6 lm85.c driver update
In reply to Justin ThiessenQuoted 10 lines · Jean Delvare
Quoted 10 lines
Quoted 2 lines
Hm, there are a number of rejects in this patch. Care to resync up with the next kernel release and resend this?
Ok, try this:
signed off by: Justin Thiessen <[email protected]>
patch for kernel 2.6.X lm85 driver follows:
----------------------------------------------------
Quoted 6 lines
drivers/i2c/chips/lm85.c
Quoted 2 lines
drivers/i2c/chips/lm85.c renamed from drivers/i2c/chips/lm85.c.orig +0
-0Unfortunately this won't be OK either. 2.6.10-rc1 has a lot of i2c changes, including a number of things your patch attempts to fix (macro abuse reported by Mark Hoffman). So it won't apply to Greg's tree.
Aiiiiiiiieeeeeee.
Quoted 2 lines
Please provide your patch against 2.6.10-rc1. Sorry that you always seem to provide your patch against the not-really-last tree ;)
It's not your fault I expect kernel releases to be rolled out by punchcards and horse-drawn wagon.
Thanks for the heads-up; see the following patch for a new, "improved" version.
Justin Thiessen
---------------
[email protected]
----
Signed off by: Justin Thiessen <[email protected]
drivers/i2c/chips/lm85.c renamed from drivers/i2c/chips/lm85.c.orig +388 -29
@@ -4,6 +4,7 @@ Copyright (c) 1998, 1999 Frodo Looijaard <[email protected]> Copyright (c) 2002, 2003 Philip Pokorny <[email protected]> Copyright (c) 2003 Margit Schubert-While <[email protected]> + Copyright (c) 2004 Justin Thiessen <[email protected]> Chip details at <http://www.national.com/ds/LM/LM85.pdf>
@@ -29,11 +30,6 @@#include <linux/i2c.h> #include <linux/i2c-sensor.h> #include <linux/i2c-vid.h> -/* -#include <asm/io.h> -*/ - -#undef LM85EXTENDEDFUNC/* Extended functionality */ /* Addresses to scan */ static unsigned short normal_i2c[] = {0x2c ,0x2d ,0x2e ,I2C_CLIENT_END };
@@ -163,8 +159,6 @@ static int lm85_scaling[] = { /* .001 V#define EXT_FROM_REG (val,sensor) (((val)>>(sensor * 2))&0x03) -#ifdef LM85EXTENDEDFUNC /* Extended functionality */ -/* ZONEs have the following parameters: * Limit (low) temp, 1. degC * Hysteresis (below limit), 1. degC (0-15)
@@ -183,20 +177,32 @@ static int lm85_scaling[] = { /* .001 V* Filter constant (or disabled) .1 seconds */ -/* These are the zone temperature range encodings */ -static int lm85_range_map[] = {/* .1 degC */ -20 ,25 ,33 ,40 ,50 ,66 , -80 ,100 ,133 ,160 ,200 ,266 , -320 ,400 ,533 ,800 +/* These are the zone temperature range encodings in .001 degree C */ +static int lm85_range_map[] = { +2000 ,2500 ,3300 ,4000 ,5000 ,6600 , +8000 ,10000 ,13300 ,16000 ,20000 ,26600 , +32000 ,40000 ,53300 ,80000 };static int RANGE_TO_REG (int range ) {int i; -if ( range >= lm85_range_map[15 ] ) {return 15 ; } -for ( i =0 ; i <15 ; ++i ) -if ( range <= lm85_range_map[i] ) -break ; +if ( range < lm85_range_map[0 ] ) { +return 0 ; + }else if ( range > lm85_range_map[15 ] ) { +return 15 ; + }else {/* find closest match */ +for ( i =14 ; i >=0 ; --i ) { +if ( range > lm85_range_map[i] ) {/* range bracketed */ +if ((lm85_range_map[i+1 ] - range) < + (range - lm85_range_map[i])) { + i++; +break ; + } +break ; + } + } + }return ( i &0x0f ); }#define RANGE_FROM_REG (val) (lm85_range_map[(val)&0x0f])
@@ -240,7 +246,7 @@ static int SPINUP_TO_REG( int spinup )/* These are the PWM frequency encodings */ static int lm85_freq_map[] = {/* .1 Hz */ -100 ,150 ,230 ,300 ,380 ,470 ,620 ,980 +100 ,150 ,230 ,300 ,380 ,470 ,620 ,940 };static int FREQ_TO_REG (int freq ) {
@@ -266,13 +272,9 @@ static int FREQ_TO_REG( int freq )* -2 -- PWM responds to manual control */ -#endif /* Extended functionality */ -static int lm85_zone_map[] = {1 ,2 ,3 ,-1 ,0 ,23 ,123 ,-2 };#define ZONE_FROM_REG (val) (lm85_zone_map[((val)>>5)&0x07]) -#ifdef LM85EXTENDEDFUNC /* Extended functionality */ -static int ZONE_TO_REG (int zone ) {int i;
@@ -285,10 +287,8 @@ static int ZONE_TO_REG( int zone )return ( (i &0x07 )<<5 ); } -#endif /* Extended functionality */ - -#define HYST_TO_REG (val) (SENSORS_LIMIT((-(val)+5)/10,0,15)) -#define HYST_FROM_REG (val) (-(val)*10) +#define HYST_TO_REG (val) (SENSORS_LIMIT(((val)+500)/1000,0,15)) +#define HYST_FROM_REG (val) ((val)*1000)#define OFFSET_TO_REG (val) (SENSORS_LIMIT((val)/25,-127,127))#define OFFSET_FROM_REG (val) ((val)*25)
@@ -338,6 +338,14 @@ struct lm85_zone {u8 hyst;/* Low limit hysteresis. (0-15) */ u8 range;/* Temp range, encoded */ s8 critical;/* "All fans ON" temp limit */ +u8 off_desired;/* Actual "off" temperature specified. Preserved +* to prevent "drift" as other autofan control +* values change. +*/ +u8 max_desired;/* Actual "max" temperature specified. Preserved +* to prevent "drift" as other autofan control +* values change. +*/ };struct lm85_autofan {
@@ -448,7 +456,8 @@ static ssize_t set_fan_##offset##_min (s { \return set_fan_min (dev, buf, count, offset -1 ); \ } \ -static DEVICE_ATTR(fan##offset##_input, S_IRUGO, show_fan_##offset, NULL);\ +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);
@@ -615,7 +624,8 @@ static ssize_t set_in_##offset##_max (st { \return set_in_max (dev, buf, count, offset); \ } \ -static DEVICE_ATTR(in##offset##_input, S_IRUGO, show_in_##offset, NULL); \ +static DEVICE_ATTR(in##offset##_input, S_IRUGO, show_in_##offset, \ + NULL); \ static DEVICE_ATTR(in##offset##_min, S_IRUGO | S_IWUSR, \ show_in_##offset##_min, set_in_##offset##_min); \ static DEVICE_ATTR(in##offset##_max, S_IRUGO | S_IWUSR, \
@@ -695,7 +705,8 @@ static ssize_t set_temp_##offset##_max ( { \return set_temp_max (dev, buf, count, offset -1 ); \ } \ -static DEVICE_ATTR(temp##offset##_input, S_IRUGO, show_temp_##offset, NULL); \ +static DEVICE_ATTR(temp##offset##_input, S_IRUGO, show_temp_##offset, \ + NULL); \ static DEVICE_ATTR(temp##offset##_min, S_IRUGO | S_IWUSR, \ show_temp_##offset##_min, set_temp_##offset##_min); \ static DEVICE_ATTR(temp##offset##_max, S_IRUGO | S_IWUSR, \
@@ -706,6 +717,330 @@ show_temp_reg(2);show_temp_reg (3 ); +/* Automatic PWM control */ + +static ssize_t show_pwm_auto_channels (struct device *dev,char *buf,int nr) +{ +struct lm85_data *data =lm85_update_device (dev); +return sprintf (buf,"%d\n" ,ZONE_FROM_REG (data->autofan[nr].config)); +} +static ssize_t set_pwm_auto_channels (struct device *dev,const char *buf, +size_t count,int nr) +{ +struct i2c_client *client =to_i2c_client (dev); +struct lm85_data *data =i2c_get_clientdata (client); +int val; + +down (&data->update_lock); + val =simple_strtol (buf,NULL ,10 ); + data->autofan[nr].config = (data->autofan[nr].config & (~0xe0 )) + |ZONE_TO_REG (val) ; +lm85_write_value (client,LM85_REG_AFAN_CONFIG (nr), + data->autofan[nr].config); +up (&data->update_lock); +return count; +} +static ssize_t show_pwm_auto_pwm_min (struct device *dev,char *buf,int nr) +{ +struct lm85_data *data =lm85_update_device (dev); +return sprintf (buf,"%d\n" ,PWM_FROM_REG (data->autofan[nr].min_pwm)); +} +static ssize_t set_pwm_auto_pwm_min (struct device *dev,const char *buf, +size_t count,int nr) +{ +struct i2c_client *client =to_i2c_client (dev); +struct lm85_data *data =i2c_get_clientdata (client); +int val; + +down (&data->update_lock); + val =simple_strtol (buf,NULL ,10 ); + data->autofan[nr].min_pwm =PWM_TO_REG (val); +lm85_write_value (client,LM85_REG_AFAN_MINPWM (nr), + data->autofan[nr].min_pwm); +up (&data->update_lock); +return count; +} +static ssize_t show_pwm_auto_pwm_minctl (struct device *dev,char *buf,int nr) +{ +struct lm85_data *data =lm85_update_device (dev); +return sprintf (buf,"%d\n" , data->autofan[nr].min_off); +} +static ssize_t set_pwm_auto_pwm_minctl (struct device *dev,const char *buf, +size_t count,int nr) +{ +struct i2c_client *client =to_i2c_client (dev); +struct lm85_data *data =i2c_get_clientdata (client); +int val; + +down (&data->update_lock); + val =simple_strtol (buf,NULL ,10 ); + data->autofan[nr].min_off = val; +lm85_write_value (client,LM85_REG_AFAN_SPIKE1 , data->smooth[0 ] + | data->syncpwm3 + | (data->autofan[0 ].min_off ?0x20 :0 ) + | (data->autofan[1 ].min_off ?0x40 :0 ) + | (data->autofan[2 ].min_off ?0x80 :0 ) + ); +up (&data->update_lock); +return count; +} +static ssize_t show_pwm_auto_pwm_freq (struct device *dev,char *buf,int nr) +{ +struct lm85_data *data =lm85_update_device (dev); +return sprintf (buf,"%d\n" ,FREQ_FROM_REG (data->autofan[nr].freq)); +} +static ssize_t set_pwm_auto_pwm_freq (struct device *dev,const char *buf, +size_t count,int nr) +{ +struct i2c_client *client =to_i2c_client (dev); +struct lm85_data *data =i2c_get_clientdata (client); +int val; + +down (&data->update_lock); + val =simple_strtol (buf,NULL ,10 ); + data->autofan[nr].freq =FREQ_TO_REG (val); +lm85_write_value (client,LM85_REG_AFAN_RANGE (nr), + (data->zone[nr].range <<4 ) + | data->autofan[nr].freq + ); +up (&data->update_lock); +return count; +} +#define pwm_auto (offset) \ +static ssize_t show_pwm##offset##_auto_channels (struct device *dev, \ + char *buf) \ +{ \ + return show_pwm_auto_channels(dev, buf, offset - 1); \ +} \ +static ssize_t set_pwm##offset##_auto_channels (struct device *dev, \ + const char *buf, size_t count) \ +{ \ + return set_pwm_auto_channels(dev, buf, count, offset - 1); \ +} \ +static ssize_t show_pwm##offset##_auto_pwm_min (struct device *dev, \ + char *buf) \ +{ \ + return show_pwm_auto_pwm_min(dev, buf, offset - 1); \ +} \ +static ssize_t set_pwm##offset##_auto_pwm_min (struct device *dev, \ + const char *buf, size_t count) \ +{ \ + return set_pwm_auto_pwm_min(dev, buf, count, offset - 1); \ +} \ +static ssize_t show_pwm##offset##_auto_pwm_minctl (struct device *dev, \ + char *buf) \ +{ \ + return show_pwm_auto_pwm_minctl(dev, buf, offset - 1); \ +} \ +static ssize_t set_pwm##offset##_auto_pwm_minctl (struct device *dev, \ + const char *buf, size_t count) \ +{ \ + return set_pwm_auto_pwm_minctl(dev, buf, count, offset - 1); \ +} \ +static ssize_t show_pwm##offset##_auto_pwm_freq (struct device *dev, \ + char *buf) \ +{ \ + return show_pwm_auto_pwm_freq(dev, buf, offset - 1); \ +} \ +static ssize_t set_pwm##offset##_auto_pwm_freq(struct device *dev, \ + const char *buf, size_t count) \ +{ \ + return set_pwm_auto_pwm_freq(dev, buf, count, offset - 1); \ +} \ +static DEVICE_ATTR(pwm##offset##_auto_channels, S_IRUGO | S_IWUSR, \ + show_pwm##offset##_auto_channels, \ + set_pwm##offset##_auto_channels); \ +static DEVICE_ATTR(pwm##offset##_auto_pwm_min, S_IRUGO | S_IWUSR, \ + show_pwm##offset##_auto_pwm_min, \ + set_pwm##offset##_auto_pwm_min); \ +static DEVICE_ATTR(pwm##offset##_auto_pwm_minctl, S_IRUGO | S_IWUSR, \ + show_pwm##offset##_auto_pwm_minctl, \ + set_pwm##offset##_auto_pwm_minctl); \ +static DEVICE_ATTR(pwm##offset##_auto_pwm_freq, S_IRUGO | S_IWUSR, \ + show_pwm##offset##_auto_pwm_freq, \ + set_pwm##offset##_auto_pwm_freq); +pwm_auto (1 ); +pwm_auto (2 ); +pwm_auto (3 ); + +/* Temperature settings for automatic PWM control */ + +static ssize_t show_temp_auto_temp_off (struct device *dev,char *buf,int nr) +{ +struct lm85_data *data =lm85_update_device (dev); +return sprintf (buf,"%d\n" ,TEMP_FROM_REG (data->zone[nr].limit) - +HYST_FROM_REG (data->zone[nr].hyst)); +} +static ssize_t set_temp_auto_temp_off (struct device *dev,const char *buf, +size_t count,int nr) +{ +struct i2c_client *client =to_i2c_client (dev); +struct lm85_data *data =i2c_get_clientdata (client); +int min, val; + +down (&data->update_lock); + val =simple_strtol (buf,NULL ,10 ); + min =TEMP_FROM_REG (data->zone[nr].limit); + data->zone[nr].off_desired =TEMP_TO_REG (val); + data->zone[nr].hyst =HYST_TO_REG (min - val); +if ( nr =0 || nr =1 ) { +lm85_write_value (client,LM85_REG_AFAN_HYST1 , + (data->zone[0 ].hyst <<4 ) + | data->zone[1 ].hyst + ); + }else { +lm85_write_value (client,LM85_REG_AFAN_HYST2 , + (data->zone[2 ].hyst <<4 ) + ); + } +up (&data->update_lock); +return count; +} +static ssize_t show_temp_auto_temp_min (struct device *dev,char *buf,int nr) +{ +struct lm85_data *data =lm85_update_device (dev); +return sprintf (buf,"%d\n" ,TEMP_FROM_REG (data->zone[nr].limit) ); +} +static ssize_t set_temp_auto_temp_min (struct device *dev,const char *buf, +size_t count,int nr) +{ +struct i2c_client *client =to_i2c_client (dev); +struct lm85_data *data =i2c_get_clientdata (client); +int val; + +down (&data->update_lock); + val =simple_strtol (buf,NULL ,10 ); + data->zone[nr].limit =TEMP_TO_REG (val); +lm85_write_value (client,LM85_REG_AFAN_LIMIT (nr), + data->zone[nr].limit); + +/* Update temp_auto_max and temp_auto_range */ + data->zone[nr].range =RANGE_TO_REG ( +TEMP_FROM_REG (data->zone[nr].max_desired) - +TEMP_FROM_REG (data->zone[nr].limit)); +lm85_write_value (client,LM85_REG_AFAN_RANGE (nr), + ((data->zone[nr].range &0x0f ) <<4 ) + | (data->autofan[nr].freq &0x07 )); + +/* Update temp_auto_hyst and temp_auto_off */ + data->zone[nr].hyst =HYST_TO_REG (TEMP_FROM_REG ( + data->zone[nr].limit) -TEMP_FROM_REG ( + data->zone[nr].off_desired)); +if ( nr =0 || nr =1 ) { +lm85_write_value (client,LM85_REG_AFAN_HYST1 , + (data->zone[0 ].hyst <<4 ) + | data->zone[1 ].hyst + ); + }else { +lm85_write_value (client,LM85_REG_AFAN_HYST2 , + (data->zone[2 ].hyst <<4 ) + ); + } +up (&data->update_lock); +return count; +} +static ssize_t show_temp_auto_temp_max (struct device *dev,char *buf,int nr) +{ +struct lm85_data *data =lm85_update_device (dev); +return sprintf (buf,"%d\n" ,TEMP_FROM_REG (data->zone[nr].limit) + +RANGE_FROM_REG (data->zone[nr].range)); +} +static ssize_t set_temp_auto_temp_max (struct device *dev,const char *buf, +size_t count,int nr) +{ +struct i2c_client *client =to_i2c_client (dev); +struct lm85_data *data =i2c_get_clientdata (client); +int min, val; + +down (&data->update_lock); + min =TEMP_FROM_REG (data->zone[nr].limit); + val =simple_strtol (buf,NULL ,10 ); + data->zone[nr].max_desired =TEMP_TO_REG (val); + data->zone[nr].range =RANGE_TO_REG ( + val - min); +lm85_write_value (client,LM85_REG_AFAN_RANGE (nr), + ((data->zone[nr].range &0x0f ) <<4 ) + | (data->autofan[nr].freq &0x07 )); +up (&data->update_lock); +return count; +} +static ssize_t show_temp_auto_temp_crit (struct device *dev,char *buf,int nr) +{ +struct lm85_data *data =lm85_update_device (dev); +return sprintf (buf,"%d\n" ,TEMP_FROM_REG (data->zone[nr].critical)); +} +static ssize_t set_temp_auto_temp_crit (struct device *dev,const char *buf, +size_t count,int nr) +{ +struct i2c_client *client =to_i2c_client (dev); +struct lm85_data *data =i2c_get_clientdata (client); +int val; + +down (&data->update_lock); + val =simple_strtol (buf,NULL ,10 ); + data->zone[nr].critical =TEMP_TO_REG (val); +lm85_write_value (client,LM85_REG_AFAN_CRITICAL (nr), + data->zone[nr].critical); +up (&data->update_lock); +return count; +} +#define temp_auto (offset) \ +static ssize_t show_temp##offset##_auto_temp_off (struct device *dev, \ + char *buf) \ +{ \ + return show_temp_auto_temp_off(dev, buf, offset - 1); \ +} \ +static ssize_t set_temp##offset##_auto_temp_off (struct device *dev, \ + const char *buf, size_t count) \ +{ \ + return set_temp_auto_temp_off(dev, buf, count, offset - 1); \ +} \ +static ssize_t show_temp##offset##_auto_temp_min (struct device *dev, \ + char *buf) \ +{ \ + return show_temp_auto_temp_min(dev, buf, offset - 1); \ +} \ +static ssize_t set_temp##offset##_auto_temp_min (struct device *dev, \ + const char *buf, size_t count) \ +{ \ + return set_temp_auto_temp_min(dev, buf, count, offset - 1); \ +} \ +static ssize_t show_temp##offset##_auto_temp_max (struct device *dev, \ + char *buf) \ +{ \ + return show_temp_auto_temp_max(dev, buf, offset - 1); \ +} \ +static ssize_t set_temp##offset##_auto_temp_max (struct device *dev, \ + const char *buf, size_t count) \ +{ \ + return set_temp_auto_temp_max(dev, buf, count, offset - 1); \ +} \ +static ssize_t show_temp##offset##_auto_temp_crit (struct device *dev, \ + char *buf) \ +{ \ + return show_temp_auto_temp_crit(dev, buf, offset - 1); \ +} \ +static ssize_t set_temp##offset##_auto_temp_crit (struct device *dev, \ + const char *buf, size_t count) \ +{ \ + return set_temp_auto_temp_crit(dev, buf, count, offset - 1); \ +} \ +static DEVICE_ATTR(temp##offset##_auto_temp_off, S_IRUGO | S_IWUSR, \ + show_temp##offset##_auto_temp_off, \ + set_temp##offset##_auto_temp_off); \ +static DEVICE_ATTR(temp##offset##_auto_temp_min, S_IRUGO | S_IWUSR, \ + show_temp##offset##_auto_temp_min, \ + set_temp##offset##_auto_temp_min); \ +static DEVICE_ATTR(temp##offset##_auto_temp_max, S_IRUGO | S_IWUSR, \ + show_temp##offset##_auto_temp_max, \ + set_temp##offset##_auto_temp_max); \ +static DEVICE_ATTR(temp##offset##_auto_temp_crit, S_IRUGO | S_IWUSR, \ + show_temp##offset##_auto_temp_crit, \ + set_temp##offset##_auto_temp_crit); +temp_auto (1 ); +temp_auto (2 ); +temp_auto (3 ); +int lm85_attach_adapter (struct i2c_adapter *adapter) {if (!(adapter->class &I2C_CLASS_HWMON ))
@@ -879,6 +1214,30 @@ int lm85_detect(struct i2c_adapter *adapdevice_create_file (&new_client->dev, &dev_attr_vrm);device_create_file (&new_client->dev, &dev_attr_cpu0_vid);device_create_file (&new_client->dev, &dev_attr_alarms); +device_create_file (&new_client->dev, &dev_attr_pwm1_auto_channels); +device_create_file (&new_client->dev, &dev_attr_pwm2_auto_channels); +device_create_file (&new_client->dev, &dev_attr_pwm3_auto_channels); +device_create_file (&new_client->dev, &dev_attr_pwm1_auto_pwm_min); +device_create_file (&new_client->dev, &dev_attr_pwm2_auto_pwm_min); +device_create_file (&new_client->dev, &dev_attr_pwm3_auto_pwm_min); +device_create_file (&new_client->dev, &dev_attr_pwm1_auto_pwm_minctl); +device_create_file (&new_client->dev, &dev_attr_pwm2_auto_pwm_minctl); +device_create_file (&new_client->dev, &dev_attr_pwm3_auto_pwm_minctl); +device_create_file (&new_client->dev, &dev_attr_pwm1_auto_pwm_freq); +device_create_file (&new_client->dev, &dev_attr_pwm2_auto_pwm_freq); +device_create_file (&new_client->dev, &dev_attr_pwm3_auto_pwm_freq); +device_create_file (&new_client->dev, &dev_attr_temp1_auto_temp_off); +device_create_file (&new_client->dev, &dev_attr_temp2_auto_temp_off); +device_create_file (&new_client->dev, &dev_attr_temp3_auto_temp_off); +device_create_file (&new_client->dev, &dev_attr_temp1_auto_temp_min); +device_create_file (&new_client->dev, &dev_attr_temp2_auto_temp_min); +device_create_file (&new_client->dev, &dev_attr_temp3_auto_temp_min); +device_create_file (&new_client->dev, &dev_attr_temp1_auto_temp_max); +device_create_file (&new_client->dev, &dev_attr_temp2_auto_temp_max); +device_create_file (&new_client->dev, &dev_attr_temp3_auto_temp_max); +device_create_file (&new_client->dev, &dev_attr_temp1_auto_temp_crit); +device_create_file (&new_client->dev, &dev_attr_temp2_auto_temp_crit); +device_create_file (&new_client->dev, &dev_attr_temp3_auto_temp_crit);return 0 ;
@@ -1162,7 +1521,7 @@ static void __exit sm_lm85_exit(void)* post 2.7.0 CVS changes. */ MODULE_LICENSE ("GPL" ); -MODULE_AUTHOR ("Philip Pokorny <[email protected]>, Margit Schubert-While <[email protected]>" ); +MODULE_AUTHOR ("Philip Pokorny <[email protected]>, Margit Schubert-While <[email protected]>, Justin Thiessen <[email protected]" );MODULE_DESCRIPTION ("LM85-B, LM85-C driver" );module_init (sm_lm85_init);