2011-04-04 15:39:15 +02:00
/*
* AD5504 , AD5501 High Voltage Digital to Analog Converter
*
* 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-04 15:39:15 +02:00
2012-04-25 15:54:58 +01:00
# include <linux/iio/iio.h>
# include <linux/iio/sysfs.h>
# include <linux/iio/events.h>
2012-06-04 11:36:28 +02:00
# include <linux/iio/dac/ad5504.h>
2011-04-04 15:39:15 +02:00
2012-06-04 11:36:26 +02:00
# define AD5505_BITS 12
# define AD5504_RES_MASK ((1 << (AD5505_BITS)) - 1)
# define AD5504_CMD_READ (1 << 15)
# define AD5504_CMD_WRITE (0 << 15)
# define AD5504_ADDR(addr) ((addr) << 12)
/* Registers */
# define AD5504_ADDR_NOOP 0
# define AD5504_ADDR_DAC(x) ((x) + 1)
# define AD5504_ADDR_ALL_DAC 5
# define AD5504_ADDR_CTRL 7
/* Control Register */
# define AD5504_DAC_PWR(ch) ((ch) << 2)
# define AD5504_DAC_PWRDWN_MODE(mode) ((mode) << 6)
# define AD5504_DAC_PWRDN_20K 0
# define AD5504_DAC_PWRDN_3STATE 1
/**
* struct ad5446_state - driver instance specific data
* @ us : spi_device
* @ reg : supply regulator
* @ vref_mv : actual reference voltage used
* @ pwr_down_mask power down mask
* @ pwr_down_mode current power down mode
*/
struct ad5504_state {
struct spi_device * spi ;
struct regulator * reg ;
unsigned short vref_mv ;
unsigned pwr_down_mask ;
unsigned pwr_down_mode ;
} ;
/**
* ad5504_supported_device_ids :
*/
enum ad5504_supported_device_ids {
ID_AD5504 ,
ID_AD5501 ,
} ;
2011-04-04 15:39:15 +02:00
static int ad5504_spi_write ( struct spi_device * spi , u8 addr , u16 val )
{
u16 tmp = cpu_to_be16 ( AD5504_CMD_WRITE |
AD5504_ADDR ( addr ) |
( val & AD5504_RES_MASK ) ) ;
return spi_write ( spi , ( u8 * ) & tmp , 2 ) ;
}
2011-11-15 16:31:23 +01:00
static int ad5504_spi_read ( struct spi_device * spi , u8 addr )
2011-04-04 15:39:15 +02:00
{
u16 tmp = cpu_to_be16 ( AD5504_CMD_READ | AD5504_ADDR ( addr ) ) ;
2011-11-15 16:31:23 +01:00
u16 val ;
2011-04-04 15:39:15 +02:00
int ret ;
struct spi_transfer t = {
. tx_buf = & tmp ,
2011-11-15 16:31:23 +01:00
. rx_buf = & val ,
2011-04-04 15:39:15 +02:00
. len = 2 ,
} ;
2013-01-09 17:31:00 +00:00
ret = spi_sync_transfer ( spi , & t , 1 ) ;
2011-04-04 15:39:15 +02:00
2011-11-15 16:31:23 +01:00
if ( ret < 0 )
return ret ;
2011-04-04 15:39:15 +02:00
2011-11-15 16:31:23 +01:00
return be16_to_cpu ( val ) & AD5504_RES_MASK ;
2011-04-04 15:39:15 +02:00
}
2011-11-15 16:31:23 +01:00
static int ad5504_read_raw ( struct iio_dev * indio_dev ,
struct iio_chan_spec const * chan ,
int * val ,
int * val2 ,
long m )
2011-04-04 15:39:15 +02:00
{
2011-06-27 13:07:32 +01:00
struct ad5504_state * st = iio_priv ( indio_dev ) ;
2011-11-15 16:31:23 +01:00
unsigned long scale_uv ;
2011-04-04 15:39:15 +02:00
int ret ;
2011-11-15 16:31:23 +01:00
switch ( m ) {
2012-04-15 17:41:19 +01:00
case IIO_CHAN_INFO_RAW :
2011-11-15 16:31:23 +01:00
ret = ad5504_spi_read ( st - > spi , chan - > address ) ;
if ( ret < 0 )
return ret ;
2011-04-04 15:39:15 +02:00
2011-11-15 16:31:23 +01:00
* val = ret ;
return IIO_VAL_INT ;
case IIO_CHAN_INFO_SCALE :
scale_uv = ( st - > vref_mv * 1000 ) > > chan - > scan_type . realbits ;
* val = scale_uv / 1000 ;
* val2 = ( scale_uv % 1000 ) * 1000 ;
return IIO_VAL_INT_PLUS_MICRO ;
}
return - EINVAL ;
2011-04-04 15:39:15 +02:00
}
2011-11-15 16:31:23 +01:00
static int ad5504_write_raw ( struct iio_dev * indio_dev ,
struct iio_chan_spec const * chan ,
int val ,
int val2 ,
long mask )
2011-04-04 15:39:15 +02:00
{
2011-06-27 13:07:32 +01:00
struct ad5504_state * st = iio_priv ( indio_dev ) ;
2011-04-04 15:39:15 +02:00
int ret ;
2011-11-15 16:31:23 +01:00
switch ( mask ) {
2012-04-15 17:41:19 +01:00
case IIO_CHAN_INFO_RAW :
2011-11-15 16:31:23 +01:00
if ( val > = ( 1 < < chan - > scan_type . realbits ) | | val < 0 )
return - EINVAL ;
2011-04-04 15:39:15 +02:00
2011-11-15 16:31:23 +01:00
return ad5504_spi_write ( st - > spi , chan - > address , val ) ;
default :
ret = - EINVAL ;
}
return - EINVAL ;
2011-04-04 15:39:15 +02:00
}
2012-06-04 11:36:15 +02:00
static const char * const ad5504_powerdown_modes [ ] = {
" 20kohm_to_gnd " ,
" three_state " ,
} ;
static int ad5504_get_powerdown_mode ( struct iio_dev * indio_dev ,
const struct iio_chan_spec * chan )
2011-04-04 15:39:15 +02:00
{
2011-06-27 13:07:32 +01:00
struct ad5504_state * st = iio_priv ( indio_dev ) ;
2011-04-04 15:39:15 +02:00
2012-06-04 11:36:15 +02:00
return st - > pwr_down_mode ;
2011-04-04 15:39:15 +02:00
}
2012-06-04 11:36:15 +02:00
static int ad5504_set_powerdown_mode ( struct iio_dev * indio_dev ,
const struct iio_chan_spec * chan , unsigned int mode )
2011-04-04 15:39:15 +02:00
{
2011-06-27 13:07:32 +01:00
struct ad5504_state * st = iio_priv ( indio_dev ) ;
2011-04-04 15:39:15 +02:00
2012-06-04 11:36:15 +02:00
st - > pwr_down_mode = mode ;
2011-04-04 15:39:15 +02:00
2012-06-04 11:36:15 +02:00
return 0 ;
2011-04-04 15:39:15 +02:00
}
2012-06-04 11:36:15 +02:00
static const struct iio_enum ad5504_powerdown_mode_enum = {
. items = ad5504_powerdown_modes ,
. num_items = ARRAY_SIZE ( ad5504_powerdown_modes ) ,
. get = ad5504_get_powerdown_mode ,
. set = ad5504_set_powerdown_mode ,
} ;
static ssize_t ad5504_read_dac_powerdown ( struct iio_dev * indio_dev ,
uintptr_t private , const struct iio_chan_spec * chan , char * buf )
2011-04-04 15:39:15 +02:00
{
2011-06-27 13:07:32 +01:00
struct ad5504_state * st = iio_priv ( indio_dev ) ;
2011-04-04 15:39:15 +02:00
return sprintf ( buf , " %d \n " ,
2012-06-04 11:36:15 +02:00
! ( st - > pwr_down_mask & ( 1 < < chan - > channel ) ) ) ;
2011-04-04 15:39:15 +02:00
}
2012-06-04 11:36:15 +02:00
static ssize_t ad5504_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-04 15:39:15 +02:00
{
2012-06-04 11:36:22 +02:00
bool pwr_down ;
2011-04-04 15:39:15 +02:00
int ret ;
2011-06-27 13:07:32 +01:00
struct ad5504_state * st = iio_priv ( indio_dev ) ;
2011-04-04 15:39:15 +02:00
2012-06-04 11:36:22 +02:00
ret = strtobool ( buf , & pwr_down ) ;
2011-04-04 15:39:15 +02:00
if ( ret )
return ret ;
2012-06-04 11:36:22 +02:00
if ( pwr_down )
2012-06-04 11:36:15 +02:00
st - > pwr_down_mask | = ( 1 < < chan - > channel ) ;
2011-04-04 15:39:15 +02:00
else
2012-06-04 11:36:22 +02:00
st - > pwr_down_mask & = ~ ( 1 < < chan - > channel ) ;
2011-04-04 15:39:15 +02:00
ret = ad5504_spi_write ( st - > spi , AD5504_ADDR_CTRL ,
AD5504_DAC_PWRDWN_MODE ( st - > pwr_down_mode ) |
AD5504_DAC_PWR ( st - > pwr_down_mask ) ) ;
/* writes to the CTRL register must be followed by a NOOP */
ad5504_spi_write ( st - > spi , AD5504_ADDR_NOOP , 0 ) ;
return ret ? ret : len ;
}
static IIO_CONST_ATTR ( temp0_thresh_rising_value , " 110000 " ) ;
static IIO_CONST_ATTR ( temp0_thresh_rising_en , " 1 " ) ;
static struct attribute * ad5504_ev_attributes [ ] = {
& iio_const_attr_temp0_thresh_rising_value . dev_attr . attr ,
& iio_const_attr_temp0_thresh_rising_en . dev_attr . attr ,
NULL ,
} ;
static struct attribute_group ad5504_ev_attribute_group = {
. attrs = ad5504_ev_attributes ,
2011-08-30 12:32:45 +01:00
. name = " events " ,
2011-04-04 15:39:15 +02:00
} ;
2011-05-18 14:41:04 +01:00
static irqreturn_t ad5504_event_handler ( int irq , void * private )
2011-04-04 15:39:15 +02:00
{
2011-08-30 12:41:06 +01:00
iio_push_event ( private ,
2011-08-12 17:56:04 +01:00
IIO_UNMOD_EVENT_CODE ( IIO_TEMP ,
2011-05-18 14:41:04 +01:00
0 ,
IIO_EV_TYPE_THRESH ,
IIO_EV_DIR_RISING ) ,
iio_get_time_ns ( ) ) ;
return IRQ_HANDLED ;
2011-04-04 15:39:15 +02:00
}
2011-05-18 14:42:37 +01:00
static const struct iio_info ad5504_info = {
2011-11-15 16:31:23 +01:00
. write_raw = ad5504_write_raw ,
. read_raw = ad5504_read_raw ,
2011-05-18 14:42:37 +01:00
. event_attrs = & ad5504_ev_attribute_group ,
. driver_module = THIS_MODULE ,
} ;
2012-06-04 11:36:15 +02:00
static const struct iio_chan_spec_ext_info ad5504_ext_info [ ] = {
{
. name = " powerdown " ,
. read = ad5504_read_dac_powerdown ,
. write = ad5504_write_dac_powerdown ,
} ,
IIO_ENUM ( " powerdown_mode " , true , & ad5504_powerdown_mode_enum ) ,
IIO_ENUM_AVAILABLE ( " powerdown_mode " , & ad5504_powerdown_mode_enum ) ,
{ } ,
} ;
# define AD5504_CHANNEL(_chan) { \
. type = IIO_VOLTAGE , \
. indexed = 1 , \
. output = 1 , \
. channel = ( _chan ) , \
. info_mask = IIO_CHAN_INFO_RAW_SEPARATE_BIT | \
IIO_CHAN_INFO_SCALE_SHARED_BIT , \
. address = AD5504_ADDR_DAC ( _chan ) , \
. scan_type = IIO_ST ( ' u ' , 12 , 16 , 0 ) , \
. ext_info = ad5504_ext_info , \
}
static const struct iio_chan_spec ad5504_channels [ ] = {
AD5504_CHANNEL ( 0 ) ,
AD5504_CHANNEL ( 1 ) ,
AD5504_CHANNEL ( 2 ) ,
AD5504_CHANNEL ( 3 ) ,
2011-05-18 14:42:37 +01:00
} ;
2012-12-21 13:21:43 -08:00
static int ad5504_probe ( struct spi_device * spi )
2011-04-04 15:39:15 +02:00
{
struct ad5504_platform_data * pdata = spi - > dev . platform_data ;
2011-06-27 13:07:32 +01:00
struct iio_dev * indio_dev ;
2011-04-04 15:39:15 +02:00
struct ad5504_state * st ;
2011-06-27 13:07:32 +01:00
struct regulator * reg ;
2011-04-04 15:39:15 +02:00
int ret , voltage_uv = 0 ;
2012-04-26 13:35:01 +02:00
indio_dev = iio_device_alloc ( sizeof ( * st ) ) ;
2011-09-02 17:14:40 +01:00
if ( indio_dev = = NULL ) {
ret = - ENOMEM ;
goto error_ret ;
}
2011-06-27 13:07:32 +01:00
reg = regulator_get ( & spi - > dev , " vcc " ) ;
if ( ! IS_ERR ( reg ) ) {
ret = regulator_enable ( reg ) ;
2011-04-04 15:39:15 +02:00
if ( ret )
goto error_put_reg ;
2012-12-14 12:06:00 +00:00
ret = regulator_get_voltage ( reg ) ;
if ( ret < 0 )
goto error_disable_reg ;
voltage_uv = ret ;
2011-04-04 15:39:15 +02:00
}
2011-06-27 13:07:32 +01:00
spi_set_drvdata ( spi , indio_dev ) ;
st = iio_priv ( indio_dev ) ;
2011-04-04 15:39:15 +02:00
if ( voltage_uv )
st - > vref_mv = voltage_uv / 1000 ;
else if ( pdata )
st - > vref_mv = pdata - > vref_mv ;
else
dev_warn ( & spi - > dev , " reference voltage unspecified \n " ) ;
2011-06-27 13:07:32 +01:00
st - > reg = reg ;
2011-04-04 15:39:15 +02:00
st - > spi = spi ;
2011-06-27 13:07:32 +01:00
indio_dev - > dev . parent = & spi - > dev ;
indio_dev - > name = spi_get_device_id ( st - > spi ) - > name ;
2012-06-04 11:36:15 +02:00
indio_dev - > info = & ad5504_info ;
if ( spi_get_device_id ( st - > spi ) - > driver_data = = ID_AD5501 )
2011-11-15 16:31:23 +01:00
indio_dev - > num_channels = 1 ;
2012-06-04 11:36:15 +02:00
else
2011-11-15 16:31:23 +01:00
indio_dev - > num_channels = 4 ;
indio_dev - > channels = ad5504_channels ;
2011-06-27 13:07:32 +01:00
indio_dev - > modes = INDIO_DIRECT_MODE ;
2011-04-04 15:39:15 +02:00
if ( spi - > irq ) {
2011-05-18 14:41:04 +01:00
ret = request_threaded_irq ( spi - > irq ,
NULL ,
& ad5504_event_handler ,
IRQF_TRIGGER_FALLING | IRQF_ONESHOT ,
spi_get_device_id ( st - > spi ) - > name ,
2011-06-27 13:07:32 +01:00
indio_dev ) ;
2011-04-04 15:39:15 +02:00
if ( ret )
2011-09-02 17:14:40 +01:00
goto error_disable_reg ;
2011-04-04 15:39:15 +02:00
}
2011-09-02 17:14:40 +01:00
ret = iio_device_register ( indio_dev ) ;
if ( ret )
goto error_free_irq ;
2011-04-04 15:39:15 +02:00
return 0 ;
2011-09-02 17:14:40 +01:00
error_free_irq :
2012-06-04 11:36:25 +02:00
if ( spi - > irq )
free_irq ( spi - > irq , indio_dev ) ;
2011-04-04 15:39:15 +02:00
error_disable_reg :
2011-06-27 13:07:32 +01:00
if ( ! IS_ERR ( reg ) )
2011-08-12 16:55:33 +01:00
regulator_disable ( reg ) ;
2011-04-04 15:39:15 +02:00
error_put_reg :
2011-06-27 13:07:32 +01:00
if ( ! IS_ERR ( reg ) )
regulator_put ( reg ) ;
2011-04-04 15:39:15 +02:00
2012-04-26 13:35:01 +02:00
iio_device_free ( indio_dev ) ;
2011-09-02 17:14:40 +01:00
error_ret :
2011-04-04 15:39:15 +02:00
return ret ;
}
2012-12-21 13:21:43 -08:00
static int ad5504_remove ( struct spi_device * spi )
2011-04-04 15:39:15 +02:00
{
2011-06-27 13:07:32 +01:00
struct iio_dev * indio_dev = spi_get_drvdata ( spi ) ;
struct ad5504_state * st = iio_priv ( indio_dev ) ;
2011-09-02 17:14:40 +01:00
2011-10-14 14:46:58 +01:00
iio_device_unregister ( indio_dev ) ;
2011-04-04 15:39:15 +02:00
if ( spi - > irq )
2011-06-27 13:07:32 +01:00
free_irq ( spi - > irq , indio_dev ) ;
2011-04-04 15:39:15 +02:00
2011-09-02 17:14:40 +01:00
if ( ! IS_ERR ( st - > reg ) ) {
regulator_disable ( st - > reg ) ;
regulator_put ( st - > reg ) ;
2011-04-04 15:39:15 +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-04 15:39:15 +02:00
return 0 ;
}
static const struct spi_device_id ad5504_id [ ] = {
{ " ad5504 " , ID_AD5504 } ,
{ " ad5501 " , ID_AD5501 } ,
{ }
} ;
2011-11-16 08:53:31 +01:00
MODULE_DEVICE_TABLE ( spi , ad5504_id ) ;
2011-04-04 15:39:15 +02:00
static struct spi_driver ad5504_driver = {
. driver = {
. name = " ad5504 " ,
. owner = THIS_MODULE ,
} ,
. probe = ad5504_probe ,
2012-12-21 13:21:43 -08:00
. remove = ad5504_remove ,
2011-04-04 15:39:15 +02:00
. id_table = ad5504_id ,
} ;
2011-11-16 10:13:39 +01:00
module_spi_driver ( ad5504_driver ) ;
2011-04-04 15:39:15 +02:00
MODULE_AUTHOR ( " Michael Hennerich <hennerich@blackfin.uclinux.org> " ) ;
MODULE_DESCRIPTION ( " Analog Devices AD5501/AD5501 DAC " ) ;
MODULE_LICENSE ( " GPL v2 " ) ;