2019-05-28 19:57:06 +03:00
// SPDX-License-Identifier: GPL-2.0-only
2012-08-10 20:36:00 +04:00
/*
* Support code for Analog Devices Sigma - Delta ADCs
*
* Copyright 2012 Analog Devices Inc .
* Author : Lars - Peter Clausen < lars @ metafoo . de >
*/
# include <linux/interrupt.h>
# include <linux/device.h>
# include <linux/kernel.h>
# include <linux/slab.h>
# include <linux/spi/spi.h>
# include <linux/err.h>
# include <linux/module.h>
# include <linux/iio/iio.h>
# include <linux/iio/sysfs.h>
# include <linux/iio/buffer.h>
# include <linux/iio/trigger.h>
# include <linux/iio/trigger_consumer.h>
# include <linux/iio/triggered_buffer.h>
# include <linux/iio/adc/ad_sigma_delta.h>
# include <asm/unaligned.h>
# define AD_SD_COMM_CHAN_MASK 0x3
# define AD_SD_REG_COMM 0x00
# define AD_SD_REG_DATA 0x03
/**
* ad_sd_set_comm ( ) - Set communications register
*
* @ sigma_delta : The sigma delta device
* @ comm : New value for the communications register
*/
void ad_sd_set_comm ( struct ad_sigma_delta * sigma_delta , uint8_t comm )
{
/* Some variants use the lower two bits of the communications register
* to select the channel */
sigma_delta - > comm = comm & AD_SD_COMM_CHAN_MASK ;
}
EXPORT_SYMBOL_GPL ( ad_sd_set_comm ) ;
/**
* ad_sd_write_reg ( ) - Write a register
*
* @ sigma_delta : The sigma delta device
* @ reg : Address of the register
* @ size : Size of the register ( 0 - 3 )
* @ val : Value to write to the register
*
* Returns 0 on success , an error code otherwise .
* */
int ad_sd_write_reg ( struct ad_sigma_delta * sigma_delta , unsigned int reg ,
unsigned int size , unsigned int val )
{
2020-11-24 15:38:07 +03:00
uint8_t * data = sigma_delta - > tx_buf ;
2012-08-10 20:36:00 +04:00
struct spi_transfer t = {
. tx_buf = data ,
. len = size + 1 ,
2019-03-19 14:37:55 +03:00
. cs_change = sigma_delta - > keep_cs_asserted ,
2012-08-10 20:36:00 +04:00
} ;
struct spi_message m ;
int ret ;
data [ 0 ] = ( reg < < sigma_delta - > info - > addr_shift ) | sigma_delta - > comm ;
switch ( size ) {
case 3 :
2020-04-21 03:31:20 +03:00
put_unaligned_be24 ( val , & data [ 1 ] ) ;
2012-08-10 20:36:00 +04:00
break ;
case 2 :
put_unaligned_be16 ( val , & data [ 1 ] ) ;
break ;
case 1 :
data [ 1 ] = val ;
break ;
case 0 :
break ;
default :
return - EINVAL ;
}
spi_message_init ( & m ) ;
spi_message_add_tail ( & t , & m ) ;
if ( sigma_delta - > bus_locked )
ret = spi_sync_locked ( sigma_delta - > spi , & m ) ;
else
ret = spi_sync ( sigma_delta - > spi , & m ) ;
return ret ;
}
EXPORT_SYMBOL_GPL ( ad_sd_write_reg ) ;
static int ad_sd_read_reg_raw ( struct ad_sigma_delta * sigma_delta ,
unsigned int reg , unsigned int size , uint8_t * val )
{
2020-11-24 15:38:07 +03:00
uint8_t * data = sigma_delta - > tx_buf ;
2012-08-10 20:36:00 +04:00
int ret ;
struct spi_transfer t [ ] = {
{
. tx_buf = data ,
. len = 1 ,
} , {
. rx_buf = val ,
. len = size ,
. cs_change = sigma_delta - > bus_locked ,
} ,
} ;
struct spi_message m ;
spi_message_init ( & m ) ;
if ( sigma_delta - > info - > has_registers ) {
data [ 0 ] = reg < < sigma_delta - > info - > addr_shift ;
data [ 0 ] | = sigma_delta - > info - > read_mask ;
2019-03-19 13:47:00 +03:00
data [ 0 ] | = sigma_delta - > comm ;
2012-08-10 20:36:00 +04:00
spi_message_add_tail ( & t [ 0 ] , & m ) ;
}
spi_message_add_tail ( & t [ 1 ] , & m ) ;
if ( sigma_delta - > bus_locked )
ret = spi_sync_locked ( sigma_delta - > spi , & m ) ;
else
ret = spi_sync ( sigma_delta - > spi , & m ) ;
return ret ;
}
/**
* ad_sd_read_reg ( ) - Read a register
*
* @ sigma_delta : The sigma delta device
* @ reg : Address of the register
* @ size : Size of the register ( 1 - 4 )
* @ val : Read value
*
* Returns 0 on success , an error code otherwise .
* */
int ad_sd_read_reg ( struct ad_sigma_delta * sigma_delta ,
unsigned int reg , unsigned int size , unsigned int * val )
{
int ret ;
2020-11-24 15:38:07 +03:00
ret = ad_sd_read_reg_raw ( sigma_delta , reg , size , sigma_delta - > rx_buf ) ;
2012-08-10 20:36:00 +04:00
if ( ret < 0 )
goto out ;
switch ( size ) {
case 4 :
2020-11-24 15:38:07 +03:00
* val = get_unaligned_be32 ( sigma_delta - > rx_buf ) ;
2012-08-10 20:36:00 +04:00
break ;
case 3 :
2020-11-24 15:38:07 +03:00
* val = get_unaligned_be24 ( sigma_delta - > rx_buf ) ;
2012-08-10 20:36:00 +04:00
break ;
case 2 :
2020-11-24 15:38:07 +03:00
* val = get_unaligned_be16 ( sigma_delta - > rx_buf ) ;
2012-08-10 20:36:00 +04:00
break ;
case 1 :
2020-11-24 15:38:07 +03:00
* val = sigma_delta - > rx_buf [ 0 ] ;
2012-08-10 20:36:00 +04:00
break ;
default :
ret = - EINVAL ;
break ;
}
out :
return ret ;
}
EXPORT_SYMBOL_GPL ( ad_sd_read_reg ) ;
2017-09-05 15:14:45 +03:00
/**
* ad_sd_reset ( ) - Reset the serial interface
*
* @ sigma_delta : The sigma delta device
* @ reset_length : Number of SCLKs with DIN = 1
*
* Returns 0 on success , an error code otherwise .
* */
int ad_sd_reset ( struct ad_sigma_delta * sigma_delta ,
unsigned int reset_length )
{
uint8_t * buf ;
unsigned int size ;
int ret ;
size = DIV_ROUND_UP ( reset_length , 8 ) ;
buf = kcalloc ( size , sizeof ( * buf ) , GFP_KERNEL ) ;
if ( ! buf )
return - ENOMEM ;
memset ( buf , 0xff , size ) ;
ret = spi_write ( sigma_delta - > spi , buf , size ) ;
kfree ( buf ) ;
return ret ;
}
EXPORT_SYMBOL_GPL ( ad_sd_reset ) ;
2019-09-02 16:08:29 +03:00
int ad_sd_calibrate ( struct ad_sigma_delta * sigma_delta ,
2012-08-10 20:36:00 +04:00
unsigned int mode , unsigned int channel )
{
int ret ;
2018-07-23 12:18:38 +03:00
unsigned long timeout ;
2012-08-10 20:36:00 +04:00
ret = ad_sigma_delta_set_channel ( sigma_delta , channel ) ;
if ( ret )
return ret ;
spi_bus_lock ( sigma_delta - > spi - > master ) ;
sigma_delta - > bus_locked = true ;
2019-03-19 14:37:55 +03:00
sigma_delta - > keep_cs_asserted = true ;
2013-11-15 02:32:02 +04:00
reinit_completion ( & sigma_delta - > completion ) ;
2012-08-10 20:36:00 +04:00
ret = ad_sigma_delta_set_mode ( sigma_delta , mode ) ;
if ( ret < 0 )
goto out ;
sigma_delta - > irq_dis = false ;
enable_irq ( sigma_delta - > spi - > irq ) ;
2018-07-23 12:18:38 +03:00
timeout = wait_for_completion_timeout ( & sigma_delta - > completion , 2 * HZ ) ;
if ( timeout = = 0 ) {
2012-08-10 20:36:00 +04:00
sigma_delta - > irq_dis = true ;
disable_irq_nosync ( sigma_delta - > spi - > irq ) ;
ret = - EIO ;
} else {
ret = 0 ;
}
out :
2019-03-19 14:37:55 +03:00
sigma_delta - > keep_cs_asserted = false ;
ad_sigma_delta_set_mode ( sigma_delta , AD_SD_MODE_IDLE ) ;
2012-08-10 20:36:00 +04:00
sigma_delta - > bus_locked = false ;
spi_bus_unlock ( sigma_delta - > spi - > master ) ;
return ret ;
}
2019-09-02 16:08:29 +03:00
EXPORT_SYMBOL_GPL ( ad_sd_calibrate ) ;
2012-08-10 20:36:00 +04:00
/**
* ad_sd_calibrate_all ( ) - Performs channel calibration
* @ sigma_delta : The sigma delta device
* @ cb : Array of channels and calibration type to perform
* @ n : Number of items in cb
*
* Returns 0 on success , an error code otherwise .
* */
int ad_sd_calibrate_all ( struct ad_sigma_delta * sigma_delta ,
const struct ad_sd_calib_data * cb , unsigned int n )
{
unsigned int i ;
int ret ;
for ( i = 0 ; i < n ; i + + ) {
ret = ad_sd_calibrate ( sigma_delta , cb [ i ] . mode , cb [ i ] . channel ) ;
if ( ret )
return ret ;
}
return 0 ;
}
EXPORT_SYMBOL_GPL ( ad_sd_calibrate_all ) ;
/**
* ad_sigma_delta_single_conversion ( ) - Performs a single data conversion
* @ indio_dev : The IIO device
* @ chan : The conversion is done for this channel
* @ val : Pointer to the location where to store the read value
*
* Returns : 0 on success , an error value otherwise .
*/
int ad_sigma_delta_single_conversion ( struct iio_dev * indio_dev ,
const struct iio_chan_spec * chan , int * val )
{
struct ad_sigma_delta * sigma_delta = iio_device_get_drvdata ( indio_dev ) ;
unsigned int sample , raw_sample ;
2018-11-13 14:20:24 +03:00
unsigned int data_reg ;
2012-08-10 20:36:00 +04:00
int ret = 0 ;
if ( iio_buffer_enabled ( indio_dev ) )
return - EBUSY ;
mutex_lock ( & indio_dev - > mlock ) ;
ad_sigma_delta_set_channel ( sigma_delta , chan - > address ) ;
spi_bus_lock ( sigma_delta - > spi - > master ) ;
sigma_delta - > bus_locked = true ;
2019-03-19 14:37:55 +03:00
sigma_delta - > keep_cs_asserted = true ;
2013-11-15 02:32:02 +04:00
reinit_completion ( & sigma_delta - > completion ) ;
2012-08-10 20:36:00 +04:00
ad_sigma_delta_set_mode ( sigma_delta , AD_SD_MODE_SINGLE ) ;
sigma_delta - > irq_dis = false ;
enable_irq ( sigma_delta - > spi - > irq ) ;
ret = wait_for_completion_interruptible_timeout (
& sigma_delta - > completion , HZ ) ;
if ( ret = = 0 )
ret = - EIO ;
if ( ret < 0 )
goto out ;
2018-11-13 14:20:24 +03:00
if ( sigma_delta - > info - > data_reg ! = 0 )
data_reg = sigma_delta - > info - > data_reg ;
else
data_reg = AD_SD_REG_DATA ;
ret = ad_sd_read_reg ( sigma_delta , data_reg ,
2012-08-10 20:36:00 +04:00
DIV_ROUND_UP ( chan - > scan_type . realbits + chan - > scan_type . shift , 8 ) ,
& raw_sample ) ;
out :
if ( ! sigma_delta - > irq_dis ) {
disable_irq_nosync ( sigma_delta - > spi - > irq ) ;
sigma_delta - > irq_dis = true ;
}
2019-03-19 14:37:55 +03:00
sigma_delta - > keep_cs_asserted = false ;
2012-08-10 20:36:00 +04:00
ad_sigma_delta_set_mode ( sigma_delta , AD_SD_MODE_IDLE ) ;
2019-03-19 14:37:55 +03:00
sigma_delta - > bus_locked = false ;
spi_bus_unlock ( sigma_delta - > spi - > master ) ;
2012-08-10 20:36:00 +04:00
mutex_unlock ( & indio_dev - > mlock ) ;
if ( ret )
return ret ;
sample = raw_sample > > chan - > scan_type . shift ;
sample & = ( 1 < < chan - > scan_type . realbits ) - 1 ;
* val = sample ;
ret = ad_sigma_delta_postprocess_sample ( sigma_delta , raw_sample ) ;
if ( ret )
return ret ;
return IIO_VAL_INT ;
}
EXPORT_SYMBOL_GPL ( ad_sigma_delta_single_conversion ) ;
static int ad_sd_buffer_postenable ( struct iio_dev * indio_dev )
{
struct ad_sigma_delta * sigma_delta = iio_device_get_drvdata ( indio_dev ) ;
unsigned int channel ;
int ret ;
channel = find_first_bit ( indio_dev - > active_scan_mask ,
indio_dev - > masklength ) ;
ret = ad_sigma_delta_set_channel ( sigma_delta ,
indio_dev - > channels [ channel ] . address ) ;
if ( ret )
2019-05-30 10:25:41 +03:00
return ret ;
2012-08-10 20:36:00 +04:00
spi_bus_lock ( sigma_delta - > spi - > master ) ;
sigma_delta - > bus_locked = true ;
2019-03-19 14:37:55 +03:00
sigma_delta - > keep_cs_asserted = true ;
2012-08-10 20:36:00 +04:00
ret = ad_sigma_delta_set_mode ( sigma_delta , AD_SD_MODE_CONTINUOUS ) ;
if ( ret )
goto err_unlock ;
sigma_delta - > irq_dis = false ;
enable_irq ( sigma_delta - > spi - > irq ) ;
return 0 ;
err_unlock :
spi_bus_unlock ( sigma_delta - > spi - > master ) ;
return ret ;
}
static int ad_sd_buffer_postdisable ( struct iio_dev * indio_dev )
{
struct ad_sigma_delta * sigma_delta = iio_device_get_drvdata ( indio_dev ) ;
2013-11-15 02:32:02 +04:00
reinit_completion ( & sigma_delta - > completion ) ;
2012-08-10 20:36:00 +04:00
wait_for_completion_timeout ( & sigma_delta - > completion , HZ ) ;
if ( ! sigma_delta - > irq_dis ) {
disable_irq_nosync ( sigma_delta - > spi - > irq ) ;
sigma_delta - > irq_dis = true ;
}
2019-03-19 14:37:55 +03:00
sigma_delta - > keep_cs_asserted = false ;
2012-08-10 20:36:00 +04:00
ad_sigma_delta_set_mode ( sigma_delta , AD_SD_MODE_IDLE ) ;
sigma_delta - > bus_locked = false ;
return spi_bus_unlock ( sigma_delta - > spi - > master ) ;
}
static irqreturn_t ad_sd_trigger_handler ( int irq , void * p )
{
struct iio_poll_func * pf = p ;
struct iio_dev * indio_dev = pf - > indio_dev ;
struct ad_sigma_delta * sigma_delta = iio_device_get_drvdata ( indio_dev ) ;
2020-11-24 15:38:07 +03:00
uint8_t * data = sigma_delta - > rx_buf ;
2012-08-10 20:36:00 +04:00
unsigned int reg_size ;
2018-11-13 14:20:24 +03:00
unsigned int data_reg ;
2012-08-10 20:36:00 +04:00
reg_size = indio_dev - > channels [ 0 ] . scan_type . realbits +
indio_dev - > channels [ 0 ] . scan_type . shift ;
reg_size = DIV_ROUND_UP ( reg_size , 8 ) ;
2018-11-13 14:20:24 +03:00
if ( sigma_delta - > info - > data_reg ! = 0 )
data_reg = sigma_delta - > info - > data_reg ;
else
data_reg = AD_SD_REG_DATA ;
2012-08-10 20:36:00 +04:00
switch ( reg_size ) {
case 4 :
case 2 :
case 1 :
2020-07-16 16:58:59 +03:00
ad_sd_read_reg_raw ( sigma_delta , data_reg , reg_size , & data [ 0 ] ) ;
2012-08-10 20:36:00 +04:00
break ;
case 3 :
/* We store 24 bit samples in a 32 bit word. Keep the upper
* byte set to zero . */
2020-07-16 16:58:59 +03:00
ad_sd_read_reg_raw ( sigma_delta , data_reg , reg_size , & data [ 1 ] ) ;
2012-08-10 20:36:00 +04:00
break ;
}
2013-09-19 16:59:00 +04:00
iio_push_to_buffers_with_timestamp ( indio_dev , data , pf - > timestamp ) ;
2012-08-10 20:36:00 +04:00
iio_trigger_notify_done ( indio_dev - > trig ) ;
sigma_delta - > irq_dis = false ;
enable_irq ( sigma_delta - > spi - > irq ) ;
return IRQ_HANDLED ;
}
static const struct iio_buffer_setup_ops ad_sd_buffer_setup_ops = {
. postenable = & ad_sd_buffer_postenable ,
. postdisable = & ad_sd_buffer_postdisable ,
. validate_scan_mask = & iio_validate_scan_mask_onehot ,
} ;
static irqreturn_t ad_sd_data_rdy_trig_poll ( int irq , void * private )
{
struct ad_sigma_delta * sigma_delta = private ;
complete ( & sigma_delta - > completion ) ;
disable_irq_nosync ( irq ) ;
sigma_delta - > irq_dis = true ;
2014-12-06 09:46:00 +03:00
iio_trigger_poll ( sigma_delta - > trig ) ;
2012-08-10 20:36:00 +04:00
return IRQ_HANDLED ;
}
/**
* ad_sd_validate_trigger ( ) - validate_trigger callback for ad_sigma_delta devices
* @ indio_dev : The IIO device
* @ trig : The new trigger
*
* Returns : 0 if the ' trig ' matches the trigger registered by the ad_sigma_delta
* device , - EINVAL otherwise .
*/
int ad_sd_validate_trigger ( struct iio_dev * indio_dev , struct iio_trigger * trig )
{
struct ad_sigma_delta * sigma_delta = iio_device_get_drvdata ( indio_dev ) ;
if ( sigma_delta - > trig ! = trig )
return - EINVAL ;
return 0 ;
}
EXPORT_SYMBOL_GPL ( ad_sd_validate_trigger ) ;
static const struct iio_trigger_ops ad_sd_trigger_ops = {
} ;
static int ad_sd_probe_trigger ( struct iio_dev * indio_dev )
{
struct ad_sigma_delta * sigma_delta = iio_device_get_drvdata ( indio_dev ) ;
int ret ;
2021-03-09 22:36:13 +03:00
sigma_delta - > trig = iio_trigger_alloc ( & sigma_delta - > spi - > dev ,
" %s-dev%d " , indio_dev - > name ,
2021-04-26 20:49:03 +03:00
iio_device_id ( indio_dev ) ) ;
2012-08-10 20:36:00 +04:00
if ( sigma_delta - > trig = = NULL ) {
ret = - ENOMEM ;
goto error_ret ;
}
sigma_delta - > trig - > ops = & ad_sd_trigger_ops ;
init_completion ( & sigma_delta - > completion ) ;
2021-04-02 21:45:44 +03:00
sigma_delta - > irq_dis = true ;
2012-08-10 20:36:00 +04:00
ret = request_irq ( sigma_delta - > spi - > irq ,
ad_sd_data_rdy_trig_poll ,
2021-04-02 21:45:44 +03:00
sigma_delta - > info - > irq_flags | IRQF_NO_AUTOEN ,
2012-08-10 20:36:00 +04:00
indio_dev - > name ,
sigma_delta ) ;
if ( ret )
goto error_free_trig ;
2013-03-25 12:58:00 +04:00
iio_trigger_set_drvdata ( sigma_delta - > trig , sigma_delta ) ;
2012-08-10 20:36:00 +04:00
ret = iio_trigger_register ( sigma_delta - > trig ) ;
if ( ret )
goto error_free_irq ;
/* select default trigger */
2014-08-23 00:48:00 +04:00
indio_dev - > trig = iio_trigger_get ( sigma_delta - > trig ) ;
2012-08-10 20:36:00 +04:00
return 0 ;
error_free_irq :
free_irq ( sigma_delta - > spi - > irq , sigma_delta ) ;
error_free_trig :
iio_trigger_free ( sigma_delta - > trig ) ;
error_ret :
return ret ;
}
static void ad_sd_remove_trigger ( struct iio_dev * indio_dev )
{
struct ad_sigma_delta * sigma_delta = iio_device_get_drvdata ( indio_dev ) ;
iio_trigger_unregister ( sigma_delta - > trig ) ;
free_irq ( sigma_delta - > spi - > irq , sigma_delta ) ;
iio_trigger_free ( sigma_delta - > trig ) ;
}
/**
* ad_sd_setup_buffer_and_trigger ( ) -
* @ indio_dev : The IIO device
*/
int ad_sd_setup_buffer_and_trigger ( struct iio_dev * indio_dev )
{
int ret ;
ret = iio_triggered_buffer_setup ( indio_dev , & iio_pollfunc_store_time ,
& ad_sd_trigger_handler , & ad_sd_buffer_setup_ops ) ;
if ( ret )
return ret ;
ret = ad_sd_probe_trigger ( indio_dev ) ;
if ( ret ) {
iio_triggered_buffer_cleanup ( indio_dev ) ;
return ret ;
}
return 0 ;
}
EXPORT_SYMBOL_GPL ( ad_sd_setup_buffer_and_trigger ) ;
/**
* ad_sd_cleanup_buffer_and_trigger ( ) -
* @ indio_dev : The IIO device
*/
void ad_sd_cleanup_buffer_and_trigger ( struct iio_dev * indio_dev )
{
ad_sd_remove_trigger ( indio_dev ) ;
iio_triggered_buffer_cleanup ( indio_dev ) ;
}
EXPORT_SYMBOL_GPL ( ad_sd_cleanup_buffer_and_trigger ) ;
/**
* ad_sd_init ( ) - Initializes a ad_sigma_delta struct
* @ sigma_delta : The ad_sigma_delta device
* @ indio_dev : The IIO device which the Sigma Delta device is used for
* @ spi : The SPI device for the ad_sigma_delta device
* @ info : Device specific callbacks and options
*
* This function needs to be called before any other operations are performed on
* the ad_sigma_delta struct .
*/
int ad_sd_init ( struct ad_sigma_delta * sigma_delta , struct iio_dev * indio_dev ,
struct spi_device * spi , const struct ad_sigma_delta_info * info )
{
sigma_delta - > spi = spi ;
sigma_delta - > info = info ;
iio_device_set_drvdata ( indio_dev , sigma_delta ) ;
return 0 ;
}
EXPORT_SYMBOL_GPL ( ad_sd_init ) ;
MODULE_AUTHOR ( " Lars-Peter Clausen <lars@metafoo.de> " ) ;
MODULE_DESCRIPTION ( " Analog Devices Sigma-Delta ADCs " ) ;
MODULE_LICENSE ( " GPL v2 " ) ;