2018-02-18 08:36:46 +03:00
// SPDX-License-Identifier: GPL-2.0+
2016-01-27 05:34:31 +03:00
/*
2019-12-16 03:00:45 +03:00
* atlas - sensor . c - Support for Atlas Scientific OEM SM sensors
2016-01-27 05:34:31 +03:00
*
2019-12-16 03:00:45 +03:00
* Copyright ( C ) 2015 - 2019 Konsulko Group
2018-02-18 08:36:46 +03:00
* Author : Matt Ranostay < matt . ranostay @ konsulko . com >
2016-01-27 05:34:31 +03:00
*/
# include <linux/module.h>
# include <linux/init.h>
# include <linux/interrupt.h>
# include <linux/delay.h>
# include <linux/mutex.h>
# include <linux/err.h>
# include <linux/irq.h>
# include <linux/irq_work.h>
# include <linux/i2c.h>
2020-09-10 20:32:29 +03:00
# include <linux/mod_devicetable.h>
2016-01-27 05:34:31 +03:00
# include <linux/regmap.h>
# include <linux/iio/iio.h>
# include <linux/iio/buffer.h>
# include <linux/iio/trigger.h>
# include <linux/iio/trigger_consumer.h>
# include <linux/iio/triggered_buffer.h>
# include <linux/pm_runtime.h>
2019-12-16 03:00:45 +03:00
# define ATLAS_REGMAP_NAME "atlas_regmap"
# define ATLAS_DRV_NAME "atlas"
2016-01-27 05:34:31 +03:00
# define ATLAS_REG_DEV_TYPE 0x00
# define ATLAS_REG_DEV_VERSION 0x01
# define ATLAS_REG_INT_CONTROL 0x04
# define ATLAS_REG_INT_CONTROL_EN BIT(3)
# define ATLAS_REG_PWR_CONTROL 0x06
2016-05-25 07:29:18 +03:00
# define ATLAS_REG_PH_CALIB_STATUS 0x0d
# define ATLAS_REG_PH_CALIB_STATUS_MASK 0x07
# define ATLAS_REG_PH_CALIB_STATUS_LOW BIT(0)
# define ATLAS_REG_PH_CALIB_STATUS_MID BIT(1)
# define ATLAS_REG_PH_CALIB_STATUS_HIGH BIT(2)
2016-01-27 05:34:31 +03:00
2016-05-25 07:29:20 +03:00
# define ATLAS_REG_EC_CALIB_STATUS 0x0f
# define ATLAS_REG_EC_CALIB_STATUS_MASK 0x0f
# define ATLAS_REG_EC_CALIB_STATUS_DRY BIT(0)
# define ATLAS_REG_EC_CALIB_STATUS_SINGLE BIT(1)
# define ATLAS_REG_EC_CALIB_STATUS_LOW BIT(2)
# define ATLAS_REG_EC_CALIB_STATUS_HIGH BIT(3)
2020-02-18 06:42:38 +03:00
# define ATLAS_REG_DO_CALIB_STATUS 0x09
# define ATLAS_REG_DO_CALIB_STATUS_MASK 0x03
# define ATLAS_REG_DO_CALIB_STATUS_PRESSURE BIT(0)
# define ATLAS_REG_DO_CALIB_STATUS_DO BIT(1)
2020-04-25 10:25:20 +03:00
# define ATLAS_REG_RTD_DATA 0x0e
2016-05-25 07:29:18 +03:00
# define ATLAS_REG_PH_TEMP_DATA 0x0e
2016-01-27 05:34:31 +03:00
# define ATLAS_REG_PH_DATA 0x16
2016-05-25 07:29:20 +03:00
# define ATLAS_REG_EC_PROBE 0x08
# define ATLAS_REG_EC_TEMP_DATA 0x10
# define ATLAS_REG_EC_DATA 0x18
# define ATLAS_REG_TDS_DATA 0x1c
# define ATLAS_REG_PSS_DATA 0x20
2016-08-19 23:36:11 +03:00
# define ATLAS_REG_ORP_CALIB_STATUS 0x0d
# define ATLAS_REG_ORP_DATA 0x0e
2020-02-18 06:42:38 +03:00
# define ATLAS_REG_DO_TEMP_DATA 0x12
# define ATLAS_REG_DO_DATA 0x22
2018-04-10 09:18:22 +03:00
# define ATLAS_PH_INT_TIME_IN_MS 450
# define ATLAS_EC_INT_TIME_IN_MS 650
# define ATLAS_ORP_INT_TIME_IN_MS 450
2020-02-18 06:42:38 +03:00
# define ATLAS_DO_INT_TIME_IN_MS 450
2020-04-25 10:25:20 +03:00
# define ATLAS_RTD_INT_TIME_IN_MS 450
2016-01-27 05:34:31 +03:00
2016-05-25 07:29:18 +03:00
enum {
ATLAS_PH_SM ,
2016-05-25 07:29:20 +03:00
ATLAS_EC_SM ,
2016-08-19 23:36:11 +03:00
ATLAS_ORP_SM ,
2020-02-18 06:42:38 +03:00
ATLAS_DO_SM ,
2020-04-25 10:25:20 +03:00
ATLAS_RTD_SM ,
2016-05-25 07:29:18 +03:00
} ;
2016-01-27 05:34:31 +03:00
struct atlas_data {
struct i2c_client * client ;
struct iio_trigger * trig ;
2016-05-25 07:29:18 +03:00
struct atlas_device * chip ;
2016-01-27 05:34:31 +03:00
struct regmap * regmap ;
struct irq_work work ;
2020-02-18 06:42:37 +03:00
unsigned int interrupt_enabled ;
2021-05-01 20:13:46 +03:00
/* 96-bit data + 32-bit pad + 64-bit timestamp */
__be32 buffer [ 6 ] __aligned ( 8 ) ;
2016-01-27 05:34:31 +03:00
} ;
static const struct regmap_config atlas_regmap_config = {
. name = ATLAS_REGMAP_NAME ,
. reg_bits = 8 ,
. val_bits = 8 ,
} ;
2020-01-04 04:29:08 +03:00
static int atlas_buffer_num_channels ( const struct iio_chan_spec * spec )
{
int idx = 0 ;
for ( ; spec - > type ! = IIO_TIMESTAMP ; spec + + )
idx + + ;
return idx ;
} ;
2016-05-25 07:29:18 +03:00
static const struct iio_chan_spec atlas_ph_channels [ ] = {
2016-01-27 05:34:31 +03:00
{
. type = IIO_PH ,
2016-05-25 07:29:18 +03:00
. address = ATLAS_REG_PH_DATA ,
2016-01-27 05:34:31 +03:00
. info_mask_separate =
BIT ( IIO_CHAN_INFO_RAW ) | BIT ( IIO_CHAN_INFO_SCALE ) ,
. scan_index = 0 ,
. scan_type = {
. sign = ' u ' ,
. realbits = 32 ,
. storagebits = 32 ,
. endianness = IIO_BE ,
} ,
} ,
IIO_CHAN_SOFT_TIMESTAMP ( 1 ) ,
{
. type = IIO_TEMP ,
2016-05-25 07:29:18 +03:00
. address = ATLAS_REG_PH_TEMP_DATA ,
2016-01-27 05:34:31 +03:00
. info_mask_separate =
BIT ( IIO_CHAN_INFO_RAW ) | BIT ( IIO_CHAN_INFO_SCALE ) ,
. output = 1 ,
. scan_index = - 1
} ,
} ;
2020-02-18 06:42:38 +03:00
# define ATLAS_CONCENTRATION_CHANNEL(_idx, _addr) \
2016-05-25 07:29:20 +03:00
{ \
. type = IIO_CONCENTRATION , \
. indexed = 1 , \
. channel = _idx , \
. address = _addr , \
. info_mask_separate = \
BIT ( IIO_CHAN_INFO_RAW ) | BIT ( IIO_CHAN_INFO_SCALE ) , \
. scan_index = _idx + 1 , \
. scan_type = { \
. sign = ' u ' , \
. realbits = 32 , \
. storagebits = 32 , \
. endianness = IIO_BE , \
} , \
}
static const struct iio_chan_spec atlas_ec_channels [ ] = {
{
. type = IIO_ELECTRICALCONDUCTIVITY ,
. address = ATLAS_REG_EC_DATA ,
. info_mask_separate =
BIT ( IIO_CHAN_INFO_RAW ) | BIT ( IIO_CHAN_INFO_SCALE ) ,
. scan_index = 0 ,
. scan_type = {
. sign = ' u ' ,
. realbits = 32 ,
. storagebits = 32 ,
. endianness = IIO_BE ,
} ,
} ,
2020-02-18 06:42:38 +03:00
ATLAS_CONCENTRATION_CHANNEL ( 0 , ATLAS_REG_TDS_DATA ) ,
ATLAS_CONCENTRATION_CHANNEL ( 1 , ATLAS_REG_PSS_DATA ) ,
2016-05-25 07:29:20 +03:00
IIO_CHAN_SOFT_TIMESTAMP ( 3 ) ,
{
. type = IIO_TEMP ,
. address = ATLAS_REG_EC_TEMP_DATA ,
. info_mask_separate =
BIT ( IIO_CHAN_INFO_RAW ) | BIT ( IIO_CHAN_INFO_SCALE ) ,
. output = 1 ,
. scan_index = - 1
} ,
} ;
2016-08-19 23:36:11 +03:00
static const struct iio_chan_spec atlas_orp_channels [ ] = {
{
. type = IIO_VOLTAGE ,
. address = ATLAS_REG_ORP_DATA ,
. info_mask_separate =
BIT ( IIO_CHAN_INFO_RAW ) | BIT ( IIO_CHAN_INFO_SCALE ) ,
. scan_index = 0 ,
. scan_type = {
. sign = ' s ' ,
. realbits = 32 ,
. storagebits = 32 ,
. endianness = IIO_BE ,
} ,
} ,
IIO_CHAN_SOFT_TIMESTAMP ( 1 ) ,
} ;
2020-02-18 06:42:38 +03:00
static const struct iio_chan_spec atlas_do_channels [ ] = {
2020-04-20 02:28:47 +03:00
{
. type = IIO_CONCENTRATION ,
. address = ATLAS_REG_DO_DATA ,
. info_mask_separate =
BIT ( IIO_CHAN_INFO_RAW ) | BIT ( IIO_CHAN_INFO_SCALE ) ,
. scan_index = 0 ,
. scan_type = {
. sign = ' u ' ,
. realbits = 32 ,
. storagebits = 32 ,
. endianness = IIO_BE ,
} ,
} ,
2020-02-18 06:42:38 +03:00
IIO_CHAN_SOFT_TIMESTAMP ( 1 ) ,
{
. type = IIO_TEMP ,
. address = ATLAS_REG_DO_TEMP_DATA ,
. info_mask_separate =
BIT ( IIO_CHAN_INFO_RAW ) | BIT ( IIO_CHAN_INFO_SCALE ) ,
. output = 1 ,
. scan_index = - 1
} ,
} ;
2020-04-25 10:25:20 +03:00
static const struct iio_chan_spec atlas_rtd_channels [ ] = {
{
. type = IIO_TEMP ,
. address = ATLAS_REG_RTD_DATA ,
. info_mask_separate = BIT ( IIO_CHAN_INFO_PROCESSED ) ,
. scan_index = 0 ,
. scan_type = {
. sign = ' s ' ,
. realbits = 32 ,
. storagebits = 32 ,
. endianness = IIO_BE ,
} ,
} ,
IIO_CHAN_SOFT_TIMESTAMP ( 1 ) ,
} ;
2016-05-25 07:29:18 +03:00
static int atlas_check_ph_calibration ( struct atlas_data * data )
{
struct device * dev = & data - > client - > dev ;
int ret ;
unsigned int val ;
ret = regmap_read ( data - > regmap , ATLAS_REG_PH_CALIB_STATUS , & val ) ;
if ( ret )
return ret ;
if ( ! ( val & ATLAS_REG_PH_CALIB_STATUS_MASK ) ) {
dev_warn ( dev , " device has not been calibrated \n " ) ;
return 0 ;
}
if ( ! ( val & ATLAS_REG_PH_CALIB_STATUS_LOW ) )
dev_warn ( dev , " device missing low point calibration \n " ) ;
if ( ! ( val & ATLAS_REG_PH_CALIB_STATUS_MID ) )
dev_warn ( dev , " device missing mid point calibration \n " ) ;
if ( ! ( val & ATLAS_REG_PH_CALIB_STATUS_HIGH ) )
dev_warn ( dev , " device missing high point calibration \n " ) ;
return 0 ;
}
2016-05-25 07:29:20 +03:00
static int atlas_check_ec_calibration ( struct atlas_data * data )
{
struct device * dev = & data - > client - > dev ;
int ret ;
unsigned int val ;
2016-09-24 22:16:21 +03:00
__be16 rval ;
2016-05-25 07:29:20 +03:00
2016-09-24 22:16:21 +03:00
ret = regmap_bulk_read ( data - > regmap , ATLAS_REG_EC_PROBE , & rval , 2 ) ;
2016-05-25 07:29:20 +03:00
if ( ret )
return ret ;
2016-09-24 22:16:21 +03:00
val = be16_to_cpu ( rval ) ;
dev_info ( dev , " probe set to K = %d.%.2d " , val / 100 , val % 100 ) ;
2016-05-25 07:29:20 +03:00
ret = regmap_read ( data - > regmap , ATLAS_REG_EC_CALIB_STATUS , & val ) ;
if ( ret )
return ret ;
if ( ! ( val & ATLAS_REG_EC_CALIB_STATUS_MASK ) ) {
dev_warn ( dev , " device has not been calibrated \n " ) ;
return 0 ;
}
if ( ! ( val & ATLAS_REG_EC_CALIB_STATUS_DRY ) )
dev_warn ( dev , " device missing dry point calibration \n " ) ;
if ( val & ATLAS_REG_EC_CALIB_STATUS_SINGLE ) {
dev_warn ( dev , " device using single point calibration \n " ) ;
} else {
if ( ! ( val & ATLAS_REG_EC_CALIB_STATUS_LOW ) )
dev_warn ( dev , " device missing low point calibration \n " ) ;
if ( ! ( val & ATLAS_REG_EC_CALIB_STATUS_HIGH ) )
dev_warn ( dev , " device missing high point calibration \n " ) ;
}
return 0 ;
}
2016-08-19 23:36:11 +03:00
static int atlas_check_orp_calibration ( struct atlas_data * data )
{
struct device * dev = & data - > client - > dev ;
int ret ;
unsigned int val ;
ret = regmap_read ( data - > regmap , ATLAS_REG_ORP_CALIB_STATUS , & val ) ;
if ( ret )
return ret ;
if ( ! val )
dev_warn ( dev , " device has not been calibrated \n " ) ;
return 0 ;
2020-02-18 06:42:38 +03:00
}
static int atlas_check_do_calibration ( struct atlas_data * data )
{
struct device * dev = & data - > client - > dev ;
int ret ;
unsigned int val ;
ret = regmap_read ( data - > regmap , ATLAS_REG_DO_CALIB_STATUS , & val ) ;
if ( ret )
return ret ;
if ( ! ( val & ATLAS_REG_DO_CALIB_STATUS_MASK ) ) {
dev_warn ( dev , " device has not been calibrated \n " ) ;
return 0 ;
}
if ( ! ( val & ATLAS_REG_DO_CALIB_STATUS_PRESSURE ) )
dev_warn ( dev , " device missing atmospheric pressure calibration \n " ) ;
if ( ! ( val & ATLAS_REG_DO_CALIB_STATUS_DO ) )
dev_warn ( dev , " device missing dissolved oxygen calibration \n " ) ;
return 0 ;
}
2016-08-19 23:36:11 +03:00
2016-05-25 07:29:18 +03:00
struct atlas_device {
const struct iio_chan_spec * channels ;
int num_channels ;
int data_reg ;
int ( * calibration ) ( struct atlas_data * data ) ;
int delay ;
} ;
static struct atlas_device atlas_devices [ ] = {
[ ATLAS_PH_SM ] = {
. channels = atlas_ph_channels ,
. num_channels = 3 ,
. data_reg = ATLAS_REG_PH_DATA ,
. calibration = & atlas_check_ph_calibration ,
2018-04-10 09:18:22 +03:00
. delay = ATLAS_PH_INT_TIME_IN_MS ,
2016-05-25 07:29:18 +03:00
} ,
2016-05-25 07:29:20 +03:00
[ ATLAS_EC_SM ] = {
. channels = atlas_ec_channels ,
. num_channels = 5 ,
. data_reg = ATLAS_REG_EC_DATA ,
. calibration = & atlas_check_ec_calibration ,
2018-04-10 09:18:22 +03:00
. delay = ATLAS_EC_INT_TIME_IN_MS ,
2016-05-25 07:29:20 +03:00
} ,
2016-08-19 23:36:11 +03:00
[ ATLAS_ORP_SM ] = {
. channels = atlas_orp_channels ,
. num_channels = 2 ,
. data_reg = ATLAS_REG_ORP_DATA ,
. calibration = & atlas_check_orp_calibration ,
2018-04-10 09:18:22 +03:00
. delay = ATLAS_ORP_INT_TIME_IN_MS ,
2016-08-19 23:36:11 +03:00
} ,
2020-02-18 06:42:38 +03:00
[ ATLAS_DO_SM ] = {
. channels = atlas_do_channels ,
. num_channels = 3 ,
. data_reg = ATLAS_REG_DO_DATA ,
. calibration = & atlas_check_do_calibration ,
. delay = ATLAS_DO_INT_TIME_IN_MS ,
} ,
2020-04-25 10:25:20 +03:00
[ ATLAS_RTD_SM ] = {
. channels = atlas_rtd_channels ,
. num_channels = 2 ,
. data_reg = ATLAS_REG_RTD_DATA ,
. delay = ATLAS_RTD_INT_TIME_IN_MS ,
} ,
2016-05-25 07:29:18 +03:00
} ;
2016-01-27 05:34:31 +03:00
static int atlas_set_powermode ( struct atlas_data * data , int on )
{
return regmap_write ( data - > regmap , ATLAS_REG_PWR_CONTROL , on ) ;
}
static int atlas_set_interrupt ( struct atlas_data * data , bool state )
{
2020-02-18 06:42:37 +03:00
if ( ! data - > interrupt_enabled )
return 0 ;
2016-01-27 05:34:31 +03:00
return regmap_update_bits ( data - > regmap , ATLAS_REG_INT_CONTROL ,
ATLAS_REG_INT_CONTROL_EN ,
state ? ATLAS_REG_INT_CONTROL_EN : 0 ) ;
}
static int atlas_buffer_postenable ( struct iio_dev * indio_dev )
{
struct atlas_data * data = iio_priv ( indio_dev ) ;
int ret ;
2021-05-16 19:21:00 +03:00
ret = pm_runtime_resume_and_get ( & data - > client - > dev ) ;
if ( ret )
2016-01-27 05:34:31 +03:00
return ret ;
return atlas_set_interrupt ( data , true ) ;
}
static int atlas_buffer_predisable ( struct iio_dev * indio_dev )
{
struct atlas_data * data = iio_priv ( indio_dev ) ;
int ret ;
2019-09-20 10:31:22 +03:00
ret = atlas_set_interrupt ( data , false ) ;
2016-01-27 05:34:31 +03:00
if ( ret )
return ret ;
2019-09-20 10:31:22 +03:00
pm_runtime_mark_last_busy ( & data - > client - > dev ) ;
ret = pm_runtime_put_autosuspend ( & data - > client - > dev ) ;
2016-01-27 05:34:31 +03:00
if ( ret )
return ret ;
2020-05-25 14:38:53 +03:00
return 0 ;
2016-01-27 05:34:31 +03:00
}
static const struct iio_buffer_setup_ops atlas_buffer_setup_ops = {
. postenable = atlas_buffer_postenable ,
. predisable = atlas_buffer_predisable ,
} ;
static void atlas_work_handler ( struct irq_work * work )
{
struct atlas_data * data = container_of ( work , struct atlas_data , work ) ;
iio_trigger_poll ( data - > trig ) ;
}
static irqreturn_t atlas_trigger_handler ( int irq , void * private )
{
struct iio_poll_func * pf = private ;
struct iio_dev * indio_dev = pf - > indio_dev ;
struct atlas_data * data = iio_priv ( indio_dev ) ;
2020-01-04 04:29:08 +03:00
int channels = atlas_buffer_num_channels ( data - > chip - > channels ) ;
2016-01-27 05:34:31 +03:00
int ret ;
2016-05-25 07:29:18 +03:00
ret = regmap_bulk_read ( data - > regmap , data - > chip - > data_reg ,
2020-04-05 21:03:17 +03:00
& data - > buffer , sizeof ( __be32 ) * channels ) ;
2016-01-27 05:34:31 +03:00
2016-02-15 05:59:07 +03:00
if ( ! ret )
2016-01-27 05:34:31 +03:00
iio_push_to_buffers_with_timestamp ( indio_dev , data - > buffer ,
2016-03-09 21:05:49 +03:00
iio_get_time_ns ( indio_dev ) ) ;
2016-01-27 05:34:31 +03:00
iio_trigger_notify_done ( indio_dev - > trig ) ;
return IRQ_HANDLED ;
}
static irqreturn_t atlas_interrupt_handler ( int irq , void * private )
{
struct iio_dev * indio_dev = private ;
struct atlas_data * data = iio_priv ( indio_dev ) ;
irq_work_queue ( & data - > work ) ;
return IRQ_HANDLED ;
}
2016-05-25 07:29:18 +03:00
static int atlas_read_measurement ( struct atlas_data * data , int reg , __be32 * val )
2016-01-27 05:34:31 +03:00
{
struct device * dev = & data - > client - > dev ;
int suspended = pm_runtime_suspended ( dev ) ;
int ret ;
2021-05-16 19:21:00 +03:00
ret = pm_runtime_resume_and_get ( dev ) ;
if ( ret )
2016-01-27 05:34:31 +03:00
return ret ;
if ( suspended )
2018-04-10 09:18:22 +03:00
msleep ( data - > chip - > delay ) ;
2016-01-27 05:34:31 +03:00
2020-04-05 21:03:17 +03:00
ret = regmap_bulk_read ( data - > regmap , reg , val , sizeof ( * val ) ) ;
2016-01-27 05:34:31 +03:00
pm_runtime_mark_last_busy ( dev ) ;
pm_runtime_put_autosuspend ( dev ) ;
return ret ;
}
static int atlas_read_raw ( struct iio_dev * indio_dev ,
struct iio_chan_spec const * chan ,
int * val , int * val2 , long mask )
{
struct atlas_data * data = iio_priv ( indio_dev ) ;
switch ( mask ) {
2020-04-25 10:25:20 +03:00
case IIO_CHAN_INFO_PROCESSED :
2016-01-27 05:34:31 +03:00
case IIO_CHAN_INFO_RAW : {
int ret ;
__be32 reg ;
switch ( chan - > type ) {
case IIO_TEMP :
ret = regmap_bulk_read ( data - > regmap , chan - > address ,
2020-04-05 21:03:17 +03:00
& reg , sizeof ( reg ) ) ;
2016-01-27 05:34:31 +03:00
break ;
case IIO_PH :
2016-05-25 07:29:20 +03:00
case IIO_CONCENTRATION :
case IIO_ELECTRICALCONDUCTIVITY :
2016-08-19 23:36:11 +03:00
case IIO_VOLTAGE :
2016-08-19 23:36:09 +03:00
ret = iio_device_claim_direct_mode ( indio_dev ) ;
if ( ret )
return ret ;
2016-01-27 05:34:31 +03:00
2016-08-19 23:36:09 +03:00
ret = atlas_read_measurement ( data , chan - > address , & reg ) ;
2016-01-27 05:34:31 +03:00
2016-08-19 23:36:09 +03:00
iio_device_release_direct_mode ( indio_dev ) ;
2016-01-27 05:34:31 +03:00
break ;
default :
ret = - EINVAL ;
}
if ( ! ret ) {
* val = be32_to_cpu ( reg ) ;
ret = IIO_VAL_INT ;
}
return ret ;
}
case IIO_CHAN_INFO_SCALE :
switch ( chan - > type ) {
case IIO_TEMP :
2018-12-31 06:07:01 +03:00
* val = 10 ;
return IIO_VAL_INT ;
2016-01-27 05:34:31 +03:00
case IIO_PH :
* val = 1 ; /* 0.001 */
* val2 = 1000 ;
break ;
2016-05-25 07:29:20 +03:00
case IIO_ELECTRICALCONDUCTIVITY :
* val = 1 ; /* 0.00001 */
2016-07-26 01:06:56 +03:00
* val2 = 100000 ;
2016-05-25 07:29:20 +03:00
break ;
case IIO_CONCENTRATION :
* val = 0 ; /* 0.000000001 */
* val2 = 1000 ;
return IIO_VAL_INT_PLUS_NANO ;
2016-08-19 23:36:11 +03:00
case IIO_VOLTAGE :
* val = 1 ; /* 0.1 */
* val2 = 10 ;
break ;
2016-01-27 05:34:31 +03:00
default :
return - EINVAL ;
}
return IIO_VAL_FRACTIONAL ;
}
return - EINVAL ;
}
static int atlas_write_raw ( struct iio_dev * indio_dev ,
struct iio_chan_spec const * chan ,
int val , int val2 , long mask )
{
struct atlas_data * data = iio_priv ( indio_dev ) ;
2018-12-31 06:07:01 +03:00
__be32 reg = cpu_to_be32 ( val / 10 ) ;
2016-01-27 05:34:31 +03:00
if ( val2 ! = 0 | | val < 0 | | val > 20000 )
return - EINVAL ;
if ( mask ! = IIO_CHAN_INFO_RAW | | chan - > type ! = IIO_TEMP )
return - EINVAL ;
return regmap_bulk_write ( data - > regmap , chan - > address ,
& reg , sizeof ( reg ) ) ;
}
static const struct iio_info atlas_info = {
. read_raw = atlas_read_raw ,
. write_raw = atlas_write_raw ,
} ;
2016-05-25 07:29:18 +03:00
static const struct i2c_device_id atlas_id [ ] = {
{ " atlas-ph-sm " , ATLAS_PH_SM } ,
2016-05-25 07:29:20 +03:00
{ " atlas-ec-sm " , ATLAS_EC_SM } ,
2016-08-19 23:36:11 +03:00
{ " atlas-orp-sm " , ATLAS_ORP_SM } ,
2020-02-18 06:42:38 +03:00
{ " atlas-do-sm " , ATLAS_DO_SM } ,
2020-04-25 10:25:20 +03:00
{ " atlas-rtd-sm " , ATLAS_RTD_SM } ,
2016-05-25 07:29:18 +03:00
{ }
} ;
MODULE_DEVICE_TABLE ( i2c , atlas_id ) ;
2016-01-27 05:34:31 +03:00
2016-05-25 07:29:18 +03:00
static const struct of_device_id atlas_dt_ids [ ] = {
{ . compatible = " atlas,ph-sm " , . data = ( void * ) ATLAS_PH_SM , } ,
2016-05-25 07:29:20 +03:00
{ . compatible = " atlas,ec-sm " , . data = ( void * ) ATLAS_EC_SM , } ,
2016-08-19 23:36:11 +03:00
{ . compatible = " atlas,orp-sm " , . data = ( void * ) ATLAS_ORP_SM , } ,
2020-02-18 06:42:38 +03:00
{ . compatible = " atlas,do-sm " , . data = ( void * ) ATLAS_DO_SM , } ,
2020-04-25 10:25:20 +03:00
{ . compatible = " atlas,rtd-sm " , . data = ( void * ) ATLAS_RTD_SM , } ,
2016-05-25 07:29:18 +03:00
{ }
2016-01-27 05:34:31 +03:00
} ;
2016-05-25 07:29:18 +03:00
MODULE_DEVICE_TABLE ( of , atlas_dt_ids ) ;
2016-01-27 05:34:31 +03:00
static int atlas_probe ( struct i2c_client * client ,
const struct i2c_device_id * id )
{
struct atlas_data * data ;
2016-05-25 07:29:18 +03:00
struct atlas_device * chip ;
2016-01-27 05:34:31 +03:00
struct iio_trigger * trig ;
struct iio_dev * indio_dev ;
int ret ;
indio_dev = devm_iio_device_alloc ( & client - > dev , sizeof ( * data ) ) ;
if ( ! indio_dev )
return - ENOMEM ;
2020-09-10 20:32:29 +03:00
if ( ! dev_fwnode ( & client - > dev ) )
2016-05-25 07:29:18 +03:00
chip = & atlas_devices [ id - > driver_data ] ;
else
2020-09-10 20:32:29 +03:00
chip = & atlas_devices [ ( unsigned long ) device_get_match_data ( & client - > dev ) ] ;
2016-05-25 07:29:18 +03:00
2016-01-27 05:34:31 +03:00
indio_dev - > info = & atlas_info ;
indio_dev - > name = ATLAS_DRV_NAME ;
2016-05-25 07:29:18 +03:00
indio_dev - > channels = chip - > channels ;
indio_dev - > num_channels = chip - > num_channels ;
2016-01-27 05:34:31 +03:00
indio_dev - > modes = INDIO_BUFFER_SOFTWARE | INDIO_DIRECT_MODE ;
trig = devm_iio_trigger_alloc ( & client - > dev , " %s-dev%d " ,
2021-04-26 20:49:03 +03:00
indio_dev - > name , iio_device_id ( indio_dev ) ) ;
2016-01-27 05:34:31 +03:00
if ( ! trig )
return - ENOMEM ;
data = iio_priv ( indio_dev ) ;
data - > client = client ;
data - > trig = trig ;
2016-05-25 07:29:18 +03:00
data - > chip = chip ;
2016-01-27 05:34:31 +03:00
iio_trigger_set_drvdata ( trig , indio_dev ) ;
i2c_set_clientdata ( client , indio_dev ) ;
data - > regmap = devm_regmap_init_i2c ( client , & atlas_regmap_config ) ;
if ( IS_ERR ( data - > regmap ) ) {
dev_err ( & client - > dev , " regmap initialization failed \n " ) ;
return PTR_ERR ( data - > regmap ) ;
}
ret = pm_runtime_set_active ( & client - > dev ) ;
if ( ret )
return ret ;
2016-05-25 07:29:18 +03:00
ret = chip - > calibration ( data ) ;
2016-01-27 05:34:31 +03:00
if ( ret )
return ret ;
ret = iio_trigger_register ( trig ) ;
if ( ret ) {
dev_err ( & client - > dev , " failed to register trigger \n " ) ;
return ret ;
}
ret = iio_triggered_buffer_setup ( indio_dev , & iio_pollfunc_store_time ,
& atlas_trigger_handler , & atlas_buffer_setup_ops ) ;
if ( ret ) {
dev_err ( & client - > dev , " cannot setup iio trigger \n " ) ;
goto unregister_trigger ;
}
init_irq_work ( & data - > work , atlas_work_handler ) ;
2020-02-18 06:42:37 +03:00
if ( client - > irq > 0 ) {
/* interrupt pin toggles on new conversion */
ret = devm_request_threaded_irq ( & client - > dev , client - > irq ,
NULL , atlas_interrupt_handler ,
IRQF_TRIGGER_RISING |
IRQF_TRIGGER_FALLING | IRQF_ONESHOT ,
" atlas_irq " ,
indio_dev ) ;
if ( ret )
dev_warn ( & client - > dev ,
" request irq (%d) failed \n " , client - > irq ) ;
else
data - > interrupt_enabled = 1 ;
2016-01-27 05:34:31 +03:00
}
ret = atlas_set_powermode ( data , 1 ) ;
if ( ret ) {
dev_err ( & client - > dev , " cannot power device on " ) ;
goto unregister_buffer ;
}
pm_runtime_enable ( & client - > dev ) ;
pm_runtime_set_autosuspend_delay ( & client - > dev , 2500 ) ;
pm_runtime_use_autosuspend ( & client - > dev ) ;
ret = iio_device_register ( indio_dev ) ;
if ( ret ) {
dev_err ( & client - > dev , " unable to register device \n " ) ;
goto unregister_pm ;
}
return 0 ;
unregister_pm :
pm_runtime_disable ( & client - > dev ) ;
atlas_set_powermode ( data , 0 ) ;
unregister_buffer :
iio_triggered_buffer_cleanup ( indio_dev ) ;
unregister_trigger :
iio_trigger_unregister ( data - > trig ) ;
return ret ;
}
static int atlas_remove ( struct i2c_client * client )
{
struct iio_dev * indio_dev = i2c_get_clientdata ( client ) ;
struct atlas_data * data = iio_priv ( indio_dev ) ;
iio_device_unregister ( indio_dev ) ;
iio_triggered_buffer_cleanup ( indio_dev ) ;
iio_trigger_unregister ( data - > trig ) ;
pm_runtime_disable ( & client - > dev ) ;
pm_runtime_set_suspended ( & client - > dev ) ;
return atlas_set_powermode ( data , 0 ) ;
}
# ifdef CONFIG_PM
static int atlas_runtime_suspend ( struct device * dev )
{
struct atlas_data * data =
iio_priv ( i2c_get_clientdata ( to_i2c_client ( dev ) ) ) ;
return atlas_set_powermode ( data , 0 ) ;
}
static int atlas_runtime_resume ( struct device * dev )
{
struct atlas_data * data =
iio_priv ( i2c_get_clientdata ( to_i2c_client ( dev ) ) ) ;
return atlas_set_powermode ( data , 1 ) ;
}
# endif
static const struct dev_pm_ops atlas_pm_ops = {
SET_RUNTIME_PM_OPS ( atlas_runtime_suspend ,
atlas_runtime_resume , NULL )
} ;
static struct i2c_driver atlas_driver = {
. driver = {
. name = ATLAS_DRV_NAME ,
2020-09-10 20:32:29 +03:00
. of_match_table = atlas_dt_ids ,
2016-01-27 05:34:31 +03:00
. pm = & atlas_pm_ops ,
} ,
. probe = atlas_probe ,
. remove = atlas_remove ,
. id_table = atlas_id ,
} ;
module_i2c_driver ( atlas_driver ) ;
2018-02-18 08:36:46 +03:00
MODULE_AUTHOR ( " Matt Ranostay <matt.ranostay@konsulko.com> " ) ;
2019-12-16 03:00:45 +03:00
MODULE_DESCRIPTION ( " Atlas Scientific SM sensors " ) ;
2016-01-27 05:34:31 +03:00
MODULE_LICENSE ( " GPL " ) ;