2011-04-18 09:40:58 +02:00
/*
2011-12-09 17:12:42 +01:00
* AD5760 , AD5780 , AD5781 , AD5790 , AD5791 Voltage Output Digital to Analog
* Converter
2011-04-18 09:40:58 +02:00
*
* Copyright 2011 Analog Devices Inc .
*
* Licensed under the GPL - 2.
*/
# include <linux/interrupt.h>
# include <linux/fs.h>
# include <linux/device.h>
# include <linux/kernel.h>
# include <linux/spi/spi.h>
# include <linux/slab.h>
# include <linux/sysfs.h>
# include <linux/regulator/consumer.h>
2011-07-03 15:49:50 -04:00
# include <linux/module.h>
2011-04-18 09:40:58 +02:00
2012-04-25 15:54:58 +01:00
# include <linux/iio/iio.h>
# include <linux/iio/sysfs.h>
2012-06-04 11:36:28 +02:00
# include <linux/iio/dac/ad5791.h>
2011-04-18 09:40:58 +02:00
2012-06-04 11:36:27 +02:00
# define AD5791_RES_MASK(x) ((1 << (x)) - 1)
# define AD5791_DAC_MASK AD5791_RES_MASK(20)
# define AD5791_DAC_MSB (1 << 19)
# define AD5791_CMD_READ (1 << 23)
# define AD5791_CMD_WRITE (0 << 23)
# define AD5791_ADDR(addr) ((addr) << 20)
/* Registers */
# define AD5791_ADDR_NOOP 0
# define AD5791_ADDR_DAC0 1
# define AD5791_ADDR_CTRL 2
# define AD5791_ADDR_CLRCODE 3
# define AD5791_ADDR_SW_CTRL 4
/* Control Register */
# define AD5791_CTRL_RBUF (1 << 1)
# define AD5791_CTRL_OPGND (1 << 2)
# define AD5791_CTRL_DACTRI (1 << 3)
# define AD5791_CTRL_BIN2SC (1 << 4)
# define AD5791_CTRL_SDODIS (1 << 5)
# define AD5761_CTRL_LINCOMP(x) ((x) << 6)
# define AD5791_LINCOMP_0_10 0
# define AD5791_LINCOMP_10_12 1
# define AD5791_LINCOMP_12_16 2
# define AD5791_LINCOMP_16_19 3
# define AD5791_LINCOMP_19_20 12
# define AD5780_LINCOMP_0_10 0
# define AD5780_LINCOMP_10_20 12
/* Software Control Register */
# define AD5791_SWCTRL_LDAC (1 << 0)
# define AD5791_SWCTRL_CLR (1 << 1)
# define AD5791_SWCTRL_RESET (1 << 2)
# define AD5791_DAC_PWRDN_6K 0
# define AD5791_DAC_PWRDN_3STATE 1
/**
* struct ad5791_chip_info - chip specific information
* @ get_lin_comp : function pointer to the device specific function
*/
struct ad5791_chip_info {
int ( * get_lin_comp ) ( unsigned int span ) ;
} ;
/**
* struct ad5791_state - driver instance specific data
* @ us : spi_device
* @ reg_vdd : positive supply regulator
* @ reg_vss : negative supply regulator
* @ chip_info : chip model specific constants
* @ vref_mv : actual reference voltage used
* @ vref_neg_mv : voltage of the negative supply
* @ pwr_down_mode current power down mode
*/
struct ad5791_state {
struct spi_device * spi ;
struct regulator * reg_vdd ;
struct regulator * reg_vss ;
const struct ad5791_chip_info * chip_info ;
unsigned short vref_mv ;
unsigned int vref_neg_mv ;
unsigned ctrl ;
unsigned pwr_down_mode ;
bool pwr_down ;
} ;
/**
* ad5791_supported_device_ids :
*/
enum ad5791_supported_device_ids {
ID_AD5760 ,
ID_AD5780 ,
ID_AD5781 ,
ID_AD5791 ,
} ;
2011-04-18 09:40:58 +02:00
static int ad5791_spi_write ( struct spi_device * spi , u8 addr , u32 val )
{
union {
u32 d32 ;
u8 d8 [ 4 ] ;
} data ;
data . d32 = cpu_to_be32 ( AD5791_CMD_WRITE |
AD5791_ADDR ( addr ) |
( val & AD5791_DAC_MASK ) ) ;
return spi_write ( spi , & data . d8 [ 1 ] , 3 ) ;
}
static int ad5791_spi_read ( struct spi_device * spi , u8 addr , u32 * val )
{
union {
u32 d32 ;
u8 d8 [ 4 ] ;
} data [ 3 ] ;
int ret ;
struct spi_transfer xfers [ ] = {
{
. tx_buf = & data [ 0 ] . d8 [ 1 ] ,
. bits_per_word = 8 ,
. len = 3 ,
. cs_change = 1 ,
} , {
. tx_buf = & data [ 1 ] . d8 [ 1 ] ,
. rx_buf = & data [ 2 ] . d8 [ 1 ] ,
. bits_per_word = 8 ,
. len = 3 ,
} ,
} ;
data [ 0 ] . d32 = cpu_to_be32 ( AD5791_CMD_READ |
AD5791_ADDR ( addr ) ) ;
data [ 1 ] . d32 = cpu_to_be32 ( AD5791_ADDR ( AD5791_ADDR_NOOP ) ) ;
2013-01-09 17:31:00 +00:00
ret = spi_sync_transfer ( spi , xfers , ARRAY_SIZE ( xfers ) ) ;
2011-04-18 09:40:58 +02:00
* val = be32_to_cpu ( data [ 2 ] . d32 ) ;
return ret ;
}
2012-06-04 11:36:18 +02:00
static const char * const ad5791_powerdown_modes [ ] = {
" 6kohm_to_gnd " ,
" three_state " ,
2011-09-02 17:25:19 +01:00
} ;
2011-04-18 09:40:58 +02:00
2012-06-04 11:36:18 +02:00
static int ad5791_get_powerdown_mode ( struct iio_dev * indio_dev ,
const struct iio_chan_spec * chan )
2011-04-18 09:40:58 +02:00
{
2011-06-27 13:07:34 +01:00
struct ad5791_state * st = iio_priv ( indio_dev ) ;
2011-04-18 09:40:58 +02:00
2012-06-04 11:36:18 +02:00
return st - > pwr_down_mode ;
2011-04-18 09:40:58 +02:00
}
2012-06-04 11:36:18 +02:00
static int ad5791_set_powerdown_mode ( struct iio_dev * indio_dev ,
const struct iio_chan_spec * chan , unsigned int mode )
2011-04-18 09:40:58 +02:00
{
2011-06-27 13:07:34 +01:00
struct ad5791_state * st = iio_priv ( indio_dev ) ;
2011-04-18 09:40:58 +02:00
2012-06-04 11:36:18 +02:00
st - > pwr_down_mode = mode ;
2011-04-18 09:40:58 +02:00
2012-06-04 11:36:18 +02:00
return 0 ;
2011-04-18 09:40:58 +02:00
}
2012-06-04 11:36:18 +02:00
static const struct iio_enum ad5791_powerdown_mode_enum = {
. items = ad5791_powerdown_modes ,
. num_items = ARRAY_SIZE ( ad5791_powerdown_modes ) ,
. get = ad5791_get_powerdown_mode ,
. set = ad5791_set_powerdown_mode ,
} ;
static ssize_t ad5791_read_dac_powerdown ( struct iio_dev * indio_dev ,
uintptr_t private , const struct iio_chan_spec * chan , char * buf )
2011-04-18 09:40:58 +02:00
{
2011-06-27 13:07:34 +01:00
struct ad5791_state * st = iio_priv ( indio_dev ) ;
2011-04-18 09:40:58 +02:00
return sprintf ( buf , " %d \n " , st - > pwr_down ) ;
}
2012-06-04 11:36:18 +02:00
static ssize_t ad5791_write_dac_powerdown ( struct iio_dev * indio_dev ,
uintptr_t private , const struct iio_chan_spec * chan , const char * buf ,
size_t len )
2011-04-18 09:40:58 +02:00
{
2012-06-04 11:36:24 +02:00
bool pwr_down ;
2011-04-18 09:40:58 +02:00
int ret ;
2011-06-27 13:07:34 +01:00
struct ad5791_state * st = iio_priv ( indio_dev ) ;
2011-04-18 09:40:58 +02:00
2012-06-04 11:36:24 +02:00
ret = strtobool ( buf , & pwr_down ) ;
2011-04-18 09:40:58 +02:00
if ( ret )
return ret ;
2012-06-04 11:36:24 +02:00
if ( ! pwr_down ) {
2011-04-18 09:40:58 +02:00
st - > ctrl & = ~ ( AD5791_CTRL_OPGND | AD5791_CTRL_DACTRI ) ;
2012-06-04 11:36:24 +02:00
} else {
2011-04-18 09:40:58 +02:00
if ( st - > pwr_down_mode = = AD5791_DAC_PWRDN_6K )
st - > ctrl | = AD5791_CTRL_OPGND ;
else if ( st - > pwr_down_mode = = AD5791_DAC_PWRDN_3STATE )
st - > ctrl | = AD5791_CTRL_DACTRI ;
2012-06-04 11:36:24 +02:00
}
st - > pwr_down = pwr_down ;
2011-04-18 09:40:58 +02:00
ret = ad5791_spi_write ( st - > spi , AD5791_ADDR_CTRL , st - > ctrl ) ;
return ret ? ret : len ;
}
static int ad5791_get_lin_comp ( unsigned int span )
{
if ( span < = 10000 )
return AD5791_LINCOMP_0_10 ;
else if ( span < = 12000 )
return AD5791_LINCOMP_10_12 ;
else if ( span < = 16000 )
return AD5791_LINCOMP_12_16 ;
else if ( span < = 19000 )
return AD5791_LINCOMP_16_19 ;
else
return AD5791_LINCOMP_19_20 ;
}
2011-04-27 17:13:58 +02:00
static int ad5780_get_lin_comp ( unsigned int span )
{
if ( span < = 10000 )
return AD5780_LINCOMP_0_10 ;
else
return AD5780_LINCOMP_10_20 ;
}
static const struct ad5791_chip_info ad5791_chip_info_tbl [ ] = {
[ ID_AD5760 ] = {
. get_lin_comp = ad5780_get_lin_comp ,
} ,
[ ID_AD5780 ] = {
. get_lin_comp = ad5780_get_lin_comp ,
} ,
[ ID_AD5781 ] = {
. get_lin_comp = ad5791_get_lin_comp ,
} ,
[ ID_AD5791 ] = {
. get_lin_comp = ad5791_get_lin_comp ,
} ,
} ;
2011-09-02 17:25:19 +01:00
static int ad5791_read_raw ( struct iio_dev * indio_dev ,
struct iio_chan_spec const * chan ,
int * val ,
int * val2 ,
long m )
{
struct ad5791_state * st = iio_priv ( indio_dev ) ;
2011-10-19 17:47:50 +02:00
u64 val64 ;
2011-09-02 17:25:19 +01:00
int ret ;
switch ( m ) {
2012-04-15 17:41:19 +01:00
case IIO_CHAN_INFO_RAW :
2011-09-02 17:25:19 +01:00
ret = ad5791_spi_read ( st - > spi , chan - > address , val ) ;
if ( ret )
return ret ;
* val & = AD5791_DAC_MASK ;
* val > > = chan - > scan_type . shift ;
return IIO_VAL_INT ;
2011-10-26 17:41:36 +01:00
case IIO_CHAN_INFO_SCALE :
2011-09-02 17:25:19 +01:00
* val = 0 ;
2011-10-19 17:47:52 +02:00
* val2 = ( ( ( u64 ) st - > vref_mv ) * 1000000ULL ) > > chan - > scan_type . realbits ;
2011-09-02 17:25:19 +01:00
return IIO_VAL_INT_PLUS_MICRO ;
2011-10-26 17:41:36 +01:00
case IIO_CHAN_INFO_OFFSET :
2011-10-19 17:47:50 +02:00
val64 = ( ( ( u64 ) st - > vref_neg_mv ) < < chan - > scan_type . realbits ) ;
do_div ( val64 , st - > vref_mv ) ;
* val = - val64 ;
return IIO_VAL_INT ;
2011-09-02 17:25:19 +01:00
default :
return - EINVAL ;
}
} ;
2012-06-04 11:36:18 +02:00
static const struct iio_chan_spec_ext_info ad5791_ext_info [ ] = {
{
. name = " powerdown " ,
. shared = true ,
. read = ad5791_read_dac_powerdown ,
. write = ad5791_write_dac_powerdown ,
} ,
IIO_ENUM ( " powerdown_mode " , true , & ad5791_powerdown_mode_enum ) ,
IIO_ENUM_AVAILABLE ( " powerdown_mode " , & ad5791_powerdown_mode_enum ) ,
{ } ,
} ;
# define AD5791_CHAN(bits, shift) { \
. type = IIO_VOLTAGE , \
. output = 1 , \
. indexed = 1 , \
. address = AD5791_ADDR_DAC0 , \
. channel = 0 , \
. info_mask = IIO_CHAN_INFO_RAW_SEPARATE_BIT | \
IIO_CHAN_INFO_SCALE_SHARED_BIT | \
IIO_CHAN_INFO_OFFSET_SHARED_BIT , \
. scan_type = IIO_ST ( ' u ' , bits , 24 , shift ) , \
. ext_info = ad5791_ext_info , \
}
static const struct iio_chan_spec ad5791_channels [ ] = {
[ ID_AD5760 ] = AD5791_CHAN ( 16 , 4 ) ,
[ ID_AD5780 ] = AD5791_CHAN ( 18 , 2 ) ,
[ ID_AD5781 ] = AD5791_CHAN ( 18 , 2 ) ,
[ ID_AD5791 ] = AD5791_CHAN ( 20 , 0 )
} ;
2011-09-02 17:25:19 +01:00
static int ad5791_write_raw ( struct iio_dev * indio_dev ,
struct iio_chan_spec const * chan ,
int val ,
int val2 ,
long mask )
{
struct ad5791_state * st = iio_priv ( indio_dev ) ;
switch ( mask ) {
2012-04-15 17:41:19 +01:00
case IIO_CHAN_INFO_RAW :
2011-10-19 17:47:49 +02:00
val & = AD5791_RES_MASK ( chan - > scan_type . realbits ) ;
2011-09-02 17:25:19 +01:00
val < < = chan - > scan_type . shift ;
return ad5791_spi_write ( st - > spi , chan - > address , val ) ;
default :
return - EINVAL ;
}
}
2011-05-18 14:42:37 +01:00
static const struct iio_info ad5791_info = {
2011-09-02 17:25:19 +01:00
. read_raw = & ad5791_read_raw ,
. write_raw = & ad5791_write_raw ,
2011-05-18 14:42:37 +01:00
. driver_module = THIS_MODULE ,
} ;
2012-12-21 13:21:43 -08:00
static int ad5791_probe ( struct spi_device * spi )
2011-04-18 09:40:58 +02:00
{
struct ad5791_platform_data * pdata = spi - > dev . platform_data ;
2011-06-27 13:07:34 +01:00
struct iio_dev * indio_dev ;
2011-04-18 09:40:58 +02:00
struct ad5791_state * st ;
int ret , pos_voltage_uv = 0 , neg_voltage_uv = 0 ;
2012-04-26 13:35:01 +02:00
indio_dev = iio_device_alloc ( sizeof ( * st ) ) ;
2011-08-30 12:41:19 +01:00
if ( indio_dev = = NULL ) {
ret = - ENOMEM ;
goto error_ret ;
}
st = iio_priv ( indio_dev ) ;
st - > reg_vdd = regulator_get ( & spi - > dev , " vdd " ) ;
if ( ! IS_ERR ( st - > reg_vdd ) ) {
ret = regulator_enable ( st - > reg_vdd ) ;
2011-04-18 09:40:58 +02:00
if ( ret )
goto error_put_reg_pos ;
2012-12-14 07:55:00 +00:00
ret = regulator_get_voltage ( st - > reg_vdd ) ;
if ( ret < 0 )
goto error_disable_reg_pos ;
pos_voltage_uv = ret ;
2011-04-18 09:40:58 +02:00
}
2011-08-30 12:41:19 +01:00
st - > reg_vss = regulator_get ( & spi - > dev , " vss " ) ;
if ( ! IS_ERR ( st - > reg_vss ) ) {
ret = regulator_enable ( st - > reg_vss ) ;
2011-04-18 09:40:58 +02:00
if ( ret )
goto error_put_reg_neg ;
2012-12-14 07:55:00 +00:00
ret = regulator_get_voltage ( st - > reg_vss ) ;
if ( ret < 0 )
goto error_disable_reg_neg ;
neg_voltage_uv = ret ;
2011-04-18 09:40:58 +02:00
}
2011-06-27 13:07:34 +01:00
st - > pwr_down = true ;
st - > spi = spi ;
2011-10-19 17:47:50 +02:00
if ( ! IS_ERR ( st - > reg_vss ) & & ! IS_ERR ( st - > reg_vdd ) ) {
st - > vref_mv = ( pos_voltage_uv + neg_voltage_uv ) / 1000 ;
st - > vref_neg_mv = neg_voltage_uv / 1000 ;
} else if ( pdata ) {
st - > vref_mv = pdata - > vref_pos_mv + pdata - > vref_neg_mv ;
st - > vref_neg_mv = pdata - > vref_neg_mv ;
} else {
2011-04-18 09:40:58 +02:00
dev_warn ( & spi - > dev , " reference voltage unspecified \n " ) ;
2011-10-19 17:47:50 +02:00
}
2011-04-18 09:40:58 +02:00
ret = ad5791_spi_write ( spi , AD5791_ADDR_SW_CTRL , AD5791_SWCTRL_RESET ) ;
if ( ret )
2011-08-30 12:41:19 +01:00
goto error_disable_reg_neg ;
2011-04-18 09:40:58 +02:00
2011-09-02 17:25:19 +01:00
st - > chip_info = & ad5791_chip_info_tbl [ spi_get_device_id ( spi )
- > driver_data ] ;
2011-04-18 09:40:58 +02:00
2011-04-27 17:13:58 +02:00
st - > ctrl = AD5761_CTRL_LINCOMP ( st - > chip_info - > get_lin_comp ( st - > vref_mv ) )
| ( ( pdata & & pdata - > use_rbuf_gain2 ) ? 0 : AD5791_CTRL_RBUF ) |
2011-04-18 09:40:58 +02:00
AD5791_CTRL_BIN2SC ;
ret = ad5791_spi_write ( spi , AD5791_ADDR_CTRL , st - > ctrl |
AD5791_CTRL_OPGND | AD5791_CTRL_DACTRI ) ;
if ( ret )
2011-08-30 12:41:19 +01:00
goto error_disable_reg_neg ;
2011-04-18 09:40:58 +02:00
2011-06-27 13:07:34 +01:00
spi_set_drvdata ( spi , indio_dev ) ;
indio_dev - > dev . parent = & spi - > dev ;
indio_dev - > info = & ad5791_info ;
indio_dev - > modes = INDIO_DIRECT_MODE ;
2011-09-02 17:25:19 +01:00
indio_dev - > channels
= & ad5791_channels [ spi_get_device_id ( spi ) - > driver_data ] ;
indio_dev - > num_channels = 1 ;
indio_dev - > name = spi_get_device_id ( st - > spi ) - > name ;
2011-06-27 13:07:34 +01:00
ret = iio_device_register ( indio_dev ) ;
2011-04-18 09:40:58 +02:00
if ( ret )
2011-08-30 12:41:19 +01:00
goto error_disable_reg_neg ;
2011-04-18 09:40:58 +02:00
return 0 ;
error_disable_reg_neg :
2011-08-30 12:41:19 +01:00
if ( ! IS_ERR ( st - > reg_vss ) )
regulator_disable ( st - > reg_vss ) ;
2011-04-18 09:40:58 +02:00
error_put_reg_neg :
2011-08-30 12:41:19 +01:00
if ( ! IS_ERR ( st - > reg_vss ) )
regulator_put ( st - > reg_vss ) ;
2011-04-18 09:40:58 +02:00
2012-12-14 07:55:00 +00:00
error_disable_reg_pos :
2011-08-30 12:41:19 +01:00
if ( ! IS_ERR ( st - > reg_vdd ) )
regulator_disable ( st - > reg_vdd ) ;
2011-04-18 09:40:58 +02:00
error_put_reg_pos :
2011-08-30 12:41:19 +01:00
if ( ! IS_ERR ( st - > reg_vdd ) )
regulator_put ( st - > reg_vdd ) ;
2012-04-26 13:35:01 +02:00
iio_device_free ( indio_dev ) ;
2011-08-30 12:41:19 +01:00
error_ret :
2011-04-18 09:40:58 +02:00
return ret ;
}
2012-12-21 13:21:43 -08:00
static int ad5791_remove ( struct spi_device * spi )
2011-04-18 09:40:58 +02:00
{
2011-06-27 13:07:34 +01:00
struct iio_dev * indio_dev = spi_get_drvdata ( spi ) ;
struct ad5791_state * st = iio_priv ( indio_dev ) ;
2011-04-18 09:40:58 +02:00
2011-10-14 14:46:58 +01:00
iio_device_unregister ( indio_dev ) ;
2011-04-18 09:40:58 +02:00
if ( ! IS_ERR ( st - > reg_vdd ) ) {
2011-08-30 12:41:19 +01:00
regulator_disable ( st - > reg_vdd ) ;
regulator_put ( st - > reg_vdd ) ;
2011-04-18 09:40:58 +02:00
}
if ( ! IS_ERR ( st - > reg_vss ) ) {
2011-08-30 12:41:19 +01:00
regulator_disable ( st - > reg_vss ) ;
regulator_put ( st - > reg_vss ) ;
2011-04-18 09:40:58 +02:00
}
2012-04-26 13:35:01 +02:00
iio_device_free ( indio_dev ) ;
2011-09-02 17:14:40 +01:00
2011-04-18 09:40:58 +02:00
return 0 ;
}
static const struct spi_device_id ad5791_id [ ] = {
2011-04-27 17:13:58 +02:00
{ " ad5760 " , ID_AD5760 } ,
{ " ad5780 " , ID_AD5780 } ,
2011-04-18 09:40:58 +02:00
{ " ad5781 " , ID_AD5781 } ,
2011-12-09 17:12:42 +01:00
{ " ad5790 " , ID_AD5791 } ,
2011-04-27 17:13:58 +02:00
{ " ad5791 " , ID_AD5791 } ,
2011-04-18 09:40:58 +02:00
{ }
} ;
2011-11-16 08:53:31 +01:00
MODULE_DEVICE_TABLE ( spi , ad5791_id ) ;
2011-04-18 09:40:58 +02:00
static struct spi_driver ad5791_driver = {
. driver = {
. name = " ad5791 " ,
. owner = THIS_MODULE ,
} ,
. probe = ad5791_probe ,
2012-12-21 13:21:43 -08:00
. remove = ad5791_remove ,
2011-04-18 09:40:58 +02:00
. id_table = ad5791_id ,
} ;
2011-11-16 10:13:39 +01:00
module_spi_driver ( ad5791_driver ) ;
2011-04-18 09:40:58 +02:00
MODULE_AUTHOR ( " Michael Hennerich <hennerich@blackfin.uclinux.org> " ) ;
2011-12-09 17:12:42 +01:00
MODULE_DESCRIPTION ( " Analog Devices AD5760/AD5780/AD5781/AD5790/AD5791 DAC " ) ;
2011-04-18 09:40:58 +02:00
MODULE_LICENSE ( " GPL v2 " ) ;