2018-02-17 21:36:46 -08:00
// SPDX-License-Identifier: GPL-2.0+
2015-12-05 22:58:22 -08:00
/*
* ams - iaq - core . c - Support for AMS iAQ - Core VOC sensors
*
2018-02-17 21:36:46 -08:00
* Copyright ( C ) 2015 , 2018
* Author : Matt Ranostay < matt . ranostay @ konsulko . com >
2015-12-05 22:58:22 -08:00
*/
# include <linux/module.h>
2020-09-10 18:32:28 +01:00
# include <linux/mod_devicetable.h>
2015-12-05 22:58:22 -08:00
# include <linux/mutex.h>
# include <linux/init.h>
# include <linux/i2c.h>
# include <linux/iio/iio.h>
# define AMS_IAQCORE_DATA_SIZE 9
# define AMS_IAQCORE_VOC_CO2_IDX 0
# define AMS_IAQCORE_VOC_RESISTANCE_IDX 1
# define AMS_IAQCORE_VOC_TVOC_IDX 2
struct ams_iaqcore_reading {
__be16 co2_ppm ;
u8 status ;
__be32 resistance ;
__be16 voc_ppb ;
} __attribute__ ( ( __packed__ ) ) ;
struct ams_iaqcore_data {
struct i2c_client * client ;
struct mutex lock ;
unsigned long last_update ;
struct ams_iaqcore_reading buffer ;
} ;
static const struct iio_chan_spec ams_iaqcore_channels [ ] = {
{
. type = IIO_CONCENTRATION ,
. channel2 = IIO_MOD_CO2 ,
. modified = 1 ,
. info_mask_separate = BIT ( IIO_CHAN_INFO_PROCESSED ) ,
. address = AMS_IAQCORE_VOC_CO2_IDX ,
} ,
{
. type = IIO_RESISTANCE ,
. info_mask_separate = BIT ( IIO_CHAN_INFO_PROCESSED ) ,
. address = AMS_IAQCORE_VOC_RESISTANCE_IDX ,
} ,
{
. type = IIO_CONCENTRATION ,
. channel2 = IIO_MOD_VOC ,
. modified = 1 ,
. info_mask_separate = BIT ( IIO_CHAN_INFO_PROCESSED ) ,
. address = AMS_IAQCORE_VOC_TVOC_IDX ,
} ,
} ;
static int ams_iaqcore_read_measurement ( struct ams_iaqcore_data * data )
{
struct i2c_client * client = data - > client ;
int ret ;
struct i2c_msg msg = {
. addr = client - > addr ,
. flags = client - > flags | I2C_M_RD ,
. len = AMS_IAQCORE_DATA_SIZE ,
. buf = ( char * ) & data - > buffer ,
} ;
ret = i2c_transfer ( client - > adapter , & msg , 1 ) ;
return ( ret = = AMS_IAQCORE_DATA_SIZE ) ? 0 : ret ;
}
static int ams_iaqcore_get_measurement ( struct ams_iaqcore_data * data )
{
int ret ;
/* sensor can only be polled once a second max per datasheet */
if ( ! time_after ( jiffies , data - > last_update + HZ ) )
return 0 ;
ret = ams_iaqcore_read_measurement ( data ) ;
if ( ret < 0 )
return ret ;
data - > last_update = jiffies ;
return 0 ;
}
static int ams_iaqcore_read_raw ( struct iio_dev * indio_dev ,
struct iio_chan_spec const * chan , int * val ,
int * val2 , long mask )
{
struct ams_iaqcore_data * data = iio_priv ( indio_dev ) ;
int ret ;
if ( mask ! = IIO_CHAN_INFO_PROCESSED )
return - EINVAL ;
mutex_lock ( & data - > lock ) ;
ret = ams_iaqcore_get_measurement ( data ) ;
if ( ret )
goto err_out ;
switch ( chan - > address ) {
case AMS_IAQCORE_VOC_CO2_IDX :
* val = 0 ;
* val2 = be16_to_cpu ( data - > buffer . co2_ppm ) ;
ret = IIO_VAL_INT_PLUS_MICRO ;
break ;
case AMS_IAQCORE_VOC_RESISTANCE_IDX :
* val = be32_to_cpu ( data - > buffer . resistance ) ;
ret = IIO_VAL_INT ;
break ;
case AMS_IAQCORE_VOC_TVOC_IDX :
* val = 0 ;
* val2 = be16_to_cpu ( data - > buffer . voc_ppb ) ;
ret = IIO_VAL_INT_PLUS_NANO ;
break ;
default :
ret = - EINVAL ;
}
err_out :
mutex_unlock ( & data - > lock ) ;
return ret ;
}
static const struct iio_info ams_iaqcore_info = {
. read_raw = ams_iaqcore_read_raw ,
} ;
2022-11-18 23:36:47 +01:00
static int ams_iaqcore_probe ( struct i2c_client * client )
2015-12-05 22:58:22 -08:00
{
struct iio_dev * indio_dev ;
struct ams_iaqcore_data * data ;
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 ;
/* so initial reading will complete */
data - > last_update = jiffies - HZ ;
mutex_init ( & data - > lock ) ;
2017-03-30 18:11:22 +05:30
indio_dev - > info = & ams_iaqcore_info ;
2015-12-05 22:58:22 -08:00
indio_dev - > name = dev_name ( & client - > dev ) ;
indio_dev - > modes = INDIO_DIRECT_MODE ;
indio_dev - > channels = ams_iaqcore_channels ;
indio_dev - > num_channels = ARRAY_SIZE ( ams_iaqcore_channels ) ;
return devm_iio_device_register ( & client - > dev , indio_dev ) ;
}
static const struct i2c_device_id ams_iaqcore_id [ ] = {
{ " ams-iaq-core " , 0 } ,
{ }
} ;
MODULE_DEVICE_TABLE ( i2c , ams_iaqcore_id ) ;
static const struct of_device_id ams_iaqcore_dt_ids [ ] = {
{ . compatible = " ams,iaq-core " } ,
{ }
} ;
MODULE_DEVICE_TABLE ( of , ams_iaqcore_dt_ids ) ;
static struct i2c_driver ams_iaqcore_driver = {
. driver = {
. name = " ams-iaq-core " ,
2020-09-10 18:32:28 +01:00
. of_match_table = ams_iaqcore_dt_ids ,
2015-12-05 22:58:22 -08:00
} ,
2023-05-15 22:50:48 +02:00
. probe = ams_iaqcore_probe ,
2015-12-05 22:58:22 -08:00
. id_table = ams_iaqcore_id ,
} ;
module_i2c_driver ( ams_iaqcore_driver ) ;
2018-02-17 21:36:46 -08:00
MODULE_AUTHOR ( " Matt Ranostay <matt.ranostay@konsulko.com> " ) ;
2015-12-05 22:58:22 -08:00
MODULE_DESCRIPTION ( " AMS iAQ-Core VOC sensors " ) ;
MODULE_LICENSE ( " GPL v2 " ) ;