2018-07-20 19:34:26 +02:00
// SPDX-License-Identifier: GPL-2.0
/*
* isl29501 . c : ISL29501 Time of Flight sensor driver .
*
* Copyright ( C ) 2018
* Author : Mathieu Othacehe < m . othacehe @ gmail . com >
*
* 7 - bit I2C slave address : 0x57
*/
# include <linux/kernel.h>
# include <linux/module.h>
# include <linux/i2c.h>
# include <linux/err.h>
# include <linux/of_device.h>
# include <linux/iio/iio.h>
# include <linux/iio/sysfs.h>
# include <linux/iio/trigger_consumer.h>
# include <linux/iio/buffer.h>
# include <linux/iio/triggered_buffer.h>
/* Control, setting and status registers */
# define ISL29501_DEVICE_ID 0x00
# define ISL29501_ID 0x0A
/* Sampling control registers */
# define ISL29501_INTEGRATION_PERIOD 0x10
# define ISL29501_SAMPLE_PERIOD 0x11
/* Closed loop calibration registers */
# define ISL29501_CROSSTALK_I_MSB 0x24
# define ISL29501_CROSSTALK_I_LSB 0x25
# define ISL29501_CROSSTALK_I_EXPONENT 0x26
# define ISL29501_CROSSTALK_Q_MSB 0x27
# define ISL29501_CROSSTALK_Q_LSB 0x28
# define ISL29501_CROSSTALK_Q_EXPONENT 0x29
# define ISL29501_CROSSTALK_GAIN_MSB 0x2A
# define ISL29501_CROSSTALK_GAIN_LSB 0x2B
# define ISL29501_MAGNITUDE_REF_EXP 0x2C
# define ISL29501_MAGNITUDE_REF_MSB 0x2D
# define ISL29501_MAGNITUDE_REF_LSB 0x2E
# define ISL29501_PHASE_OFFSET_MSB 0x2F
# define ISL29501_PHASE_OFFSET_LSB 0x30
/* Analog control registers */
# define ISL29501_DRIVER_RANGE 0x90
# define ISL29501_EMITTER_DAC 0x91
# define ISL29501_COMMAND_REGISTER 0xB0
/* Commands */
# define ISL29501_EMUL_SAMPLE_START_PIN 0x49
# define ISL29501_RESET_ALL_REGISTERS 0xD7
# define ISL29501_RESET_INT_SM 0xD1
/* Ambiant light and temperature corrections */
# define ISL29501_TEMP_REFERENCE 0x31
# define ISL29501_PHASE_EXPONENT 0x33
# define ISL29501_TEMP_COEFF_A 0x34
# define ISL29501_TEMP_COEFF_B 0x39
# define ISL29501_AMBIANT_COEFF_A 0x36
# define ISL29501_AMBIANT_COEFF_B 0x3B
/* Data output registers */
# define ISL29501_DISTANCE_MSB_DATA 0xD1
# define ISL29501_DISTANCE_LSB_DATA 0xD2
# define ISL29501_PRECISION_MSB 0xD3
# define ISL29501_PRECISION_LSB 0xD4
# define ISL29501_MAGNITUDE_EXPONENT 0xD5
# define ISL29501_MAGNITUDE_MSB 0xD6
# define ISL29501_MAGNITUDE_LSB 0xD7
# define ISL29501_PHASE_MSB 0xD8
# define ISL29501_PHASE_LSB 0xD9
# define ISL29501_I_RAW_EXPONENT 0xDA
# define ISL29501_I_RAW_MSB 0xDB
# define ISL29501_I_RAW_LSB 0xDC
# define ISL29501_Q_RAW_EXPONENT 0xDD
# define ISL29501_Q_RAW_MSB 0xDE
# define ISL29501_Q_RAW_LSB 0xDF
# define ISL29501_DIE_TEMPERATURE 0xE2
# define ISL29501_AMBIENT_LIGHT 0xE3
# define ISL29501_GAIN_MSB 0xE6
# define ISL29501_GAIN_LSB 0xE7
# define ISL29501_MAX_EXP_VAL 15
# define ISL29501_INT_TIME_AVAILABLE \
" 0.00007 0.00014 0.00028 0.00057 0.00114 " \
" 0.00228 0.00455 0.00910 0.01820 0.03640 " \
" 0.07281 0.14561 "
# define ISL29501_CURRENT_SCALE_AVAILABLE \
" 0.0039 0.0078 0.0118 0.0157 0.0196 " \
" 0.0235 0.0275 0.0314 0.0352 0.0392 " \
" 0.0431 0.0471 0.0510 0.0549 0.0588 "
enum isl29501_correction_coeff {
COEFF_TEMP_A ,
COEFF_TEMP_B ,
COEFF_LIGHT_A ,
COEFF_LIGHT_B ,
COEFF_MAX ,
} ;
struct isl29501_private {
struct i2c_client * client ;
struct mutex lock ;
/* Exact representation of correction coefficients. */
unsigned int shadow_coeffs [ COEFF_MAX ] ;
} ;
enum isl29501_register_name {
REG_DISTANCE ,
REG_PHASE ,
REG_TEMPERATURE ,
REG_AMBIENT_LIGHT ,
REG_GAIN ,
REG_GAIN_BIAS ,
REG_PHASE_EXP ,
REG_CALIB_PHASE_TEMP_A ,
REG_CALIB_PHASE_TEMP_B ,
REG_CALIB_PHASE_LIGHT_A ,
REG_CALIB_PHASE_LIGHT_B ,
REG_DISTANCE_BIAS ,
REG_TEMPERATURE_BIAS ,
REG_INT_TIME ,
REG_SAMPLE_TIME ,
REG_DRIVER_RANGE ,
REG_EMITTER_DAC ,
} ;
struct isl29501_register_desc {
u8 msb ;
u8 lsb ;
} ;
static const struct isl29501_register_desc isl29501_registers [ ] = {
[ REG_DISTANCE ] = {
. msb = ISL29501_DISTANCE_MSB_DATA ,
. lsb = ISL29501_DISTANCE_LSB_DATA ,
} ,
[ REG_PHASE ] = {
. msb = ISL29501_PHASE_MSB ,
. lsb = ISL29501_PHASE_LSB ,
} ,
[ REG_TEMPERATURE ] = {
. lsb = ISL29501_DIE_TEMPERATURE ,
} ,
[ REG_AMBIENT_LIGHT ] = {
. lsb = ISL29501_AMBIENT_LIGHT ,
} ,
[ REG_GAIN ] = {
. msb = ISL29501_GAIN_MSB ,
. lsb = ISL29501_GAIN_LSB ,
} ,
[ REG_GAIN_BIAS ] = {
. msb = ISL29501_CROSSTALK_GAIN_MSB ,
. lsb = ISL29501_CROSSTALK_GAIN_LSB ,
} ,
[ REG_PHASE_EXP ] = {
. lsb = ISL29501_PHASE_EXPONENT ,
} ,
[ REG_CALIB_PHASE_TEMP_A ] = {
. lsb = ISL29501_TEMP_COEFF_A ,
} ,
[ REG_CALIB_PHASE_TEMP_B ] = {
. lsb = ISL29501_TEMP_COEFF_B ,
} ,
[ REG_CALIB_PHASE_LIGHT_A ] = {
. lsb = ISL29501_AMBIANT_COEFF_A ,
} ,
[ REG_CALIB_PHASE_LIGHT_B ] = {
. lsb = ISL29501_AMBIANT_COEFF_B ,
} ,
[ REG_DISTANCE_BIAS ] = {
. msb = ISL29501_PHASE_OFFSET_MSB ,
. lsb = ISL29501_PHASE_OFFSET_LSB ,
} ,
[ REG_TEMPERATURE_BIAS ] = {
. lsb = ISL29501_TEMP_REFERENCE ,
} ,
[ REG_INT_TIME ] = {
. lsb = ISL29501_INTEGRATION_PERIOD ,
} ,
[ REG_SAMPLE_TIME ] = {
. lsb = ISL29501_SAMPLE_PERIOD ,
} ,
[ REG_DRIVER_RANGE ] = {
. lsb = ISL29501_DRIVER_RANGE ,
} ,
[ REG_EMITTER_DAC ] = {
. lsb = ISL29501_EMITTER_DAC ,
} ,
} ;
static int isl29501_register_read ( struct isl29501_private * isl29501 ,
enum isl29501_register_name name ,
u32 * val )
{
const struct isl29501_register_desc * reg = & isl29501_registers [ name ] ;
u8 msb = 0 , lsb = 0 ;
s32 ret ;
mutex_lock ( & isl29501 - > lock ) ;
if ( reg - > msb ) {
ret = i2c_smbus_read_byte_data ( isl29501 - > client , reg - > msb ) ;
if ( ret < 0 )
goto err ;
msb = ret ;
}
if ( reg - > lsb ) {
ret = i2c_smbus_read_byte_data ( isl29501 - > client , reg - > lsb ) ;
if ( ret < 0 )
goto err ;
lsb = ret ;
}
mutex_unlock ( & isl29501 - > lock ) ;
* val = ( msb < < 8 ) + lsb ;
return 0 ;
err :
mutex_unlock ( & isl29501 - > lock ) ;
return ret ;
}
static u32 isl29501_register_write ( struct isl29501_private * isl29501 ,
enum isl29501_register_name name ,
u32 value )
{
const struct isl29501_register_desc * reg = & isl29501_registers [ name ] ;
int ret ;
if ( ! reg - > msb & & value > U8_MAX )
return - ERANGE ;
if ( value > U16_MAX )
return - ERANGE ;
mutex_lock ( & isl29501 - > lock ) ;
if ( reg - > msb ) {
ret = i2c_smbus_write_byte_data ( isl29501 - > client ,
2018-08-23 23:24:35 +02:00
reg - > msb , value > > 8 ) ;
2018-07-20 19:34:26 +02:00
if ( ret < 0 )
goto err ;
}
2018-08-23 23:24:35 +02:00
ret = i2c_smbus_write_byte_data ( isl29501 - > client , reg - > lsb , value ) ;
2018-07-20 19:34:26 +02:00
err :
mutex_unlock ( & isl29501 - > lock ) ;
return ret ;
}
static ssize_t isl29501_read_ext ( struct iio_dev * indio_dev ,
uintptr_t private ,
const struct iio_chan_spec * chan ,
char * buf )
{
struct isl29501_private * isl29501 = iio_priv ( indio_dev ) ;
enum isl29501_register_name reg = private ;
int ret ;
u32 value , gain , coeff , exp ;
switch ( reg ) {
case REG_GAIN :
case REG_GAIN_BIAS :
ret = isl29501_register_read ( isl29501 , reg , & gain ) ;
if ( ret < 0 )
return ret ;
value = gain ;
break ;
case REG_CALIB_PHASE_TEMP_A :
case REG_CALIB_PHASE_TEMP_B :
case REG_CALIB_PHASE_LIGHT_A :
case REG_CALIB_PHASE_LIGHT_B :
ret = isl29501_register_read ( isl29501 , REG_PHASE_EXP , & exp ) ;
if ( ret < 0 )
return ret ;
ret = isl29501_register_read ( isl29501 , reg , & coeff ) ;
if ( ret < 0 )
return ret ;
value = coeff < < exp ;
break ;
default :
return - EINVAL ;
}
return sprintf ( buf , " %u \n " , value ) ;
}
static int isl29501_set_shadow_coeff ( struct isl29501_private * isl29501 ,
enum isl29501_register_name reg ,
unsigned int val )
{
enum isl29501_correction_coeff coeff ;
switch ( reg ) {
case REG_CALIB_PHASE_TEMP_A :
coeff = COEFF_TEMP_A ;
break ;
case REG_CALIB_PHASE_TEMP_B :
coeff = COEFF_TEMP_B ;
break ;
case REG_CALIB_PHASE_LIGHT_A :
coeff = COEFF_LIGHT_A ;
break ;
case REG_CALIB_PHASE_LIGHT_B :
coeff = COEFF_LIGHT_B ;
break ;
default :
return - EINVAL ;
}
isl29501 - > shadow_coeffs [ coeff ] = val ;
return 0 ;
}
static int isl29501_write_coeff ( struct isl29501_private * isl29501 ,
enum isl29501_correction_coeff coeff ,
int val )
{
enum isl29501_register_name reg ;
switch ( coeff ) {
case COEFF_TEMP_A :
reg = REG_CALIB_PHASE_TEMP_A ;
break ;
case COEFF_TEMP_B :
reg = REG_CALIB_PHASE_TEMP_B ;
break ;
case COEFF_LIGHT_A :
reg = REG_CALIB_PHASE_LIGHT_A ;
break ;
case COEFF_LIGHT_B :
reg = REG_CALIB_PHASE_LIGHT_B ;
break ;
default :
return - EINVAL ;
}
return isl29501_register_write ( isl29501 , reg , val ) ;
}
static unsigned int isl29501_find_corr_exp ( unsigned int val ,
unsigned int max_exp ,
unsigned int max_mantissa )
{
unsigned int exp = 1 ;
/*
* Correction coefficients are represented under
* mantissa * 2 ^ exponent form , where mantissa and exponent
* are stored in two separate registers of the sensor .
*
* Compute and return the lowest exponent such as :
* mantissa = value / 2 ^ exponent
*
* where mantissa < max_mantissa .
*/
if ( val < = max_mantissa )
return 0 ;
while ( ( val > > exp ) > max_mantissa ) {
exp + + ;
if ( exp > max_exp )
return max_exp ;
}
return exp ;
}
static ssize_t isl29501_write_ext ( struct iio_dev * indio_dev ,
uintptr_t private ,
const struct iio_chan_spec * chan ,
const char * buf , size_t len )
{
struct isl29501_private * isl29501 = iio_priv ( indio_dev ) ;
enum isl29501_register_name reg = private ;
unsigned int val ;
int max_exp = 0 ;
int ret ;
int i ;
ret = kstrtouint ( buf , 10 , & val ) ;
if ( ret )
return ret ;
switch ( reg ) {
case REG_GAIN_BIAS :
if ( val > U16_MAX )
return - ERANGE ;
ret = isl29501_register_write ( isl29501 , reg , val ) ;
if ( ret < 0 )
return ret ;
break ;
case REG_CALIB_PHASE_TEMP_A :
case REG_CALIB_PHASE_TEMP_B :
case REG_CALIB_PHASE_LIGHT_A :
case REG_CALIB_PHASE_LIGHT_B :
if ( val > ( U8_MAX < < ISL29501_MAX_EXP_VAL ) )
return - ERANGE ;
/* Store the correction coefficient under its exact form. */
ret = isl29501_set_shadow_coeff ( isl29501 , reg , val ) ;
if ( ret < 0 )
return ret ;
/*
* Find the highest exponent needed to represent
* correction coefficients .
*/
for ( i = 0 ; i < COEFF_MAX ; i + + ) {
int corr ;
int corr_exp ;
corr = isl29501 - > shadow_coeffs [ i ] ;
corr_exp = isl29501_find_corr_exp ( corr ,
ISL29501_MAX_EXP_VAL ,
U8_MAX / 2 ) ;
dev_dbg ( & isl29501 - > client - > dev ,
" found exp of corr(%d) = %d \n " , corr , corr_exp ) ;
max_exp = max ( max_exp , corr_exp ) ;
}
/*
* Represent every correction coefficient under
* mantissa * 2 ^ max_exponent form and force the
* writing of those coefficients on the sensor .
*/
for ( i = 0 ; i < COEFF_MAX ; i + + ) {
int corr ;
int mantissa ;
corr = isl29501 - > shadow_coeffs [ i ] ;
if ( ! corr )
continue ;
mantissa = corr > > max_exp ;
ret = isl29501_write_coeff ( isl29501 , i , mantissa ) ;
if ( ret < 0 )
return ret ;
}
ret = isl29501_register_write ( isl29501 , REG_PHASE_EXP , max_exp ) ;
if ( ret < 0 )
return ret ;
break ;
default :
return - EINVAL ;
}
return len ;
}
# define _ISL29501_EXT_INFO(_name, _ident) { \
. name = _name , \
. read = isl29501_read_ext , \
. write = isl29501_write_ext , \
. private = _ident , \
. shared = IIO_SEPARATE , \
}
static const struct iio_chan_spec_ext_info isl29501_ext_info [ ] = {
_ISL29501_EXT_INFO ( " agc_gain " , REG_GAIN ) ,
_ISL29501_EXT_INFO ( " agc_gain_bias " , REG_GAIN_BIAS ) ,
_ISL29501_EXT_INFO ( " calib_phase_temp_a " , REG_CALIB_PHASE_TEMP_A ) ,
_ISL29501_EXT_INFO ( " calib_phase_temp_b " , REG_CALIB_PHASE_TEMP_B ) ,
_ISL29501_EXT_INFO ( " calib_phase_light_a " , REG_CALIB_PHASE_LIGHT_A ) ,
_ISL29501_EXT_INFO ( " calib_phase_light_b " , REG_CALIB_PHASE_LIGHT_B ) ,
{ } ,
} ;
# define ISL29501_DISTANCE_SCAN_INDEX 0
# define ISL29501_TIMESTAMP_SCAN_INDEX 1
static const struct iio_chan_spec isl29501_channels [ ] = {
{
. type = IIO_PROXIMITY ,
. scan_index = ISL29501_DISTANCE_SCAN_INDEX ,
. info_mask_separate =
BIT ( IIO_CHAN_INFO_RAW ) |
BIT ( IIO_CHAN_INFO_SCALE ) |
BIT ( IIO_CHAN_INFO_CALIBBIAS ) ,
. scan_type = {
. sign = ' u ' ,
. realbits = 16 ,
. storagebits = 16 ,
. endianness = IIO_CPU ,
} ,
. info_mask_shared_by_all = BIT ( IIO_CHAN_INFO_INT_TIME ) |
BIT ( IIO_CHAN_INFO_SAMP_FREQ ) ,
. ext_info = isl29501_ext_info ,
} ,
{
. type = IIO_PHASE ,
. scan_index = - 1 ,
. info_mask_separate = BIT ( IIO_CHAN_INFO_RAW ) |
BIT ( IIO_CHAN_INFO_SCALE ) ,
} ,
{
. type = IIO_CURRENT ,
. scan_index = - 1 ,
. output = 1 ,
. info_mask_separate = BIT ( IIO_CHAN_INFO_RAW ) |
BIT ( IIO_CHAN_INFO_SCALE ) ,
} ,
{
. type = IIO_TEMP ,
. scan_index = - 1 ,
. info_mask_separate = BIT ( IIO_CHAN_INFO_RAW ) |
BIT ( IIO_CHAN_INFO_SCALE ) |
BIT ( IIO_CHAN_INFO_CALIBBIAS ) ,
} ,
{
. type = IIO_INTENSITY ,
. scan_index = - 1 ,
. modified = 1 ,
. channel2 = IIO_MOD_LIGHT_CLEAR ,
. info_mask_separate = BIT ( IIO_CHAN_INFO_RAW ) |
BIT ( IIO_CHAN_INFO_SCALE ) ,
} ,
IIO_CHAN_SOFT_TIMESTAMP ( ISL29501_TIMESTAMP_SCAN_INDEX ) ,
} ;
static int isl29501_reset_registers ( struct isl29501_private * isl29501 )
{
int ret ;
ret = i2c_smbus_write_byte_data ( isl29501 - > client ,
ISL29501_COMMAND_REGISTER ,
ISL29501_RESET_ALL_REGISTERS ) ;
if ( ret < 0 ) {
dev_err ( & isl29501 - > client - > dev ,
" cannot reset registers %d \n " , ret ) ;
return ret ;
}
ret = i2c_smbus_write_byte_data ( isl29501 - > client ,
ISL29501_COMMAND_REGISTER ,
ISL29501_RESET_INT_SM ) ;
if ( ret < 0 )
dev_err ( & isl29501 - > client - > dev ,
" cannot reset state machine %d \n " , ret ) ;
return ret ;
}
static int isl29501_begin_acquisition ( struct isl29501_private * isl29501 )
{
int ret ;
ret = i2c_smbus_write_byte_data ( isl29501 - > client ,
ISL29501_COMMAND_REGISTER ,
ISL29501_EMUL_SAMPLE_START_PIN ) ;
if ( ret < 0 )
dev_err ( & isl29501 - > client - > dev ,
" cannot begin acquisition %d \n " , ret ) ;
return ret ;
}
static IIO_CONST_ATTR_INT_TIME_AVAIL ( ISL29501_INT_TIME_AVAILABLE ) ;
static IIO_CONST_ATTR ( out_current_scale_available ,
ISL29501_CURRENT_SCALE_AVAILABLE ) ;
static struct attribute * isl29501_attributes [ ] = {
& iio_const_attr_integration_time_available . dev_attr . attr ,
& iio_const_attr_out_current_scale_available . dev_attr . attr ,
NULL
} ;
static const struct attribute_group isl29501_attribute_group = {
. attrs = isl29501_attributes ,
} ;
static const int isl29501_current_scale_table [ ] [ 2 ] = {
{ 0 , 3900 } , { 0 , 7800 } , { 0 , 11800 } , { 0 , 15700 } ,
{ 0 , 19600 } , { 0 , 23500 } , { 0 , 27500 } , { 0 , 31400 } ,
{ 0 , 35200 } , { 0 , 39200 } , { 0 , 43100 } , { 0 , 47100 } ,
{ 0 , 51000 } , { 0 , 54900 } , { 0 , 58800 } ,
} ;
static const int isl29501_int_time [ ] [ 2 ] = {
{ 0 , 70 } , /* 0.07 ms */
{ 0 , 140 } , /* 0.14 ms */
{ 0 , 280 } , /* 0.28 ms */
{ 0 , 570 } , /* 0.57 ms */
{ 0 , 1140 } , /* 1.14 ms */
{ 0 , 2280 } , /* 2.28 ms */
{ 0 , 4550 } , /* 4.55 ms */
{ 0 , 9100 } , /* 9.11 ms */
{ 0 , 18200 } , /* 18.2 ms */
{ 0 , 36400 } , /* 36.4 ms */
{ 0 , 72810 } , /* 72.81 ms */
{ 0 , 145610 } /* 145.28 ms */
} ;
static int isl29501_get_raw ( struct isl29501_private * isl29501 ,
const struct iio_chan_spec * chan ,
int * raw )
{
int ret ;
switch ( chan - > type ) {
case IIO_PROXIMITY :
ret = isl29501_register_read ( isl29501 , REG_DISTANCE , raw ) ;
if ( ret < 0 )
return ret ;
return IIO_VAL_INT ;
case IIO_INTENSITY :
ret = isl29501_register_read ( isl29501 ,
REG_AMBIENT_LIGHT ,
raw ) ;
if ( ret < 0 )
return ret ;
return IIO_VAL_INT ;
case IIO_PHASE :
ret = isl29501_register_read ( isl29501 , REG_PHASE , raw ) ;
if ( ret < 0 )
return ret ;
return IIO_VAL_INT ;
case IIO_CURRENT :
ret = isl29501_register_read ( isl29501 , REG_EMITTER_DAC , raw ) ;
if ( ret < 0 )
return ret ;
return IIO_VAL_INT ;
case IIO_TEMP :
ret = isl29501_register_read ( isl29501 , REG_TEMPERATURE , raw ) ;
if ( ret < 0 )
return ret ;
return IIO_VAL_INT ;
default :
return - EINVAL ;
}
}
static int isl29501_get_scale ( struct isl29501_private * isl29501 ,
const struct iio_chan_spec * chan ,
int * val , int * val2 )
{
int ret ;
u32 current_scale ;
switch ( chan - > type ) {
case IIO_PROXIMITY :
/* distance = raw_distance * 33.31 / 65536 (m) */
* val = 3331 ;
* val2 = 6553600 ;
return IIO_VAL_FRACTIONAL ;
case IIO_PHASE :
/* phase = raw_phase * 2pi / 65536 (rad) */
* val = 0 ;
* val2 = 95874 ;
return IIO_VAL_INT_PLUS_NANO ;
case IIO_INTENSITY :
/* light = raw_light * 35 / 10000 (mA) */
* val = 35 ;
* val2 = 10000 ;
return IIO_VAL_FRACTIONAL ;
case IIO_CURRENT :
ret = isl29501_register_read ( isl29501 ,
REG_DRIVER_RANGE ,
& current_scale ) ;
if ( ret < 0 )
return ret ;
if ( current_scale > ARRAY_SIZE ( isl29501_current_scale_table ) )
return - EINVAL ;
if ( ! current_scale ) {
* val = 0 ;
* val2 = 0 ;
return IIO_VAL_INT ;
}
* val = isl29501_current_scale_table [ current_scale - 1 ] [ 0 ] ;
* val2 = isl29501_current_scale_table [ current_scale - 1 ] [ 1 ] ;
return IIO_VAL_INT_PLUS_MICRO ;
case IIO_TEMP :
/* temperature = raw_temperature * 125 / 100000 (milli °C) */
* val = 125 ;
* val2 = 100000 ;
return IIO_VAL_FRACTIONAL ;
default :
return - EINVAL ;
}
}
static int isl29501_get_calibbias ( struct isl29501_private * isl29501 ,
const struct iio_chan_spec * chan ,
int * bias )
{
switch ( chan - > type ) {
case IIO_PROXIMITY :
return isl29501_register_read ( isl29501 ,
REG_DISTANCE_BIAS ,
bias ) ;
case IIO_TEMP :
return isl29501_register_read ( isl29501 ,
REG_TEMPERATURE_BIAS ,
bias ) ;
default :
return - EINVAL ;
}
}
static int isl29501_get_inttime ( struct isl29501_private * isl29501 ,
int * val , int * val2 )
{
int ret ;
u32 inttime ;
ret = isl29501_register_read ( isl29501 , REG_INT_TIME , & inttime ) ;
if ( ret < 0 )
return ret ;
if ( inttime > = ARRAY_SIZE ( isl29501_int_time ) )
return - EINVAL ;
* val = isl29501_int_time [ inttime ] [ 0 ] ;
* val2 = isl29501_int_time [ inttime ] [ 1 ] ;
return IIO_VAL_INT_PLUS_MICRO ;
}
static int isl29501_get_freq ( struct isl29501_private * isl29501 ,
int * val , int * val2 )
{
int ret ;
int sample_time ;
unsigned long long freq ;
u32 temp ;
ret = isl29501_register_read ( isl29501 , REG_SAMPLE_TIME , & sample_time ) ;
if ( ret < 0 )
return ret ;
/* freq = 1 / (0.000450 * (sample_time + 1) * 10^-6) */
freq = 1000000ULL * 1000000ULL ;
do_div ( freq , 450 * ( sample_time + 1 ) ) ;
temp = do_div ( freq , 1000000 ) ;
* val = freq ;
* val2 = temp ;
return IIO_VAL_INT_PLUS_MICRO ;
}
static int isl29501_read_raw ( struct iio_dev * indio_dev ,
struct iio_chan_spec const * chan , int * val ,
int * val2 , long mask )
{
struct isl29501_private * isl29501 = iio_priv ( indio_dev ) ;
switch ( mask ) {
case IIO_CHAN_INFO_RAW :
return isl29501_get_raw ( isl29501 , chan , val ) ;
case IIO_CHAN_INFO_SCALE :
return isl29501_get_scale ( isl29501 , chan , val , val2 ) ;
case IIO_CHAN_INFO_INT_TIME :
return isl29501_get_inttime ( isl29501 , val , val2 ) ;
case IIO_CHAN_INFO_SAMP_FREQ :
return isl29501_get_freq ( isl29501 , val , val2 ) ;
case IIO_CHAN_INFO_CALIBBIAS :
return isl29501_get_calibbias ( isl29501 , chan , val ) ;
default :
return - EINVAL ;
}
}
static int isl29501_set_raw ( struct isl29501_private * isl29501 ,
const struct iio_chan_spec * chan ,
int raw )
{
switch ( chan - > type ) {
case IIO_CURRENT :
return isl29501_register_write ( isl29501 , REG_EMITTER_DAC , raw ) ;
default :
return - EINVAL ;
}
}
static int isl29501_set_inttime ( struct isl29501_private * isl29501 ,
int val , int val2 )
{
int i ;
for ( i = 0 ; i < ARRAY_SIZE ( isl29501_int_time ) ; i + + ) {
if ( isl29501_int_time [ i ] [ 0 ] = = val & &
isl29501_int_time [ i ] [ 1 ] = = val2 ) {
return isl29501_register_write ( isl29501 ,
REG_INT_TIME ,
i ) ;
}
}
return - EINVAL ;
}
static int isl29501_set_scale ( struct isl29501_private * isl29501 ,
const struct iio_chan_spec * chan ,
int val , int val2 )
{
int i ;
if ( chan - > type ! = IIO_CURRENT )
return - EINVAL ;
for ( i = 0 ; i < ARRAY_SIZE ( isl29501_current_scale_table ) ; i + + ) {
if ( isl29501_current_scale_table [ i ] [ 0 ] = = val & &
isl29501_current_scale_table [ i ] [ 1 ] = = val2 ) {
return isl29501_register_write ( isl29501 ,
REG_DRIVER_RANGE ,
i + 1 ) ;
}
}
return - EINVAL ;
}
static int isl29501_set_calibbias ( struct isl29501_private * isl29501 ,
const struct iio_chan_spec * chan ,
int bias )
{
switch ( chan - > type ) {
case IIO_PROXIMITY :
return isl29501_register_write ( isl29501 ,
REG_DISTANCE_BIAS ,
bias ) ;
case IIO_TEMP :
return isl29501_register_write ( isl29501 ,
REG_TEMPERATURE_BIAS ,
bias ) ;
default :
return - EINVAL ;
}
}
static int isl29501_set_freq ( struct isl29501_private * isl29501 ,
int val , int val2 )
{
int freq ;
unsigned long long sample_time ;
/* sample_freq = 1 / (0.000450 * (sample_time + 1) * 10^-6) */
freq = val * 1000000 + val2 % 1000000 ;
sample_time = 2222ULL * 1000000ULL ;
do_div ( sample_time , freq ) ;
sample_time - = 1 ;
if ( sample_time > 255 )
return - ERANGE ;
return isl29501_register_write ( isl29501 , REG_SAMPLE_TIME , sample_time ) ;
}
static int isl29501_write_raw ( struct iio_dev * indio_dev ,
struct iio_chan_spec const * chan ,
int val , int val2 , long mask )
{
struct isl29501_private * isl29501 = iio_priv ( indio_dev ) ;
switch ( mask ) {
case IIO_CHAN_INFO_RAW :
return isl29501_set_raw ( isl29501 , chan , val ) ;
case IIO_CHAN_INFO_INT_TIME :
return isl29501_set_inttime ( isl29501 , val , val2 ) ;
case IIO_CHAN_INFO_SAMP_FREQ :
return isl29501_set_freq ( isl29501 , val , val2 ) ;
case IIO_CHAN_INFO_SCALE :
return isl29501_set_scale ( isl29501 , chan , val , val2 ) ;
case IIO_CHAN_INFO_CALIBBIAS :
return isl29501_set_calibbias ( isl29501 , chan , val ) ;
default :
return - EINVAL ;
}
}
static const struct iio_info isl29501_info = {
. read_raw = & isl29501_read_raw ,
. write_raw = & isl29501_write_raw ,
. attrs = & isl29501_attribute_group ,
} ;
static int isl29501_init_chip ( struct isl29501_private * isl29501 )
{
int ret ;
ret = i2c_smbus_read_byte_data ( isl29501 - > client , ISL29501_DEVICE_ID ) ;
if ( ret < 0 ) {
dev_err ( & isl29501 - > client - > dev , " Error reading device id \n " ) ;
return ret ;
}
if ( ret ! = ISL29501_ID ) {
dev_err ( & isl29501 - > client - > dev ,
" Wrong chip id, got %x expected %x \n " ,
ret , ISL29501_DEVICE_ID ) ;
return - ENODEV ;
}
ret = isl29501_reset_registers ( isl29501 ) ;
if ( ret < 0 )
return ret ;
return isl29501_begin_acquisition ( isl29501 ) ;
}
static irqreturn_t isl29501_trigger_handler ( int irq , void * p )
{
struct iio_poll_func * pf = p ;
struct iio_dev * indio_dev = pf - > indio_dev ;
struct isl29501_private * isl29501 = iio_priv ( indio_dev ) ;
const unsigned long * active_mask = indio_dev - > active_scan_mask ;
u32 buffer [ 4 ] = { } ; /* 1x16-bit + ts */
if ( test_bit ( ISL29501_DISTANCE_SCAN_INDEX , active_mask ) )
isl29501_register_read ( isl29501 , REG_DISTANCE , buffer ) ;
iio_push_to_buffers_with_timestamp ( indio_dev , buffer , pf - > timestamp ) ;
iio_trigger_notify_done ( indio_dev - > trig ) ;
return IRQ_HANDLED ;
}
static int isl29501_probe ( struct i2c_client * client ,
const struct i2c_device_id * id )
{
struct iio_dev * indio_dev ;
struct isl29501_private * isl29501 ;
int ret ;
indio_dev = devm_iio_device_alloc ( & client - > dev , sizeof ( * isl29501 ) ) ;
if ( ! indio_dev )
return - ENOMEM ;
isl29501 = iio_priv ( indio_dev ) ;
i2c_set_clientdata ( client , indio_dev ) ;
isl29501 - > client = client ;
mutex_init ( & isl29501 - > lock ) ;
ret = isl29501_init_chip ( isl29501 ) ;
if ( ret < 0 )
return ret ;
indio_dev - > modes = INDIO_DIRECT_MODE ;
indio_dev - > channels = isl29501_channels ;
indio_dev - > num_channels = ARRAY_SIZE ( isl29501_channels ) ;
indio_dev - > name = client - > name ;
indio_dev - > info = & isl29501_info ;
ret = devm_iio_triggered_buffer_setup ( & client - > dev , indio_dev ,
iio_pollfunc_store_time ,
isl29501_trigger_handler ,
NULL ) ;
if ( ret < 0 ) {
dev_err ( & client - > dev , " unable to setup iio triggered buffer \n " ) ;
return ret ;
}
return devm_iio_device_register ( & client - > dev , indio_dev ) ;
}
static const struct i2c_device_id isl29501_id [ ] = {
{ " isl29501 " , 0 } ,
{ }
} ;
MODULE_DEVICE_TABLE ( i2c , isl29501_id ) ;
# if defined(CONFIG_OF)
static const struct of_device_id isl29501_i2c_matches [ ] = {
{ . compatible = " renesas,isl29501 " } ,
{ }
} ;
MODULE_DEVICE_TABLE ( of , isl29501_i2c_matches ) ;
# endif
static struct i2c_driver isl29501_driver = {
. driver = {
. name = " isl29501 " ,
} ,
. id_table = isl29501_id ,
. probe = isl29501_probe ,
} ;
module_i2c_driver ( isl29501_driver ) ;
MODULE_AUTHOR ( " Mathieu Othacehe <m.othacehe@gmail.com> " ) ;
MODULE_DESCRIPTION ( " ISL29501 Time of Flight sensor driver " ) ;
MODULE_LICENSE ( " GPL v2 " ) ;