2019-02-04 13:48:32 +01:00
// SPDX-License-Identifier: GPL-2.0
/*
* DAC7612 Dual , 12 - Bit Serial input Digital - to - Analog Converter
*
* Copyright 2019 Qtechnology A / S
2020-04-30 15:52:24 +02:00
* 2019 Ricardo Ribalda < ribalda @ kernel . org >
2019-02-04 13:48:32 +01:00
*
* Licensed under the GPL - 2.
*/
# include <linux/kernel.h>
# include <linux/module.h>
# include <linux/spi/spi.h>
# include <linux/gpio/consumer.h>
# include <linux/iio/iio.h>
# define DAC7612_RESOLUTION 12
# define DAC7612_ADDRESS 4
# define DAC7612_START 5
struct dac7612 {
struct spi_device * spi ;
struct gpio_desc * loaddacs ;
uint16_t cache [ 2 ] ;
2020-09-16 12:25:35 +03:00
/*
* Lock to protect the state of the device from potential concurrent
* write accesses from userspace . The write operation requires an
* SPI write , then toggling of a GPIO , so the lock aims to protect
* the sanity of the entire sequence of operation .
*/
struct mutex lock ;
2019-02-04 13:48:32 +01:00
/*
* DMA ( thus cache coherency maintenance ) requires the
* transfer buffers to live in their own cache lines .
*/
uint8_t data [ 2 ] ____cacheline_aligned ;
} ;
static int dac7612_cmd_single ( struct dac7612 * priv , int channel , u16 val )
{
int ret ;
priv - > data [ 0 ] = BIT ( DAC7612_START ) | ( channel < < DAC7612_ADDRESS ) ;
priv - > data [ 0 ] | = val > > 8 ;
priv - > data [ 1 ] = val & 0xff ;
priv - > cache [ channel ] = val ;
ret = spi_write ( priv - > spi , priv - > data , sizeof ( priv - > data ) ) ;
if ( ret )
return ret ;
gpiod_set_value ( priv - > loaddacs , 1 ) ;
gpiod_set_value ( priv - > loaddacs , 0 ) ;
return 0 ;
}
# define dac7612_CHANNEL(chan, name) { \
. type = IIO_VOLTAGE , \
. channel = ( chan ) , \
. indexed = 1 , \
. output = 1 , \
. datasheet_name = name , \
. info_mask_separate = BIT ( IIO_CHAN_INFO_RAW ) , \
. info_mask_shared_by_type = BIT ( IIO_CHAN_INFO_SCALE ) , \
}
static const struct iio_chan_spec dac7612_channels [ ] = {
dac7612_CHANNEL ( 0 , " OUTA " ) ,
dac7612_CHANNEL ( 1 , " OUTB " ) ,
} ;
static int dac7612_read_raw ( struct iio_dev * iio_dev ,
const struct iio_chan_spec * chan ,
int * val , int * val2 , long mask )
{
struct dac7612 * priv ;
switch ( mask ) {
case IIO_CHAN_INFO_RAW :
priv = iio_priv ( iio_dev ) ;
* val = priv - > cache [ chan - > channel ] ;
return IIO_VAL_INT ;
case IIO_CHAN_INFO_SCALE :
* val = 1 ;
return IIO_VAL_INT ;
default :
return - EINVAL ;
}
}
static int dac7612_write_raw ( struct iio_dev * iio_dev ,
const struct iio_chan_spec * chan ,
int val , int val2 , long mask )
{
struct dac7612 * priv = iio_priv ( iio_dev ) ;
int ret ;
if ( mask ! = IIO_CHAN_INFO_RAW )
return - EINVAL ;
if ( ( val > = BIT ( DAC7612_RESOLUTION ) ) | | val < 0 | | val2 )
return - EINVAL ;
if ( val = = priv - > cache [ chan - > channel ] )
return 0 ;
2020-09-16 12:25:35 +03:00
mutex_lock ( & priv - > lock ) ;
2019-02-04 13:48:32 +01:00
ret = dac7612_cmd_single ( priv , chan - > channel , val ) ;
2020-09-16 12:25:35 +03:00
mutex_unlock ( & priv - > lock ) ;
2019-02-04 13:48:32 +01:00
return ret ;
}
static const struct iio_info dac7612_info = {
. read_raw = dac7612_read_raw ,
. write_raw = dac7612_write_raw ,
} ;
static int dac7612_probe ( struct spi_device * spi )
{
struct iio_dev * iio_dev ;
struct dac7612 * priv ;
int i ;
int ret ;
iio_dev = devm_iio_device_alloc ( & spi - > dev , sizeof ( * priv ) ) ;
if ( ! iio_dev )
return - ENOMEM ;
priv = iio_priv ( iio_dev ) ;
/*
* LOADDACS pin can be controlled by the driver or externally .
* When controlled by the driver , the DAC value is updated after
* every write .
* When the driver does not control the PIN , the user or an external
* event can change the value of all DACs by pulsing down the LOADDACs
* pin .
*/
priv - > loaddacs = devm_gpiod_get_optional ( & spi - > dev , " ti,loaddacs " ,
GPIOD_OUT_LOW ) ;
if ( IS_ERR ( priv - > loaddacs ) )
return PTR_ERR ( priv - > loaddacs ) ;
priv - > spi = spi ;
spi_set_drvdata ( spi , iio_dev ) ;
iio_dev - > info = & dac7612_info ;
iio_dev - > modes = INDIO_DIRECT_MODE ;
iio_dev - > channels = dac7612_channels ;
iio_dev - > num_channels = ARRAY_SIZE ( priv - > cache ) ;
iio_dev - > name = spi_get_device_id ( spi ) - > name ;
2020-09-16 12:25:35 +03:00
mutex_init ( & priv - > lock ) ;
2019-02-04 13:48:32 +01:00
for ( i = 0 ; i < ARRAY_SIZE ( priv - > cache ) ; i + + ) {
ret = dac7612_cmd_single ( priv , i , 0 ) ;
if ( ret )
return ret ;
}
return devm_iio_device_register ( & spi - > dev , iio_dev ) ;
}
static const struct spi_device_id dac7612_id [ ] = {
{ " ti-dac7612 " } ,
{ }
} ;
MODULE_DEVICE_TABLE ( spi , dac7612_id ) ;
static const struct of_device_id dac7612_of_match [ ] = {
{ . compatible = " ti,dac7612 " } ,
{ . compatible = " ti,dac7612u " } ,
{ . compatible = " ti,dac7612ub " } ,
{ } ,
} ;
MODULE_DEVICE_TABLE ( of , dac7612_of_match ) ;
static struct spi_driver dac7612_driver = {
. driver = {
. name = " ti-dac7612 " ,
. of_match_table = dac7612_of_match ,
} ,
. probe = dac7612_probe ,
. id_table = dac7612_id ,
} ;
module_spi_driver ( dac7612_driver ) ;
2020-04-30 15:52:24 +02:00
MODULE_AUTHOR ( " Ricardo Ribalda <ribalda@kernel.org> " ) ;
2019-02-04 13:48:32 +01:00
MODULE_DESCRIPTION ( " Texas Instruments DAC7612 DAC driver " ) ;
MODULE_LICENSE ( " GPL v2 " ) ;