2018-02-17 21:36:46 -08:00
// SPDX-License-Identifier: GPL-2.0+
2015-08-18 07:40:38 -07:00
/*
* pulsedlight - lidar - lite - v2 . c - Support for PulsedLight LIDAR sensor
*
2018-02-17 21:36:46 -08:00
* Copyright ( C ) 2015 , 2017 - 2018
* Author : Matt Ranostay < matt . ranostay @ konsulko . com >
2015-08-18 07:40:38 -07:00
*
2015-11-15 17:20:05 -08:00
* TODO : interrupt mode , and signal strength reporting
2015-08-18 07:40:38 -07:00
*/
# include <linux/err.h>
# include <linux/init.h>
# include <linux/i2c.h>
# include <linux/delay.h>
# include <linux/module.h>
2015-11-15 17:20:05 -08:00
# include <linux/pm_runtime.h>
2015-08-18 07:40:38 -07:00
# include <linux/iio/iio.h>
# include <linux/iio/sysfs.h>
# include <linux/iio/buffer.h>
# include <linux/iio/trigger.h>
# include <linux/iio/triggered_buffer.h>
# include <linux/iio/trigger_consumer.h>
# define LIDAR_REG_CONTROL 0x00
# define LIDAR_REG_CONTROL_ACQUIRE BIT(2)
# define LIDAR_REG_STATUS 0x01
# define LIDAR_REG_STATUS_INVALID BIT(3)
# define LIDAR_REG_STATUS_READY BIT(0)
2015-12-01 21:05:22 -08:00
# define LIDAR_REG_DATA_HBYTE 0x0f
# define LIDAR_REG_DATA_LBYTE 0x10
# define LIDAR_REG_DATA_WORD_READ BIT(7)
2015-11-15 17:20:05 -08:00
# define LIDAR_REG_PWR_CONTROL 0x65
2015-08-18 07:40:38 -07:00
# define LIDAR_DRV_NAME "lidar"
struct lidar_data {
struct iio_dev * indio_dev ;
struct i2c_client * client ;
2015-12-01 21:05:22 -08:00
int ( * xfer ) ( struct lidar_data * data , u8 reg , u8 * val , int len ) ;
int i2c_enabled ;
2015-08-18 07:40:38 -07:00
u16 buffer [ 8 ] ; /* 2 byte distance + 8 byte timestamp */
} ;
static const struct iio_chan_spec lidar_channels [ ] = {
{
. type = IIO_DISTANCE ,
. info_mask_separate =
BIT ( IIO_CHAN_INFO_RAW ) | BIT ( IIO_CHAN_INFO_SCALE ) ,
. scan_index = 0 ,
. scan_type = {
. sign = ' u ' ,
. realbits = 16 ,
. storagebits = 16 ,
} ,
} ,
IIO_CHAN_SOFT_TIMESTAMP ( 1 ) ,
} ;
2015-12-01 21:05:22 -08:00
static int lidar_i2c_xfer ( struct lidar_data * data , u8 reg , u8 * val , int len )
{
struct i2c_client * client = data - > client ;
struct i2c_msg msg [ 2 ] ;
int ret ;
msg [ 0 ] . addr = client - > addr ;
msg [ 0 ] . flags = client - > flags | I2C_M_STOP ;
msg [ 0 ] . len = 1 ;
msg [ 0 ] . buf = ( char * ) & reg ;
msg [ 1 ] . addr = client - > addr ;
msg [ 1 ] . flags = client - > flags | I2C_M_RD ;
msg [ 1 ] . len = len ;
msg [ 1 ] . buf = ( char * ) val ;
ret = i2c_transfer ( client - > adapter , msg , 2 ) ;
2015-12-26 20:56:30 -08:00
return ( ret = = 2 ) ? 0 : - EIO ;
2015-12-01 21:05:22 -08:00
}
static int lidar_smbus_xfer ( struct lidar_data * data , u8 reg , u8 * val , int len )
2015-08-18 07:40:38 -07:00
{
struct i2c_client * client = data - > client ;
int ret ;
/*
* Device needs a STOP condition between address write , and data read
* so in turn i2c_smbus_read_byte_data cannot be used
*/
2015-12-01 21:05:22 -08:00
while ( len - - ) {
ret = i2c_smbus_write_byte ( client , reg + + ) ;
if ( ret < 0 ) {
dev_err ( & client - > dev , " cannot write addr value " ) ;
return ret ;
}
ret = i2c_smbus_read_byte ( client ) ;
if ( ret < 0 ) {
dev_err ( & client - > dev , " cannot read data value " ) ;
return ret ;
}
* ( val + + ) = ret ;
2015-08-18 07:40:38 -07:00
}
2015-12-01 21:05:22 -08:00
return 0 ;
}
static int lidar_read_byte ( struct lidar_data * data , u8 reg )
{
int ret ;
u8 val ;
ret = data - > xfer ( data , reg , & val , 1 ) ;
2015-08-18 07:40:38 -07:00
if ( ret < 0 )
2015-12-01 21:05:22 -08:00
return ret ;
2015-08-18 07:40:38 -07:00
2015-12-01 21:05:22 -08:00
return val ;
2015-08-18 07:40:38 -07:00
}
static inline int lidar_write_control ( struct lidar_data * data , int val )
{
return i2c_smbus_write_byte_data ( data - > client , LIDAR_REG_CONTROL , val ) ;
}
2015-11-15 17:20:05 -08:00
static inline int lidar_write_power ( struct lidar_data * data , int val )
{
return i2c_smbus_write_byte_data ( data - > client ,
LIDAR_REG_PWR_CONTROL , val ) ;
}
2015-08-18 07:40:38 -07:00
static int lidar_read_measurement ( struct lidar_data * data , u16 * reg )
{
2019-10-13 11:26:00 +01:00
__be16 value ;
2015-12-01 21:05:22 -08:00
int ret = data - > xfer ( data , LIDAR_REG_DATA_HBYTE |
( data - > i2c_enabled ? LIDAR_REG_DATA_WORD_READ : 0 ) ,
2019-10-13 11:26:00 +01:00
( u8 * ) & value , 2 ) ;
2015-08-18 07:40:38 -07:00
2015-12-01 21:05:22 -08:00
if ( ! ret )
2019-10-13 11:26:00 +01:00
* reg = be16_to_cpu ( value ) ;
2015-08-18 07:40:38 -07:00
2015-12-01 21:05:22 -08:00
return ret ;
2015-08-18 07:40:38 -07:00
}
static int lidar_get_measurement ( struct lidar_data * data , u16 * reg )
{
struct i2c_client * client = data - > client ;
int tries = 10 ;
int ret ;
2015-11-15 17:20:05 -08:00
pm_runtime_get_sync ( & client - > dev ) ;
2015-08-18 07:40:38 -07:00
/* start sample */
ret = lidar_write_control ( data , LIDAR_REG_CONTROL_ACQUIRE ) ;
if ( ret < 0 ) {
dev_err ( & client - > dev , " cannot send start measurement command " ) ;
return ret ;
}
while ( tries - - ) {
usleep_range ( 1000 , 2000 ) ;
ret = lidar_read_byte ( data , LIDAR_REG_STATUS ) ;
if ( ret < 0 )
break ;
2015-10-26 20:18:23 -07:00
/* return -EINVAL since laser is likely pointed out of range */
2015-08-18 07:40:38 -07:00
if ( ret & LIDAR_REG_STATUS_INVALID ) {
* reg = 0 ;
2015-10-26 20:18:23 -07:00
ret = - EINVAL ;
2015-08-18 07:40:38 -07:00
break ;
}
/* sample ready to read */
if ( ! ( ret & LIDAR_REG_STATUS_READY ) ) {
ret = lidar_read_measurement ( data , reg ) ;
break ;
}
ret = - EIO ;
}
2015-11-15 17:20:05 -08:00
pm_runtime_mark_last_busy ( & client - > dev ) ;
pm_runtime_put_autosuspend ( & client - > dev ) ;
2015-08-18 07:40:38 -07:00
return ret ;
}
static int lidar_read_raw ( struct iio_dev * indio_dev ,
struct iio_chan_spec const * chan ,
int * val , int * val2 , long mask )
{
struct lidar_data * data = iio_priv ( indio_dev ) ;
int ret = - EINVAL ;
switch ( mask ) {
case IIO_CHAN_INFO_RAW : {
u16 reg ;
2016-06-07 21:18:04 -07:00
if ( iio_device_claim_direct_mode ( indio_dev ) )
return - EBUSY ;
2015-08-18 07:40:38 -07:00
ret = lidar_get_measurement ( data , & reg ) ;
if ( ! ret ) {
* val = reg ;
ret = IIO_VAL_INT ;
}
2016-06-07 21:18:04 -07:00
iio_device_release_direct_mode ( indio_dev ) ;
2015-08-18 07:40:38 -07:00
break ;
}
case IIO_CHAN_INFO_SCALE :
* val = 0 ;
* val2 = 10000 ;
ret = IIO_VAL_INT_PLUS_MICRO ;
break ;
}
return ret ;
}
static irqreturn_t lidar_trigger_handler ( int irq , void * private )
{
struct iio_poll_func * pf = private ;
struct iio_dev * indio_dev = pf - > indio_dev ;
struct lidar_data * data = iio_priv ( indio_dev ) ;
int ret ;
ret = lidar_get_measurement ( data , data - > buffer ) ;
if ( ! ret ) {
iio_push_to_buffers_with_timestamp ( indio_dev , data - > buffer ,
2016-03-09 19:05:49 +01:00
iio_get_time_ns ( indio_dev ) ) ;
2015-10-26 20:18:23 -07:00
} else if ( ret ! = - EINVAL ) {
2015-08-18 07:40:38 -07:00
dev_err ( & data - > client - > dev , " cannot read LIDAR measurement " ) ;
}
iio_trigger_notify_done ( indio_dev - > trig ) ;
return IRQ_HANDLED ;
}
static const struct iio_info lidar_info = {
. read_raw = lidar_read_raw ,
} ;
static int lidar_probe ( struct i2c_client * client ,
const struct i2c_device_id * id )
{
struct lidar_data * data ;
struct iio_dev * indio_dev ;
int ret ;
indio_dev = devm_iio_device_alloc ( & client - > dev , sizeof ( * data ) ) ;
if ( ! indio_dev )
return - ENOMEM ;
2015-12-01 21:05:22 -08:00
data = iio_priv ( indio_dev ) ;
if ( i2c_check_functionality ( client - > adapter , I2C_FUNC_I2C ) ) {
data - > xfer = lidar_i2c_xfer ;
data - > i2c_enabled = 1 ;
} else if ( i2c_check_functionality ( client - > adapter ,
I2C_FUNC_SMBUS_WORD_DATA | I2C_FUNC_SMBUS_BYTE ) )
data - > xfer = lidar_smbus_xfer ;
else
2016-02-26 22:13:49 -08:00
return - EOPNOTSUPP ;
2015-08-18 07:40:38 -07:00
indio_dev - > info = & lidar_info ;
indio_dev - > name = LIDAR_DRV_NAME ;
indio_dev - > channels = lidar_channels ;
indio_dev - > num_channels = ARRAY_SIZE ( lidar_channels ) ;
2017-02-11 13:34:20 +01:00
indio_dev - > dev . parent = & client - > dev ;
2015-08-18 07:40:38 -07:00
indio_dev - > modes = INDIO_DIRECT_MODE ;
i2c_set_clientdata ( client , indio_dev ) ;
data - > client = client ;
data - > indio_dev = indio_dev ;
ret = iio_triggered_buffer_setup ( indio_dev , NULL ,
lidar_trigger_handler , NULL ) ;
if ( ret )
return ret ;
ret = iio_device_register ( indio_dev ) ;
if ( ret )
goto error_unreg_buffer ;
2015-11-15 17:20:05 -08:00
pm_runtime_set_autosuspend_delay ( & client - > dev , 1000 ) ;
pm_runtime_use_autosuspend ( & client - > dev ) ;
ret = pm_runtime_set_active ( & client - > dev ) ;
if ( ret )
goto error_unreg_buffer ;
pm_runtime_enable ( & client - > dev ) ;
pm_runtime_idle ( & client - > dev ) ;
2015-08-18 07:40:38 -07:00
return 0 ;
error_unreg_buffer :
iio_triggered_buffer_cleanup ( indio_dev ) ;
return ret ;
}
static int lidar_remove ( struct i2c_client * client )
{
struct iio_dev * indio_dev = i2c_get_clientdata ( client ) ;
iio_device_unregister ( indio_dev ) ;
iio_triggered_buffer_cleanup ( indio_dev ) ;
2015-11-15 17:20:05 -08:00
pm_runtime_disable ( & client - > dev ) ;
pm_runtime_set_suspended ( & client - > dev ) ;
2015-08-18 07:40:38 -07:00
return 0 ;
}
static const struct i2c_device_id lidar_id [ ] = {
{ " lidar-lite-v2 " , 0 } ,
2016-12-28 21:13:06 -08:00
{ " lidar-lite-v3 " , 0 } ,
2015-08-18 07:40:38 -07:00
{ } ,
} ;
MODULE_DEVICE_TABLE ( i2c , lidar_id ) ;
static const struct of_device_id lidar_dt_ids [ ] = {
{ . compatible = " pulsedlight,lidar-lite-v2 " } ,
2016-12-28 21:13:06 -08:00
{ . compatible = " grmn,lidar-lite-v3 " } ,
2015-08-18 07:40:38 -07:00
{ }
} ;
2015-09-22 23:22:03 -07:00
MODULE_DEVICE_TABLE ( of , lidar_dt_ids ) ;
2015-08-18 07:40:38 -07:00
2015-11-15 17:20:05 -08:00
# ifdef CONFIG_PM
static int lidar_pm_runtime_suspend ( struct device * dev )
{
struct iio_dev * indio_dev = i2c_get_clientdata ( to_i2c_client ( dev ) ) ;
struct lidar_data * data = iio_priv ( indio_dev ) ;
return lidar_write_power ( data , 0x0f ) ;
}
static int lidar_pm_runtime_resume ( struct device * dev )
{
struct iio_dev * indio_dev = i2c_get_clientdata ( to_i2c_client ( dev ) ) ;
struct lidar_data * data = iio_priv ( indio_dev ) ;
int ret = lidar_write_power ( data , 0 ) ;
/* regulator and FPGA needs settling time */
usleep_range ( 15000 , 20000 ) ;
return ret ;
}
# endif
static const struct dev_pm_ops lidar_pm_ops = {
SET_RUNTIME_PM_OPS ( lidar_pm_runtime_suspend ,
lidar_pm_runtime_resume , NULL )
} ;
2015-08-18 07:40:38 -07:00
static struct i2c_driver lidar_driver = {
. driver = {
. name = LIDAR_DRV_NAME ,
. of_match_table = of_match_ptr ( lidar_dt_ids ) ,
2015-11-15 17:20:05 -08:00
. pm = & lidar_pm_ops ,
2015-08-18 07:40:38 -07:00
} ,
. probe = lidar_probe ,
. remove = lidar_remove ,
. id_table = lidar_id ,
} ;
module_i2c_driver ( lidar_driver ) ;
2018-02-17 21:36:46 -08:00
MODULE_AUTHOR ( " Matt Ranostay <matt.ranostay@konsulko.com> " ) ;
2015-08-18 07:40:38 -07:00
MODULE_DESCRIPTION ( " PulsedLight LIDAR sensor " ) ;
MODULE_LICENSE ( " GPL " ) ;