2018-02-09 08:57:27 +08:00
// SPDX-License-Identifier: GPL-2.0+
/*
* Analog Devices AD5272 digital potentiometer driver
* Copyright ( C ) 2018 Phil Reid < preid @ electromag . com . au >
*
2020-07-04 21:27:43 +02:00
* Datasheet : https : //www.analog.com/media/en/technical-documentation/data-sheets/AD5272_5274.pdf
2018-02-09 08:57:27 +08:00
*
* DEVID # Wipers # Positions Resistor Opts ( kOhm ) i2c address
* ad5272 1 1024 20 , 50 , 100 01011 xx
* ad5274 1 256 20 , 100 01011 xx
*/
# include <linux/delay.h>
# include <linux/gpio/consumer.h>
# include <linux/i2c.h>
# include <linux/iio/iio.h>
# include <linux/module.h>
2020-09-10 18:32:05 +01:00
# include <linux/mod_devicetable.h>
2018-02-09 08:57:27 +08:00
# define AD5272_RDAC_WR 1
# define AD5272_RDAC_RD 2
# define AD5272_RESET 4
# define AD5272_CTL 7
# define AD5272_RDAC_WR_EN BIT(1)
struct ad5272_cfg {
int max_pos ;
int kohms ;
int shift ;
} ;
enum ad5272_type {
AD5272_020 ,
AD5272_050 ,
AD5272_100 ,
AD5274_020 ,
AD5274_100 ,
} ;
static const struct ad5272_cfg ad5272_cfg [ ] = {
[ AD5272_020 ] = { . max_pos = 1024 , . kohms = 20 } ,
[ AD5272_050 ] = { . max_pos = 1024 , . kohms = 50 } ,
[ AD5272_100 ] = { . max_pos = 1024 , . kohms = 100 } ,
[ AD5274_020 ] = { . max_pos = 256 , . kohms = 20 , . shift = 2 } ,
[ AD5274_100 ] = { . max_pos = 256 , . kohms = 100 , . shift = 2 } ,
} ;
struct ad5272_data {
struct i2c_client * client ;
struct mutex lock ;
const struct ad5272_cfg * cfg ;
u8 buf [ 2 ] ____cacheline_aligned ;
} ;
static const struct iio_chan_spec ad5272_channel = {
. type = IIO_RESISTANCE ,
. output = 1 ,
. info_mask_separate = BIT ( IIO_CHAN_INFO_RAW ) ,
. info_mask_shared_by_type = BIT ( IIO_CHAN_INFO_SCALE ) ,
} ;
static int ad5272_write ( struct ad5272_data * data , int reg , int val )
{
int ret ;
data - > buf [ 0 ] = ( reg < < 2 ) | ( ( val > > 8 ) & 0x3 ) ;
data - > buf [ 1 ] = ( u8 ) val ;
mutex_lock ( & data - > lock ) ;
ret = i2c_master_send ( data - > client , data - > buf , sizeof ( data - > buf ) ) ;
mutex_unlock ( & data - > lock ) ;
return ret < 0 ? ret : 0 ;
}
static int ad5272_read ( struct ad5272_data * data , int reg , int * val )
{
int ret ;
data - > buf [ 0 ] = reg < < 2 ;
data - > buf [ 1 ] = 0 ;
mutex_lock ( & data - > lock ) ;
ret = i2c_master_send ( data - > client , data - > buf , sizeof ( data - > buf ) ) ;
if ( ret < 0 )
goto error ;
ret = i2c_master_recv ( data - > client , data - > buf , sizeof ( data - > buf ) ) ;
if ( ret < 0 )
goto error ;
* val = ( ( data - > buf [ 0 ] & 0x3 ) < < 8 ) | data - > buf [ 1 ] ;
ret = 0 ;
error :
mutex_unlock ( & data - > lock ) ;
return ret ;
}
static int ad5272_read_raw ( struct iio_dev * indio_dev ,
struct iio_chan_spec const * chan ,
int * val , int * val2 , long mask )
{
struct ad5272_data * data = iio_priv ( indio_dev ) ;
int ret ;
switch ( mask ) {
case IIO_CHAN_INFO_RAW : {
ret = ad5272_read ( data , AD5272_RDAC_RD , val ) ;
* val = * val > > data - > cfg - > shift ;
return ret ? ret : IIO_VAL_INT ;
}
case IIO_CHAN_INFO_SCALE :
* val = 1000 * data - > cfg - > kohms ;
* val2 = data - > cfg - > max_pos ;
return IIO_VAL_FRACTIONAL ;
}
return - EINVAL ;
}
static int ad5272_write_raw ( struct iio_dev * indio_dev ,
struct iio_chan_spec const * chan ,
int val , int val2 , long mask )
{
struct ad5272_data * data = iio_priv ( indio_dev ) ;
if ( mask ! = IIO_CHAN_INFO_RAW )
return - EINVAL ;
if ( val > = data - > cfg - > max_pos | | val < 0 | | val2 )
return - EINVAL ;
return ad5272_write ( data , AD5272_RDAC_WR , val < < data - > cfg - > shift ) ;
}
static const struct iio_info ad5272_info = {
. read_raw = ad5272_read_raw ,
. write_raw = ad5272_write_raw ,
} ;
static int ad5272_reset ( struct ad5272_data * data )
{
struct gpio_desc * reset_gpio ;
reset_gpio = devm_gpiod_get_optional ( & data - > client - > dev , " reset " ,
2020-11-24 13:00:14 +08:00
GPIOD_OUT_HIGH ) ;
2018-02-09 08:57:27 +08:00
if ( IS_ERR ( reset_gpio ) )
return PTR_ERR ( reset_gpio ) ;
if ( reset_gpio ) {
udelay ( 1 ) ;
2020-11-24 13:00:14 +08:00
gpiod_set_value ( reset_gpio , 0 ) ;
2018-02-09 08:57:27 +08:00
} else {
ad5272_write ( data , AD5272_RESET , 0 ) ;
}
usleep_range ( 1000 , 2000 ) ;
return 0 ;
}
static int ad5272_probe ( struct i2c_client * client ,
const struct i2c_device_id * id )
{
struct device * dev = & client - > dev ;
struct iio_dev * indio_dev ;
struct ad5272_data * data ;
int ret ;
indio_dev = devm_iio_device_alloc ( dev , sizeof ( * data ) ) ;
if ( ! indio_dev )
return - ENOMEM ;
i2c_set_clientdata ( client , indio_dev ) ;
data = iio_priv ( indio_dev ) ;
data - > client = client ;
mutex_init ( & data - > lock ) ;
data - > cfg = & ad5272_cfg [ id - > driver_data ] ;
ret = ad5272_reset ( data ) ;
if ( ret )
return ret ;
ret = ad5272_write ( data , AD5272_CTL , AD5272_RDAC_WR_EN ) ;
if ( ret < 0 )
return - ENODEV ;
indio_dev - > info = & ad5272_info ;
indio_dev - > channels = & ad5272_channel ;
indio_dev - > num_channels = 1 ;
indio_dev - > name = client - > name ;
return devm_iio_device_register ( dev , indio_dev ) ;
}
static const struct of_device_id ad5272_dt_ids [ ] = {
{ . compatible = " adi,ad5272-020 " , . data = ( void * ) AD5272_020 } ,
{ . compatible = " adi,ad5272-050 " , . data = ( void * ) AD5272_050 } ,
{ . compatible = " adi,ad5272-100 " , . data = ( void * ) AD5272_100 } ,
{ . compatible = " adi,ad5274-020 " , . data = ( void * ) AD5274_020 } ,
{ . compatible = " adi,ad5274-100 " , . data = ( void * ) AD5274_100 } ,
{ }
} ;
MODULE_DEVICE_TABLE ( of , ad5272_dt_ids ) ;
static const struct i2c_device_id ad5272_id [ ] = {
{ " ad5272-020 " , AD5272_020 } ,
{ " ad5272-050 " , AD5272_050 } ,
{ " ad5272-100 " , AD5272_100 } ,
{ " ad5274-020 " , AD5274_020 } ,
{ " ad5274-100 " , AD5274_100 } ,
{ }
} ;
MODULE_DEVICE_TABLE ( i2c , ad5272_id ) ;
static struct i2c_driver ad5272_driver = {
. driver = {
. name = " ad5272 " ,
2020-09-10 18:32:05 +01:00
. of_match_table = ad5272_dt_ids ,
2018-02-09 08:57:27 +08:00
} ,
. probe = ad5272_probe ,
. id_table = ad5272_id ,
} ;
module_i2c_driver ( ad5272_driver ) ;
MODULE_AUTHOR ( " Phil Reid <preid@eletromag.com.au> " ) ;
MODULE_DESCRIPTION ( " AD5272 digital potentiometer " ) ;
MODULE_LICENSE ( " GPL v2 " ) ;