2018-02-18 08:36:46 +03:00
// SPDX-License-Identifier: GPL-2.0+
2016-08-20 06:17:03 +03:00
/*
* ti - adc161s626 . c - Texas Instruments ADC161S626 1 - channel differential ADC
*
* ADC Devices Supported :
* adc141s626 - 14 - bit ADC
* adc161s626 - 16 - bit ADC
*
2018-02-18 08:36:46 +03:00
* Copyright ( C ) 2016 - 2018
* Author : Matt Ranostay < matt . ranostay @ konsulko . com >
2016-08-20 06:17:03 +03:00
*/
# include <linux/module.h>
# include <linux/init.h>
# include <linux/err.h>
# include <linux/spi/spi.h>
# include <linux/iio/iio.h>
# include <linux/iio/trigger.h>
# include <linux/iio/buffer.h>
# include <linux/iio/trigger_consumer.h>
# include <linux/iio/triggered_buffer.h>
2016-09-19 01:42:15 +03:00
# include <linux/regulator/consumer.h>
2016-08-20 06:17:03 +03:00
# define TI_ADC_DRV_NAME "ti-adc161s626"
enum {
TI_ADC141S626 ,
TI_ADC161S626 ,
} ;
static const struct iio_chan_spec ti_adc141s626_channels [ ] = {
{
. type = IIO_VOLTAGE ,
. channel = 0 ,
2016-09-19 01:42:15 +03:00
. info_mask_separate = BIT ( IIO_CHAN_INFO_RAW ) |
BIT ( IIO_CHAN_INFO_SCALE ) |
BIT ( IIO_CHAN_INFO_OFFSET ) ,
2016-08-20 06:17:03 +03:00
. scan_index = 0 ,
. scan_type = {
. sign = ' s ' ,
. realbits = 14 ,
. storagebits = 16 ,
} ,
} ,
IIO_CHAN_SOFT_TIMESTAMP ( 1 ) ,
} ;
static const struct iio_chan_spec ti_adc161s626_channels [ ] = {
{
. type = IIO_VOLTAGE ,
. channel = 0 ,
2016-09-19 01:42:15 +03:00
. info_mask_separate = BIT ( IIO_CHAN_INFO_RAW ) |
BIT ( IIO_CHAN_INFO_SCALE ) |
BIT ( IIO_CHAN_INFO_OFFSET ) ,
2016-08-20 06:17:03 +03:00
. scan_index = 0 ,
. scan_type = {
. sign = ' s ' ,
. realbits = 16 ,
. storagebits = 16 ,
} ,
} ,
IIO_CHAN_SOFT_TIMESTAMP ( 1 ) ,
} ;
struct ti_adc_data {
struct iio_dev * indio_dev ;
struct spi_device * spi ;
2016-09-19 01:42:15 +03:00
struct regulator * ref ;
2016-08-20 06:17:03 +03:00
u8 read_size ;
u8 shift ;
u8 buffer [ 16 ] ____cacheline_aligned ;
} ;
static int ti_adc_read_measurement ( struct ti_adc_data * data ,
struct iio_chan_spec const * chan , int * val )
{
int ret ;
switch ( data - > read_size ) {
case 2 : {
__be16 buf ;
ret = spi_read ( data - > spi , ( void * ) & buf , 2 ) ;
if ( ret )
return ret ;
* val = be16_to_cpu ( buf ) ;
break ;
}
case 3 : {
__be32 buf ;
ret = spi_read ( data - > spi , ( void * ) & buf , 3 ) ;
if ( ret )
return ret ;
* val = be32_to_cpu ( buf ) > > 8 ;
break ;
}
default :
return - EINVAL ;
}
* val = sign_extend32 ( * val > > data - > shift , chan - > scan_type . realbits - 1 ) ;
return 0 ;
}
static irqreturn_t ti_adc_trigger_handler ( int irq , void * private )
{
struct iio_poll_func * pf = private ;
struct iio_dev * indio_dev = pf - > indio_dev ;
struct ti_adc_data * data = iio_priv ( indio_dev ) ;
int ret ;
ret = ti_adc_read_measurement ( data , & indio_dev - > channels [ 0 ] ,
( int * ) & data - > buffer ) ;
if ( ! ret )
iio_push_to_buffers_with_timestamp ( indio_dev ,
data - > buffer ,
iio_get_time_ns ( indio_dev ) ) ;
iio_trigger_notify_done ( indio_dev - > trig ) ;
return IRQ_HANDLED ;
}
static int ti_adc_read_raw ( struct iio_dev * indio_dev ,
struct iio_chan_spec const * chan ,
int * val , int * val2 , long mask )
{
struct ti_adc_data * data = iio_priv ( indio_dev ) ;
int ret ;
2016-09-19 01:42:15 +03:00
switch ( mask ) {
case IIO_CHAN_INFO_RAW :
ret = iio_device_claim_direct_mode ( indio_dev ) ;
if ( ret )
return ret ;
2016-08-20 06:17:03 +03:00
2016-09-19 01:42:15 +03:00
ret = ti_adc_read_measurement ( data , chan , val ) ;
iio_device_release_direct_mode ( indio_dev ) ;
2016-08-20 06:17:03 +03:00
2016-09-19 01:42:15 +03:00
if ( ret )
return ret ;
2016-08-20 06:17:03 +03:00
return IIO_VAL_INT ;
2016-09-19 01:42:15 +03:00
case IIO_CHAN_INFO_SCALE :
ret = regulator_get_voltage ( data - > ref ) ;
if ( ret < 0 )
return ret ;
* val = ret / 1000 ;
* val2 = chan - > scan_type . realbits ;
return IIO_VAL_FRACTIONAL_LOG2 ;
case IIO_CHAN_INFO_OFFSET :
* val = 1 < < ( chan - > scan_type . realbits - 1 ) ;
return IIO_VAL_INT ;
}
2016-08-20 06:17:03 +03:00
return 0 ;
}
static const struct iio_info ti_adc_info = {
. read_raw = ti_adc_read_raw ,
} ;
static int ti_adc_probe ( struct spi_device * spi )
{
struct iio_dev * indio_dev ;
struct ti_adc_data * data ;
int ret ;
indio_dev = devm_iio_device_alloc ( & spi - > dev , sizeof ( * data ) ) ;
if ( ! indio_dev )
return - ENOMEM ;
indio_dev - > info = & ti_adc_info ;
indio_dev - > dev . parent = & spi - > dev ;
indio_dev - > dev . of_node = spi - > dev . of_node ;
indio_dev - > name = TI_ADC_DRV_NAME ;
indio_dev - > modes = INDIO_DIRECT_MODE ;
spi_set_drvdata ( spi , indio_dev ) ;
data = iio_priv ( indio_dev ) ;
data - > spi = spi ;
switch ( spi_get_device_id ( spi ) - > driver_data ) {
case TI_ADC141S626 :
indio_dev - > channels = ti_adc141s626_channels ;
indio_dev - > num_channels = ARRAY_SIZE ( ti_adc141s626_channels ) ;
data - > shift = 0 ;
data - > read_size = 2 ;
break ;
case TI_ADC161S626 :
indio_dev - > channels = ti_adc161s626_channels ;
indio_dev - > num_channels = ARRAY_SIZE ( ti_adc161s626_channels ) ;
data - > shift = 6 ;
data - > read_size = 3 ;
break ;
}
2016-09-19 01:42:15 +03:00
data - > ref = devm_regulator_get ( & spi - > dev , " vdda " ) ;
if ( ! IS_ERR ( data - > ref ) ) {
ret = regulator_enable ( data - > ref ) ;
if ( ret < 0 )
return ret ;
}
2016-08-20 06:17:03 +03:00
ret = iio_triggered_buffer_setup ( indio_dev , NULL ,
ti_adc_trigger_handler , NULL ) ;
if ( ret )
2016-09-19 01:42:15 +03:00
goto error_regulator_disable ;
2016-08-20 06:17:03 +03:00
ret = iio_device_register ( indio_dev ) ;
if ( ret )
goto error_unreg_buffer ;
return 0 ;
error_unreg_buffer :
iio_triggered_buffer_cleanup ( indio_dev ) ;
2016-09-19 01:42:15 +03:00
error_regulator_disable :
regulator_disable ( data - > ref ) ;
2016-08-20 06:17:03 +03:00
return ret ;
}
static int ti_adc_remove ( struct spi_device * spi )
{
struct iio_dev * indio_dev = spi_get_drvdata ( spi ) ;
2016-09-19 01:42:15 +03:00
struct ti_adc_data * data = iio_priv ( indio_dev ) ;
2016-08-20 06:17:03 +03:00
iio_device_unregister ( indio_dev ) ;
iio_triggered_buffer_cleanup ( indio_dev ) ;
2016-09-19 01:42:15 +03:00
regulator_disable ( data - > ref ) ;
2016-08-20 06:17:03 +03:00
return 0 ;
}
static const struct of_device_id ti_adc_dt_ids [ ] = {
{ . compatible = " ti,adc141s626 " , } ,
{ . compatible = " ti,adc161s626 " , } ,
{ }
} ;
MODULE_DEVICE_TABLE ( of , ti_adc_dt_ids ) ;
static const struct spi_device_id ti_adc_id [ ] = {
{ " adc141s626 " , TI_ADC141S626 } ,
{ " adc161s626 " , TI_ADC161S626 } ,
{ } ,
} ;
MODULE_DEVICE_TABLE ( spi , ti_adc_id ) ;
static struct spi_driver ti_adc_driver = {
. driver = {
. name = TI_ADC_DRV_NAME ,
. of_match_table = of_match_ptr ( ti_adc_dt_ids ) ,
} ,
. probe = ti_adc_probe ,
. remove = ti_adc_remove ,
. id_table = ti_adc_id ,
} ;
module_spi_driver ( ti_adc_driver ) ;
2018-02-18 08:36:46 +03:00
MODULE_AUTHOR ( " Matt Ranostay <matt.ranostay@konsulko.com> " ) ;
2016-08-20 06:17:03 +03:00
MODULE_DESCRIPTION ( " Texas Instruments ADC1x1S 1-channel differential ADC " ) ;
MODULE_LICENSE ( " GPL " ) ;