2011-06-28 19:11:23 +04:00
/*
* Copyright ( C ) 2011 Alexander Stein < alexander . stein @ systec - electronic . com >
*
2014-04-22 19:48:57 +04:00
* The LM95245 is a sensor chip made by TI / National Semiconductor .
2011-06-28 19:11:23 +04:00
* It reports up to two temperatures ( its own plus an external one ) .
*
* This driver is based on lm95241 . c
*
* 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>
static const unsigned short normal_i2c [ ] = {
0x18 , 0x19 , 0x29 , 0x4c , 0x4d , I2C_CLIENT_END } ;
/* LM95245 registers */
/* general registers */
# define LM95245_REG_RW_CONFIG1 0x03
# define LM95245_REG_RW_CONVERS_RATE 0x04
# define LM95245_REG_W_ONE_SHOT 0x0F
/* diode configuration */
# define LM95245_REG_RW_CONFIG2 0xBF
# define LM95245_REG_RW_REMOTE_OFFH 0x11
# define LM95245_REG_RW_REMOTE_OFFL 0x12
/* status registers */
# define LM95245_REG_R_STATUS1 0x02
# define LM95245_REG_R_STATUS2 0x33
/* limit registers */
# define LM95245_REG_RW_REMOTE_OS_LIMIT 0x07
# define LM95245_REG_RW_LOCAL_OS_TCRIT_LIMIT 0x20
# define LM95245_REG_RW_REMOTE_TCRIT_LIMIT 0x19
# define LM95245_REG_RW_COMMON_HYSTERESIS 0x21
/* temperature signed */
# define LM95245_REG_R_LOCAL_TEMPH_S 0x00
# define LM95245_REG_R_LOCAL_TEMPL_S 0x30
# define LM95245_REG_R_REMOTE_TEMPH_S 0x01
# define LM95245_REG_R_REMOTE_TEMPL_S 0x10
/* temperature unsigned */
# define LM95245_REG_R_REMOTE_TEMPH_U 0x31
# define LM95245_REG_R_REMOTE_TEMPL_U 0x32
/* id registers */
# define LM95245_REG_R_MAN_ID 0xFE
# define LM95245_REG_R_CHIP_ID 0xFF
/* LM95245 specific bitfields */
# define CFG_STOP 0x40
# define CFG_REMOTE_TCRIT_MASK 0x10
# define CFG_REMOTE_OS_MASK 0x08
# define CFG_LOCAL_TCRIT_MASK 0x04
# define CFG_LOCAL_OS_MASK 0x02
# define CFG2_OS_A0 0x40
# define CFG2_DIODE_FAULT_OS 0x20
# define CFG2_DIODE_FAULT_TCRIT 0x10
# define CFG2_REMOTE_TT 0x08
# define CFG2_REMOTE_FILTER_DIS 0x00
# define CFG2_REMOTE_FILTER_EN 0x06
/* conversation rate in ms */
# define RATE_CR0063 0x00
# define RATE_CR0364 0x01
# define RATE_CR1000 0x02
# define RATE_CR2500 0x03
# define STATUS1_DIODE_FAULT 0x04
# define STATUS1_RTCRIT 0x02
# define STATUS1_LOC 0x01
# define MANUFACTURER_ID 0x01
2014-04-22 19:48:57 +04:00
# define LM95235_REVISION 0xB1
# define LM95245_REVISION 0xB3
2011-06-28 19:11:23 +04:00
static const u8 lm95245_reg_address [ ] = {
LM95245_REG_R_LOCAL_TEMPH_S ,
LM95245_REG_R_LOCAL_TEMPL_S ,
LM95245_REG_R_REMOTE_TEMPH_S ,
LM95245_REG_R_REMOTE_TEMPL_S ,
LM95245_REG_R_REMOTE_TEMPH_U ,
LM95245_REG_R_REMOTE_TEMPL_U ,
LM95245_REG_RW_LOCAL_OS_TCRIT_LIMIT ,
LM95245_REG_RW_REMOTE_TCRIT_LIMIT ,
LM95245_REG_RW_COMMON_HYSTERESIS ,
LM95245_REG_R_STATUS1 ,
} ;
/* Client data (each client gets its own) */
struct lm95245_data {
2014-01-20 21:17:16 +04:00
struct i2c_client * client ;
2011-06-28 19:11:23 +04:00
struct mutex update_lock ;
unsigned long last_updated ; /* in jiffies */
unsigned long interval ; /* in msecs */
bool valid ; /* zero until following fields are valid */
/* registers values */
u8 regs [ ARRAY_SIZE ( lm95245_reg_address ) ] ;
u8 config1 , config2 ;
} ;
/* Conversions */
static int temp_from_reg_unsigned ( u8 val_h , u8 val_l )
{
return val_h * 1000 + val_l * 1000 / 256 ;
}
static int temp_from_reg_signed ( u8 val_h , u8 val_l )
{
if ( val_h & 0x80 )
return ( val_h - 0x100 ) * 1000 ;
return temp_from_reg_unsigned ( val_h , val_l ) ;
}
static struct lm95245_data * lm95245_update_device ( struct device * dev )
{
2014-01-20 21:17:16 +04:00
struct lm95245_data * data = dev_get_drvdata ( dev ) ;
struct i2c_client * client = data - > client ;
2011-06-28 19:11:23 +04:00
mutex_lock ( & data - > update_lock ) ;
if ( time_after ( jiffies , data - > last_updated
+ msecs_to_jiffies ( data - > interval ) ) | | ! data - > valid ) {
int i ;
for ( i = 0 ; i < ARRAY_SIZE ( lm95245_reg_address ) ; i + + )
data - > regs [ i ]
= i2c_smbus_read_byte_data ( client ,
lm95245_reg_address [ i ] ) ;
data - > last_updated = jiffies ;
data - > valid = 1 ;
}
mutex_unlock ( & data - > update_lock ) ;
return data ;
}
static unsigned long lm95245_read_conversion_rate ( struct i2c_client * client )
{
int rate ;
unsigned long interval ;
rate = i2c_smbus_read_byte_data ( client , LM95245_REG_RW_CONVERS_RATE ) ;
switch ( rate ) {
case RATE_CR0063 :
interval = 63 ;
break ;
case RATE_CR0364 :
interval = 364 ;
break ;
case RATE_CR1000 :
interval = 1000 ;
break ;
case RATE_CR2500 :
default :
interval = 2500 ;
break ;
}
return interval ;
}
static unsigned long lm95245_set_conversion_rate ( struct i2c_client * client ,
unsigned long interval )
{
int rate ;
if ( interval < = 63 ) {
interval = 63 ;
rate = RATE_CR0063 ;
} else if ( interval < = 364 ) {
interval = 364 ;
rate = RATE_CR0364 ;
} else if ( interval < = 1000 ) {
interval = 1000 ;
rate = RATE_CR1000 ;
} else {
interval = 2500 ;
rate = RATE_CR2500 ;
}
i2c_smbus_write_byte_data ( client , LM95245_REG_RW_CONVERS_RATE , rate ) ;
return interval ;
}
/* Sysfs stuff */
static ssize_t show_input ( struct device * dev , struct device_attribute * attr ,
char * buf )
{
struct lm95245_data * data = lm95245_update_device ( dev ) ;
int temp ;
int index = to_sensor_dev_attr ( attr ) - > index ;
/*
* Index 0 ( Local temp ) is always signed
* Index 2 ( Remote temp ) has both signed and unsigned data
* use signed calculation for remote if signed bit is set
*/
if ( index = = 0 | | data - > regs [ index ] & 0x80 )
temp = temp_from_reg_signed ( data - > regs [ index ] ,
data - > regs [ index + 1 ] ) ;
else
temp = temp_from_reg_unsigned ( data - > regs [ index + 2 ] ,
data - > regs [ index + 3 ] ) ;
return snprintf ( buf , PAGE_SIZE - 1 , " %d \n " , temp ) ;
}
static ssize_t show_limit ( struct device * dev , struct device_attribute * attr ,
char * buf )
{
struct lm95245_data * data = lm95245_update_device ( dev ) ;
int index = to_sensor_dev_attr ( attr ) - > index ;
return snprintf ( buf , PAGE_SIZE - 1 , " %d \n " ,
data - > regs [ index ] * 1000 ) ;
}
static ssize_t set_limit ( struct device * dev , struct device_attribute * attr ,
const char * buf , size_t count )
{
2014-01-20 21:17:16 +04:00
struct lm95245_data * data = dev_get_drvdata ( dev ) ;
2011-06-28 19:11:23 +04:00
int index = to_sensor_dev_attr ( attr ) - > index ;
2014-01-20 21:17:16 +04:00
struct i2c_client * client = data - > client ;
2011-06-28 19:11:23 +04:00
unsigned long val ;
2012-01-04 23:58:52 +04:00
if ( kstrtoul ( buf , 10 , & val ) < 0 )
2011-06-28 19:11:23 +04:00
return - EINVAL ;
val / = 1000 ;
2013-01-09 20:09:34 +04:00
val = clamp_val ( val , 0 , ( index = = 6 ? 127 : 255 ) ) ;
2011-06-28 19:11:23 +04:00
mutex_lock ( & data - > update_lock ) ;
data - > valid = 0 ;
i2c_smbus_write_byte_data ( client , lm95245_reg_address [ index ] , val ) ;
mutex_unlock ( & data - > update_lock ) ;
return count ;
}
2014-02-03 06:29:28 +04:00
static ssize_t show_crit_hyst ( struct device * dev , struct device_attribute * attr ,
char * buf )
{
struct lm95245_data * data = lm95245_update_device ( dev ) ;
int index = to_sensor_dev_attr ( attr ) - > index ;
int hyst = data - > regs [ index ] - data - > regs [ 8 ] ;
return snprintf ( buf , PAGE_SIZE - 1 , " %d \n " , hyst * 1000 ) ;
}
2011-06-28 19:11:23 +04:00
static ssize_t set_crit_hyst ( struct device * dev , struct device_attribute * attr ,
const char * buf , size_t count )
{
2014-01-20 21:17:16 +04:00
struct lm95245_data * data = dev_get_drvdata ( dev ) ;
2014-02-03 06:29:28 +04:00
int index = to_sensor_dev_attr ( attr ) - > index ;
2014-01-20 21:17:16 +04:00
struct i2c_client * client = data - > client ;
2011-06-28 19:11:23 +04:00
unsigned long val ;
2014-02-03 06:29:28 +04:00
int hyst , limit ;
2011-06-28 19:11:23 +04:00
2012-01-04 23:58:52 +04:00
if ( kstrtoul ( buf , 10 , & val ) < 0 )
2011-06-28 19:11:23 +04:00
return - EINVAL ;
mutex_lock ( & data - > update_lock ) ;
2014-02-03 06:29:28 +04:00
limit = i2c_smbus_read_byte_data ( client , lm95245_reg_address [ index ] ) ;
hyst = limit - val / 1000 ;
hyst = clamp_val ( hyst , 0 , 31 ) ;
data - > regs [ 8 ] = hyst ;
2011-06-28 19:11:23 +04:00
/* shared crit hysteresis */
i2c_smbus_write_byte_data ( client , LM95245_REG_RW_COMMON_HYSTERESIS ,
2014-02-03 06:29:28 +04:00
hyst ) ;
2011-06-28 19:11:23 +04:00
mutex_unlock ( & data - > update_lock ) ;
return count ;
}
static ssize_t show_type ( struct device * dev , struct device_attribute * attr ,
char * buf )
{
2014-01-20 21:17:16 +04:00
struct lm95245_data * data = dev_get_drvdata ( dev ) ;
2011-06-28 19:11:23 +04:00
return snprintf ( buf , PAGE_SIZE - 1 ,
data - > config2 & CFG2_REMOTE_TT ? " 1 \n " : " 2 \n " ) ;
}
static ssize_t set_type ( struct device * dev , struct device_attribute * attr ,
const char * buf , size_t count )
{
2014-01-20 21:17:16 +04:00
struct lm95245_data * data = dev_get_drvdata ( dev ) ;
struct i2c_client * client = data - > client ;
2011-06-28 19:11:23 +04:00
unsigned long val ;
2012-01-04 23:58:52 +04:00
if ( kstrtoul ( buf , 10 , & val ) < 0 )
2011-06-28 19:11:23 +04:00
return - EINVAL ;
if ( val ! = 1 & & val ! = 2 )
return - EINVAL ;
mutex_lock ( & data - > update_lock ) ;
if ( val = = 1 )
data - > config2 | = CFG2_REMOTE_TT ;
else
data - > config2 & = ~ CFG2_REMOTE_TT ;
data - > valid = 0 ;
i2c_smbus_write_byte_data ( client , LM95245_REG_RW_CONFIG2 ,
data - > config2 ) ;
mutex_unlock ( & data - > update_lock ) ;
return count ;
}
static ssize_t show_alarm ( struct device * dev , struct device_attribute * attr ,
char * buf )
{
struct lm95245_data * data = lm95245_update_device ( dev ) ;
int index = to_sensor_dev_attr ( attr ) - > index ;
return snprintf ( buf , PAGE_SIZE - 1 , " %d \n " ,
! ! ( data - > regs [ 9 ] & index ) ) ;
}
static ssize_t show_interval ( struct device * dev , struct device_attribute * attr ,
char * buf )
{
struct lm95245_data * data = lm95245_update_device ( dev ) ;
return snprintf ( buf , PAGE_SIZE - 1 , " %lu \n " , data - > interval ) ;
}
static ssize_t set_interval ( struct device * dev , struct device_attribute * attr ,
const char * buf , size_t count )
{
2014-01-20 21:17:16 +04:00
struct lm95245_data * data = dev_get_drvdata ( dev ) ;
struct i2c_client * client = data - > client ;
2011-06-28 19:11:23 +04:00
unsigned long val ;
2012-01-04 23:58:52 +04:00
if ( kstrtoul ( buf , 10 , & val ) < 0 )
2011-06-28 19:11:23 +04:00
return - EINVAL ;
mutex_lock ( & data - > update_lock ) ;
data - > interval = lm95245_set_conversion_rate ( client , val ) ;
mutex_unlock ( & data - > update_lock ) ;
return count ;
}
static SENSOR_DEVICE_ATTR ( temp1_input , S_IRUGO , show_input , NULL , 0 ) ;
static SENSOR_DEVICE_ATTR ( temp1_crit , S_IWUSR | S_IRUGO , show_limit ,
set_limit , 6 ) ;
2014-02-03 06:29:28 +04:00
static SENSOR_DEVICE_ATTR ( temp1_crit_hyst , S_IWUSR | S_IRUGO , show_crit_hyst ,
set_crit_hyst , 6 ) ;
2011-06-28 19:11:23 +04:00
static SENSOR_DEVICE_ATTR ( temp1_crit_alarm , S_IRUGO , show_alarm , NULL ,
STATUS1_LOC ) ;
static SENSOR_DEVICE_ATTR ( temp2_input , S_IRUGO , show_input , NULL , 2 ) ;
static SENSOR_DEVICE_ATTR ( temp2_crit , S_IWUSR | S_IRUGO , show_limit ,
set_limit , 7 ) ;
2014-02-22 20:17:34 +04:00
static SENSOR_DEVICE_ATTR ( temp2_crit_hyst , S_IRUGO , show_crit_hyst , NULL , 7 ) ;
2011-06-28 19:11:23 +04:00
static SENSOR_DEVICE_ATTR ( temp2_crit_alarm , S_IRUGO , show_alarm , NULL ,
STATUS1_RTCRIT ) ;
static SENSOR_DEVICE_ATTR ( temp2_type , S_IWUSR | S_IRUGO , show_type ,
set_type , 0 ) ;
static SENSOR_DEVICE_ATTR ( temp2_fault , S_IRUGO , show_alarm , NULL ,
STATUS1_DIODE_FAULT ) ;
static DEVICE_ATTR ( update_interval , S_IWUSR | S_IRUGO , show_interval ,
set_interval ) ;
2014-01-20 21:17:16 +04:00
static struct attribute * lm95245_attrs [ ] = {
2011-06-28 19:11:23 +04:00
& sensor_dev_attr_temp1_input . dev_attr . attr ,
& sensor_dev_attr_temp1_crit . dev_attr . attr ,
& sensor_dev_attr_temp1_crit_hyst . dev_attr . attr ,
& sensor_dev_attr_temp1_crit_alarm . dev_attr . attr ,
& sensor_dev_attr_temp2_input . dev_attr . attr ,
& sensor_dev_attr_temp2_crit . dev_attr . attr ,
& sensor_dev_attr_temp2_crit_hyst . dev_attr . attr ,
& sensor_dev_attr_temp2_crit_alarm . dev_attr . attr ,
& sensor_dev_attr_temp2_type . dev_attr . attr ,
& sensor_dev_attr_temp2_fault . dev_attr . attr ,
& dev_attr_update_interval . attr ,
NULL
} ;
2014-01-20 21:17:16 +04:00
ATTRIBUTE_GROUPS ( lm95245 ) ;
2011-06-28 19:11:23 +04:00
/* Return 0 if detection is successful, -ENODEV otherwise */
static int lm95245_detect ( struct i2c_client * new_client ,
struct i2c_board_info * info )
{
struct i2c_adapter * adapter = new_client - > adapter ;
2014-04-22 19:48:57 +04:00
int address = new_client - > addr ;
const char * name ;
int rev , id ;
2011-06-28 19:11:23 +04:00
if ( ! i2c_check_functionality ( adapter , I2C_FUNC_SMBUS_BYTE_DATA ) )
return - ENODEV ;
2014-04-22 19:48:57 +04:00
id = i2c_smbus_read_byte_data ( new_client , LM95245_REG_R_MAN_ID ) ;
if ( id ! = MANUFACTURER_ID )
2011-06-28 19:11:23 +04:00
return - ENODEV ;
2014-04-22 19:48:57 +04:00
rev = i2c_smbus_read_byte_data ( new_client , LM95245_REG_R_CHIP_ID ) ;
switch ( rev ) {
case LM95235_REVISION :
if ( address ! = 0x18 & & address ! = 0x29 & & address ! = 0x4c )
return - ENODEV ;
name = " lm95235 " ;
break ;
case LM95245_REVISION :
name = " lm95245 " ;
break ;
default :
return - ENODEV ;
}
strlcpy ( info - > type , name , I2C_NAME_SIZE ) ;
2011-06-28 19:11:23 +04:00
return 0 ;
}
2014-01-20 21:17:16 +04:00
static void lm95245_init_client ( struct i2c_client * client ,
struct lm95245_data * data )
2011-06-28 19:11:23 +04:00
{
data - > interval = lm95245_read_conversion_rate ( client ) ;
data - > config1 = i2c_smbus_read_byte_data ( client ,
LM95245_REG_RW_CONFIG1 ) ;
data - > config2 = i2c_smbus_read_byte_data ( client ,
LM95245_REG_RW_CONFIG2 ) ;
if ( data - > config1 & CFG_STOP ) {
/* Clear the standby bit */
data - > config1 & = ~ CFG_STOP ;
i2c_smbus_write_byte_data ( client , LM95245_REG_RW_CONFIG1 ,
data - > config1 ) ;
}
}
2014-01-20 21:17:16 +04:00
static int lm95245_probe ( struct i2c_client * client ,
2011-06-28 19:11:23 +04:00
const struct i2c_device_id * id )
{
2014-01-20 21:17:16 +04:00
struct device * dev = & client - > dev ;
2011-06-28 19:11:23 +04:00
struct lm95245_data * data ;
2014-01-20 21:17:16 +04:00
struct device * hwmon_dev ;
2011-06-28 19:11:23 +04:00
2014-01-20 21:17:16 +04:00
data = devm_kzalloc ( dev , sizeof ( struct lm95245_data ) , GFP_KERNEL ) ;
2012-06-02 20:58:12 +04:00
if ( ! data )
return - ENOMEM ;
2011-06-28 19:11:23 +04:00
2014-01-20 21:17:16 +04:00
data - > client = client ;
2011-06-28 19:11:23 +04:00
mutex_init ( & data - > update_lock ) ;
/* Initialize the LM95245 chip */
2014-01-20 21:17:16 +04:00
lm95245_init_client ( client , data ) ;
2011-06-28 19:11:23 +04:00
2014-01-20 21:17:16 +04:00
hwmon_dev = devm_hwmon_device_register_with_groups ( dev , client - > name ,
data ,
lm95245_groups ) ;
return PTR_ERR_OR_ZERO ( hwmon_dev ) ;
2011-06-28 19:11:23 +04:00
}
/* Driver data (common to all clients) */
static const struct i2c_device_id lm95245_id [ ] = {
2014-04-22 19:48:57 +04:00
{ " lm95235 " , 0 } ,
{ " lm95245 " , 0 } ,
2011-06-28 19:11:23 +04:00
{ }
} ;
MODULE_DEVICE_TABLE ( i2c , lm95245_id ) ;
static struct i2c_driver lm95245_driver = {
. class = I2C_CLASS_HWMON ,
. driver = {
2014-04-22 19:48:57 +04:00
. name = " lm95245 " ,
2011-06-28 19:11:23 +04:00
} ,
. probe = lm95245_probe ,
. id_table = lm95245_id ,
. detect = lm95245_detect ,
. address_list = normal_i2c ,
} ;
2012-01-20 11:38:18 +04:00
module_i2c_driver ( lm95245_driver ) ;
2011-06-28 19:11:23 +04:00
MODULE_AUTHOR ( " Alexander Stein <alexander.stein@systec-electronic.com> " ) ;
2014-04-22 19:48:57 +04:00
MODULE_DESCRIPTION ( " LM95235/LM95245 sensor driver " ) ;
2011-06-28 19:11:23 +04:00
MODULE_LICENSE ( " GPL " ) ;