2019-03-17 23:38:03 +03:00
// SPDX-License-Identifier: GPL-2.0+
/*
* mb1232 . c - Support for MaxBotix I2CXL - MaxSonar - EZ series ultrasonic
* ranger with i2c interface
* actually tested with mb1232 type
*
* Copyright ( c ) 2019 Andreas Klinger < ak @ it - klinger . de >
*
* For details about the device see :
* https : //www.maxbotix.com/documents/I2CXL-MaxSonar-EZ_Datasheet.pdf
*/
# include <linux/err.h>
# include <linux/i2c.h>
# include <linux/of_irq.h>
# include <linux/delay.h>
# include <linux/module.h>
# include <linux/bitops.h>
# include <linux/iio/iio.h>
# include <linux/iio/sysfs.h>
# include <linux/iio/buffer.h>
# include <linux/iio/trigger_consumer.h>
# include <linux/iio/triggered_buffer.h>
/* registers of MaxSonar device */
# define MB1232_RANGE_COMMAND 0x51 /* Command for reading range */
# define MB1232_ADDR_UNLOCK_1 0xAA /* Command 1 for changing address */
# define MB1232_ADDR_UNLOCK_2 0xA5 /* Command 2 for changing address */
struct mb1232_data {
struct i2c_client * client ;
struct mutex lock ;
/*
* optionally a gpio can be used to announce when ranging has
* finished
* since we are just using the falling trigger of it we request
* only the interrupt for announcing when data is ready to be read
*/
struct completion ranging ;
int irqnr ;
2020-07-22 18:50:42 +03:00
/* Ensure correct alignment of data to push to IIO buffer */
struct {
s16 distance ;
s64 ts __aligned ( 8 ) ;
} scan ;
2019-03-17 23:38:03 +03:00
} ;
static irqreturn_t mb1232_handle_irq ( int irq , void * dev_id )
{
struct iio_dev * indio_dev = dev_id ;
struct mb1232_data * data = iio_priv ( indio_dev ) ;
complete ( & data - > ranging ) ;
return IRQ_HANDLED ;
}
static s16 mb1232_read_distance ( struct mb1232_data * data )
{
struct i2c_client * client = data - > client ;
int ret ;
s16 distance ;
__be16 buf ;
mutex_lock ( & data - > lock ) ;
reinit_completion ( & data - > ranging ) ;
ret = i2c_smbus_write_byte ( client , MB1232_RANGE_COMMAND ) ;
if ( ret < 0 ) {
dev_err ( & client - > dev , " write command - err: %d \n " , ret ) ;
goto error_unlock ;
}
if ( data - > irqnr > = 0 ) {
/* it cannot take more than 100 ms */
ret = wait_for_completion_killable_timeout ( & data - > ranging ,
HZ / 10 ) ;
if ( ret < 0 )
goto error_unlock ;
else if ( ret = = 0 ) {
ret = - ETIMEDOUT ;
goto error_unlock ;
}
} else {
/* use simple sleep if announce irq is not connected */
msleep ( 15 ) ;
}
ret = i2c_master_recv ( client , ( char * ) & buf , sizeof ( buf ) ) ;
if ( ret < 0 ) {
dev_err ( & client - > dev , " i2c_master_recv: ret=%d \n " , ret ) ;
goto error_unlock ;
}
distance = __be16_to_cpu ( buf ) ;
/* check for not returning misleading error codes */
if ( distance < 0 ) {
dev_err ( & client - > dev , " distance=%d \n " , distance ) ;
ret = - EINVAL ;
goto error_unlock ;
}
mutex_unlock ( & data - > lock ) ;
return distance ;
error_unlock :
mutex_unlock ( & data - > lock ) ;
return ret ;
}
static irqreturn_t mb1232_trigger_handler ( int irq , void * p )
{
struct iio_poll_func * pf = p ;
struct iio_dev * indio_dev = pf - > indio_dev ;
struct mb1232_data * data = iio_priv ( indio_dev ) ;
2020-07-22 18:50:42 +03:00
data - > scan . distance = mb1232_read_distance ( data ) ;
if ( data - > scan . distance < 0 )
2019-03-17 23:38:03 +03:00
goto err ;
2020-07-22 18:50:42 +03:00
iio_push_to_buffers_with_timestamp ( indio_dev , & data - > scan ,
pf - > timestamp ) ;
2019-03-17 23:38:03 +03:00
err :
iio_trigger_notify_done ( indio_dev - > trig ) ;
return IRQ_HANDLED ;
}
static int mb1232_read_raw ( struct iio_dev * indio_dev ,
struct iio_chan_spec const * channel , int * val ,
int * val2 , long mask )
{
struct mb1232_data * data = iio_priv ( indio_dev ) ;
int ret ;
if ( channel - > type ! = IIO_DISTANCE )
return - EINVAL ;
switch ( mask ) {
case IIO_CHAN_INFO_RAW :
ret = mb1232_read_distance ( 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 const struct iio_chan_spec mb1232_channels [ ] = {
{
. type = IIO_DISTANCE ,
. info_mask_separate = BIT ( IIO_CHAN_INFO_RAW ) |
BIT ( IIO_CHAN_INFO_SCALE ) ,
. scan_index = 0 ,
. scan_type = {
. sign = ' s ' ,
. realbits = 16 ,
. storagebits = 16 ,
. endianness = IIO_CPU ,
} ,
} ,
IIO_CHAN_SOFT_TIMESTAMP ( 1 ) ,
} ;
static const struct iio_info mb1232_info = {
. read_raw = mb1232_read_raw ,
} ;
static int mb1232_probe ( struct i2c_client * client ,
const struct i2c_device_id * id )
{
struct iio_dev * indio_dev ;
struct mb1232_data * data ;
int ret ;
struct device * dev = & client - > dev ;
if ( ! i2c_check_functionality ( client - > adapter ,
I2C_FUNC_SMBUS_READ_BYTE |
I2C_FUNC_SMBUS_WRITE_BYTE ) )
return - ENODEV ;
indio_dev = devm_iio_device_alloc ( dev , sizeof ( * data ) ) ;
if ( ! indio_dev )
return - ENOMEM ;
data = iio_priv ( indio_dev ) ;
i2c_set_clientdata ( client , indio_dev ) ;
data - > client = client ;
indio_dev - > info = & mb1232_info ;
indio_dev - > name = id - > name ;
indio_dev - > modes = INDIO_DIRECT_MODE ;
indio_dev - > channels = mb1232_channels ;
indio_dev - > num_channels = ARRAY_SIZE ( mb1232_channels ) ;
mutex_init ( & data - > lock ) ;
init_completion ( & data - > ranging ) ;
data - > irqnr = irq_of_parse_and_map ( dev - > of_node , 0 ) ;
if ( data - > irqnr < = 0 ) {
/* usage of interrupt is optional */
data - > irqnr = - 1 ;
} else {
ret = devm_request_irq ( dev , data - > irqnr , mb1232_handle_irq ,
IRQF_TRIGGER_FALLING , id - > name , indio_dev ) ;
if ( ret < 0 ) {
dev_err ( dev , " request_irq: %d \n " , ret ) ;
return ret ;
}
}
ret = devm_iio_triggered_buffer_setup ( dev , indio_dev ,
iio_pollfunc_store_time , mb1232_trigger_handler , NULL ) ;
if ( ret < 0 ) {
dev_err ( dev , " setup of iio triggered buffer failed \n " ) ;
return ret ;
}
return devm_iio_device_register ( dev , indio_dev ) ;
}
static const struct of_device_id of_mb1232_match [ ] = {
{ . compatible = " maxbotix,mb1202 " , } ,
{ . compatible = " maxbotix,mb1212 " , } ,
{ . compatible = " maxbotix,mb1222 " , } ,
{ . compatible = " maxbotix,mb1232 " , } ,
{ . compatible = " maxbotix,mb1242 " , } ,
{ . compatible = " maxbotix,mb7040 " , } ,
{ . compatible = " maxbotix,mb7137 " , } ,
{ } ,
} ;
MODULE_DEVICE_TABLE ( of , of_mb1232_match ) ;
static const struct i2c_device_id mb1232_id [ ] = {
{ " maxbotix-mb1202 " , } ,
{ " maxbotix-mb1212 " , } ,
{ " maxbotix-mb1222 " , } ,
{ " maxbotix-mb1232 " , } ,
{ " maxbotix-mb1242 " , } ,
{ " maxbotix-mb7040 " , } ,
{ " maxbotix-mb7137 " , } ,
{ }
} ;
MODULE_DEVICE_TABLE ( i2c , mb1232_id ) ;
static struct i2c_driver mb1232_driver = {
. driver = {
. name = " maxbotix-mb1232 " ,
. of_match_table = of_mb1232_match ,
} ,
. probe = mb1232_probe ,
. id_table = mb1232_id ,
} ;
module_i2c_driver ( mb1232_driver ) ;
MODULE_AUTHOR ( " Andreas Klinger <ak@it-klinger.de> " ) ;
MODULE_DESCRIPTION ( " Maxbotix I2CXL-MaxSonar i2c ultrasonic ranger driver " ) ;
MODULE_LICENSE ( " GPL " ) ;