2019-05-30 16:18:08 +03:00
// SPDX-License-Identifier: GPL-2.0
2012-05-11 13:36:54 +04:00
/*
2019-05-30 16:18:11 +03:00
* AD8366 and similar Gain Amplifiers
* This driver supports the following gain amplifiers :
* AD8366 Dual - Digital Variable Gain Amplifier ( VGA )
* ADA4961 BiCMOS RF Digital Gain Amplifier ( DGA )
2019-05-30 16:18:12 +03:00
* ADL5240 Digitally controlled variable gain amplifier ( VGA )
2020-02-21 19:18:26 +03:00
* HMC1119 0.25 dB LSB , 7 - Bit , Silicon Digital Attenuator
2012-05-11 13:36:54 +04:00
*
2019-05-30 16:18:08 +03:00
* Copyright 2012 - 2019 Analog Devices Inc .
2012-05-11 13:36:54 +04:00
*/
# include <linux/device.h>
# include <linux/kernel.h>
# include <linux/slab.h>
# include <linux/sysfs.h>
# include <linux/spi/spi.h>
# include <linux/regulator/consumer.h>
2019-05-30 16:18:11 +03:00
# include <linux/gpio/consumer.h>
2012-05-11 13:36:54 +04:00
# include <linux/err.h>
# include <linux/module.h>
# include <linux/bitrev.h>
# include <linux/iio/iio.h>
# include <linux/iio/sysfs.h>
2019-05-30 16:18:10 +03:00
enum ad8366_type {
ID_AD8366 ,
2019-05-30 16:18:11 +03:00
ID_ADA4961 ,
2019-05-30 16:18:12 +03:00
ID_ADL5240 ,
2020-02-21 19:18:26 +03:00
ID_HMC1119 ,
2019-05-30 16:18:10 +03:00
} ;
struct ad8366_info {
int gain_min ;
int gain_max ;
} ;
2012-05-11 13:36:54 +04:00
struct ad8366_state {
struct spi_device * spi ;
struct regulator * reg ;
2019-05-30 16:18:09 +03:00
struct mutex lock ; /* protect sensor state */
2019-05-30 16:18:11 +03:00
struct gpio_desc * reset_gpio ;
2012-05-11 13:36:54 +04:00
unsigned char ch [ 2 ] ;
2019-05-30 16:18:10 +03:00
enum ad8366_type type ;
struct ad8366_info * info ;
2012-05-11 13:36:54 +04:00
/*
* DMA ( thus cache coherency maintenance ) requires the
* transfer buffers to live in their own cache lines .
*/
unsigned char data [ 2 ] ____cacheline_aligned ;
} ;
2019-05-30 16:18:10 +03:00
static struct ad8366_info ad8366_infos [ ] = {
[ ID_AD8366 ] = {
. gain_min = 4500 ,
. gain_max = 20500 ,
} ,
2019-05-30 16:18:11 +03:00
[ ID_ADA4961 ] = {
. gain_min = - 6000 ,
. gain_max = 15000 ,
} ,
2019-05-30 16:18:12 +03:00
[ ID_ADL5240 ] = {
. gain_min = - 11500 ,
. gain_max = 20000 ,
} ,
2020-02-21 19:18:26 +03:00
[ ID_HMC1119 ] = {
. gain_min = - 31750 ,
. gain_max = 0 ,
} ,
2019-05-30 16:18:10 +03:00
} ;
2012-05-11 13:36:54 +04:00
static int ad8366_write ( struct iio_dev * indio_dev ,
2014-12-30 21:45:44 +03:00
unsigned char ch_a , unsigned char ch_b )
2012-05-11 13:36:54 +04:00
{
struct ad8366_state * st = iio_priv ( indio_dev ) ;
int ret ;
2019-05-30 16:18:10 +03:00
switch ( st - > type ) {
case ID_AD8366 :
ch_a = bitrev8 ( ch_a & 0x3F ) ;
ch_b = bitrev8 ( ch_b & 0x3F ) ;
2012-05-11 13:36:54 +04:00
2019-05-30 16:18:10 +03:00
st - > data [ 0 ] = ch_b > > 4 ;
st - > data [ 1 ] = ( ch_b < < 4 ) | ( ch_a > > 2 ) ;
break ;
2019-05-30 16:18:11 +03:00
case ID_ADA4961 :
st - > data [ 0 ] = ch_a & 0x1F ;
break ;
2019-05-30 16:18:12 +03:00
case ID_ADL5240 :
st - > data [ 0 ] = ( ch_a & 0x3F ) ;
break ;
2020-02-21 19:18:26 +03:00
case ID_HMC1119 :
st - > data [ 0 ] = ch_a ;
break ;
2019-05-30 16:18:10 +03:00
}
2012-05-11 13:36:54 +04:00
2019-05-30 16:18:10 +03:00
ret = spi_write ( st - > spi , st - > data , indio_dev - > num_channels ) ;
2012-05-11 13:36:54 +04:00
if ( ret < 0 )
dev_err ( & indio_dev - > dev , " write failed (%d) " , ret ) ;
return ret ;
}
static int ad8366_read_raw ( struct iio_dev * indio_dev ,
struct iio_chan_spec const * chan ,
int * val ,
int * val2 ,
long m )
{
struct ad8366_state * st = iio_priv ( indio_dev ) ;
int ret ;
2019-05-30 16:18:10 +03:00
int code , gain = 0 ;
2012-05-11 13:36:54 +04:00
2019-05-30 16:18:09 +03:00
mutex_lock ( & st - > lock ) ;
2012-05-11 13:36:54 +04:00
switch ( m ) {
case IIO_CHAN_INFO_HARDWAREGAIN :
code = st - > ch [ chan - > channel ] ;
2019-05-30 16:18:10 +03:00
switch ( st - > type ) {
case ID_AD8366 :
gain = code * 253 + 4500 ;
break ;
2019-05-30 16:18:11 +03:00
case ID_ADA4961 :
gain = 15000 - code * 1000 ;
break ;
2019-05-30 16:18:12 +03:00
case ID_ADL5240 :
gain = 20000 - 31500 + code * 500 ;
break ;
2020-02-21 19:18:26 +03:00
case ID_HMC1119 :
gain = - 1 * code * 250 ;
break ;
2019-05-30 16:18:10 +03:00
}
2012-05-11 13:36:54 +04:00
/* Values in dB */
2019-05-30 16:18:10 +03:00
* val = gain / 1000 ;
* val2 = ( gain % 1000 ) * 1000 ;
2012-05-11 13:36:54 +04:00
ret = IIO_VAL_INT_PLUS_MICRO_DB ;
break ;
default :
ret = - EINVAL ;
}
2019-05-30 16:18:09 +03:00
mutex_unlock ( & st - > lock ) ;
2012-05-11 13:36:54 +04:00
return ret ;
} ;
static int ad8366_write_raw ( struct iio_dev * indio_dev ,
struct iio_chan_spec const * chan ,
int val ,
int val2 ,
long mask )
{
struct ad8366_state * st = iio_priv ( indio_dev ) ;
2019-05-30 16:18:10 +03:00
struct ad8366_info * inf = st - > info ;
int code = 0 , gain ;
2012-05-11 13:36:54 +04:00
int ret ;
/* Values in dB */
2019-05-30 16:18:10 +03:00
if ( val < 0 )
gain = ( val * 1000 ) - ( val2 / 1000 ) ;
else
gain = ( val * 1000 ) + ( val2 / 1000 ) ;
2012-05-11 13:36:54 +04:00
2019-05-30 16:18:10 +03:00
if ( gain > inf - > gain_max | | gain < inf - > gain_min )
2012-05-11 13:36:54 +04:00
return - EINVAL ;
2019-05-30 16:18:10 +03:00
switch ( st - > type ) {
case ID_AD8366 :
code = ( gain - 4500 ) / 253 ;
break ;
2019-05-30 16:18:11 +03:00
case ID_ADA4961 :
code = ( 15000 - gain ) / 1000 ;
break ;
2019-05-30 16:18:12 +03:00
case ID_ADL5240 :
code = ( ( gain - 500 - 20000 ) / 500 ) & 0x3F ;
break ;
2020-02-21 19:18:26 +03:00
case ID_HMC1119 :
code = ( abs ( gain ) / 250 ) & 0x7F ;
break ;
2019-05-30 16:18:10 +03:00
}
2012-05-11 13:36:54 +04:00
2019-05-30 16:18:09 +03:00
mutex_lock ( & st - > lock ) ;
2012-05-11 13:36:54 +04:00
switch ( mask ) {
case IIO_CHAN_INFO_HARDWAREGAIN :
st - > ch [ chan - > channel ] = code ;
ret = ad8366_write ( indio_dev , st - > ch [ 0 ] , st - > ch [ 1 ] ) ;
break ;
default :
ret = - EINVAL ;
}
2019-05-30 16:18:09 +03:00
mutex_unlock ( & st - > lock ) ;
2012-05-11 13:36:54 +04:00
return ret ;
}
2020-02-06 18:11:46 +03:00
static int ad8366_write_raw_get_fmt ( struct iio_dev * indio_dev ,
struct iio_chan_spec const * chan ,
long mask )
{
switch ( mask ) {
case IIO_CHAN_INFO_HARDWAREGAIN :
return IIO_VAL_INT_PLUS_MICRO_DB ;
default :
return - EINVAL ;
}
}
2012-05-11 13:36:54 +04:00
static const struct iio_info ad8366_info = {
. read_raw = & ad8366_read_raw ,
. write_raw = & ad8366_write_raw ,
2020-02-06 18:11:46 +03:00
. write_raw_get_fmt = & ad8366_write_raw_get_fmt ,
2012-05-11 13:36:54 +04:00
} ;
# define AD8366_CHAN(_channel) { \
. type = IIO_VOLTAGE , \
. output = 1 , \
. indexed = 1 , \
. channel = _channel , \
2013-02-27 23:08:08 +04:00
. info_mask_separate = BIT ( IIO_CHAN_INFO_HARDWAREGAIN ) , \
2012-05-11 13:36:54 +04:00
}
static const struct iio_chan_spec ad8366_channels [ ] = {
AD8366_CHAN ( 0 ) ,
AD8366_CHAN ( 1 ) ,
} ;
2019-05-30 16:18:11 +03:00
static const struct iio_chan_spec ada4961_channels [ ] = {
AD8366_CHAN ( 0 ) ,
} ;
2012-12-22 01:21:43 +04:00
static int ad8366_probe ( struct spi_device * spi )
2012-05-11 13:36:54 +04:00
{
struct iio_dev * indio_dev ;
struct ad8366_state * st ;
int ret ;
2013-07-30 10:24:00 +04:00
indio_dev = devm_iio_device_alloc ( & spi - > dev , sizeof ( * st ) ) ;
2012-05-11 13:36:54 +04:00
if ( indio_dev = = NULL )
return - ENOMEM ;
st = iio_priv ( indio_dev ) ;
2013-07-30 10:24:00 +04:00
st - > reg = devm_regulator_get ( & spi - > dev , " vcc " ) ;
2012-05-11 13:36:54 +04:00
if ( ! IS_ERR ( st - > reg ) ) {
ret = regulator_enable ( st - > reg ) ;
if ( ret )
2013-07-30 10:24:00 +04:00
return ret ;
2012-05-11 13:36:54 +04:00
}
spi_set_drvdata ( spi , indio_dev ) ;
2019-05-30 16:18:09 +03:00
mutex_init ( & st - > lock ) ;
2012-05-11 13:36:54 +04:00
st - > spi = spi ;
2019-05-30 16:18:10 +03:00
st - > type = spi_get_device_id ( spi ) - > driver_data ;
switch ( st - > type ) {
case ID_AD8366 :
indio_dev - > channels = ad8366_channels ;
indio_dev - > num_channels = ARRAY_SIZE ( ad8366_channels ) ;
break ;
2019-05-30 16:18:11 +03:00
case ID_ADA4961 :
2019-05-30 16:18:12 +03:00
case ID_ADL5240 :
2020-02-21 19:18:26 +03:00
case ID_HMC1119 :
2020-06-03 12:26:10 +03:00
st - > reset_gpio = devm_gpiod_get_optional ( & spi - > dev , " reset " , GPIOD_OUT_HIGH ) ;
if ( IS_ERR ( st - > reset_gpio ) ) {
ret = PTR_ERR ( st - > reset_gpio ) ;
goto error_disable_reg ;
}
2019-05-30 16:18:11 +03:00
indio_dev - > channels = ada4961_channels ;
indio_dev - > num_channels = ARRAY_SIZE ( ada4961_channels ) ;
break ;
2019-05-30 16:18:10 +03:00
default :
dev_err ( & spi - > dev , " Invalid device ID \n " ) ;
ret = - EINVAL ;
goto error_disable_reg ;
}
2012-05-11 13:36:54 +04:00
2019-05-30 16:18:10 +03:00
st - > info = & ad8366_infos [ st - > type ] ;
2012-05-11 13:36:54 +04:00
indio_dev - > name = spi_get_device_id ( spi ) - > name ;
indio_dev - > info = & ad8366_info ;
indio_dev - > modes = INDIO_DIRECT_MODE ;
2018-05-21 09:40:05 +03:00
ret = ad8366_write ( indio_dev , 0 , 0 ) ;
if ( ret < 0 )
goto error_disable_reg ;
2012-05-11 13:36:54 +04:00
ret = iio_device_register ( indio_dev ) ;
if ( ret )
goto error_disable_reg ;
return 0 ;
error_disable_reg :
if ( ! IS_ERR ( st - > reg ) )
regulator_disable ( st - > reg ) ;
return ret ;
}
2012-12-22 01:21:43 +04:00
static int ad8366_remove ( struct spi_device * spi )
2012-05-11 13:36:54 +04:00
{
struct iio_dev * indio_dev = spi_get_drvdata ( spi ) ;
struct ad8366_state * st = iio_priv ( indio_dev ) ;
struct regulator * reg = st - > reg ;
iio_device_unregister ( indio_dev ) ;
2013-09-24 15:49:00 +04:00
if ( ! IS_ERR ( reg ) )
2012-05-11 13:36:54 +04:00
regulator_disable ( reg ) ;
return 0 ;
}
static const struct spi_device_id ad8366_id [ ] = {
2019-05-30 16:18:10 +03:00
{ " ad8366 " , ID_AD8366 } ,
2019-05-30 16:18:11 +03:00
{ " ada4961 " , ID_ADA4961 } ,
2019-05-30 16:18:12 +03:00
{ " adl5240 " , ID_ADL5240 } ,
2020-02-21 19:18:26 +03:00
{ " hmc1119 " , ID_HMC1119 } ,
2012-05-11 13:36:54 +04:00
{ }
} ;
2015-08-20 10:07:14 +03:00
MODULE_DEVICE_TABLE ( spi , ad8366_id ) ;
2012-05-11 13:36:54 +04:00
static struct spi_driver ad8366_driver = {
. driver = {
. name = KBUILD_MODNAME ,
} ,
. probe = ad8366_probe ,
2012-12-22 01:21:43 +04:00
. remove = ad8366_remove ,
2012-05-11 13:36:54 +04:00
. id_table = ad8366_id ,
} ;
module_spi_driver ( ad8366_driver ) ;
2018-08-14 14:23:17 +03:00
MODULE_AUTHOR ( " Michael Hennerich <michael.hennerich@analog.com> " ) ;
2019-05-30 16:18:11 +03:00
MODULE_DESCRIPTION ( " Analog Devices AD8366 and similar Gain Amplifiers " ) ;
2012-05-11 13:36:54 +04:00
MODULE_LICENSE ( " GPL v2 " ) ;