2014-10-01 22:01:00 +01:00
/*
* ltr501 . c - Support for Lite - On LTR501 ambient light and proximity sensor
*
* Copyright 2014 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 .
*
* 7 - bit I2C slave address 0x23
*
2015-04-19 02:10:03 -07:00
* TODO : IR LED characteristics
2014-10-01 22:01:00 +01:00
*/
# include <linux/module.h>
# include <linux/i2c.h>
# include <linux/err.h>
# include <linux/delay.h>
2015-04-17 22:15:10 -07:00
# include <linux/regmap.h>
2015-04-19 02:10:04 -07:00
# include <linux/acpi.h>
2014-10-01 22:01:00 +01:00
# include <linux/iio/iio.h>
2015-04-19 02:10:02 -07:00
# include <linux/iio/events.h>
2014-10-01 22:01:00 +01:00
# include <linux/iio/sysfs.h>
# include <linux/iio/trigger_consumer.h>
# include <linux/iio/buffer.h>
# include <linux/iio/triggered_buffer.h>
# define LTR501_DRV_NAME "ltr501"
# define LTR501_ALS_CONTR 0x80 /* ALS operation mode, SW reset */
# define LTR501_PS_CONTR 0x81 /* PS operation mode */
2015-04-19 02:10:03 -07:00
# define LTR501_PS_MEAS_RATE 0x84 /* measurement rate*/
2015-04-19 02:10:01 -07:00
# define LTR501_ALS_MEAS_RATE 0x85 /* ALS integ time, measurement rate*/
2014-10-01 22:01:00 +01:00
# define LTR501_PART_ID 0x86
# define LTR501_MANUFAC_ID 0x87
# define LTR501_ALS_DATA1 0x88 /* 16-bit, little endian */
# define LTR501_ALS_DATA0 0x8a /* 16-bit, little endian */
# define LTR501_ALS_PS_STATUS 0x8c
# define LTR501_PS_DATA 0x8d /* 16-bit, little endian */
2015-04-19 02:10:02 -07:00
# define LTR501_INTR 0x8f /* output mode, polarity, mode */
# define LTR501_PS_THRESH_UP 0x90 /* 11 bit, ps upper threshold */
# define LTR501_PS_THRESH_LOW 0x92 /* 11 bit, ps lower threshold */
# define LTR501_ALS_THRESH_UP 0x97 /* 16 bit, ALS upper threshold */
# define LTR501_ALS_THRESH_LOW 0x99 /* 16 bit, ALS lower threshold */
2015-04-19 02:10:03 -07:00
# define LTR501_INTR_PRST 0x9e /* ps thresh, als thresh */
2015-04-17 22:15:10 -07:00
# define LTR501_MAX_REG 0x9f
2014-10-01 22:01:00 +01:00
# define LTR501_ALS_CONTR_SW_RESET BIT(2)
# define LTR501_CONTR_PS_GAIN_MASK (BIT(3) | BIT(2))
# define LTR501_CONTR_PS_GAIN_SHIFT 2
# define LTR501_CONTR_ALS_GAIN_MASK BIT(3)
# define LTR501_CONTR_ACTIVE BIT(1)
2015-04-19 02:10:02 -07:00
# define LTR501_STATUS_ALS_INTR BIT(3)
2014-10-01 22:01:00 +01:00
# define LTR501_STATUS_ALS_RDY BIT(2)
2015-04-19 02:10:02 -07:00
# define LTR501_STATUS_PS_INTR BIT(1)
2014-10-01 22:01:00 +01:00
# define LTR501_STATUS_PS_RDY BIT(0)
# define LTR501_PS_DATA_MASK 0x7ff
2015-04-19 02:10:02 -07:00
# define LTR501_PS_THRESH_MASK 0x7ff
# define LTR501_ALS_THRESH_MASK 0xffff
2014-10-01 22:01:00 +01:00
2015-04-19 02:10:03 -07:00
# define LTR501_ALS_DEF_PERIOD 500000
# define LTR501_PS_DEF_PERIOD 100000
2015-04-17 22:15:10 -07:00
# define LTR501_REGMAP_NAME "ltr501_regmap"
2015-05-15 18:23:21 -07:00
# define LTR501_LUX_CONV(vis_coeff, vis_data, ir_coeff, ir_data) \
( ( vis_coeff * vis_data ) - ( ir_coeff * ir_data ) )
2015-04-19 02:10:01 -07:00
static const int int_time_mapping [ ] = { 100000 , 50000 , 200000 , 400000 } ;
static const struct reg_field reg_field_it =
REG_FIELD ( LTR501_ALS_MEAS_RATE , 3 , 4 ) ;
2015-04-19 02:10:02 -07:00
static const struct reg_field reg_field_als_intr =
REG_FIELD ( LTR501_INTR , 1 , 1 ) ;
2017-05-17 12:41:19 +02:00
static const struct reg_field reg_field_ps_intr =
REG_FIELD ( LTR501_INTR , 0 , 0 ) ;
2015-04-19 02:10:03 -07:00
static const struct reg_field reg_field_als_rate =
REG_FIELD ( LTR501_ALS_MEAS_RATE , 0 , 2 ) ;
static const struct reg_field reg_field_ps_rate =
REG_FIELD ( LTR501_PS_MEAS_RATE , 0 , 3 ) ;
static const struct reg_field reg_field_als_prst =
REG_FIELD ( LTR501_INTR_PRST , 0 , 3 ) ;
static const struct reg_field reg_field_ps_prst =
REG_FIELD ( LTR501_INTR_PRST , 4 , 7 ) ;
struct ltr501_samp_table {
int freq_val ; /* repetition frequency in micro HZ*/
int time_val ; /* repetition rate in micro seconds */
} ;
2015-04-19 02:10:01 -07:00
2015-04-21 19:10:59 +03:00
# define LTR501_RESERVED_GAIN -1
enum {
ltr501 = 0 ,
ltr559 ,
2015-04-21 19:11:00 +03:00
ltr301 ,
2015-04-21 19:10:59 +03:00
} ;
struct ltr501_gain {
int scale ;
int uscale ;
} ;
static struct ltr501_gain ltr501_als_gain_tbl [ ] = {
{ 1 , 0 } ,
{ 0 , 5000 } ,
} ;
static struct ltr501_gain ltr559_als_gain_tbl [ ] = {
{ 1 , 0 } ,
{ 0 , 500000 } ,
{ 0 , 250000 } ,
{ 0 , 125000 } ,
{ LTR501_RESERVED_GAIN , LTR501_RESERVED_GAIN } ,
{ LTR501_RESERVED_GAIN , LTR501_RESERVED_GAIN } ,
{ 0 , 20000 } ,
{ 0 , 10000 } ,
} ;
static struct ltr501_gain ltr501_ps_gain_tbl [ ] = {
{ 1 , 0 } ,
{ 0 , 250000 } ,
{ 0 , 125000 } ,
{ 0 , 62500 } ,
} ;
static struct ltr501_gain ltr559_ps_gain_tbl [ ] = {
{ 0 , 62500 } , /* x16 gain */
{ 0 , 31250 } , /* x32 gain */
{ 0 , 15625 } , /* bits X1 are for x64 gain */
{ 0 , 15624 } ,
} ;
struct ltr501_chip_info {
u8 partid ;
struct ltr501_gain * als_gain ;
int als_gain_tbl_size ;
struct ltr501_gain * ps_gain ;
int ps_gain_tbl_size ;
u8 als_mode_active ;
u8 als_gain_mask ;
u8 als_gain_shift ;
2015-04-21 19:11:00 +03:00
struct iio_chan_spec const * channels ;
const int no_channels ;
const struct iio_info * info ;
const struct iio_info * info_no_irq ;
2015-04-21 19:10:59 +03:00
} ;
2014-10-01 22:01:00 +01:00
struct ltr501_data {
struct i2c_client * client ;
struct mutex lock_als , lock_ps ;
2015-04-21 19:10:59 +03:00
struct ltr501_chip_info * chip_info ;
2014-10-01 22:01:00 +01:00
u8 als_contr , ps_contr ;
2015-04-19 02:10:03 -07:00
int als_period , ps_period ; /* period in micro seconds */
2015-04-17 22:15:10 -07:00
struct regmap * regmap ;
2015-04-19 02:10:01 -07:00
struct regmap_field * reg_it ;
2015-04-19 02:10:02 -07:00
struct regmap_field * reg_als_intr ;
struct regmap_field * reg_ps_intr ;
2015-04-19 02:10:03 -07:00
struct regmap_field * reg_als_rate ;
struct regmap_field * reg_ps_rate ;
struct regmap_field * reg_als_prst ;
struct regmap_field * reg_ps_prst ;
} ;
static const struct ltr501_samp_table ltr501_als_samp_table [ ] = {
{ 20000000 , 50000 } , { 10000000 , 100000 } ,
{ 5000000 , 200000 } , { 2000000 , 500000 } ,
{ 1000000 , 1000000 } , { 500000 , 2000000 } ,
{ 500000 , 2000000 } , { 500000 , 2000000 }
2014-10-01 22:01:00 +01:00
} ;
2015-04-19 02:10:03 -07:00
static const struct ltr501_samp_table ltr501_ps_samp_table [ ] = {
{ 20000000 , 50000 } , { 14285714 , 70000 } ,
{ 10000000 , 100000 } , { 5000000 , 200000 } ,
{ 2000000 , 500000 } , { 1000000 , 1000000 } ,
{ 500000 , 2000000 } , { 500000 , 2000000 } ,
{ 500000 , 2000000 }
} ;
2015-12-19 14:14:54 +01:00
static int ltr501_match_samp_freq ( const struct ltr501_samp_table * tab ,
2015-04-19 02:10:03 -07:00
int len , int val , int val2 )
{
int i , freq ;
freq = val * 1000000 + val2 ;
for ( i = 0 ; i < len ; i + + ) {
if ( tab [ i ] . freq_val = = freq )
return i ;
}
return - EINVAL ;
}
static int ltr501_als_read_samp_freq ( struct ltr501_data * data ,
int * val , int * val2 )
{
int ret , i ;
ret = regmap_field_read ( data - > reg_als_rate , & i ) ;
if ( ret < 0 )
return ret ;
if ( i < 0 | | i > = ARRAY_SIZE ( ltr501_als_samp_table ) )
return - EINVAL ;
* val = ltr501_als_samp_table [ i ] . freq_val / 1000000 ;
* val2 = ltr501_als_samp_table [ i ] . freq_val % 1000000 ;
return IIO_VAL_INT_PLUS_MICRO ;
}
static int ltr501_ps_read_samp_freq ( struct ltr501_data * data ,
int * val , int * val2 )
{
int ret , i ;
ret = regmap_field_read ( data - > reg_ps_rate , & i ) ;
if ( ret < 0 )
return ret ;
if ( i < 0 | | i > = ARRAY_SIZE ( ltr501_ps_samp_table ) )
return - EINVAL ;
* val = ltr501_ps_samp_table [ i ] . freq_val / 1000000 ;
* val2 = ltr501_ps_samp_table [ i ] . freq_val % 1000000 ;
return IIO_VAL_INT_PLUS_MICRO ;
}
static int ltr501_als_write_samp_freq ( struct ltr501_data * data ,
int val , int val2 )
{
int i , ret ;
i = ltr501_match_samp_freq ( ltr501_als_samp_table ,
ARRAY_SIZE ( ltr501_als_samp_table ) ,
val , val2 ) ;
if ( i < 0 )
return i ;
mutex_lock ( & data - > lock_als ) ;
ret = regmap_field_write ( data - > reg_als_rate , i ) ;
mutex_unlock ( & data - > lock_als ) ;
return ret ;
}
static int ltr501_ps_write_samp_freq ( struct ltr501_data * data ,
int val , int val2 )
{
int i , ret ;
i = ltr501_match_samp_freq ( ltr501_ps_samp_table ,
ARRAY_SIZE ( ltr501_ps_samp_table ) ,
val , val2 ) ;
if ( i < 0 )
return i ;
mutex_lock ( & data - > lock_ps ) ;
ret = regmap_field_write ( data - > reg_ps_rate , i ) ;
mutex_unlock ( & data - > lock_ps ) ;
return ret ;
}
static int ltr501_als_read_samp_period ( struct ltr501_data * data , int * val )
{
int ret , i ;
ret = regmap_field_read ( data - > reg_als_rate , & i ) ;
if ( ret < 0 )
return ret ;
if ( i < 0 | | i > = ARRAY_SIZE ( ltr501_als_samp_table ) )
return - EINVAL ;
* val = ltr501_als_samp_table [ i ] . time_val ;
return IIO_VAL_INT ;
}
static int ltr501_ps_read_samp_period ( struct ltr501_data * data , int * val )
{
int ret , i ;
ret = regmap_field_read ( data - > reg_ps_rate , & i ) ;
if ( ret < 0 )
return ret ;
if ( i < 0 | | i > = ARRAY_SIZE ( ltr501_ps_samp_table ) )
return - EINVAL ;
* val = ltr501_ps_samp_table [ i ] . time_val ;
return IIO_VAL_INT ;
}
2015-05-15 18:23:21 -07:00
/* IR and visible spectrum coeff's are given in data sheet */
static unsigned long ltr501_calculate_lux ( u16 vis_data , u16 ir_data )
{
unsigned long ratio , lux ;
if ( vis_data = = 0 )
return 0 ;
/* multiply numerator by 100 to avoid handling ratio < 1 */
ratio = DIV_ROUND_UP ( ir_data * 100 , ir_data + vis_data ) ;
if ( ratio < 45 )
lux = LTR501_LUX_CONV ( 1774 , vis_data , - 1105 , ir_data ) ;
else if ( ratio > = 45 & & ratio < 64 )
lux = LTR501_LUX_CONV ( 3772 , vis_data , 1336 , ir_data ) ;
else if ( ratio > = 64 & & ratio < 85 )
lux = LTR501_LUX_CONV ( 1690 , vis_data , 169 , ir_data ) ;
else
lux = 0 ;
return lux / 1000 ;
}
2014-10-01 22:01:00 +01:00
static int ltr501_drdy ( struct ltr501_data * data , u8 drdy_mask )
{
int tries = 100 ;
2015-04-17 22:15:10 -07:00
int ret , status ;
2014-10-01 22:01:00 +01:00
while ( tries - - ) {
2015-04-17 22:15:10 -07:00
ret = regmap_read ( data - > regmap , LTR501_ALS_PS_STATUS , & status ) ;
2014-10-01 22:01:00 +01:00
if ( ret < 0 )
return ret ;
2015-04-17 22:15:10 -07:00
if ( ( status & drdy_mask ) = = drdy_mask )
2014-10-01 22:01:00 +01:00
return 0 ;
msleep ( 25 ) ;
}
dev_err ( & data - > client - > dev , " ltr501_drdy() failed, data not ready \n " ) ;
return - EIO ;
}
2015-04-19 02:10:01 -07:00
static int ltr501_set_it_time ( struct ltr501_data * data , int it )
{
int ret , i , index = - 1 , status ;
for ( i = 0 ; i < ARRAY_SIZE ( int_time_mapping ) ; i + + ) {
if ( int_time_mapping [ i ] = = it ) {
index = i ;
break ;
}
}
/* Make sure integ time index is valid */
if ( index < 0 )
return - EINVAL ;
ret = regmap_read ( data - > regmap , LTR501_ALS_CONTR , & status ) ;
if ( ret < 0 )
return ret ;
if ( status & LTR501_CONTR_ALS_GAIN_MASK ) {
/*
* 200 ms and 400 ms integ time can only be
* used in dynamic range 1
*/
if ( index > 1 )
return - EINVAL ;
} else
/* 50 ms integ time can only be used in dynamic range 2 */
if ( index = = 1 )
return - EINVAL ;
return regmap_field_write ( data - > reg_it , index ) ;
}
/* read int time in micro seconds */
static int ltr501_read_it_time ( struct ltr501_data * data , int * val , int * val2 )
{
int ret , index ;
ret = regmap_field_read ( data - > reg_it , & index ) ;
if ( ret < 0 )
return ret ;
/* Make sure integ time index is valid */
if ( index < 0 | | index > = ARRAY_SIZE ( int_time_mapping ) )
return - EINVAL ;
* val2 = int_time_mapping [ index ] ;
* val = 0 ;
return IIO_VAL_INT_PLUS_MICRO ;
}
2014-10-01 22:01:00 +01:00
static int ltr501_read_als ( struct ltr501_data * data , __le16 buf [ 2 ] )
{
2015-04-17 22:15:10 -07:00
int ret ;
ret = ltr501_drdy ( data , LTR501_STATUS_ALS_RDY ) ;
2014-10-01 22:01:00 +01:00
if ( ret < 0 )
return ret ;
/* always read both ALS channels in given order */
2015-04-17 22:15:10 -07:00
return regmap_bulk_read ( data - > regmap , LTR501_ALS_DATA1 ,
buf , 2 * sizeof ( __le16 ) ) ;
2014-10-01 22:01:00 +01:00
}
static int ltr501_read_ps ( struct ltr501_data * data )
{
2015-04-17 22:15:10 -07:00
int ret , status ;
ret = ltr501_drdy ( data , LTR501_STATUS_PS_RDY ) ;
if ( ret < 0 )
return ret ;
ret = regmap_bulk_read ( data - > regmap , LTR501_PS_DATA ,
& status , 2 ) ;
2014-10-01 22:01:00 +01:00
if ( ret < 0 )
return ret ;
2015-04-17 22:15:10 -07:00
return status ;
2014-10-01 22:01:00 +01:00
}
2015-04-19 02:10:03 -07:00
static int ltr501_read_intr_prst ( struct ltr501_data * data ,
enum iio_chan_type type ,
int * val2 )
{
int ret , samp_period , prst ;
switch ( type ) {
case IIO_INTENSITY :
ret = regmap_field_read ( data - > reg_als_prst , & prst ) ;
if ( ret < 0 )
return ret ;
ret = ltr501_als_read_samp_period ( data , & samp_period ) ;
if ( ret < 0 )
return ret ;
* val2 = samp_period * prst ;
return IIO_VAL_INT_PLUS_MICRO ;
case IIO_PROXIMITY :
ret = regmap_field_read ( data - > reg_ps_prst , & prst ) ;
if ( ret < 0 )
return ret ;
ret = ltr501_ps_read_samp_period ( data , & samp_period ) ;
if ( ret < 0 )
return ret ;
* val2 = samp_period * prst ;
return IIO_VAL_INT_PLUS_MICRO ;
default :
return - EINVAL ;
}
return - EINVAL ;
}
static int ltr501_write_intr_prst ( struct ltr501_data * data ,
enum iio_chan_type type ,
int val , int val2 )
{
int ret , samp_period , new_val ;
unsigned long period ;
if ( val < 0 | | val2 < 0 )
return - EINVAL ;
/* period in microseconds */
period = ( ( val * 1000000 ) + val2 ) ;
switch ( type ) {
case IIO_INTENSITY :
ret = ltr501_als_read_samp_period ( data , & samp_period ) ;
if ( ret < 0 )
return ret ;
/* period should be atleast equal to sampling period */
if ( period < samp_period )
return - EINVAL ;
new_val = DIV_ROUND_UP ( period , samp_period ) ;
if ( new_val < 0 | | new_val > 0x0f )
return - EINVAL ;
mutex_lock ( & data - > lock_als ) ;
ret = regmap_field_write ( data - > reg_als_prst , new_val ) ;
mutex_unlock ( & data - > lock_als ) ;
if ( ret > = 0 )
data - > als_period = period ;
return ret ;
case IIO_PROXIMITY :
ret = ltr501_ps_read_samp_period ( data , & samp_period ) ;
if ( ret < 0 )
return ret ;
/* period should be atleast equal to rate */
if ( period < samp_period )
return - EINVAL ;
new_val = DIV_ROUND_UP ( period , samp_period ) ;
if ( new_val < 0 | | new_val > 0x0f )
return - EINVAL ;
mutex_lock ( & data - > lock_ps ) ;
ret = regmap_field_write ( data - > reg_ps_prst , new_val ) ;
mutex_unlock ( & data - > lock_ps ) ;
if ( ret > = 0 )
data - > ps_period = period ;
return ret ;
default :
return - EINVAL ;
}
return - EINVAL ;
}
2015-04-19 02:10:02 -07:00
static const struct iio_event_spec ltr501_als_event_spec [ ] = {
{
. type = IIO_EV_TYPE_THRESH ,
. dir = IIO_EV_DIR_RISING ,
. mask_separate = BIT ( IIO_EV_INFO_VALUE ) ,
} , {
. type = IIO_EV_TYPE_THRESH ,
. dir = IIO_EV_DIR_FALLING ,
. mask_separate = BIT ( IIO_EV_INFO_VALUE ) ,
} , {
. type = IIO_EV_TYPE_THRESH ,
. dir = IIO_EV_DIR_EITHER ,
2015-04-19 02:10:03 -07:00
. mask_separate = BIT ( IIO_EV_INFO_ENABLE ) |
BIT ( IIO_EV_INFO_PERIOD ) ,
2015-04-19 02:10:02 -07:00
} ,
} ;
static const struct iio_event_spec ltr501_pxs_event_spec [ ] = {
{
. type = IIO_EV_TYPE_THRESH ,
. dir = IIO_EV_DIR_RISING ,
. mask_separate = BIT ( IIO_EV_INFO_VALUE ) ,
} , {
. type = IIO_EV_TYPE_THRESH ,
. dir = IIO_EV_DIR_FALLING ,
. mask_separate = BIT ( IIO_EV_INFO_VALUE ) ,
} , {
. type = IIO_EV_TYPE_THRESH ,
. dir = IIO_EV_DIR_EITHER ,
2015-04-19 02:10:03 -07:00
. mask_separate = BIT ( IIO_EV_INFO_ENABLE ) |
BIT ( IIO_EV_INFO_PERIOD ) ,
2015-04-19 02:10:02 -07:00
} ,
} ;
# define LTR501_INTENSITY_CHANNEL(_idx, _addr, _mod, _shared, \
_evspec , _evsize ) { \
2014-10-01 22:01:00 +01:00
. type = IIO_INTENSITY , \
. modified = 1 , \
. address = ( _addr ) , \
. channel2 = ( _mod ) , \
. info_mask_separate = BIT ( IIO_CHAN_INFO_RAW ) , \
. info_mask_shared_by_type = ( _shared ) , \
. scan_index = ( _idx ) , \
. scan_type = { \
. sign = ' u ' , \
. realbits = 16 , \
. storagebits = 16 , \
. endianness = IIO_CPU , \
2015-04-19 02:10:02 -07:00
} , \
. event_spec = _evspec , \
. num_event_specs = _evsize , \
2014-10-01 22:01:00 +01:00
}
2015-05-15 18:23:21 -07:00
# define LTR501_LIGHT_CHANNEL() { \
. type = IIO_LIGHT , \
. info_mask_separate = BIT ( IIO_CHAN_INFO_PROCESSED ) , \
. scan_index = - 1 , \
}
2014-10-01 22:01:00 +01:00
static const struct iio_chan_spec ltr501_channels [ ] = {
2015-05-15 18:23:21 -07:00
LTR501_LIGHT_CHANNEL ( ) ,
2015-04-19 02:10:02 -07:00
LTR501_INTENSITY_CHANNEL ( 0 , LTR501_ALS_DATA0 , IIO_MOD_LIGHT_BOTH , 0 ,
ltr501_als_event_spec ,
ARRAY_SIZE ( ltr501_als_event_spec ) ) ,
2014-10-01 22:01:00 +01:00
LTR501_INTENSITY_CHANNEL ( 1 , LTR501_ALS_DATA1 , IIO_MOD_LIGHT_IR ,
2015-04-19 02:10:01 -07:00
BIT ( IIO_CHAN_INFO_SCALE ) |
2015-04-19 02:10:03 -07:00
BIT ( IIO_CHAN_INFO_INT_TIME ) |
BIT ( IIO_CHAN_INFO_SAMP_FREQ ) ,
2015-04-19 02:10:02 -07:00
NULL , 0 ) ,
2014-10-01 22:01:00 +01:00
{
. type = IIO_PROXIMITY ,
. address = LTR501_PS_DATA ,
. info_mask_separate = BIT ( IIO_CHAN_INFO_RAW ) |
BIT ( IIO_CHAN_INFO_SCALE ) ,
. scan_index = 2 ,
. scan_type = {
. sign = ' u ' ,
. realbits = 11 ,
. storagebits = 16 ,
. endianness = IIO_CPU ,
} ,
2015-04-19 02:10:02 -07:00
. event_spec = ltr501_pxs_event_spec ,
. num_event_specs = ARRAY_SIZE ( ltr501_pxs_event_spec ) ,
2014-10-01 22:01:00 +01:00
} ,
IIO_CHAN_SOFT_TIMESTAMP ( 3 ) ,
} ;
2015-04-21 19:11:00 +03:00
static const struct iio_chan_spec ltr301_channels [ ] = {
2015-05-15 18:23:21 -07:00
LTR501_LIGHT_CHANNEL ( ) ,
2015-04-21 19:11:00 +03:00
LTR501_INTENSITY_CHANNEL ( 0 , LTR501_ALS_DATA0 , IIO_MOD_LIGHT_BOTH , 0 ,
ltr501_als_event_spec ,
ARRAY_SIZE ( ltr501_als_event_spec ) ) ,
LTR501_INTENSITY_CHANNEL ( 1 , LTR501_ALS_DATA1 , IIO_MOD_LIGHT_IR ,
BIT ( IIO_CHAN_INFO_SCALE ) |
BIT ( IIO_CHAN_INFO_INT_TIME ) |
BIT ( IIO_CHAN_INFO_SAMP_FREQ ) ,
NULL , 0 ) ,
IIO_CHAN_SOFT_TIMESTAMP ( 2 ) ,
} ;
2014-10-01 22:01:00 +01:00
static int ltr501_read_raw ( struct iio_dev * indio_dev ,
2015-04-09 17:17:47 +03:00
struct iio_chan_spec const * chan ,
int * val , int * val2 , long mask )
2014-10-01 22:01:00 +01:00
{
struct ltr501_data * data = iio_priv ( indio_dev ) ;
__le16 buf [ 2 ] ;
int ret , i ;
switch ( mask ) {
2015-05-15 18:23:21 -07:00
case IIO_CHAN_INFO_PROCESSED :
switch ( chan - > type ) {
case IIO_LIGHT :
2016-10-15 22:00:53 -07:00
ret = iio_device_claim_direct_mode ( indio_dev ) ;
if ( ret )
return ret ;
2015-05-15 18:23:21 -07:00
mutex_lock ( & data - > lock_als ) ;
ret = ltr501_read_als ( data , buf ) ;
mutex_unlock ( & data - > lock_als ) ;
2016-10-15 22:00:53 -07:00
iio_device_release_direct_mode ( indio_dev ) ;
2015-05-15 18:23:21 -07:00
if ( ret < 0 )
return ret ;
* val = ltr501_calculate_lux ( le16_to_cpu ( buf [ 1 ] ) ,
le16_to_cpu ( buf [ 0 ] ) ) ;
return IIO_VAL_INT ;
default :
return - EINVAL ;
}
2014-10-01 22:01:00 +01:00
case IIO_CHAN_INFO_RAW :
2016-10-15 22:00:53 -07:00
ret = iio_device_claim_direct_mode ( indio_dev ) ;
if ( ret )
return ret ;
2014-10-01 22:01:00 +01:00
switch ( chan - > type ) {
case IIO_INTENSITY :
mutex_lock ( & data - > lock_als ) ;
ret = ltr501_read_als ( data , buf ) ;
mutex_unlock ( & data - > lock_als ) ;
if ( ret < 0 )
2016-10-15 22:00:53 -07:00
break ;
2014-10-01 22:01:00 +01:00
* val = le16_to_cpu ( chan - > address = = LTR501_ALS_DATA1 ?
2015-04-09 17:17:47 +03:00
buf [ 0 ] : buf [ 1 ] ) ;
2016-10-15 22:00:53 -07:00
ret = IIO_VAL_INT ;
break ;
2014-10-01 22:01:00 +01:00
case IIO_PROXIMITY :
mutex_lock ( & data - > lock_ps ) ;
ret = ltr501_read_ps ( data ) ;
mutex_unlock ( & data - > lock_ps ) ;
if ( ret < 0 )
2016-10-15 22:00:53 -07:00
break ;
2014-10-01 22:01:00 +01:00
* val = ret & LTR501_PS_DATA_MASK ;
2016-10-15 22:00:53 -07:00
ret = IIO_VAL_INT ;
break ;
2014-10-01 22:01:00 +01:00
default :
2016-10-15 22:00:53 -07:00
ret = - EINVAL ;
break ;
2014-10-01 22:01:00 +01:00
}
2016-10-15 22:00:53 -07:00
iio_device_release_direct_mode ( indio_dev ) ;
return ret ;
2014-10-01 22:01:00 +01:00
case IIO_CHAN_INFO_SCALE :
switch ( chan - > type ) {
case IIO_INTENSITY :
2015-04-21 19:10:59 +03:00
i = ( data - > als_contr & data - > chip_info - > als_gain_mask )
> > data - > chip_info - > als_gain_shift ;
* val = data - > chip_info - > als_gain [ i ] . scale ;
* val2 = data - > chip_info - > als_gain [ i ] . uscale ;
return IIO_VAL_INT_PLUS_MICRO ;
2014-10-01 22:01:00 +01:00
case IIO_PROXIMITY :
i = ( data - > ps_contr & LTR501_CONTR_PS_GAIN_MASK ) > >
LTR501_CONTR_PS_GAIN_SHIFT ;
2015-04-21 19:10:59 +03:00
* val = data - > chip_info - > ps_gain [ i ] . scale ;
* val2 = data - > chip_info - > ps_gain [ i ] . uscale ;
2014-10-01 22:01:00 +01:00
return IIO_VAL_INT_PLUS_MICRO ;
default :
return - EINVAL ;
}
2015-04-19 02:10:01 -07:00
case IIO_CHAN_INFO_INT_TIME :
switch ( chan - > type ) {
case IIO_INTENSITY :
return ltr501_read_it_time ( data , val , val2 ) ;
default :
return - EINVAL ;
}
2015-04-19 02:10:03 -07:00
case IIO_CHAN_INFO_SAMP_FREQ :
switch ( chan - > type ) {
case IIO_INTENSITY :
return ltr501_als_read_samp_freq ( data , val , val2 ) ;
case IIO_PROXIMITY :
return ltr501_ps_read_samp_freq ( data , val , val2 ) ;
default :
return - EINVAL ;
}
2014-10-01 22:01:00 +01:00
}
return - EINVAL ;
}
2015-04-21 19:10:59 +03:00
static int ltr501_get_gain_index ( struct ltr501_gain * gain , int size ,
int val , int val2 )
2014-10-01 22:01:00 +01:00
{
int i ;
2015-04-21 19:10:59 +03:00
for ( i = 0 ; i < size ; i + + )
if ( val = = gain [ i ] . scale & & val2 = = gain [ i ] . uscale )
2014-10-01 22:01:00 +01:00
return i ;
return - 1 ;
}
static int ltr501_write_raw ( struct iio_dev * indio_dev ,
2015-04-09 17:17:47 +03:00
struct iio_chan_spec const * chan ,
int val , int val2 , long mask )
2014-10-01 22:01:00 +01:00
{
struct ltr501_data * data = iio_priv ( indio_dev ) ;
2015-04-19 02:10:03 -07:00
int i , ret , freq_val , freq_val2 ;
2015-04-21 19:10:59 +03:00
struct ltr501_chip_info * info = data - > chip_info ;
2014-10-01 22:01:00 +01:00
2016-10-15 22:02:19 -07:00
ret = iio_device_claim_direct_mode ( indio_dev ) ;
if ( ret )
return ret ;
2014-10-01 22:01:00 +01:00
switch ( mask ) {
case IIO_CHAN_INFO_SCALE :
switch ( chan - > type ) {
case IIO_INTENSITY :
2015-04-21 19:10:59 +03:00
i = ltr501_get_gain_index ( info - > als_gain ,
info - > als_gain_tbl_size ,
val , val2 ) ;
2016-10-15 22:02:19 -07:00
if ( i < 0 ) {
ret = - EINVAL ;
break ;
}
2015-04-17 22:15:10 -07:00
2015-04-21 19:10:59 +03:00
data - > als_contr & = ~ info - > als_gain_mask ;
data - > als_contr | = i < < info - > als_gain_shift ;
2016-10-15 22:02:19 -07:00
ret = regmap_write ( data - > regmap , LTR501_ALS_CONTR ,
data - > als_contr ) ;
break ;
2014-10-01 22:01:00 +01:00
case IIO_PROXIMITY :
2015-04-21 19:10:59 +03:00
i = ltr501_get_gain_index ( info - > ps_gain ,
info - > ps_gain_tbl_size ,
val , val2 ) ;
2016-10-15 22:02:19 -07:00
if ( i < 0 ) {
ret = - EINVAL ;
break ;
}
2014-10-01 22:01:00 +01:00
data - > ps_contr & = ~ LTR501_CONTR_PS_GAIN_MASK ;
data - > ps_contr | = i < < LTR501_CONTR_PS_GAIN_SHIFT ;
2015-04-17 22:15:10 -07:00
2016-10-15 22:02:19 -07:00
ret = regmap_write ( data - > regmap , LTR501_PS_CONTR ,
data - > ps_contr ) ;
break ;
2014-10-01 22:01:00 +01:00
default :
2016-10-15 22:02:19 -07:00
ret = - EINVAL ;
break ;
2014-10-01 22:01:00 +01:00
}
2016-10-15 22:02:19 -07:00
break ;
2015-04-19 02:10:01 -07:00
case IIO_CHAN_INFO_INT_TIME :
switch ( chan - > type ) {
case IIO_INTENSITY :
2016-10-15 22:02:19 -07:00
if ( val ! = 0 ) {
ret = - EINVAL ;
break ;
}
2015-04-19 02:10:01 -07:00
mutex_lock ( & data - > lock_als ) ;
2016-10-15 22:02:19 -07:00
ret = ltr501_set_it_time ( data , val2 ) ;
2015-04-19 02:10:01 -07:00
mutex_unlock ( & data - > lock_als ) ;
2016-10-15 22:02:19 -07:00
break ;
2015-04-19 02:10:01 -07:00
default :
2016-10-15 22:02:19 -07:00
ret = - EINVAL ;
break ;
2015-04-19 02:10:01 -07:00
}
2016-10-15 22:02:19 -07:00
break ;
2015-04-19 02:10:03 -07:00
case IIO_CHAN_INFO_SAMP_FREQ :
switch ( chan - > type ) {
case IIO_INTENSITY :
ret = ltr501_als_read_samp_freq ( data , & freq_val ,
& freq_val2 ) ;
if ( ret < 0 )
2016-10-15 22:02:19 -07:00
break ;
2015-04-19 02:10:03 -07:00
ret = ltr501_als_write_samp_freq ( data , val , val2 ) ;
if ( ret < 0 )
2016-10-15 22:02:19 -07:00
break ;
2015-04-19 02:10:03 -07:00
/* update persistence count when changing frequency */
ret = ltr501_write_intr_prst ( data , chan - > type ,
0 , data - > als_period ) ;
if ( ret < 0 )
2016-10-15 22:02:19 -07:00
ret = ltr501_als_write_samp_freq ( data , freq_val ,
freq_val2 ) ;
break ;
2015-04-19 02:10:03 -07:00
case IIO_PROXIMITY :
ret = ltr501_ps_read_samp_freq ( data , & freq_val ,
& freq_val2 ) ;
if ( ret < 0 )
2016-10-15 22:02:19 -07:00
break ;
2015-04-19 02:10:03 -07:00
ret = ltr501_ps_write_samp_freq ( data , val , val2 ) ;
if ( ret < 0 )
2016-10-15 22:02:19 -07:00
break ;
2015-04-19 02:10:03 -07:00
/* update persistence count when changing frequency */
ret = ltr501_write_intr_prst ( data , chan - > type ,
0 , data - > ps_period ) ;
if ( ret < 0 )
2016-10-15 22:02:19 -07:00
ret = ltr501_ps_write_samp_freq ( data , freq_val ,
freq_val2 ) ;
break ;
2015-04-19 02:10:03 -07:00
default :
2016-10-15 22:02:19 -07:00
ret = - EINVAL ;
break ;
2015-04-19 02:10:03 -07:00
}
2016-10-15 22:02:19 -07:00
break ;
default :
ret = - EINVAL ;
break ;
2014-10-01 22:01:00 +01:00
}
2016-10-15 22:02:19 -07:00
iio_device_release_direct_mode ( indio_dev ) ;
return ret ;
2014-10-01 22:01:00 +01:00
}
2015-04-19 02:10:02 -07:00
static int ltr501_read_thresh ( struct iio_dev * indio_dev ,
const struct iio_chan_spec * chan ,
enum iio_event_type type ,
enum iio_event_direction dir ,
enum iio_event_info info ,
int * val , int * val2 )
{
struct ltr501_data * data = iio_priv ( indio_dev ) ;
int ret , thresh_data ;
switch ( chan - > type ) {
case IIO_INTENSITY :
switch ( dir ) {
case IIO_EV_DIR_RISING :
ret = regmap_bulk_read ( data - > regmap ,
LTR501_ALS_THRESH_UP ,
& thresh_data , 2 ) ;
if ( ret < 0 )
return ret ;
* val = thresh_data & LTR501_ALS_THRESH_MASK ;
return IIO_VAL_INT ;
case IIO_EV_DIR_FALLING :
ret = regmap_bulk_read ( data - > regmap ,
LTR501_ALS_THRESH_LOW ,
& thresh_data , 2 ) ;
if ( ret < 0 )
return ret ;
* val = thresh_data & LTR501_ALS_THRESH_MASK ;
return IIO_VAL_INT ;
default :
return - EINVAL ;
}
case IIO_PROXIMITY :
switch ( dir ) {
case IIO_EV_DIR_RISING :
ret = regmap_bulk_read ( data - > regmap ,
LTR501_PS_THRESH_UP ,
& thresh_data , 2 ) ;
if ( ret < 0 )
return ret ;
* val = thresh_data & LTR501_PS_THRESH_MASK ;
return IIO_VAL_INT ;
case IIO_EV_DIR_FALLING :
ret = regmap_bulk_read ( data - > regmap ,
LTR501_PS_THRESH_LOW ,
& thresh_data , 2 ) ;
if ( ret < 0 )
return ret ;
* val = thresh_data & LTR501_PS_THRESH_MASK ;
return IIO_VAL_INT ;
default :
return - EINVAL ;
}
default :
return - EINVAL ;
}
return - EINVAL ;
}
static int ltr501_write_thresh ( struct iio_dev * indio_dev ,
const struct iio_chan_spec * chan ,
enum iio_event_type type ,
enum iio_event_direction dir ,
enum iio_event_info info ,
int val , int val2 )
{
struct ltr501_data * data = iio_priv ( indio_dev ) ;
int ret ;
if ( val < 0 )
return - EINVAL ;
switch ( chan - > type ) {
case IIO_INTENSITY :
if ( val > LTR501_ALS_THRESH_MASK )
return - EINVAL ;
switch ( dir ) {
case IIO_EV_DIR_RISING :
mutex_lock ( & data - > lock_als ) ;
ret = regmap_bulk_write ( data - > regmap ,
LTR501_ALS_THRESH_UP ,
& val , 2 ) ;
mutex_unlock ( & data - > lock_als ) ;
return ret ;
case IIO_EV_DIR_FALLING :
mutex_lock ( & data - > lock_als ) ;
ret = regmap_bulk_write ( data - > regmap ,
LTR501_ALS_THRESH_LOW ,
& val , 2 ) ;
mutex_unlock ( & data - > lock_als ) ;
return ret ;
default :
return - EINVAL ;
}
case IIO_PROXIMITY :
if ( val > LTR501_PS_THRESH_MASK )
return - EINVAL ;
2015-05-15 16:42:56 -07:00
switch ( dir ) {
2015-04-19 02:10:02 -07:00
case IIO_EV_DIR_RISING :
mutex_lock ( & data - > lock_ps ) ;
ret = regmap_bulk_write ( data - > regmap ,
LTR501_PS_THRESH_UP ,
& val , 2 ) ;
mutex_unlock ( & data - > lock_ps ) ;
return ret ;
case IIO_EV_DIR_FALLING :
mutex_lock ( & data - > lock_ps ) ;
ret = regmap_bulk_write ( data - > regmap ,
LTR501_PS_THRESH_LOW ,
& val , 2 ) ;
mutex_unlock ( & data - > lock_ps ) ;
return ret ;
default :
return - EINVAL ;
}
default :
return - EINVAL ;
}
return - EINVAL ;
}
2015-04-19 02:10:03 -07:00
static int ltr501_read_event ( struct iio_dev * indio_dev ,
const struct iio_chan_spec * chan ,
enum iio_event_type type ,
enum iio_event_direction dir ,
enum iio_event_info info ,
int * val , int * val2 )
{
int ret ;
switch ( info ) {
case IIO_EV_INFO_VALUE :
return ltr501_read_thresh ( indio_dev , chan , type , dir ,
info , val , val2 ) ;
case IIO_EV_INFO_PERIOD :
ret = ltr501_read_intr_prst ( iio_priv ( indio_dev ) ,
chan - > type , val2 ) ;
* val = * val2 / 1000000 ;
* val2 = * val2 % 1000000 ;
return ret ;
default :
return - EINVAL ;
}
return - EINVAL ;
}
static int ltr501_write_event ( struct iio_dev * indio_dev ,
const struct iio_chan_spec * chan ,
enum iio_event_type type ,
enum iio_event_direction dir ,
enum iio_event_info info ,
int val , int val2 )
{
switch ( info ) {
case IIO_EV_INFO_VALUE :
if ( val2 ! = 0 )
return - EINVAL ;
return ltr501_write_thresh ( indio_dev , chan , type , dir ,
info , val , val2 ) ;
case IIO_EV_INFO_PERIOD :
return ltr501_write_intr_prst ( iio_priv ( indio_dev ) , chan - > type ,
val , val2 ) ;
default :
return - EINVAL ;
}
return - EINVAL ;
}
2015-04-19 02:10:02 -07:00
static int ltr501_read_event_config ( struct iio_dev * indio_dev ,
const struct iio_chan_spec * chan ,
enum iio_event_type type ,
enum iio_event_direction dir )
{
struct ltr501_data * data = iio_priv ( indio_dev ) ;
int ret , status ;
switch ( chan - > type ) {
case IIO_INTENSITY :
ret = regmap_field_read ( data - > reg_als_intr , & status ) ;
if ( ret < 0 )
return ret ;
return status ;
case IIO_PROXIMITY :
ret = regmap_field_read ( data - > reg_ps_intr , & status ) ;
if ( ret < 0 )
return ret ;
return status ;
default :
return - EINVAL ;
}
return - EINVAL ;
}
static int ltr501_write_event_config ( struct iio_dev * indio_dev ,
const struct iio_chan_spec * chan ,
enum iio_event_type type ,
enum iio_event_direction dir , int state )
{
struct ltr501_data * data = iio_priv ( indio_dev ) ;
int ret ;
/* only 1 and 0 are valid inputs */
2015-05-02 11:25:48 +01:00
if ( state ! = 1 & & state ! = 0 )
2015-04-19 02:10:02 -07:00
return - EINVAL ;
switch ( chan - > type ) {
case IIO_INTENSITY :
mutex_lock ( & data - > lock_als ) ;
ret = regmap_field_write ( data - > reg_als_intr , state ) ;
mutex_unlock ( & data - > lock_als ) ;
return ret ;
case IIO_PROXIMITY :
mutex_lock ( & data - > lock_ps ) ;
ret = regmap_field_write ( data - > reg_ps_intr , state ) ;
mutex_unlock ( & data - > lock_ps ) ;
return ret ;
default :
return - EINVAL ;
}
return - EINVAL ;
}
2015-04-21 19:10:59 +03:00
static ssize_t ltr501_show_proximity_scale_avail ( struct device * dev ,
struct device_attribute * attr ,
char * buf )
{
struct ltr501_data * data = iio_priv ( dev_to_iio_dev ( dev ) ) ;
struct ltr501_chip_info * info = data - > chip_info ;
ssize_t len = 0 ;
int i ;
for ( i = 0 ; i < info - > ps_gain_tbl_size ; i + + ) {
if ( info - > ps_gain [ i ] . scale = = LTR501_RESERVED_GAIN )
continue ;
len + = scnprintf ( buf + len , PAGE_SIZE - len , " %d.%06d " ,
info - > ps_gain [ i ] . scale ,
info - > ps_gain [ i ] . uscale ) ;
}
buf [ len - 1 ] = ' \n ' ;
return len ;
}
static ssize_t ltr501_show_intensity_scale_avail ( struct device * dev ,
struct device_attribute * attr ,
char * buf )
{
struct ltr501_data * data = iio_priv ( dev_to_iio_dev ( dev ) ) ;
struct ltr501_chip_info * info = data - > chip_info ;
ssize_t len = 0 ;
int i ;
for ( i = 0 ; i < info - > als_gain_tbl_size ; i + + ) {
if ( info - > als_gain [ i ] . scale = = LTR501_RESERVED_GAIN )
continue ;
len + = scnprintf ( buf + len , PAGE_SIZE - len , " %d.%06d " ,
info - > als_gain [ i ] . scale ,
info - > als_gain [ i ] . uscale ) ;
}
buf [ len - 1 ] = ' \n ' ;
return len ;
}
2015-04-19 02:10:01 -07:00
static IIO_CONST_ATTR_INT_TIME_AVAIL ( " 0.05 0.1 0.2 0.4 " ) ;
2015-04-19 02:10:03 -07:00
static IIO_CONST_ATTR_SAMP_FREQ_AVAIL ( " 20 10 5 2 1 0.5 " ) ;
2014-10-01 22:01:00 +01:00
2015-04-21 19:10:59 +03:00
static IIO_DEVICE_ATTR ( in_proximity_scale_available , S_IRUGO ,
ltr501_show_proximity_scale_avail , NULL , 0 ) ;
static IIO_DEVICE_ATTR ( in_intensity_scale_available , S_IRUGO ,
ltr501_show_intensity_scale_avail , NULL , 0 ) ;
2014-10-01 22:01:00 +01:00
static struct attribute * ltr501_attributes [ ] = {
2015-04-21 19:10:59 +03:00
& iio_dev_attr_in_proximity_scale_available . dev_attr . attr ,
& iio_dev_attr_in_intensity_scale_available . dev_attr . attr ,
2015-04-19 02:10:01 -07:00
& iio_const_attr_integration_time_available . dev_attr . attr ,
2015-04-19 02:10:03 -07:00
& iio_const_attr_sampling_frequency_available . dev_attr . attr ,
2014-10-01 22:01:00 +01:00
NULL
} ;
2015-04-21 19:11:00 +03:00
static struct attribute * ltr301_attributes [ ] = {
& iio_dev_attr_in_intensity_scale_available . dev_attr . attr ,
& iio_const_attr_integration_time_available . dev_attr . attr ,
& iio_const_attr_sampling_frequency_available . dev_attr . attr ,
NULL
} ;
2014-10-01 22:01:00 +01:00
static const struct attribute_group ltr501_attribute_group = {
. attrs = ltr501_attributes ,
} ;
2015-04-21 19:11:00 +03:00
static const struct attribute_group ltr301_attribute_group = {
. attrs = ltr301_attributes ,
} ;
2015-04-19 02:10:02 -07:00
static const struct iio_info ltr501_info_no_irq = {
. read_raw = ltr501_read_raw ,
. write_raw = ltr501_write_raw ,
. attrs = & ltr501_attribute_group ,
} ;
2014-10-01 22:01:00 +01:00
static const struct iio_info ltr501_info = {
. read_raw = ltr501_read_raw ,
. write_raw = ltr501_write_raw ,
. attrs = & ltr501_attribute_group ,
2015-04-19 02:10:03 -07:00
. read_event_value = & ltr501_read_event ,
. write_event_value = & ltr501_write_event ,
2015-04-19 02:10:02 -07:00
. read_event_config = & ltr501_read_event_config ,
. write_event_config = & ltr501_write_event_config ,
2014-10-01 22:01:00 +01:00
} ;
2015-04-21 19:11:00 +03:00
static const struct iio_info ltr301_info_no_irq = {
. read_raw = ltr501_read_raw ,
. write_raw = ltr501_write_raw ,
. attrs = & ltr301_attribute_group ,
} ;
static const struct iio_info ltr301_info = {
. read_raw = ltr501_read_raw ,
. write_raw = ltr501_write_raw ,
. attrs = & ltr301_attribute_group ,
. read_event_value = & ltr501_read_event ,
. write_event_value = & ltr501_write_event ,
. read_event_config = & ltr501_read_event_config ,
. write_event_config = & ltr501_write_event_config ,
} ;
2015-04-21 19:10:59 +03:00
static struct ltr501_chip_info ltr501_chip_info_tbl [ ] = {
[ ltr501 ] = {
. partid = 0x08 ,
. als_gain = ltr501_als_gain_tbl ,
. als_gain_tbl_size = ARRAY_SIZE ( ltr501_als_gain_tbl ) ,
. ps_gain = ltr501_ps_gain_tbl ,
. ps_gain_tbl_size = ARRAY_SIZE ( ltr501_ps_gain_tbl ) ,
. als_mode_active = BIT ( 0 ) | BIT ( 1 ) ,
. als_gain_mask = BIT ( 3 ) ,
. als_gain_shift = 3 ,
2015-04-21 19:11:00 +03:00
. info = & ltr501_info ,
. info_no_irq = & ltr501_info_no_irq ,
. channels = ltr501_channels ,
. no_channels = ARRAY_SIZE ( ltr501_channels ) ,
2015-04-21 19:10:59 +03:00
} ,
[ ltr559 ] = {
. partid = 0x09 ,
. als_gain = ltr559_als_gain_tbl ,
. als_gain_tbl_size = ARRAY_SIZE ( ltr559_als_gain_tbl ) ,
. ps_gain = ltr559_ps_gain_tbl ,
. ps_gain_tbl_size = ARRAY_SIZE ( ltr559_ps_gain_tbl ) ,
. als_mode_active = BIT ( 1 ) ,
. als_gain_mask = BIT ( 2 ) | BIT ( 3 ) | BIT ( 4 ) ,
. als_gain_shift = 2 ,
2015-04-21 19:11:00 +03:00
. info = & ltr501_info ,
. info_no_irq = & ltr501_info_no_irq ,
. channels = ltr501_channels ,
. no_channels = ARRAY_SIZE ( ltr501_channels ) ,
} ,
[ ltr301 ] = {
. partid = 0x08 ,
. als_gain = ltr501_als_gain_tbl ,
. als_gain_tbl_size = ARRAY_SIZE ( ltr501_als_gain_tbl ) ,
. als_mode_active = BIT ( 0 ) | BIT ( 1 ) ,
. als_gain_mask = BIT ( 3 ) ,
. als_gain_shift = 3 ,
. info = & ltr301_info ,
. info_no_irq = & ltr301_info_no_irq ,
. channels = ltr301_channels ,
. no_channels = ARRAY_SIZE ( ltr301_channels ) ,
2015-04-21 19:10:59 +03:00
} ,
} ;
2015-04-17 22:15:10 -07:00
static int ltr501_write_contr ( struct ltr501_data * data , u8 als_val , u8 ps_val )
2014-10-01 22:01:00 +01:00
{
2015-04-17 22:15:10 -07:00
int ret ;
ret = regmap_write ( data - > regmap , LTR501_ALS_CONTR , als_val ) ;
2014-10-01 22:01:00 +01:00
if ( ret < 0 )
return ret ;
2015-04-17 22:15:10 -07:00
return regmap_write ( data - > regmap , LTR501_PS_CONTR , ps_val ) ;
2014-10-01 22:01:00 +01:00
}
static irqreturn_t ltr501_trigger_handler ( int irq , void * p )
{
struct iio_poll_func * pf = p ;
struct iio_dev * indio_dev = pf - > indio_dev ;
struct ltr501_data * data = iio_priv ( indio_dev ) ;
u16 buf [ 8 ] ;
__le16 als_buf [ 2 ] ;
u8 mask = 0 ;
int j = 0 ;
2015-04-17 22:15:10 -07:00
int ret , psdata ;
2014-10-01 22:01:00 +01:00
memset ( buf , 0 , sizeof ( buf ) ) ;
/* figure out which data needs to be ready */
if ( test_bit ( 0 , indio_dev - > active_scan_mask ) | |
2015-04-09 17:17:47 +03:00
test_bit ( 1 , indio_dev - > active_scan_mask ) )
2014-10-01 22:01:00 +01:00
mask | = LTR501_STATUS_ALS_RDY ;
if ( test_bit ( 2 , indio_dev - > active_scan_mask ) )
mask | = LTR501_STATUS_PS_RDY ;
ret = ltr501_drdy ( data , mask ) ;
if ( ret < 0 )
goto done ;
if ( mask & LTR501_STATUS_ALS_RDY ) {
2015-04-17 22:15:10 -07:00
ret = regmap_bulk_read ( data - > regmap , LTR501_ALS_DATA1 ,
( u8 * ) als_buf , sizeof ( als_buf ) ) ;
2014-10-01 22:01:00 +01:00
if ( ret < 0 )
return ret ;
if ( test_bit ( 0 , indio_dev - > active_scan_mask ) )
buf [ j + + ] = le16_to_cpu ( als_buf [ 1 ] ) ;
if ( test_bit ( 1 , indio_dev - > active_scan_mask ) )
buf [ j + + ] = le16_to_cpu ( als_buf [ 0 ] ) ;
}
if ( mask & LTR501_STATUS_PS_RDY ) {
2015-04-17 22:15:10 -07:00
ret = regmap_bulk_read ( data - > regmap , LTR501_PS_DATA ,
& psdata , 2 ) ;
2014-10-01 22:01:00 +01:00
if ( ret < 0 )
goto done ;
2015-04-17 22:15:10 -07:00
buf [ j + + ] = psdata & LTR501_PS_DATA_MASK ;
2014-10-01 22:01:00 +01:00
}
2016-03-09 19:05:49 +01:00
iio_push_to_buffers_with_timestamp ( indio_dev , buf ,
iio_get_time_ns ( indio_dev ) ) ;
2014-10-01 22:01:00 +01:00
done :
iio_trigger_notify_done ( indio_dev - > trig ) ;
return IRQ_HANDLED ;
}
2015-04-19 02:10:02 -07:00
static irqreturn_t ltr501_interrupt_handler ( int irq , void * private )
{
struct iio_dev * indio_dev = private ;
struct ltr501_data * data = iio_priv ( indio_dev ) ;
int ret , status ;
ret = regmap_read ( data - > regmap , LTR501_ALS_PS_STATUS , & status ) ;
if ( ret < 0 ) {
dev_err ( & data - > client - > dev ,
" irq read int reg failed \n " ) ;
return IRQ_HANDLED ;
}
if ( status & LTR501_STATUS_ALS_INTR )
iio_push_event ( indio_dev ,
IIO_UNMOD_EVENT_CODE ( IIO_INTENSITY , 0 ,
IIO_EV_TYPE_THRESH ,
IIO_EV_DIR_EITHER ) ,
2016-03-09 19:05:49 +01:00
iio_get_time_ns ( indio_dev ) ) ;
2015-04-19 02:10:02 -07:00
if ( status & LTR501_STATUS_PS_INTR )
iio_push_event ( indio_dev ,
IIO_UNMOD_EVENT_CODE ( IIO_PROXIMITY , 0 ,
IIO_EV_TYPE_THRESH ,
IIO_EV_DIR_EITHER ) ,
2016-03-09 19:05:49 +01:00
iio_get_time_ns ( indio_dev ) ) ;
2015-04-19 02:10:02 -07:00
return IRQ_HANDLED ;
}
2014-10-01 22:01:00 +01:00
static int ltr501_init ( struct ltr501_data * data )
{
2015-04-17 22:15:10 -07:00
int ret , status ;
2014-10-01 22:01:00 +01:00
2015-04-17 22:15:10 -07:00
ret = regmap_read ( data - > regmap , LTR501_ALS_CONTR , & status ) ;
2014-10-01 22:01:00 +01:00
if ( ret < 0 )
return ret ;
2015-06-28 12:31:53 +02:00
data - > als_contr = status | data - > chip_info - > als_mode_active ;
2015-04-17 22:15:10 -07:00
ret = regmap_read ( data - > regmap , LTR501_PS_CONTR , & status ) ;
2014-10-01 22:01:00 +01:00
if ( ret < 0 )
return ret ;
2015-04-17 22:15:10 -07:00
data - > ps_contr = status | LTR501_CONTR_ACTIVE ;
2015-04-19 02:10:03 -07:00
ret = ltr501_read_intr_prst ( data , IIO_INTENSITY , & data - > als_period ) ;
if ( ret < 0 )
return ret ;
ret = ltr501_read_intr_prst ( data , IIO_PROXIMITY , & data - > ps_period ) ;
if ( ret < 0 )
return ret ;
2015-04-17 22:15:10 -07:00
return ltr501_write_contr ( data , data - > als_contr , data - > ps_contr ) ;
2014-10-01 22:01:00 +01:00
}
2015-04-17 22:15:10 -07:00
static bool ltr501_is_volatile_reg ( struct device * dev , unsigned int reg )
{
switch ( reg ) {
case LTR501_ALS_DATA1 :
case LTR501_ALS_DATA0 :
case LTR501_ALS_PS_STATUS :
case LTR501_PS_DATA :
return true ;
default :
return false ;
}
}
static struct regmap_config ltr501_regmap_config = {
. name = LTR501_REGMAP_NAME ,
. reg_bits = 8 ,
. val_bits = 8 ,
. max_register = LTR501_MAX_REG ,
. cache_type = REGCACHE_RBTREE ,
. volatile_reg = ltr501_is_volatile_reg ,
} ;
2015-04-01 18:50:17 +03:00
static int ltr501_powerdown ( struct ltr501_data * data )
{
2015-04-21 19:10:59 +03:00
return ltr501_write_contr ( data , data - > als_contr &
~ data - > chip_info - > als_mode_active ,
2015-04-01 18:50:17 +03:00
data - > ps_contr & ~ LTR501_CONTR_ACTIVE ) ;
}
2015-04-21 19:10:59 +03:00
static const char * ltr501_match_acpi_device ( struct device * dev , int * chip_idx )
{
const struct acpi_device_id * id ;
id = acpi_match_device ( dev - > driver - > acpi_match_table , dev ) ;
if ( ! id )
return NULL ;
* chip_idx = id - > driver_data ;
return dev_name ( dev ) ;
}
2014-10-01 22:01:00 +01:00
static int ltr501_probe ( struct i2c_client * client ,
2015-04-09 17:17:47 +03:00
const struct i2c_device_id * id )
2014-10-01 22:01:00 +01:00
{
struct ltr501_data * data ;
struct iio_dev * indio_dev ;
2015-04-17 22:15:10 -07:00
struct regmap * regmap ;
2015-04-21 19:10:59 +03:00
int ret , partid , chip_idx = 0 ;
const char * name = NULL ;
2014-10-01 22:01:00 +01:00
indio_dev = devm_iio_device_alloc ( & client - > dev , sizeof ( * data ) ) ;
if ( ! indio_dev )
return - ENOMEM ;
2015-04-17 22:15:10 -07:00
regmap = devm_regmap_init_i2c ( client , & ltr501_regmap_config ) ;
if ( IS_ERR ( regmap ) ) {
dev_err ( & client - > dev , " Regmap initialization failed. \n " ) ;
return PTR_ERR ( regmap ) ;
}
2014-10-01 22:01:00 +01:00
data = iio_priv ( indio_dev ) ;
i2c_set_clientdata ( client , indio_dev ) ;
data - > client = client ;
2015-04-17 22:15:10 -07:00
data - > regmap = regmap ;
2014-10-01 22:01:00 +01:00
mutex_init ( & data - > lock_als ) ;
mutex_init ( & data - > lock_ps ) ;
2015-04-19 02:10:01 -07:00
data - > reg_it = devm_regmap_field_alloc ( & client - > dev , regmap ,
reg_field_it ) ;
if ( IS_ERR ( data - > reg_it ) ) {
dev_err ( & client - > dev , " Integ time reg field init failed. \n " ) ;
return PTR_ERR ( data - > reg_it ) ;
}
2015-04-19 02:10:02 -07:00
data - > reg_als_intr = devm_regmap_field_alloc ( & client - > dev , regmap ,
reg_field_als_intr ) ;
if ( IS_ERR ( data - > reg_als_intr ) ) {
dev_err ( & client - > dev , " ALS intr mode reg field init failed \n " ) ;
return PTR_ERR ( data - > reg_als_intr ) ;
}
data - > reg_ps_intr = devm_regmap_field_alloc ( & client - > dev , regmap ,
reg_field_ps_intr ) ;
if ( IS_ERR ( data - > reg_ps_intr ) ) {
dev_err ( & client - > dev , " PS intr mode reg field init failed. \n " ) ;
return PTR_ERR ( data - > reg_ps_intr ) ;
}
2015-04-19 02:10:03 -07:00
data - > reg_als_rate = devm_regmap_field_alloc ( & client - > dev , regmap ,
reg_field_als_rate ) ;
if ( IS_ERR ( data - > reg_als_rate ) ) {
dev_err ( & client - > dev , " ALS samp rate field init failed. \n " ) ;
return PTR_ERR ( data - > reg_als_rate ) ;
}
data - > reg_ps_rate = devm_regmap_field_alloc ( & client - > dev , regmap ,
reg_field_ps_rate ) ;
if ( IS_ERR ( data - > reg_ps_rate ) ) {
dev_err ( & client - > dev , " PS samp rate field init failed. \n " ) ;
return PTR_ERR ( data - > reg_ps_rate ) ;
}
data - > reg_als_prst = devm_regmap_field_alloc ( & client - > dev , regmap ,
reg_field_als_prst ) ;
if ( IS_ERR ( data - > reg_als_prst ) ) {
dev_err ( & client - > dev , " ALS prst reg field init failed \n " ) ;
return PTR_ERR ( data - > reg_als_prst ) ;
}
data - > reg_ps_prst = devm_regmap_field_alloc ( & client - > dev , regmap ,
reg_field_ps_prst ) ;
if ( IS_ERR ( data - > reg_ps_prst ) ) {
dev_err ( & client - > dev , " PS prst reg field init failed. \n " ) ;
return PTR_ERR ( data - > reg_ps_prst ) ;
}
2015-04-17 22:15:10 -07:00
ret = regmap_read ( data - > regmap , LTR501_PART_ID , & partid ) ;
2014-10-01 22:01:00 +01:00
if ( ret < 0 )
return ret ;
2015-04-21 19:10:59 +03:00
if ( id ) {
name = id - > name ;
chip_idx = id - > driver_data ;
} else if ( ACPI_HANDLE ( & client - > dev ) ) {
name = ltr501_match_acpi_device ( & client - > dev , & chip_idx ) ;
} else {
return - ENODEV ;
}
data - > chip_info = & ltr501_chip_info_tbl [ chip_idx ] ;
if ( ( partid > > 4 ) ! = data - > chip_info - > partid )
2014-10-01 22:01:00 +01:00
return - ENODEV ;
indio_dev - > dev . parent = & client - > dev ;
2015-04-21 19:11:00 +03:00
indio_dev - > info = data - > chip_info - > info ;
indio_dev - > channels = data - > chip_info - > channels ;
indio_dev - > num_channels = data - > chip_info - > no_channels ;
2015-04-21 19:10:59 +03:00
indio_dev - > name = name ;
2014-10-01 22:01:00 +01:00
indio_dev - > modes = INDIO_DIRECT_MODE ;
ret = ltr501_init ( data ) ;
if ( ret < 0 )
return ret ;
2015-04-19 02:10:02 -07:00
if ( client - > irq > 0 ) {
ret = devm_request_threaded_irq ( & client - > dev , client - > irq ,
NULL , ltr501_interrupt_handler ,
IRQF_TRIGGER_FALLING |
IRQF_ONESHOT ,
" ltr501_thresh_event " ,
indio_dev ) ;
if ( ret ) {
dev_err ( & client - > dev , " request irq (%d) failed \n " ,
client - > irq ) ;
return ret ;
}
} else {
2015-04-21 19:11:00 +03:00
indio_dev - > info = data - > chip_info - > info_no_irq ;
2015-04-19 02:10:02 -07:00
}
2014-10-01 22:01:00 +01:00
ret = iio_triggered_buffer_setup ( indio_dev , NULL ,
2015-04-09 17:17:47 +03:00
ltr501_trigger_handler , NULL ) ;
2014-10-01 22:01:00 +01:00
if ( ret )
2015-04-01 18:50:17 +03:00
goto powerdown_on_error ;
2014-10-01 22:01:00 +01:00
ret = iio_device_register ( indio_dev ) ;
if ( ret )
goto error_unreg_buffer ;
return 0 ;
error_unreg_buffer :
iio_triggered_buffer_cleanup ( indio_dev ) ;
2015-04-01 18:50:17 +03:00
powerdown_on_error :
ltr501_powerdown ( data ) ;
2014-10-01 22:01:00 +01:00
return ret ;
}
static int ltr501_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 ) ;
ltr501_powerdown ( iio_priv ( indio_dev ) ) ;
return 0 ;
}
# ifdef CONFIG_PM_SLEEP
static int ltr501_suspend ( struct device * dev )
{
struct ltr501_data * data = iio_priv ( i2c_get_clientdata (
2015-04-09 17:17:47 +03:00
to_i2c_client ( dev ) ) ) ;
2014-10-01 22:01:00 +01:00
return ltr501_powerdown ( data ) ;
}
static int ltr501_resume ( struct device * dev )
{
struct ltr501_data * data = iio_priv ( i2c_get_clientdata (
2015-04-09 17:17:47 +03:00
to_i2c_client ( dev ) ) ) ;
2014-10-01 22:01:00 +01:00
2015-04-17 22:15:10 -07:00
return ltr501_write_contr ( data , data - > als_contr ,
2014-10-01 22:01:00 +01:00
data - > ps_contr ) ;
}
# endif
static SIMPLE_DEV_PM_OPS ( ltr501_pm_ops , ltr501_suspend , ltr501_resume ) ;
2015-04-19 02:10:04 -07:00
static const struct acpi_device_id ltr_acpi_match [ ] = {
2015-04-21 19:10:59 +03:00
{ " LTER0501 " , ltr501 } ,
{ " LTER0559 " , ltr559 } ,
2015-04-21 19:11:00 +03:00
{ " LTER0301 " , ltr301 } ,
2015-04-19 02:10:04 -07:00
{ } ,
} ;
MODULE_DEVICE_TABLE ( acpi , ltr_acpi_match ) ;
2014-10-01 22:01:00 +01:00
static const struct i2c_device_id ltr501_id [ ] = {
2015-04-21 19:10:59 +03:00
{ " ltr501 " , ltr501 } ,
{ " ltr559 " , ltr559 } ,
2015-04-21 19:11:00 +03:00
{ " ltr301 " , ltr301 } ,
2014-10-01 22:01:00 +01:00
{ }
} ;
MODULE_DEVICE_TABLE ( i2c , ltr501_id ) ;
static struct i2c_driver ltr501_driver = {
. driver = {
. name = LTR501_DRV_NAME ,
. pm = & ltr501_pm_ops ,
2015-04-19 02:10:04 -07:00
. acpi_match_table = ACPI_PTR ( ltr_acpi_match ) ,
2014-10-01 22:01:00 +01:00
} ,
. probe = ltr501_probe ,
. remove = ltr501_remove ,
. id_table = ltr501_id ,
} ;
module_i2c_driver ( ltr501_driver ) ;
MODULE_AUTHOR ( " Peter Meerwald <pmeerw@pmeerw.net> " ) ;
MODULE_DESCRIPTION ( " Lite-On LTR501 ambient light and proximity sensor driver " ) ;
MODULE_LICENSE ( " GPL " ) ;