2015-09-06 23:27:39 +03:00
/*
* hdc100x . c - Support for the TI HDC100x temperature + humidity sensors
*
* Copyright ( C ) 2015 Matt Ranostay < mranostay @ gmail . com >
*
* 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 .
*
2017-06-19 19:01:05 +03:00
* Datasheets :
* http : //www.ti.com/product/HDC1000/datasheet
* http : //www.ti.com/product/HDC1008/datasheet
* http : //www.ti.com/product/HDC1010/datasheet
* http : //www.ti.com/product/HDC1050/datasheet
* http : //www.ti.com/product/HDC1080/datasheet
2015-09-06 23:27:39 +03:00
*/
# include <linux/delay.h>
# include <linux/module.h>
# include <linux/init.h>
# include <linux/i2c.h>
# include <linux/iio/iio.h>
# include <linux/iio/sysfs.h>
2016-09-02 20:23:17 +03:00
# include <linux/iio/buffer.h>
# include <linux/iio/trigger_consumer.h>
# include <linux/iio/triggered_buffer.h>
2015-09-06 23:27:39 +03:00
# define HDC100X_REG_TEMP 0x00
# define HDC100X_REG_HUMIDITY 0x01
# define HDC100X_REG_CONFIG 0x02
2016-09-02 20:23:17 +03:00
# define HDC100X_REG_CONFIG_ACQ_MODE BIT(12)
2015-09-06 23:27:39 +03:00
# define HDC100X_REG_CONFIG_HEATER_EN BIT(13)
struct hdc100x_data {
struct i2c_client * client ;
struct mutex lock ;
u16 config ;
/* integration time of the sensor */
int adc_int_us [ 2 ] ;
} ;
/* integration time in us */
static const int hdc100x_int_time [ ] [ 3 ] = {
{ 6350 , 3650 , 0 } , /* IIO_TEMP channel*/
{ 6500 , 3850 , 2500 } , /* IIO_HUMIDITYRELATIVE channel */
} ;
/* HDC100X_REG_CONFIG shift and mask values */
static const struct {
int shift ;
int mask ;
} hdc100x_resolution_shift [ 2 ] = {
{ /* IIO_TEMP channel */
. shift = 10 ,
. mask = 1
} ,
{ /* IIO_HUMIDITYRELATIVE channel */
. shift = 8 ,
2016-05-20 20:06:41 +03:00
. mask = 3 ,
2015-09-06 23:27:39 +03:00
} ,
} ;
static IIO_CONST_ATTR ( temp_integration_time_available ,
" 0.00365 0.00635 " ) ;
static IIO_CONST_ATTR ( humidityrelative_integration_time_available ,
" 0.0025 0.00385 0.0065 " ) ;
static IIO_CONST_ATTR ( out_current_heater_raw_available ,
" 0 1 " ) ;
static struct attribute * hdc100x_attributes [ ] = {
& iio_const_attr_temp_integration_time_available . dev_attr . attr ,
& iio_const_attr_humidityrelative_integration_time_available . dev_attr . attr ,
& iio_const_attr_out_current_heater_raw_available . dev_attr . attr ,
NULL
} ;
2017-04-01 11:23:33 +03:00
static const struct attribute_group hdc100x_attribute_group = {
2015-09-06 23:27:39 +03:00
. attrs = hdc100x_attributes ,
} ;
static const struct iio_chan_spec hdc100x_channels [ ] = {
{
. type = IIO_TEMP ,
. address = HDC100X_REG_TEMP ,
. info_mask_separate = BIT ( IIO_CHAN_INFO_RAW ) |
BIT ( IIO_CHAN_INFO_SCALE ) |
BIT ( IIO_CHAN_INFO_INT_TIME ) |
BIT ( IIO_CHAN_INFO_OFFSET ) ,
2016-09-02 20:23:17 +03:00
. scan_index = 0 ,
. scan_type = {
. sign = ' s ' ,
. realbits = 16 ,
. storagebits = 16 ,
. endianness = IIO_BE ,
} ,
2015-09-06 23:27:39 +03:00
} ,
{
. type = IIO_HUMIDITYRELATIVE ,
. address = HDC100X_REG_HUMIDITY ,
. info_mask_separate = BIT ( IIO_CHAN_INFO_RAW ) |
BIT ( IIO_CHAN_INFO_SCALE ) |
2016-09-02 20:23:17 +03:00
BIT ( IIO_CHAN_INFO_INT_TIME ) ,
. scan_index = 1 ,
. scan_type = {
. sign = ' u ' ,
. realbits = 16 ,
. storagebits = 16 ,
. endianness = IIO_BE ,
} ,
2015-09-06 23:27:39 +03:00
} ,
{
. type = IIO_CURRENT ,
. info_mask_separate = BIT ( IIO_CHAN_INFO_RAW ) ,
. extend_name = " heater " ,
. output = 1 ,
2016-09-02 20:23:17 +03:00
. scan_index = - 1 ,
2015-09-06 23:27:39 +03:00
} ,
2016-09-02 20:23:17 +03:00
IIO_CHAN_SOFT_TIMESTAMP ( 2 ) ,
2015-09-06 23:27:39 +03:00
} ;
2016-09-02 20:23:17 +03:00
static const unsigned long hdc100x_scan_masks [ ] = { 0x3 , 0 } ;
2015-09-06 23:27:39 +03:00
static int hdc100x_update_config ( struct hdc100x_data * data , int mask , int val )
{
int tmp = ( ~ mask & data - > config ) | val ;
int ret ;
ret = i2c_smbus_write_word_swapped ( data - > client ,
HDC100X_REG_CONFIG , tmp ) ;
if ( ! ret )
data - > config = tmp ;
return ret ;
}
static int hdc100x_set_it_time ( struct hdc100x_data * data , int chan , int val2 )
{
int shift = hdc100x_resolution_shift [ chan ] . shift ;
int ret = - EINVAL ;
int i ;
for ( i = 0 ; i < ARRAY_SIZE ( hdc100x_int_time [ chan ] ) ; i + + ) {
if ( val2 & & val2 = = hdc100x_int_time [ chan ] [ i ] ) {
ret = hdc100x_update_config ( data ,
hdc100x_resolution_shift [ chan ] . mask < < shift ,
i < < shift ) ;
if ( ! ret )
data - > adc_int_us [ chan ] = val2 ;
break ;
}
}
return ret ;
}
static int hdc100x_get_measurement ( struct hdc100x_data * data ,
struct iio_chan_spec const * chan )
{
struct i2c_client * client = data - > client ;
int delay = data - > adc_int_us [ chan - > address ] ;
int ret ;
2016-08-08 21:14:36 +03:00
__be16 val ;
2015-09-06 23:27:39 +03:00
/* start measurement */
ret = i2c_smbus_write_byte ( client , chan - > address ) ;
if ( ret < 0 ) {
dev_err ( & client - > dev , " cannot start measurement " ) ;
return ret ;
}
/* wait for integration time to pass */
usleep_range ( delay , delay + 1000 ) ;
2016-08-08 21:14:36 +03:00
/* read measurement */
ret = i2c_master_recv ( data - > client , ( char * ) & val , sizeof ( val ) ) ;
2015-09-06 23:27:39 +03:00
if ( ret < 0 ) {
2016-08-08 21:14:36 +03:00
dev_err ( & client - > dev , " cannot read sensor data \n " ) ;
2015-09-06 23:27:39 +03:00
return ret ;
}
2016-08-08 21:14:36 +03:00
return be16_to_cpu ( val ) ;
2015-09-06 23:27:39 +03:00
}
static int hdc100x_get_heater_status ( struct hdc100x_data * data )
{
return ! ! ( data - > config & HDC100X_REG_CONFIG_HEATER_EN ) ;
}
static int hdc100x_read_raw ( struct iio_dev * indio_dev ,
struct iio_chan_spec const * chan , int * val ,
int * val2 , long mask )
{
struct hdc100x_data * data = iio_priv ( indio_dev ) ;
switch ( mask ) {
case IIO_CHAN_INFO_RAW : {
int ret ;
mutex_lock ( & data - > lock ) ;
if ( chan - > type = = IIO_CURRENT ) {
* val = hdc100x_get_heater_status ( data ) ;
ret = IIO_VAL_INT ;
} else {
2016-09-02 20:23:17 +03:00
ret = iio_device_claim_direct_mode ( indio_dev ) ;
if ( ret ) {
mutex_unlock ( & data - > lock ) ;
return ret ;
}
2015-09-06 23:27:39 +03:00
ret = hdc100x_get_measurement ( data , chan ) ;
2016-09-02 20:23:17 +03:00
iio_device_release_direct_mode ( indio_dev ) ;
2015-09-06 23:27:39 +03:00
if ( ret > = 0 ) {
* val = ret ;
ret = IIO_VAL_INT ;
}
}
mutex_unlock ( & data - > lock ) ;
return ret ;
}
case IIO_CHAN_INFO_INT_TIME :
* val = 0 ;
* val2 = data - > adc_int_us [ chan - > address ] ;
return IIO_VAL_INT_PLUS_MICRO ;
case IIO_CHAN_INFO_SCALE :
if ( chan - > type = = IIO_TEMP ) {
2016-05-27 05:55:06 +03:00
* val = 165000 ;
2016-05-30 05:52:02 +03:00
* val2 = 65536 ;
2015-09-06 23:27:39 +03:00
return IIO_VAL_FRACTIONAL ;
} else {
2016-05-30 05:52:02 +03:00
* val = 100 ;
* val2 = 65536 ;
return IIO_VAL_FRACTIONAL ;
2015-09-06 23:27:39 +03:00
}
break ;
case IIO_CHAN_INFO_OFFSET :
2016-05-30 05:52:02 +03:00
* val = - 15887 ;
* val2 = 515151 ;
2015-09-27 09:18:57 +03:00
return IIO_VAL_INT_PLUS_MICRO ;
2015-09-06 23:27:39 +03:00
default :
return - EINVAL ;
}
}
static int hdc100x_write_raw ( struct iio_dev * indio_dev ,
struct iio_chan_spec const * chan ,
int val , int val2 , long mask )
{
struct hdc100x_data * data = iio_priv ( indio_dev ) ;
int ret = - EINVAL ;
switch ( mask ) {
case IIO_CHAN_INFO_INT_TIME :
if ( val ! = 0 )
return - EINVAL ;
mutex_lock ( & data - > lock ) ;
ret = hdc100x_set_it_time ( data , chan - > address , val2 ) ;
mutex_unlock ( & data - > lock ) ;
return ret ;
case IIO_CHAN_INFO_RAW :
if ( chan - > type ! = IIO_CURRENT | | val2 ! = 0 )
return - EINVAL ;
mutex_lock ( & data - > lock ) ;
ret = hdc100x_update_config ( data , HDC100X_REG_CONFIG_HEATER_EN ,
val ? HDC100X_REG_CONFIG_HEATER_EN : 0 ) ;
mutex_unlock ( & data - > lock ) ;
return ret ;
default :
return - EINVAL ;
}
}
2016-09-02 20:23:17 +03:00
static int hdc100x_buffer_postenable ( struct iio_dev * indio_dev )
{
struct hdc100x_data * data = iio_priv ( indio_dev ) ;
int ret ;
/* Buffer is enabled. First set ACQ Mode, then attach poll func */
mutex_lock ( & data - > lock ) ;
ret = hdc100x_update_config ( data , HDC100X_REG_CONFIG_ACQ_MODE ,
HDC100X_REG_CONFIG_ACQ_MODE ) ;
mutex_unlock ( & data - > lock ) ;
if ( ret )
return ret ;
return iio_triggered_buffer_postenable ( indio_dev ) ;
}
static int hdc100x_buffer_predisable ( struct iio_dev * indio_dev )
{
struct hdc100x_data * data = iio_priv ( indio_dev ) ;
int ret ;
/* First detach poll func, then reset ACQ mode. OK to disable buffer */
ret = iio_triggered_buffer_predisable ( indio_dev ) ;
if ( ret )
return ret ;
mutex_lock ( & data - > lock ) ;
ret = hdc100x_update_config ( data , HDC100X_REG_CONFIG_ACQ_MODE , 0 ) ;
mutex_unlock ( & data - > lock ) ;
return ret ;
}
static const struct iio_buffer_setup_ops hdc_buffer_setup_ops = {
. postenable = hdc100x_buffer_postenable ,
. predisable = hdc100x_buffer_predisable ,
} ;
static irqreturn_t hdc100x_trigger_handler ( int irq , void * p )
{
struct iio_poll_func * pf = p ;
struct iio_dev * indio_dev = pf - > indio_dev ;
struct hdc100x_data * data = iio_priv ( indio_dev ) ;
struct i2c_client * client = data - > client ;
int delay = data - > adc_int_us [ 0 ] + data - > adc_int_us [ 1 ] ;
int ret ;
s16 buf [ 8 ] ; /* 2x s16 + padding + 8 byte timestamp */
/* dual read starts at temp register */
mutex_lock ( & data - > lock ) ;
ret = i2c_smbus_write_byte ( client , HDC100X_REG_TEMP ) ;
if ( ret < 0 ) {
dev_err ( & client - > dev , " cannot start measurement \n " ) ;
goto err ;
}
usleep_range ( delay , delay + 1000 ) ;
ret = i2c_master_recv ( client , ( u8 * ) buf , 4 ) ;
if ( ret < 0 ) {
dev_err ( & client - > dev , " cannot read sensor data \n " ) ;
goto err ;
}
iio_push_to_buffers_with_timestamp ( indio_dev , buf ,
iio_get_time_ns ( indio_dev ) ) ;
err :
mutex_unlock ( & data - > lock ) ;
iio_trigger_notify_done ( indio_dev - > trig ) ;
return IRQ_HANDLED ;
}
2015-09-06 23:27:39 +03:00
static const struct iio_info hdc100x_info = {
. read_raw = hdc100x_read_raw ,
. write_raw = hdc100x_write_raw ,
. attrs = & hdc100x_attribute_group ,
. driver_module = THIS_MODULE ,
} ;
static int hdc100x_probe ( struct i2c_client * client ,
const struct i2c_device_id * id )
{
struct iio_dev * indio_dev ;
struct hdc100x_data * data ;
2016-09-02 20:23:17 +03:00
int ret ;
2015-09-06 23:27:39 +03:00
2016-08-08 21:14:36 +03:00
if ( ! i2c_check_functionality ( client - > adapter , I2C_FUNC_SMBUS_WORD_DATA |
I2C_FUNC_SMBUS_BYTE | I2C_FUNC_I2C ) )
2016-02-27 09:13:49 +03:00
return - EOPNOTSUPP ;
2015-09-06 23:27:39 +03:00
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 ;
mutex_init ( & data - > lock ) ;
indio_dev - > dev . parent = & client - > dev ;
indio_dev - > name = dev_name ( & client - > dev ) ;
indio_dev - > modes = INDIO_DIRECT_MODE ;
indio_dev - > info = & hdc100x_info ;
indio_dev - > channels = hdc100x_channels ;
indio_dev - > num_channels = ARRAY_SIZE ( hdc100x_channels ) ;
2016-09-02 20:23:17 +03:00
indio_dev - > available_scan_masks = hdc100x_scan_masks ;
2015-09-06 23:27:39 +03:00
/* be sure we are in a known state */
hdc100x_set_it_time ( data , 0 , hdc100x_int_time [ 0 ] [ 0 ] ) ;
hdc100x_set_it_time ( data , 1 , hdc100x_int_time [ 1 ] [ 0 ] ) ;
2016-09-02 20:23:17 +03:00
hdc100x_update_config ( data , HDC100X_REG_CONFIG_ACQ_MODE , 0 ) ;
ret = iio_triggered_buffer_setup ( indio_dev , NULL ,
hdc100x_trigger_handler ,
& hdc_buffer_setup_ops ) ;
if ( ret < 0 ) {
dev_err ( & client - > dev , " iio triggered buffer setup failed \n " ) ;
return ret ;
}
ret = iio_device_register ( indio_dev ) ;
if ( ret < 0 )
iio_triggered_buffer_cleanup ( indio_dev ) ;
return ret ;
}
static int hdc100x_remove ( struct i2c_client * client )
{
struct iio_dev * indio_dev = i2c_get_clientdata ( client ) ;
iio_device_unregister ( indio_dev ) ;
iio_triggered_buffer_cleanup ( indio_dev ) ;
2015-09-06 23:27:39 +03:00
2016-09-02 20:23:17 +03:00
return 0 ;
2015-09-06 23:27:39 +03:00
}
static const struct i2c_device_id hdc100x_id [ ] = {
{ " hdc100x " , 0 } ,
2017-06-19 19:01:05 +03:00
{ " hdc1000 " , 0 } ,
{ " hdc1008 " , 0 } ,
{ " hdc1010 " , 0 } ,
{ " hdc1050 " , 0 } ,
{ " hdc1080 " , 0 } ,
2015-09-06 23:27:39 +03:00
{ }
} ;
MODULE_DEVICE_TABLE ( i2c , hdc100x_id ) ;
2017-06-19 19:01:06 +03:00
static const struct of_device_id hdc100x_dt_ids [ ] = {
{ . compatible = " ti,hdc1000 " } ,
{ . compatible = " ti,hdc1008 " } ,
{ . compatible = " ti,hdc1010 " } ,
{ . compatible = " ti,hdc1050 " } ,
{ . compatible = " ti,hdc1080 " } ,
{ }
} ;
MODULE_DEVICE_TABLE ( of , hdc100x_dt_ids ) ;
2015-09-06 23:27:39 +03:00
static struct i2c_driver hdc100x_driver = {
. driver = {
. name = " hdc100x " ,
2017-06-19 19:01:06 +03:00
. of_match_table = of_match_ptr ( hdc100x_dt_ids ) ,
2015-09-06 23:27:39 +03:00
} ,
. probe = hdc100x_probe ,
2016-09-02 20:23:17 +03:00
. remove = hdc100x_remove ,
2015-09-06 23:27:39 +03:00
. id_table = hdc100x_id ,
} ;
module_i2c_driver ( hdc100x_driver ) ;
MODULE_AUTHOR ( " Matt Ranostay <mranostay@gmail.com> " ) ;
MODULE_DESCRIPTION ( " TI HDC100x humidity and temperature sensor driver " ) ;
MODULE_LICENSE ( " GPL " ) ;