2005-04-17 02:20:36 +04:00
/*
lm85 . c - Part of lm_sensors , Linux kernel modules for hardware
monitoring
2008-04-29 16:03:37 +04:00
Copyright ( c ) 1998 , 1999 Frodo Looijaard < frodol @ dds . nl >
2005-04-17 02:20:36 +04:00
Copyright ( c ) 2002 , 2003 Philip Pokorny < ppokorny @ penguincomputing . com >
Copyright ( c ) 2003 Margit Schubert - While < margitsw @ t - online . de >
Copyright ( c ) 2004 Justin Thiessen < jthiessen @ penguincomputing . com >
2009-12-09 22:35:53 +03:00
Copyright ( C ) 2007 - - 2009 Jean Delvare < khali @ linux - fr . org >
2005-04-17 02:20:36 +04:00
Chip details at < http : //www.national.com/ds/LM/LM85.pdf>
This program is free software ; you can redistribute it and / or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation ; either version 2 of the License , or
( at your option ) any later version .
This program is distributed in the hope that it will be useful ,
but WITHOUT ANY WARRANTY ; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
GNU General Public License for more details .
You should have received a copy of the GNU General Public License
along with this program ; if not , write to the Free Software
Foundation , Inc . , 675 Mass Ave , Cambridge , MA 0213 9 , USA .
*/
# include <linux/module.h>
# include <linux/init.h>
# include <linux/slab.h>
# include <linux/jiffies.h>
# include <linux/i2c.h>
2005-07-16 05:39:18 +04:00
# include <linux/hwmon.h>
2005-07-31 23:52:01 +04:00
# include <linux/hwmon-vid.h>
2007-07-05 22:36:12 +04:00
# include <linux/hwmon-sysfs.h>
2005-07-16 05:39:18 +04:00
# include <linux/err.h>
2006-01-19 01:19:26 +03:00
# include <linux/mutex.h>
2005-04-17 02:20:36 +04:00
/* Addresses to scan */
2008-02-18 06:28:03 +03:00
static const unsigned short normal_i2c [ ] = { 0x2c , 0x2d , 0x2e , I2C_CLIENT_END } ;
2005-04-17 02:20:36 +04:00
2009-12-14 23:17:27 +03:00
enum chips {
any_chip , lm85b , lm85c ,
adm1027 , adt7463 , adt7468 ,
2011-02-19 19:27:47 +03:00
emc6d100 , emc6d102 , emc6d103 , emc6d103s
2009-12-14 23:17:27 +03:00
} ;
2005-04-17 02:20:36 +04:00
/* The LM85 registers */
# define LM85_REG_IN(nr) (0x20 + (nr))
# define LM85_REG_IN_MIN(nr) (0x44 + (nr) * 2)
# define LM85_REG_IN_MAX(nr) (0x45 + (nr) * 2)
# define LM85_REG_TEMP(nr) (0x25 + (nr))
# define LM85_REG_TEMP_MIN(nr) (0x4e + (nr) * 2)
# define LM85_REG_TEMP_MAX(nr) (0x4f + (nr) * 2)
/* Fan speeds are LSB, MSB (2 bytes) */
2008-04-29 16:03:37 +04:00
# define LM85_REG_FAN(nr) (0x28 + (nr) * 2)
# define LM85_REG_FAN_MIN(nr) (0x54 + (nr) * 2)
2005-04-17 02:20:36 +04:00
# define LM85_REG_PWM(nr) (0x30 + (nr))
# define LM85_REG_COMPANY 0x3e
# define LM85_REG_VERSTEP 0x3f
2008-11-13 00:26:59 +03:00
# define ADT7468_REG_CFG5 0x7c
2010-10-28 22:31:50 +04:00
# define ADT7468_OFF64 (1 << 0)
# define ADT7468_HFPWM (1 << 1)
2008-11-13 00:26:59 +03:00
# define IS_ADT7468_OFF64(data) \
( ( data ) - > type = = adt7468 & & ! ( ( data ) - > cfg5 & ADT7468_OFF64 ) )
2010-10-28 22:31:50 +04:00
# define IS_ADT7468_HFPWM(data) \
( ( data ) - > type = = adt7468 & & ! ( ( data ) - > cfg5 & ADT7468_HFPWM ) )
2008-11-13 00:26:59 +03:00
2005-04-17 02:20:36 +04:00
/* These are the recognized values for the above regs */
# define LM85_COMPANY_NATIONAL 0x01
# define LM85_COMPANY_ANALOG_DEV 0x41
2008-04-29 16:03:37 +04:00
# define LM85_COMPANY_SMSC 0x5c
2005-04-17 02:20:36 +04:00
# define LM85_VERSTEP_VMASK 0xf0
# define LM85_VERSTEP_GENERIC 0x60
2009-03-10 22:55:47 +03:00
# define LM85_VERSTEP_GENERIC2 0x70
2005-04-17 02:20:36 +04:00
# define LM85_VERSTEP_LM85C 0x60
# define LM85_VERSTEP_LM85B 0x62
2009-09-15 19:18:14 +04:00
# define LM85_VERSTEP_LM96000_1 0x68
# define LM85_VERSTEP_LM96000_2 0x69
2005-04-17 02:20:36 +04:00
# define LM85_VERSTEP_ADM1027 0x60
# define LM85_VERSTEP_ADT7463 0x62
# define LM85_VERSTEP_ADT7463C 0x6A
2008-11-13 00:26:59 +03:00
# define LM85_VERSTEP_ADT7468_1 0x71
# define LM85_VERSTEP_ADT7468_2 0x72
2005-04-17 02:20:36 +04:00
# define LM85_VERSTEP_EMC6D100_A0 0x60
# define LM85_VERSTEP_EMC6D100_A1 0x61
# define LM85_VERSTEP_EMC6D102 0x65
2011-02-18 11:18:26 +03:00
# define LM85_VERSTEP_EMC6D103_A0 0x68
# define LM85_VERSTEP_EMC6D103_A1 0x69
# define LM85_VERSTEP_EMC6D103S 0x6A /* Also known as EMC6D103:A2 */
2005-04-17 02:20:36 +04:00
# define LM85_REG_CONFIG 0x40
# define LM85_REG_ALARM1 0x41
# define LM85_REG_ALARM2 0x42
# define LM85_REG_VID 0x43
/* Automated FAN control */
# define LM85_REG_AFAN_CONFIG(nr) (0x5c + (nr))
# define LM85_REG_AFAN_RANGE(nr) (0x5f + (nr))
# define LM85_REG_AFAN_SPIKE1 0x62
# define LM85_REG_AFAN_MINPWM(nr) (0x64 + (nr))
# define LM85_REG_AFAN_LIMIT(nr) (0x67 + (nr))
# define LM85_REG_AFAN_CRITICAL(nr) (0x6a + (nr))
# define LM85_REG_AFAN_HYST1 0x6d
# define LM85_REG_AFAN_HYST2 0x6e
# define ADM1027_REG_EXTEND_ADC1 0x76
# define ADM1027_REG_EXTEND_ADC2 0x77
# define EMC6D100_REG_ALARM3 0x7d
/* IN5, IN6 and IN7 */
2008-04-29 16:03:37 +04:00
# define EMC6D100_REG_IN(nr) (0x70 + ((nr) - 5))
# define EMC6D100_REG_IN_MIN(nr) (0x73 + ((nr) - 5) * 2)
# define EMC6D100_REG_IN_MAX(nr) (0x74 + ((nr) - 5) * 2)
2005-04-17 02:20:36 +04:00
# define EMC6D102_REG_EXTEND_ADC1 0x85
# define EMC6D102_REG_EXTEND_ADC2 0x86
# define EMC6D102_REG_EXTEND_ADC3 0x87
# define EMC6D102_REG_EXTEND_ADC4 0x88
2008-04-29 16:03:37 +04:00
/* Conversions. Rounding and limit checking is only done on the TO_REG
2005-04-17 02:20:36 +04:00
variants . Note that you should be a bit careful with which arguments
these macros are called : arguments may be evaluated more than once .
*/
2011-03-31 05:57:33 +04:00
/* IN are scaled according to built-in resistors */
2008-06-25 16:47:35 +04:00
static const int lm85_scaling [ ] = { /* .001 Volts */
2008-04-29 16:03:37 +04:00
2500 , 2250 , 3300 , 5000 , 12000 ,
3300 , 1500 , 1800 /*EMC6D100*/
} ;
# define SCALE(val, from, to) (((val) * (to) + ((from) / 2)) / (from))
2005-04-17 02:20:36 +04:00
2008-04-29 16:03:37 +04:00
# define INS_TO_REG(n, val) \
SENSORS_LIMIT ( SCALE ( val , lm85_scaling [ n ] , 192 ) , 0 , 255 )
2005-04-17 02:20:36 +04:00
2008-04-29 16:03:37 +04:00
# define INSEXT_FROM_REG(n, val, ext) \
2007-07-05 22:38:32 +04:00
SCALE ( ( ( val ) < < 4 ) + ( ext ) , 192 < < 4 , lm85_scaling [ n ] )
2005-04-17 02:20:36 +04:00
2008-04-29 16:03:37 +04:00
# define INS_FROM_REG(n, val) SCALE((val), 192, lm85_scaling[n])
2005-04-17 02:20:36 +04:00
/* FAN speed is measured using 90kHz clock */
2007-07-05 22:39:40 +04:00
static inline u16 FAN_TO_REG ( unsigned long val )
{
if ( ! val )
return 0xffff ;
return SENSORS_LIMIT ( 5400000 / val , 1 , 0xfffe ) ;
}
2008-04-29 16:03:37 +04:00
# define FAN_FROM_REG(val) ((val) == 0 ? -1 : (val) == 0xffff ? 0 : \
5400000 / ( val ) )
2005-04-17 02:20:36 +04:00
/* Temperature is reported in .001 degC increments */
# define TEMP_TO_REG(val) \
2008-04-29 16:03:37 +04:00
SENSORS_LIMIT ( SCALE ( val , 1000 , 1 ) , - 127 , 127 )
# define TEMPEXT_FROM_REG(val, ext) \
2007-07-05 22:38:32 +04:00
SCALE ( ( ( val ) < < 4 ) + ( ext ) , 16 , 1000 )
# define TEMP_FROM_REG(val) ((val) * 1000)
2005-04-17 02:20:36 +04:00
2008-04-29 16:03:37 +04:00
# define PWM_TO_REG(val) SENSORS_LIMIT(val, 0, 255)
2005-04-17 02:20:36 +04:00
# define PWM_FROM_REG(val) (val)
/* ZONEs have the following parameters:
* Limit ( low ) temp , 1. degC
* Hysteresis ( below limit ) , 1. degC ( 0 - 15 )
* Range of speed control , .1 degC ( 2 - 80 )
* Critical ( high ) temp , 1. degC
*
* FAN PWMs have the following parameters :
* Reference Zone , 1 , 2 , 3 , etc .
* Spinup time , .05 sec
* PWM value at limit / low temp , 1 count
* PWM Frequency , 1. Hz
* PWM is Min or OFF below limit , flag
* Invert PWM output , flag
*
* Some chips filter the temp , others the fan .
* Filter constant ( or disabled ) .1 seconds
*/
/* These are the zone temperature range encodings in .001 degree C */
2008-06-25 16:47:35 +04:00
static const int lm85_range_map [ ] = {
2008-04-29 16:03:37 +04:00
2000 , 2500 , 3300 , 4000 , 5000 , 6600 , 8000 , 10000 ,
13300 , 16000 , 20000 , 26600 , 32000 , 40000 , 53300 , 80000
} ;
static int RANGE_TO_REG ( int range )
2005-04-17 02:20:36 +04:00
{
int i ;
2008-04-03 12:40:39 +04:00
/* Find the closest match */
2008-10-17 19:51:14 +04:00
for ( i = 0 ; i < 15 ; + + i ) {
if ( range < = ( lm85_range_map [ i ] + lm85_range_map [ i + 1 ] ) / 2 )
break ;
2005-04-17 02:20:36 +04:00
}
2008-04-03 12:40:39 +04:00
2008-10-17 19:51:14 +04:00
return i ;
2005-04-17 02:20:36 +04:00
}
2008-04-29 16:03:37 +04:00
# define RANGE_FROM_REG(val) lm85_range_map[(val) & 0x0f]
2005-04-17 02:20:36 +04:00
/* These are the PWM frequency encodings */
2008-10-17 19:51:13 +04:00
static const int lm85_freq_map [ 8 ] = { /* 1 Hz */
2008-10-17 19:51:14 +04:00
10 , 15 , 23 , 30 , 38 , 47 , 61 , 94
} ;
static const int adm1027_freq_map [ 8 ] = { /* 1 Hz */
11 , 15 , 22 , 29 , 35 , 44 , 59 , 88
2008-04-29 16:03:37 +04:00
} ;
2008-10-17 19:51:14 +04:00
static int FREQ_TO_REG ( const int * map , int freq )
2005-04-17 02:20:36 +04:00
{
int i ;
2008-10-17 19:51:13 +04:00
/* Find the closest match */
2008-04-29 16:03:37 +04:00
for ( i = 0 ; i < 7 ; + + i )
2008-10-17 19:51:14 +04:00
if ( freq < = ( map [ i ] + map [ i + 1 ] ) / 2 )
2008-04-29 16:03:37 +04:00
break ;
2008-06-25 16:47:35 +04:00
return i ;
2005-04-17 02:20:36 +04:00
}
2008-10-17 19:51:14 +04:00
static int FREQ_FROM_REG ( const int * map , u8 reg )
{
return map [ reg & 0x07 ] ;
}
2005-04-17 02:20:36 +04:00
/* Since we can't use strings, I'm abusing these numbers
* to stand in for the following meanings :
* 1 - - PWM responds to Zone 1
* 2 - - PWM responds to Zone 2
* 3 - - PWM responds to Zone 3
* 23 - - PWM responds to the higher temp of Zone 2 or 3
* 123 - - PWM responds to highest of Zone 1 , 2 , or 3
* 0 - - PWM is always at 0 % ( ie , off )
* - 1 - - PWM is always at 100 %
* - 2 - - PWM responds to manual control
*/
2008-06-25 16:47:35 +04:00
static const int lm85_zone_map [ ] = { 1 , 2 , 3 , - 1 , 0 , 23 , 123 , - 2 } ;
# define ZONE_FROM_REG(val) lm85_zone_map[(val) >> 5]
2005-04-17 02:20:36 +04:00
2008-04-29 16:03:37 +04:00
static int ZONE_TO_REG ( int zone )
2005-04-17 02:20:36 +04:00
{
int i ;
2008-04-29 16:03:37 +04:00
for ( i = 0 ; i < = 7 ; + + i )
if ( zone = = lm85_zone_map [ i ] )
break ;
if ( i > 7 ) /* Not found. */
2005-04-17 02:20:36 +04:00
i = 3 ; /* Always 100% */
2008-06-25 16:47:35 +04:00
return i < < 5 ;
2005-04-17 02:20:36 +04:00
}
2008-04-29 16:03:37 +04:00
# define HYST_TO_REG(val) SENSORS_LIMIT(((val) + 500) / 1000, 0, 15)
# define HYST_FROM_REG(val) ((val) * 1000)
2005-04-17 02:20:36 +04:00
/* Chip sampling rates
*
* Some sensors are not updated more frequently than once per second
* so it doesn ' t make sense to read them more often than that .
* We cache the results and return the saved data if the driver
* is called again before a second has elapsed .
*
* Also , there is significant configuration data for this chip
* given the automatic PWM fan control that is possible . There
* are about 47 bytes of config data to only 22 bytes of actual
* readings . So , we keep the config data up to date in the cache
* when it is written and only sample it once every 1 * minute *
*/
# define LM85_DATA_INTERVAL (HZ + HZ / 2)
# define LM85_CONFIG_INTERVAL (1 * 60 * HZ)
/* LM85 can automatically adjust fan speeds based on temperature
* This structure encapsulates an entire Zone config . There are
* three zones ( one for each temperature input ) on the lm85
*/
struct lm85_zone {
s8 limit ; /* Low temp limit */
u8 hyst ; /* Low limit hysteresis. (0-15) */
u8 range ; /* Temp range, encoded */
s8 critical ; /* "All fans ON" temp limit */
2008-04-29 16:03:37 +04:00
u8 max_desired ; /* Actual "max" temperature specified. Preserved
2005-04-17 02:20:36 +04:00
* to prevent " drift " as other autofan control
* values change .
*/
} ;
struct lm85_autofan {
u8 config ; /* Register value */
u8 min_pwm ; /* Minimum PWM value, encoded */
u8 min_off ; /* Min PWM or OFF below "limit", flag */
} ;
2007-02-14 23:15:03 +03:00
/* For each registered chip, we need to keep some data in memory.
The structure is dynamically allocated . */
2005-04-17 02:20:36 +04:00
struct lm85_data {
2007-08-21 00:46:20 +04:00
struct device * hwmon_dev ;
2008-10-17 19:51:14 +04:00
const int * freq_map ;
2005-04-17 02:20:36 +04:00
enum chips type ;
2011-02-25 19:19:42 +03:00
bool has_vid5 ; /* true if VID5 is configured for ADT7463 or ADT7468 */
2006-01-19 01:19:26 +03:00
struct mutex update_lock ;
2005-04-17 02:20:36 +04:00
int valid ; /* !=0 if following fields are valid */
unsigned long last_reading ; /* In jiffies */
unsigned long last_config ; /* In jiffies */
u8 in [ 8 ] ; /* Register value */
u8 in_max [ 8 ] ; /* Register value */
u8 in_min [ 8 ] ; /* Register value */
s8 temp [ 3 ] ; /* Register value */
s8 temp_min [ 3 ] ; /* Register value */
s8 temp_max [ 3 ] ; /* Register value */
u16 fan [ 4 ] ; /* Register value */
u16 fan_min [ 4 ] ; /* Register value */
u8 pwm [ 3 ] ; /* Register value */
2008-10-17 19:51:13 +04:00
u8 pwm_freq [ 3 ] ; /* Register encoding */
2005-04-17 02:20:36 +04:00
u8 temp_ext [ 3 ] ; /* Decoded values */
u8 in_ext [ 8 ] ; /* Decoded values */
u8 vid ; /* Register value */
u8 vrm ; /* VRM version */
u32 alarms ; /* Register encoding, combined */
2008-11-13 00:26:59 +03:00
u8 cfg5 ; /* Config Register 5 on ADT7468 */
2005-04-17 02:20:36 +04:00
struct lm85_autofan autofan [ 3 ] ;
struct lm85_zone zone [ 3 ] ;
} ;
2009-12-14 23:17:23 +03:00
static int lm85_detect ( struct i2c_client * client , struct i2c_board_info * info ) ;
2008-10-17 19:51:14 +04:00
static int lm85_probe ( struct i2c_client * client ,
const struct i2c_device_id * id ) ;
static int lm85_remove ( struct i2c_client * client ) ;
2005-04-17 02:20:36 +04:00
2006-02-28 01:14:58 +03:00
static int lm85_read_value ( struct i2c_client * client , u8 reg ) ;
2008-06-25 16:47:35 +04:00
static void lm85_write_value ( struct i2c_client * client , u8 reg , int value ) ;
2005-04-17 02:20:36 +04:00
static struct lm85_data * lm85_update_device ( struct device * dev ) ;
2008-10-17 19:51:14 +04:00
static const struct i2c_device_id lm85_id [ ] = {
{ " adm1027 " , adm1027 } ,
{ " adt7463 " , adt7463 } ,
2009-03-10 22:55:47 +03:00
{ " adt7468 " , adt7468 } ,
2008-10-17 19:51:14 +04:00
{ " lm85 " , any_chip } ,
{ " lm85b " , lm85b } ,
{ " lm85c " , lm85c } ,
{ " emc6d100 " , emc6d100 } ,
{ " emc6d101 " , emc6d100 } ,
{ " emc6d102 " , emc6d102 } ,
2011-02-18 11:18:26 +03:00
{ " emc6d103 " , emc6d103 } ,
2011-02-19 19:27:47 +03:00
{ " emc6d103s " , emc6d103s } ,
2008-10-17 19:51:14 +04:00
{ }
} ;
MODULE_DEVICE_TABLE ( i2c , lm85_id ) ;
2005-04-17 02:20:36 +04:00
static struct i2c_driver lm85_driver = {
2008-10-17 19:51:14 +04:00
. class = I2C_CLASS_HWMON ,
2005-11-26 22:37:41 +03:00
. driver = {
. name = " lm85 " ,
} ,
2008-10-17 19:51:14 +04:00
. probe = lm85_probe ,
. remove = lm85_remove ,
. id_table = lm85_id ,
. detect = lm85_detect ,
2009-12-14 23:17:25 +03:00
. address_list = normal_i2c ,
2005-04-17 02:20:36 +04:00
} ;
/* 4 Fans */
2007-07-05 22:36:12 +04:00
static ssize_t show_fan ( struct device * dev , struct device_attribute * attr ,
char * buf )
2005-04-17 02:20:36 +04:00
{
2007-07-05 22:36:12 +04:00
int nr = to_sensor_dev_attr ( attr ) - > index ;
2005-04-17 02:20:36 +04:00
struct lm85_data * data = lm85_update_device ( dev ) ;
2008-04-29 16:03:37 +04:00
return sprintf ( buf , " %d \n " , FAN_FROM_REG ( data - > fan [ nr ] ) ) ;
2005-04-17 02:20:36 +04:00
}
2007-07-05 22:36:12 +04:00
static ssize_t show_fan_min ( struct device * dev , struct device_attribute * attr ,
char * buf )
2005-04-17 02:20:36 +04:00
{
2007-07-05 22:36:12 +04:00
int nr = to_sensor_dev_attr ( attr ) - > index ;
2005-04-17 02:20:36 +04:00
struct lm85_data * data = lm85_update_device ( dev ) ;
2008-04-29 16:03:37 +04:00
return sprintf ( buf , " %d \n " , FAN_FROM_REG ( data - > fan_min [ nr ] ) ) ;
2005-04-17 02:20:36 +04:00
}
2007-07-05 22:36:12 +04:00
static ssize_t set_fan_min ( struct device * dev , struct device_attribute * attr ,
const char * buf , size_t count )
2005-04-17 02:20:36 +04:00
{
2007-07-05 22:36:12 +04:00
int nr = to_sensor_dev_attr ( attr ) - > index ;
2005-04-17 02:20:36 +04:00
struct i2c_client * client = to_i2c_client ( dev ) ;
struct lm85_data * data = i2c_get_clientdata ( client ) ;
2007-07-05 22:39:40 +04:00
unsigned long val = simple_strtoul ( buf , NULL , 10 ) ;
2005-04-17 02:20:36 +04:00
2006-01-19 01:19:26 +03:00
mutex_lock ( & data - > update_lock ) ;
2005-04-17 02:20:36 +04:00
data - > fan_min [ nr ] = FAN_TO_REG ( val ) ;
lm85_write_value ( client , LM85_REG_FAN_MIN ( nr ) , data - > fan_min [ nr ] ) ;
2006-01-19 01:19:26 +03:00
mutex_unlock ( & data - > update_lock ) ;
2005-04-17 02:20:36 +04:00
return count ;
}
# define show_fan_offset(offset) \
2007-07-05 22:36:12 +04:00
static SENSOR_DEVICE_ATTR ( fan # # offset # # _input , S_IRUGO , \
show_fan , NULL , offset - 1 ) ; \
static SENSOR_DEVICE_ATTR ( fan # # offset # # _min , S_IRUGO | S_IWUSR , \
show_fan_min , set_fan_min , offset - 1 )
2005-04-17 02:20:36 +04:00
show_fan_offset ( 1 ) ;
show_fan_offset ( 2 ) ;
show_fan_offset ( 3 ) ;
show_fan_offset ( 4 ) ;
/* vid, vrm, alarms */
2008-04-29 16:03:37 +04:00
static ssize_t show_vid_reg ( struct device * dev , struct device_attribute * attr ,
char * buf )
2005-04-17 02:20:36 +04:00
{
struct lm85_data * data = lm85_update_device ( dev ) ;
2005-11-26 22:07:54 +03:00
int vid ;
2011-02-25 19:19:42 +03:00
if ( data - > has_vid5 ) {
2005-11-26 22:07:54 +03:00
/* 6-pin VID (VRM 10) */
vid = vid_from_reg ( data - > vid & 0x3f , data - > vrm ) ;
} else {
/* 5-pin VID (VRM 9) */
vid = vid_from_reg ( data - > vid & 0x1f , data - > vrm ) ;
}
return sprintf ( buf , " %d \n " , vid ) ;
2005-04-17 02:20:36 +04:00
}
static DEVICE_ATTR ( cpu0_vid , S_IRUGO , show_vid_reg , NULL ) ;
2008-04-29 16:03:37 +04:00
static ssize_t show_vrm_reg ( struct device * dev , struct device_attribute * attr ,
char * buf )
2005-04-17 02:20:36 +04:00
{
2007-10-08 20:24:35 +04:00
struct lm85_data * data = dev_get_drvdata ( dev ) ;
2005-04-17 02:20:36 +04:00
return sprintf ( buf , " %ld \n " , ( long ) data - > vrm ) ;
}
2008-04-29 16:03:37 +04:00
static ssize_t store_vrm_reg ( struct device * dev , struct device_attribute * attr ,
const char * buf , size_t count )
2005-04-17 02:20:36 +04:00
{
2007-12-01 13:25:33 +03:00
struct lm85_data * data = dev_get_drvdata ( dev ) ;
data - > vrm = simple_strtoul ( buf , NULL , 10 ) ;
2005-04-17 02:20:36 +04:00
return count ;
}
static DEVICE_ATTR ( vrm , S_IRUGO | S_IWUSR , show_vrm_reg , store_vrm_reg ) ;
2008-04-29 16:03:37 +04:00
static ssize_t show_alarms_reg ( struct device * dev , struct device_attribute
* attr , char * buf )
2005-04-17 02:20:36 +04:00
{
struct lm85_data * data = lm85_update_device ( dev ) ;
2005-05-16 20:52:38 +04:00
return sprintf ( buf , " %u \n " , data - > alarms ) ;
2005-04-17 02:20:36 +04:00
}
static DEVICE_ATTR ( alarms , S_IRUGO , show_alarms_reg , NULL ) ;
2007-07-05 22:37:58 +04:00
static ssize_t show_alarm ( struct device * dev , struct device_attribute * attr ,
char * buf )
{
int nr = to_sensor_dev_attr ( attr ) - > index ;
struct lm85_data * data = lm85_update_device ( dev ) ;
return sprintf ( buf , " %u \n " , ( data - > alarms > > nr ) & 1 ) ;
}
static SENSOR_DEVICE_ATTR ( in0_alarm , S_IRUGO , show_alarm , NULL , 0 ) ;
static SENSOR_DEVICE_ATTR ( in1_alarm , S_IRUGO , show_alarm , NULL , 1 ) ;
static SENSOR_DEVICE_ATTR ( in2_alarm , S_IRUGO , show_alarm , NULL , 2 ) ;
static SENSOR_DEVICE_ATTR ( in3_alarm , S_IRUGO , show_alarm , NULL , 3 ) ;
static SENSOR_DEVICE_ATTR ( in4_alarm , S_IRUGO , show_alarm , NULL , 8 ) ;
static SENSOR_DEVICE_ATTR ( in5_alarm , S_IRUGO , show_alarm , NULL , 18 ) ;
static SENSOR_DEVICE_ATTR ( in6_alarm , S_IRUGO , show_alarm , NULL , 16 ) ;
static SENSOR_DEVICE_ATTR ( in7_alarm , S_IRUGO , show_alarm , NULL , 17 ) ;
static SENSOR_DEVICE_ATTR ( temp1_alarm , S_IRUGO , show_alarm , NULL , 4 ) ;
static SENSOR_DEVICE_ATTR ( temp1_fault , S_IRUGO , show_alarm , NULL , 14 ) ;
static SENSOR_DEVICE_ATTR ( temp2_alarm , S_IRUGO , show_alarm , NULL , 5 ) ;
static SENSOR_DEVICE_ATTR ( temp3_alarm , S_IRUGO , show_alarm , NULL , 6 ) ;
static SENSOR_DEVICE_ATTR ( temp3_fault , S_IRUGO , show_alarm , NULL , 15 ) ;
static SENSOR_DEVICE_ATTR ( fan1_alarm , S_IRUGO , show_alarm , NULL , 10 ) ;
static SENSOR_DEVICE_ATTR ( fan2_alarm , S_IRUGO , show_alarm , NULL , 11 ) ;
static SENSOR_DEVICE_ATTR ( fan3_alarm , S_IRUGO , show_alarm , NULL , 12 ) ;
static SENSOR_DEVICE_ATTR ( fan4_alarm , S_IRUGO , show_alarm , NULL , 13 ) ;
2005-04-17 02:20:36 +04:00
/* pwm */
2007-07-05 22:36:12 +04:00
static ssize_t show_pwm ( struct device * dev , struct device_attribute * attr ,
char * buf )
2005-04-17 02:20:36 +04:00
{
2007-07-05 22:36:12 +04:00
int nr = to_sensor_dev_attr ( attr ) - > index ;
2005-04-17 02:20:36 +04:00
struct lm85_data * data = lm85_update_device ( dev ) ;
2008-04-29 16:03:37 +04:00
return sprintf ( buf , " %d \n " , PWM_FROM_REG ( data - > pwm [ nr ] ) ) ;
2005-04-17 02:20:36 +04:00
}
2007-07-05 22:36:12 +04:00
static ssize_t set_pwm ( struct device * dev , struct device_attribute * attr ,
const char * buf , size_t count )
2005-04-17 02:20:36 +04:00
{
2007-07-05 22:36:12 +04:00
int nr = to_sensor_dev_attr ( attr ) - > index ;
2005-04-17 02:20:36 +04:00
struct i2c_client * client = to_i2c_client ( dev ) ;
struct lm85_data * data = i2c_get_clientdata ( client ) ;
long val = simple_strtol ( buf , NULL , 10 ) ;
2006-01-19 01:19:26 +03:00
mutex_lock ( & data - > update_lock ) ;
2005-04-17 02:20:36 +04:00
data - > pwm [ nr ] = PWM_TO_REG ( val ) ;
lm85_write_value ( client , LM85_REG_PWM ( nr ) , data - > pwm [ nr ] ) ;
2006-01-19 01:19:26 +03:00
mutex_unlock ( & data - > update_lock ) ;
2005-04-17 02:20:36 +04:00
return count ;
}
2007-07-05 22:36:12 +04:00
static ssize_t show_pwm_enable ( struct device * dev , struct device_attribute
* attr , char * buf )
2005-04-17 02:20:36 +04:00
{
2007-07-05 22:36:12 +04:00
int nr = to_sensor_dev_attr ( attr ) - > index ;
2005-04-17 02:20:36 +04:00
struct lm85_data * data = lm85_update_device ( dev ) ;
2007-12-04 01:23:21 +03:00
int pwm_zone , enable ;
2005-04-17 02:20:36 +04:00
pwm_zone = ZONE_FROM_REG ( data - > autofan [ nr ] . config ) ;
2007-12-04 01:23:21 +03:00
switch ( pwm_zone ) {
case - 1 : /* PWM is always at 100% */
enable = 0 ;
break ;
case 0 : /* PWM is always at 0% */
case - 2 : /* PWM responds to manual control */
enable = 1 ;
break ;
default : /* PWM in automatic mode */
enable = 2 ;
}
return sprintf ( buf , " %d \n " , enable ) ;
2005-04-17 02:20:36 +04:00
}
2007-12-04 01:28:42 +03:00
static ssize_t set_pwm_enable ( struct device * dev , struct device_attribute
* attr , const char * buf , size_t count )
{
int nr = to_sensor_dev_attr ( attr ) - > index ;
struct i2c_client * client = to_i2c_client ( dev ) ;
struct lm85_data * data = i2c_get_clientdata ( client ) ;
long val = simple_strtol ( buf , NULL , 10 ) ;
u8 config ;
switch ( val ) {
case 0 :
config = 3 ;
break ;
case 1 :
config = 7 ;
break ;
case 2 :
/* Here we have to choose arbitrarily one of the 5 possible
configurations ; I go for the safest */
config = 6 ;
break ;
default :
return - EINVAL ;
}
mutex_lock ( & data - > update_lock ) ;
data - > autofan [ nr ] . config = lm85_read_value ( client ,
LM85_REG_AFAN_CONFIG ( nr ) ) ;
data - > autofan [ nr ] . config = ( data - > autofan [ nr ] . config & ~ 0xe0 )
| ( config < < 5 ) ;
lm85_write_value ( client , LM85_REG_AFAN_CONFIG ( nr ) ,
data - > autofan [ nr ] . config ) ;
mutex_unlock ( & data - > update_lock ) ;
return count ;
}
2008-10-17 19:51:13 +04:00
static ssize_t show_pwm_freq ( struct device * dev ,
struct device_attribute * attr , char * buf )
{
int nr = to_sensor_dev_attr ( attr ) - > index ;
struct lm85_data * data = lm85_update_device ( dev ) ;
2010-10-28 22:31:50 +04:00
int freq ;
if ( IS_ADT7468_HFPWM ( data ) )
freq = 22500 ;
else
freq = FREQ_FROM_REG ( data - > freq_map , data - > pwm_freq [ nr ] ) ;
return sprintf ( buf , " %d \n " , freq ) ;
2008-10-17 19:51:13 +04:00
}
static ssize_t set_pwm_freq ( struct device * dev ,
struct device_attribute * attr , const char * buf , size_t count )
{
int nr = to_sensor_dev_attr ( attr ) - > index ;
struct i2c_client * client = to_i2c_client ( dev ) ;
struct lm85_data * data = i2c_get_clientdata ( client ) ;
long val = simple_strtol ( buf , NULL , 10 ) ;
mutex_lock ( & data - > update_lock ) ;
2010-10-28 22:31:50 +04:00
/* The ADT7468 has a special high-frequency PWM output mode,
* where all PWM outputs are driven by a 22.5 kHz clock .
* This might confuse the user , but there ' s not much we can do . */
if ( data - > type = = adt7468 & & val > = 11300 ) { /* High freq. mode */
data - > cfg5 & = ~ ADT7468_HFPWM ;
lm85_write_value ( client , ADT7468_REG_CFG5 , data - > cfg5 ) ;
} else { /* Low freq. mode */
data - > pwm_freq [ nr ] = FREQ_TO_REG ( data - > freq_map , val ) ;
lm85_write_value ( client , LM85_REG_AFAN_RANGE ( nr ) ,
( data - > zone [ nr ] . range < < 4 )
| data - > pwm_freq [ nr ] ) ;
if ( data - > type = = adt7468 ) {
data - > cfg5 | = ADT7468_HFPWM ;
lm85_write_value ( client , ADT7468_REG_CFG5 , data - > cfg5 ) ;
}
}
2008-10-17 19:51:13 +04:00
mutex_unlock ( & data - > update_lock ) ;
return count ;
}
2005-04-17 02:20:36 +04:00
# define show_pwm_reg(offset) \
2007-07-05 22:36:12 +04:00
static SENSOR_DEVICE_ATTR ( pwm # # offset , S_IRUGO | S_IWUSR , \
show_pwm , set_pwm , offset - 1 ) ; \
2007-12-04 01:28:42 +03:00
static SENSOR_DEVICE_ATTR ( pwm # # offset # # _enable , S_IRUGO | S_IWUSR , \
2008-10-17 19:51:13 +04:00
show_pwm_enable , set_pwm_enable , offset - 1 ) ; \
static SENSOR_DEVICE_ATTR ( pwm # # offset # # _freq , S_IRUGO | S_IWUSR , \
show_pwm_freq , set_pwm_freq , offset - 1 )
2005-04-17 02:20:36 +04:00
show_pwm_reg ( 1 ) ;
show_pwm_reg ( 2 ) ;
show_pwm_reg ( 3 ) ;
/* Voltages */
2007-07-05 22:36:12 +04:00
static ssize_t show_in ( struct device * dev , struct device_attribute * attr ,
char * buf )
2005-04-17 02:20:36 +04:00
{
2007-07-05 22:36:12 +04:00
int nr = to_sensor_dev_attr ( attr ) - > index ;
2005-04-17 02:20:36 +04:00
struct lm85_data * data = lm85_update_device ( dev ) ;
2008-04-29 16:03:37 +04:00
return sprintf ( buf , " %d \n " , INSEXT_FROM_REG ( nr , data - > in [ nr ] ,
data - > in_ext [ nr ] ) ) ;
2005-04-17 02:20:36 +04:00
}
2007-07-05 22:36:12 +04:00
2008-04-29 16:03:37 +04:00
static ssize_t show_in_min ( struct device * dev , struct device_attribute * attr ,
2007-07-05 22:36:12 +04:00
char * buf )
2005-04-17 02:20:36 +04:00
{
2007-07-05 22:36:12 +04:00
int nr = to_sensor_dev_attr ( attr ) - > index ;
2005-04-17 02:20:36 +04:00
struct lm85_data * data = lm85_update_device ( dev ) ;
2008-04-29 16:03:37 +04:00
return sprintf ( buf , " %d \n " , INS_FROM_REG ( nr , data - > in_min [ nr ] ) ) ;
2005-04-17 02:20:36 +04:00
}
2007-07-05 22:36:12 +04:00
static ssize_t set_in_min ( struct device * dev , struct device_attribute * attr ,
const char * buf , size_t count )
2005-04-17 02:20:36 +04:00
{
2007-07-05 22:36:12 +04:00
int nr = to_sensor_dev_attr ( attr ) - > index ;
2005-04-17 02:20:36 +04:00
struct i2c_client * client = to_i2c_client ( dev ) ;
struct lm85_data * data = i2c_get_clientdata ( client ) ;
long val = simple_strtol ( buf , NULL , 10 ) ;
2006-01-19 01:19:26 +03:00
mutex_lock ( & data - > update_lock ) ;
2005-04-17 02:20:36 +04:00
data - > in_min [ nr ] = INS_TO_REG ( nr , val ) ;
lm85_write_value ( client , LM85_REG_IN_MIN ( nr ) , data - > in_min [ nr ] ) ;
2006-01-19 01:19:26 +03:00
mutex_unlock ( & data - > update_lock ) ;
2005-04-17 02:20:36 +04:00
return count ;
}
2007-07-05 22:36:12 +04:00
static ssize_t show_in_max ( struct device * dev , struct device_attribute * attr ,
char * buf )
2005-04-17 02:20:36 +04:00
{
2007-07-05 22:36:12 +04:00
int nr = to_sensor_dev_attr ( attr ) - > index ;
2005-04-17 02:20:36 +04:00
struct lm85_data * data = lm85_update_device ( dev ) ;
2008-04-29 16:03:37 +04:00
return sprintf ( buf , " %d \n " , INS_FROM_REG ( nr , data - > in_max [ nr ] ) ) ;
2005-04-17 02:20:36 +04:00
}
2007-07-05 22:36:12 +04:00
static ssize_t set_in_max ( struct device * dev , struct device_attribute * attr ,
const char * buf , size_t count )
2005-04-17 02:20:36 +04:00
{
2007-07-05 22:36:12 +04:00
int nr = to_sensor_dev_attr ( attr ) - > index ;
2005-04-17 02:20:36 +04:00
struct i2c_client * client = to_i2c_client ( dev ) ;
struct lm85_data * data = i2c_get_clientdata ( client ) ;
long val = simple_strtol ( buf , NULL , 10 ) ;
2006-01-19 01:19:26 +03:00
mutex_lock ( & data - > update_lock ) ;
2005-04-17 02:20:36 +04:00
data - > in_max [ nr ] = INS_TO_REG ( nr , val ) ;
lm85_write_value ( client , LM85_REG_IN_MAX ( nr ) , data - > in_max [ nr ] ) ;
2006-01-19 01:19:26 +03:00
mutex_unlock ( & data - > update_lock ) ;
2005-04-17 02:20:36 +04:00
return count ;
}
2007-07-05 22:36:12 +04:00
2005-04-17 02:20:36 +04:00
# define show_in_reg(offset) \
2007-07-05 22:36:12 +04:00
static SENSOR_DEVICE_ATTR ( in # # offset # # _input , S_IRUGO , \
show_in , NULL , offset ) ; \
static SENSOR_DEVICE_ATTR ( in # # offset # # _min , S_IRUGO | S_IWUSR , \
show_in_min , set_in_min , offset ) ; \
static SENSOR_DEVICE_ATTR ( in # # offset # # _max , S_IRUGO | S_IWUSR , \
show_in_max , set_in_max , offset )
2005-04-17 02:20:36 +04:00
show_in_reg ( 0 ) ;
show_in_reg ( 1 ) ;
show_in_reg ( 2 ) ;
show_in_reg ( 3 ) ;
show_in_reg ( 4 ) ;
2007-07-05 22:37:21 +04:00
show_in_reg ( 5 ) ;
show_in_reg ( 6 ) ;
show_in_reg ( 7 ) ;
2005-04-17 02:20:36 +04:00
/* Temps */
2007-07-05 22:36:12 +04:00
static ssize_t show_temp ( struct device * dev , struct device_attribute * attr ,
char * buf )
2005-04-17 02:20:36 +04:00
{
2007-07-05 22:36:12 +04:00
int nr = to_sensor_dev_attr ( attr ) - > index ;
2005-04-17 02:20:36 +04:00
struct lm85_data * data = lm85_update_device ( dev ) ;
2008-04-29 16:03:37 +04:00
return sprintf ( buf , " %d \n " , TEMPEXT_FROM_REG ( data - > temp [ nr ] ,
data - > temp_ext [ nr ] ) ) ;
2005-04-17 02:20:36 +04:00
}
2007-07-05 22:36:12 +04:00
static ssize_t show_temp_min ( struct device * dev , struct device_attribute * attr ,
char * buf )
2005-04-17 02:20:36 +04:00
{
2007-07-05 22:36:12 +04:00
int nr = to_sensor_dev_attr ( attr ) - > index ;
2005-04-17 02:20:36 +04:00
struct lm85_data * data = lm85_update_device ( dev ) ;
2008-04-29 16:03:37 +04:00
return sprintf ( buf , " %d \n " , TEMP_FROM_REG ( data - > temp_min [ nr ] ) ) ;
2005-04-17 02:20:36 +04:00
}
2007-07-05 22:36:12 +04:00
static ssize_t set_temp_min ( struct device * dev , struct device_attribute * attr ,
const char * buf , size_t count )
2005-04-17 02:20:36 +04:00
{
2007-07-05 22:36:12 +04:00
int nr = to_sensor_dev_attr ( attr ) - > index ;
2005-04-17 02:20:36 +04:00
struct i2c_client * client = to_i2c_client ( dev ) ;
struct lm85_data * data = i2c_get_clientdata ( client ) ;
long val = simple_strtol ( buf , NULL , 10 ) ;
2008-11-13 00:26:59 +03:00
if ( IS_ADT7468_OFF64 ( data ) )
val + = 64 ;
2006-01-19 01:19:26 +03:00
mutex_lock ( & data - > update_lock ) ;
2005-04-17 02:20:36 +04:00
data - > temp_min [ nr ] = TEMP_TO_REG ( val ) ;
lm85_write_value ( client , LM85_REG_TEMP_MIN ( nr ) , data - > temp_min [ nr ] ) ;
2006-01-19 01:19:26 +03:00
mutex_unlock ( & data - > update_lock ) ;
2005-04-17 02:20:36 +04:00
return count ;
}
2007-07-05 22:36:12 +04:00
static ssize_t show_temp_max ( struct device * dev , struct device_attribute * attr ,
char * buf )
2005-04-17 02:20:36 +04:00
{
2007-07-05 22:36:12 +04:00
int nr = to_sensor_dev_attr ( attr ) - > index ;
2005-04-17 02:20:36 +04:00
struct lm85_data * data = lm85_update_device ( dev ) ;
2008-04-29 16:03:37 +04:00
return sprintf ( buf , " %d \n " , TEMP_FROM_REG ( data - > temp_max [ nr ] ) ) ;
2005-04-17 02:20:36 +04:00
}
2007-07-05 22:36:12 +04:00
static ssize_t set_temp_max ( struct device * dev , struct device_attribute * attr ,
const char * buf , size_t count )
2005-04-17 02:20:36 +04:00
{
2007-07-05 22:36:12 +04:00
int nr = to_sensor_dev_attr ( attr ) - > index ;
2005-04-17 02:20:36 +04:00
struct i2c_client * client = to_i2c_client ( dev ) ;
struct lm85_data * data = i2c_get_clientdata ( client ) ;
2008-04-29 16:03:37 +04:00
long val = simple_strtol ( buf , NULL , 10 ) ;
2005-04-17 02:20:36 +04:00
2008-11-13 00:26:59 +03:00
if ( IS_ADT7468_OFF64 ( data ) )
val + = 64 ;
2006-01-19 01:19:26 +03:00
mutex_lock ( & data - > update_lock ) ;
2005-04-17 02:20:36 +04:00
data - > temp_max [ nr ] = TEMP_TO_REG ( val ) ;
lm85_write_value ( client , LM85_REG_TEMP_MAX ( nr ) , data - > temp_max [ nr ] ) ;
2006-01-19 01:19:26 +03:00
mutex_unlock ( & data - > update_lock ) ;
2005-04-17 02:20:36 +04:00
return count ;
}
2007-07-05 22:36:12 +04:00
2005-04-17 02:20:36 +04:00
# define show_temp_reg(offset) \
2007-07-05 22:36:12 +04:00
static SENSOR_DEVICE_ATTR ( temp # # offset # # _input , S_IRUGO , \
show_temp , NULL , offset - 1 ) ; \
static SENSOR_DEVICE_ATTR ( temp # # offset # # _min , S_IRUGO | S_IWUSR , \
show_temp_min , set_temp_min , offset - 1 ) ; \
static SENSOR_DEVICE_ATTR ( temp # # offset # # _max , S_IRUGO | S_IWUSR , \
show_temp_max , set_temp_max , offset - 1 ) ;
2005-04-17 02:20:36 +04:00
show_temp_reg ( 1 ) ;
show_temp_reg ( 2 ) ;
show_temp_reg ( 3 ) ;
/* Automatic PWM control */
2007-07-05 22:36:12 +04:00
static ssize_t show_pwm_auto_channels ( struct device * dev ,
struct device_attribute * attr , char * buf )
2005-04-17 02:20:36 +04:00
{
2007-07-05 22:36:12 +04:00
int nr = to_sensor_dev_attr ( attr ) - > index ;
2005-04-17 02:20:36 +04:00
struct lm85_data * data = lm85_update_device ( dev ) ;
2008-04-29 16:03:37 +04:00
return sprintf ( buf , " %d \n " , ZONE_FROM_REG ( data - > autofan [ nr ] . config ) ) ;
2005-04-17 02:20:36 +04:00
}
2007-07-05 22:36:12 +04:00
static ssize_t set_pwm_auto_channels ( struct device * dev ,
struct device_attribute * attr , const char * buf , size_t count )
2005-04-17 02:20:36 +04:00
{
2007-07-05 22:36:12 +04:00
int nr = to_sensor_dev_attr ( attr ) - > index ;
2005-04-17 02:20:36 +04:00
struct i2c_client * client = to_i2c_client ( dev ) ;
struct lm85_data * data = i2c_get_clientdata ( client ) ;
2008-04-29 16:03:37 +04:00
long val = simple_strtol ( buf , NULL , 10 ) ;
2005-04-17 02:20:36 +04:00
2006-01-19 01:19:26 +03:00
mutex_lock ( & data - > update_lock ) ;
2005-04-17 02:20:36 +04:00
data - > autofan [ nr ] . config = ( data - > autofan [ nr ] . config & ( ~ 0xe0 ) )
2008-04-29 16:03:37 +04:00
| ZONE_TO_REG ( val ) ;
2005-04-17 02:20:36 +04:00
lm85_write_value ( client , LM85_REG_AFAN_CONFIG ( nr ) ,
data - > autofan [ nr ] . config ) ;
2006-01-19 01:19:26 +03:00
mutex_unlock ( & data - > update_lock ) ;
2005-04-17 02:20:36 +04:00
return count ;
}
2007-07-05 22:36:12 +04:00
static ssize_t show_pwm_auto_pwm_min ( struct device * dev ,
struct device_attribute * attr , char * buf )
2005-04-17 02:20:36 +04:00
{
2007-07-05 22:36:12 +04:00
int nr = to_sensor_dev_attr ( attr ) - > index ;
2005-04-17 02:20:36 +04:00
struct lm85_data * data = lm85_update_device ( dev ) ;
2008-04-29 16:03:37 +04:00
return sprintf ( buf , " %d \n " , PWM_FROM_REG ( data - > autofan [ nr ] . min_pwm ) ) ;
2005-04-17 02:20:36 +04:00
}
2007-07-05 22:36:12 +04:00
static ssize_t set_pwm_auto_pwm_min ( struct device * dev ,
struct device_attribute * attr , const char * buf , size_t count )
2005-04-17 02:20:36 +04:00
{
2007-07-05 22:36:12 +04:00
int nr = to_sensor_dev_attr ( attr ) - > index ;
2005-04-17 02:20:36 +04:00
struct i2c_client * client = to_i2c_client ( dev ) ;
struct lm85_data * data = i2c_get_clientdata ( client ) ;
long val = simple_strtol ( buf , NULL , 10 ) ;
2006-01-19 01:19:26 +03:00
mutex_lock ( & data - > update_lock ) ;
2005-04-17 02:20:36 +04:00
data - > autofan [ nr ] . min_pwm = PWM_TO_REG ( val ) ;
lm85_write_value ( client , LM85_REG_AFAN_MINPWM ( nr ) ,
data - > autofan [ nr ] . min_pwm ) ;
2006-01-19 01:19:26 +03:00
mutex_unlock ( & data - > update_lock ) ;
2005-04-17 02:20:36 +04:00
return count ;
}
2007-07-05 22:36:12 +04:00
static ssize_t show_pwm_auto_pwm_minctl ( struct device * dev ,
struct device_attribute * attr , char * buf )
2005-04-17 02:20:36 +04:00
{
2007-07-05 22:36:12 +04:00
int nr = to_sensor_dev_attr ( attr ) - > index ;
2005-04-17 02:20:36 +04:00
struct lm85_data * data = lm85_update_device ( dev ) ;
2008-04-29 16:03:37 +04:00
return sprintf ( buf , " %d \n " , data - > autofan [ nr ] . min_off ) ;
2005-04-17 02:20:36 +04:00
}
2007-07-05 22:36:12 +04:00
static ssize_t set_pwm_auto_pwm_minctl ( struct device * dev ,
struct device_attribute * attr , const char * buf , size_t count )
2005-04-17 02:20:36 +04:00
{
2007-07-05 22:36:12 +04:00
int nr = to_sensor_dev_attr ( attr ) - > index ;
2005-04-17 02:20:36 +04:00
struct i2c_client * client = to_i2c_client ( dev ) ;
struct lm85_data * data = i2c_get_clientdata ( client ) ;
long val = simple_strtol ( buf , NULL , 10 ) ;
2008-04-12 21:56:35 +04:00
u8 tmp ;
2005-04-17 02:20:36 +04:00
2006-01-19 01:19:26 +03:00
mutex_lock ( & data - > update_lock ) ;
2005-04-17 02:20:36 +04:00
data - > autofan [ nr ] . min_off = val ;
2008-04-12 21:56:35 +04:00
tmp = lm85_read_value ( client , LM85_REG_AFAN_SPIKE1 ) ;
tmp & = ~ ( 0x20 < < nr ) ;
if ( data - > autofan [ nr ] . min_off )
tmp | = 0x20 < < nr ;
lm85_write_value ( client , LM85_REG_AFAN_SPIKE1 , tmp ) ;
2006-01-19 01:19:26 +03:00
mutex_unlock ( & data - > update_lock ) ;
2005-04-17 02:20:36 +04:00
return count ;
}
2007-07-05 22:36:12 +04:00
2005-04-17 02:20:36 +04:00
# define pwm_auto(offset) \
2007-07-05 22:36:12 +04:00
static SENSOR_DEVICE_ATTR ( pwm # # offset # # _auto_channels , \
S_IRUGO | S_IWUSR , show_pwm_auto_channels , \
set_pwm_auto_channels , offset - 1 ) ; \
static SENSOR_DEVICE_ATTR ( pwm # # offset # # _auto_pwm_min , \
S_IRUGO | S_IWUSR , show_pwm_auto_pwm_min , \
set_pwm_auto_pwm_min , offset - 1 ) ; \
static SENSOR_DEVICE_ATTR ( pwm # # offset # # _auto_pwm_minctl , \
S_IRUGO | S_IWUSR , show_pwm_auto_pwm_minctl , \
2008-10-17 19:51:13 +04:00
set_pwm_auto_pwm_minctl , offset - 1 )
2007-07-05 22:36:12 +04:00
2005-04-17 02:20:36 +04:00
pwm_auto ( 1 ) ;
pwm_auto ( 2 ) ;
pwm_auto ( 3 ) ;
/* Temperature settings for automatic PWM control */
2007-07-05 22:36:12 +04:00
static ssize_t show_temp_auto_temp_off ( struct device * dev ,
struct device_attribute * attr , char * buf )
2005-04-17 02:20:36 +04:00
{
2007-07-05 22:36:12 +04:00
int nr = to_sensor_dev_attr ( attr ) - > index ;
2005-04-17 02:20:36 +04:00
struct lm85_data * data = lm85_update_device ( dev ) ;
2008-04-29 16:03:37 +04:00
return sprintf ( buf , " %d \n " , TEMP_FROM_REG ( data - > zone [ nr ] . limit ) -
2005-04-17 02:20:36 +04:00
HYST_FROM_REG ( data - > zone [ nr ] . hyst ) ) ;
}
2007-07-05 22:36:12 +04:00
static ssize_t set_temp_auto_temp_off ( struct device * dev ,
struct device_attribute * attr , const char * buf , size_t count )
2005-04-17 02:20:36 +04:00
{
2007-07-05 22:36:12 +04:00
int nr = to_sensor_dev_attr ( attr ) - > index ;
2005-04-17 02:20:36 +04:00
struct i2c_client * client = to_i2c_client ( dev ) ;
struct lm85_data * data = i2c_get_clientdata ( client ) ;
int min ;
long val = simple_strtol ( buf , NULL , 10 ) ;
2006-01-19 01:19:26 +03:00
mutex_lock ( & data - > update_lock ) ;
2005-04-17 02:20:36 +04:00
min = TEMP_FROM_REG ( data - > zone [ nr ] . limit ) ;
data - > zone [ nr ] . hyst = HYST_TO_REG ( min - val ) ;
2008-04-29 16:03:37 +04:00
if ( nr = = 0 | | nr = = 1 ) {
2005-04-17 02:20:36 +04:00
lm85_write_value ( client , LM85_REG_AFAN_HYST1 ,
( data - > zone [ 0 ] . hyst < < 4 )
2008-04-29 16:03:37 +04:00
| data - > zone [ 1 ] . hyst ) ;
2005-04-17 02:20:36 +04:00
} else {
lm85_write_value ( client , LM85_REG_AFAN_HYST2 ,
2008-04-29 16:03:37 +04:00
( data - > zone [ 2 ] . hyst < < 4 ) ) ;
2005-04-17 02:20:36 +04:00
}
2006-01-19 01:19:26 +03:00
mutex_unlock ( & data - > update_lock ) ;
2005-04-17 02:20:36 +04:00
return count ;
}
2007-07-05 22:36:12 +04:00
static ssize_t show_temp_auto_temp_min ( struct device * dev ,
struct device_attribute * attr , char * buf )
2005-04-17 02:20:36 +04:00
{
2007-07-05 22:36:12 +04:00
int nr = to_sensor_dev_attr ( attr ) - > index ;
2005-04-17 02:20:36 +04:00
struct lm85_data * data = lm85_update_device ( dev ) ;
2008-04-29 16:03:37 +04:00
return sprintf ( buf , " %d \n " , TEMP_FROM_REG ( data - > zone [ nr ] . limit ) ) ;
2005-04-17 02:20:36 +04:00
}
2007-07-05 22:36:12 +04:00
static ssize_t set_temp_auto_temp_min ( struct device * dev ,
struct device_attribute * attr , const char * buf , size_t count )
2005-04-17 02:20:36 +04:00
{
2007-07-05 22:36:12 +04:00
int nr = to_sensor_dev_attr ( attr ) - > index ;
2005-04-17 02:20:36 +04:00
struct i2c_client * client = to_i2c_client ( dev ) ;
struct lm85_data * data = i2c_get_clientdata ( client ) ;
long val = simple_strtol ( buf , NULL , 10 ) ;
2006-01-19 01:19:26 +03:00
mutex_lock ( & data - > update_lock ) ;
2005-04-17 02:20:36 +04:00
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 )
2008-10-17 19:51:13 +04:00
| ( data - > pwm_freq [ nr ] & 0x07 ) ) ;
2005-04-17 02:20:36 +04:00
2006-01-19 01:19:26 +03:00
mutex_unlock ( & data - > update_lock ) ;
2005-04-17 02:20:36 +04:00
return count ;
}
2007-07-05 22:36:12 +04:00
static ssize_t show_temp_auto_temp_max ( struct device * dev ,
struct device_attribute * attr , char * buf )
2005-04-17 02:20:36 +04:00
{
2007-07-05 22:36:12 +04:00
int nr = to_sensor_dev_attr ( attr ) - > index ;
2005-04-17 02:20:36 +04:00
struct lm85_data * data = lm85_update_device ( dev ) ;
2008-04-29 16:03:37 +04:00
return sprintf ( buf , " %d \n " , TEMP_FROM_REG ( data - > zone [ nr ] . limit ) +
2005-04-17 02:20:36 +04:00
RANGE_FROM_REG ( data - > zone [ nr ] . range ) ) ;
}
2007-07-05 22:36:12 +04:00
static ssize_t set_temp_auto_temp_max ( struct device * dev ,
struct device_attribute * attr , const char * buf , size_t count )
2005-04-17 02:20:36 +04:00
{
2007-07-05 22:36:12 +04:00
int nr = to_sensor_dev_attr ( attr ) - > index ;
2005-04-17 02:20:36 +04:00
struct i2c_client * client = to_i2c_client ( dev ) ;
struct lm85_data * data = i2c_get_clientdata ( client ) ;
int min ;
long val = simple_strtol ( buf , NULL , 10 ) ;
2006-01-19 01:19:26 +03:00
mutex_lock ( & data - > update_lock ) ;
2005-04-17 02:20:36 +04:00
min = TEMP_FROM_REG ( data - > zone [ nr ] . limit ) ;
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 )
2008-10-17 19:51:13 +04:00
| ( data - > pwm_freq [ nr ] & 0x07 ) ) ;
2006-01-19 01:19:26 +03:00
mutex_unlock ( & data - > update_lock ) ;
2005-04-17 02:20:36 +04:00
return count ;
}
2007-07-05 22:36:12 +04:00
static ssize_t show_temp_auto_temp_crit ( struct device * dev ,
struct device_attribute * attr , char * buf )
2005-04-17 02:20:36 +04:00
{
2007-07-05 22:36:12 +04:00
int nr = to_sensor_dev_attr ( attr ) - > index ;
2005-04-17 02:20:36 +04:00
struct lm85_data * data = lm85_update_device ( dev ) ;
2008-04-29 16:03:37 +04:00
return sprintf ( buf , " %d \n " , TEMP_FROM_REG ( data - > zone [ nr ] . critical ) ) ;
2005-04-17 02:20:36 +04:00
}
2007-07-05 22:36:12 +04:00
static ssize_t set_temp_auto_temp_crit ( struct device * dev ,
2008-04-29 16:03:37 +04:00
struct device_attribute * attr , const char * buf , size_t count )
2005-04-17 02:20:36 +04:00
{
2007-07-05 22:36:12 +04:00
int nr = to_sensor_dev_attr ( attr ) - > index ;
2005-04-17 02:20:36 +04:00
struct i2c_client * client = to_i2c_client ( dev ) ;
struct lm85_data * data = i2c_get_clientdata ( client ) ;
long val = simple_strtol ( buf , NULL , 10 ) ;
2006-01-19 01:19:26 +03:00
mutex_lock ( & data - > update_lock ) ;
2005-04-17 02:20:36 +04:00
data - > zone [ nr ] . critical = TEMP_TO_REG ( val ) ;
lm85_write_value ( client , LM85_REG_AFAN_CRITICAL ( nr ) ,
data - > zone [ nr ] . critical ) ;
2006-01-19 01:19:26 +03:00
mutex_unlock ( & data - > update_lock ) ;
2005-04-17 02:20:36 +04:00
return count ;
}
2007-07-05 22:36:12 +04:00
2005-04-17 02:20:36 +04:00
# define temp_auto(offset) \
2007-07-05 22:36:12 +04:00
static SENSOR_DEVICE_ATTR ( temp # # offset # # _auto_temp_off , \
S_IRUGO | S_IWUSR , show_temp_auto_temp_off , \
set_temp_auto_temp_off , offset - 1 ) ; \
static SENSOR_DEVICE_ATTR ( temp # # offset # # _auto_temp_min , \
S_IRUGO | S_IWUSR , show_temp_auto_temp_min , \
set_temp_auto_temp_min , offset - 1 ) ; \
static SENSOR_DEVICE_ATTR ( temp # # offset # # _auto_temp_max , \
S_IRUGO | S_IWUSR , show_temp_auto_temp_max , \
set_temp_auto_temp_max , offset - 1 ) ; \
static SENSOR_DEVICE_ATTR ( temp # # offset # # _auto_temp_crit , \
S_IRUGO | S_IWUSR , show_temp_auto_temp_crit , \
set_temp_auto_temp_crit , offset - 1 ) ;
2005-04-17 02:20:36 +04:00
temp_auto ( 1 ) ;
temp_auto ( 2 ) ;
temp_auto ( 3 ) ;
2006-09-24 23:14:35 +04:00
static struct attribute * lm85_attributes [ ] = {
2007-07-05 22:36:12 +04:00
& sensor_dev_attr_fan1_input . dev_attr . attr ,
& sensor_dev_attr_fan2_input . dev_attr . attr ,
& sensor_dev_attr_fan3_input . dev_attr . attr ,
& sensor_dev_attr_fan4_input . dev_attr . attr ,
& sensor_dev_attr_fan1_min . dev_attr . attr ,
& sensor_dev_attr_fan2_min . dev_attr . attr ,
& sensor_dev_attr_fan3_min . dev_attr . attr ,
& sensor_dev_attr_fan4_min . dev_attr . attr ,
2007-07-05 22:37:58 +04:00
& sensor_dev_attr_fan1_alarm . dev_attr . attr ,
& sensor_dev_attr_fan2_alarm . dev_attr . attr ,
& sensor_dev_attr_fan3_alarm . dev_attr . attr ,
& sensor_dev_attr_fan4_alarm . dev_attr . attr ,
2007-07-05 22:36:12 +04:00
& sensor_dev_attr_pwm1 . dev_attr . attr ,
& sensor_dev_attr_pwm2 . dev_attr . attr ,
& sensor_dev_attr_pwm3 . dev_attr . attr ,
& sensor_dev_attr_pwm1_enable . dev_attr . attr ,
& sensor_dev_attr_pwm2_enable . dev_attr . attr ,
& sensor_dev_attr_pwm3_enable . dev_attr . attr ,
2008-10-17 19:51:13 +04:00
& sensor_dev_attr_pwm1_freq . dev_attr . attr ,
& sensor_dev_attr_pwm2_freq . dev_attr . attr ,
& sensor_dev_attr_pwm3_freq . dev_attr . attr ,
2007-07-05 22:36:12 +04:00
& sensor_dev_attr_in0_input . dev_attr . attr ,
& sensor_dev_attr_in1_input . dev_attr . attr ,
& sensor_dev_attr_in2_input . dev_attr . attr ,
& sensor_dev_attr_in3_input . dev_attr . attr ,
& sensor_dev_attr_in0_min . dev_attr . attr ,
& sensor_dev_attr_in1_min . dev_attr . attr ,
& sensor_dev_attr_in2_min . dev_attr . attr ,
& sensor_dev_attr_in3_min . dev_attr . attr ,
& sensor_dev_attr_in0_max . dev_attr . attr ,
& sensor_dev_attr_in1_max . dev_attr . attr ,
& sensor_dev_attr_in2_max . dev_attr . attr ,
& sensor_dev_attr_in3_max . dev_attr . attr ,
2007-07-05 22:37:58 +04:00
& sensor_dev_attr_in0_alarm . dev_attr . attr ,
& sensor_dev_attr_in1_alarm . dev_attr . attr ,
& sensor_dev_attr_in2_alarm . dev_attr . attr ,
& sensor_dev_attr_in3_alarm . dev_attr . attr ,
2007-07-05 22:36:12 +04:00
& sensor_dev_attr_temp1_input . dev_attr . attr ,
& sensor_dev_attr_temp2_input . dev_attr . attr ,
& sensor_dev_attr_temp3_input . dev_attr . attr ,
& sensor_dev_attr_temp1_min . dev_attr . attr ,
& sensor_dev_attr_temp2_min . dev_attr . attr ,
& sensor_dev_attr_temp3_min . dev_attr . attr ,
& sensor_dev_attr_temp1_max . dev_attr . attr ,
& sensor_dev_attr_temp2_max . dev_attr . attr ,
& sensor_dev_attr_temp3_max . dev_attr . attr ,
2007-07-05 22:37:58 +04:00
& sensor_dev_attr_temp1_alarm . dev_attr . attr ,
& sensor_dev_attr_temp2_alarm . dev_attr . attr ,
& sensor_dev_attr_temp3_alarm . dev_attr . attr ,
& sensor_dev_attr_temp1_fault . dev_attr . attr ,
& sensor_dev_attr_temp3_fault . dev_attr . attr ,
2007-07-05 22:36:12 +04:00
& sensor_dev_attr_pwm1_auto_channels . dev_attr . attr ,
& sensor_dev_attr_pwm2_auto_channels . dev_attr . attr ,
& sensor_dev_attr_pwm3_auto_channels . dev_attr . attr ,
& sensor_dev_attr_pwm1_auto_pwm_min . dev_attr . attr ,
& sensor_dev_attr_pwm2_auto_pwm_min . dev_attr . attr ,
& sensor_dev_attr_pwm3_auto_pwm_min . dev_attr . attr ,
& sensor_dev_attr_temp1_auto_temp_min . dev_attr . attr ,
& sensor_dev_attr_temp2_auto_temp_min . dev_attr . attr ,
& sensor_dev_attr_temp3_auto_temp_min . dev_attr . attr ,
& sensor_dev_attr_temp1_auto_temp_max . dev_attr . attr ,
& sensor_dev_attr_temp2_auto_temp_max . dev_attr . attr ,
& sensor_dev_attr_temp3_auto_temp_max . dev_attr . attr ,
& sensor_dev_attr_temp1_auto_temp_crit . dev_attr . attr ,
& sensor_dev_attr_temp2_auto_temp_crit . dev_attr . attr ,
& sensor_dev_attr_temp3_auto_temp_crit . dev_attr . attr ,
2006-09-24 23:14:35 +04:00
& dev_attr_vrm . attr ,
& dev_attr_cpu0_vid . attr ,
& dev_attr_alarms . attr ,
NULL
} ;
static const struct attribute_group lm85_group = {
. attrs = lm85_attributes ,
} ;
2011-02-19 19:27:47 +03:00
static struct attribute * lm85_attributes_minctl [ ] = {
& sensor_dev_attr_pwm1_auto_pwm_minctl . dev_attr . attr ,
& sensor_dev_attr_pwm2_auto_pwm_minctl . dev_attr . attr ,
& sensor_dev_attr_pwm3_auto_pwm_minctl . dev_attr . attr ,
2011-04-29 18:33:36 +04:00
NULL
2011-02-19 19:27:47 +03:00
} ;
static const struct attribute_group lm85_group_minctl = {
. attrs = lm85_attributes_minctl ,
} ;
static struct attribute * lm85_attributes_temp_off [ ] = {
& sensor_dev_attr_temp1_auto_temp_off . dev_attr . attr ,
& sensor_dev_attr_temp2_auto_temp_off . dev_attr . attr ,
& sensor_dev_attr_temp3_auto_temp_off . dev_attr . attr ,
2011-04-29 18:33:36 +04:00
NULL
2011-02-19 19:27:47 +03:00
} ;
static const struct attribute_group lm85_group_temp_off = {
. attrs = lm85_attributes_temp_off ,
} ;
2007-07-05 22:37:21 +04:00
static struct attribute * lm85_attributes_in4 [ ] = {
2007-07-05 22:36:12 +04:00
& sensor_dev_attr_in4_input . dev_attr . attr ,
& sensor_dev_attr_in4_min . dev_attr . attr ,
& sensor_dev_attr_in4_max . dev_attr . attr ,
2007-07-05 22:37:58 +04:00
& sensor_dev_attr_in4_alarm . dev_attr . attr ,
2006-09-24 23:14:35 +04:00
NULL
} ;
2007-07-05 22:37:21 +04:00
static const struct attribute_group lm85_group_in4 = {
. attrs = lm85_attributes_in4 ,
} ;
static struct attribute * lm85_attributes_in567 [ ] = {
& sensor_dev_attr_in5_input . dev_attr . attr ,
& sensor_dev_attr_in6_input . dev_attr . attr ,
& sensor_dev_attr_in7_input . dev_attr . attr ,
& sensor_dev_attr_in5_min . dev_attr . attr ,
& sensor_dev_attr_in6_min . dev_attr . attr ,
& sensor_dev_attr_in7_min . dev_attr . attr ,
& sensor_dev_attr_in5_max . dev_attr . attr ,
& sensor_dev_attr_in6_max . dev_attr . attr ,
& sensor_dev_attr_in7_max . dev_attr . attr ,
2007-07-05 22:37:58 +04:00
& sensor_dev_attr_in5_alarm . dev_attr . attr ,
& sensor_dev_attr_in6_alarm . dev_attr . attr ,
& sensor_dev_attr_in7_alarm . dev_attr . attr ,
2007-07-05 22:37:21 +04:00
NULL
} ;
static const struct attribute_group lm85_group_in567 = {
. attrs = lm85_attributes_in567 ,
2006-09-24 23:14:35 +04:00
} ;
2008-06-25 17:10:30 +04:00
static void lm85_init_client ( struct i2c_client * client )
{
int value ;
/* Start monitoring if needed */
value = lm85_read_value ( client , LM85_REG_CONFIG ) ;
if ( ! ( value & 0x01 ) ) {
dev_info ( & client - > dev , " Starting monitoring \n " ) ;
lm85_write_value ( client , LM85_REG_CONFIG , value | 0x01 ) ;
}
/* Warn about unusual configuration bits */
if ( value & 0x02 )
dev_warn ( & client - > dev , " Device configuration is locked \n " ) ;
if ( ! ( value & 0x04 ) )
dev_warn ( & client - > dev , " Device is not ready \n " ) ;
}
2009-09-15 19:18:14 +04:00
static int lm85_is_fake ( struct i2c_client * client )
{
/*
* Differenciate between real LM96000 and Winbond WPCD377I . The latter
* emulate the former except that it has no hardware monitoring function
* so the readings are always 0.
*/
int i ;
u8 in_temp , fan ;
for ( i = 0 ; i < 8 ; i + + ) {
in_temp = i2c_smbus_read_byte_data ( client , 0x20 + i ) ;
fan = i2c_smbus_read_byte_data ( client , 0x28 + i ) ;
if ( in_temp ! = 0x00 | | fan ! = 0xff )
return 0 ;
}
return 1 ;
}
2008-10-17 19:51:14 +04:00
/* Return 0 if detection is successful, -ENODEV otherwise */
2009-12-14 23:17:23 +03:00
static int lm85_detect ( struct i2c_client * client , struct i2c_board_info * info )
2005-04-17 02:20:36 +04:00
{
2008-10-17 19:51:14 +04:00
struct i2c_adapter * adapter = client - > adapter ;
int address = client - > addr ;
2008-06-25 16:47:35 +04:00
const char * type_name ;
2009-12-09 22:35:53 +03:00
int company , verstep ;
2005-04-17 02:20:36 +04:00
2008-06-25 16:47:35 +04:00
if ( ! i2c_check_functionality ( adapter , I2C_FUNC_SMBUS_BYTE_DATA ) ) {
2005-04-17 02:20:36 +04:00
/* We need to be able to do byte I/O */
2008-10-17 19:51:14 +04:00
return - ENODEV ;
2008-04-29 16:03:37 +04:00
}
2005-04-17 02:20:36 +04:00
2009-12-09 22:35:53 +03:00
/* Determine the chip type */
company = lm85_read_value ( client , LM85_REG_COMPANY ) ;
verstep = lm85_read_value ( client , LM85_REG_VERSTEP ) ;
dev_dbg ( & adapter - > dev , " Detecting device at 0x%02x with "
" COMPANY: 0x%02x and VERSTEP: 0x%02x \n " ,
address , company , verstep ) ;
/* All supported chips have the version in common */
if ( ( verstep & LM85_VERSTEP_VMASK ) ! = LM85_VERSTEP_GENERIC & &
( verstep & LM85_VERSTEP_VMASK ) ! = LM85_VERSTEP_GENERIC2 ) {
dev_dbg ( & adapter - > dev ,
" Autodetection failed: unsupported version \n " ) ;
return - ENODEV ;
}
type_name = " lm85 " ;
/* Now, refine the detection */
if ( company = = LM85_COMPANY_NATIONAL ) {
switch ( verstep ) {
case LM85_VERSTEP_LM85C :
type_name = " lm85c " ;
break ;
case LM85_VERSTEP_LM85B :
type_name = " lm85b " ;
break ;
case LM85_VERSTEP_LM96000_1 :
case LM85_VERSTEP_LM96000_2 :
/* Check for Winbond WPCD377I */
if ( lm85_is_fake ( client ) ) {
dev_dbg ( & adapter - > dev ,
" Found Winbond WPCD377I, ignoring \n " ) ;
return - ENODEV ;
2008-10-17 19:51:13 +04:00
}
2009-12-09 22:35:53 +03:00
break ;
}
} else if ( company = = LM85_COMPANY_ANALOG_DEV ) {
switch ( verstep ) {
case LM85_VERSTEP_ADM1027 :
type_name = " adm1027 " ;
break ;
case LM85_VERSTEP_ADT7463 :
case LM85_VERSTEP_ADT7463C :
type_name = " adt7463 " ;
break ;
case LM85_VERSTEP_ADT7468_1 :
case LM85_VERSTEP_ADT7468_2 :
type_name = " adt7468 " ;
break ;
2005-04-17 02:20:36 +04:00
}
2009-12-09 22:35:53 +03:00
} else if ( company = = LM85_COMPANY_SMSC ) {
switch ( verstep ) {
case LM85_VERSTEP_EMC6D100_A0 :
case LM85_VERSTEP_EMC6D100_A1 :
/* Note: we can't tell a '100 from a '101 */
type_name = " emc6d100 " ;
break ;
case LM85_VERSTEP_EMC6D102 :
type_name = " emc6d102 " ;
break ;
2011-02-18 11:18:26 +03:00
case LM85_VERSTEP_EMC6D103_A0 :
case LM85_VERSTEP_EMC6D103_A1 :
type_name = " emc6d103 " ;
break ;
case LM85_VERSTEP_EMC6D103S :
type_name = " emc6d103s " ;
break ;
2009-12-09 22:35:53 +03:00
}
} else {
dev_dbg ( & adapter - > dev ,
" Autodetection failed: unknown vendor \n " ) ;
return - ENODEV ;
2005-04-17 02:20:36 +04:00
}
2008-10-17 19:51:14 +04:00
strlcpy ( info - > type , type_name , I2C_NAME_SIZE ) ;
return 0 ;
}
2005-04-17 02:20:36 +04:00
2011-02-25 19:26:47 +03:00
static void lm85_remove_files ( struct i2c_client * client , struct lm85_data * data )
{
sysfs_remove_group ( & client - > dev . kobj , & lm85_group ) ;
2011-02-19 19:27:47 +03:00
if ( data - > type ! = emc6d103s ) {
sysfs_remove_group ( & client - > dev . kobj , & lm85_group_minctl ) ;
sysfs_remove_group ( & client - > dev . kobj , & lm85_group_temp_off ) ;
}
2011-02-25 19:26:47 +03:00
if ( ! data - > has_vid5 )
sysfs_remove_group ( & client - > dev . kobj , & lm85_group_in4 ) ;
if ( data - > type = = emc6d100 )
sysfs_remove_group ( & client - > dev . kobj , & lm85_group_in567 ) ;
}
2008-10-17 19:51:14 +04:00
static int lm85_probe ( struct i2c_client * client ,
const struct i2c_device_id * id )
{
struct lm85_data * data ;
int err ;
data = kzalloc ( sizeof ( struct lm85_data ) , GFP_KERNEL ) ;
if ( ! data )
return - ENOMEM ;
i2c_set_clientdata ( client , data ) ;
data - > type = id - > driver_data ;
2006-01-19 01:19:26 +03:00
mutex_init ( & data - > update_lock ) ;
2005-04-17 02:20:36 +04:00
2008-10-17 19:51:14 +04:00
/* Fill in the chip specific driver values */
switch ( data - > type ) {
case adm1027 :
case adt7463 :
2010-10-28 22:31:50 +04:00
case adt7468 :
2008-10-17 19:51:14 +04:00
case emc6d100 :
case emc6d102 :
2011-02-18 11:18:26 +03:00
case emc6d103 :
2011-02-19 19:27:47 +03:00
case emc6d103s :
2008-10-17 19:51:14 +04:00
data - > freq_map = adm1027_freq_map ;
break ;
default :
data - > freq_map = lm85_freq_map ;
}
2005-04-17 02:20:36 +04:00
/* Set the VRM version */
2005-07-31 23:52:01 +04:00
data - > vrm = vid_which_vrm ( ) ;
2005-04-17 02:20:36 +04:00
/* Initialize the LM85 chip */
2008-06-25 16:47:35 +04:00
lm85_init_client ( client ) ;
2005-04-17 02:20:36 +04:00
/* Register sysfs hooks */
2008-06-25 16:47:35 +04:00
err = sysfs_create_group ( & client - > dev . kobj , & lm85_group ) ;
if ( err )
2008-10-17 19:51:14 +04:00
goto err_kfree ;
2005-04-17 02:20:36 +04:00
2011-02-19 19:27:47 +03:00
/* minctl and temp_off exist on all chips except emc6d103s */
if ( data - > type ! = emc6d103s ) {
err = sysfs_create_group ( & client - > dev . kobj , & lm85_group_minctl ) ;
if ( err )
goto err_kfree ;
err = sysfs_create_group ( & client - > dev . kobj ,
& lm85_group_temp_off ) ;
if ( err )
goto err_kfree ;
}
2008-11-13 00:26:59 +03:00
/* The ADT7463/68 have an optional VRM 10 mode where pin 21 is used
2005-11-26 22:07:54 +03:00
as a sixth digital VID input rather than an analog input . */
2011-02-25 19:19:42 +03:00
if ( data - > type = = adt7463 | | data - > type = = adt7468 ) {
u8 vid = lm85_read_value ( client , LM85_REG_VID ) ;
if ( vid & 0x80 )
data - > has_vid5 = true ;
}
if ( ! data - > has_vid5 )
2008-06-25 16:47:35 +04:00
if ( ( err = sysfs_create_group ( & client - > dev . kobj ,
2007-07-05 22:37:21 +04:00
& lm85_group_in4 ) ) )
2008-10-17 19:51:14 +04:00
goto err_remove_files ;
2007-07-05 22:37:21 +04:00
/* The EMC6D100 has 3 additional voltage inputs */
2008-10-17 19:51:14 +04:00
if ( data - > type = = emc6d100 )
2008-06-25 16:47:35 +04:00
if ( ( err = sysfs_create_group ( & client - > dev . kobj ,
2007-07-05 22:37:21 +04:00
& lm85_group_in567 ) ) )
2008-10-17 19:51:14 +04:00
goto err_remove_files ;
2006-09-24 23:14:35 +04:00
2008-06-25 16:47:35 +04:00
data - > hwmon_dev = hwmon_device_register ( & client - > dev ) ;
2007-08-21 00:46:20 +04:00
if ( IS_ERR ( data - > hwmon_dev ) ) {
err = PTR_ERR ( data - > hwmon_dev ) ;
2008-10-17 19:51:14 +04:00
goto err_remove_files ;
2005-11-26 22:07:54 +03:00
}
2005-04-17 02:20:36 +04:00
return 0 ;
/* Error out and cleanup code */
2008-10-17 19:51:14 +04:00
err_remove_files :
2011-02-25 19:26:47 +03:00
lm85_remove_files ( client , data ) ;
2008-10-17 19:51:14 +04:00
err_kfree :
2005-04-17 02:20:36 +04:00
kfree ( data ) ;
return err ;
}
2008-10-17 19:51:14 +04:00
static int lm85_remove ( struct i2c_client * client )
2005-04-17 02:20:36 +04:00
{
2005-07-16 05:39:18 +04:00
struct lm85_data * data = i2c_get_clientdata ( client ) ;
2007-08-21 00:46:20 +04:00
hwmon_device_unregister ( data - > hwmon_dev ) ;
2011-02-25 19:26:47 +03:00
lm85_remove_files ( client , data ) ;
2005-07-16 05:39:18 +04:00
kfree ( data ) ;
2005-04-17 02:20:36 +04:00
return 0 ;
}
2005-10-26 23:05:46 +04:00
static int lm85_read_value ( struct i2c_client * client , u8 reg )
2005-04-17 02:20:36 +04:00
{
int res ;
/* What size location is it? */
2008-04-29 16:03:37 +04:00
switch ( reg ) {
case LM85_REG_FAN ( 0 ) : /* Read WORD data */
case LM85_REG_FAN ( 1 ) :
case LM85_REG_FAN ( 2 ) :
case LM85_REG_FAN ( 3 ) :
case LM85_REG_FAN_MIN ( 0 ) :
case LM85_REG_FAN_MIN ( 1 ) :
case LM85_REG_FAN_MIN ( 2 ) :
case LM85_REG_FAN_MIN ( 3 ) :
case LM85_REG_ALARM1 : /* Read both bytes at once */
res = i2c_smbus_read_byte_data ( client , reg ) & 0xff ;
res | = i2c_smbus_read_byte_data ( client , reg + 1 ) < < 8 ;
break ;
2005-04-17 02:20:36 +04:00
default : /* Read BYTE data */
res = i2c_smbus_read_byte_data ( client , reg ) ;
2008-04-29 16:03:37 +04:00
break ;
2005-04-17 02:20:36 +04:00
}
2008-04-29 16:03:37 +04:00
return res ;
2005-04-17 02:20:36 +04:00
}
2008-06-25 16:47:35 +04:00
static void lm85_write_value ( struct i2c_client * client , u8 reg , int value )
2005-04-17 02:20:36 +04:00
{
2008-04-29 16:03:37 +04:00
switch ( reg ) {
case LM85_REG_FAN ( 0 ) : /* Write WORD data */
case LM85_REG_FAN ( 1 ) :
case LM85_REG_FAN ( 2 ) :
case LM85_REG_FAN ( 3 ) :
case LM85_REG_FAN_MIN ( 0 ) :
case LM85_REG_FAN_MIN ( 1 ) :
case LM85_REG_FAN_MIN ( 2 ) :
case LM85_REG_FAN_MIN ( 3 ) :
2005-04-17 02:20:36 +04:00
/* NOTE: ALARM is read only, so not included here */
2008-06-25 16:47:35 +04:00
i2c_smbus_write_byte_data ( client , reg , value & 0xff ) ;
i2c_smbus_write_byte_data ( client , reg + 1 , value > > 8 ) ;
2008-04-29 16:03:37 +04:00
break ;
2005-04-17 02:20:36 +04:00
default : /* Write BYTE data */
2008-06-25 16:47:35 +04:00
i2c_smbus_write_byte_data ( client , reg , value ) ;
2008-04-29 16:03:37 +04:00
break ;
2005-04-17 02:20:36 +04:00
}
}
static struct lm85_data * lm85_update_device ( struct device * dev )
{
struct i2c_client * client = to_i2c_client ( dev ) ;
struct lm85_data * data = i2c_get_clientdata ( client ) ;
int i ;
2006-01-19 01:19:26 +03:00
mutex_lock ( & data - > update_lock ) ;
2005-04-17 02:20:36 +04:00
2008-04-29 16:03:37 +04:00
if ( ! data - > valid | |
time_after ( jiffies , data - > last_reading + LM85_DATA_INTERVAL ) ) {
2005-04-17 02:20:36 +04:00
/* Things that change quickly */
dev_dbg ( & client - > dev , " Reading sensor values \n " ) ;
2008-04-29 16:03:37 +04:00
2005-04-17 02:20:36 +04:00
/* Have to read extended bits first to "freeze" the
* more significant bits that are read later .
2007-07-05 22:38:32 +04:00
* There are 2 additional resolution bits per channel and we
* have room for 4 , so we shift them to the left .
2005-04-17 02:20:36 +04:00
*/
2008-11-13 00:26:59 +03:00
if ( data - > type = = adm1027 | | data - > type = = adt7463 | |
data - > type = = adt7468 ) {
2005-04-17 02:20:36 +04:00
int ext1 = lm85_read_value ( client ,
ADM1027_REG_EXTEND_ADC1 ) ;
int ext2 = lm85_read_value ( client ,
ADM1027_REG_EXTEND_ADC2 ) ;
int val = ( ext1 < < 8 ) + ext2 ;
2008-04-29 16:03:37 +04:00
for ( i = 0 ; i < = 4 ; i + + )
data - > in_ext [ i ] =
( ( val > > ( i * 2 ) ) & 0x03 ) < < 2 ;
2005-04-17 02:20:36 +04:00
2008-04-29 16:03:37 +04:00
for ( i = 0 ; i < = 2 ; i + + )
data - > temp_ext [ i ] =
( val > > ( ( i + 4 ) * 2 ) ) & 0x0c ;
2005-04-17 02:20:36 +04:00
}
2005-11-26 22:07:54 +03:00
data - > vid = lm85_read_value ( client , LM85_REG_VID ) ;
for ( i = 0 ; i < = 3 ; + + i ) {
2005-04-17 02:20:36 +04:00
data - > in [ i ] =
lm85_read_value ( client , LM85_REG_IN ( i ) ) ;
2008-06-25 16:47:35 +04:00
data - > fan [ i ] =
lm85_read_value ( client , LM85_REG_FAN ( i ) ) ;
2005-04-17 02:20:36 +04:00
}
2011-02-25 19:19:42 +03:00
if ( ! data - > has_vid5 )
data - > in [ 4 ] = lm85_read_value ( client , LM85_REG_IN ( 4 ) ) ;
2005-11-26 22:07:54 +03:00
2008-11-13 00:26:59 +03:00
if ( data - > type = = adt7468 )
data - > cfg5 = lm85_read_value ( client , ADT7468_REG_CFG5 ) ;
2005-04-17 02:20:36 +04:00
for ( i = 0 ; i < = 2 ; + + i ) {
data - > temp [ i ] =
lm85_read_value ( client , LM85_REG_TEMP ( i ) ) ;
data - > pwm [ i ] =
lm85_read_value ( client , LM85_REG_PWM ( i ) ) ;
2008-11-13 00:26:59 +03:00
if ( IS_ADT7468_OFF64 ( data ) )
data - > temp [ i ] - = 64 ;
2005-04-17 02:20:36 +04:00
}
data - > alarms = lm85_read_value ( client , LM85_REG_ALARM1 ) ;
2008-05-01 10:47:33 +04:00
if ( data - > type = = emc6d100 ) {
2005-04-17 02:20:36 +04:00
/* Three more voltage sensors */
for ( i = 5 ; i < = 7 ; + + i ) {
2008-04-29 16:03:37 +04:00
data - > in [ i ] = lm85_read_value ( client ,
EMC6D100_REG_IN ( i ) ) ;
2005-04-17 02:20:36 +04:00
}
/* More alarm bits */
2008-04-29 16:03:37 +04:00
data - > alarms | = lm85_read_value ( client ,
EMC6D100_REG_ALARM3 ) < < 16 ;
2011-02-19 19:27:47 +03:00
} else if ( data - > type = = emc6d102 | | data - > type = = emc6d103 | |
data - > type = = emc6d103s ) {
2005-04-17 02:20:36 +04:00
/* Have to read LSB bits after the MSB ones because
the reading of the MSB bits has frozen the
LSBs ( backward from the ADM1027 ) .
*/
int ext1 = lm85_read_value ( client ,
EMC6D102_REG_EXTEND_ADC1 ) ;
int ext2 = lm85_read_value ( client ,
EMC6D102_REG_EXTEND_ADC2 ) ;
int ext3 = lm85_read_value ( client ,
EMC6D102_REG_EXTEND_ADC3 ) ;
int ext4 = lm85_read_value ( client ,
EMC6D102_REG_EXTEND_ADC4 ) ;
data - > in_ext [ 0 ] = ext3 & 0x0f ;
data - > in_ext [ 1 ] = ext4 & 0x0f ;
2008-06-25 16:47:35 +04:00
data - > in_ext [ 2 ] = ext4 > > 4 ;
data - > in_ext [ 3 ] = ext3 > > 4 ;
data - > in_ext [ 4 ] = ext2 > > 4 ;
2005-04-17 02:20:36 +04:00
data - > temp_ext [ 0 ] = ext1 & 0x0f ;
data - > temp_ext [ 1 ] = ext2 & 0x0f ;
2008-06-25 16:47:35 +04:00
data - > temp_ext [ 2 ] = ext1 > > 4 ;
2005-04-17 02:20:36 +04:00
}
2008-04-29 16:03:37 +04:00
data - > last_reading = jiffies ;
} /* last_reading */
2005-04-17 02:20:36 +04:00
2008-04-29 16:03:37 +04:00
if ( ! data - > valid | |
time_after ( jiffies , data - > last_config + LM85_CONFIG_INTERVAL ) ) {
2005-04-17 02:20:36 +04:00
/* Things that don't change often */
dev_dbg ( & client - > dev , " Reading config values \n " ) ;
2005-11-26 22:07:54 +03:00
for ( i = 0 ; i < = 3 ; + + i ) {
2005-04-17 02:20:36 +04:00
data - > in_min [ i ] =
lm85_read_value ( client , LM85_REG_IN_MIN ( i ) ) ;
data - > in_max [ i ] =
lm85_read_value ( client , LM85_REG_IN_MAX ( i ) ) ;
2008-06-25 16:47:35 +04:00
data - > fan_min [ i ] =
lm85_read_value ( client , LM85_REG_FAN_MIN ( i ) ) ;
2005-04-17 02:20:36 +04:00
}
2011-02-25 19:19:42 +03:00
if ( ! data - > has_vid5 ) {
2005-11-26 22:07:54 +03:00
data - > in_min [ 4 ] = lm85_read_value ( client ,
LM85_REG_IN_MIN ( 4 ) ) ;
data - > in_max [ 4 ] = lm85_read_value ( client ,
LM85_REG_IN_MAX ( 4 ) ) ;
}
2008-04-29 16:03:37 +04:00
if ( data - > type = = emc6d100 ) {
2005-04-17 02:20:36 +04:00
for ( i = 5 ; i < = 7 ; + + i ) {
2008-04-29 16:03:37 +04:00
data - > in_min [ i ] = lm85_read_value ( client ,
EMC6D100_REG_IN_MIN ( i ) ) ;
data - > in_max [ i ] = lm85_read_value ( client ,
EMC6D100_REG_IN_MAX ( i ) ) ;
2005-04-17 02:20:36 +04:00
}
}
for ( i = 0 ; i < = 2 ; + + i ) {
2008-06-25 16:47:35 +04:00
int val ;
2005-04-17 02:20:36 +04:00
data - > temp_min [ i ] =
lm85_read_value ( client , LM85_REG_TEMP_MIN ( i ) ) ;
data - > temp_max [ i ] =
lm85_read_value ( client , LM85_REG_TEMP_MAX ( i ) ) ;
data - > autofan [ i ] . config =
lm85_read_value ( client , LM85_REG_AFAN_CONFIG ( i ) ) ;
val = lm85_read_value ( client , LM85_REG_AFAN_RANGE ( i ) ) ;
2008-10-17 19:51:13 +04:00
data - > pwm_freq [ i ] = val & 0x07 ;
2008-06-25 16:47:35 +04:00
data - > zone [ i ] . range = val > > 4 ;
2005-04-17 02:20:36 +04:00
data - > autofan [ i ] . min_pwm =
lm85_read_value ( client , LM85_REG_AFAN_MINPWM ( i ) ) ;
data - > zone [ i ] . limit =
lm85_read_value ( client , LM85_REG_AFAN_LIMIT ( i ) ) ;
data - > zone [ i ] . critical =
lm85_read_value ( client , LM85_REG_AFAN_CRITICAL ( i ) ) ;
2008-11-13 00:26:59 +03:00
if ( IS_ADT7468_OFF64 ( data ) ) {
data - > temp_min [ i ] - = 64 ;
data - > temp_max [ i ] - = 64 ;
data - > zone [ i ] . limit - = 64 ;
data - > zone [ i ] . critical - = 64 ;
}
2005-04-17 02:20:36 +04:00
}
2011-02-19 19:27:47 +03:00
if ( data - > type ! = emc6d103s ) {
i = lm85_read_value ( client , LM85_REG_AFAN_SPIKE1 ) ;
data - > autofan [ 0 ] . min_off = ( i & 0x20 ) ! = 0 ;
data - > autofan [ 1 ] . min_off = ( i & 0x40 ) ! = 0 ;
data - > autofan [ 2 ] . min_off = ( i & 0x80 ) ! = 0 ;
2005-04-17 02:20:36 +04:00
2011-02-19 19:27:47 +03:00
i = lm85_read_value ( client , LM85_REG_AFAN_HYST1 ) ;
data - > zone [ 0 ] . hyst = i > > 4 ;
data - > zone [ 1 ] . hyst = i & 0x0f ;
2005-04-17 02:20:36 +04:00
2011-02-19 19:27:47 +03:00
i = lm85_read_value ( client , LM85_REG_AFAN_HYST2 ) ;
data - > zone [ 2 ] . hyst = i > > 4 ;
}
2005-04-17 02:20:36 +04:00
data - > last_config = jiffies ;
2008-04-29 16:03:37 +04:00
} /* last_config */
2005-04-17 02:20:36 +04:00
data - > valid = 1 ;
2006-01-19 01:19:26 +03:00
mutex_unlock ( & data - > update_lock ) ;
2005-04-17 02:20:36 +04:00
return data ;
}
static int __init sm_lm85_init ( void )
{
return i2c_add_driver ( & lm85_driver ) ;
}
2008-04-29 16:03:37 +04:00
static void __exit sm_lm85_exit ( void )
2005-04-17 02:20:36 +04:00
{
i2c_del_driver ( & lm85_driver ) ;
}
MODULE_LICENSE ( " GPL " ) ;
2008-04-29 16:03:37 +04:00
MODULE_AUTHOR ( " Philip Pokorny <ppokorny@penguincomputing.com>, "
" Margit Schubert-While <margitsw@t-online.de>, "
2008-06-25 16:47:35 +04:00
" Justin Thiessen <jthiessen@penguincomputing.com> " ) ;
2005-04-17 02:20:36 +04:00
MODULE_DESCRIPTION ( " LM85-B, LM85-C driver " ) ;
module_init ( sm_lm85_init ) ;
module_exit ( sm_lm85_exit ) ;