2019-05-30 02:57:44 +03:00
// SPDX-License-Identifier: GPL-2.0-only
2017-10-10 16:48:47 +03:00
/*
* rfd77402 . c - Support for RF Digital RFD77402 Time - of - Flight ( distance ) sensor
*
* Copyright 2017 Peter Meerwald - Stadler < pmeerw @ pmeerw . net >
*
* 7 - bit I2C slave address 0x4c
*
* TODO : interrupt
* https : //media.digikey.com/pdf/Data%20Sheets/RF%20Digital%20PDFs/RFD77402.pdf
*/
# include <linux/module.h>
# include <linux/i2c.h>
# include <linux/delay.h>
# include <linux/iio/iio.h>
# define RFD77402_DRV_NAME "rfd77402"
# define RFD77402_ICSR 0x00 /* Interrupt Control Status Register */
# define RFD77402_ICSR_INT_MODE BIT(2)
# define RFD77402_ICSR_INT_POL BIT(3)
# define RFD77402_ICSR_RESULT BIT(4)
# define RFD77402_ICSR_M2H_MSG BIT(5)
# define RFD77402_ICSR_H2M_MSG BIT(6)
# define RFD77402_ICSR_RESET BIT(7)
# define RFD77402_CMD_R 0x04
# define RFD77402_CMD_SINGLE 0x01
# define RFD77402_CMD_STANDBY 0x10
# define RFD77402_CMD_MCPU_OFF 0x11
# define RFD77402_CMD_MCPU_ON 0x12
# define RFD77402_CMD_RESET BIT(6)
# define RFD77402_CMD_VALID BIT(7)
# define RFD77402_STATUS_R 0x06
# define RFD77402_STATUS_PM_MASK GENMASK(4, 0)
# define RFD77402_STATUS_STANDBY 0x00
# define RFD77402_STATUS_MCPU_OFF 0x10
# define RFD77402_STATUS_MCPU_ON 0x18
# define RFD77402_RESULT_R 0x08
# define RFD77402_RESULT_DIST_MASK GENMASK(12, 2)
# define RFD77402_RESULT_ERR_MASK GENMASK(14, 13)
# define RFD77402_RESULT_VALID BIT(15)
# define RFD77402_PMU_CFG 0x14
# define RFD77402_PMU_MCPU_INIT BIT(9)
# define RFD77402_I2C_INIT_CFG 0x1c
# define RFD77402_I2C_ADDR_INCR BIT(0)
# define RFD77402_I2C_DATA_INCR BIT(2)
# define RFD77402_I2C_HOST_DEBUG BIT(5)
# define RFD77402_I2C_MCPU_DEBUG BIT(6)
# define RFD77402_CMD_CFGR_A 0x0c
# define RFD77402_CMD_CFGR_B 0x0e
# define RFD77402_HFCFG_0 0x20
# define RFD77402_HFCFG_1 0x22
# define RFD77402_HFCFG_2 0x24
# define RFD77402_HFCFG_3 0x26
# define RFD77402_MOD_CHIP_ID 0x28
/* magic configuration values from datasheet */
static const struct {
u8 reg ;
u16 val ;
} rf77402_tof_config [ ] = {
{ RFD77402_CMD_CFGR_A , 0xe100 } ,
{ RFD77402_CMD_CFGR_B , 0x10ff } ,
{ RFD77402_HFCFG_0 , 0x07d0 } ,
{ RFD77402_HFCFG_1 , 0x5008 } ,
{ RFD77402_HFCFG_2 , 0xa041 } ,
{ RFD77402_HFCFG_3 , 0x45d4 } ,
} ;
struct rfd77402_data {
struct i2c_client * client ;
/* Serialize reads from the sensor */
struct mutex lock ;
} ;
static const struct iio_chan_spec rfd77402_channels [ ] = {
{
. type = IIO_DISTANCE ,
. info_mask_separate = BIT ( IIO_CHAN_INFO_RAW ) |
BIT ( IIO_CHAN_INFO_SCALE ) ,
} ,
} ;
2021-06-30 16:16:35 +03:00
static int rfd77402_set_state ( struct i2c_client * client , u8 state , u16 check )
2017-10-10 16:48:47 +03:00
{
int ret ;
2021-06-30 16:16:35 +03:00
ret = i2c_smbus_write_byte_data ( client , RFD77402_CMD_R ,
2017-10-10 16:48:47 +03:00
state | RFD77402_CMD_VALID ) ;
if ( ret < 0 )
return ret ;
usleep_range ( 10000 , 20000 ) ;
2021-06-30 16:16:35 +03:00
ret = i2c_smbus_read_word_data ( client , RFD77402_STATUS_R ) ;
2017-10-10 16:48:47 +03:00
if ( ret < 0 )
return ret ;
if ( ( ret & RFD77402_STATUS_PM_MASK ) ! = check )
return - ENODEV ;
return 0 ;
}
2021-06-30 16:16:35 +03:00
static int rfd77402_measure ( struct i2c_client * client )
2017-10-10 16:48:47 +03:00
{
int ret ;
int tries = 10 ;
2021-06-30 16:16:35 +03:00
ret = rfd77402_set_state ( client , RFD77402_CMD_MCPU_ON ,
2017-10-10 16:48:47 +03:00
RFD77402_STATUS_MCPU_ON ) ;
if ( ret < 0 )
return ret ;
2021-06-30 16:16:35 +03:00
ret = i2c_smbus_write_byte_data ( client , RFD77402_CMD_R ,
2017-10-10 16:48:47 +03:00
RFD77402_CMD_SINGLE |
RFD77402_CMD_VALID ) ;
if ( ret < 0 )
goto err ;
while ( tries - - > 0 ) {
2021-06-30 16:16:35 +03:00
ret = i2c_smbus_read_byte_data ( client , RFD77402_ICSR ) ;
2017-10-10 16:48:47 +03:00
if ( ret < 0 )
goto err ;
if ( ret & RFD77402_ICSR_RESULT )
break ;
msleep ( 20 ) ;
}
if ( tries < 0 ) {
ret = - ETIMEDOUT ;
goto err ;
}
2021-06-30 16:16:35 +03:00
ret = i2c_smbus_read_word_data ( client , RFD77402_RESULT_R ) ;
2017-10-10 16:48:47 +03:00
if ( ret < 0 )
goto err ;
if ( ( ret & RFD77402_RESULT_ERR_MASK ) | |
! ( ret & RFD77402_RESULT_VALID ) ) {
ret = - EIO ;
goto err ;
}
return ( ret & RFD77402_RESULT_DIST_MASK ) > > 2 ;
err :
2021-06-30 16:16:35 +03:00
rfd77402_set_state ( client , RFD77402_CMD_MCPU_OFF ,
2017-10-10 16:48:47 +03:00
RFD77402_STATUS_MCPU_OFF ) ;
return ret ;
}
static int rfd77402_read_raw ( struct iio_dev * indio_dev ,
struct iio_chan_spec const * chan ,
int * val , int * val2 , long mask )
{
struct rfd77402_data * data = iio_priv ( indio_dev ) ;
int ret ;
switch ( mask ) {
case IIO_CHAN_INFO_RAW :
mutex_lock ( & data - > lock ) ;
2021-06-30 16:16:35 +03:00
ret = rfd77402_measure ( data - > client ) ;
2017-10-10 16:48:47 +03:00
mutex_unlock ( & data - > lock ) ;
if ( ret < 0 )
return ret ;
* val = ret ;
return IIO_VAL_INT ;
case IIO_CHAN_INFO_SCALE :
/* 1 LSB is 1 mm */
* val = 0 ;
* val2 = 1000 ;
return IIO_VAL_INT_PLUS_MICRO ;
default :
return - EINVAL ;
}
}
static const struct iio_info rfd77402_info = {
. read_raw = rfd77402_read_raw ,
} ;
2021-06-30 16:16:35 +03:00
static int rfd77402_init ( struct i2c_client * client )
2017-10-10 16:48:47 +03:00
{
int ret , i ;
2021-06-30 16:16:35 +03:00
ret = rfd77402_set_state ( client , RFD77402_CMD_STANDBY ,
2017-10-10 16:48:47 +03:00
RFD77402_STATUS_STANDBY ) ;
if ( ret < 0 )
return ret ;
/* configure INT pad as push-pull, active low */
2021-06-30 16:16:35 +03:00
ret = i2c_smbus_write_byte_data ( client , RFD77402_ICSR ,
2017-10-10 16:48:47 +03:00
RFD77402_ICSR_INT_MODE ) ;
if ( ret < 0 )
return ret ;
/* I2C configuration */
2021-06-30 16:16:35 +03:00
ret = i2c_smbus_write_word_data ( client , RFD77402_I2C_INIT_CFG ,
2017-10-10 16:48:47 +03:00
RFD77402_I2C_ADDR_INCR |
RFD77402_I2C_DATA_INCR |
RFD77402_I2C_HOST_DEBUG |
RFD77402_I2C_MCPU_DEBUG ) ;
if ( ret < 0 )
return ret ;
/* set initialization */
2021-06-30 16:16:35 +03:00
ret = i2c_smbus_write_word_data ( client , RFD77402_PMU_CFG , 0x0500 ) ;
2017-10-10 16:48:47 +03:00
if ( ret < 0 )
return ret ;
2021-06-30 16:16:35 +03:00
ret = rfd77402_set_state ( client , RFD77402_CMD_MCPU_OFF ,
2017-10-10 16:48:47 +03:00
RFD77402_STATUS_MCPU_OFF ) ;
if ( ret < 0 )
return ret ;
/* set initialization */
2021-06-30 16:16:35 +03:00
ret = i2c_smbus_write_word_data ( client , RFD77402_PMU_CFG , 0x0600 ) ;
2017-10-10 16:48:47 +03:00
if ( ret < 0 )
return ret ;
2021-06-30 16:16:35 +03:00
ret = rfd77402_set_state ( client , RFD77402_CMD_MCPU_ON ,
2017-10-10 16:48:47 +03:00
RFD77402_STATUS_MCPU_ON ) ;
if ( ret < 0 )
return ret ;
for ( i = 0 ; i < ARRAY_SIZE ( rf77402_tof_config ) ; i + + ) {
2021-06-30 16:16:35 +03:00
ret = i2c_smbus_write_word_data ( client ,
2017-10-10 16:48:47 +03:00
rf77402_tof_config [ i ] . reg ,
rf77402_tof_config [ i ] . val ) ;
if ( ret < 0 )
return ret ;
}
2021-06-30 16:16:35 +03:00
ret = rfd77402_set_state ( client , RFD77402_CMD_STANDBY ,
2017-10-10 16:48:47 +03:00
RFD77402_STATUS_STANDBY ) ;
return ret ;
}
2021-06-30 16:16:35 +03:00
static int rfd77402_powerdown ( struct i2c_client * client )
2017-10-10 16:48:47 +03:00
{
2021-06-30 16:16:35 +03:00
return rfd77402_set_state ( client , RFD77402_CMD_STANDBY ,
2017-10-10 16:48:47 +03:00
RFD77402_STATUS_STANDBY ) ;
}
2021-06-30 16:16:36 +03:00
static void rfd77402_disable ( void * client )
{
rfd77402_powerdown ( client ) ;
}
2017-10-10 16:48:47 +03:00
static int rfd77402_probe ( struct i2c_client * client ,
const struct i2c_device_id * id )
{
struct rfd77402_data * data ;
struct iio_dev * indio_dev ;
int ret ;
ret = i2c_smbus_read_word_data ( client , RFD77402_MOD_CHIP_ID ) ;
if ( ret < 0 )
return ret ;
if ( ret ! = 0xad01 & & ret ! = 0xad02 ) /* known chip ids */
return - ENODEV ;
indio_dev = devm_iio_device_alloc ( & client - > dev , sizeof ( * data ) ) ;
if ( ! indio_dev )
return - ENOMEM ;
data = iio_priv ( indio_dev ) ;
data - > client = client ;
mutex_init ( & data - > lock ) ;
indio_dev - > info = & rfd77402_info ;
indio_dev - > channels = rfd77402_channels ;
indio_dev - > num_channels = ARRAY_SIZE ( rfd77402_channels ) ;
indio_dev - > name = RFD77402_DRV_NAME ;
indio_dev - > modes = INDIO_DIRECT_MODE ;
2021-06-30 16:16:35 +03:00
ret = rfd77402_init ( client ) ;
2017-10-10 16:48:47 +03:00
if ( ret < 0 )
return ret ;
2021-06-30 16:16:36 +03:00
ret = devm_add_action_or_reset ( & client - > dev , rfd77402_disable , client ) ;
2017-10-10 16:48:47 +03:00
if ( ret )
2021-06-30 16:16:36 +03:00
return ret ;
2017-10-10 16:48:47 +03:00
2021-06-30 16:16:36 +03:00
return devm_iio_device_register ( & client - > dev , indio_dev ) ;
2017-10-10 16:48:47 +03:00
}
static int rfd77402_suspend ( struct device * dev )
{
2021-06-30 16:16:35 +03:00
return rfd77402_powerdown ( to_i2c_client ( dev ) ) ;
2017-10-10 16:48:47 +03:00
}
static int rfd77402_resume ( struct device * dev )
{
2021-06-30 16:16:35 +03:00
return rfd77402_init ( to_i2c_client ( dev ) ) ;
2017-10-10 16:48:47 +03:00
}
2022-01-30 22:31:31 +03:00
static DEFINE_SIMPLE_DEV_PM_OPS ( rfd77402_pm_ops , rfd77402_suspend ,
rfd77402_resume ) ;
2017-10-10 16:48:47 +03:00
static const struct i2c_device_id rfd77402_id [ ] = {
2021-12-30 20:49:02 +03:00
{ " rfd77402 " , 0 } ,
2017-10-10 16:48:47 +03:00
{ }
} ;
MODULE_DEVICE_TABLE ( i2c , rfd77402_id ) ;
static struct i2c_driver rfd77402_driver = {
. driver = {
. name = RFD77402_DRV_NAME ,
2022-01-30 22:31:31 +03:00
. pm = pm_sleep_ptr ( & rfd77402_pm_ops ) ,
2017-10-10 16:48:47 +03:00
} ,
. probe = rfd77402_probe ,
. id_table = rfd77402_id ,
} ;
module_i2c_driver ( rfd77402_driver ) ;
MODULE_AUTHOR ( " Peter Meerwald-Stadler <pmeerw@pmeerw.net> " ) ;
MODULE_DESCRIPTION ( " RFD77402 Time-of-Flight sensor driver " ) ;
MODULE_LICENSE ( " GPL " ) ;