2019-05-28 20:10:04 +03:00
// SPDX-License-Identifier: GPL-2.0-only
2012-04-13 14:33:32 +04:00
/*
* IIO driver for the light sensor ISL29028 .
* ISL29028 is Concurrent Ambient Light and Proximity Sensor
*
* Copyright ( c ) 2012 , NVIDIA CORPORATION . All rights reserved .
2017-02-09 04:54:29 +03:00
* Copyright ( c ) 2016 - 2017 Brian Masney < masneyb @ onstation . org >
2012-04-13 14:33:32 +04:00
*
2017-04-28 18:55:58 +03:00
* Datasheets :
* - http : //www.intersil.com/content/dam/Intersil/documents/isl2/isl29028.pdf
* - http : //www.intersil.com/content/dam/Intersil/documents/isl2/isl29030.pdf
2012-04-13 14:33:32 +04:00
*/
# include <linux/module.h>
# include <linux/i2c.h>
# include <linux/err.h>
# include <linux/mutex.h>
# include <linux/delay.h>
# include <linux/slab.h>
# include <linux/regmap.h>
2012-04-25 18:54:58 +04:00
# include <linux/iio/iio.h>
# include <linux/iio/sysfs.h>
2017-01-17 12:25:02 +03:00
# include <linux/pm_runtime.h>
2012-04-13 14:33:32 +04:00
2016-12-04 05:19:29 +03:00
# define ISL29028_CONV_TIME_MS 100
2012-04-13 14:33:32 +04:00
2016-12-04 05:19:29 +03:00
# define ISL29028_REG_CONFIGURE 0x01
2012-04-13 14:33:32 +04:00
2016-12-04 05:19:29 +03:00
# define ISL29028_CONF_ALS_IR_MODE_ALS 0
# define ISL29028_CONF_ALS_IR_MODE_IR BIT(0)
# define ISL29028_CONF_ALS_IR_MODE_MASK BIT(0)
2012-04-13 14:33:32 +04:00
2016-12-04 05:19:29 +03:00
# define ISL29028_CONF_ALS_RANGE_LOW_LUX 0
2016-07-05 14:55:41 +03:00
# define ISL29028_CONF_ALS_RANGE_HIGH_LUX BIT(1)
2016-12-04 05:19:29 +03:00
# define ISL29028_CONF_ALS_RANGE_MASK BIT(1)
2012-04-13 14:33:32 +04:00
2016-12-04 05:19:29 +03:00
# define ISL29028_CONF_ALS_DIS 0
# define ISL29028_CONF_ALS_EN BIT(2)
# define ISL29028_CONF_ALS_EN_MASK BIT(2)
2012-04-13 14:33:32 +04:00
2016-12-04 05:19:29 +03:00
# define ISL29028_CONF_PROX_SLP_SH 4
# define ISL29028_CONF_PROX_SLP_MASK (7 << ISL29028_CONF_PROX_SLP_SH)
2012-04-13 14:33:32 +04:00
2016-12-04 05:19:29 +03:00
# define ISL29028_CONF_PROX_EN BIT(7)
# define ISL29028_CONF_PROX_EN_MASK BIT(7)
2012-04-13 14:33:32 +04:00
2016-12-04 05:19:29 +03:00
# define ISL29028_REG_INTERRUPT 0x02
2012-04-13 14:33:32 +04:00
2016-12-04 05:19:29 +03:00
# define ISL29028_REG_PROX_DATA 0x08
# define ISL29028_REG_ALSIR_L 0x09
# define ISL29028_REG_ALSIR_U 0x0A
2012-04-13 14:33:32 +04:00
2016-12-04 05:19:29 +03:00
# define ISL29028_REG_TEST1_MODE 0x0E
# define ISL29028_REG_TEST2_MODE 0x0F
2012-04-13 14:33:32 +04:00
2016-12-04 05:19:29 +03:00
# define ISL29028_NUM_REGS (ISL29028_REG_TEST2_MODE + 1)
2012-04-13 14:33:32 +04:00
2017-01-17 12:25:02 +03:00
# define ISL29028_POWER_OFF_DELAY_MS 2000
2017-04-25 04:34:33 +03:00
struct isl29028_prox_data {
int sampling_int ;
int sampling_fract ;
int sleep_time ;
} ;
static const struct isl29028_prox_data isl29028_prox_data [ ] = {
{ 1 , 250000 , 800 } ,
{ 2 , 500000 , 400 } ,
{ 5 , 0 , 200 } ,
{ 10 , 0 , 100 } ,
{ 13 , 300000 , 75 } ,
{ 20 , 0 , 50 } ,
{ 80 , 0 , 13 } , /*
* Note : Data sheet lists 12.5 ms sleep time .
* Round up a half millisecond for msleep ( ) .
*/
{ 100 , 0 , 0 }
} ;
2017-02-09 04:54:26 +03:00
2016-07-05 14:55:42 +03:00
enum isl29028_als_ir_mode {
ISL29028_MODE_NONE = 0 ,
ISL29028_MODE_ALS ,
ISL29028_MODE_IR ,
2012-04-13 14:33:32 +04:00
} ;
struct isl29028_chip {
2017-01-17 12:24:48 +03:00
struct mutex lock ;
struct regmap * regmap ;
2017-04-25 04:34:33 +03:00
int prox_sampling_int ;
int prox_sampling_frac ;
2017-01-17 12:24:48 +03:00
bool enable_prox ;
int lux_scale ;
2016-07-05 14:55:42 +03:00
enum isl29028_als_ir_mode als_ir_mode ;
2012-04-13 14:33:32 +04:00
} ;
2017-04-25 04:34:33 +03:00
static int isl29028_find_prox_sleep_index ( int sampling_int , int sampling_fract )
2012-04-13 14:33:32 +04:00
{
2017-02-09 04:54:26 +03:00
int i ;
2012-04-13 14:33:32 +04:00
2017-04-25 04:34:33 +03:00
for ( i = 0 ; i < ARRAY_SIZE ( isl29028_prox_data ) ; + + i ) {
if ( isl29028_prox_data [ i ] . sampling_int = = sampling_int & &
isl29028_prox_data [ i ] . sampling_fract = = sampling_fract )
return i ;
2012-04-13 14:33:32 +04:00
}
2017-01-17 12:24:51 +03:00
2017-04-25 04:34:33 +03:00
return - EINVAL ;
2017-02-09 04:54:26 +03:00
}
static int isl29028_set_proxim_sampling ( struct isl29028_chip * chip ,
2017-04-25 04:34:33 +03:00
int sampling_int , int sampling_fract )
2017-02-09 04:54:26 +03:00
{
struct device * dev = regmap_get_device ( chip - > regmap ) ;
int sleep_index , ret ;
2017-04-25 04:34:33 +03:00
sleep_index = isl29028_find_prox_sleep_index ( sampling_int ,
sampling_fract ) ;
if ( sleep_index < 0 )
return sleep_index ;
2017-01-17 12:24:53 +03:00
ret = regmap_update_bits ( chip - > regmap , ISL29028_REG_CONFIGURE ,
ISL29028_CONF_PROX_SLP_MASK ,
2017-02-09 04:54:26 +03:00
sleep_index < < ISL29028_CONF_PROX_SLP_SH ) ;
2017-01-17 12:24:53 +03:00
if ( ret < 0 ) {
dev_err ( dev , " %s(): Error %d setting the proximity sampling \n " ,
__func__ , ret ) ;
return ret ;
}
2017-04-25 04:34:33 +03:00
chip - > prox_sampling_int = sampling_int ;
chip - > prox_sampling_frac = sampling_fract ;
2017-01-17 12:24:53 +03:00
return ret ;
2012-04-13 14:33:32 +04:00
}
2017-01-17 12:24:59 +03:00
static int isl29028_enable_proximity ( struct isl29028_chip * chip )
2012-04-13 14:33:32 +04:00
{
2017-04-25 04:34:33 +03:00
int prox_index , ret ;
2012-04-13 14:33:32 +04:00
2017-04-25 04:34:33 +03:00
ret = isl29028_set_proxim_sampling ( chip , chip - > prox_sampling_int ,
chip - > prox_sampling_frac ) ;
2017-01-17 12:25:00 +03:00
if ( ret < 0 )
return ret ;
2012-04-13 14:33:32 +04:00
ret = regmap_update_bits ( chip - > regmap , ISL29028_REG_CONFIGURE ,
2017-01-17 12:24:59 +03:00
ISL29028_CONF_PROX_EN_MASK ,
ISL29028_CONF_PROX_EN ) ;
2012-04-13 14:33:32 +04:00
if ( ret < 0 )
return ret ;
/* Wait for conversion to be complete for first sample */
2017-04-25 04:34:33 +03:00
prox_index = isl29028_find_prox_sleep_index ( chip - > prox_sampling_int ,
chip - > prox_sampling_frac ) ;
if ( prox_index < 0 )
return prox_index ;
msleep ( isl29028_prox_data [ prox_index ] . sleep_time ) ;
2017-01-17 12:24:51 +03:00
2012-04-13 14:33:32 +04:00
return 0 ;
}
static int isl29028_set_als_scale ( struct isl29028_chip * chip , int lux_scale )
{
2017-01-17 12:24:54 +03:00
struct device * dev = regmap_get_device ( chip - > regmap ) ;
2016-07-05 14:55:41 +03:00
int val = ( lux_scale = = 2000 ) ? ISL29028_CONF_ALS_RANGE_HIGH_LUX :
ISL29028_CONF_ALS_RANGE_LOW_LUX ;
2017-01-17 12:24:54 +03:00
int ret ;
ret = regmap_update_bits ( chip - > regmap , ISL29028_REG_CONFIGURE ,
ISL29028_CONF_ALS_RANGE_MASK , val ) ;
if ( ret < 0 ) {
dev_err ( dev , " %s(): Error %d setting the ALS scale \n " , __func__ ,
ret ) ;
return ret ;
}
chip - > lux_scale = lux_scale ;
2012-04-13 14:33:32 +04:00
2017-01-17 12:24:54 +03:00
return ret ;
2012-04-13 14:33:32 +04:00
}
static int isl29028_set_als_ir_mode ( struct isl29028_chip * chip ,
2016-07-05 14:55:42 +03:00
enum isl29028_als_ir_mode mode )
2012-04-13 14:33:32 +04:00
{
2017-01-17 12:25:01 +03:00
int ret ;
2012-04-13 14:33:32 +04:00
2016-12-04 05:19:25 +03:00
if ( chip - > als_ir_mode = = mode )
return 0 ;
2017-01-17 12:25:01 +03:00
ret = isl29028_set_als_scale ( chip , chip - > lux_scale ) ;
if ( ret < 0 )
return ret ;
2012-04-13 14:33:32 +04:00
switch ( mode ) {
2016-07-05 14:55:42 +03:00
case ISL29028_MODE_ALS :
2012-04-13 14:33:32 +04:00
ret = regmap_update_bits ( chip - > regmap , ISL29028_REG_CONFIGURE ,
2016-07-05 14:55:41 +03:00
ISL29028_CONF_ALS_IR_MODE_MASK ,
ISL29028_CONF_ALS_IR_MODE_ALS ) ;
2012-04-13 14:33:32 +04:00
if ( ret < 0 )
return ret ;
ret = regmap_update_bits ( chip - > regmap , ISL29028_REG_CONFIGURE ,
2016-07-05 14:55:41 +03:00
ISL29028_CONF_ALS_RANGE_MASK ,
ISL29028_CONF_ALS_RANGE_HIGH_LUX ) ;
2012-04-13 14:33:32 +04:00
break ;
2016-07-05 14:55:42 +03:00
case ISL29028_MODE_IR :
2012-04-13 14:33:32 +04:00
ret = regmap_update_bits ( chip - > regmap , ISL29028_REG_CONFIGURE ,
2016-07-05 14:55:41 +03:00
ISL29028_CONF_ALS_IR_MODE_MASK ,
ISL29028_CONF_ALS_IR_MODE_IR ) ;
2012-04-13 14:33:32 +04:00
break ;
2016-07-05 14:55:42 +03:00
case ISL29028_MODE_NONE :
2012-04-13 14:33:32 +04:00
return regmap_update_bits ( chip - > regmap , ISL29028_REG_CONFIGURE ,
2017-01-17 12:24:49 +03:00
ISL29028_CONF_ALS_EN_MASK ,
ISL29028_CONF_ALS_DIS ) ;
2012-04-13 14:33:32 +04:00
}
if ( ret < 0 )
return ret ;
/* Enable the ALS/IR */
ret = regmap_update_bits ( chip - > regmap , ISL29028_REG_CONFIGURE ,
2016-07-05 14:55:41 +03:00
ISL29028_CONF_ALS_EN_MASK ,
ISL29028_CONF_ALS_EN ) ;
2012-04-13 14:33:32 +04:00
if ( ret < 0 )
return ret ;
/* Need to wait for conversion time if ALS/IR mode enabled */
2017-02-09 04:54:25 +03:00
msleep ( ISL29028_CONV_TIME_MS ) ;
2016-12-04 05:19:25 +03:00
chip - > als_ir_mode = mode ;
2012-04-13 14:33:32 +04:00
return 0 ;
}
static int isl29028_read_als_ir ( struct isl29028_chip * chip , int * als_ir )
{
2016-03-15 21:49:12 +03:00
struct device * dev = regmap_get_device ( chip - > regmap ) ;
2012-04-13 14:33:32 +04:00
unsigned int lsb ;
unsigned int msb ;
int ret ;
ret = regmap_read ( chip - > regmap , ISL29028_REG_ALSIR_L , & lsb ) ;
if ( ret < 0 ) {
2016-03-15 21:49:12 +03:00
dev_err ( dev ,
2017-01-17 12:24:55 +03:00
" %s(): Error %d reading register ALSIR_L \n " ,
__func__ , ret ) ;
2012-04-13 14:33:32 +04:00
return ret ;
}
ret = regmap_read ( chip - > regmap , ISL29028_REG_ALSIR_U , & msb ) ;
if ( ret < 0 ) {
2016-03-15 21:49:12 +03:00
dev_err ( dev ,
2017-01-17 12:24:55 +03:00
" %s(): Error %d reading register ALSIR_U \n " ,
__func__ , ret ) ;
2012-04-13 14:33:32 +04:00
return ret ;
}
* als_ir = ( ( msb & 0xF ) < < 8 ) | ( lsb & 0xFF ) ;
2017-01-17 12:24:51 +03:00
2012-04-13 14:33:32 +04:00
return 0 ;
}
static int isl29028_read_proxim ( struct isl29028_chip * chip , int * prox )
{
2016-03-15 21:49:12 +03:00
struct device * dev = regmap_get_device ( chip - > regmap ) ;
2012-04-13 14:33:32 +04:00
unsigned int data ;
int ret ;
2017-01-17 12:24:50 +03:00
if ( ! chip - > enable_prox ) {
2017-01-17 12:24:59 +03:00
ret = isl29028_enable_proximity ( chip ) ;
2017-01-17 12:24:50 +03:00
if ( ret < 0 )
return ret ;
2017-01-17 12:24:51 +03:00
2017-01-17 12:24:50 +03:00
chip - > enable_prox = true ;
}
2012-04-13 14:33:32 +04:00
ret = regmap_read ( chip - > regmap , ISL29028_REG_PROX_DATA , & data ) ;
if ( ret < 0 ) {
2017-01-17 12:24:55 +03:00
dev_err ( dev , " %s(): Error %d reading register PROX_DATA \n " ,
__func__ , ret ) ;
2012-04-13 14:33:32 +04:00
return ret ;
}
2017-01-17 12:24:51 +03:00
2012-04-13 14:33:32 +04:00
* prox = data ;
2017-01-17 12:24:51 +03:00
2012-04-13 14:33:32 +04:00
return 0 ;
}
static int isl29028_als_get ( struct isl29028_chip * chip , int * als_data )
{
2016-03-15 21:49:12 +03:00
struct device * dev = regmap_get_device ( chip - > regmap ) ;
2012-04-13 14:33:32 +04:00
int ret ;
int als_ir_data ;
2016-12-04 05:19:25 +03:00
ret = isl29028_set_als_ir_mode ( chip , ISL29028_MODE_ALS ) ;
if ( ret < 0 ) {
2017-01-17 12:24:55 +03:00
dev_err ( dev , " %s(): Error %d enabling ALS mode \n " , __func__ ,
ret ) ;
2016-12-04 05:19:25 +03:00
return ret ;
2012-04-13 14:33:32 +04:00
}
ret = isl29028_read_als_ir ( chip , & als_ir_data ) ;
if ( ret < 0 )
return ret ;
/*
* convert als data count to lux .
* if lux_scale = 125 , lux = count * 0.031
* if lux_scale = 2000 , lux = count * 0.49
*/
if ( chip - > lux_scale = = 125 )
als_ir_data = ( als_ir_data * 31 ) / 1000 ;
else
als_ir_data = ( als_ir_data * 49 ) / 100 ;
* als_data = als_ir_data ;
2017-01-17 12:24:51 +03:00
2012-04-13 14:33:32 +04:00
return 0 ;
}
static int isl29028_ir_get ( struct isl29028_chip * chip , int * ir_data )
{
2016-03-15 21:49:12 +03:00
struct device * dev = regmap_get_device ( chip - > regmap ) ;
2012-04-13 14:33:32 +04:00
int ret ;
2016-12-04 05:19:25 +03:00
ret = isl29028_set_als_ir_mode ( chip , ISL29028_MODE_IR ) ;
if ( ret < 0 ) {
2017-01-17 12:24:55 +03:00
dev_err ( dev , " %s(): Error %d enabling IR mode \n " , __func__ ,
ret ) ;
2016-12-04 05:19:25 +03:00
return ret ;
2012-04-13 14:33:32 +04:00
}
2017-01-17 12:24:51 +03:00
2012-04-13 14:33:32 +04:00
return isl29028_read_als_ir ( chip , ir_data ) ;
}
2017-01-17 12:25:02 +03:00
static int isl29028_set_pm_runtime_busy ( struct isl29028_chip * chip , bool on )
{
struct device * dev = regmap_get_device ( chip - > regmap ) ;
int ret ;
if ( on ) {
ret = pm_runtime_get_sync ( dev ) ;
if ( ret < 0 )
pm_runtime_put_noidle ( dev ) ;
} else {
pm_runtime_mark_last_busy ( dev ) ;
ret = pm_runtime_put_autosuspend ( dev ) ;
}
return ret ;
}
2012-04-13 14:33:32 +04:00
/* Channel IO */
static int isl29028_write_raw ( struct iio_dev * indio_dev ,
2016-02-18 10:35:36 +03:00
struct iio_chan_spec const * chan ,
int val , int val2 , long mask )
2012-04-13 14:33:32 +04:00
{
struct isl29028_chip * chip = iio_priv ( indio_dev ) ;
2016-03-15 21:49:12 +03:00
struct device * dev = regmap_get_device ( chip - > regmap ) ;
2017-01-17 12:25:02 +03:00
int ret ;
ret = isl29028_set_pm_runtime_busy ( chip , true ) ;
if ( ret < 0 )
return ret ;
2012-04-13 14:33:32 +04:00
mutex_lock ( & chip - > lock ) ;
2017-01-17 12:25:02 +03:00
ret = - EINVAL ;
2012-04-13 14:33:32 +04:00
switch ( chan - > type ) {
case IIO_PROXIMITY :
2012-04-16 19:57:42 +04:00
if ( mask ! = IIO_CHAN_INFO_SAMP_FREQ ) {
2016-03-15 21:49:12 +03:00
dev_err ( dev ,
2017-01-17 12:24:55 +03:00
" %s(): proximity: Mask value 0x%08lx is not supported \n " ,
__func__ , mask ) ;
2012-04-13 14:33:32 +04:00
break ;
}
2017-01-17 12:24:51 +03:00
2012-04-13 14:33:32 +04:00
if ( val < 1 | | val > 100 ) {
2016-03-15 21:49:12 +03:00
dev_err ( dev ,
2017-01-17 12:24:55 +03:00
" %s(): proximity: Sampling frequency %d is not in the range [1:100] \n " ,
__func__ , val ) ;
2012-04-13 14:33:32 +04:00
break ;
}
2017-01-17 12:24:51 +03:00
2017-04-25 04:34:33 +03:00
ret = isl29028_set_proxim_sampling ( chip , val , val2 ) ;
2012-04-13 14:33:32 +04:00
break ;
case IIO_LIGHT :
2012-04-16 19:57:42 +04:00
if ( mask ! = IIO_CHAN_INFO_SCALE ) {
2016-03-15 21:49:12 +03:00
dev_err ( dev ,
2017-01-17 12:24:55 +03:00
" %s(): light: Mask value 0x%08lx is not supported \n " ,
__func__ , mask ) ;
2012-04-13 14:33:32 +04:00
break ;
}
2017-01-17 12:24:51 +03:00
2017-01-17 12:24:58 +03:00
if ( val ! = 125 & & val ! = 2000 ) {
2016-03-15 21:49:12 +03:00
dev_err ( dev ,
2017-01-17 12:24:55 +03:00
" %s(): light: Lux scale %d is not in the set {125, 2000} \n " ,
__func__ , val ) ;
2012-04-13 14:33:32 +04:00
break ;
}
2017-01-17 12:24:51 +03:00
2012-04-13 14:33:32 +04:00
ret = isl29028_set_als_scale ( chip , val ) ;
break ;
default :
2017-01-17 12:24:55 +03:00
dev_err ( dev , " %s(): Unsupported channel type %x \n " ,
__func__ , chan - > type ) ;
2012-04-13 14:33:32 +04:00
break ;
}
2017-01-17 12:24:51 +03:00
2012-04-13 14:33:32 +04:00
mutex_unlock ( & chip - > lock ) ;
2017-01-17 12:24:51 +03:00
2017-01-17 12:25:02 +03:00
if ( ret < 0 )
return ret ;
ret = isl29028_set_pm_runtime_busy ( chip , false ) ;
if ( ret < 0 )
return ret ;
2012-04-13 14:33:32 +04:00
return ret ;
}
static int isl29028_read_raw ( struct iio_dev * indio_dev ,
2016-02-18 10:35:36 +03:00
struct iio_chan_spec const * chan ,
int * val , int * val2 , long mask )
2012-04-13 14:33:32 +04:00
{
struct isl29028_chip * chip = iio_priv ( indio_dev ) ;
2016-03-15 21:49:12 +03:00
struct device * dev = regmap_get_device ( chip - > regmap ) ;
2017-01-17 12:25:02 +03:00
int ret , pm_ret ;
ret = isl29028_set_pm_runtime_busy ( chip , true ) ;
if ( ret < 0 )
return ret ;
2012-04-13 14:33:32 +04:00
mutex_lock ( & chip - > lock ) ;
2017-01-17 12:25:02 +03:00
ret = - EINVAL ;
2012-04-13 14:33:32 +04:00
switch ( mask ) {
2012-04-15 20:41:29 +04:00
case IIO_CHAN_INFO_RAW :
case IIO_CHAN_INFO_PROCESSED :
2012-04-13 14:33:32 +04:00
switch ( chan - > type ) {
case IIO_LIGHT :
ret = isl29028_als_get ( chip , val ) ;
break ;
case IIO_INTENSITY :
ret = isl29028_ir_get ( chip , val ) ;
break ;
case IIO_PROXIMITY :
2017-01-17 12:24:50 +03:00
ret = isl29028_read_proxim ( chip , val ) ;
2012-04-13 14:33:32 +04:00
break ;
default :
break ;
}
2017-01-17 12:24:51 +03:00
2012-04-13 14:33:32 +04:00
if ( ret < 0 )
break ;
2017-01-17 12:24:51 +03:00
2012-04-13 14:33:32 +04:00
ret = IIO_VAL_INT ;
break ;
2012-04-16 19:57:42 +04:00
case IIO_CHAN_INFO_SAMP_FREQ :
2012-04-13 14:33:32 +04:00
if ( chan - > type ! = IIO_PROXIMITY )
break ;
2017-01-17 12:24:51 +03:00
2017-04-25 04:34:33 +03:00
* val = chip - > prox_sampling_int ;
* val2 = chip - > prox_sampling_frac ;
2012-04-13 14:33:32 +04:00
ret = IIO_VAL_INT ;
break ;
2012-04-16 19:57:42 +04:00
case IIO_CHAN_INFO_SCALE :
2012-04-13 14:33:32 +04:00
if ( chan - > type ! = IIO_LIGHT )
break ;
* val = chip - > lux_scale ;
ret = IIO_VAL_INT ;
break ;
default :
2017-01-17 12:24:55 +03:00
dev_err ( dev , " %s(): mask value 0x%08lx is not supported \n " ,
__func__ , mask ) ;
2012-04-13 14:33:32 +04:00
break ;
}
2017-01-17 12:24:51 +03:00
2012-04-13 14:33:32 +04:00
mutex_unlock ( & chip - > lock ) ;
2017-01-17 12:24:51 +03:00
2017-01-17 12:25:02 +03:00
if ( ret < 0 )
return ret ;
/**
* Preserve the ret variable if the call to
* isl29028_set_pm_runtime_busy ( ) is successful so the reading
* ( if applicable ) is returned to user space .
*/
pm_ret = isl29028_set_pm_runtime_busy ( chip , false ) ;
if ( pm_ret < 0 )
return pm_ret ;
2012-04-13 14:33:32 +04:00
return ret ;
}
static IIO_CONST_ATTR ( in_proximity_sampling_frequency_available ,
2017-04-25 04:34:33 +03:00
" 1.25 2.5 5 10 13.3 20 80 100 " ) ;
2016-07-05 14:55:43 +03:00
static IIO_CONST_ATTR ( in_illuminance_scale_available , " 125 2000 " ) ;
2012-04-13 14:33:32 +04:00
# define ISL29028_CONST_ATTR(name) (&iio_const_attr_##name.dev_attr.attr)
static struct attribute * isl29028_attributes [ ] = {
ISL29028_CONST_ATTR ( in_proximity_sampling_frequency_available ) ,
ISL29028_CONST_ATTR ( in_illuminance_scale_available ) ,
NULL ,
} ;
static const struct attribute_group isl29108_group = {
. attrs = isl29028_attributes ,
} ;
static const struct iio_chan_spec isl29028_channels [ ] = {
{
. type = IIO_LIGHT ,
2013-02-27 23:39:52 +04:00
. info_mask_separate = BIT ( IIO_CHAN_INFO_PROCESSED ) |
BIT ( IIO_CHAN_INFO_SCALE ) ,
2012-04-13 14:33:32 +04:00
} , {
. type = IIO_INTENSITY ,
2013-02-27 23:39:52 +04:00
. info_mask_separate = BIT ( IIO_CHAN_INFO_RAW ) ,
2012-04-13 14:33:32 +04:00
} , {
. type = IIO_PROXIMITY ,
2013-02-27 23:39:52 +04:00
. info_mask_separate = BIT ( IIO_CHAN_INFO_RAW ) |
BIT ( IIO_CHAN_INFO_SAMP_FREQ ) ,
2012-04-13 14:33:32 +04:00
}
} ;
static const struct iio_info isl29028_info = {
. attrs = & isl29108_group ,
2016-02-12 18:10:28 +03:00
. read_raw = isl29028_read_raw ,
. write_raw = isl29028_write_raw ,
2012-04-13 14:33:32 +04:00
} ;
2017-01-17 12:25:02 +03:00
static int isl29028_clear_configure_reg ( struct isl29028_chip * chip )
2012-04-13 14:33:32 +04:00
{
2016-03-15 21:49:12 +03:00
struct device * dev = regmap_get_device ( chip - > regmap ) ;
2012-04-13 14:33:32 +04:00
int ret ;
ret = regmap_write ( chip - > regmap , ISL29028_REG_CONFIGURE , 0x0 ) ;
2017-01-17 12:25:02 +03:00
if ( ret < 0 )
2017-01-17 12:24:55 +03:00
dev_err ( dev , " %s(): Error %d clearing the CONFIGURE register \n " ,
__func__ , ret ) ;
2017-01-17 12:25:02 +03:00
chip - > als_ir_mode = ISL29028_MODE_NONE ;
chip - > enable_prox = false ;
2012-04-13 14:33:32 +04:00
2017-01-17 12:25:01 +03:00
return ret ;
2012-04-13 14:33:32 +04:00
}
2016-07-05 14:55:44 +03:00
static bool isl29028_is_volatile_reg ( struct device * dev , unsigned int reg )
2012-04-13 14:33:32 +04:00
{
switch ( reg ) {
case ISL29028_REG_INTERRUPT :
case ISL29028_REG_PROX_DATA :
case ISL29028_REG_ALSIR_L :
case ISL29028_REG_ALSIR_U :
return true ;
default :
return false ;
}
}
static const struct regmap_config isl29028_regmap_config = {
. reg_bits = 8 ,
. val_bits = 8 ,
2016-07-05 14:55:44 +03:00
. volatile_reg = isl29028_is_volatile_reg ,
2012-04-13 14:33:32 +04:00
. max_register = ISL29028_NUM_REGS - 1 ,
. num_reg_defaults_raw = ISL29028_NUM_REGS ,
. cache_type = REGCACHE_RBTREE ,
} ;
2012-11-19 22:21:57 +04:00
static int isl29028_probe ( struct i2c_client * client ,
2016-02-18 10:35:36 +03:00
const struct i2c_device_id * id )
2012-04-13 14:33:32 +04:00
{
struct isl29028_chip * chip ;
struct iio_dev * indio_dev ;
int ret ;
2013-07-22 15:03:00 +04:00
indio_dev = devm_iio_device_alloc ( & client - > dev , sizeof ( * chip ) ) ;
2017-01-17 12:24:57 +03:00
if ( ! indio_dev )
2012-04-13 14:33:32 +04:00
return - ENOMEM ;
chip = iio_priv ( indio_dev ) ;
i2c_set_clientdata ( client , indio_dev ) ;
mutex_init ( & chip - > lock ) ;
chip - > regmap = devm_regmap_init_i2c ( client , & isl29028_regmap_config ) ;
if ( IS_ERR ( chip - > regmap ) ) {
ret = PTR_ERR ( chip - > regmap ) ;
2017-01-17 12:24:55 +03:00
dev_err ( & client - > dev , " %s: Error %d initializing regmap \n " ,
__func__ , ret ) ;
2013-07-22 15:03:00 +04:00
return ret ;
2012-04-13 14:33:32 +04:00
}
2016-12-04 05:19:27 +03:00
chip - > enable_prox = false ;
2017-04-25 04:34:33 +03:00
chip - > prox_sampling_int = 20 ;
chip - > prox_sampling_frac = 0 ;
2016-12-04 05:19:27 +03:00
chip - > lux_scale = 2000 ;
ret = regmap_write ( chip - > regmap , ISL29028_REG_TEST1_MODE , 0x0 ) ;
if ( ret < 0 ) {
dev_err ( & client - > dev ,
2017-01-17 12:24:55 +03:00
" %s(): Error %d writing to TEST1_MODE register \n " ,
__func__ , ret ) ;
2016-12-04 05:19:27 +03:00
return ret ;
}
2017-01-17 12:24:51 +03:00
2016-12-04 05:19:27 +03:00
ret = regmap_write ( chip - > regmap , ISL29028_REG_TEST2_MODE , 0x0 ) ;
if ( ret < 0 ) {
dev_err ( & client - > dev ,
2017-01-17 12:24:55 +03:00
" %s(): Error %d writing to TEST2_MODE register \n " ,
__func__ , ret ) ;
2016-12-04 05:19:27 +03:00
return ret ;
}
2017-01-17 12:25:02 +03:00
ret = isl29028_clear_configure_reg ( chip ) ;
2017-01-17 12:24:56 +03:00
if ( ret < 0 )
2013-07-22 15:03:00 +04:00
return ret ;
2012-04-13 14:33:32 +04:00
indio_dev - > info = & isl29028_info ;
indio_dev - > channels = isl29028_channels ;
indio_dev - > num_channels = ARRAY_SIZE ( isl29028_channels ) ;
indio_dev - > name = id - > name ;
indio_dev - > modes = INDIO_DIRECT_MODE ;
2017-01-17 12:24:51 +03:00
2017-01-17 12:25:02 +03:00
pm_runtime_enable ( & client - > dev ) ;
pm_runtime_set_autosuspend_delay ( & client - > dev ,
ISL29028_POWER_OFF_DELAY_MS ) ;
pm_runtime_use_autosuspend ( & client - > dev ) ;
2015-10-02 13:37:49 +03:00
ret = devm_iio_device_register ( indio_dev - > dev . parent , indio_dev ) ;
2012-04-13 14:33:32 +04:00
if ( ret < 0 ) {
2016-03-15 21:49:12 +03:00
dev_err ( & client - > dev ,
2017-01-17 12:24:55 +03:00
" %s(): iio registration failed with error %d \n " ,
__func__ , ret ) ;
2013-07-22 15:03:00 +04:00
return ret ;
2012-04-13 14:33:32 +04:00
}
2017-01-17 12:24:51 +03:00
2012-04-13 14:33:32 +04:00
return 0 ;
}
2017-01-17 12:25:02 +03:00
static int isl29028_remove ( struct i2c_client * client )
{
struct iio_dev * indio_dev = i2c_get_clientdata ( client ) ;
struct isl29028_chip * chip = iio_priv ( indio_dev ) ;
iio_device_unregister ( indio_dev ) ;
pm_runtime_disable ( & client - > dev ) ;
pm_runtime_set_suspended ( & client - > dev ) ;
pm_runtime_put_noidle ( & client - > dev ) ;
return isl29028_clear_configure_reg ( chip ) ;
}
static int __maybe_unused isl29028_suspend ( struct device * dev )
{
struct iio_dev * indio_dev = i2c_get_clientdata ( to_i2c_client ( dev ) ) ;
struct isl29028_chip * chip = iio_priv ( indio_dev ) ;
int ret ;
mutex_lock ( & chip - > lock ) ;
ret = isl29028_clear_configure_reg ( chip ) ;
mutex_unlock ( & chip - > lock ) ;
return ret ;
}
static int __maybe_unused isl29028_resume ( struct device * dev )
{
/**
* The specific component ( ALS / IR or proximity ) will enable itself as
* needed the next time that the user requests a reading . This is done
* above in isl29028_set_als_ir_mode ( ) and isl29028_enable_proximity ( ) .
*/
return 0 ;
}
static const struct dev_pm_ops isl29028_pm_ops = {
2017-02-09 04:54:28 +03:00
SET_SYSTEM_SLEEP_PM_OPS ( pm_runtime_force_suspend ,
pm_runtime_force_resume )
2017-01-17 12:25:02 +03:00
SET_RUNTIME_PM_OPS ( isl29028_suspend , isl29028_resume , NULL )
} ;
2012-04-13 14:33:32 +04:00
static const struct i2c_device_id isl29028_id [ ] = {
{ " isl29028 " , 0 } ,
2017-04-28 18:55:58 +03:00
{ " isl29030 " , 0 } ,
2012-04-13 14:33:32 +04:00
{ }
} ;
MODULE_DEVICE_TABLE ( i2c , isl29028_id ) ;
static const struct of_device_id isl29028_of_match [ ] = {
2015-02-17 02:58:39 +03:00
{ . compatible = " isl,isl29028 " , } , /* for backward compat., don't use */
{ . compatible = " isil,isl29028 " , } ,
2017-04-28 18:55:58 +03:00
{ . compatible = " isil,isl29030 " , } ,
2012-04-13 14:33:32 +04:00
{ } ,
} ;
MODULE_DEVICE_TABLE ( of , isl29028_of_match ) ;
static struct i2c_driver isl29028_driver = {
. driver = {
. name = " isl29028 " ,
2017-01-17 12:25:02 +03:00
. pm = & isl29028_pm_ops ,
2012-04-13 14:33:32 +04:00
. of_match_table = isl29028_of_match ,
} ,
. probe = isl29028_probe ,
2017-01-17 12:25:02 +03:00
. remove = isl29028_remove ,
2012-04-13 14:33:32 +04:00
. id_table = isl29028_id ,
} ;
module_i2c_driver ( isl29028_driver ) ;
MODULE_DESCRIPTION ( " ISL29028 Ambient Light and Proximity Sensor driver " ) ;
MODULE_LICENSE ( " GPL v2 " ) ;
MODULE_AUTHOR ( " Laxman Dewangan <ldewangan@nvidia.com> " ) ;