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>
2013-07-16 22:57:51 +04:00
# include <linux/thermal.h>
# include <linux/of.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 {
2014-02-02 22:08:09 +04:00
struct i2c_client * client ;
2010-05-27 21:58:56 +04:00
struct device * hwmon_dev ;
2013-07-16 22:57:51 +04:00
struct thermal_zone_device * tz ;
2010-05-27 21:58:56 +04:00
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 ] ;
2015-12-01 19:10:21 +03:00
bool first_time ;
2010-05-27 21:58:56 +04:00
} ;
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 ,
} ;
2014-02-02 22:08:09 +04:00
static struct tmp102 * tmp102_update_device ( struct device * dev )
2010-05-27 21:58:56 +04:00
{
2014-02-02 22:08:09 +04:00
struct tmp102 * tmp102 = dev_get_drvdata ( dev ) ;
struct i2c_client * client = tmp102 - > client ;
2010-05-27 21:58:56 +04:00
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 ;
2015-12-01 19:10:21 +03:00
tmp102 - > first_time = false ;
2010-05-27 21:58:56 +04:00
}
mutex_unlock ( & tmp102 - > lock ) ;
return tmp102 ;
}
2015-07-24 09:12:54 +03:00
static int tmp102_read_temp ( void * dev , int * temp )
2013-07-16 22:57:51 +04:00
{
2014-02-02 22:08:09 +04:00
struct tmp102 * tmp102 = tmp102_update_device ( dev ) ;
2013-07-16 22:57:51 +04:00
2015-12-01 19:10:21 +03:00
/* Is it too early even to return a conversion? */
if ( tmp102 - > first_time ) {
dev_dbg ( dev , " %s: Conversion not ready yet.. \n " , __func__ ) ;
return - EAGAIN ;
}
2013-07-16 22:57:51 +04:00
* temp = tmp102 - > temp [ 0 ] ;
return 0 ;
}
2010-05-27 21:58:56 +04:00
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 ) ;
2014-02-02 22:08:09 +04:00
struct tmp102 * tmp102 = tmp102_update_device ( dev ) ;
2010-05-27 21:58:56 +04:00
2015-12-01 19:10:21 +03:00
/* Is it too early even to return a read? */
if ( tmp102 - > first_time )
return - EAGAIN ;
2010-05-27 21:58:56 +04:00
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 ) ;
2014-02-02 22:08:09 +04:00
struct tmp102 * tmp102 = dev_get_drvdata ( dev ) ;
struct i2c_client * client = tmp102 - > client ;
2010-05-27 21:58:56 +04:00
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 ) ;
2014-02-02 22:08:09 +04:00
static struct attribute * tmp102_attrs [ ] = {
2010-05-27 21:58:56 +04:00
& 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
} ;
2014-02-02 22:08:09 +04:00
ATTRIBUTE_GROUPS ( tmp102 ) ;
2010-05-27 21:58:56 +04:00
# 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)
2014-11-08 04:24:39 +03:00
static const struct thermal_zone_of_device_ops tmp102_of_thermal_ops = {
. get_temp = tmp102_read_temp ,
} ;
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 )
{
2014-02-03 02:22:30 +04:00
struct device * dev = & client - > dev ;
2014-02-02 22:08:09 +04:00
struct device * hwmon_dev ;
2010-05-27 21:58:56 +04:00
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 ) ) {
2014-02-03 02:22:30 +04:00
dev_err ( dev ,
2013-01-10 22:01:24 +04:00
" adapter doesn't support SMBus word transactions \n " ) ;
2010-05-27 21:58:56 +04:00
return - ENODEV ;
}
2014-02-03 02:22:30 +04:00
tmp102 = devm_kzalloc ( dev , sizeof ( * tmp102 ) , GFP_KERNEL ) ;
2012-06-02 22:35:52 +04:00
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 ) ;
2014-02-02 22:08:09 +04:00
tmp102 - > client = client ;
2010-05-27 21:58:56 +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:59 +04:00
if ( status < 0 ) {
2014-02-03 02:22:30 +04:00
dev_err ( 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 ) {
2014-02-03 02:22:30 +04:00
dev_err ( 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 ) {
2014-02-03 02:22:30 +04:00
dev_err ( 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 ) {
2014-02-03 02:22:30 +04:00
dev_err ( dev , " config settings did not stick \n " ) ;
2010-05-27 21:58:57 +04:00
status = - ENODEV ;
2010-05-27 21:58:59 +04:00
goto fail_restore_config ;
2010-05-27 21:58:56 +04:00
}
2015-12-01 19:10:21 +03:00
tmp102 - > last_update = jiffies ;
/* Mark that we are not ready with data until conversion is complete */
tmp102 - > first_time = true ;
2010-05-27 21:58:56 +04:00
mutex_init ( & tmp102 - > lock ) ;
2014-02-02 22:08:09 +04:00
hwmon_dev = hwmon_device_register_with_groups ( dev , client - > name ,
tmp102 , tmp102_groups ) ;
if ( IS_ERR ( hwmon_dev ) ) {
2014-02-03 02:22:30 +04:00
dev_dbg ( dev , " unable to register hwmon device \n " ) ;
2014-02-02 22:08:09 +04:00
status = PTR_ERR ( hwmon_dev ) ;
goto fail_restore_config ;
2010-05-27 21:58:56 +04:00
}
2014-02-02 22:08:09 +04:00
tmp102 - > hwmon_dev = hwmon_dev ;
tmp102 - > tz = thermal_zone_of_sensor_register ( hwmon_dev , 0 , hwmon_dev ,
2014-11-08 04:24:39 +03:00
& tmp102_of_thermal_ops ) ;
2013-07-16 22:57:51 +04:00
if ( IS_ERR ( tmp102 - > tz ) )
tmp102 - > tz = NULL ;
2014-02-03 02:22:30 +04:00
dev_info ( dev , " initialized \n " ) ;
2010-05-27 21:58:56 +04:00
return 0 ;
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 ) ;
2014-02-02 22:08:09 +04:00
thermal_zone_of_sensor_unregister ( tmp102 - > hwmon_dev , tmp102 - > tz ) ;
2010-05-27 21:58:56 +04:00
hwmon_device_unregister ( tmp102 - > hwmon_dev ) ;
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 ;
}
2015-02-03 18:01:58 +03:00
# ifdef CONFIG_PM_SLEEP
2010-05-27 21:58:56 +04:00
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
}
# endif /* CONFIG_PM */
2015-02-03 18:01:58 +03:00
static SIMPLE_DEV_PM_OPS ( tmp102_dev_pm_ops , tmp102_suspend , tmp102_resume ) ;
2010-05-27 21:58:56 +04:00
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 ,
2015-02-03 18:01:58 +03:00
. driver . pm = & tmp102_dev_pm_ops ,
2010-05-27 21:58:56 +04:00
. 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 " ) ;