2013-08-08 20:39:00 +04:00
/*
* tmp006 . c - Support for TI TMP006 IR thermopile sensor
*
* Copyright ( c ) 2013 Peter Meerwald < pmeerw @ pmeerw . net >
*
* This file is subject to the terms and conditions of version 2 of
* the GNU General Public License . See the file COPYING in the main
* directory of this archive for more details .
*
* Driver for the Texas Instruments I2C 16 - bit IR thermopile sensor
*
* ( 7 - bit I2C slave address 0x40 , changeable via ADR pins )
*
* TODO : data ready irq
*/
# include <linux/err.h>
# include <linux/i2c.h>
# include <linux/delay.h>
# include <linux/module.h>
# include <linux/pm.h>
# include <linux/bitops.h>
# include <linux/iio/iio.h>
# include <linux/iio/sysfs.h>
# define TMP006_VOBJECT 0x00
# define TMP006_TAMBIENT 0x01
# define TMP006_CONFIG 0x02
# define TMP006_MANUFACTURER_ID 0xfe
# define TMP006_DEVICE_ID 0xff
# define TMP006_TAMBIENT_SHIFT 2
# define TMP006_CONFIG_RESET BIT(15)
# define TMP006_CONFIG_DRDY_EN BIT(8)
# define TMP006_CONFIG_DRDY BIT(7)
2015-06-22 00:50:22 +03:00
# define TMP006_CONFIG_MOD_MASK GENMASK(14, 12)
2013-08-08 20:39:00 +04:00
2015-06-22 00:50:22 +03:00
# define TMP006_CONFIG_CR_MASK GENMASK(11, 9)
2013-08-08 20:39:00 +04:00
# define TMP006_CONFIG_CR_SHIFT 9
2015-04-15 23:39:38 +03:00
# define TMP006_MANUFACTURER_MAGIC 0x5449
# define TMP006_DEVICE_MAGIC 0x0067
2013-08-08 20:39:00 +04:00
struct tmp006_data {
struct i2c_client * client ;
u16 config ;
} ;
static int tmp006_read_measurement ( struct tmp006_data * data , u8 reg )
{
s32 ret ;
int tries = 50 ;
while ( tries - - > 0 ) {
ret = i2c_smbus_read_word_swapped ( data - > client ,
TMP006_CONFIG ) ;
if ( ret < 0 )
return ret ;
if ( ret & TMP006_CONFIG_DRDY )
break ;
msleep ( 100 ) ;
}
if ( tries < 0 )
return - EIO ;
return i2c_smbus_read_word_swapped ( data - > client , reg ) ;
}
2013-09-08 17:57:00 +04:00
static const int tmp006_freqs [ 5 ] [ 2 ] = { { 4 , 0 } , { 2 , 0 } , { 1 , 0 } ,
{ 0 , 500000 } , { 0 , 250000 } } ;
2013-08-08 20:39:00 +04:00
static int tmp006_read_raw ( struct iio_dev * indio_dev ,
struct iio_chan_spec const * channel , int * val ,
int * val2 , long mask )
{
struct tmp006_data * data = iio_priv ( indio_dev ) ;
s32 ret ;
2013-09-08 17:57:00 +04:00
int cr ;
2013-08-08 20:39:00 +04:00
switch ( mask ) {
case IIO_CHAN_INFO_RAW :
if ( channel - > type = = IIO_VOLTAGE ) {
/* LSB is 156.25 nV */
ret = tmp006_read_measurement ( data , TMP006_VOBJECT ) ;
if ( ret < 0 )
return ret ;
* val = sign_extend32 ( ret , 15 ) ;
} else if ( channel - > type = = IIO_TEMP ) {
/* LSB is 0.03125 degrees Celsius */
ret = tmp006_read_measurement ( data , TMP006_TAMBIENT ) ;
if ( ret < 0 )
return ret ;
* val = sign_extend32 ( ret , 15 ) > > TMP006_TAMBIENT_SHIFT ;
} else {
break ;
}
return IIO_VAL_INT ;
case IIO_CHAN_INFO_SCALE :
if ( channel - > type = = IIO_VOLTAGE ) {
* val = 0 ;
* val2 = 156250 ;
} else if ( channel - > type = = IIO_TEMP ) {
* val = 31 ;
* val2 = 250000 ;
} else {
break ;
}
return IIO_VAL_INT_PLUS_MICRO ;
2013-09-08 17:57:00 +04:00
case IIO_CHAN_INFO_SAMP_FREQ :
cr = ( data - > config & TMP006_CONFIG_CR_MASK )
> > TMP006_CONFIG_CR_SHIFT ;
* val = tmp006_freqs [ cr ] [ 0 ] ;
* val2 = tmp006_freqs [ cr ] [ 1 ] ;
return IIO_VAL_INT_PLUS_MICRO ;
2013-08-08 20:39:00 +04:00
default :
break ;
}
return - EINVAL ;
}
2013-09-08 17:57:00 +04:00
static int tmp006_write_raw ( struct iio_dev * indio_dev ,
struct iio_chan_spec const * chan ,
int val ,
int val2 ,
long mask )
2013-08-08 20:39:00 +04:00
{
struct tmp006_data * data = iio_priv ( indio_dev ) ;
int i ;
2015-06-22 00:50:21 +03:00
if ( mask ! = IIO_CHAN_INFO_SAMP_FREQ )
return - EINVAL ;
2013-08-08 20:39:00 +04:00
for ( i = 0 ; i < ARRAY_SIZE ( tmp006_freqs ) ; i + + )
2013-09-08 17:57:00 +04:00
if ( ( val = = tmp006_freqs [ i ] [ 0 ] ) & &
( val2 = = tmp006_freqs [ i ] [ 1 ] ) ) {
data - > config & = ~ TMP006_CONFIG_CR_MASK ;
data - > config | = i < < TMP006_CONFIG_CR_SHIFT ;
2013-08-08 20:39:00 +04:00
2013-09-08 17:57:00 +04:00
return i2c_smbus_write_word_swapped ( data - > client ,
TMP006_CONFIG ,
data - > config ) ;
2013-08-08 20:39:00 +04:00
2013-09-08 17:57:00 +04:00
}
return - EINVAL ;
2013-08-08 20:39:00 +04:00
}
static IIO_CONST_ATTR ( sampling_frequency_available , " 4 2 1 0.5 0.25 " ) ;
static struct attribute * tmp006_attributes [ ] = {
& iio_const_attr_sampling_frequency_available . dev_attr . attr ,
NULL
} ;
static const struct attribute_group tmp006_attribute_group = {
. attrs = tmp006_attributes ,
} ;
static const struct iio_chan_spec tmp006_channels [ ] = {
{
. type = IIO_VOLTAGE ,
. info_mask_separate = BIT ( IIO_CHAN_INFO_RAW ) |
BIT ( IIO_CHAN_INFO_SCALE ) ,
2013-09-08 17:57:00 +04:00
. info_mask_shared_by_all = BIT ( IIO_CHAN_INFO_SAMP_FREQ ) ,
2013-08-08 20:39:00 +04:00
} ,
{
. type = IIO_TEMP ,
. info_mask_separate = BIT ( IIO_CHAN_INFO_RAW ) |
BIT ( IIO_CHAN_INFO_SCALE ) ,
2013-09-08 17:57:00 +04:00
. info_mask_shared_by_all = BIT ( IIO_CHAN_INFO_SAMP_FREQ ) ,
2013-08-08 20:39:00 +04:00
}
} ;
static const struct iio_info tmp006_info = {
. read_raw = tmp006_read_raw ,
2013-09-08 17:57:00 +04:00
. write_raw = tmp006_write_raw ,
2013-08-08 20:39:00 +04:00
. attrs = & tmp006_attribute_group ,
. driver_module = THIS_MODULE ,
} ;
static bool tmp006_check_identification ( struct i2c_client * client )
{
int mid , did ;
mid = i2c_smbus_read_word_swapped ( client , TMP006_MANUFACTURER_ID ) ;
if ( mid < 0 )
return false ;
did = i2c_smbus_read_word_swapped ( client , TMP006_DEVICE_ID ) ;
if ( did < 0 )
return false ;
2015-04-15 23:39:38 +03:00
return mid = = TMP006_MANUFACTURER_MAGIC & & did = = TMP006_DEVICE_MAGIC ;
2013-08-08 20:39:00 +04:00
}
static int tmp006_probe ( struct i2c_client * client ,
const struct i2c_device_id * id )
{
struct iio_dev * indio_dev ;
struct tmp006_data * data ;
int ret ;
if ( ! i2c_check_functionality ( client - > adapter , I2C_FUNC_SMBUS_WORD_DATA ) )
2016-02-27 09:13:49 +03:00
return - EOPNOTSUPP ;
2013-08-08 20:39:00 +04:00
if ( ! tmp006_check_identification ( client ) ) {
dev_err ( & client - > dev , " no TMP006 sensor \n " ) ;
return - ENODEV ;
}
indio_dev = devm_iio_device_alloc ( & client - > dev , sizeof ( * data ) ) ;
if ( ! indio_dev )
return - ENOMEM ;
data = iio_priv ( indio_dev ) ;
i2c_set_clientdata ( client , indio_dev ) ;
data - > client = client ;
indio_dev - > dev . parent = & client - > dev ;
indio_dev - > name = dev_name ( & client - > dev ) ;
indio_dev - > modes = INDIO_DIRECT_MODE ;
indio_dev - > info = & tmp006_info ;
indio_dev - > channels = tmp006_channels ;
indio_dev - > num_channels = ARRAY_SIZE ( tmp006_channels ) ;
ret = i2c_smbus_read_word_swapped ( data - > client , TMP006_CONFIG ) ;
if ( ret < 0 )
return ret ;
data - > config = ret ;
return iio_device_register ( indio_dev ) ;
}
static int tmp006_powerdown ( struct tmp006_data * data )
{
return i2c_smbus_write_word_swapped ( data - > client , TMP006_CONFIG ,
data - > config & ~ TMP006_CONFIG_MOD_MASK ) ;
}
static int tmp006_remove ( struct i2c_client * client )
{
struct iio_dev * indio_dev = i2c_get_clientdata ( client ) ;
iio_device_unregister ( indio_dev ) ;
tmp006_powerdown ( iio_priv ( indio_dev ) ) ;
return 0 ;
}
# ifdef CONFIG_PM_SLEEP
static int tmp006_suspend ( struct device * dev )
{
2013-09-19 01:47:00 +04:00
struct iio_dev * indio_dev = i2c_get_clientdata ( to_i2c_client ( dev ) ) ;
return tmp006_powerdown ( iio_priv ( indio_dev ) ) ;
2013-08-08 20:39:00 +04:00
}
static int tmp006_resume ( struct device * dev )
{
2013-09-19 01:47:00 +04:00
struct tmp006_data * data = iio_priv ( i2c_get_clientdata (
to_i2c_client ( dev ) ) ) ;
2013-08-08 20:39:00 +04:00
return i2c_smbus_write_word_swapped ( data - > client , TMP006_CONFIG ,
data - > config | TMP006_CONFIG_MOD_MASK ) ;
}
# endif
static SIMPLE_DEV_PM_OPS ( tmp006_pm_ops , tmp006_suspend , tmp006_resume ) ;
static const struct i2c_device_id tmp006_id [ ] = {
{ " tmp006 " , 0 } ,
{ }
} ;
MODULE_DEVICE_TABLE ( i2c , tmp006_id ) ;
static struct i2c_driver tmp006_driver = {
. driver = {
. name = " tmp006 " ,
. pm = & tmp006_pm_ops ,
} ,
. probe = tmp006_probe ,
. remove = tmp006_remove ,
. id_table = tmp006_id ,
} ;
module_i2c_driver ( tmp006_driver ) ;
MODULE_AUTHOR ( " Peter Meerwald <pmeerw@pmeerw.net> " ) ;
MODULE_DESCRIPTION ( " TI TMP006 IR thermopile sensor driver " ) ;
MODULE_LICENSE ( " GPL " ) ;