2012-06-19 11:58:54 +04:00
/*
2016-07-05 13:23:18 +03:00
* vcnl4000 . c - Support for Vishay VCNL4000 / 4010 / 4020 combined ambient
* light and proximity sensor
2012-06-19 11:58:54 +04:00
*
* Copyright 2012 Peter Meerwald < pmeerw @ pmeerw . net >
*
* This file is subject to the terms and conditions of version 2 of
* the GNU General Public License . See the file COPYING in the main
* directory of this archive for more details .
*
* IIO driver for VCNL4000 ( 7 - bit I2C slave address 0x13 )
*
* TODO :
* allow to adjust IR current
* proximity threshold and event handling
2016-07-05 13:23:18 +03:00
* periodic ALS / proximity measurement ( VCNL4010 / 20 )
* interrupts ( VCNL4010 / 20 )
2012-06-19 11:58:54 +04:00
*/
# include <linux/module.h>
# include <linux/i2c.h>
# include <linux/err.h>
# include <linux/delay.h>
# include <linux/iio/iio.h>
# include <linux/iio/sysfs.h>
# define VCNL4000_DRV_NAME "vcnl4000"
2016-07-05 13:23:18 +03:00
# define VCNL4000_ID 0x01
# define VCNL4010_ID 0x02 /* for VCNL4020, VCNL4010 */
2012-06-19 11:58:54 +04:00
# define VCNL4000_COMMAND 0x80 /* Command register */
# define VCNL4000_PROD_REV 0x81 /* Product ID and Revision ID */
# define VCNL4000_LED_CURRENT 0x83 /* IR LED current for proximity mode */
# define VCNL4000_AL_PARAM 0x84 /* Ambient light parameter register */
# define VCNL4000_AL_RESULT_HI 0x85 /* Ambient light result register, MSB */
# define VCNL4000_AL_RESULT_LO 0x86 /* Ambient light result register, LSB */
# define VCNL4000_PS_RESULT_HI 0x87 /* Proximity result register, MSB */
# define VCNL4000_PS_RESULT_LO 0x88 /* Proximity result register, LSB */
# define VCNL4000_PS_MEAS_FREQ 0x89 /* Proximity test signal frequency */
# define VCNL4000_PS_MOD_ADJ 0x8a /* Proximity modulator timing adjustment */
/* Bit masks for COMMAND register */
# define VCNL4000_AL_RDY 0x40 /* ALS data ready? */
# define VCNL4000_PS_RDY 0x20 /* proximity data ready? */
# define VCNL4000_AL_OD 0x10 /* start on-demand ALS measurement */
# define VCNL4000_PS_OD 0x08 /* start on-demand proximity measurement */
struct vcnl4000_data {
struct i2c_client * client ;
} ;
static const struct i2c_device_id vcnl4000_id [ ] = {
{ " vcnl4000 " , 0 } ,
{ }
} ;
MODULE_DEVICE_TABLE ( i2c , vcnl4000_id ) ;
static int vcnl4000_measure ( struct vcnl4000_data * data , u8 req_mask ,
u8 rdy_mask , u8 data_reg , int * val )
{
int tries = 20 ;
2013-11-25 16:42:00 +04:00
__be16 buf ;
2012-06-19 11:58:54 +04:00
int ret ;
ret = i2c_smbus_write_byte_data ( data - > client , VCNL4000_COMMAND ,
req_mask ) ;
if ( ret < 0 )
return ret ;
/* wait for data to become ready */
while ( tries - - ) {
ret = i2c_smbus_read_byte_data ( data - > client , VCNL4000_COMMAND ) ;
if ( ret < 0 )
return ret ;
if ( ret & rdy_mask )
break ;
msleep ( 20 ) ; /* measurement takes up to 100 ms */
}
if ( tries < 0 ) {
dev_err ( & data - > client - > dev ,
" vcnl4000_measure() failed, data not ready \n " ) ;
return - EIO ;
}
ret = i2c_smbus_read_i2c_block_data ( data - > client ,
data_reg , sizeof ( buf ) , ( u8 * ) & buf ) ;
if ( ret < 0 )
return ret ;
* val = be16_to_cpu ( buf ) ;
return 0 ;
}
static const struct iio_chan_spec vcnl4000_channels [ ] = {
{
. type = IIO_LIGHT ,
2013-02-27 23:33:55 +04:00
. info_mask_separate = BIT ( IIO_CHAN_INFO_RAW ) |
BIT ( IIO_CHAN_INFO_SCALE ) ,
2012-06-19 11:58:54 +04:00
} , {
. type = IIO_PROXIMITY ,
2013-02-27 23:33:55 +04:00
. info_mask_separate = BIT ( IIO_CHAN_INFO_RAW ) ,
2012-06-19 11:58:54 +04:00
}
} ;
static int vcnl4000_read_raw ( struct iio_dev * indio_dev ,
struct iio_chan_spec const * chan ,
int * val , int * val2 , long mask )
{
int ret = - EINVAL ;
struct vcnl4000_data * data = iio_priv ( indio_dev ) ;
switch ( mask ) {
case IIO_CHAN_INFO_RAW :
switch ( chan - > type ) {
case IIO_LIGHT :
ret = vcnl4000_measure ( data ,
VCNL4000_AL_OD , VCNL4000_AL_RDY ,
VCNL4000_AL_RESULT_HI , val ) ;
if ( ret < 0 )
return ret ;
ret = IIO_VAL_INT ;
break ;
case IIO_PROXIMITY :
ret = vcnl4000_measure ( data ,
VCNL4000_PS_OD , VCNL4000_PS_RDY ,
VCNL4000_PS_RESULT_HI , val ) ;
if ( ret < 0 )
return ret ;
ret = IIO_VAL_INT ;
break ;
default :
break ;
}
break ;
case IIO_CHAN_INFO_SCALE :
if ( chan - > type = = IIO_LIGHT ) {
* val = 0 ;
* val2 = 250000 ;
ret = IIO_VAL_INT_PLUS_MICRO ;
}
break ;
default :
break ;
}
return ret ;
}
static const struct iio_info vcnl4000_info = {
. read_raw = vcnl4000_read_raw ,
. driver_module = THIS_MODULE ,
} ;
2012-12-22 01:21:43 +04:00
static int vcnl4000_probe ( struct i2c_client * client ,
const struct i2c_device_id * id )
2012-06-19 11:58:54 +04:00
{
struct vcnl4000_data * data ;
struct iio_dev * indio_dev ;
2016-07-05 13:23:18 +03:00
int ret , prod_id ;
2012-06-19 11:58:54 +04:00
2013-07-30 02:18:00 +04:00
indio_dev = devm_iio_device_alloc ( & client - > dev , sizeof ( * data ) ) ;
2012-06-19 11:58:54 +04:00
if ( ! indio_dev )
return - ENOMEM ;
data = iio_priv ( indio_dev ) ;
i2c_set_clientdata ( client , indio_dev ) ;
data - > client = client ;
ret = i2c_smbus_read_byte_data ( data - > client , VCNL4000_PROD_REV ) ;
if ( ret < 0 )
2013-07-30 02:18:00 +04:00
return ret ;
2012-06-19 11:58:54 +04:00
2016-07-05 13:23:18 +03:00
prod_id = ret > > 4 ;
if ( prod_id ! = VCNL4010_ID & & prod_id ! = VCNL4000_ID )
return - ENODEV ;
dev_dbg ( & client - > dev , " %s Ambient light/proximity sensor, Rev: %02x \n " ,
( prod_id = = VCNL4010_ID ) ? " VCNL4010/4020 " : " VCNL4000 " ,
ret & 0xf ) ;
2012-06-19 11:58:54 +04:00
indio_dev - > dev . parent = & client - > dev ;
indio_dev - > info = & vcnl4000_info ;
indio_dev - > channels = vcnl4000_channels ;
indio_dev - > num_channels = ARRAY_SIZE ( vcnl4000_channels ) ;
indio_dev - > name = VCNL4000_DRV_NAME ;
indio_dev - > modes = INDIO_DIRECT_MODE ;
2013-10-29 15:39:00 +04:00
return devm_iio_device_register ( & client - > dev , indio_dev ) ;
2012-06-19 11:58:54 +04:00
}
static struct i2c_driver vcnl4000_driver = {
. driver = {
. name = VCNL4000_DRV_NAME ,
} ,
. probe = vcnl4000_probe ,
. id_table = vcnl4000_id ,
} ;
module_i2c_driver ( vcnl4000_driver ) ;
MODULE_AUTHOR ( " Peter Meerwald <pmeerw@pmeerw.net> " ) ;
MODULE_DESCRIPTION ( " Vishay VCNL4000 proximity/ambient light sensor driver " ) ;
MODULE_LICENSE ( " GPL " ) ;