2005-04-17 02:20:36 +04:00
/*
* lm80 . c - From lm_sensors , Linux kernel modules for hardware
2012-01-19 23:02:20 +04:00
* monitoring
2005-04-17 02:20:36 +04:00
* Copyright ( C ) 1998 , 1999 Frodo Looijaard < frodol @ dds . nl >
2012-01-19 23:02:20 +04:00
* and Philip Edelbrock < phil @ netroedge . com >
2005-04-17 02:20:36 +04:00
*
* Ported to Linux 2.6 by Tiago Sousa < mirage @ kaotik . org >
*
* 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>
2008-01-05 17:37:05 +03: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 [ ] = { 0x28 , 0x29 , 0x2a , 0x2b , 0x2c , 0x2d ,
0x2e , 0x2f , I2C_CLIENT_END } ;
2005-04-17 02:20:36 +04:00
/* Many LM80 constants specified below */
/* The LM80 registers */
# define LM80_REG_IN_MAX(nr) (0x2a + (nr) * 2)
# define LM80_REG_IN_MIN(nr) (0x2b + (nr) * 2)
# define LM80_REG_IN(nr) (0x20 + (nr))
# define LM80_REG_FAN1 0x28
# define LM80_REG_FAN2 0x29
# define LM80_REG_FAN_MIN(nr) (0x3b + (nr))
# define LM80_REG_TEMP 0x27
# define LM80_REG_TEMP_HOT_MAX 0x38
# define LM80_REG_TEMP_HOT_HYST 0x39
# define LM80_REG_TEMP_OS_MAX 0x3a
# define LM80_REG_TEMP_OS_HYST 0x3b
# define LM80_REG_CONFIG 0x00
# define LM80_REG_ALARM1 0x01
# define LM80_REG_ALARM2 0x02
# define LM80_REG_MASK1 0x03
# define LM80_REG_MASK2 0x04
# define LM80_REG_FANDIV 0x05
# define LM80_REG_RES 0x06
2012-01-31 18:27:11 +04:00
# define LM96080_REG_CONV_RATE 0x07
# define LM96080_REG_MAN_ID 0x3e
# define LM96080_REG_DEV_ID 0x3f
2005-04-17 02:20:36 +04:00
2012-01-19 23:02:20 +04:00
/*
* Conversions . Rounding and limit checking is only done on the TO_REG
* variants . Note that you should be a bit careful with which arguments
* these macros are called : arguments may be evaluated more than once .
* Fixing this is just not worth it .
*/
2005-04-17 02:20:36 +04:00
2013-01-09 20:09:34 +04:00
# define IN_TO_REG(val) (clamp_val(((val) + 5) / 10, 0, 255))
2012-01-04 23:58:53 +04:00
# define IN_FROM_REG(val) ((val) * 10)
2005-04-17 02:20:36 +04:00
static inline unsigned char FAN_TO_REG ( unsigned rpm , unsigned div )
{
if ( rpm = = 0 )
return 255 ;
2013-01-09 20:09:34 +04:00
rpm = clamp_val ( rpm , 1 , 1000000 ) ;
return clamp_val ( ( 1350000 + rpm * div / 2 ) / ( rpm * div ) , 1 , 254 ) ;
2005-04-17 02:20:36 +04:00
}
2012-01-04 23:58:53 +04:00
# define FAN_FROM_REG(val, div) ((val) == 0 ? -1 : \
( val ) = = 255 ? 0 : 1350000 / ( ( div ) * ( val ) ) )
2005-04-17 02:20:36 +04:00
2014-04-13 20:30:09 +04:00
# define TEMP_FROM_REG(reg) ((reg) * 125 / 32)
# define TEMP_TO_REG(temp) (DIV_ROUND_CLOSEST(clamp_val((temp), \
- 128000 , 127000 ) , 1000 ) < < 8 )
2005-04-17 02:20:36 +04:00
# define DIV_FROM_REG(val) (1 << (val))
2014-04-13 20:45:15 +04:00
enum temp_index {
t_input = 0 ,
t_hot_max ,
t_hot_hyst ,
t_os_max ,
t_os_hyst ,
t_num_temp
} ;
static const u8 temp_regs [ t_num_temp ] = {
[ t_input ] = LM80_REG_TEMP ,
[ t_hot_max ] = LM80_REG_TEMP_HOT_MAX ,
[ t_hot_hyst ] = LM80_REG_TEMP_HOT_HYST ,
[ t_os_max ] = LM80_REG_TEMP_OS_MAX ,
[ t_os_hyst ] = LM80_REG_TEMP_OS_HYST ,
} ;
2014-04-13 21:53:50 +04:00
enum in_index {
i_input = 0 ,
i_max ,
i_min ,
i_num_in
} ;
2014-04-13 22:28:29 +04:00
enum fan_index {
f_input ,
f_min ,
f_num_fan
} ;
2005-04-17 02:20:36 +04:00
/*
* Client data ( each client gets its own )
*/
struct lm80_data {
2014-04-04 20:01:34 +04:00
struct i2c_client * client ;
2006-01-19 01:19:26 +03:00
struct mutex update_lock ;
2012-01-10 18:49:36 +04:00
char error ; /* !=0 if error occurred during last update */
2005-04-17 02:20:36 +04:00
char valid ; /* !=0 if following fields are valid */
unsigned long last_updated ; /* In jiffies */
2014-04-13 21:53:50 +04:00
u8 in [ i_num_in ] [ 7 ] ; /* Register value, 1st index is enum in_index */
2014-04-13 22:28:29 +04:00
u8 fan [ f_num_fan ] [ 2 ] ; /* Register value, 1st index enum fan_index */
2005-04-17 02:20:36 +04:00
u8 fan_div [ 2 ] ; /* Register encoding, shifted right */
2014-04-13 20:45:15 +04:00
s16 temp [ t_num_temp ] ; /* Register values, normalized to 16 bit */
2005-04-17 02:20:36 +04:00
u16 alarms ; /* Register encoding, combined */
} ;
2014-04-13 22:56:15 +04:00
static int lm80_read_value ( struct i2c_client * client , u8 reg )
{
return i2c_smbus_read_byte_data ( client , reg ) ;
}
2005-04-17 02:20:36 +04:00
2014-04-13 22:56:15 +04:00
static int lm80_write_value ( struct i2c_client * client , u8 reg , u8 value )
{
return i2c_smbus_write_byte_data ( client , reg , value ) ;
}
2005-04-17 02:20:36 +04:00
2014-04-13 22:56:15 +04:00
/* Called when we have found a new LM80 and after read errors */
static void lm80_init_client ( struct i2c_client * client )
{
/*
* Reset all except Watchdog values and last conversion values
* This sets fan - divs to 2 , among others . This makes most other
* initializations unnecessary
*/
lm80_write_value ( client , LM80_REG_CONFIG , 0x80 ) ;
/* Set 11-bit temperature resolution */
lm80_write_value ( client , LM80_REG_RES , 0x08 ) ;
2005-04-17 02:20:36 +04:00
2014-04-13 22:56:15 +04:00
/* Start monitoring */
lm80_write_value ( client , LM80_REG_CONFIG , 0x01 ) ;
}
2008-07-16 21:30:14 +04:00
2014-04-13 22:56:15 +04:00
static struct lm80_data * lm80_update_device ( struct device * dev )
{
struct lm80_data * data = dev_get_drvdata ( dev ) ;
struct i2c_client * client = data - > client ;
int i ;
int rv ;
int prev_rv ;
struct lm80_data * ret = data ;
mutex_lock ( & data - > update_lock ) ;
if ( data - > error )
lm80_init_client ( client ) ;
if ( time_after ( jiffies , data - > last_updated + 2 * HZ ) | | ! data - > valid ) {
dev_dbg ( dev , " Starting lm80 update \n " ) ;
for ( i = 0 ; i < = 6 ; i + + ) {
rv = lm80_read_value ( client , LM80_REG_IN ( i ) ) ;
if ( rv < 0 )
goto abort ;
data - > in [ i_input ] [ i ] = rv ;
rv = lm80_read_value ( client , LM80_REG_IN_MIN ( i ) ) ;
if ( rv < 0 )
goto abort ;
data - > in [ i_min ] [ i ] = rv ;
rv = lm80_read_value ( client , LM80_REG_IN_MAX ( i ) ) ;
if ( rv < 0 )
goto abort ;
data - > in [ i_max ] [ i ] = rv ;
}
rv = lm80_read_value ( client , LM80_REG_FAN1 ) ;
if ( rv < 0 )
goto abort ;
data - > fan [ f_input ] [ 0 ] = rv ;
rv = lm80_read_value ( client , LM80_REG_FAN_MIN ( 1 ) ) ;
if ( rv < 0 )
goto abort ;
data - > fan [ f_min ] [ 0 ] = rv ;
rv = lm80_read_value ( client , LM80_REG_FAN2 ) ;
if ( rv < 0 )
goto abort ;
data - > fan [ f_input ] [ 1 ] = rv ;
rv = lm80_read_value ( client , LM80_REG_FAN_MIN ( 2 ) ) ;
if ( rv < 0 )
goto abort ;
data - > fan [ f_min ] [ 1 ] = rv ;
prev_rv = rv = lm80_read_value ( client , LM80_REG_TEMP ) ;
if ( rv < 0 )
goto abort ;
rv = lm80_read_value ( client , LM80_REG_RES ) ;
if ( rv < 0 )
goto abort ;
data - > temp [ t_input ] = ( prev_rv < < 8 ) | ( rv & 0xf0 ) ;
for ( i = t_input + 1 ; i < t_num_temp ; i + + ) {
rv = lm80_read_value ( client , temp_regs [ i ] ) ;
if ( rv < 0 )
goto abort ;
data - > temp [ i ] = rv < < 8 ;
}
rv = lm80_read_value ( client , LM80_REG_FANDIV ) ;
if ( rv < 0 )
goto abort ;
data - > fan_div [ 0 ] = ( rv > > 2 ) & 0x03 ;
data - > fan_div [ 1 ] = ( rv > > 4 ) & 0x03 ;
prev_rv = rv = lm80_read_value ( client , LM80_REG_ALARM1 ) ;
if ( rv < 0 )
goto abort ;
rv = lm80_read_value ( client , LM80_REG_ALARM2 ) ;
if ( rv < 0 )
goto abort ;
data - > alarms = prev_rv + ( rv < < 8 ) ;
data - > last_updated = jiffies ;
data - > valid = 1 ;
data - > error = 0 ;
}
goto done ;
abort :
ret = ERR_PTR ( rv ) ;
data - > valid = 0 ;
data - > error = 1 ;
done :
mutex_unlock ( & data - > update_lock ) ;
return ret ;
}
2005-04-17 02:20:36 +04:00
/*
* Sysfs stuff
*/
2014-04-13 21:53:50 +04:00
static ssize_t show_in ( struct device * dev , struct device_attribute * attr ,
char * buf )
{
struct lm80_data * data = lm80_update_device ( dev ) ;
int index = to_sensor_dev_attr_2 ( attr ) - > index ;
int nr = to_sensor_dev_attr_2 ( attr ) - > nr ;
if ( IS_ERR ( data ) )
return PTR_ERR ( data ) ;
return sprintf ( buf , " %d \n " , IN_FROM_REG ( data - > in [ nr ] [ index ] ) ) ;
2005-04-17 02:20:36 +04:00
}
2014-04-13 21:53:50 +04:00
static ssize_t set_in ( struct device * dev , struct device_attribute * attr ,
const char * buf , size_t count )
{
struct lm80_data * data = dev_get_drvdata ( dev ) ;
struct i2c_client * client = data - > client ;
int index = to_sensor_dev_attr_2 ( attr ) - > index ;
int nr = to_sensor_dev_attr_2 ( attr ) - > nr ;
long val ;
u8 reg ;
int err = kstrtol ( buf , 10 , & val ) ;
if ( err < 0 )
return err ;
reg = nr = = i_min ? LM80_REG_IN_MIN ( index ) : LM80_REG_IN_MAX ( index ) ;
mutex_lock ( & data - > update_lock ) ;
data - > in [ nr ] [ index ] = IN_TO_REG ( val ) ;
lm80_write_value ( client , reg , data - > in [ nr ] [ index ] ) ;
mutex_unlock ( & data - > update_lock ) ;
return count ;
2005-04-17 02:20:36 +04:00
}
2008-01-05 17:37:05 +03:00
2014-04-13 22:28:29 +04:00
static ssize_t show_fan ( struct device * dev , struct device_attribute * attr ,
char * buf )
{
int index = to_sensor_dev_attr_2 ( attr ) - > index ;
int nr = to_sensor_dev_attr_2 ( attr ) - > nr ;
struct lm80_data * data = lm80_update_device ( dev ) ;
if ( IS_ERR ( data ) )
return PTR_ERR ( data ) ;
return sprintf ( buf , " %d \n " , FAN_FROM_REG ( data - > fan [ nr ] [ index ] ,
DIV_FROM_REG ( data - > fan_div [ index ] ) ) ) ;
2005-04-17 02:20:36 +04:00
}
2008-01-05 17:37:05 +03:00
static ssize_t show_fan_div ( struct device * dev , struct device_attribute * attr ,
char * buf )
{
int nr = to_sensor_dev_attr ( attr ) - > index ;
struct lm80_data * data = lm80_update_device ( dev ) ;
2012-01-05 17:41:53 +04:00
if ( IS_ERR ( data ) )
return PTR_ERR ( data ) ;
2008-01-05 17:37:05 +03:00
return sprintf ( buf , " %d \n " , DIV_FROM_REG ( data - > fan_div [ nr ] ) ) ;
2005-04-17 02:20:36 +04:00
}
2008-01-05 17:37:05 +03:00
static ssize_t set_fan_min ( struct device * dev , struct device_attribute * attr ,
const char * buf , size_t count )
{
2014-04-13 22:28:29 +04:00
int index = to_sensor_dev_attr_2 ( attr ) - > index ;
int nr = to_sensor_dev_attr_2 ( attr ) - > nr ;
2014-04-04 20:01:34 +04:00
struct lm80_data * data = dev_get_drvdata ( dev ) ;
struct i2c_client * client = data - > client ;
2012-01-10 18:49:35 +04:00
unsigned long val ;
int err = kstrtoul ( buf , 10 , & val ) ;
if ( err < 0 )
return err ;
2008-01-05 17:37:05 +03:00
mutex_lock ( & data - > update_lock ) ;
2014-04-13 22:28:29 +04:00
data - > fan [ nr ] [ index ] = FAN_TO_REG ( val ,
DIV_FROM_REG ( data - > fan_div [ index ] ) ) ;
lm80_write_value ( client , LM80_REG_FAN_MIN ( index + 1 ) ,
data - > fan [ nr ] [ index ] ) ;
2008-01-05 17:37:05 +03:00
mutex_unlock ( & data - > update_lock ) ;
return count ;
2005-04-17 02:20:36 +04:00
}
2012-01-19 23:02:20 +04:00
/*
* Note : we save and restore the fan minimum here , because its value is
* determined in part by the fan divisor . This follows the principle of
* least surprise ; the user doesn ' t expect the fan minimum to change just
* because the divisor changed .
*/
2008-01-05 17:37:05 +03:00
static ssize_t set_fan_div ( struct device * dev , struct device_attribute * attr ,
const char * buf , size_t count )
2005-04-17 02:20:36 +04:00
{
2008-01-05 17:37:05 +03:00
int nr = to_sensor_dev_attr ( attr ) - > index ;
2014-04-04 20:01:34 +04:00
struct lm80_data * data = dev_get_drvdata ( dev ) ;
struct i2c_client * client = data - > client ;
2012-01-10 18:49:35 +04:00
unsigned long min , val ;
2005-04-17 02:20:36 +04:00
u8 reg ;
2012-01-10 18:49:35 +04:00
int err = kstrtoul ( buf , 10 , & val ) ;
if ( err < 0 )
return err ;
2005-04-17 02:20:36 +04:00
/* Save fan_min */
2006-01-19 01:19:26 +03:00
mutex_lock ( & data - > update_lock ) ;
2014-04-13 22:28:29 +04:00
min = FAN_FROM_REG ( data - > fan [ f_min ] [ nr ] ,
2005-04-17 02:20:36 +04:00
DIV_FROM_REG ( data - > fan_div [ nr ] ) ) ;
switch ( val ) {
2012-01-04 23:58:53 +04:00
case 1 :
data - > fan_div [ nr ] = 0 ;
break ;
case 2 :
data - > fan_div [ nr ] = 1 ;
break ;
case 4 :
data - > fan_div [ nr ] = 2 ;
break ;
case 8 :
data - > fan_div [ nr ] = 3 ;
break ;
2005-04-17 02:20:36 +04:00
default :
2014-04-04 20:01:34 +04:00
dev_err ( dev ,
2013-01-10 22:01:24 +04:00
" fan_div value %ld not supported. Choose one of 1, 2, 4 or 8! \n " ,
val ) ;
2006-01-19 01:19:26 +03:00
mutex_unlock ( & data - > update_lock ) ;
2005-04-17 02:20:36 +04:00
return - EINVAL ;
}
2014-04-13 22:28:29 +04:00
reg = ( lm80_read_value ( client , LM80_REG_FANDIV ) &
~ ( 3 < < ( 2 * ( nr + 1 ) ) ) ) | ( data - > fan_div [ nr ] < < ( 2 * ( nr + 1 ) ) ) ;
2005-04-17 02:20:36 +04:00
lm80_write_value ( client , LM80_REG_FANDIV , reg ) ;
/* Restore fan_min */
2014-04-13 22:28:29 +04:00
data - > fan [ f_min ] [ nr ] = FAN_TO_REG ( min , DIV_FROM_REG ( data - > fan_div [ nr ] ) ) ;
lm80_write_value ( client , LM80_REG_FAN_MIN ( nr + 1 ) ,
data - > fan [ f_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 ;
}
2014-04-13 20:45:15 +04:00
static ssize_t show_temp ( struct device * dev , struct device_attribute * devattr ,
char * buf )
2005-04-17 02:20:36 +04:00
{
2014-04-13 20:45:15 +04:00
struct sensor_device_attribute * attr = to_sensor_dev_attr ( devattr ) ;
2005-04-17 02:20:36 +04:00
struct lm80_data * data = lm80_update_device ( dev ) ;
2012-01-05 17:41:53 +04:00
if ( IS_ERR ( data ) )
return PTR_ERR ( data ) ;
2014-04-13 20:45:15 +04:00
return sprintf ( buf , " %d \n " , TEMP_FROM_REG ( data - > temp [ attr - > index ] ) ) ;
2005-04-17 02:20:36 +04:00
}
2014-04-13 20:45:15 +04:00
static ssize_t set_temp ( struct device * dev , struct device_attribute * devattr ,
const char * buf , size_t count )
{
struct sensor_device_attribute * attr = to_sensor_dev_attr ( devattr ) ;
struct lm80_data * data = dev_get_drvdata ( dev ) ;
struct i2c_client * client = data - > client ;
int nr = attr - > index ;
long val ;
int err = kstrtol ( buf , 10 , & val ) ;
if ( err < 0 )
return err ;
2005-04-17 02:20:36 +04:00
2014-04-13 20:45:15 +04:00
mutex_lock ( & data - > update_lock ) ;
data - > temp [ nr ] = TEMP_TO_REG ( val ) ;
lm80_write_value ( client , temp_regs [ nr ] , data - > temp [ nr ] > > 8 ) ;
mutex_unlock ( & data - > update_lock ) ;
return count ;
2005-04-17 02:20:36 +04:00
}
2016-12-22 15:04:51 +03:00
static ssize_t alarms_show ( struct device * dev , struct device_attribute * attr ,
2008-01-05 17:35:09 +03:00
char * buf )
2005-04-17 02:20:36 +04:00
{
struct lm80_data * data = lm80_update_device ( dev ) ;
2012-01-05 17:41:53 +04:00
if ( IS_ERR ( data ) )
return PTR_ERR ( data ) ;
2005-04-17 02:20:36 +04:00
return sprintf ( buf , " %u \n " , data - > alarms ) ;
}
2008-01-05 17:40:38 +03:00
static ssize_t show_alarm ( struct device * dev , struct device_attribute * attr ,
char * buf )
{
int bitnr = to_sensor_dev_attr ( attr ) - > index ;
struct lm80_data * data = lm80_update_device ( dev ) ;
2012-01-05 17:41:53 +04:00
if ( IS_ERR ( data ) )
return PTR_ERR ( data ) ;
2008-01-05 17:40:38 +03:00
return sprintf ( buf , " %u \n " , ( data - > alarms > > bitnr ) & 1 ) ;
}
2014-04-13 21:53:50 +04:00
static SENSOR_DEVICE_ATTR_2 ( in0_min , S_IWUSR | S_IRUGO ,
show_in , set_in , i_min , 0 ) ;
static SENSOR_DEVICE_ATTR_2 ( in1_min , S_IWUSR | S_IRUGO ,
show_in , set_in , i_min , 1 ) ;
static SENSOR_DEVICE_ATTR_2 ( in2_min , S_IWUSR | S_IRUGO ,
show_in , set_in , i_min , 2 ) ;
static SENSOR_DEVICE_ATTR_2 ( in3_min , S_IWUSR | S_IRUGO ,
show_in , set_in , i_min , 3 ) ;
static SENSOR_DEVICE_ATTR_2 ( in4_min , S_IWUSR | S_IRUGO ,
show_in , set_in , i_min , 4 ) ;
static SENSOR_DEVICE_ATTR_2 ( in5_min , S_IWUSR | S_IRUGO ,
show_in , set_in , i_min , 5 ) ;
static SENSOR_DEVICE_ATTR_2 ( in6_min , S_IWUSR | S_IRUGO ,
show_in , set_in , i_min , 6 ) ;
static SENSOR_DEVICE_ATTR_2 ( in0_max , S_IWUSR | S_IRUGO ,
show_in , set_in , i_max , 0 ) ;
static SENSOR_DEVICE_ATTR_2 ( in1_max , S_IWUSR | S_IRUGO ,
show_in , set_in , i_max , 1 ) ;
static SENSOR_DEVICE_ATTR_2 ( in2_max , S_IWUSR | S_IRUGO ,
show_in , set_in , i_max , 2 ) ;
static SENSOR_DEVICE_ATTR_2 ( in3_max , S_IWUSR | S_IRUGO ,
show_in , set_in , i_max , 3 ) ;
static SENSOR_DEVICE_ATTR_2 ( in4_max , S_IWUSR | S_IRUGO ,
show_in , set_in , i_max , 4 ) ;
static SENSOR_DEVICE_ATTR_2 ( in5_max , S_IWUSR | S_IRUGO ,
show_in , set_in , i_max , 5 ) ;
static SENSOR_DEVICE_ATTR_2 ( in6_max , S_IWUSR | S_IRUGO ,
show_in , set_in , i_max , 6 ) ;
static SENSOR_DEVICE_ATTR_2 ( in0_input , S_IRUGO , show_in , NULL , i_input , 0 ) ;
static SENSOR_DEVICE_ATTR_2 ( in1_input , S_IRUGO , show_in , NULL , i_input , 1 ) ;
static SENSOR_DEVICE_ATTR_2 ( in2_input , S_IRUGO , show_in , NULL , i_input , 2 ) ;
static SENSOR_DEVICE_ATTR_2 ( in3_input , S_IRUGO , show_in , NULL , i_input , 3 ) ;
static SENSOR_DEVICE_ATTR_2 ( in4_input , S_IRUGO , show_in , NULL , i_input , 4 ) ;
static SENSOR_DEVICE_ATTR_2 ( in5_input , S_IRUGO , show_in , NULL , i_input , 5 ) ;
static SENSOR_DEVICE_ATTR_2 ( in6_input , S_IRUGO , show_in , NULL , i_input , 6 ) ;
2014-04-13 22:28:29 +04:00
static SENSOR_DEVICE_ATTR_2 ( fan1_min , S_IWUSR | S_IRUGO ,
show_fan , set_fan_min , f_min , 0 ) ;
static SENSOR_DEVICE_ATTR_2 ( fan2_min , S_IWUSR | S_IRUGO ,
show_fan , set_fan_min , f_min , 1 ) ;
static SENSOR_DEVICE_ATTR_2 ( fan1_input , S_IRUGO , show_fan , NULL , f_input , 0 ) ;
static SENSOR_DEVICE_ATTR_2 ( fan2_input , S_IRUGO , show_fan , NULL , f_input , 1 ) ;
2008-01-05 17:37:05 +03:00
static SENSOR_DEVICE_ATTR ( fan1_div , S_IWUSR | S_IRUGO ,
show_fan_div , set_fan_div , 0 ) ;
static SENSOR_DEVICE_ATTR ( fan2_div , S_IWUSR | S_IRUGO ,
show_fan_div , set_fan_div , 1 ) ;
2014-04-13 20:45:15 +04:00
static SENSOR_DEVICE_ATTR ( temp1_input , S_IRUGO , show_temp , NULL , t_input ) ;
static SENSOR_DEVICE_ATTR ( temp1_max , S_IWUSR | S_IRUGO , show_temp ,
set_temp , t_hot_max ) ;
static SENSOR_DEVICE_ATTR ( temp1_max_hyst , S_IWUSR | S_IRUGO , show_temp ,
set_temp , t_hot_hyst ) ;
static SENSOR_DEVICE_ATTR ( temp1_crit , S_IWUSR | S_IRUGO , show_temp ,
set_temp , t_os_max ) ;
static SENSOR_DEVICE_ATTR ( temp1_crit_hyst , S_IWUSR | S_IRUGO , show_temp ,
set_temp , t_os_hyst ) ;
2016-12-22 15:04:51 +03:00
static DEVICE_ATTR_RO ( alarms ) ;
2008-01-05 17:40:38 +03:00
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 , 4 ) ;
static SENSOR_DEVICE_ATTR ( in5_alarm , S_IRUGO , show_alarm , NULL , 5 ) ;
static SENSOR_DEVICE_ATTR ( in6_alarm , S_IRUGO , show_alarm , NULL , 6 ) ;
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 ( temp1_max_alarm , S_IRUGO , show_alarm , NULL , 8 ) ;
static SENSOR_DEVICE_ATTR ( temp1_crit_alarm , S_IRUGO , show_alarm , NULL , 13 ) ;
2005-04-17 02:20:36 +04:00
/*
* Real code
*/
2014-04-04 20:01:34 +04:00
static struct attribute * lm80_attrs [ ] = {
2008-01-05 17:37:05 +03:00
& 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_in4_min . dev_attr . attr ,
& sensor_dev_attr_in5_min . dev_attr . attr ,
& sensor_dev_attr_in6_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 ,
& sensor_dev_attr_in4_max . dev_attr . attr ,
& sensor_dev_attr_in5_max . dev_attr . attr ,
& sensor_dev_attr_in6_max . dev_attr . attr ,
& 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_in4_input . dev_attr . attr ,
& sensor_dev_attr_in5_input . dev_attr . attr ,
& sensor_dev_attr_in6_input . dev_attr . attr ,
& sensor_dev_attr_fan1_min . dev_attr . attr ,
& sensor_dev_attr_fan2_min . dev_attr . attr ,
& sensor_dev_attr_fan1_input . dev_attr . attr ,
& sensor_dev_attr_fan2_input . dev_attr . attr ,
& sensor_dev_attr_fan1_div . dev_attr . attr ,
& sensor_dev_attr_fan2_div . dev_attr . attr ,
2014-04-13 20:45:15 +04:00
& sensor_dev_attr_temp1_input . dev_attr . attr ,
& sensor_dev_attr_temp1_max . dev_attr . attr ,
& sensor_dev_attr_temp1_max_hyst . dev_attr . attr ,
& sensor_dev_attr_temp1_crit . dev_attr . attr ,
& sensor_dev_attr_temp1_crit_hyst . dev_attr . attr ,
2006-09-24 23:14:35 +04:00
& dev_attr_alarms . attr ,
2008-01-05 17:40:38 +03: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 ,
& sensor_dev_attr_in4_alarm . dev_attr . attr ,
& sensor_dev_attr_in5_alarm . dev_attr . attr ,
& sensor_dev_attr_in6_alarm . dev_attr . attr ,
& sensor_dev_attr_fan1_alarm . dev_attr . attr ,
& sensor_dev_attr_fan2_alarm . dev_attr . attr ,
& sensor_dev_attr_temp1_max_alarm . dev_attr . attr ,
& sensor_dev_attr_temp1_crit_alarm . dev_attr . attr ,
2006-09-24 23:14:35 +04:00
NULL
} ;
2014-04-04 20:01:34 +04:00
ATTRIBUTE_GROUPS ( lm80 ) ;
2006-09-24 23:14:35 +04:00
2008-07-16 21:30:14 +04:00
/* Return 0 if detection is successful, -ENODEV otherwise */
2009-12-14 23:17:23 +03:00
static int lm80_detect ( struct i2c_client * client , struct i2c_board_info * info )
2005-04-17 02:20:36 +04:00
{
2008-07-16 21:30:14 +04:00
struct i2c_adapter * adapter = client - > adapter ;
2012-01-31 18:27:11 +04:00
int i , cur , man_id , dev_id ;
const char * name = NULL ;
2005-04-17 02:20:36 +04:00
if ( ! i2c_check_functionality ( adapter , I2C_FUNC_SMBUS_BYTE_DATA ) )
2008-07-16 21:30:14 +04:00
return - ENODEV ;
2005-04-17 02:20:36 +04:00
2012-01-31 18:27:11 +04:00
/* First check for unused bits, common to both chip types */
if ( ( lm80_read_value ( client , LM80_REG_ALARM2 ) & 0xc0 )
| | ( lm80_read_value ( client , LM80_REG_CONFIG ) & 0x80 ) )
2008-07-16 21:30:14 +04:00
return - ENODEV ;
2012-01-31 18:27:11 +04:00
/*
* The LM96080 has manufacturer and stepping / die rev registers so we
* can just check that . The LM80 does not have such registers so we
* have to use a more expensive trick .
*/
man_id = lm80_read_value ( client , LM96080_REG_MAN_ID ) ;
dev_id = lm80_read_value ( client , LM96080_REG_DEV_ID ) ;
if ( man_id = = 0x01 & & dev_id = = 0x08 ) {
/* Check more unused bits for confirmation */
if ( lm80_read_value ( client , LM96080_REG_CONV_RATE ) & 0xfe )
2012-01-04 23:58:53 +04:00
return - ENODEV ;
2012-01-31 18:27:11 +04:00
name = " lm96080 " ;
} else {
/* Check 6-bit addressing */
for ( i = 0x2a ; i < = 0x3d ; i + + ) {
cur = i2c_smbus_read_byte_data ( client , i ) ;
if ( ( i2c_smbus_read_byte_data ( client , i + 0x40 ) ! = cur )
| | ( i2c_smbus_read_byte_data ( client , i + 0x80 ) ! = cur )
| | ( i2c_smbus_read_byte_data ( client , i + 0xc0 ) ! = cur ) )
return - ENODEV ;
}
name = " lm80 " ;
2005-04-17 02:20:36 +04:00
}
2012-01-31 18:27:11 +04:00
strlcpy ( info - > type , name , I2C_NAME_SIZE ) ;
2005-04-17 02:20:36 +04:00
2008-07-16 21:30:14 +04:00
return 0 ;
}
2005-04-17 02:20:36 +04:00
2008-07-16 21:30:14 +04:00
static int lm80_probe ( struct i2c_client * client ,
const struct i2c_device_id * id )
{
2014-04-04 20:01:34 +04:00
struct device * dev = & client - > dev ;
struct device * hwmon_dev ;
2008-07-16 21:30:14 +04:00
struct lm80_data * data ;
2014-04-04 20:01:34 +04:00
data = devm_kzalloc ( dev , sizeof ( struct lm80_data ) , GFP_KERNEL ) ;
2012-06-02 20:58:09 +04:00
if ( ! data )
return - ENOMEM ;
2008-07-16 21:30:14 +04:00
2014-04-04 20:01:34 +04:00
data - > client = client ;
2008-07-16 21:30:14 +04:00
mutex_init ( & data - > update_lock ) ;
2005-04-17 02:20:36 +04:00
/* Initialize the LM80 chip */
2008-01-05 17:35:09 +03:00
lm80_init_client ( client ) ;
2005-04-17 02:20:36 +04:00
/* A few vars need to be filled upon startup */
2014-04-13 22:28:29 +04:00
data - > fan [ f_min ] [ 0 ] = lm80_read_value ( client , LM80_REG_FAN_MIN ( 1 ) ) ;
data - > fan [ f_min ] [ 1 ] = lm80_read_value ( client , LM80_REG_FAN_MIN ( 2 ) ) ;
2005-04-17 02:20:36 +04:00
2014-04-04 20:01:34 +04:00
hwmon_dev = devm_hwmon_device_register_with_groups ( dev , client - > name ,
data , lm80_groups ) ;
2005-07-16 05:39:18 +04:00
2014-04-04 20:01:34 +04:00
return PTR_ERR_OR_ZERO ( hwmon_dev ) ;
2005-04-17 02:20:36 +04:00
}
2014-04-13 22:56:15 +04:00
/*
* Driver data ( common to all clients )
*/
2005-04-17 02:20:36 +04:00
2014-04-13 22:56:15 +04:00
static const struct i2c_device_id lm80_id [ ] = {
{ " lm80 " , 0 } ,
{ " lm96080 " , 1 } ,
{ }
} ;
MODULE_DEVICE_TABLE ( i2c , lm80_id ) ;
2005-04-17 02:20:36 +04:00
2014-04-13 22:56:15 +04:00
static struct i2c_driver lm80_driver = {
. class = I2C_CLASS_HWMON ,
. driver = {
. name = " lm80 " ,
} ,
. probe = lm80_probe ,
. id_table = lm80_id ,
. detect = lm80_detect ,
. address_list = normal_i2c ,
} ;
2005-04-17 02:20:36 +04:00
2012-01-20 11:38:18 +04:00
module_i2c_driver ( lm80_driver ) ;
2005-04-17 02:20:36 +04:00
MODULE_AUTHOR ( " Frodo Looijaard <frodol@dds.nl> and "
" Philip Edelbrock <phil@netroedge.com> " ) ;
MODULE_DESCRIPTION ( " LM80 driver " ) ;
MODULE_LICENSE ( " GPL " ) ;