2019-04-12 11:15:37 +02:00
// SPDX-License-Identifier: GPL-2.0+
/*
* ADS8344 16 - bit 8 - Channel ADC driver
*
* Author : Gregory CLEMENT < gregory . clement @ bootlin . com >
*
2020-07-04 21:27:43 +02:00
* Datasheet : https : //www.ti.com/lit/ds/symlink/ads8344.pdf
2019-04-12 11:15:37 +02:00
*/
# include <linux/delay.h>
# include <linux/iio/buffer.h>
# include <linux/iio/iio.h>
# include <linux/module.h>
# include <linux/regulator/consumer.h>
# include <linux/spi/spi.h>
# define ADS8344_START BIT(7)
# define ADS8344_SINGLE_END BIT(2)
# define ADS8344_CHANNEL(channel) ((channel) << 4)
# define ADS8344_CLOCK_INTERNAL 0x2 /* PD1 = 1 and PD0 = 0 */
struct ads8344 {
struct spi_device * spi ;
struct regulator * reg ;
/*
* Lock protecting access to adc - > tx_buff and rx_buff ,
* especially from concurrent read on sysfs file .
*/
struct mutex lock ;
2022-05-08 18:56:17 +01:00
u8 tx_buf __aligned ( IIO_DMA_MINALIGN ) ;
2020-04-16 22:54:27 +02:00
u8 rx_buf [ 3 ] ;
2019-04-12 11:15:37 +02:00
} ;
2020-04-30 15:05:47 +02:00
# define ADS8344_VOLTAGE_CHANNEL(chan, addr) \
2019-04-12 11:15:37 +02:00
{ \
. type = IIO_VOLTAGE , \
. indexed = 1 , \
. channel = chan , \
. info_mask_separate = BIT ( IIO_CHAN_INFO_RAW ) , \
. info_mask_shared_by_type = BIT ( IIO_CHAN_INFO_SCALE ) , \
2020-04-30 15:05:47 +02:00
. address = addr , \
2019-04-12 11:15:37 +02:00
}
2020-04-30 15:05:47 +02:00
# define ADS8344_VOLTAGE_CHANNEL_DIFF(chan1, chan2, addr) \
2019-04-12 11:15:37 +02:00
{ \
. type = IIO_VOLTAGE , \
. indexed = 1 , \
. channel = ( chan1 ) , \
. channel2 = ( chan2 ) , \
. differential = 1 , \
. info_mask_separate = BIT ( IIO_CHAN_INFO_RAW ) , \
. info_mask_shared_by_type = BIT ( IIO_CHAN_INFO_SCALE ) , \
2020-04-30 15:05:47 +02:00
. address = addr , \
2019-04-12 11:15:37 +02:00
}
static const struct iio_chan_spec ads8344_channels [ ] = {
ADS8344_VOLTAGE_CHANNEL ( 0 , 0 ) ,
ADS8344_VOLTAGE_CHANNEL ( 1 , 4 ) ,
ADS8344_VOLTAGE_CHANNEL ( 2 , 1 ) ,
ADS8344_VOLTAGE_CHANNEL ( 3 , 5 ) ,
ADS8344_VOLTAGE_CHANNEL ( 4 , 2 ) ,
ADS8344_VOLTAGE_CHANNEL ( 5 , 6 ) ,
ADS8344_VOLTAGE_CHANNEL ( 6 , 3 ) ,
ADS8344_VOLTAGE_CHANNEL ( 7 , 7 ) ,
ADS8344_VOLTAGE_CHANNEL_DIFF ( 0 , 1 , 8 ) ,
ADS8344_VOLTAGE_CHANNEL_DIFF ( 2 , 3 , 9 ) ,
ADS8344_VOLTAGE_CHANNEL_DIFF ( 4 , 5 , 10 ) ,
ADS8344_VOLTAGE_CHANNEL_DIFF ( 6 , 7 , 11 ) ,
ADS8344_VOLTAGE_CHANNEL_DIFF ( 1 , 0 , 12 ) ,
ADS8344_VOLTAGE_CHANNEL_DIFF ( 3 , 2 , 13 ) ,
ADS8344_VOLTAGE_CHANNEL_DIFF ( 5 , 4 , 14 ) ,
ADS8344_VOLTAGE_CHANNEL_DIFF ( 7 , 6 , 15 ) ,
} ;
static int ads8344_adc_conversion ( struct ads8344 * adc , int channel ,
bool differential )
{
struct spi_device * spi = adc - > spi ;
int ret ;
adc - > tx_buf = ADS8344_START ;
if ( ! differential )
adc - > tx_buf | = ADS8344_SINGLE_END ;
adc - > tx_buf | = ADS8344_CHANNEL ( channel ) ;
adc - > tx_buf | = ADS8344_CLOCK_INTERNAL ;
ret = spi_write ( spi , & adc - > tx_buf , 1 ) ;
if ( ret )
return ret ;
udelay ( 9 ) ;
2020-04-16 22:54:27 +02:00
ret = spi_read ( spi , adc - > rx_buf , sizeof ( adc - > rx_buf ) ) ;
2019-04-12 11:15:37 +02:00
if ( ret )
return ret ;
2020-04-16 22:54:27 +02:00
return adc - > rx_buf [ 0 ] < < 9 | adc - > rx_buf [ 1 ] < < 1 | adc - > rx_buf [ 2 ] > > 7 ;
2019-04-12 11:15:37 +02:00
}
static int ads8344_read_raw ( struct iio_dev * iio ,
struct iio_chan_spec const * channel , int * value ,
int * shift , long mask )
{
struct ads8344 * adc = iio_priv ( iio ) ;
switch ( mask ) {
case IIO_CHAN_INFO_RAW :
mutex_lock ( & adc - > lock ) ;
2020-04-30 15:05:47 +02:00
* value = ads8344_adc_conversion ( adc , channel - > address ,
2019-04-12 11:15:37 +02:00
channel - > differential ) ;
mutex_unlock ( & adc - > lock ) ;
if ( * value < 0 )
return * value ;
return IIO_VAL_INT ;
case IIO_CHAN_INFO_SCALE :
* value = regulator_get_voltage ( adc - > reg ) ;
if ( * value < 0 )
return * value ;
/* convert regulator output voltage to mV */
* value / = 1000 ;
* shift = 16 ;
return IIO_VAL_FRACTIONAL_LOG2 ;
default :
return - EINVAL ;
}
}
static const struct iio_info ads8344_info = {
. read_raw = ads8344_read_raw ,
} ;
2021-09-03 10:37:07 +03:00
static void ads8344_reg_disable ( void * data )
{
regulator_disable ( data ) ;
}
2019-04-12 11:15:37 +02:00
static int ads8344_probe ( struct spi_device * spi )
{
struct iio_dev * indio_dev ;
struct ads8344 * adc ;
int ret ;
indio_dev = devm_iio_device_alloc ( & spi - > dev , sizeof ( * adc ) ) ;
if ( ! indio_dev )
return - ENOMEM ;
adc = iio_priv ( indio_dev ) ;
adc - > spi = spi ;
mutex_init ( & adc - > lock ) ;
indio_dev - > name = dev_name ( & spi - > dev ) ;
indio_dev - > info = & ads8344_info ;
indio_dev - > modes = INDIO_DIRECT_MODE ;
indio_dev - > channels = ads8344_channels ;
indio_dev - > num_channels = ARRAY_SIZE ( ads8344_channels ) ;
adc - > reg = devm_regulator_get ( & spi - > dev , " vref " ) ;
if ( IS_ERR ( adc - > reg ) )
return PTR_ERR ( adc - > reg ) ;
ret = regulator_enable ( adc - > reg ) ;
if ( ret )
return ret ;
2021-09-03 10:37:07 +03:00
ret = devm_add_action_or_reset ( & spi - > dev , ads8344_reg_disable , adc - > reg ) ;
if ( ret )
2019-04-12 11:15:37 +02:00
return ret ;
2021-09-03 10:37:07 +03:00
return devm_iio_device_register ( & spi - > dev , indio_dev ) ;
2019-04-12 11:15:37 +02:00
}
static const struct of_device_id ads8344_of_match [ ] = {
{ . compatible = " ti,ads8344 " , } ,
{ }
} ;
MODULE_DEVICE_TABLE ( of , ads8344_of_match ) ;
static struct spi_driver ads8344_driver = {
. driver = {
. name = " ads8344 " ,
. of_match_table = ads8344_of_match ,
} ,
. probe = ads8344_probe ,
} ;
module_spi_driver ( ads8344_driver ) ;
MODULE_AUTHOR ( " Gregory CLEMENT <gregory.clement@bootlin.com> " ) ;
MODULE_DESCRIPTION ( " ADS8344 driver " ) ;
MODULE_LICENSE ( " GPL " ) ;