2010-05-27 21:58:57 +04:00
/* Texas Instruments TMP102 SMBus temperature sensor driver
2010-05-27 21:58:56 +04:00
*
2010-05-27 21:58:57 +04:00
* Copyright ( C ) 2010 Steven King < sfking @ fdwdc . com >
2010-05-27 21:58:56 +04:00
*
* 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 . , 51 Franklin St - Fifth Floor , Boston , MA 02110 - 1301 USA
*/
# include <linux/module.h>
# include <linux/init.h>
# include <linux/slab.h>
# include <linux/i2c.h>
# include <linux/hwmon.h>
# include <linux/hwmon-sysfs.h>
# include <linux/err.h>
# include <linux/mutex.h>
2010-05-27 21:58:57 +04:00
# include <linux/device.h>
2012-10-10 17:25:56 +04:00
# include <linux/jiffies.h>
2010-05-27 21:58:56 +04:00
# define DRIVER_NAME "tmp102"
# define TMP102_TEMP_REG 0x00
# define TMP102_CONF_REG 0x01
/* note: these bit definitions are byte swapped */
# define TMP102_CONF_SD 0x0100
# define TMP102_CONF_TM 0x0200
# define TMP102_CONF_POL 0x0400
# define TMP102_CONF_F0 0x0800
# define TMP102_CONF_F1 0x1000
# define TMP102_CONF_R0 0x2000
# define TMP102_CONF_R1 0x4000
# define TMP102_CONF_OS 0x8000
# define TMP102_CONF_EM 0x0010
# define TMP102_CONF_AL 0x0020
# define TMP102_CONF_CR0 0x0040
# define TMP102_CONF_CR1 0x0080
# define TMP102_TLOW_REG 0x02
# define TMP102_THIGH_REG 0x03
struct tmp102 {
struct device * hwmon_dev ;
struct mutex lock ;
2010-05-27 21:58:59 +04:00
u16 config_orig ;
2010-05-27 21:58:56 +04:00
unsigned long last_update ;
int temp [ 3 ] ;
} ;
2010-05-27 21:58:57 +04:00
/* convert left adjusted 13-bit TMP102 register value to milliCelsius */
static inline int tmp102_reg_to_mC ( s16 val )
2010-05-27 21:58:56 +04:00
{
2010-05-27 21:58:57 +04:00
return ( ( val & ~ 0x01 ) * 1000 ) / 128 ;
2010-05-27 21:58:56 +04:00
}
2010-05-27 21:58:57 +04:00
/* convert milliCelsius to left adjusted 13-bit TMP102 register value */
static inline u16 tmp102_mC_to_reg ( int val )
2010-05-27 21:58:56 +04:00
{
return ( val * 128 ) / 1000 ;
}
static const u8 tmp102_reg [ ] = {
TMP102_TEMP_REG ,
TMP102_TLOW_REG ,
TMP102_THIGH_REG ,
} ;
static struct tmp102 * tmp102_update_device ( struct i2c_client * client )
{
struct tmp102 * tmp102 = i2c_get_clientdata ( client ) ;
mutex_lock ( & tmp102 - > lock ) ;
2010-05-27 21:58:57 +04:00
if ( time_after ( jiffies , tmp102 - > last_update + HZ / 3 ) ) {
2010-05-27 21:58:56 +04:00
int i ;
for ( i = 0 ; i < ARRAY_SIZE ( tmp102 - > temp ) ; + + i ) {
2011-11-04 15:00:47 +04:00
int status = i2c_smbus_read_word_swapped ( client ,
tmp102_reg [ i ] ) ;
2010-05-27 21:58:56 +04:00
if ( status > - 1 )
tmp102 - > temp [ i ] = tmp102_reg_to_mC ( status ) ;
}
tmp102 - > last_update = jiffies ;
}
mutex_unlock ( & tmp102 - > lock ) ;
return tmp102 ;
}
static ssize_t tmp102_show_temp ( struct device * dev ,
struct device_attribute * attr ,
char * buf )
{
struct sensor_device_attribute * sda = to_sensor_dev_attr ( attr ) ;
struct tmp102 * tmp102 = tmp102_update_device ( to_i2c_client ( dev ) ) ;
return sprintf ( buf , " %d \n " , tmp102 - > temp [ sda - > index ] ) ;
}
static ssize_t tmp102_set_temp ( struct device * dev ,
struct device_attribute * attr ,
const char * buf , size_t count )
{
struct sensor_device_attribute * sda = to_sensor_dev_attr ( attr ) ;
struct i2c_client * client = to_i2c_client ( dev ) ;
struct tmp102 * tmp102 = i2c_get_clientdata ( client ) ;
long val ;
2010-05-27 21:58:57 +04:00
int status ;
2010-05-27 21:58:56 +04:00
2012-01-04 23:58:52 +04:00
if ( kstrtol ( buf , 10 , & val ) < 0 )
2010-05-27 21:58:56 +04:00
return - EINVAL ;
2013-01-09 20:09:34 +04:00
val = clamp_val ( val , - 256000 , 255000 ) ;
2010-05-27 21:58:57 +04:00
2010-05-27 21:58:56 +04:00
mutex_lock ( & tmp102 - > lock ) ;
2010-05-27 21:58:57 +04:00
tmp102 - > temp [ sda - > index ] = val ;
2011-11-04 15:00:47 +04:00
status = i2c_smbus_write_word_swapped ( client , tmp102_reg [ sda - > index ] ,
tmp102_mC_to_reg ( val ) ) ;
2010-05-27 21:58:56 +04:00
mutex_unlock ( & tmp102 - > lock ) ;
return status ? : count ;
}
static SENSOR_DEVICE_ATTR ( temp1_input , S_IRUGO , tmp102_show_temp , NULL , 0 ) ;
static SENSOR_DEVICE_ATTR ( temp1_max_hyst , S_IWUSR | S_IRUGO , tmp102_show_temp ,
tmp102_set_temp , 1 ) ;
static SENSOR_DEVICE_ATTR ( temp1_max , S_IWUSR | S_IRUGO , tmp102_show_temp ,
tmp102_set_temp , 2 ) ;
static struct attribute * tmp102_attributes [ ] = {
& sensor_dev_attr_temp1_input . dev_attr . attr ,
& sensor_dev_attr_temp1_max_hyst . dev_attr . attr ,
& sensor_dev_attr_temp1_max . dev_attr . attr ,
NULL
} ;
static const struct attribute_group tmp102_attr_group = {
. attrs = tmp102_attributes ,
} ;
# define TMP102_CONFIG (TMP102_CONF_TM | TMP102_CONF_EM | TMP102_CONF_CR1)
# define TMP102_CONFIG_RD_ONLY (TMP102_CONF_R0 | TMP102_CONF_R1 | TMP102_CONF_AL)
2012-11-19 22:22:35 +04:00
static int tmp102_probe ( struct i2c_client * client ,
2010-05-27 21:58:56 +04:00
const struct i2c_device_id * id )
{
struct tmp102 * tmp102 ;
int status ;
2010-05-27 21:58:57 +04:00
if ( ! i2c_check_functionality ( client - > adapter ,
2010-05-27 21:58:56 +04:00
I2C_FUNC_SMBUS_WORD_DATA ) ) {
2013-01-10 22:01:24 +04:00
dev_err ( & client - > dev ,
" adapter doesn't support SMBus word transactions \n " ) ;
2010-05-27 21:58:56 +04:00
return - ENODEV ;
}
2012-06-02 22:35:52 +04:00
tmp102 = devm_kzalloc ( & client - > dev , sizeof ( * tmp102 ) , GFP_KERNEL ) ;
if ( ! tmp102 )
2010-05-27 21:58:56 +04:00
return - ENOMEM ;
2012-06-02 22:35:52 +04:00
2010-05-27 21:58:56 +04:00
i2c_set_clientdata ( client , tmp102 ) ;
2011-11-04 15:00:47 +04:00
status = i2c_smbus_read_word_swapped ( client , TMP102_CONF_REG ) ;
2010-05-27 21:58:59 +04:00
if ( status < 0 ) {
dev_err ( & client - > dev , " error reading config register \n " ) ;
2012-06-02 22:35:52 +04:00
return status ;
2010-05-27 21:58:59 +04:00
}
tmp102 - > config_orig = status ;
2011-11-04 15:00:47 +04:00
status = i2c_smbus_write_word_swapped ( client , TMP102_CONF_REG ,
TMP102_CONFIG ) ;
2010-05-27 21:58:57 +04:00
if ( status < 0 ) {
dev_err ( & client - > dev , " error writing config register \n " ) ;
2010-05-27 21:58:59 +04:00
goto fail_restore_config ;
2010-05-27 21:58:57 +04:00
}
2011-11-04 15:00:47 +04:00
status = i2c_smbus_read_word_swapped ( client , TMP102_CONF_REG ) ;
2010-05-27 21:58:56 +04:00
if ( status < 0 ) {
2010-05-27 21:58:57 +04:00
dev_err ( & client - > dev , " error reading config register \n " ) ;
2010-05-27 21:58:59 +04:00
goto fail_restore_config ;
2010-05-27 21:58:56 +04:00
}
status & = ~ TMP102_CONFIG_RD_ONLY ;
if ( status ! = TMP102_CONFIG ) {
2010-05-27 21:58:57 +04:00
dev_err ( & client - > dev , " config settings did not stick \n " ) ;
status = - ENODEV ;
2010-05-27 21:58:59 +04:00
goto fail_restore_config ;
2010-05-27 21:58:56 +04:00
}
tmp102 - > last_update = jiffies - HZ ;
mutex_init ( & tmp102 - > lock ) ;
status = sysfs_create_group ( & client - > dev . kobj , & tmp102_attr_group ) ;
if ( status ) {
dev_dbg ( & client - > dev , " could not create sysfs files \n " ) ;
2010-05-27 21:58:59 +04:00
goto fail_restore_config ;
2010-05-27 21:58:56 +04:00
}
tmp102 - > hwmon_dev = hwmon_device_register ( & client - > dev ) ;
if ( IS_ERR ( tmp102 - > hwmon_dev ) ) {
dev_dbg ( & client - > dev , " unable to register hwmon device \n " ) ;
status = PTR_ERR ( tmp102 - > hwmon_dev ) ;
2010-05-27 21:58:59 +04:00
goto fail_remove_sysfs ;
2010-05-27 21:58:56 +04:00
}
dev_info ( & client - > dev , " initialized \n " ) ;
return 0 ;
2010-05-27 21:58:59 +04:00
fail_remove_sysfs :
2010-05-27 21:58:56 +04:00
sysfs_remove_group ( & client - > dev . kobj , & tmp102_attr_group ) ;
2010-05-27 21:58:59 +04:00
fail_restore_config :
2011-11-04 15:00:47 +04:00
i2c_smbus_write_word_swapped ( client , TMP102_CONF_REG ,
tmp102 - > config_orig ) ;
2010-05-27 21:58:57 +04:00
return status ;
2010-05-27 21:58:56 +04:00
}
2012-11-19 22:25:51 +04:00
static int tmp102_remove ( struct i2c_client * client )
2010-05-27 21:58:56 +04:00
{
struct tmp102 * tmp102 = i2c_get_clientdata ( client ) ;
hwmon_device_unregister ( tmp102 - > hwmon_dev ) ;
sysfs_remove_group ( & client - > dev . kobj , & tmp102_attr_group ) ;
2010-05-27 21:58:59 +04:00
/* Stop monitoring if device was stopped originally */
if ( tmp102 - > config_orig & TMP102_CONF_SD ) {
int config ;
2011-11-04 15:00:47 +04:00
config = i2c_smbus_read_word_swapped ( client , TMP102_CONF_REG ) ;
2010-05-27 21:58:59 +04:00
if ( config > = 0 )
2011-11-04 15:00:47 +04:00
i2c_smbus_write_word_swapped ( client , TMP102_CONF_REG ,
config | TMP102_CONF_SD ) ;
2010-05-27 21:58:59 +04:00
}
2010-05-27 21:58:56 +04:00
return 0 ;
}
# ifdef CONFIG_PM
static int tmp102_suspend ( struct device * dev )
{
struct i2c_client * client = to_i2c_client ( dev ) ;
2010-05-27 21:58:58 +04:00
int config ;
2010-05-27 21:58:56 +04:00
2011-11-04 15:00:47 +04:00
config = i2c_smbus_read_word_swapped ( client , TMP102_CONF_REG ) ;
2010-05-27 21:58:58 +04:00
if ( config < 0 )
return config ;
2010-05-27 21:58:56 +04:00
2010-05-27 21:58:58 +04:00
config | = TMP102_CONF_SD ;
2011-11-04 15:00:47 +04:00
return i2c_smbus_write_word_swapped ( client , TMP102_CONF_REG , config ) ;
2010-05-27 21:58:56 +04:00
}
static int tmp102_resume ( struct device * dev )
{
struct i2c_client * client = to_i2c_client ( dev ) ;
2010-05-27 21:58:58 +04:00
int config ;
2010-05-27 21:58:56 +04:00
2011-11-04 15:00:47 +04:00
config = i2c_smbus_read_word_swapped ( client , TMP102_CONF_REG ) ;
2010-05-27 21:58:58 +04:00
if ( config < 0 )
return config ;
2010-05-27 21:58:56 +04:00
2010-05-27 21:58:58 +04:00
config & = ~ TMP102_CONF_SD ;
2011-11-04 15:00:47 +04:00
return i2c_smbus_write_word_swapped ( client , TMP102_CONF_REG , config ) ;
2010-05-27 21:58:56 +04:00
}
static const struct dev_pm_ops tmp102_dev_pm_ops = {
. suspend = tmp102_suspend ,
. resume = tmp102_resume ,
} ;
# define TMP102_DEV_PM_OPS (&tmp102_dev_pm_ops)
# else
# define TMP102_DEV_PM_OPS NULL
# endif /* CONFIG_PM */
static const struct i2c_device_id tmp102_id [ ] = {
2010-05-27 21:58:57 +04:00
{ " tmp102 " , 0 } ,
2010-05-27 21:58:56 +04:00
{ }
} ;
2010-05-27 21:58:57 +04:00
MODULE_DEVICE_TABLE ( i2c , tmp102_id ) ;
2010-05-27 21:58:56 +04:00
static struct i2c_driver tmp102_driver = {
. driver . name = DRIVER_NAME ,
. driver . pm = TMP102_DEV_PM_OPS ,
. probe = tmp102_probe ,
2012-11-19 22:21:20 +04:00
. remove = tmp102_remove ,
2010-05-27 21:58:56 +04:00
. id_table = tmp102_id ,
} ;
2012-01-20 11:38:18 +04:00
module_i2c_driver ( tmp102_driver ) ;
2010-05-27 21:58:56 +04:00
MODULE_AUTHOR ( " Steven King <sfking@fdwdc.com> " ) ;
MODULE_DESCRIPTION ( " Texas Instruments TMP102 temperature sensor driver " ) ;
MODULE_LICENSE ( " GPL " ) ;