2019-12-09 21:32:47 +01:00
// SPDX-License-Identifier: GPL-2.0-only
/*
* ltc2497 - core . c - Common code for Analog Devices / Linear Technology
* LTC2496 and LTC2497 ADCs
*
* Copyright ( C ) 2017 Analog Devices Inc .
*/
# include <linux/delay.h>
# include <linux/iio/iio.h>
# include <linux/iio/driver.h>
# include <linux/module.h>
# include <linux/regulator/consumer.h>
# include "ltc2497.h"
# define LTC2497_SGL BIT(4)
# define LTC2497_DIFF 0
# define LTC2497_SIGN BIT(3)
static int ltc2497core_wait_conv ( struct ltc2497core_driverdata * ddata )
{
s64 time_elapsed ;
time_elapsed = ktime_ms_delta ( ktime_get ( ) , ddata - > time_prev ) ;
if ( time_elapsed < LTC2497_CONVERSION_TIME_MS ) {
/* delay if conversion time not passed
* since last read or write
*/
if ( msleep_interruptible (
LTC2497_CONVERSION_TIME_MS - time_elapsed ) )
return - ERESTARTSYS ;
return 0 ;
}
if ( time_elapsed - LTC2497_CONVERSION_TIME_MS < = 0 ) {
/* We're in automatic mode -
* so the last reading is still not outdated
*/
return 0 ;
}
return 1 ;
}
static int ltc2497core_read ( struct ltc2497core_driverdata * ddata , u8 address , int * val )
{
int ret ;
ret = ltc2497core_wait_conv ( ddata ) ;
if ( ret < 0 )
return ret ;
if ( ret | | ddata - > addr_prev ! = address ) {
ret = ddata - > result_and_measure ( ddata , address , NULL ) ;
if ( ret < 0 )
return ret ;
ddata - > addr_prev = address ;
if ( msleep_interruptible ( LTC2497_CONVERSION_TIME_MS ) )
return - ERESTARTSYS ;
}
ret = ddata - > result_and_measure ( ddata , address , val ) ;
if ( ret < 0 )
return ret ;
ddata - > time_prev = ktime_get ( ) ;
return ret ;
}
static int ltc2497core_read_raw ( struct iio_dev * indio_dev ,
struct iio_chan_spec const * chan ,
int * val , int * val2 , long mask )
{
struct ltc2497core_driverdata * ddata = iio_priv ( indio_dev ) ;
int ret ;
switch ( mask ) {
case IIO_CHAN_INFO_RAW :
mutex_lock ( & indio_dev - > mlock ) ;
ret = ltc2497core_read ( ddata , chan - > address , val ) ;
mutex_unlock ( & indio_dev - > mlock ) ;
if ( ret < 0 )
return ret ;
return IIO_VAL_INT ;
case IIO_CHAN_INFO_SCALE :
ret = regulator_get_voltage ( ddata - > ref ) ;
if ( ret < 0 )
return ret ;
* val = ret / 1000 ;
2022-09-16 17:09:21 +03:00
* val2 = ddata - > chip_info - > resolution + 1 ;
2019-12-09 21:32:47 +01:00
return IIO_VAL_FRACTIONAL_LOG2 ;
default :
return - EINVAL ;
}
}
# define LTC2497_CHAN(_chan, _addr, _ds_name) { \
. type = IIO_VOLTAGE , \
. indexed = 1 , \
. channel = ( _chan ) , \
. address = ( _addr | ( _chan / 2 ) | ( ( _chan & 1 ) ? LTC2497_SIGN : 0 ) ) , \
. info_mask_separate = BIT ( IIO_CHAN_INFO_RAW ) , \
. info_mask_shared_by_type = BIT ( IIO_CHAN_INFO_SCALE ) , \
. datasheet_name = ( _ds_name ) , \
}
# define LTC2497_CHAN_DIFF(_chan, _addr) { \
. type = IIO_VOLTAGE , \
. indexed = 1 , \
. channel = ( _chan ) * 2 + ( ( _addr ) & LTC2497_SIGN ? 1 : 0 ) , \
. channel2 = ( _chan ) * 2 + ( ( _addr ) & LTC2497_SIGN ? 0 : 1 ) , \
. address = ( _addr | _chan ) , \
. info_mask_separate = BIT ( IIO_CHAN_INFO_RAW ) , \
. info_mask_shared_by_type = BIT ( IIO_CHAN_INFO_SCALE ) , \
. differential = 1 , \
}
static const struct iio_chan_spec ltc2497core_channel [ ] = {
LTC2497_CHAN ( 0 , LTC2497_SGL , " CH0 " ) ,
LTC2497_CHAN ( 1 , LTC2497_SGL , " CH1 " ) ,
LTC2497_CHAN ( 2 , LTC2497_SGL , " CH2 " ) ,
LTC2497_CHAN ( 3 , LTC2497_SGL , " CH3 " ) ,
LTC2497_CHAN ( 4 , LTC2497_SGL , " CH4 " ) ,
LTC2497_CHAN ( 5 , LTC2497_SGL , " CH5 " ) ,
LTC2497_CHAN ( 6 , LTC2497_SGL , " CH6 " ) ,
LTC2497_CHAN ( 7 , LTC2497_SGL , " CH7 " ) ,
LTC2497_CHAN ( 8 , LTC2497_SGL , " CH8 " ) ,
LTC2497_CHAN ( 9 , LTC2497_SGL , " CH9 " ) ,
LTC2497_CHAN ( 10 , LTC2497_SGL , " CH10 " ) ,
LTC2497_CHAN ( 11 , LTC2497_SGL , " CH11 " ) ,
LTC2497_CHAN ( 12 , LTC2497_SGL , " CH12 " ) ,
LTC2497_CHAN ( 13 , LTC2497_SGL , " CH13 " ) ,
LTC2497_CHAN ( 14 , LTC2497_SGL , " CH14 " ) ,
LTC2497_CHAN ( 15 , LTC2497_SGL , " CH15 " ) ,
LTC2497_CHAN_DIFF ( 0 , LTC2497_DIFF ) ,
LTC2497_CHAN_DIFF ( 1 , LTC2497_DIFF ) ,
LTC2497_CHAN_DIFF ( 2 , LTC2497_DIFF ) ,
LTC2497_CHAN_DIFF ( 3 , LTC2497_DIFF ) ,
LTC2497_CHAN_DIFF ( 4 , LTC2497_DIFF ) ,
LTC2497_CHAN_DIFF ( 5 , LTC2497_DIFF ) ,
LTC2497_CHAN_DIFF ( 6 , LTC2497_DIFF ) ,
LTC2497_CHAN_DIFF ( 7 , LTC2497_DIFF ) ,
LTC2497_CHAN_DIFF ( 0 , LTC2497_DIFF | LTC2497_SIGN ) ,
LTC2497_CHAN_DIFF ( 1 , LTC2497_DIFF | LTC2497_SIGN ) ,
LTC2497_CHAN_DIFF ( 2 , LTC2497_DIFF | LTC2497_SIGN ) ,
LTC2497_CHAN_DIFF ( 3 , LTC2497_DIFF | LTC2497_SIGN ) ,
LTC2497_CHAN_DIFF ( 4 , LTC2497_DIFF | LTC2497_SIGN ) ,
LTC2497_CHAN_DIFF ( 5 , LTC2497_DIFF | LTC2497_SIGN ) ,
LTC2497_CHAN_DIFF ( 6 , LTC2497_DIFF | LTC2497_SIGN ) ,
LTC2497_CHAN_DIFF ( 7 , LTC2497_DIFF | LTC2497_SIGN ) ,
} ;
static const struct iio_info ltc2497core_info = {
. read_raw = ltc2497core_read_raw ,
} ;
int ltc2497core_probe ( struct device * dev , struct iio_dev * indio_dev )
{
struct ltc2497core_driverdata * ddata = iio_priv ( indio_dev ) ;
int ret ;
2022-09-16 17:09:22 +03:00
/*
* Keep using dev_name ( ) for the iio_dev ' s name on some of the parts ,
* since updating it would result in a ABI breakage .
*/
if ( ddata - > chip_info - > name )
indio_dev - > name = ddata - > chip_info - > name ;
else
indio_dev - > name = dev_name ( dev ) ;
2019-12-09 21:32:47 +01:00
indio_dev - > info = & ltc2497core_info ;
indio_dev - > modes = INDIO_DIRECT_MODE ;
indio_dev - > channels = ltc2497core_channel ;
indio_dev - > num_channels = ARRAY_SIZE ( ltc2497core_channel ) ;
ret = ddata - > result_and_measure ( ddata , LTC2497_CONFIG_DEFAULT , NULL ) ;
if ( ret < 0 )
return ret ;
ddata - > ref = devm_regulator_get ( dev , " vref " ) ;
2020-08-29 08:47:13 +02:00
if ( IS_ERR ( ddata - > ref ) )
return dev_err_probe ( dev , PTR_ERR ( ddata - > ref ) ,
" Failed to get vref regulator \n " ) ;
2019-12-09 21:32:47 +01:00
ret = regulator_enable ( ddata - > ref ) ;
if ( ret < 0 ) {
dev_err ( dev , " Failed to enable vref regulator: %pe \n " ,
ERR_PTR ( ret ) ) ;
return ret ;
}
if ( dev - > platform_data ) {
struct iio_map * plat_data ;
plat_data = ( struct iio_map * ) dev - > platform_data ;
ret = iio_map_array_register ( indio_dev , plat_data ) ;
if ( ret ) {
dev_err ( & indio_dev - > dev , " iio map err: %d \n " , ret ) ;
goto err_regulator_disable ;
}
}
ddata - > addr_prev = LTC2497_CONFIG_DEFAULT ;
ddata - > time_prev = ktime_get ( ) ;
ret = iio_device_register ( indio_dev ) ;
if ( ret < 0 )
goto err_array_unregister ;
return 0 ;
err_array_unregister :
iio_map_array_unregister ( indio_dev ) ;
err_regulator_disable :
regulator_disable ( ddata - > ref ) ;
return ret ;
}
EXPORT_SYMBOL_NS ( ltc2497core_probe , LTC2497 ) ;
void ltc2497core_remove ( struct iio_dev * indio_dev )
{
struct ltc2497core_driverdata * ddata = iio_priv ( indio_dev ) ;
iio_device_unregister ( indio_dev ) ;
iio_map_array_unregister ( indio_dev ) ;
regulator_disable ( ddata - > ref ) ;
}
EXPORT_SYMBOL_NS ( ltc2497core_remove , LTC2497 ) ;
MODULE_DESCRIPTION ( " common code for LTC2496/LTC2497 drivers " ) ;
MODULE_LICENSE ( " GPL v2 " ) ;