2016-04-05 09:46:19 +02:00
/*
* AD5592R Digital < - > Analog converters driver
*
* Copyright 2014 - 2016 Analog Devices Inc .
* Author : Paul Cercueil < paul . cercueil @ analog . com >
*
* Licensed under the GPL - 2.
*/
# include <linux/bitops.h>
# include <linux/delay.h>
# include <linux/iio/iio.h>
# include <linux/module.h>
# include <linux/mutex.h>
# include <linux/of.h>
# include <linux/regulator/consumer.h>
# include <linux/gpio/consumer.h>
# include <linux/gpio/driver.h>
# include <linux/gpio.h>
# include <linux/property.h>
# include <dt-bindings/iio/adi,ad5592r.h>
# include "ad5592r-base.h"
static int ad5592r_gpio_get ( struct gpio_chip * chip , unsigned offset )
{
struct ad5592r_state * st = gpiochip_get_data ( chip ) ;
int ret = 0 ;
u8 val ;
mutex_lock ( & st - > gpio_lock ) ;
if ( st - > gpio_out & BIT ( offset ) )
val = st - > gpio_val ;
else
ret = st - > ops - > gpio_read ( st , & val ) ;
mutex_unlock ( & st - > gpio_lock ) ;
if ( ret < 0 )
return ret ;
return ! ! ( val & BIT ( offset ) ) ;
}
static void ad5592r_gpio_set ( struct gpio_chip * chip , unsigned offset , int value )
{
struct ad5592r_state * st = gpiochip_get_data ( chip ) ;
mutex_lock ( & st - > gpio_lock ) ;
if ( value )
st - > gpio_val | = BIT ( offset ) ;
else
st - > gpio_val & = ~ BIT ( offset ) ;
st - > ops - > reg_write ( st , AD5592R_REG_GPIO_SET , st - > gpio_val ) ;
mutex_unlock ( & st - > gpio_lock ) ;
}
static int ad5592r_gpio_direction_input ( struct gpio_chip * chip , unsigned offset )
{
struct ad5592r_state * st = gpiochip_get_data ( chip ) ;
int ret ;
mutex_lock ( & st - > gpio_lock ) ;
st - > gpio_out & = ~ BIT ( offset ) ;
st - > gpio_in | = BIT ( offset ) ;
ret = st - > ops - > reg_write ( st , AD5592R_REG_GPIO_OUT_EN , st - > gpio_out ) ;
if ( ret < 0 )
goto err_unlock ;
ret = st - > ops - > reg_write ( st , AD5592R_REG_GPIO_IN_EN , st - > gpio_in ) ;
err_unlock :
mutex_unlock ( & st - > gpio_lock ) ;
return ret ;
}
static int ad5592r_gpio_direction_output ( struct gpio_chip * chip ,
unsigned offset , int value )
{
struct ad5592r_state * st = gpiochip_get_data ( chip ) ;
int ret ;
mutex_lock ( & st - > gpio_lock ) ;
if ( value )
st - > gpio_val | = BIT ( offset ) ;
else
st - > gpio_val & = ~ BIT ( offset ) ;
st - > gpio_in & = ~ BIT ( offset ) ;
st - > gpio_out | = BIT ( offset ) ;
ret = st - > ops - > reg_write ( st , AD5592R_REG_GPIO_SET , st - > gpio_val ) ;
if ( ret < 0 )
goto err_unlock ;
ret = st - > ops - > reg_write ( st , AD5592R_REG_GPIO_OUT_EN , st - > gpio_out ) ;
if ( ret < 0 )
goto err_unlock ;
ret = st - > ops - > reg_write ( st , AD5592R_REG_GPIO_IN_EN , st - > gpio_in ) ;
err_unlock :
mutex_unlock ( & st - > gpio_lock ) ;
return ret ;
}
static int ad5592r_gpio_request ( struct gpio_chip * chip , unsigned offset )
{
struct ad5592r_state * st = gpiochip_get_data ( chip ) ;
if ( ! ( st - > gpio_map & BIT ( offset ) ) ) {
dev_err ( st - > dev , " GPIO %d is reserved by alternate function \n " ,
offset ) ;
return - ENODEV ;
}
return 0 ;
}
static int ad5592r_gpio_init ( struct ad5592r_state * st )
{
if ( ! st - > gpio_map )
return 0 ;
st - > gpiochip . label = dev_name ( st - > dev ) ;
st - > gpiochip . base = - 1 ;
st - > gpiochip . ngpio = 8 ;
st - > gpiochip . parent = st - > dev ;
st - > gpiochip . can_sleep = true ;
st - > gpiochip . direction_input = ad5592r_gpio_direction_input ;
st - > gpiochip . direction_output = ad5592r_gpio_direction_output ;
st - > gpiochip . get = ad5592r_gpio_get ;
st - > gpiochip . set = ad5592r_gpio_set ;
st - > gpiochip . request = ad5592r_gpio_request ;
st - > gpiochip . owner = THIS_MODULE ;
mutex_init ( & st - > gpio_lock ) ;
return gpiochip_add_data ( & st - > gpiochip , st ) ;
}
static void ad5592r_gpio_cleanup ( struct ad5592r_state * st )
{
if ( st - > gpio_map )
gpiochip_remove ( & st - > gpiochip ) ;
}
static int ad5592r_reset ( struct ad5592r_state * st )
{
struct gpio_desc * gpio ;
struct iio_dev * iio_dev = iio_priv_to_dev ( st ) ;
gpio = devm_gpiod_get_optional ( st - > dev , " reset " , GPIOD_OUT_LOW ) ;
if ( IS_ERR ( gpio ) )
return PTR_ERR ( gpio ) ;
if ( gpio ) {
udelay ( 1 ) ;
gpiod_set_value ( gpio , 1 ) ;
} else {
mutex_lock ( & iio_dev - > mlock ) ;
/* Writing this magic value resets the device */
st - > ops - > reg_write ( st , AD5592R_REG_RESET , 0xdac ) ;
mutex_unlock ( & iio_dev - > mlock ) ;
}
udelay ( 250 ) ;
return 0 ;
}
static int ad5592r_get_vref ( struct ad5592r_state * st )
{
int ret ;
if ( st - > reg ) {
ret = regulator_get_voltage ( st - > reg ) ;
if ( ret < 0 )
return ret ;
return ret / 1000 ;
} else {
return 2500 ;
}
}
static int ad5592r_set_channel_modes ( struct ad5592r_state * st )
{
const struct ad5592r_rw_ops * ops = st - > ops ;
int ret ;
unsigned i ;
struct iio_dev * iio_dev = iio_priv_to_dev ( st ) ;
u8 pulldown = 0 , tristate = 0 , dac = 0 , adc = 0 ;
u16 read_back ;
for ( i = 0 ; i < st - > num_channels ; i + + ) {
switch ( st - > channel_modes [ i ] ) {
case CH_MODE_DAC :
dac | = BIT ( i ) ;
break ;
case CH_MODE_ADC :
adc | = BIT ( i ) ;
break ;
case CH_MODE_DAC_AND_ADC :
dac | = BIT ( i ) ;
adc | = BIT ( i ) ;
break ;
case CH_MODE_GPIO :
st - > gpio_map | = BIT ( i ) ;
st - > gpio_in | = BIT ( i ) ; /* Default to input */
break ;
case CH_MODE_UNUSED :
/* fall-through */
default :
switch ( st - > channel_offstate [ i ] ) {
case CH_OFFSTATE_OUT_TRISTATE :
tristate | = BIT ( i ) ;
break ;
case CH_OFFSTATE_OUT_LOW :
st - > gpio_out | = BIT ( i ) ;
break ;
case CH_OFFSTATE_OUT_HIGH :
st - > gpio_out | = BIT ( i ) ;
st - > gpio_val | = BIT ( i ) ;
break ;
case CH_OFFSTATE_PULLDOWN :
/* fall-through */
default :
pulldown | = BIT ( i ) ;
break ;
}
}
}
mutex_lock ( & iio_dev - > mlock ) ;
/* Pull down unused pins to GND */
ret = ops - > reg_write ( st , AD5592R_REG_PULLDOWN , pulldown ) ;
if ( ret )
goto err_unlock ;
ret = ops - > reg_write ( st , AD5592R_REG_TRISTATE , tristate ) ;
if ( ret )
goto err_unlock ;
/* Configure pins that we use */
ret = ops - > reg_write ( st , AD5592R_REG_DAC_EN , dac ) ;
if ( ret )
goto err_unlock ;
ret = ops - > reg_write ( st , AD5592R_REG_ADC_EN , adc ) ;
if ( ret )
goto err_unlock ;
ret = ops - > reg_write ( st , AD5592R_REG_GPIO_SET , st - > gpio_val ) ;
if ( ret )
goto err_unlock ;
ret = ops - > reg_write ( st , AD5592R_REG_GPIO_OUT_EN , st - > gpio_out ) ;
if ( ret )
goto err_unlock ;
ret = ops - > reg_write ( st , AD5592R_REG_GPIO_IN_EN , st - > gpio_in ) ;
if ( ret )
goto err_unlock ;
/* Verify that we can read back at least one register */
ret = ops - > reg_read ( st , AD5592R_REG_ADC_EN , & read_back ) ;
if ( ! ret & & ( read_back & 0xff ) ! = adc )
ret = - EIO ;
err_unlock :
mutex_unlock ( & iio_dev - > mlock ) ;
return ret ;
}
static int ad5592r_reset_channel_modes ( struct ad5592r_state * st )
{
int i ;
for ( i = 0 ; i < ARRAY_SIZE ( st - > channel_modes ) ; i + + )
st - > channel_modes [ i ] = CH_MODE_UNUSED ;
return ad5592r_set_channel_modes ( st ) ;
}
static int ad5592r_write_raw ( struct iio_dev * iio_dev ,
struct iio_chan_spec const * chan , int val , int val2 , long mask )
{
struct ad5592r_state * st = iio_priv ( iio_dev ) ;
int ret ;
switch ( mask ) {
case IIO_CHAN_INFO_RAW :
if ( val > = ( 1 < < chan - > scan_type . realbits ) | | val < 0 )
return - EINVAL ;
if ( ! chan - > output )
return - EINVAL ;
mutex_lock ( & iio_dev - > mlock ) ;
ret = st - > ops - > write_dac ( st , chan - > channel , val ) ;
if ( ! ret )
st - > cached_dac [ chan - > channel ] = val ;
mutex_unlock ( & iio_dev - > mlock ) ;
return ret ;
case IIO_CHAN_INFO_SCALE :
if ( chan - > type = = IIO_VOLTAGE ) {
bool gain ;
if ( val = = st - > scale_avail [ 0 ] [ 0 ] & &
val2 = = st - > scale_avail [ 0 ] [ 1 ] )
gain = false ;
else if ( val = = st - > scale_avail [ 1 ] [ 0 ] & &
val2 = = st - > scale_avail [ 1 ] [ 1 ] )
gain = true ;
else
return - EINVAL ;
mutex_lock ( & iio_dev - > mlock ) ;
ret = st - > ops - > reg_read ( st , AD5592R_REG_CTRL ,
& st - > cached_gp_ctrl ) ;
if ( ret < 0 ) {
mutex_unlock ( & iio_dev - > mlock ) ;
return ret ;
}
if ( chan - > output ) {
if ( gain )
st - > cached_gp_ctrl | =
AD5592R_REG_CTRL_DAC_RANGE ;
else
st - > cached_gp_ctrl & =
~ AD5592R_REG_CTRL_DAC_RANGE ;
} else {
if ( gain )
st - > cached_gp_ctrl | =
AD5592R_REG_CTRL_ADC_RANGE ;
else
st - > cached_gp_ctrl & =
~ AD5592R_REG_CTRL_ADC_RANGE ;
}
ret = st - > ops - > reg_write ( st , AD5592R_REG_CTRL ,
st - > cached_gp_ctrl ) ;
mutex_unlock ( & iio_dev - > mlock ) ;
return ret ;
}
break ;
default :
return - EINVAL ;
}
return 0 ;
}
static int ad5592r_read_raw ( struct iio_dev * iio_dev ,
struct iio_chan_spec const * chan ,
int * val , int * val2 , long m )
{
struct ad5592r_state * st = iio_priv ( iio_dev ) ;
u16 read_val ;
int ret ;
switch ( m ) {
case IIO_CHAN_INFO_RAW :
mutex_lock ( & iio_dev - > mlock ) ;
if ( ! chan - > output ) {
ret = st - > ops - > read_adc ( st , chan - > channel , & read_val ) ;
if ( ret )
goto unlock ;
if ( ( read_val > > 12 & 0x7 ) ! = ( chan - > channel & 0x7 ) ) {
dev_err ( st - > dev , " Error while reading channel %u \n " ,
chan - > channel ) ;
ret = - EIO ;
goto unlock ;
}
read_val & = GENMASK ( 11 , 0 ) ;
} else {
read_val = st - > cached_dac [ chan - > channel ] ;
}
dev_dbg ( st - > dev , " Channel %u read: 0x%04hX \n " ,
chan - > channel , read_val ) ;
* val = ( int ) read_val ;
ret = IIO_VAL_INT ;
break ;
case IIO_CHAN_INFO_SCALE :
* val = ad5592r_get_vref ( st ) ;
if ( chan - > type = = IIO_TEMP ) {
s64 tmp = * val * ( 3767897513LL / 25LL ) ;
* val = div_s64_rem ( tmp , 1000000000LL , val2 ) ;
ret = IIO_VAL_INT_PLUS_MICRO ;
} else {
int mult ;
mutex_lock ( & iio_dev - > mlock ) ;
if ( chan - > output )
mult = ! ! ( st - > cached_gp_ctrl &
AD5592R_REG_CTRL_DAC_RANGE ) ;
else
mult = ! ! ( st - > cached_gp_ctrl &
AD5592R_REG_CTRL_ADC_RANGE ) ;
* val * = + + mult ;
* val2 = chan - > scan_type . realbits ;
ret = IIO_VAL_FRACTIONAL_LOG2 ;
}
break ;
case IIO_CHAN_INFO_OFFSET :
ret = ad5592r_get_vref ( st ) ;
mutex_lock ( & iio_dev - > mlock ) ;
if ( st - > cached_gp_ctrl & AD5592R_REG_CTRL_ADC_RANGE )
* val = ( - 34365 * 25 ) / ret ;
else
* val = ( - 75365 * 25 ) / ret ;
ret = IIO_VAL_INT ;
break ;
default :
ret = - EINVAL ;
}
unlock :
mutex_unlock ( & iio_dev - > mlock ) ;
return ret ;
}
static int ad5592r_write_raw_get_fmt ( struct iio_dev * indio_dev ,
struct iio_chan_spec const * chan , long mask )
{
switch ( mask ) {
case IIO_CHAN_INFO_SCALE :
return IIO_VAL_INT_PLUS_NANO ;
default :
return IIO_VAL_INT_PLUS_MICRO ;
}
return - EINVAL ;
}
static const struct iio_info ad5592r_info = {
. read_raw = ad5592r_read_raw ,
. write_raw = ad5592r_write_raw ,
. write_raw_get_fmt = ad5592r_write_raw_get_fmt ,
. driver_module = THIS_MODULE ,
} ;
static ssize_t ad5592r_show_scale_available ( struct iio_dev * iio_dev ,
uintptr_t private ,
const struct iio_chan_spec * chan ,
char * buf )
{
struct ad5592r_state * st = iio_priv ( iio_dev ) ;
return sprintf ( buf , " %d.%09u %d.%09u \n " ,
st - > scale_avail [ 0 ] [ 0 ] , st - > scale_avail [ 0 ] [ 1 ] ,
st - > scale_avail [ 1 ] [ 0 ] , st - > scale_avail [ 1 ] [ 1 ] ) ;
}
static struct iio_chan_spec_ext_info ad5592r_ext_info [ ] = {
{
. name = " scale_available " ,
. read = ad5592r_show_scale_available ,
. shared = true ,
} ,
{ } ,
} ;
static void ad5592r_setup_channel ( struct iio_dev * iio_dev ,
struct iio_chan_spec * chan , bool output , unsigned id )
{
chan - > type = IIO_VOLTAGE ;
chan - > indexed = 1 ;
chan - > output = output ;
chan - > channel = id ;
chan - > info_mask_separate = BIT ( IIO_CHAN_INFO_RAW ) ;
chan - > info_mask_shared_by_type = BIT ( IIO_CHAN_INFO_SCALE ) ;
chan - > scan_type . sign = ' u ' ;
chan - > scan_type . realbits = 12 ;
chan - > scan_type . storagebits = 16 ;
chan - > ext_info = ad5592r_ext_info ;
}
static int ad5592r_alloc_channels ( struct ad5592r_state * st )
{
unsigned i , curr_channel = 0 ,
num_channels = st - > num_channels ;
struct iio_dev * iio_dev = iio_priv_to_dev ( st ) ;
struct iio_chan_spec * channels ;
struct fwnode_handle * child ;
u32 reg , tmp ;
int ret ;
device_for_each_child_node ( st - > dev , child ) {
ret = fwnode_property_read_u32 ( child , " reg " , & reg ) ;
2016-04-29 12:03:31 +03:00
if ( ret | | reg > = ARRAY_SIZE ( st - > channel_modes ) )
2016-04-05 09:46:19 +02:00
continue ;
ret = fwnode_property_read_u32 ( child , " adi,mode " , & tmp ) ;
if ( ! ret )
st - > channel_modes [ reg ] = tmp ;
fwnode_property_read_u32 ( child , " adi,off-state " , & tmp ) ;
if ( ! ret )
st - > channel_offstate [ reg ] = tmp ;
}
channels = devm_kzalloc ( st - > dev ,
( 1 + 2 * num_channels ) * sizeof ( * channels ) , GFP_KERNEL ) ;
if ( ! channels )
return - ENOMEM ;
for ( i = 0 ; i < num_channels ; i + + ) {
switch ( st - > channel_modes [ i ] ) {
case CH_MODE_DAC :
ad5592r_setup_channel ( iio_dev , & channels [ curr_channel ] ,
true , i ) ;
curr_channel + + ;
break ;
case CH_MODE_ADC :
ad5592r_setup_channel ( iio_dev , & channels [ curr_channel ] ,
false , i ) ;
curr_channel + + ;
break ;
case CH_MODE_DAC_AND_ADC :
ad5592r_setup_channel ( iio_dev , & channels [ curr_channel ] ,
true , i ) ;
curr_channel + + ;
ad5592r_setup_channel ( iio_dev , & channels [ curr_channel ] ,
false , i ) ;
curr_channel + + ;
break ;
default :
continue ;
}
}
channels [ curr_channel ] . type = IIO_TEMP ;
channels [ curr_channel ] . channel = 8 ;
channels [ curr_channel ] . info_mask_separate = BIT ( IIO_CHAN_INFO_RAW ) |
BIT ( IIO_CHAN_INFO_SCALE ) |
BIT ( IIO_CHAN_INFO_OFFSET ) ;
curr_channel + + ;
iio_dev - > num_channels = curr_channel ;
iio_dev - > channels = channels ;
return 0 ;
}
static void ad5592r_init_scales ( struct ad5592r_state * st , int vref_mV )
{
s64 tmp = ( s64 ) vref_mV * 1000000000LL > > 12 ;
st - > scale_avail [ 0 ] [ 0 ] =
div_s64_rem ( tmp , 1000000000LL , & st - > scale_avail [ 0 ] [ 1 ] ) ;
st - > scale_avail [ 1 ] [ 0 ] =
div_s64_rem ( tmp * 2 , 1000000000LL , & st - > scale_avail [ 1 ] [ 1 ] ) ;
}
int ad5592r_probe ( struct device * dev , const char * name ,
const struct ad5592r_rw_ops * ops )
{
struct iio_dev * iio_dev ;
struct ad5592r_state * st ;
int ret ;
iio_dev = devm_iio_device_alloc ( dev , sizeof ( * st ) ) ;
if ( ! iio_dev )
return - ENOMEM ;
st = iio_priv ( iio_dev ) ;
st - > dev = dev ;
st - > ops = ops ;
st - > num_channels = 8 ;
dev_set_drvdata ( dev , iio_dev ) ;
st - > reg = devm_regulator_get_optional ( dev , " vref " ) ;
if ( IS_ERR ( st - > reg ) ) {
if ( ( PTR_ERR ( st - > reg ) ! = - ENODEV ) & & dev - > of_node )
return PTR_ERR ( st - > reg ) ;
st - > reg = NULL ;
} else {
ret = regulator_enable ( st - > reg ) ;
if ( ret )
return ret ;
}
iio_dev - > dev . parent = dev ;
iio_dev - > name = name ;
iio_dev - > info = & ad5592r_info ;
iio_dev - > modes = INDIO_DIRECT_MODE ;
ad5592r_init_scales ( st , ad5592r_get_vref ( st ) ) ;
ret = ad5592r_reset ( st ) ;
if ( ret )
goto error_disable_reg ;
ret = ops - > reg_write ( st , AD5592R_REG_PD ,
( st - > reg = = NULL ) ? AD5592R_REG_PD_EN_REF : 0 ) ;
if ( ret )
goto error_disable_reg ;
ret = ad5592r_alloc_channels ( st ) ;
if ( ret )
goto error_disable_reg ;
ret = ad5592r_set_channel_modes ( st ) ;
if ( ret )
goto error_reset_ch_modes ;
ret = iio_device_register ( iio_dev ) ;
if ( ret )
goto error_reset_ch_modes ;
ret = ad5592r_gpio_init ( st ) ;
if ( ret )
goto error_dev_unregister ;
return 0 ;
error_dev_unregister :
iio_device_unregister ( iio_dev ) ;
error_reset_ch_modes :
ad5592r_reset_channel_modes ( st ) ;
error_disable_reg :
if ( st - > reg )
regulator_disable ( st - > reg ) ;
return ret ;
}
EXPORT_SYMBOL_GPL ( ad5592r_probe ) ;
int ad5592r_remove ( struct device * dev )
{
struct iio_dev * iio_dev = dev_get_drvdata ( dev ) ;
struct ad5592r_state * st = iio_priv ( iio_dev ) ;
iio_device_unregister ( iio_dev ) ;
ad5592r_reset_channel_modes ( st ) ;
ad5592r_gpio_cleanup ( st ) ;
if ( st - > reg )
regulator_disable ( st - > reg ) ;
return 0 ;
}
EXPORT_SYMBOL_GPL ( ad5592r_remove ) ;
MODULE_AUTHOR ( " Paul Cercueil <paul.cercueil@analog.com> " ) ;
MODULE_DESCRIPTION ( " Analog Devices AD5592R multi-channel converters " ) ;
MODULE_LICENSE ( " GPL v2 " ) ;