2019-05-28 10:10:04 -07:00
// SPDX-License-Identifier: GPL-2.0-only
2012-04-13 16:03:32 +05:30
/*
* 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-08 20:54:29 -05:00
* Copyright ( c ) 2016 - 2017 Brian Masney < masneyb @ onstation . org >
2012-04-13 16:03:32 +05:30
*
2017-04-28 17:55:58 +02: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 16:03:32 +05:30
*/
# 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 15:54:58 +01:00
# include <linux/iio/iio.h>
# include <linux/iio/sysfs.h>
2017-01-17 04:25:02 -05:00
# include <linux/pm_runtime.h>
2012-04-13 16:03:32 +05:30
2016-12-03 21:19:29 -05:00
# define ISL29028_CONV_TIME_MS 100
2012-04-13 16:03:32 +05:30
2016-12-03 21:19:29 -05:00
# define ISL29028_REG_CONFIGURE 0x01
2012-04-13 16:03:32 +05:30
2016-12-03 21:19:29 -05: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 16:03:32 +05:30
2016-12-03 21:19:29 -05:00
# define ISL29028_CONF_ALS_RANGE_LOW_LUX 0
2016-07-05 13:55:41 +02:00
# define ISL29028_CONF_ALS_RANGE_HIGH_LUX BIT(1)
2016-12-03 21:19:29 -05:00
# define ISL29028_CONF_ALS_RANGE_MASK BIT(1)
2012-04-13 16:03:32 +05:30
2016-12-03 21:19:29 -05: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 16:03:32 +05:30
2016-12-03 21:19:29 -05:00
# define ISL29028_CONF_PROX_SLP_SH 4
# define ISL29028_CONF_PROX_SLP_MASK (7 << ISL29028_CONF_PROX_SLP_SH)
2012-04-13 16:03:32 +05:30
2016-12-03 21:19:29 -05:00
# define ISL29028_CONF_PROX_EN BIT(7)
# define ISL29028_CONF_PROX_EN_MASK BIT(7)
2012-04-13 16:03:32 +05:30
2016-12-03 21:19:29 -05:00
# define ISL29028_REG_INTERRUPT 0x02
2012-04-13 16:03:32 +05:30
2016-12-03 21:19:29 -05:00
# define ISL29028_REG_PROX_DATA 0x08
# define ISL29028_REG_ALSIR_L 0x09
# define ISL29028_REG_ALSIR_U 0x0A
2012-04-13 16:03:32 +05:30
2016-12-03 21:19:29 -05:00
# define ISL29028_REG_TEST1_MODE 0x0E
# define ISL29028_REG_TEST2_MODE 0x0F
2012-04-13 16:03:32 +05:30
2016-12-03 21:19:29 -05:00
# define ISL29028_NUM_REGS (ISL29028_REG_TEST2_MODE + 1)
2012-04-13 16:03:32 +05:30
2017-01-17 04:25:02 -05:00
# define ISL29028_POWER_OFF_DELAY_MS 2000
2017-04-24 21:34:33 -04: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-08 20:54:26 -05:00
2016-07-05 13:55:42 +02:00
enum isl29028_als_ir_mode {
ISL29028_MODE_NONE = 0 ,
ISL29028_MODE_ALS ,
ISL29028_MODE_IR ,
2012-04-13 16:03:32 +05:30
} ;
struct isl29028_chip {
2017-01-17 04:24:48 -05:00
struct mutex lock ;
struct regmap * regmap ;
2017-04-24 21:34:33 -04:00
int prox_sampling_int ;
int prox_sampling_frac ;
2017-01-17 04:24:48 -05:00
bool enable_prox ;
int lux_scale ;
2016-07-05 13:55:42 +02:00
enum isl29028_als_ir_mode als_ir_mode ;
2012-04-13 16:03:32 +05:30
} ;
2017-04-24 21:34:33 -04:00
static int isl29028_find_prox_sleep_index ( int sampling_int , int sampling_fract )
2012-04-13 16:03:32 +05:30
{
2017-02-08 20:54:26 -05:00
int i ;
2012-04-13 16:03:32 +05:30
2017-04-24 21:34:33 -04: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 16:03:32 +05:30
}
2017-01-17 04:24:51 -05:00
2017-04-24 21:34:33 -04:00
return - EINVAL ;
2017-02-08 20:54:26 -05:00
}
static int isl29028_set_proxim_sampling ( struct isl29028_chip * chip ,
2017-04-24 21:34:33 -04:00
int sampling_int , int sampling_fract )
2017-02-08 20:54:26 -05:00
{
struct device * dev = regmap_get_device ( chip - > regmap ) ;
int sleep_index , ret ;
2017-04-24 21:34:33 -04:00
sleep_index = isl29028_find_prox_sleep_index ( sampling_int ,
sampling_fract ) ;
if ( sleep_index < 0 )
return sleep_index ;
2017-01-17 04:24:53 -05:00
ret = regmap_update_bits ( chip - > regmap , ISL29028_REG_CONFIGURE ,
ISL29028_CONF_PROX_SLP_MASK ,
2017-02-08 20:54:26 -05:00
sleep_index < < ISL29028_CONF_PROX_SLP_SH ) ;
2017-01-17 04:24:53 -05:00
if ( ret < 0 ) {
dev_err ( dev , " %s(): Error %d setting the proximity sampling \n " ,
__func__ , ret ) ;
return ret ;
}
2017-04-24 21:34:33 -04:00
chip - > prox_sampling_int = sampling_int ;
chip - > prox_sampling_frac = sampling_fract ;
2017-01-17 04:24:53 -05:00
return ret ;
2012-04-13 16:03:32 +05:30
}
2017-01-17 04:24:59 -05:00
static int isl29028_enable_proximity ( struct isl29028_chip * chip )
2012-04-13 16:03:32 +05:30
{
2017-04-24 21:34:33 -04:00
int prox_index , ret ;
2012-04-13 16:03:32 +05:30
2017-04-24 21:34:33 -04:00
ret = isl29028_set_proxim_sampling ( chip , chip - > prox_sampling_int ,
chip - > prox_sampling_frac ) ;
2017-01-17 04:25:00 -05:00
if ( ret < 0 )
return ret ;
2012-04-13 16:03:32 +05:30
ret = regmap_update_bits ( chip - > regmap , ISL29028_REG_CONFIGURE ,
2017-01-17 04:24:59 -05:00
ISL29028_CONF_PROX_EN_MASK ,
ISL29028_CONF_PROX_EN ) ;
2012-04-13 16:03:32 +05:30
if ( ret < 0 )
return ret ;
/* Wait for conversion to be complete for first sample */
2017-04-24 21:34:33 -04: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 04:24:51 -05:00
2012-04-13 16:03:32 +05:30
return 0 ;
}
static int isl29028_set_als_scale ( struct isl29028_chip * chip , int lux_scale )
{
2017-01-17 04:24:54 -05:00
struct device * dev = regmap_get_device ( chip - > regmap ) ;
2016-07-05 13:55:41 +02:00
int val = ( lux_scale = = 2000 ) ? ISL29028_CONF_ALS_RANGE_HIGH_LUX :
ISL29028_CONF_ALS_RANGE_LOW_LUX ;
2017-01-17 04:24:54 -05: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 16:03:32 +05:30
2017-01-17 04:24:54 -05:00
return ret ;
2012-04-13 16:03:32 +05:30
}
static int isl29028_set_als_ir_mode ( struct isl29028_chip * chip ,
2016-07-05 13:55:42 +02:00
enum isl29028_als_ir_mode mode )
2012-04-13 16:03:32 +05:30
{
2017-01-17 04:25:01 -05:00
int ret ;
2012-04-13 16:03:32 +05:30
2016-12-03 21:19:25 -05:00
if ( chip - > als_ir_mode = = mode )
return 0 ;
2017-01-17 04:25:01 -05:00
ret = isl29028_set_als_scale ( chip , chip - > lux_scale ) ;
if ( ret < 0 )
return ret ;
2012-04-13 16:03:32 +05:30
switch ( mode ) {
2016-07-05 13:55:42 +02:00
case ISL29028_MODE_ALS :
2012-04-13 16:03:32 +05:30
ret = regmap_update_bits ( chip - > regmap , ISL29028_REG_CONFIGURE ,
2016-07-05 13:55:41 +02:00
ISL29028_CONF_ALS_IR_MODE_MASK ,
ISL29028_CONF_ALS_IR_MODE_ALS ) ;
2012-04-13 16:03:32 +05:30
if ( ret < 0 )
return ret ;
ret = regmap_update_bits ( chip - > regmap , ISL29028_REG_CONFIGURE ,
2016-07-05 13:55:41 +02:00
ISL29028_CONF_ALS_RANGE_MASK ,
ISL29028_CONF_ALS_RANGE_HIGH_LUX ) ;
2012-04-13 16:03:32 +05:30
break ;
2016-07-05 13:55:42 +02:00
case ISL29028_MODE_IR :
2012-04-13 16:03:32 +05:30
ret = regmap_update_bits ( chip - > regmap , ISL29028_REG_CONFIGURE ,
2016-07-05 13:55:41 +02:00
ISL29028_CONF_ALS_IR_MODE_MASK ,
ISL29028_CONF_ALS_IR_MODE_IR ) ;
2012-04-13 16:03:32 +05:30
break ;
2016-07-05 13:55:42 +02:00
case ISL29028_MODE_NONE :
2012-04-13 16:03:32 +05:30
return regmap_update_bits ( chip - > regmap , ISL29028_REG_CONFIGURE ,
2017-01-17 04:24:49 -05:00
ISL29028_CONF_ALS_EN_MASK ,
ISL29028_CONF_ALS_DIS ) ;
2012-04-13 16:03:32 +05:30
}
if ( ret < 0 )
return ret ;
/* Enable the ALS/IR */
ret = regmap_update_bits ( chip - > regmap , ISL29028_REG_CONFIGURE ,
2016-07-05 13:55:41 +02:00
ISL29028_CONF_ALS_EN_MASK ,
ISL29028_CONF_ALS_EN ) ;
2012-04-13 16:03:32 +05:30
if ( ret < 0 )
return ret ;
/* Need to wait for conversion time if ALS/IR mode enabled */
2017-02-08 20:54:25 -05:00
msleep ( ISL29028_CONV_TIME_MS ) ;
2016-12-03 21:19:25 -05:00
chip - > als_ir_mode = mode ;
2012-04-13 16:03:32 +05:30
return 0 ;
}
static int isl29028_read_als_ir ( struct isl29028_chip * chip , int * als_ir )
{
2016-03-15 11:49:12 -07:00
struct device * dev = regmap_get_device ( chip - > regmap ) ;
2012-04-13 16:03:32 +05:30
unsigned int lsb ;
unsigned int msb ;
int ret ;
ret = regmap_read ( chip - > regmap , ISL29028_REG_ALSIR_L , & lsb ) ;
if ( ret < 0 ) {
2016-03-15 11:49:12 -07:00
dev_err ( dev ,
2017-01-17 04:24:55 -05:00
" %s(): Error %d reading register ALSIR_L \n " ,
__func__ , ret ) ;
2012-04-13 16:03:32 +05:30
return ret ;
}
ret = regmap_read ( chip - > regmap , ISL29028_REG_ALSIR_U , & msb ) ;
if ( ret < 0 ) {
2016-03-15 11:49:12 -07:00
dev_err ( dev ,
2017-01-17 04:24:55 -05:00
" %s(): Error %d reading register ALSIR_U \n " ,
__func__ , ret ) ;
2012-04-13 16:03:32 +05:30
return ret ;
}
* als_ir = ( ( msb & 0xF ) < < 8 ) | ( lsb & 0xFF ) ;
2017-01-17 04:24:51 -05:00
2012-04-13 16:03:32 +05:30
return 0 ;
}
static int isl29028_read_proxim ( struct isl29028_chip * chip , int * prox )
{
2016-03-15 11:49:12 -07:00
struct device * dev = regmap_get_device ( chip - > regmap ) ;
2012-04-13 16:03:32 +05:30
unsigned int data ;
int ret ;
2017-01-17 04:24:50 -05:00
if ( ! chip - > enable_prox ) {
2017-01-17 04:24:59 -05:00
ret = isl29028_enable_proximity ( chip ) ;
2017-01-17 04:24:50 -05:00
if ( ret < 0 )
return ret ;
2017-01-17 04:24:51 -05:00
2017-01-17 04:24:50 -05:00
chip - > enable_prox = true ;
}
2012-04-13 16:03:32 +05:30
ret = regmap_read ( chip - > regmap , ISL29028_REG_PROX_DATA , & data ) ;
if ( ret < 0 ) {
2017-01-17 04:24:55 -05:00
dev_err ( dev , " %s(): Error %d reading register PROX_DATA \n " ,
__func__ , ret ) ;
2012-04-13 16:03:32 +05:30
return ret ;
}
2017-01-17 04:24:51 -05:00
2012-04-13 16:03:32 +05:30
* prox = data ;
2017-01-17 04:24:51 -05:00
2012-04-13 16:03:32 +05:30
return 0 ;
}
static int isl29028_als_get ( struct isl29028_chip * chip , int * als_data )
{
2016-03-15 11:49:12 -07:00
struct device * dev = regmap_get_device ( chip - > regmap ) ;
2012-04-13 16:03:32 +05:30
int ret ;
int als_ir_data ;
2016-12-03 21:19:25 -05:00
ret = isl29028_set_als_ir_mode ( chip , ISL29028_MODE_ALS ) ;
if ( ret < 0 ) {
2017-01-17 04:24:55 -05:00
dev_err ( dev , " %s(): Error %d enabling ALS mode \n " , __func__ ,
ret ) ;
2016-12-03 21:19:25 -05:00
return ret ;
2012-04-13 16:03:32 +05:30
}
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 04:24:51 -05:00
2012-04-13 16:03:32 +05:30
return 0 ;
}
static int isl29028_ir_get ( struct isl29028_chip * chip , int * ir_data )
{
2016-03-15 11:49:12 -07:00
struct device * dev = regmap_get_device ( chip - > regmap ) ;
2012-04-13 16:03:32 +05:30
int ret ;
2016-12-03 21:19:25 -05:00
ret = isl29028_set_als_ir_mode ( chip , ISL29028_MODE_IR ) ;
if ( ret < 0 ) {
2017-01-17 04:24:55 -05:00
dev_err ( dev , " %s(): Error %d enabling IR mode \n " , __func__ ,
ret ) ;
2016-12-03 21:19:25 -05:00
return ret ;
2012-04-13 16:03:32 +05:30
}
2017-01-17 04:24:51 -05:00
2012-04-13 16:03:32 +05:30
return isl29028_read_als_ir ( chip , ir_data ) ;
}
2017-01-17 04:25:02 -05: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 ) {
2021-05-09 12:33:28 +01:00
ret = pm_runtime_resume_and_get ( dev ) ;
2017-01-17 04:25:02 -05:00
} else {
pm_runtime_mark_last_busy ( dev ) ;
ret = pm_runtime_put_autosuspend ( dev ) ;
}
return ret ;
}
2012-04-13 16:03:32 +05:30
/* Channel IO */
static int isl29028_write_raw ( struct iio_dev * indio_dev ,
2016-02-18 15:35:36 +08:00
struct iio_chan_spec const * chan ,
int val , int val2 , long mask )
2012-04-13 16:03:32 +05:30
{
struct isl29028_chip * chip = iio_priv ( indio_dev ) ;
2016-03-15 11:49:12 -07:00
struct device * dev = regmap_get_device ( chip - > regmap ) ;
2017-01-17 04:25:02 -05:00
int ret ;
ret = isl29028_set_pm_runtime_busy ( chip , true ) ;
if ( ret < 0 )
return ret ;
2012-04-13 16:03:32 +05:30
mutex_lock ( & chip - > lock ) ;
2017-01-17 04:25:02 -05:00
ret = - EINVAL ;
2012-04-13 16:03:32 +05:30
switch ( chan - > type ) {
case IIO_PROXIMITY :
2012-04-16 21:27:42 +05:30
if ( mask ! = IIO_CHAN_INFO_SAMP_FREQ ) {
2016-03-15 11:49:12 -07:00
dev_err ( dev ,
2017-01-17 04:24:55 -05:00
" %s(): proximity: Mask value 0x%08lx is not supported \n " ,
__func__ , mask ) ;
2012-04-13 16:03:32 +05:30
break ;
}
2017-01-17 04:24:51 -05:00
2012-04-13 16:03:32 +05:30
if ( val < 1 | | val > 100 ) {
2016-03-15 11:49:12 -07:00
dev_err ( dev ,
2017-01-17 04:24:55 -05:00
" %s(): proximity: Sampling frequency %d is not in the range [1:100] \n " ,
__func__ , val ) ;
2012-04-13 16:03:32 +05:30
break ;
}
2017-01-17 04:24:51 -05:00
2017-04-24 21:34:33 -04:00
ret = isl29028_set_proxim_sampling ( chip , val , val2 ) ;
2012-04-13 16:03:32 +05:30
break ;
case IIO_LIGHT :
2012-04-16 21:27:42 +05:30
if ( mask ! = IIO_CHAN_INFO_SCALE ) {
2016-03-15 11:49:12 -07:00
dev_err ( dev ,
2017-01-17 04:24:55 -05:00
" %s(): light: Mask value 0x%08lx is not supported \n " ,
__func__ , mask ) ;
2012-04-13 16:03:32 +05:30
break ;
}
2017-01-17 04:24:51 -05:00
2017-01-17 04:24:58 -05:00
if ( val ! = 125 & & val ! = 2000 ) {
2016-03-15 11:49:12 -07:00
dev_err ( dev ,
2017-01-17 04:24:55 -05:00
" %s(): light: Lux scale %d is not in the set {125, 2000} \n " ,
__func__ , val ) ;
2012-04-13 16:03:32 +05:30
break ;
}
2017-01-17 04:24:51 -05:00
2012-04-13 16:03:32 +05:30
ret = isl29028_set_als_scale ( chip , val ) ;
break ;
default :
2017-01-17 04:24:55 -05:00
dev_err ( dev , " %s(): Unsupported channel type %x \n " ,
__func__ , chan - > type ) ;
2012-04-13 16:03:32 +05:30
break ;
}
2017-01-17 04:24:51 -05:00
2012-04-13 16:03:32 +05:30
mutex_unlock ( & chip - > lock ) ;
2017-01-17 04:24:51 -05:00
2017-01-17 04:25:02 -05:00
if ( ret < 0 )
return ret ;
ret = isl29028_set_pm_runtime_busy ( chip , false ) ;
if ( ret < 0 )
return ret ;
2012-04-13 16:03:32 +05:30
return ret ;
}
static int isl29028_read_raw ( struct iio_dev * indio_dev ,
2016-02-18 15:35:36 +08:00
struct iio_chan_spec const * chan ,
int * val , int * val2 , long mask )
2012-04-13 16:03:32 +05:30
{
struct isl29028_chip * chip = iio_priv ( indio_dev ) ;
2016-03-15 11:49:12 -07:00
struct device * dev = regmap_get_device ( chip - > regmap ) ;
2017-01-17 04:25:02 -05:00
int ret , pm_ret ;
ret = isl29028_set_pm_runtime_busy ( chip , true ) ;
if ( ret < 0 )
return ret ;
2012-04-13 16:03:32 +05:30
mutex_lock ( & chip - > lock ) ;
2017-01-17 04:25:02 -05:00
ret = - EINVAL ;
2012-04-13 16:03:32 +05:30
switch ( mask ) {
2012-04-15 17:41:29 +01:00
case IIO_CHAN_INFO_RAW :
case IIO_CHAN_INFO_PROCESSED :
2012-04-13 16:03:32 +05:30
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 04:24:50 -05:00
ret = isl29028_read_proxim ( chip , val ) ;
2012-04-13 16:03:32 +05:30
break ;
default :
break ;
}
2017-01-17 04:24:51 -05:00
2012-04-13 16:03:32 +05:30
if ( ret < 0 )
break ;
2017-01-17 04:24:51 -05:00
2012-04-13 16:03:32 +05:30
ret = IIO_VAL_INT ;
break ;
2012-04-16 21:27:42 +05:30
case IIO_CHAN_INFO_SAMP_FREQ :
2012-04-13 16:03:32 +05:30
if ( chan - > type ! = IIO_PROXIMITY )
break ;
2017-01-17 04:24:51 -05:00
2017-04-24 21:34:33 -04:00
* val = chip - > prox_sampling_int ;
* val2 = chip - > prox_sampling_frac ;
2012-04-13 16:03:32 +05:30
ret = IIO_VAL_INT ;
break ;
2012-04-16 21:27:42 +05:30
case IIO_CHAN_INFO_SCALE :
2012-04-13 16:03:32 +05:30
if ( chan - > type ! = IIO_LIGHT )
break ;
* val = chip - > lux_scale ;
ret = IIO_VAL_INT ;
break ;
default :
2017-01-17 04:24:55 -05:00
dev_err ( dev , " %s(): mask value 0x%08lx is not supported \n " ,
__func__ , mask ) ;
2012-04-13 16:03:32 +05:30
break ;
}
2017-01-17 04:24:51 -05:00
2012-04-13 16:03:32 +05:30
mutex_unlock ( & chip - > lock ) ;
2017-01-17 04:24:51 -05:00
2017-01-17 04:25:02 -05: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 16:03:32 +05:30
return ret ;
}
static IIO_CONST_ATTR ( in_proximity_sampling_frequency_available ,
2017-04-24 21:34:33 -04:00
" 1.25 2.5 5 10 13.3 20 80 100 " ) ;
2016-07-05 13:55:43 +02:00
static IIO_CONST_ATTR ( in_illuminance_scale_available , " 125 2000 " ) ;
2012-04-13 16:03:32 +05:30
# 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 19:39:52 +00:00
. info_mask_separate = BIT ( IIO_CHAN_INFO_PROCESSED ) |
BIT ( IIO_CHAN_INFO_SCALE ) ,
2012-04-13 16:03:32 +05:30
} , {
. type = IIO_INTENSITY ,
2013-02-27 19:39:52 +00:00
. info_mask_separate = BIT ( IIO_CHAN_INFO_RAW ) ,
2012-04-13 16:03:32 +05:30
} , {
. type = IIO_PROXIMITY ,
2013-02-27 19:39:52 +00:00
. info_mask_separate = BIT ( IIO_CHAN_INFO_RAW ) |
BIT ( IIO_CHAN_INFO_SAMP_FREQ ) ,
2012-04-13 16:03:32 +05:30
}
} ;
static const struct iio_info isl29028_info = {
. attrs = & isl29108_group ,
2016-02-12 20:40:28 +05:30
. read_raw = isl29028_read_raw ,
. write_raw = isl29028_write_raw ,
2012-04-13 16:03:32 +05:30
} ;
2017-01-17 04:25:02 -05:00
static int isl29028_clear_configure_reg ( struct isl29028_chip * chip )
2012-04-13 16:03:32 +05:30
{
2016-03-15 11:49:12 -07:00
struct device * dev = regmap_get_device ( chip - > regmap ) ;
2012-04-13 16:03:32 +05:30
int ret ;
ret = regmap_write ( chip - > regmap , ISL29028_REG_CONFIGURE , 0x0 ) ;
2017-01-17 04:25:02 -05:00
if ( ret < 0 )
2017-01-17 04:24:55 -05:00
dev_err ( dev , " %s(): Error %d clearing the CONFIGURE register \n " ,
__func__ , ret ) ;
2017-01-17 04:25:02 -05:00
chip - > als_ir_mode = ISL29028_MODE_NONE ;
chip - > enable_prox = false ;
2012-04-13 16:03:32 +05:30
2017-01-17 04:25:01 -05:00
return ret ;
2012-04-13 16:03:32 +05:30
}
2016-07-05 13:55:44 +02:00
static bool isl29028_is_volatile_reg ( struct device * dev , unsigned int reg )
2012-04-13 16:03:32 +05:30
{
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 13:55:44 +02:00
. volatile_reg = isl29028_is_volatile_reg ,
2012-04-13 16:03:32 +05:30
. max_register = ISL29028_NUM_REGS - 1 ,
. num_reg_defaults_raw = ISL29028_NUM_REGS ,
. cache_type = REGCACHE_RBTREE ,
} ;
2022-11-18 23:37:40 +01:00
static int isl29028_probe ( struct i2c_client * client )
2012-04-13 16:03:32 +05:30
{
2022-11-18 23:37:40 +01:00
const struct i2c_device_id * id = i2c_client_get_device_id ( client ) ;
2012-04-13 16:03:32 +05:30
struct isl29028_chip * chip ;
struct iio_dev * indio_dev ;
int ret ;
2013-07-22 12:03:00 +01:00
indio_dev = devm_iio_device_alloc ( & client - > dev , sizeof ( * chip ) ) ;
2017-01-17 04:24:57 -05:00
if ( ! indio_dev )
2012-04-13 16:03:32 +05:30
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 04:24:55 -05:00
dev_err ( & client - > dev , " %s: Error %d initializing regmap \n " ,
__func__ , ret ) ;
2013-07-22 12:03:00 +01:00
return ret ;
2012-04-13 16:03:32 +05:30
}
2016-12-03 21:19:27 -05:00
chip - > enable_prox = false ;
2017-04-24 21:34:33 -04:00
chip - > prox_sampling_int = 20 ;
chip - > prox_sampling_frac = 0 ;
2016-12-03 21:19:27 -05: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 04:24:55 -05:00
" %s(): Error %d writing to TEST1_MODE register \n " ,
__func__ , ret ) ;
2016-12-03 21:19:27 -05:00
return ret ;
}
2017-01-17 04:24:51 -05:00
2016-12-03 21:19:27 -05:00
ret = regmap_write ( chip - > regmap , ISL29028_REG_TEST2_MODE , 0x0 ) ;
if ( ret < 0 ) {
dev_err ( & client - > dev ,
2017-01-17 04:24:55 -05:00
" %s(): Error %d writing to TEST2_MODE register \n " ,
__func__ , ret ) ;
2016-12-03 21:19:27 -05:00
return ret ;
}
2017-01-17 04:25:02 -05:00
ret = isl29028_clear_configure_reg ( chip ) ;
2017-01-17 04:24:56 -05:00
if ( ret < 0 )
2013-07-22 12:03:00 +01:00
return ret ;
2012-04-13 16:03:32 +05:30
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 04:24:51 -05:00
2017-01-17 04:25:02 -05:00
pm_runtime_enable ( & client - > dev ) ;
pm_runtime_set_autosuspend_delay ( & client - > dev ,
ISL29028_POWER_OFF_DELAY_MS ) ;
pm_runtime_use_autosuspend ( & client - > dev ) ;
2022-07-17 08:42:41 +08:00
ret = iio_device_register ( indio_dev ) ;
2012-04-13 16:03:32 +05:30
if ( ret < 0 ) {
2016-03-15 11:49:12 -07:00
dev_err ( & client - > dev ,
2017-01-17 04:24:55 -05:00
" %s(): iio registration failed with error %d \n " ,
__func__ , ret ) ;
2013-07-22 12:03:00 +01:00
return ret ;
2012-04-13 16:03:32 +05:30
}
2017-01-17 04:24:51 -05:00
2012-04-13 16:03:32 +05:30
return 0 ;
}
2022-08-15 10:02:30 +02:00
static void isl29028_remove ( struct i2c_client * client )
2017-01-17 04:25:02 -05:00
{
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 ) ;
2022-04-30 10:16:03 +02:00
isl29028_clear_configure_reg ( chip ) ;
2017-01-17 04:25:02 -05:00
}
2022-06-21 21:27:12 +01:00
static int isl29028_suspend ( struct device * dev )
2017-01-17 04:25:02 -05:00
{
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 ;
}
2022-06-21 21:27:12 +01:00
static int isl29028_resume ( struct device * dev )
2017-01-17 04:25:02 -05:00
{
/**
* 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 ;
}
2022-06-21 21:27:12 +01:00
static DEFINE_RUNTIME_DEV_PM_OPS ( isl29028_pm_ops , isl29028_suspend ,
isl29028_resume , NULL ) ;
2017-01-17 04:25:02 -05:00
2012-04-13 16:03:32 +05:30
static const struct i2c_device_id isl29028_id [ ] = {
{ " isl29028 " , 0 } ,
2017-04-28 17:55:58 +02:00
{ " isl29030 " , 0 } ,
2012-04-13 16:03:32 +05:30
{ }
} ;
MODULE_DEVICE_TABLE ( i2c , isl29028_id ) ;
static const struct of_device_id isl29028_of_match [ ] = {
2015-02-16 15:58:39 -08:00
{ . compatible = " isl,isl29028 " , } , /* for backward compat., don't use */
{ . compatible = " isil,isl29028 " , } ,
2017-04-28 17:55:58 +02:00
{ . compatible = " isil,isl29030 " , } ,
2012-04-13 16:03:32 +05:30
{ } ,
} ;
MODULE_DEVICE_TABLE ( of , isl29028_of_match ) ;
static struct i2c_driver isl29028_driver = {
. driver = {
. name = " isl29028 " ,
2022-06-21 21:27:12 +01:00
. pm = pm_ptr ( & isl29028_pm_ops ) ,
2012-04-13 16:03:32 +05:30
. of_match_table = isl29028_of_match ,
} ,
2022-11-18 23:37:40 +01:00
. probe_new = isl29028_probe ,
2017-01-17 04:25:02 -05:00
. remove = isl29028_remove ,
2012-04-13 16:03:32 +05:30
. 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> " ) ;