2019-01-11 22:57:06 +03:00
// SPDX-License-Identifier: GPL-2.0
/* TI ADS124S0X chip family driver
* Copyright ( C ) 2018 Texas Instruments Incorporated - http : //www.ti.com/
*/
# include <linux/err.h>
# include <linux/delay.h>
# include <linux/device.h>
# include <linux/kernel.h>
# include <linux/module.h>
# include <linux/of.h>
# include <linux/of_gpio.h>
# include <linux/slab.h>
# include <linux/sysfs.h>
# include <linux/gpio/consumer.h>
# include <linux/spi/spi.h>
# include <linux/iio/iio.h>
# include <linux/iio/buffer.h>
# include <linux/iio/trigger_consumer.h>
# include <linux/iio/triggered_buffer.h>
# include <linux/iio/sysfs.h>
/* Commands */
# define ADS124S08_CMD_NOP 0x00
# define ADS124S08_CMD_WAKEUP 0x02
# define ADS124S08_CMD_PWRDWN 0x04
# define ADS124S08_CMD_RESET 0x06
# define ADS124S08_CMD_START 0x08
# define ADS124S08_CMD_STOP 0x0a
# define ADS124S08_CMD_SYOCAL 0x16
# define ADS124S08_CMD_SYGCAL 0x17
# define ADS124S08_CMD_SFOCAL 0x19
# define ADS124S08_CMD_RDATA 0x12
# define ADS124S08_CMD_RREG 0x20
# define ADS124S08_CMD_WREG 0x40
/* Registers */
# define ADS124S08_ID_REG 0x00
# define ADS124S08_STATUS 0x01
# define ADS124S08_INPUT_MUX 0x02
# define ADS124S08_PGA 0x03
# define ADS124S08_DATA_RATE 0x04
# define ADS124S08_REF 0x05
# define ADS124S08_IDACMAG 0x06
# define ADS124S08_IDACMUX 0x07
# define ADS124S08_VBIAS 0x08
# define ADS124S08_SYS 0x09
# define ADS124S08_OFCAL0 0x0a
# define ADS124S08_OFCAL1 0x0b
# define ADS124S08_OFCAL2 0x0c
# define ADS124S08_FSCAL0 0x0d
# define ADS124S08_FSCAL1 0x0e
# define ADS124S08_FSCAL2 0x0f
# define ADS124S08_GPIODAT 0x10
# define ADS124S08_GPIOCON 0x11
/* ADS124S0x common channels */
# define ADS124S08_AIN0 0x00
# define ADS124S08_AIN1 0x01
# define ADS124S08_AIN2 0x02
# define ADS124S08_AIN3 0x03
# define ADS124S08_AIN4 0x04
# define ADS124S08_AIN5 0x05
# define ADS124S08_AINCOM 0x0c
/* ADS124S08 only channels */
# define ADS124S08_AIN6 0x06
# define ADS124S08_AIN7 0x07
# define ADS124S08_AIN8 0x08
# define ADS124S08_AIN9 0x09
# define ADS124S08_AIN10 0x0a
# define ADS124S08_AIN11 0x0b
# define ADS124S08_MAX_CHANNELS 12
# define ADS124S08_POS_MUX_SHIFT 0x04
# define ADS124S08_INT_REF 0x09
# define ADS124S08_START_REG_MASK 0x1f
# define ADS124S08_NUM_BYTES_MASK 0x1f
# define ADS124S08_START_CONV 0x01
# define ADS124S08_STOP_CONV 0x00
enum ads124s_id {
ADS124S08_ID ,
ADS124S06_ID ,
} ;
struct ads124s_chip_info {
const struct iio_chan_spec * channels ;
unsigned int num_channels ;
} ;
struct ads124s_private {
const struct ads124s_chip_info * chip_info ;
struct gpio_desc * reset_gpio ;
struct spi_device * spi ;
struct mutex lock ;
u8 data [ 5 ] ____cacheline_aligned ;
} ;
# define ADS124S08_CHAN(index) \
{ \
. type = IIO_VOLTAGE , \
. indexed = 1 , \
. channel = index , \
. info_mask_separate = BIT ( IIO_CHAN_INFO_RAW ) , \
. scan_index = index , \
. scan_type = { \
. sign = ' u ' , \
. realbits = 32 , \
. storagebits = 32 , \
} , \
}
static const struct iio_chan_spec ads124s06_channels [ ] = {
ADS124S08_CHAN ( 0 ) ,
ADS124S08_CHAN ( 1 ) ,
ADS124S08_CHAN ( 2 ) ,
ADS124S08_CHAN ( 3 ) ,
ADS124S08_CHAN ( 4 ) ,
ADS124S08_CHAN ( 5 ) ,
} ;
static const struct iio_chan_spec ads124s08_channels [ ] = {
ADS124S08_CHAN ( 0 ) ,
ADS124S08_CHAN ( 1 ) ,
ADS124S08_CHAN ( 2 ) ,
ADS124S08_CHAN ( 3 ) ,
ADS124S08_CHAN ( 4 ) ,
ADS124S08_CHAN ( 5 ) ,
ADS124S08_CHAN ( 6 ) ,
ADS124S08_CHAN ( 7 ) ,
ADS124S08_CHAN ( 8 ) ,
ADS124S08_CHAN ( 9 ) ,
ADS124S08_CHAN ( 10 ) ,
ADS124S08_CHAN ( 11 ) ,
} ;
static const struct ads124s_chip_info ads124s_chip_info_tbl [ ] = {
[ ADS124S08_ID ] = {
. channels = ads124s08_channels ,
. num_channels = ARRAY_SIZE ( ads124s08_channels ) ,
} ,
[ ADS124S06_ID ] = {
. channels = ads124s06_channels ,
. num_channels = ARRAY_SIZE ( ads124s06_channels ) ,
} ,
} ;
static int ads124s_write_cmd ( struct iio_dev * indio_dev , u8 command )
{
struct ads124s_private * priv = iio_priv ( indio_dev ) ;
priv - > data [ 0 ] = command ;
return spi_write ( priv - > spi , & priv - > data [ 0 ] , 1 ) ;
}
static int ads124s_write_reg ( struct iio_dev * indio_dev , u8 reg , u8 data )
{
struct ads124s_private * priv = iio_priv ( indio_dev ) ;
priv - > data [ 0 ] = ADS124S08_CMD_WREG | reg ;
priv - > data [ 1 ] = 0x0 ;
priv - > data [ 2 ] = data ;
return spi_write ( priv - > spi , & priv - > data [ 0 ] , 3 ) ;
}
static int ads124s_reset ( struct iio_dev * indio_dev )
{
struct ads124s_private * priv = iio_priv ( indio_dev ) ;
if ( priv - > reset_gpio ) {
gpiod_set_value ( priv - > reset_gpio , 0 ) ;
udelay ( 200 ) ;
gpiod_set_value ( priv - > reset_gpio , 1 ) ;
} else {
return ads124s_write_cmd ( indio_dev , ADS124S08_CMD_RESET ) ;
}
return 0 ;
} ;
static int ads124s_read ( struct iio_dev * indio_dev , unsigned int chan )
{
struct ads124s_private * priv = iio_priv ( indio_dev ) ;
int ret ;
u32 tmp ;
struct spi_transfer t [ ] = {
{
. tx_buf = & priv - > data [ 0 ] ,
. len = 4 ,
. cs_change = 1 ,
} , {
. tx_buf = & priv - > data [ 1 ] ,
. rx_buf = & priv - > data [ 1 ] ,
. len = 4 ,
} ,
} ;
priv - > data [ 0 ] = ADS124S08_CMD_RDATA ;
2019-03-31 21:54:23 +03:00
memset ( & priv - > data [ 1 ] , ADS124S08_CMD_NOP , sizeof ( priv - > data ) - 1 ) ;
2019-01-11 22:57:06 +03:00
ret = spi_sync_transfer ( priv - > spi , t , ARRAY_SIZE ( t ) ) ;
if ( ret < 0 )
return ret ;
tmp = priv - > data [ 2 ] < < 16 | priv - > data [ 3 ] < < 8 | priv - > data [ 4 ] ;
return tmp ;
}
static int ads124s_read_raw ( struct iio_dev * indio_dev ,
struct iio_chan_spec const * chan ,
int * val , int * val2 , long m )
{
struct ads124s_private * priv = iio_priv ( indio_dev ) ;
int ret ;
mutex_lock ( & priv - > lock ) ;
switch ( m ) {
case IIO_CHAN_INFO_RAW :
ret = ads124s_write_reg ( indio_dev , ADS124S08_INPUT_MUX ,
chan - > channel ) ;
if ( ret ) {
dev_err ( & priv - > spi - > dev , " Set ADC CH failed \n " ) ;
goto out ;
}
ret = ads124s_write_cmd ( indio_dev , ADS124S08_START_CONV ) ;
if ( ret ) {
2019-02-07 12:39:02 +03:00
dev_err ( & priv - > spi - > dev , " Start conversions failed \n " ) ;
2019-01-11 22:57:06 +03:00
goto out ;
}
ret = ads124s_read ( indio_dev , chan - > channel ) ;
if ( ret < 0 ) {
dev_err ( & priv - > spi - > dev , " Read ADC failed \n " ) ;
goto out ;
}
* val = ret ;
ret = ads124s_write_cmd ( indio_dev , ADS124S08_STOP_CONV ) ;
if ( ret ) {
2019-02-07 12:39:02 +03:00
dev_err ( & priv - > spi - > dev , " Stop conversions failed \n " ) ;
2019-01-11 22:57:06 +03:00
goto out ;
}
ret = IIO_VAL_INT ;
break ;
default :
ret = - EINVAL ;
break ;
}
out :
mutex_unlock ( & priv - > lock ) ;
return ret ;
}
static const struct iio_info ads124s_info = {
. read_raw = & ads124s_read_raw ,
} ;
static irqreturn_t ads124s_trigger_handler ( int irq , void * p )
{
struct iio_poll_func * pf = p ;
struct iio_dev * indio_dev = pf - > indio_dev ;
struct ads124s_private * priv = iio_priv ( indio_dev ) ;
u32 buffer [ ADS124S08_MAX_CHANNELS + sizeof ( s64 ) / sizeof ( u16 ) ] ;
int scan_index , j = 0 ;
int ret ;
for_each_set_bit ( scan_index , indio_dev - > active_scan_mask ,
indio_dev - > masklength ) {
ret = ads124s_write_reg ( indio_dev , ADS124S08_INPUT_MUX ,
scan_index ) ;
if ( ret )
dev_err ( & priv - > spi - > dev , " Set ADC CH failed \n " ) ;
ret = ads124s_write_cmd ( indio_dev , ADS124S08_START_CONV ) ;
if ( ret )
2019-02-07 12:39:02 +03:00
dev_err ( & priv - > spi - > dev , " Start ADC conversions failed \n " ) ;
2019-01-11 22:57:06 +03:00
buffer [ j ] = ads124s_read ( indio_dev , scan_index ) ;
ret = ads124s_write_cmd ( indio_dev , ADS124S08_STOP_CONV ) ;
if ( ret )
2019-02-07 12:39:02 +03:00
dev_err ( & priv - > spi - > dev , " Stop ADC conversions failed \n " ) ;
2019-01-11 22:57:06 +03:00
j + + ;
}
iio_push_to_buffers_with_timestamp ( indio_dev , buffer ,
pf - > timestamp ) ;
iio_trigger_notify_done ( indio_dev - > trig ) ;
return IRQ_HANDLED ;
}
static int ads124s_probe ( struct spi_device * spi )
{
struct ads124s_private * ads124s_priv ;
struct iio_dev * indio_dev ;
const struct spi_device_id * spi_id = spi_get_device_id ( spi ) ;
int ret ;
indio_dev = devm_iio_device_alloc ( & spi - > dev , sizeof ( * ads124s_priv ) ) ;
if ( indio_dev = = NULL )
return - ENOMEM ;
ads124s_priv = iio_priv ( indio_dev ) ;
ads124s_priv - > reset_gpio = devm_gpiod_get_optional ( & spi - > dev ,
" reset " , GPIOD_OUT_LOW ) ;
if ( IS_ERR ( ads124s_priv - > reset_gpio ) )
dev_info ( & spi - > dev , " Reset GPIO not defined \n " ) ;
ads124s_priv - > chip_info = & ads124s_chip_info_tbl [ spi_id - > driver_data ] ;
spi_set_drvdata ( spi , indio_dev ) ;
ads124s_priv - > spi = spi ;
indio_dev - > name = spi_id - > name ;
indio_dev - > dev . parent = & spi - > dev ;
indio_dev - > dev . of_node = spi - > dev . of_node ;
indio_dev - > modes = INDIO_DIRECT_MODE ;
indio_dev - > channels = ads124s_priv - > chip_info - > channels ;
indio_dev - > num_channels = ads124s_priv - > chip_info - > num_channels ;
indio_dev - > info = & ads124s_info ;
mutex_init ( & ads124s_priv - > lock ) ;
ret = devm_iio_triggered_buffer_setup ( & spi - > dev , indio_dev , NULL ,
ads124s_trigger_handler , NULL ) ;
if ( ret ) {
dev_err ( & spi - > dev , " iio triggered buffer setup failed \n " ) ;
return ret ;
}
ads124s_reset ( indio_dev ) ;
return devm_iio_device_register ( & spi - > dev , indio_dev ) ;
}
static const struct spi_device_id ads124s_id [ ] = {
{ " ads124s06 " , ADS124S06_ID } ,
{ " ads124s08 " , ADS124S08_ID } ,
{ }
} ;
MODULE_DEVICE_TABLE ( spi , ads124s_id ) ;
static const struct of_device_id ads124s_of_table [ ] = {
{ . compatible = " ti,ads124s06 " } ,
{ . compatible = " ti,ads124s08 " } ,
{ } ,
} ;
MODULE_DEVICE_TABLE ( of , ads124s_of_table ) ;
static struct spi_driver ads124s_driver = {
. driver = {
. name = " ads124s08 " ,
. of_match_table = ads124s_of_table ,
} ,
. probe = ads124s_probe ,
. id_table = ads124s_id ,
} ;
module_spi_driver ( ads124s_driver ) ;
MODULE_AUTHOR ( " Dan Murphy <dmuprhy@ti.com> " ) ;
MODULE_DESCRIPTION ( " TI TI_ADS12S0X ADC " ) ;
MODULE_LICENSE ( " GPL v2 " ) ;