2019-05-29 16:57:44 -07:00
// SPDX-License-Identifier: GPL-2.0-only
2017-01-25 20:07:19 +01:00
/*
2017-08-16 21:34:23 +02:00
* srf08 . c - Support for Devantech SRFxx ultrasonic ranger
* with i2c interface
2017-08-16 21:34:54 +02:00
* actually supported are srf02 , srf08 , srf10
2017-01-25 20:07:19 +01:00
*
2017-08-16 21:34:54 +02:00
* Copyright ( c ) 2016 , 2017 Andreas Klinger < ak @ it - klinger . de >
2017-01-25 20:07:19 +01:00
*
* For details about the device see :
* http : //www.robot-electronics.co.uk/htm/srf08tech.html
2017-08-16 21:34:23 +02:00
* http : //www.robot-electronics.co.uk/htm/srf10tech.htm
2017-08-16 21:34:54 +02:00
* http : //www.robot-electronics.co.uk/htm/srf02tech.htm
2017-01-25 20:07:19 +01:00
*/
# include <linux/err.h>
# include <linux/i2c.h>
# include <linux/delay.h>
# include <linux/module.h>
# include <linux/bitops.h>
# include <linux/iio/iio.h>
# include <linux/iio/sysfs.h>
2017-08-16 21:34:06 +02:00
# include <linux/iio/buffer.h>
# include <linux/iio/trigger_consumer.h>
# include <linux/iio/triggered_buffer.h>
2017-01-25 20:07:19 +01:00
/* registers of SRF08 device */
# define SRF08_WRITE_COMMAND 0x00 /* Command Register */
# define SRF08_WRITE_MAX_GAIN 0x01 /* Max Gain Register: 0 .. 31 */
# define SRF08_WRITE_RANGE 0x02 /* Range Register: 0 .. 255 */
# define SRF08_READ_SW_REVISION 0x00 /* Software Revision */
# define SRF08_READ_LIGHT 0x01 /* Light Sensor during last echo */
# define SRF08_READ_ECHO_1_HIGH 0x02 /* Range of first echo received */
# define SRF08_READ_ECHO_1_LOW 0x03 /* Range of first echo received */
# define SRF08_CMD_RANGING_CM 0x51 /* Ranging Mode - Result in cm */
2017-08-16 21:34:23 +02:00
enum srf08_sensor_type {
2017-08-16 21:34:54 +02:00
SRF02 ,
2017-08-16 21:34:23 +02:00
SRF08 ,
SRF10 ,
SRF_MAX_TYPE
} ;
struct srf08_chip_info {
const int * sensitivity_avail ;
int num_sensitivity_avail ;
int sensitivity_default ;
2017-08-16 21:34:54 +02:00
/* default value of Range in mm */
int range_default ;
2017-08-16 21:34:23 +02:00
} ;
2017-01-25 20:07:19 +01:00
struct srf08_data {
struct i2c_client * client ;
2017-08-16 21:34:06 +02:00
/*
* Gain in the datasheet is called sensitivity here to distinct it
* from the gain used with amplifiers of adc ' s
*/
int sensitivity ;
/* max. Range in mm */
int range_mm ;
2017-01-25 20:07:19 +01:00
struct mutex lock ;
2017-08-16 21:34:06 +02:00
/*
* triggered buffer
* 1 x16 - bit channel + 3 x16 padding + 4 x16 timestamp
*/
s16 buffer [ 8 ] ;
2017-08-16 21:34:23 +02:00
/* Sensor-Type */
enum srf08_sensor_type sensor_type ;
/* Chip-specific information */
const struct srf08_chip_info * chip_info ;
2017-01-25 20:07:19 +01:00
} ;
/*
* in the documentation one can read about the " Gain " of the device
* which is used here for amplifying the signal and filtering out unwanted
* ones .
* But with ADC ' s this term is already used differently and that ' s why it
* is called " Sensitivity " here .
*/
2017-08-16 21:34:54 +02:00
static const struct srf08_chip_info srf02_chip_info = {
. sensitivity_avail = NULL ,
. num_sensitivity_avail = 0 ,
. sensitivity_default = 0 ,
. range_default = 0 ,
} ;
2017-08-16 21:34:23 +02:00
static const int srf08_sensitivity_avail [ ] = {
2017-01-25 20:07:19 +01:00
94 , 97 , 100 , 103 , 107 , 110 , 114 , 118 ,
123 , 128 , 133 , 139 , 145 , 152 , 159 , 168 ,
177 , 187 , 199 , 212 , 227 , 245 , 265 , 288 ,
2017-08-16 21:34:23 +02:00
317 , 352 , 395 , 450 , 524 , 626 , 777 , 1025
} ;
static const struct srf08_chip_info srf08_chip_info = {
. sensitivity_avail = srf08_sensitivity_avail ,
. num_sensitivity_avail = ARRAY_SIZE ( srf08_sensitivity_avail ) ,
. sensitivity_default = 1025 ,
2017-08-16 21:34:54 +02:00
. range_default = 6020 ,
2017-08-16 21:34:23 +02:00
} ;
static const int srf10_sensitivity_avail [ ] = {
40 , 40 , 50 , 60 , 70 , 80 , 100 , 120 ,
140 , 200 , 250 , 300 , 350 , 400 , 500 , 600 ,
700 ,
} ;
static const struct srf08_chip_info srf10_chip_info = {
. sensitivity_avail = srf10_sensitivity_avail ,
. num_sensitivity_avail = ARRAY_SIZE ( srf10_sensitivity_avail ) ,
. sensitivity_default = 700 ,
2017-08-16 21:34:54 +02:00
. range_default = 6020 ,
2017-08-16 21:34:23 +02:00
} ;
2017-01-25 20:07:19 +01:00
static int srf08_read_ranging ( struct srf08_data * data )
{
struct i2c_client * client = data - > client ;
int ret , i ;
int waittime ;
mutex_lock ( & data - > lock ) ;
ret = i2c_smbus_write_byte_data ( data - > client ,
SRF08_WRITE_COMMAND , SRF08_CMD_RANGING_CM ) ;
if ( ret < 0 ) {
dev_err ( & client - > dev , " write command - err: %d \n " , ret ) ;
mutex_unlock ( & data - > lock ) ;
return ret ;
}
/*
* we read here until a correct version number shows up as
* suggested by the documentation
*
* with an ultrasonic speed of 343 m / s and a roundtrip of it
* sleep the expected duration and try to read from the device
* if nothing useful is read try it in a shorter grid
*
* polling for not more than 20 ms should be enough
*/
waittime = 1 + data - > range_mm / 172 ;
msleep ( waittime ) ;
for ( i = 0 ; i < 4 ; i + + ) {
ret = i2c_smbus_read_byte_data ( data - > client ,
SRF08_READ_SW_REVISION ) ;
/* check if a valid version number is read */
if ( ret < 255 & & ret > 0 )
break ;
msleep ( 5 ) ;
}
if ( ret > = 255 | | ret < = 0 ) {
dev_err ( & client - > dev , " device not ready \n " ) ;
mutex_unlock ( & data - > lock ) ;
return - EIO ;
}
ret = i2c_smbus_read_word_swapped ( data - > client ,
SRF08_READ_ECHO_1_HIGH ) ;
if ( ret < 0 ) {
dev_err ( & client - > dev , " cannot read distance: ret=%d \n " , ret ) ;
mutex_unlock ( & data - > lock ) ;
return ret ;
}
mutex_unlock ( & data - > lock ) ;
return ret ;
}
2017-08-16 21:34:06 +02:00
static irqreturn_t srf08_trigger_handler ( int irq , void * p )
{
struct iio_poll_func * pf = p ;
struct iio_dev * indio_dev = pf - > indio_dev ;
struct srf08_data * data = iio_priv ( indio_dev ) ;
s16 sensor_data ;
sensor_data = srf08_read_ranging ( data ) ;
if ( sensor_data < 0 )
goto err ;
mutex_lock ( & data - > lock ) ;
data - > buffer [ 0 ] = sensor_data ;
iio_push_to_buffers_with_timestamp ( indio_dev ,
data - > buffer , pf - > timestamp ) ;
mutex_unlock ( & data - > lock ) ;
err :
iio_trigger_notify_done ( indio_dev - > trig ) ;
return IRQ_HANDLED ;
}
2017-01-25 20:07:19 +01:00
static int srf08_read_raw ( struct iio_dev * indio_dev ,
struct iio_chan_spec const * channel , int * val ,
int * val2 , long mask )
{
struct srf08_data * data = iio_priv ( indio_dev ) ;
int ret ;
if ( channel - > type ! = IIO_DISTANCE )
return - EINVAL ;
switch ( mask ) {
case IIO_CHAN_INFO_RAW :
ret = srf08_read_ranging ( data ) ;
if ( ret < 0 )
return ret ;
* val = ret ;
return IIO_VAL_INT ;
case IIO_CHAN_INFO_SCALE :
/* 1 LSB is 1 cm */
* val = 0 ;
* val2 = 10000 ;
return IIO_VAL_INT_PLUS_MICRO ;
default :
return - EINVAL ;
}
}
static ssize_t srf08_show_range_mm_available ( struct device * dev ,
struct device_attribute * attr , char * buf )
{
return sprintf ( buf , " [0.043 0.043 11.008] \n " ) ;
}
static IIO_DEVICE_ATTR ( sensor_max_range_available , S_IRUGO ,
srf08_show_range_mm_available , NULL , 0 ) ;
static ssize_t srf08_show_range_mm ( struct device * dev ,
struct device_attribute * attr , char * buf )
{
struct iio_dev * indio_dev = dev_to_iio_dev ( dev ) ;
struct srf08_data * data = iio_priv ( indio_dev ) ;
return sprintf ( buf , " %d.%03d \n " , data - > range_mm / 1000 ,
data - > range_mm % 1000 ) ;
}
/*
* set the range of the sensor to an even multiple of 43 mm
* which corresponds to 1 LSB in the register
*
* register value corresponding range
* 0x00 43 mm
* 0x01 86 mm
* 0x02 129 mm
* . . .
* 0xFF 11008 mm
*/
static ssize_t srf08_write_range_mm ( struct srf08_data * data , unsigned int val )
{
int ret ;
struct i2c_client * client = data - > client ;
unsigned int mod ;
u8 regval ;
ret = val / 43 - 1 ;
mod = val % 43 ;
if ( mod | | ( ret < 0 ) | | ( ret > 255 ) )
return - EINVAL ;
regval = ret ;
mutex_lock ( & data - > lock ) ;
ret = i2c_smbus_write_byte_data ( client , SRF08_WRITE_RANGE , regval ) ;
if ( ret < 0 ) {
dev_err ( & client - > dev , " write_range - err: %d \n " , ret ) ;
mutex_unlock ( & data - > lock ) ;
return ret ;
}
data - > range_mm = val ;
mutex_unlock ( & data - > lock ) ;
return 0 ;
}
static ssize_t srf08_store_range_mm ( struct device * dev ,
struct device_attribute * attr ,
const char * buf , size_t len )
{
struct iio_dev * indio_dev = dev_to_iio_dev ( dev ) ;
struct srf08_data * data = iio_priv ( indio_dev ) ;
int ret ;
int integer , fract ;
ret = iio_str_to_fixpoint ( buf , 100 , & integer , & fract ) ;
if ( ret )
return ret ;
ret = srf08_write_range_mm ( data , integer * 1000 + fract ) ;
if ( ret < 0 )
return ret ;
return len ;
}
static IIO_DEVICE_ATTR ( sensor_max_range , S_IRUGO | S_IWUSR ,
srf08_show_range_mm , srf08_store_range_mm , 0 ) ;
static ssize_t srf08_show_sensitivity_available ( struct device * dev ,
struct device_attribute * attr , char * buf )
{
int i , len = 0 ;
2017-08-16 21:34:23 +02:00
struct iio_dev * indio_dev = dev_to_iio_dev ( dev ) ;
struct srf08_data * data = iio_priv ( indio_dev ) ;
2017-01-25 20:07:19 +01:00
2017-08-16 21:34:23 +02:00
for ( i = 0 ; i < data - > chip_info - > num_sensitivity_avail ; i + + )
if ( data - > chip_info - > sensitivity_avail [ i ] )
len + = sprintf ( buf + len , " %d " ,
data - > chip_info - > sensitivity_avail [ i ] ) ;
2017-01-25 20:07:19 +01:00
len + = sprintf ( buf + len , " \n " ) ;
return len ;
}
static IIO_DEVICE_ATTR ( sensor_sensitivity_available , S_IRUGO ,
srf08_show_sensitivity_available , NULL , 0 ) ;
static ssize_t srf08_show_sensitivity ( struct device * dev ,
struct device_attribute * attr , char * buf )
{
struct iio_dev * indio_dev = dev_to_iio_dev ( dev ) ;
struct srf08_data * data = iio_priv ( indio_dev ) ;
int len ;
len = sprintf ( buf , " %d \n " , data - > sensitivity ) ;
return len ;
}
static ssize_t srf08_write_sensitivity ( struct srf08_data * data ,
unsigned int val )
{
struct i2c_client * client = data - > client ;
int ret , i ;
u8 regval ;
2017-08-16 21:34:23 +02:00
if ( ! val )
return - EINVAL ;
for ( i = 0 ; i < data - > chip_info - > num_sensitivity_avail ; i + + )
if ( val & & ( val = = data - > chip_info - > sensitivity_avail [ i ] ) ) {
2017-01-25 20:07:19 +01:00
regval = i ;
break ;
}
2017-08-16 21:34:23 +02:00
if ( i > = data - > chip_info - > num_sensitivity_avail )
2017-01-25 20:07:19 +01:00
return - EINVAL ;
mutex_lock ( & data - > lock ) ;
2017-08-16 21:34:23 +02:00
ret = i2c_smbus_write_byte_data ( client , SRF08_WRITE_MAX_GAIN , regval ) ;
2017-01-25 20:07:19 +01:00
if ( ret < 0 ) {
dev_err ( & client - > dev , " write_sensitivity - err: %d \n " , ret ) ;
mutex_unlock ( & data - > lock ) ;
return ret ;
}
data - > sensitivity = val ;
mutex_unlock ( & data - > lock ) ;
return 0 ;
}
static ssize_t srf08_store_sensitivity ( struct device * dev ,
struct device_attribute * attr ,
const char * buf , size_t len )
{
struct iio_dev * indio_dev = dev_to_iio_dev ( dev ) ;
struct srf08_data * data = iio_priv ( indio_dev ) ;
int ret ;
unsigned int val ;
ret = kstrtouint ( buf , 10 , & val ) ;
if ( ret )
return ret ;
ret = srf08_write_sensitivity ( data , val ) ;
if ( ret < 0 )
return ret ;
return len ;
}
static IIO_DEVICE_ATTR ( sensor_sensitivity , S_IRUGO | S_IWUSR ,
srf08_show_sensitivity , srf08_store_sensitivity , 0 ) ;
static struct attribute * srf08_attributes [ ] = {
& iio_dev_attr_sensor_max_range . dev_attr . attr ,
& iio_dev_attr_sensor_max_range_available . dev_attr . attr ,
& iio_dev_attr_sensor_sensitivity . dev_attr . attr ,
& iio_dev_attr_sensor_sensitivity_available . dev_attr . attr ,
NULL ,
} ;
static const struct attribute_group srf08_attribute_group = {
. attrs = srf08_attributes ,
} ;
static const struct iio_chan_spec srf08_channels [ ] = {
{
. type = IIO_DISTANCE ,
. info_mask_separate =
BIT ( IIO_CHAN_INFO_RAW ) |
BIT ( IIO_CHAN_INFO_SCALE ) ,
2017-08-16 21:34:06 +02:00
. scan_index = 0 ,
. scan_type = {
. sign = ' s ' ,
. realbits = 16 ,
. storagebits = 16 ,
. endianness = IIO_CPU ,
} ,
2017-01-25 20:07:19 +01:00
} ,
2017-08-16 21:34:06 +02:00
IIO_CHAN_SOFT_TIMESTAMP ( 1 ) ,
2017-01-25 20:07:19 +01:00
} ;
static const struct iio_info srf08_info = {
. read_raw = srf08_read_raw ,
. attrs = & srf08_attribute_group ,
} ;
2017-08-16 21:34:54 +02:00
/*
* srf02 don ' t have an adjustable range or sensitivity ,
* so we don ' t need attributes at all
*/
static const struct iio_info srf02_info = {
. read_raw = srf08_read_raw ,
} ;
2017-01-25 20:07:19 +01:00
static int srf08_probe ( struct i2c_client * client ,
const struct i2c_device_id * id )
{
struct iio_dev * indio_dev ;
struct srf08_data * data ;
int ret ;
if ( ! i2c_check_functionality ( client - > adapter ,
I2C_FUNC_SMBUS_READ_BYTE_DATA |
I2C_FUNC_SMBUS_WRITE_BYTE_DATA |
I2C_FUNC_SMBUS_READ_WORD_DATA ) )
return - ENODEV ;
indio_dev = devm_iio_device_alloc ( & client - > dev , sizeof ( * data ) ) ;
if ( ! indio_dev )
return - ENOMEM ;
data = iio_priv ( indio_dev ) ;
i2c_set_clientdata ( client , indio_dev ) ;
data - > client = client ;
2017-08-16 21:34:23 +02:00
data - > sensor_type = ( enum srf08_sensor_type ) id - > driver_data ;
switch ( data - > sensor_type ) {
2017-08-16 21:34:54 +02:00
case SRF02 :
data - > chip_info = & srf02_chip_info ;
indio_dev - > info = & srf02_info ;
break ;
2017-08-16 21:34:23 +02:00
case SRF08 :
data - > chip_info = & srf08_chip_info ;
2017-08-16 21:34:54 +02:00
indio_dev - > info = & srf08_info ;
2017-08-16 21:34:23 +02:00
break ;
case SRF10 :
data - > chip_info = & srf10_chip_info ;
2017-08-16 21:34:54 +02:00
indio_dev - > info = & srf08_info ;
2017-08-16 21:34:23 +02:00
break ;
default :
return - EINVAL ;
}
2017-01-25 20:07:19 +01:00
2017-08-16 21:34:23 +02:00
indio_dev - > name = id - > name ;
2017-01-25 20:07:19 +01:00
indio_dev - > dev . parent = & client - > dev ;
indio_dev - > modes = INDIO_DIRECT_MODE ;
indio_dev - > channels = srf08_channels ;
indio_dev - > num_channels = ARRAY_SIZE ( srf08_channels ) ;
mutex_init ( & data - > lock ) ;
2017-08-16 21:34:06 +02:00
ret = devm_iio_triggered_buffer_setup ( & client - > dev , indio_dev ,
iio_pollfunc_store_time , srf08_trigger_handler , NULL ) ;
if ( ret < 0 ) {
dev_err ( & client - > dev , " setup of iio triggered buffer failed \n " ) ;
return ret ;
}
2017-08-16 21:34:54 +02:00
if ( data - > chip_info - > range_default ) {
/*
* set default range of device in mm here
* these register values cannot be read from the hardware
* therefore set driver specific default values
*
* srf02 don ' t have a default value so it ' ll be omitted
*/
ret = srf08_write_range_mm ( data ,
data - > chip_info - > range_default ) ;
if ( ret < 0 )
return ret ;
}
2017-01-25 20:07:19 +01:00
2017-08-16 21:34:54 +02:00
if ( data - > chip_info - > sensitivity_default ) {
/*
* set default sensitivity of device here
* these register values cannot be read from the hardware
* therefore set driver specific default values
*
* srf02 don ' t have a default value so it ' ll be omitted
*/
ret = srf08_write_sensitivity ( data ,
2017-08-16 21:34:23 +02:00
data - > chip_info - > sensitivity_default ) ;
2017-08-16 21:34:54 +02:00
if ( ret < 0 )
return ret ;
}
2017-01-25 20:07:19 +01:00
return devm_iio_device_register ( & client - > dev , indio_dev ) ;
}
2017-08-16 21:33:47 +02:00
static const struct of_device_id of_srf08_match [ ] = {
2017-08-16 21:34:54 +02:00
{ . compatible = " devantech,srf02 " , ( void * ) SRF02 } ,
2017-08-16 21:34:23 +02:00
{ . compatible = " devantech,srf08 " , ( void * ) SRF08 } ,
{ . compatible = " devantech,srf10 " , ( void * ) SRF10 } ,
2017-08-16 21:33:47 +02:00
{ } ,
} ;
MODULE_DEVICE_TABLE ( of , of_srf08_match ) ;
2017-01-25 20:07:19 +01:00
static const struct i2c_device_id srf08_id [ ] = {
2017-08-16 21:34:54 +02:00
{ " srf02 " , SRF02 } ,
2017-08-16 21:34:23 +02:00
{ " srf08 " , SRF08 } ,
{ " srf10 " , SRF10 } ,
2017-01-25 20:07:19 +01:00
{ }
} ;
MODULE_DEVICE_TABLE ( i2c , srf08_id ) ;
static struct i2c_driver srf08_driver = {
. driver = {
. name = " srf08 " ,
2017-08-16 21:33:47 +02:00
. of_match_table = of_srf08_match ,
2017-01-25 20:07:19 +01:00
} ,
. probe = srf08_probe ,
. id_table = srf08_id ,
} ;
module_i2c_driver ( srf08_driver ) ;
MODULE_AUTHOR ( " Andreas Klinger <ak@it-klinger.de> " ) ;
2017-08-16 21:34:54 +02:00
MODULE_DESCRIPTION ( " Devantech SRF02/SRF08/SRF10 i2c ultrasonic ranger driver " ) ;
2017-01-25 20:07:19 +01:00
MODULE_LICENSE ( " GPL " ) ;