2009-04-01 02:24:27 +04:00
/*
2010-11-18 18:23:00 +03:00
* Copyright ( C ) 2008 , 2010 Davide Rizzo < elpa . rizzo @ gmail . com >
2009-04-01 02:24:27 +04:00
*
2010-11-18 18:23:00 +03:00
* The LM95241 is a sensor chip made by National Semiconductors .
* It reports up to three temperatures ( its own plus up to two external ones ) .
* Complete datasheet can be obtained from National ' s website at :
2009-04-01 02:24:27 +04:00
* http : //www.national.com/ds.cgi/LM/LM95241.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>
# include <linux/hwmon.h>
# include <linux/hwmon-sysfs.h>
# include <linux/err.h>
# include <linux/mutex.h>
# include <linux/sysfs.h>
2010-11-18 18:23:00 +03:00
# define DEVNAME "lm95241"
2009-04-01 02:24:27 +04:00
static const unsigned short normal_i2c [ ] = {
2010-11-18 18:23:00 +03:00
0x19 , 0x2a , 0x2b , I2C_CLIENT_END } ;
2009-04-01 02:24:27 +04:00
/* LM95241 registers */
# define LM95241_REG_R_MAN_ID 0xFE
# define LM95241_REG_R_CHIP_ID 0xFF
# define LM95241_REG_R_STATUS 0x02
# define LM95241_REG_RW_CONFIG 0x03
# define LM95241_REG_RW_REM_FILTER 0x06
# define LM95241_REG_RW_TRUTHERM 0x07
2010-11-18 18:23:00 +03:00
# define LM95241_REG_W_ONE_SHOT 0x0F
2009-04-01 02:24:27 +04:00
# define LM95241_REG_R_LOCAL_TEMPH 0x10
# define LM95241_REG_R_REMOTE1_TEMPH 0x11
# define LM95241_REG_R_REMOTE2_TEMPH 0x12
# define LM95241_REG_R_LOCAL_TEMPL 0x20
# define LM95241_REG_R_REMOTE1_TEMPL 0x21
# define LM95241_REG_R_REMOTE2_TEMPL 0x22
# define LM95241_REG_RW_REMOTE_MODEL 0x30
/* LM95241 specific bitfields */
# define CFG_STOP 0x40
# define CFG_CR0076 0x00
# define CFG_CR0182 0x10
# define CFG_CR1000 0x20
# define CFG_CR2700 0x30
# define R1MS_SHIFT 0
# define R2MS_SHIFT 2
# define R1MS_MASK (0x01 << (R1MS_SHIFT))
# define R2MS_MASK (0x01 << (R2MS_SHIFT))
# define R1DF_SHIFT 1
# define R2DF_SHIFT 2
# define R1DF_MASK (0x01 << (R1DF_SHIFT))
# define R2DF_MASK (0x01 << (R2DF_SHIFT))
# define R1FE_MASK 0x01
# define R2FE_MASK 0x05
# define TT1_SHIFT 0
# define TT2_SHIFT 4
# define TT_OFF 0
# define TT_ON 1
# define TT_MASK 7
2011-07-06 00:31:48 +04:00
# define NATSEMI_MAN_ID 0x01
# define LM95231_CHIP_ID 0xA1
# define LM95241_CHIP_ID 0xA4
2009-04-01 02:24:27 +04:00
2010-11-18 18:23:00 +03:00
static const u8 lm95241_reg_address [ ] = {
LM95241_REG_R_LOCAL_TEMPH ,
LM95241_REG_R_LOCAL_TEMPL ,
LM95241_REG_R_REMOTE1_TEMPH ,
LM95241_REG_R_REMOTE1_TEMPL ,
LM95241_REG_R_REMOTE2_TEMPH ,
LM95241_REG_R_REMOTE2_TEMPL
} ;
2009-04-01 02:24:27 +04:00
/* Client data (each client gets its own) */
struct lm95241_data {
2014-01-20 21:25:50 +04:00
struct i2c_client * client ;
2009-04-01 02:24:27 +04:00
struct mutex update_lock ;
2010-11-18 18:23:00 +03:00
unsigned long last_updated , interval ; /* in jiffies */
char valid ; /* zero until following fields are valid */
2009-04-01 02:24:27 +04:00
/* registers values */
2010-11-18 18:23:00 +03:00
u8 temp [ ARRAY_SIZE ( lm95241_reg_address ) ] ;
2009-04-01 02:24:27 +04:00
u8 config , model , trutherm ;
} ;
2010-11-18 18:23:00 +03:00
/* Conversions */
2011-06-30 13:09:37 +04:00
static int temp_from_reg_signed ( u8 val_h , u8 val_l )
2010-11-18 18:23:00 +03:00
{
2011-06-30 13:09:37 +04:00
s16 val_hl = ( val_h < < 8 ) | val_l ;
return val_hl * 1000 / 256 ;
}
static int temp_from_reg_unsigned ( u8 val_h , u8 val_l )
{
u16 val_hl = ( val_h < < 8 ) | val_l ;
return val_hl * 1000 / 256 ;
2010-11-18 18:23:00 +03:00
}
static struct lm95241_data * lm95241_update_device ( struct device * dev )
{
2014-01-20 21:25:50 +04:00
struct lm95241_data * data = dev_get_drvdata ( dev ) ;
struct i2c_client * client = data - > client ;
2010-11-18 18:23:00 +03:00
mutex_lock ( & data - > update_lock ) ;
if ( time_after ( jiffies , data - > last_updated + data - > interval ) | |
! data - > valid ) {
int i ;
2014-01-20 21:25:50 +04:00
dev_dbg ( dev , " Updating lm95241 data. \n " ) ;
2010-11-18 18:23:00 +03:00
for ( i = 0 ; i < ARRAY_SIZE ( lm95241_reg_address ) ; i + + )
data - > temp [ i ]
= i2c_smbus_read_byte_data ( client ,
lm95241_reg_address [ i ] ) ;
data - > last_updated = jiffies ;
data - > valid = 1 ;
}
mutex_unlock ( & data - > update_lock ) ;
return data ;
}
2009-04-01 02:24:27 +04:00
/* Sysfs stuff */
2010-11-18 18:23:00 +03:00
static ssize_t show_input ( struct device * dev , struct device_attribute * attr ,
char * buf )
{
struct lm95241_data * data = lm95241_update_device ( dev ) ;
2011-06-30 13:09:37 +04:00
int index = to_sensor_dev_attr ( attr ) - > index ;
2010-11-18 18:23:00 +03:00
return snprintf ( buf , PAGE_SIZE - 1 , " %d \n " ,
2011-06-30 13:09:37 +04:00
index = = 0 | | ( data - > config & ( 1 < < ( index / 2 ) ) ) ?
temp_from_reg_signed ( data - > temp [ index ] , data - > temp [ index + 1 ] ) :
temp_from_reg_unsigned ( data - > temp [ index ] ,
data - > temp [ index + 1 ] ) ) ;
2009-04-01 02:24:27 +04:00
}
2010-11-18 18:23:00 +03:00
static ssize_t show_type ( struct device * dev , struct device_attribute * attr ,
2009-04-01 02:24:27 +04:00
char * buf )
{
2014-01-20 21:25:50 +04:00
struct lm95241_data * data = dev_get_drvdata ( dev ) ;
2009-04-01 02:24:27 +04:00
2010-11-18 18:23:00 +03:00
return snprintf ( buf , PAGE_SIZE - 1 ,
data - > model & to_sensor_dev_attr ( attr ) - > index ? " 1 \n " : " 2 \n " ) ;
2009-04-01 02:24:27 +04:00
}
2010-11-18 18:23:00 +03:00
static ssize_t set_type ( struct device * dev , struct device_attribute * attr ,
2009-04-01 02:24:27 +04:00
const char * buf , size_t count )
{
2014-01-20 21:25:50 +04:00
struct lm95241_data * data = dev_get_drvdata ( dev ) ;
struct i2c_client * client = data - > client ;
2010-11-15 23:38:56 +03:00
unsigned long val ;
2010-11-18 18:23:00 +03:00
int shift ;
u8 mask = to_sensor_dev_attr ( attr ) - > index ;
2009-04-01 02:24:27 +04:00
2012-01-04 23:58:52 +04:00
if ( kstrtoul ( buf , 10 , & val ) < 0 )
2010-11-15 23:38:56 +03:00
return - EINVAL ;
2010-11-18 18:23:00 +03:00
if ( val ! = 1 & & val ! = 2 )
return - EINVAL ;
2010-11-15 23:38:56 +03:00
2010-11-18 18:23:00 +03:00
shift = mask = = R1MS_MASK ? TT1_SHIFT : TT2_SHIFT ;
mutex_lock ( & data - > update_lock ) ;
data - > trutherm & = ~ ( TT_MASK < < shift ) ;
if ( val = = 1 ) {
data - > model | = mask ;
data - > trutherm | = ( TT_ON < < shift ) ;
} else {
data - > model & = ~ mask ;
data - > trutherm | = ( TT_OFF < < shift ) ;
}
data - > valid = 0 ;
i2c_smbus_write_byte_data ( client , LM95241_REG_RW_REMOTE_MODEL ,
data - > model ) ;
i2c_smbus_write_byte_data ( client , LM95241_REG_RW_TRUTHERM ,
data - > trutherm ) ;
mutex_unlock ( & data - > update_lock ) ;
2009-04-01 02:24:27 +04:00
return count ;
}
2010-11-18 18:23:00 +03:00
static ssize_t show_min ( struct device * dev , struct device_attribute * attr ,
char * buf )
{
2014-01-20 21:25:50 +04:00
struct lm95241_data * data = dev_get_drvdata ( dev ) ;
2010-11-18 18:23:00 +03:00
return snprintf ( buf , PAGE_SIZE - 1 ,
data - > config & to_sensor_dev_attr ( attr ) - > index ?
" -127000 \n " : " 0 \n " ) ;
2009-04-01 02:24:27 +04:00
}
2010-11-18 18:23:00 +03:00
static ssize_t set_min ( struct device * dev , struct device_attribute * attr ,
const char * buf , size_t count )
{
2014-01-20 21:25:50 +04:00
struct lm95241_data * data = dev_get_drvdata ( dev ) ;
2010-11-18 18:23:00 +03:00
long val ;
2012-01-04 23:58:52 +04:00
if ( kstrtol ( buf , 10 , & val ) < 0 )
2010-11-18 18:23:00 +03:00
return - EINVAL ;
if ( val < - 128000 )
return - EINVAL ;
mutex_lock ( & data - > update_lock ) ;
if ( val < 0 )
data - > config | = to_sensor_dev_attr ( attr ) - > index ;
else
data - > config & = ~ to_sensor_dev_attr ( attr ) - > index ;
data - > valid = 0 ;
2014-01-20 21:25:50 +04:00
i2c_smbus_write_byte_data ( data - > client , LM95241_REG_RW_CONFIG ,
data - > config ) ;
2010-11-18 18:23:00 +03:00
mutex_unlock ( & data - > update_lock ) ;
return count ;
2009-04-01 02:24:27 +04:00
}
2010-11-18 18:23:00 +03:00
static ssize_t show_max ( struct device * dev , struct device_attribute * attr ,
char * buf )
{
2014-01-20 21:25:50 +04:00
struct lm95241_data * data = dev_get_drvdata ( dev ) ;
2010-11-18 18:23:00 +03:00
return snprintf ( buf , PAGE_SIZE - 1 ,
data - > config & to_sensor_dev_attr ( attr ) - > index ?
" 127000 \n " : " 255000 \n " ) ;
2009-04-01 02:24:27 +04:00
}
2010-11-18 18:23:00 +03:00
static ssize_t set_max ( struct device * dev , struct device_attribute * attr ,
const char * buf , size_t count )
{
2014-01-20 21:25:50 +04:00
struct lm95241_data * data = dev_get_drvdata ( dev ) ;
2010-11-18 18:23:00 +03:00
long val ;
2012-01-04 23:58:52 +04:00
if ( kstrtol ( buf , 10 , & val ) < 0 )
2010-11-18 18:23:00 +03:00
return - EINVAL ;
if ( val > = 256000 )
return - EINVAL ;
mutex_lock ( & data - > update_lock ) ;
if ( val < = 127000 )
data - > config | = to_sensor_dev_attr ( attr ) - > index ;
else
data - > config & = ~ to_sensor_dev_attr ( attr ) - > index ;
data - > valid = 0 ;
2014-01-20 21:25:50 +04:00
i2c_smbus_write_byte_data ( data - > client , LM95241_REG_RW_CONFIG ,
data - > config ) ;
2010-11-18 18:23:00 +03:00
mutex_unlock ( & data - > update_lock ) ;
return count ;
2009-04-01 02:24:27 +04:00
}
2010-11-18 18:23:00 +03:00
static ssize_t show_interval ( struct device * dev , struct device_attribute * attr ,
char * buf )
{
struct lm95241_data * data = lm95241_update_device ( dev ) ;
return snprintf ( buf , PAGE_SIZE - 1 , " %lu \n " , 1000 * data - > interval
/ HZ ) ;
2009-04-01 02:24:27 +04:00
}
2010-11-18 18:23:00 +03:00
static ssize_t set_interval ( struct device * dev , struct device_attribute * attr ,
const char * buf , size_t count )
{
2014-01-20 21:25:50 +04:00
struct lm95241_data * data = dev_get_drvdata ( dev ) ;
2010-11-18 18:23:00 +03:00
unsigned long val ;
2012-01-04 23:58:52 +04:00
if ( kstrtoul ( buf , 10 , & val ) < 0 )
2010-11-18 18:23:00 +03:00
return - EINVAL ;
data - > interval = val * HZ / 1000 ;
return count ;
2009-04-01 02:24:27 +04:00
}
2010-11-18 18:23:00 +03:00
static SENSOR_DEVICE_ATTR ( temp1_input , S_IRUGO , show_input , NULL , 0 ) ;
static SENSOR_DEVICE_ATTR ( temp2_input , S_IRUGO , show_input , NULL , 2 ) ;
static SENSOR_DEVICE_ATTR ( temp3_input , S_IRUGO , show_input , NULL , 4 ) ;
static SENSOR_DEVICE_ATTR ( temp2_type , S_IWUSR | S_IRUGO , show_type , set_type ,
R1MS_MASK ) ;
static SENSOR_DEVICE_ATTR ( temp3_type , S_IWUSR | S_IRUGO , show_type , set_type ,
R2MS_MASK ) ;
static SENSOR_DEVICE_ATTR ( temp2_min , S_IWUSR | S_IRUGO , show_min , set_min ,
R1DF_MASK ) ;
static SENSOR_DEVICE_ATTR ( temp3_min , S_IWUSR | S_IRUGO , show_min , set_min ,
R2DF_MASK ) ;
static SENSOR_DEVICE_ATTR ( temp2_max , S_IWUSR | S_IRUGO , show_max , set_max ,
R1DF_MASK ) ;
static SENSOR_DEVICE_ATTR ( temp3_max , S_IWUSR | S_IRUGO , show_max , set_max ,
R2DF_MASK ) ;
2010-09-17 19:24:15 +04:00
static DEVICE_ATTR ( update_interval , S_IWUSR | S_IRUGO , show_interval ,
set_interval ) ;
2009-04-01 02:24:27 +04:00
2014-01-20 21:25:50 +04:00
static struct attribute * lm95241_attrs [ ] = {
2010-11-18 18:23:00 +03: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_temp2_type . dev_attr . attr ,
& sensor_dev_attr_temp3_type . dev_attr . attr ,
& sensor_dev_attr_temp2_min . dev_attr . attr ,
& sensor_dev_attr_temp3_min . dev_attr . attr ,
& sensor_dev_attr_temp2_max . dev_attr . attr ,
& sensor_dev_attr_temp3_max . dev_attr . attr ,
2010-09-17 19:24:15 +04:00
& dev_attr_update_interval . attr ,
2009-04-01 02:24:27 +04:00
NULL
} ;
2014-01-20 21:25:50 +04:00
ATTRIBUTE_GROUPS ( lm95241 ) ;
2009-04-01 02:24:27 +04:00
2009-04-07 17:32:59 +04:00
/* Return 0 if detection is successful, -ENODEV otherwise */
2009-12-14 23:17:23 +03:00
static int lm95241_detect ( struct i2c_client * new_client ,
2009-04-07 17:32:59 +04:00
struct i2c_board_info * info )
2009-04-01 02:24:27 +04:00
{
2009-04-07 17:32:59 +04:00
struct i2c_adapter * adapter = new_client - > adapter ;
2009-12-09 22:35:57 +03:00
const char * name ;
2011-07-06 00:31:48 +04:00
int mfg_id , chip_id ;
2009-04-01 02:24:27 +04:00
if ( ! i2c_check_functionality ( adapter , I2C_FUNC_SMBUS_BYTE_DATA ) )
2009-04-07 17:32:59 +04:00
return - ENODEV ;
2009-04-01 02:24:27 +04:00
2011-07-06 00:31:48 +04:00
mfg_id = i2c_smbus_read_byte_data ( new_client , LM95241_REG_R_MAN_ID ) ;
if ( mfg_id ! = NATSEMI_MAN_ID )
return - ENODEV ;
chip_id = i2c_smbus_read_byte_data ( new_client , LM95241_REG_R_CHIP_ID ) ;
switch ( chip_id ) {
case LM95231_CHIP_ID :
name = " lm95231 " ;
break ;
case LM95241_CHIP_ID :
name = " lm95241 " ;
break ;
default :
2009-12-09 22:35:57 +03:00
return - ENODEV ;
2009-04-01 02:24:27 +04:00
}
2009-04-07 17:32:59 +04:00
/* Fill the i2c board info */
strlcpy ( info - > type , name , I2C_NAME_SIZE ) ;
return 0 ;
}
2009-04-01 02:24:27 +04:00
2014-01-20 21:25:50 +04:00
static void lm95241_init_client ( struct i2c_client * client ,
struct lm95241_data * data )
2010-11-18 18:23:00 +03:00
{
data - > interval = HZ ; /* 1 sec default */
data - > config = CFG_CR0076 ;
data - > trutherm = ( TT_OFF < < TT1_SHIFT ) | ( TT_OFF < < TT2_SHIFT ) ;
i2c_smbus_write_byte_data ( client , LM95241_REG_RW_CONFIG , data - > config ) ;
i2c_smbus_write_byte_data ( client , LM95241_REG_RW_REM_FILTER ,
R1FE_MASK | R2FE_MASK ) ;
i2c_smbus_write_byte_data ( client , LM95241_REG_RW_TRUTHERM ,
data - > trutherm ) ;
i2c_smbus_write_byte_data ( client , LM95241_REG_RW_REMOTE_MODEL ,
data - > model ) ;
}
2014-01-20 21:25:50 +04:00
static int lm95241_probe ( struct i2c_client * client ,
2009-04-07 17:32:59 +04:00
const struct i2c_device_id * id )
{
2014-01-20 21:25:50 +04:00
struct device * dev = & client - > dev ;
2009-04-07 17:32:59 +04:00
struct lm95241_data * data ;
2014-01-20 21:25:50 +04:00
struct device * hwmon_dev ;
2009-04-01 02:24:27 +04:00
2014-01-20 21:25:50 +04:00
data = devm_kzalloc ( dev , sizeof ( struct lm95241_data ) , GFP_KERNEL ) ;
2012-06-02 20:58:11 +04:00
if ( ! data )
return - ENOMEM ;
2009-04-07 17:32:59 +04:00
2014-01-20 21:25:50 +04:00
data - > client = client ;
2009-04-07 17:32:59 +04:00
mutex_init ( & data - > update_lock ) ;
2009-04-01 02:24:27 +04:00
/* Initialize the LM95241 chip */
2014-01-20 21:25:50 +04:00
lm95241_init_client ( client , data ) ;
2009-04-01 02:24:27 +04:00
2014-01-20 21:25:50 +04:00
hwmon_dev = devm_hwmon_device_register_with_groups ( dev , client - > name ,
data ,
lm95241_groups ) ;
return PTR_ERR_OR_ZERO ( hwmon_dev ) ;
2009-04-01 02:24:27 +04:00
}
2009-04-07 17:32:59 +04:00
/* Driver data (common to all clients) */
static const struct i2c_device_id lm95241_id [ ] = {
2011-07-06 00:31:48 +04:00
{ " lm95231 " , 0 } ,
{ " lm95241 " , 0 } ,
2009-04-07 17:32:59 +04:00
{ }
} ;
MODULE_DEVICE_TABLE ( i2c , lm95241_id ) ;
static struct i2c_driver lm95241_driver = {
. class = I2C_CLASS_HWMON ,
. driver = {
2010-11-18 18:23:00 +03:00
. name = DEVNAME ,
2009-04-07 17:32:59 +04:00
} ,
. probe = lm95241_probe ,
. id_table = lm95241_id ,
. detect = lm95241_detect ,
2009-12-14 23:17:25 +03:00
. address_list = normal_i2c ,
2009-04-07 17:32:59 +04:00
} ;
2012-01-20 11:38:18 +04:00
module_i2c_driver ( lm95241_driver ) ;
2009-04-01 02:24:27 +04:00
2010-11-18 18:23:00 +03:00
MODULE_AUTHOR ( " Davide Rizzo <elpa.rizzo@gmail.com> " ) ;
2009-04-01 02:24:27 +04:00
MODULE_DESCRIPTION ( " LM95241 sensor driver " ) ;
MODULE_LICENSE ( " GPL " ) ;