2019-05-27 09:55:06 +03:00
// SPDX-License-Identifier: GPL-2.0-or-later
2010-10-08 02:48:09 +04:00
/*
2014-08-29 18:26:00 +04:00
* A iio driver for the light sensor ISL 29018 / 29023 / 29035.
2010-10-08 02:48:09 +04:00
*
* IIO driver for monitoring ambient light intensity in luxi , proximity
* sensing and infrared sensing .
*
* Copyright ( c ) 2010 , NVIDIA Corporation .
*/
# include <linux/module.h>
# include <linux/i2c.h>
# include <linux/err.h>
# include <linux/mutex.h>
# include <linux/delay.h>
2012-04-20 11:27:38 +04:00
# include <linux/regmap.h>
2019-01-08 12:09:39 +03:00
# include <linux/regulator/consumer.h>
2010-10-08 02:48:09 +04:00
# include <linux/slab.h>
2012-04-25 18:54:58 +04:00
# include <linux/iio/iio.h>
# include <linux/iio/sysfs.h>
2014-01-09 14:20:00 +04:00
# include <linux/acpi.h>
2012-04-20 11:27:38 +04:00
2016-07-05 14:55:36 +03:00
# define ISL29018_CONV_TIME_MS 100
2010-10-08 02:48:09 +04:00
# define ISL29018_REG_ADD_COMMAND1 0x00
2016-07-05 14:55:36 +03:00
# define ISL29018_CMD1_OPMODE_SHIFT 5
# define ISL29018_CMD1_OPMODE_MASK (7 << ISL29018_CMD1_OPMODE_SHIFT)
# define ISL29018_CMD1_OPMODE_POWER_DOWN 0
# define ISL29018_CMD1_OPMODE_ALS_ONCE 1
# define ISL29018_CMD1_OPMODE_IR_ONCE 2
# define ISL29018_CMD1_OPMODE_PROX_ONCE 3
2010-10-08 02:48:09 +04:00
2016-07-05 14:55:36 +03:00
# define ISL29018_REG_ADD_COMMAND2 0x01
# define ISL29018_CMD2_RESOLUTION_SHIFT 2
# define ISL29018_CMD2_RESOLUTION_MASK (0x3 << ISL29018_CMD2_RESOLUTION_SHIFT)
2010-10-08 02:48:09 +04:00
2016-07-05 14:55:36 +03:00
# define ISL29018_CMD2_RANGE_SHIFT 0
# define ISL29018_CMD2_RANGE_MASK (0x3 << ISL29018_CMD2_RANGE_SHIFT)
2010-10-08 02:48:09 +04:00
2016-07-05 14:55:36 +03:00
# define ISL29018_CMD2_SCHEME_SHIFT 7
# define ISL29018_CMD2_SCHEME_MASK (0x1 << ISL29018_CMD2_SCHEME_SHIFT)
2010-10-08 02:48:09 +04:00
# define ISL29018_REG_ADD_DATA_LSB 0x02
# define ISL29018_REG_ADD_DATA_MSB 0x03
2011-08-10 02:18:14 +04:00
# define ISL29018_REG_TEST 0x08
# define ISL29018_TEST_SHIFT 0
# define ISL29018_TEST_MASK (0xFF << ISL29018_TEST_SHIFT)
2014-08-29 18:26:00 +04:00
# define ISL29035_REG_DEVICE_ID 0x0F
# define ISL29035_DEVICE_ID_SHIFT 0x03
# define ISL29035_DEVICE_ID_MASK (0x7 << ISL29035_DEVICE_ID_SHIFT)
# define ISL29035_DEVICE_ID 0x5
# define ISL29035_BOUT_SHIFT 0x07
# define ISL29035_BOUT_MASK (0x01 << ISL29035_BOUT_SHIFT)
2015-04-16 22:20:59 +03:00
enum isl29018_int_time {
ISL29018_INT_TIME_16 ,
ISL29018_INT_TIME_12 ,
ISL29018_INT_TIME_8 ,
ISL29018_INT_TIME_4 ,
} ;
static const unsigned int isl29018_int_utimes [ 3 ] [ 4 ] = {
{ 90000 , 5630 , 351 , 21 } ,
{ 90000 , 5600 , 352 , 22 } ,
{ 105000 , 6500 , 410 , 25 } ,
} ;
static const struct isl29018_scale {
unsigned int scale ;
unsigned int uscale ;
} isl29018_scales [ 4 ] [ 4 ] = {
{ { 0 , 15258 } , { 0 , 61035 } , { 0 , 244140 } , { 0 , 976562 } } ,
{ { 0 , 244140 } , { 0 , 976562 } , { 3 , 906250 } , { 15 , 625000 } } ,
{ { 3 , 906250 } , { 15 , 625000 } , { 62 , 500000 } , { 250 , 0 } } ,
{ { 62 , 500000 } , { 250 , 0 } , { 1000 , 0 } , { 4000 , 0 } }
} ;
2010-10-08 02:48:09 +04:00
struct isl29018_chip {
2012-04-20 11:27:38 +04:00
struct regmap * regmap ;
2010-10-08 02:48:09 +04:00
struct mutex lock ;
2014-08-29 18:26:00 +04:00
int type ;
2015-04-16 22:20:58 +03:00
unsigned int calibscale ;
unsigned int ucalibscale ;
2015-04-16 22:20:59 +03:00
unsigned int int_time ;
struct isl29018_scale scale ;
2010-10-08 02:48:09 +04:00
int prox_scheme ;
2012-10-25 03:39:00 +04:00
bool suspended ;
2019-01-08 12:09:39 +03:00
struct regulator * vcc_reg ;
2010-10-08 02:48:09 +04:00
} ;
2015-04-16 22:20:59 +03:00
static int isl29018_set_integration_time ( struct isl29018_chip * chip ,
unsigned int utime )
2010-10-08 02:48:09 +04:00
{
2016-09-25 14:41:07 +03:00
unsigned int i ;
int ret ;
2015-04-16 22:20:59 +03:00
unsigned int int_time , new_int_time ;
for ( i = 0 ; i < ARRAY_SIZE ( isl29018_int_utimes [ chip - > type ] ) ; + + i ) {
if ( utime = = isl29018_int_utimes [ chip - > type ] [ i ] ) {
new_int_time = i ;
2010-10-08 02:48:09 +04:00
break ;
}
}
2015-04-16 22:20:59 +03:00
if ( i > = ARRAY_SIZE ( isl29018_int_utimes [ chip - > type ] ) )
2010-10-08 02:48:09 +04:00
return - EINVAL ;
2016-07-05 14:55:36 +03:00
ret = regmap_update_bits ( chip - > regmap , ISL29018_REG_ADD_COMMAND2 ,
ISL29018_CMD2_RESOLUTION_MASK ,
i < < ISL29018_CMD2_RESOLUTION_SHIFT ) ;
2015-04-16 22:20:59 +03:00
if ( ret < 0 )
return ret ;
2016-07-05 14:55:39 +03:00
/* Keep the same range when integration time changes */
2015-04-16 22:20:59 +03:00
int_time = chip - > int_time ;
for ( i = 0 ; i < ARRAY_SIZE ( isl29018_scales [ int_time ] ) ; + + i ) {
if ( chip - > scale . scale = = isl29018_scales [ int_time ] [ i ] . scale & &
chip - > scale . uscale = = isl29018_scales [ int_time ] [ i ] . uscale ) {
chip - > scale = isl29018_scales [ new_int_time ] [ i ] ;
break ;
}
}
chip - > int_time = new_int_time ;
return 0 ;
2010-10-08 02:48:09 +04:00
}
2015-04-16 22:20:59 +03:00
static int isl29018_set_scale ( struct isl29018_chip * chip , int scale , int uscale )
2010-10-08 02:48:09 +04:00
{
2016-09-25 14:41:07 +03:00
unsigned int i ;
int ret ;
2015-04-16 22:20:59 +03:00
struct isl29018_scale new_scale ;
2010-10-08 02:48:09 +04:00
2015-04-16 22:20:59 +03:00
for ( i = 0 ; i < ARRAY_SIZE ( isl29018_scales [ chip - > int_time ] ) ; + + i ) {
if ( scale = = isl29018_scales [ chip - > int_time ] [ i ] . scale & &
uscale = = isl29018_scales [ chip - > int_time ] [ i ] . uscale ) {
new_scale = isl29018_scales [ chip - > int_time ] [ i ] ;
2010-10-08 02:48:09 +04:00
break ;
}
}
2015-04-16 22:20:59 +03:00
if ( i > = ARRAY_SIZE ( isl29018_scales [ chip - > int_time ] ) )
2010-10-08 02:48:09 +04:00
return - EINVAL ;
2016-07-05 14:55:36 +03:00
ret = regmap_update_bits ( chip - > regmap , ISL29018_REG_ADD_COMMAND2 ,
ISL29018_CMD2_RANGE_MASK ,
i < < ISL29018_CMD2_RANGE_SHIFT ) ;
2015-04-16 22:20:59 +03:00
if ( ret < 0 )
return ret ;
chip - > scale = new_scale ;
return 0 ;
2010-10-08 02:48:09 +04:00
}
2012-04-20 11:27:38 +04:00
static int isl29018_read_sensor_input ( struct isl29018_chip * chip , int mode )
2010-10-08 02:48:09 +04:00
{
int status ;
2012-04-20 11:27:38 +04:00
unsigned int lsb ;
unsigned int msb ;
2016-02-22 23:34:32 +03:00
struct device * dev = regmap_get_device ( chip - > regmap ) ;
2010-10-08 02:48:09 +04:00
/* Set mode */
2012-04-20 11:27:38 +04:00
status = regmap_write ( chip - > regmap , ISL29018_REG_ADD_COMMAND1 ,
2016-07-05 14:55:36 +03:00
mode < < ISL29018_CMD1_OPMODE_SHIFT ) ;
2010-10-08 02:48:09 +04:00
if ( status ) {
2016-02-22 23:34:32 +03:00
dev_err ( dev ,
2012-04-20 11:27:38 +04:00
" Error in setting operating mode err %d \n " , status ) ;
2010-10-08 02:48:09 +04:00
return status ;
}
2016-07-05 14:55:36 +03:00
msleep ( ISL29018_CONV_TIME_MS ) ;
2012-04-20 11:27:38 +04:00
status = regmap_read ( chip - > regmap , ISL29018_REG_ADD_DATA_LSB , & lsb ) ;
if ( status < 0 ) {
2016-02-22 23:34:32 +03:00
dev_err ( dev ,
2012-04-20 11:27:38 +04:00
" Error in reading LSB DATA with err %d \n " , status ) ;
return status ;
2010-10-08 02:48:09 +04:00
}
2012-04-20 11:27:38 +04:00
status = regmap_read ( chip - > regmap , ISL29018_REG_ADD_DATA_MSB , & msb ) ;
if ( status < 0 ) {
2016-02-22 23:34:32 +03:00
dev_err ( dev ,
2012-04-20 11:27:38 +04:00
" Error in reading MSB DATA with error %d \n " , status ) ;
return status ;
2010-10-08 02:48:09 +04:00
}
2016-02-22 23:34:32 +03:00
dev_vdbg ( dev , " MSB 0x%x and LSB 0x%x \n " , msb , lsb ) ;
2010-10-08 02:48:09 +04:00
return ( msb < < 8 ) | lsb ;
}
2012-04-20 11:27:38 +04:00
static int isl29018_read_lux ( struct isl29018_chip * chip , int * lux )
2010-10-08 02:48:09 +04:00
{
int lux_data ;
2015-04-16 22:20:59 +03:00
unsigned int data_x_range ;
2010-10-08 02:48:09 +04:00
2016-07-05 14:55:36 +03:00
lux_data = isl29018_read_sensor_input ( chip ,
ISL29018_CMD1_OPMODE_ALS_ONCE ) ;
2010-10-08 02:48:09 +04:00
if ( lux_data < 0 )
return lux_data ;
2015-04-16 22:20:59 +03:00
data_x_range = lux_data * chip - > scale . scale +
lux_data * chip - > scale . uscale / 1000000 ;
* lux = data_x_range * chip - > calibscale +
data_x_range * chip - > ucalibscale / 1000000 ;
2010-10-08 02:48:09 +04:00
return 0 ;
}
2012-04-20 11:27:38 +04:00
static int isl29018_read_ir ( struct isl29018_chip * chip , int * ir )
2010-10-08 02:48:09 +04:00
{
int ir_data ;
2016-07-05 14:55:36 +03:00
ir_data = isl29018_read_sensor_input ( chip ,
ISL29018_CMD1_OPMODE_IR_ONCE ) ;
2010-10-08 02:48:09 +04:00
if ( ir_data < 0 )
return ir_data ;
* ir = ir_data ;
return 0 ;
}
2012-04-20 11:27:38 +04:00
static int isl29018_read_proximity_ir ( struct isl29018_chip * chip , int scheme ,
2016-02-18 10:35:36 +03:00
int * near_ir )
2010-10-08 02:48:09 +04:00
{
int status ;
int prox_data = - 1 ;
int ir_data = - 1 ;
2016-02-22 23:34:32 +03:00
struct device * dev = regmap_get_device ( chip - > regmap ) ;
2010-10-08 02:48:09 +04:00
/* Do proximity sensing with required scheme */
2016-07-05 14:55:36 +03:00
status = regmap_update_bits ( chip - > regmap , ISL29018_REG_ADD_COMMAND2 ,
ISL29018_CMD2_SCHEME_MASK ,
scheme < < ISL29018_CMD2_SCHEME_SHIFT ) ;
2010-10-08 02:48:09 +04:00
if ( status ) {
2016-02-22 23:34:32 +03:00
dev_err ( dev , " Error in setting operating mode \n " ) ;
2010-10-08 02:48:09 +04:00
return status ;
}
2012-04-20 11:27:38 +04:00
prox_data = isl29018_read_sensor_input ( chip ,
2016-07-05 14:55:36 +03:00
ISL29018_CMD1_OPMODE_PROX_ONCE ) ;
2010-10-08 02:48:09 +04:00
if ( prox_data < 0 )
return prox_data ;
if ( scheme = = 1 ) {
* near_ir = prox_data ;
return 0 ;
}
2016-07-05 14:55:36 +03:00
ir_data = isl29018_read_sensor_input ( chip ,
ISL29018_CMD1_OPMODE_IR_ONCE ) ;
2010-10-08 02:48:09 +04:00
if ( ir_data < 0 )
return ir_data ;
if ( prox_data > = ir_data )
* near_ir = prox_data - ir_data ;
else
* near_ir = 0 ;
return 0 ;
}
2016-09-27 03:20:17 +03:00
static ssize_t in_illuminance_scale_available_show
( struct device * dev , struct device_attribute * attr ,
char * buf )
2015-04-16 22:20:59 +03:00
{
struct iio_dev * indio_dev = dev_to_iio_dev ( dev ) ;
struct isl29018_chip * chip = iio_priv ( indio_dev ) ;
2016-09-25 14:41:07 +03:00
unsigned int i ;
int len = 0 ;
2015-04-16 22:20:59 +03:00
2016-09-27 03:20:18 +03:00
mutex_lock ( & chip - > lock ) ;
2015-04-16 22:20:59 +03:00
for ( i = 0 ; i < ARRAY_SIZE ( isl29018_scales [ chip - > int_time ] ) ; + + i )
len + = sprintf ( buf + len , " %d.%06d " ,
isl29018_scales [ chip - > int_time ] [ i ] . scale ,
isl29018_scales [ chip - > int_time ] [ i ] . uscale ) ;
2016-09-27 03:20:18 +03:00
mutex_unlock ( & chip - > lock ) ;
2015-04-16 22:20:59 +03:00
buf [ len - 1 ] = ' \n ' ;
return len ;
}
2016-09-27 03:20:17 +03:00
static ssize_t in_illuminance_integration_time_available_show
( struct device * dev , struct device_attribute * attr ,
char * buf )
2015-04-16 22:20:59 +03:00
{
struct iio_dev * indio_dev = dev_to_iio_dev ( dev ) ;
struct isl29018_chip * chip = iio_priv ( indio_dev ) ;
2016-09-25 14:41:07 +03:00
unsigned int i ;
int len = 0 ;
2015-04-16 22:20:59 +03:00
for ( i = 0 ; i < ARRAY_SIZE ( isl29018_int_utimes [ chip - > type ] ) ; + + i )
len + = sprintf ( buf + len , " 0.%06d " ,
isl29018_int_utimes [ chip - > type ] [ i ] ) ;
buf [ len - 1 ] = ' \n ' ;
return len ;
}
2016-10-07 03:48:34 +03:00
/*
* From ISL29018 Data Sheet ( FN6619 .4 , Oct 8 , 2012 ) regarding the
* infrared suppression :
*
* Proximity Sensing Scheme : Bit 7. This bit programs the function
* of the proximity detection . Logic 0 of this bit , Scheme 0 , makes
* full n ( 4 , 8 , 12 , 16 ) bits ( unsigned ) proximity detection . The range
* of Scheme 0 proximity count is from 0 to 2 ^ n . Logic 1 of this bit ,
* Scheme 1 , makes n - 1 ( 3 , 7 , 11 , 15 ) bits ( 2 ' s complementary )
* proximity_less_ambient detection . The range of Scheme 1
* proximity count is from - 2 ^ ( n - 1 ) to 2 ^ ( n - 1 ) . The sign bit is extended
* for resolutions less than 16. While Scheme 0 has wider dynamic
* range , Scheme 1 proximity detection is less affected by the
* ambient IR noise variation .
*
* 0 Sensing IR from LED and ambient
* 1 Sensing IR from LED with ambient IR rejection
*/
2016-09-27 03:20:17 +03:00
static ssize_t proximity_on_chip_ambient_infrared_suppression_show
( struct device * dev , struct device_attribute * attr ,
char * buf )
2010-10-08 02:48:09 +04:00
{
2012-05-12 17:39:51 +04:00
struct iio_dev * indio_dev = dev_to_iio_dev ( dev ) ;
2011-06-27 16:07:57 +04:00
struct isl29018_chip * chip = iio_priv ( indio_dev ) ;
2010-10-08 02:48:09 +04:00
2016-02-18 10:35:38 +03:00
/*
2016-07-05 14:55:39 +03:00
* Return the " proximity scheme " i . e . if the chip does on chip
2016-02-18 10:35:38 +03:00
* infrared suppression ( 1 means perform on chip suppression )
*/
2010-10-08 02:48:09 +04:00
return sprintf ( buf , " %d \n " , chip - > prox_scheme ) ;
}
2016-09-27 03:20:17 +03:00
static ssize_t proximity_on_chip_ambient_infrared_suppression_store
( struct device * dev , struct device_attribute * attr ,
const char * buf , size_t count )
2010-10-08 02:48:09 +04:00
{
2012-05-12 17:39:51 +04:00
struct iio_dev * indio_dev = dev_to_iio_dev ( dev ) ;
2011-06-27 16:07:57 +04:00
struct isl29018_chip * chip = iio_priv ( indio_dev ) ;
staging: iio: replace strict_strto*() with kstrto*()
The usage of strict_strto*() is not preferred, because
strict_strto*() is obsolete. Thus, kstrto*() should be
used.
Previously, there were only strict_strtol(), strict_strtoul(),
strict_strtoull(), and strict_strtoll(). Thus, when converting
to the variables, only long, unsigned long, unsigned long long,
and long long can be used.
However, kstrto*() provides various functions handling all types
of variables. Therefore, the types of variables can be changed
properly.
Signed-off-by: Jingoo Han <jg1.han@samsung.com>
Signed-off-by: Jonathan Cameron <jic23@kernel.org>
2013-08-20 06:31:00 +04:00
int val ;
2010-10-08 02:48:09 +04:00
staging: iio: replace strict_strto*() with kstrto*()
The usage of strict_strto*() is not preferred, because
strict_strto*() is obsolete. Thus, kstrto*() should be
used.
Previously, there were only strict_strtol(), strict_strtoul(),
strict_strtoull(), and strict_strtoll(). Thus, when converting
to the variables, only long, unsigned long, unsigned long long,
and long long can be used.
However, kstrto*() provides various functions handling all types
of variables. Therefore, the types of variables can be changed
properly.
Signed-off-by: Jingoo Han <jg1.han@samsung.com>
Signed-off-by: Jonathan Cameron <jic23@kernel.org>
2013-08-20 06:31:00 +04:00
if ( kstrtoint ( buf , 10 , & val ) )
2010-10-08 02:48:09 +04:00
return - EINVAL ;
2016-07-05 14:55:37 +03:00
if ( ! ( val = = 0 | | val = = 1 ) )
2010-10-08 02:48:09 +04:00
return - EINVAL ;
2016-02-18 10:35:38 +03:00
/*
2016-07-05 14:55:39 +03:00
* Get the " proximity scheme " i . e . if the chip does on chip
2016-02-18 10:35:38 +03:00
* infrared suppression ( 1 means perform on chip suppression )
*/
2010-10-08 02:48:09 +04:00
mutex_lock ( & chip - > lock ) ;
staging: iio: replace strict_strto*() with kstrto*()
The usage of strict_strto*() is not preferred, because
strict_strto*() is obsolete. Thus, kstrto*() should be
used.
Previously, there were only strict_strtol(), strict_strtoul(),
strict_strtoull(), and strict_strtoll(). Thus, when converting
to the variables, only long, unsigned long, unsigned long long,
and long long can be used.
However, kstrto*() provides various functions handling all types
of variables. Therefore, the types of variables can be changed
properly.
Signed-off-by: Jingoo Han <jg1.han@samsung.com>
Signed-off-by: Jonathan Cameron <jic23@kernel.org>
2013-08-20 06:31:00 +04:00
chip - > prox_scheme = val ;
2010-10-08 02:48:09 +04:00
mutex_unlock ( & chip - > lock ) ;
return count ;
}
2011-07-07 23:01:56 +04:00
static int isl29018_write_raw ( struct iio_dev * indio_dev ,
struct iio_chan_spec const * chan ,
int val ,
int val2 ,
long mask )
2010-10-08 02:48:09 +04:00
{
2011-07-07 23:01:56 +04:00
struct isl29018_chip * chip = iio_priv ( indio_dev ) ;
int ret = - EINVAL ;
2010-10-08 02:48:09 +04:00
2011-07-07 23:01:56 +04:00
mutex_lock ( & chip - > lock ) ;
2016-09-27 03:20:20 +03:00
if ( chip - > suspended ) {
ret = - EBUSY ;
goto write_done ;
}
2015-04-16 22:20:59 +03:00
switch ( mask ) {
case IIO_CHAN_INFO_CALIBSCALE :
if ( chan - > type = = IIO_LIGHT ) {
chip - > calibscale = val ;
chip - > ucalibscale = val2 ;
ret = 0 ;
}
break ;
case IIO_CHAN_INFO_INT_TIME :
2016-09-25 14:41:06 +03:00
if ( chan - > type = = IIO_LIGHT & & ! val )
2015-04-16 22:20:59 +03:00
ret = isl29018_set_integration_time ( chip , val2 ) ;
break ;
case IIO_CHAN_INFO_SCALE :
if ( chan - > type = = IIO_LIGHT )
ret = isl29018_set_scale ( chip , val , val2 ) ;
break ;
default :
break ;
2011-07-07 23:01:56 +04:00
}
2016-09-27 03:20:20 +03:00
write_done :
mutex_unlock ( & chip - > lock ) ;
2016-10-10 10:19:55 +03:00
2012-11-08 13:05:00 +04:00
return ret ;
2010-10-08 02:48:09 +04:00
}
2011-07-07 23:01:56 +04:00
static int isl29018_read_raw ( struct iio_dev * indio_dev ,
struct iio_chan_spec const * chan ,
int * val ,
int * val2 ,
long mask )
2010-10-08 02:48:09 +04:00
{
2011-07-07 23:01:56 +04:00
int ret = - EINVAL ;
struct isl29018_chip * chip = iio_priv ( indio_dev ) ;
mutex_lock ( & chip - > lock ) ;
2012-10-25 03:39:00 +04:00
if ( chip - > suspended ) {
2016-09-27 03:20:19 +03:00
ret = - EBUSY ;
goto read_done ;
2012-10-25 03:39:00 +04:00
}
2011-07-07 23:01:56 +04:00
switch ( mask ) {
2012-04-15 20:41:22 +04:00
case IIO_CHAN_INFO_RAW :
case IIO_CHAN_INFO_PROCESSED :
2011-07-07 23:01:56 +04:00
switch ( chan - > type ) {
case IIO_LIGHT :
2012-04-20 11:27:38 +04:00
ret = isl29018_read_lux ( chip , val ) ;
2011-07-07 23:01:56 +04:00
break ;
case IIO_INTENSITY :
2012-04-20 11:27:38 +04:00
ret = isl29018_read_ir ( chip , val ) ;
2011-07-07 23:01:56 +04:00
break ;
case IIO_PROXIMITY :
2012-04-20 11:27:38 +04:00
ret = isl29018_read_proximity_ir ( chip ,
2016-02-18 10:35:36 +03:00
chip - > prox_scheme ,
val ) ;
2011-07-07 23:01:56 +04:00
break ;
default :
break ;
}
if ( ! ret )
ret = IIO_VAL_INT ;
break ;
2015-04-16 22:20:59 +03:00
case IIO_CHAN_INFO_INT_TIME :
if ( chan - > type = = IIO_LIGHT ) {
* val = 0 ;
* val2 = isl29018_int_utimes [ chip - > type ] [ chip - > int_time ] ;
ret = IIO_VAL_INT_PLUS_MICRO ;
}
break ;
case IIO_CHAN_INFO_SCALE :
if ( chan - > type = = IIO_LIGHT ) {
* val = chip - > scale . scale ;
* val2 = chip - > scale . uscale ;
ret = IIO_VAL_INT_PLUS_MICRO ;
}
break ;
2011-10-26 20:41:36 +04:00
case IIO_CHAN_INFO_CALIBSCALE :
2011-07-07 23:01:56 +04:00
if ( chan - > type = = IIO_LIGHT ) {
2015-04-16 22:20:58 +03:00
* val = chip - > calibscale ;
* val2 = chip - > ucalibscale ;
2012-09-05 23:55:00 +04:00
ret = IIO_VAL_INT_PLUS_MICRO ;
2011-07-07 23:01:56 +04:00
}
break ;
default :
break ;
}
2016-09-27 03:20:19 +03:00
read_done :
2011-07-07 23:01:56 +04:00
mutex_unlock ( & chip - > lock ) ;
2016-10-10 10:19:55 +03:00
2011-07-07 23:01:56 +04:00
return ret ;
2010-10-08 02:48:09 +04:00
}
2014-08-29 18:26:00 +04:00
# define ISL29018_LIGHT_CHANNEL { \
. type = IIO_LIGHT , \
. indexed = 1 , \
. channel = 0 , \
. info_mask_separate = BIT ( IIO_CHAN_INFO_PROCESSED ) | \
2015-04-16 22:20:59 +03:00
BIT ( IIO_CHAN_INFO_CALIBSCALE ) | \
BIT ( IIO_CHAN_INFO_SCALE ) | \
BIT ( IIO_CHAN_INFO_INT_TIME ) , \
2014-08-29 18:26:00 +04:00
}
# define ISL29018_IR_CHANNEL { \
. type = IIO_INTENSITY , \
. modified = 1 , \
. info_mask_separate = BIT ( IIO_CHAN_INFO_RAW ) , \
. channel2 = IIO_MOD_LIGHT_IR , \
}
# define ISL29018_PROXIMITY_CHANNEL { \
. type = IIO_PROXIMITY , \
. info_mask_separate = BIT ( IIO_CHAN_INFO_RAW ) , \
}
2011-07-07 23:01:56 +04:00
static const struct iio_chan_spec isl29018_channels [ ] = {
2014-08-29 18:26:00 +04:00
ISL29018_LIGHT_CHANNEL ,
ISL29018_IR_CHANNEL ,
ISL29018_PROXIMITY_CHANNEL ,
} ;
static const struct iio_chan_spec isl29023_channels [ ] = {
ISL29018_LIGHT_CHANNEL ,
ISL29018_IR_CHANNEL ,
2011-07-07 23:01:56 +04:00
} ;
2016-09-27 03:20:17 +03:00
static IIO_DEVICE_ATTR_RO ( in_illuminance_integration_time_available , 0 ) ;
static IIO_DEVICE_ATTR_RO ( in_illuminance_scale_available , 0 ) ;
static IIO_DEVICE_ATTR_RW ( proximity_on_chip_ambient_infrared_suppression , 0 ) ;
2010-10-08 02:48:09 +04:00
# define ISL29018_DEV_ATTR(name) (&iio_dev_attr_##name.dev_attr.attr)
2015-04-16 22:20:59 +03:00
2010-10-08 02:48:09 +04:00
static struct attribute * isl29018_attributes [ ] = {
2015-04-16 22:20:59 +03:00
ISL29018_DEV_ATTR ( in_illuminance_scale_available ) ,
ISL29018_DEV_ATTR ( in_illuminance_integration_time_available ) ,
2012-06-18 22:33:07 +04:00
ISL29018_DEV_ATTR ( proximity_on_chip_ambient_infrared_suppression ) ,
2010-10-08 02:48:09 +04:00
NULL
} ;
2014-08-29 18:26:00 +04:00
static struct attribute * isl29023_attributes [ ] = {
2015-04-16 22:20:59 +03:00
ISL29018_DEV_ATTR ( in_illuminance_scale_available ) ,
ISL29018_DEV_ATTR ( in_illuminance_integration_time_available ) ,
2014-08-29 18:26:00 +04:00
NULL
} ;
2014-08-29 18:26:00 +04:00
static const struct attribute_group isl29018_group = {
2010-10-08 02:48:09 +04:00
. attrs = isl29018_attributes ,
} ;
2014-08-29 18:26:00 +04:00
static const struct attribute_group isl29023_group = {
. attrs = isl29023_attributes ,
} ;
enum {
isl29018 ,
isl29023 ,
isl29035 ,
} ;
2012-04-20 11:27:38 +04:00
static int isl29018_chip_init ( struct isl29018_chip * chip )
2010-10-08 02:48:09 +04:00
{
int status ;
2016-02-22 23:34:32 +03:00
struct device * dev = regmap_get_device ( chip - > regmap ) ;
2010-10-08 02:48:09 +04:00
2014-08-29 18:26:00 +04:00
if ( chip - > type = = isl29035 ) {
2016-10-10 10:19:56 +03:00
unsigned int id ;
status = regmap_read ( chip - > regmap , ISL29035_REG_DEVICE_ID , & id ) ;
if ( status < 0 ) {
dev_err ( dev ,
" Error reading ID register with error %d \n " ,
status ) ;
return status ;
}
id = ( id & ISL29035_DEVICE_ID_MASK ) > > ISL29035_DEVICE_ID_SHIFT ;
if ( id ! = ISL29035_DEVICE_ID )
return - ENODEV ;
/* Clear brownout bit */
status = regmap_update_bits ( chip - > regmap ,
ISL29035_REG_DEVICE_ID ,
ISL29035_BOUT_MASK , 0 ) ;
2014-08-29 18:26:00 +04:00
if ( status < 0 )
return status ;
}
2016-10-10 10:19:57 +03:00
/*
* Code added per Intersil Application Note 1534 :
2011-08-10 02:18:14 +04:00
* When VDD sinks to approximately 1.8 V or below , some of
* the part ' s registers may change their state . When VDD
* recovers to 2.25 V ( or greater ) , the part may thus be in an
* unknown mode of operation . The user can return the part to
* a known mode of operation either by ( a ) setting VDD = 0 V for
* 1 second or more and then powering back up with a slew rate
* of 0.5 V / ms or greater , or ( b ) via I2C disable all ALS / PROX
* conversions , clear the test registers , and then rewrite all
* registers to the desired values .
* . . .
2016-07-05 14:55:39 +03:00
* For ISL29011 , ISL29018 , ISL29021 , ISL29023
2011-08-10 02:18:14 +04:00
* 1. Write 0x00 to register 0x08 ( TEST )
* 2. Write 0x00 to register 0x00 ( CMD1 )
* 3. Rewrite all registers to the desired values
*
* ISL29018 Data Sheet ( FN6619 .1 , Feb 11 , 2010 ) essentially says
* the same thing EXCEPT the data sheet asks for a 1 ms delay after
* writing the CMD1 register .
*/
2012-04-20 11:27:38 +04:00
status = regmap_write ( chip - > regmap , ISL29018_REG_TEST , 0x0 ) ;
2011-08-10 02:18:14 +04:00
if ( status < 0 ) {
2016-02-22 23:34:32 +03:00
dev_err ( dev , " Failed to clear isl29018 TEST reg.(%d) \n " ,
2014-09-25 21:09:09 +04:00
status ) ;
2011-08-10 02:18:14 +04:00
return status ;
}
2016-10-10 10:19:57 +03:00
/*
* See Intersil AN1534 comments above .
2011-08-10 02:18:14 +04:00
* " Operating Mode " ( COMMAND1 ) register is reprogrammed when
* data is read from the device .
*/
2012-04-20 11:27:38 +04:00
status = regmap_write ( chip - > regmap , ISL29018_REG_ADD_COMMAND1 , 0 ) ;
2011-08-10 02:18:14 +04:00
if ( status < 0 ) {
2016-02-22 23:34:32 +03:00
dev_err ( dev , " Failed to clear isl29018 CMD1 reg.(%d) \n " ,
2014-09-25 21:09:09 +04:00
status ) ;
2011-08-10 02:18:14 +04:00
return status ;
}
2014-09-23 07:52:30 +04:00
usleep_range ( 1000 , 2000 ) ; /* per data sheet, page 10 */
2011-08-10 02:18:14 +04:00
2016-07-05 14:55:39 +03:00
/* Set defaults */
2015-04-16 22:20:59 +03:00
status = isl29018_set_scale ( chip , chip - > scale . scale ,
chip - > scale . uscale ) ;
2010-10-08 02:48:09 +04:00
if ( status < 0 ) {
2016-02-22 23:34:32 +03:00
dev_err ( dev , " Init of isl29018 fails \n " ) ;
2010-10-08 02:48:09 +04:00
return status ;
}
2015-04-16 22:20:59 +03:00
status = isl29018_set_integration_time ( chip ,
isl29018_int_utimes [ chip - > type ] [ chip - > int_time ] ) ;
2016-10-10 10:19:58 +03:00
if ( status < 0 )
2016-02-22 23:34:32 +03:00
dev_err ( dev , " Init of isl29018 fails \n " ) ;
2010-10-08 02:48:09 +04:00
2016-10-10 10:19:58 +03:00
return status ;
2010-10-08 02:48:09 +04:00
}
2014-08-29 18:26:00 +04:00
static const struct iio_info isl29018_info = {
. attrs = & isl29018_group ,
2016-02-12 18:10:27 +03:00
. read_raw = isl29018_read_raw ,
. write_raw = isl29018_write_raw ,
2011-05-18 17:42:37 +04:00
} ;
2014-08-29 18:26:00 +04:00
static const struct iio_info isl29023_info = {
. attrs = & isl29023_group ,
2016-02-12 18:10:27 +03:00
. read_raw = isl29018_read_raw ,
. write_raw = isl29018_write_raw ,
2014-08-29 18:26:00 +04:00
} ;
2016-07-05 14:55:40 +03:00
static bool isl29018_is_volatile_reg ( struct device * dev , unsigned int reg )
2012-04-20 11:27:38 +04:00
{
switch ( reg ) {
case ISL29018_REG_ADD_DATA_LSB :
case ISL29018_REG_ADD_DATA_MSB :
case ISL29018_REG_ADD_COMMAND1 :
case ISL29018_REG_TEST :
2014-08-29 18:26:00 +04:00
case ISL29035_REG_DEVICE_ID :
2012-04-20 11:27:38 +04:00
return true ;
default :
return false ;
}
}
static const struct regmap_config isl29018_regmap_config = {
. reg_bits = 8 ,
. val_bits = 8 ,
2016-07-05 14:55:40 +03:00
. volatile_reg = isl29018_is_volatile_reg ,
2012-04-20 11:27:38 +04:00
. max_register = ISL29018_REG_TEST ,
. num_reg_defaults_raw = ISL29018_REG_TEST + 1 ,
. cache_type = REGCACHE_RBTREE ,
} ;
2014-08-29 18:26:00 +04:00
static const struct regmap_config isl29035_regmap_config = {
. reg_bits = 8 ,
. val_bits = 8 ,
2016-07-05 14:55:40 +03:00
. volatile_reg = isl29018_is_volatile_reg ,
2014-08-29 18:26:00 +04:00
. max_register = ISL29035_REG_DEVICE_ID ,
. num_reg_defaults_raw = ISL29035_REG_DEVICE_ID + 1 ,
. cache_type = REGCACHE_RBTREE ,
} ;
2016-07-05 14:55:40 +03:00
struct isl29018_chip_info {
2014-08-29 18:26:00 +04:00
const struct iio_chan_spec * channels ;
int num_channels ;
const struct iio_info * indio_info ;
const struct regmap_config * regmap_cfg ;
} ;
2016-07-05 14:55:40 +03:00
static const struct isl29018_chip_info isl29018_chip_info_tbl [ ] = {
2014-08-29 18:26:00 +04:00
[ isl29018 ] = {
. channels = isl29018_channels ,
. num_channels = ARRAY_SIZE ( isl29018_channels ) ,
. indio_info = & isl29018_info ,
. regmap_cfg = & isl29018_regmap_config ,
} ,
[ isl29023 ] = {
. channels = isl29023_channels ,
. num_channels = ARRAY_SIZE ( isl29023_channels ) ,
. indio_info = & isl29023_info ,
. regmap_cfg = & isl29018_regmap_config ,
} ,
[ isl29035 ] = {
. channels = isl29023_channels ,
. num_channels = ARRAY_SIZE ( isl29023_channels ) ,
. indio_info = & isl29023_info ,
. regmap_cfg = & isl29035_regmap_config ,
} ,
} ;
2014-01-09 14:20:00 +04:00
static const char * isl29018_match_acpi_device ( struct device * dev , int * data )
{
const struct acpi_device_id * id ;
id = acpi_match_device ( dev - > driver - > acpi_match_table , dev ) ;
if ( ! id )
return NULL ;
2016-02-18 10:35:37 +03:00
* data = ( int ) id - > driver_data ;
2014-01-09 14:20:00 +04:00
return dev_name ( dev ) ;
}
2019-01-08 12:09:39 +03:00
static void isl29018_disable_regulator_action ( void * _data )
{
struct isl29018_chip * chip = _data ;
int err ;
err = regulator_disable ( chip - > vcc_reg ) ;
if ( err )
pr_err ( " failed to disable isl29018's VCC regulator! \n " ) ;
}
2022-11-19 01:37:39 +03:00
static int isl29018_probe ( struct i2c_client * client )
2010-10-08 02:48:09 +04:00
{
2022-11-19 01:37:39 +03:00
const struct i2c_device_id * id = i2c_client_get_device_id ( client ) ;
2010-10-08 02:48:09 +04:00
struct isl29018_chip * chip ;
2011-06-27 16:07:57 +04:00
struct iio_dev * indio_dev ;
2010-10-08 02:48:09 +04:00
int err ;
2014-01-09 14:20:00 +04:00
const char * name = NULL ;
int dev_id = 0 ;
2010-10-08 02:48:09 +04:00
2013-07-22 15:03:00 +04:00
indio_dev = devm_iio_device_alloc ( & client - > dev , sizeof ( * chip ) ) ;
2016-07-05 14:55:37 +03:00
if ( ! indio_dev )
2013-07-22 15:03:00 +04:00
return - ENOMEM ;
2016-10-10 10:19:55 +03:00
2011-06-27 16:07:57 +04:00
chip = iio_priv ( indio_dev ) ;
2010-10-08 02:48:09 +04:00
2011-06-27 16:07:57 +04:00
i2c_set_clientdata ( client , indio_dev ) ;
2010-10-08 02:48:09 +04:00
2014-01-09 14:20:00 +04:00
if ( id ) {
name = id - > name ;
dev_id = id - > driver_data ;
}
if ( ACPI_HANDLE ( & client - > dev ) )
name = isl29018_match_acpi_device ( & client - > dev , & dev_id ) ;
2010-10-08 02:48:09 +04:00
mutex_init ( & chip - > lock ) ;
2014-01-09 14:20:00 +04:00
chip - > type = dev_id ;
2015-04-16 22:20:58 +03:00
chip - > calibscale = 1 ;
chip - > ucalibscale = 0 ;
2015-04-16 22:20:59 +03:00
chip - > int_time = ISL29018_INT_TIME_16 ;
chip - > scale = isl29018_scales [ chip - > int_time ] [ 0 ] ;
2012-10-25 03:39:00 +04:00
chip - > suspended = false ;
2010-10-08 02:48:09 +04:00
2019-01-08 12:09:39 +03:00
chip - > vcc_reg = devm_regulator_get ( & client - > dev , " vcc " ) ;
2020-08-29 09:47:22 +03:00
if ( IS_ERR ( chip - > vcc_reg ) )
return dev_err_probe ( & client - > dev , PTR_ERR ( chip - > vcc_reg ) ,
" failed to get VCC regulator! \n " ) ;
2019-01-08 12:09:39 +03:00
err = regulator_enable ( chip - > vcc_reg ) ;
if ( err ) {
dev_err ( & client - > dev , " failed to enable VCC regulator! \n " ) ;
return err ;
}
err = devm_add_action_or_reset ( & client - > dev , isl29018_disable_regulator_action ,
chip ) ;
if ( err ) {
dev_err ( & client - > dev , " failed to setup regulator cleanup action! \n " ) ;
return err ;
}
2014-08-29 18:26:00 +04:00
chip - > regmap = devm_regmap_init_i2c ( client ,
2016-07-05 14:55:40 +03:00
isl29018_chip_info_tbl [ dev_id ] . regmap_cfg ) ;
2012-04-20 11:27:38 +04:00
if ( IS_ERR ( chip - > regmap ) ) {
err = PTR_ERR ( chip - > regmap ) ;
2016-02-22 23:34:32 +03:00
dev_err ( & client - > dev , " regmap initialization fails: %d \n " , err ) ;
2013-07-22 15:03:00 +04:00
return err ;
2012-04-20 11:27:38 +04:00
}
err = isl29018_chip_init ( chip ) ;
2010-10-08 02:48:09 +04:00
if ( err )
2013-07-22 15:03:00 +04:00
return err ;
2010-10-08 02:48:09 +04:00
2016-07-05 14:55:40 +03:00
indio_dev - > info = isl29018_chip_info_tbl [ dev_id ] . indio_info ;
indio_dev - > channels = isl29018_chip_info_tbl [ dev_id ] . channels ;
indio_dev - > num_channels = isl29018_chip_info_tbl [ dev_id ] . num_channels ;
2014-01-09 14:20:00 +04:00
indio_dev - > name = name ;
2011-06-27 16:07:57 +04:00
indio_dev - > modes = INDIO_DIRECT_MODE ;
2016-10-10 10:19:55 +03:00
2016-07-05 14:55:37 +03:00
return devm_iio_device_register ( & client - > dev , indio_dev ) ;
2010-10-08 02:48:09 +04:00
}
2012-10-25 03:39:00 +04:00
static int isl29018_suspend ( struct device * dev )
{
struct isl29018_chip * chip = iio_priv ( dev_get_drvdata ( dev ) ) ;
2019-01-08 12:09:39 +03:00
int ret ;
2012-10-25 03:39:00 +04:00
mutex_lock ( & chip - > lock ) ;
2016-10-10 10:19:57 +03:00
/*
* Since this driver uses only polling commands , we are by default in
2012-10-25 03:39:00 +04:00
* auto shutdown ( ie , power - down ) mode .
* So we do not have much to do here .
*/
chip - > suspended = true ;
2019-01-08 12:09:39 +03:00
ret = regulator_disable ( chip - > vcc_reg ) ;
if ( ret )
dev_err ( dev , " failed to disable VCC regulator \n " ) ;
2012-10-25 03:39:00 +04:00
mutex_unlock ( & chip - > lock ) ;
2016-10-10 10:19:55 +03:00
2019-01-08 12:09:39 +03:00
return ret ;
2012-10-25 03:39:00 +04:00
}
static int isl29018_resume ( struct device * dev )
{
struct isl29018_chip * chip = iio_priv ( dev_get_drvdata ( dev ) ) ;
int err ;
mutex_lock ( & chip - > lock ) ;
2019-01-08 12:09:39 +03:00
err = regulator_enable ( chip - > vcc_reg ) ;
if ( err ) {
dev_err ( dev , " failed to enable VCC regulator \n " ) ;
mutex_unlock ( & chip - > lock ) ;
return err ;
}
2012-10-25 03:39:00 +04:00
err = isl29018_chip_init ( chip ) ;
if ( ! err )
chip - > suspended = false ;
mutex_unlock ( & chip - > lock ) ;
2016-10-10 10:19:55 +03:00
2012-10-25 03:39:00 +04:00
return err ;
}
2022-01-30 22:31:17 +03:00
static DEFINE_SIMPLE_DEV_PM_OPS ( isl29018_pm_ops , isl29018_suspend ,
isl29018_resume ) ;
2012-10-25 03:39:00 +04:00
2017-05-20 00:28:53 +03:00
# ifdef CONFIG_ACPI
2014-01-09 14:20:00 +04:00
static const struct acpi_device_id isl29018_acpi_match [ ] = {
{ " ISL29018 " , isl29018 } ,
{ " ISL29023 " , isl29023 } ,
{ " ISL29035 " , isl29035 } ,
{ } ,
} ;
MODULE_DEVICE_TABLE ( acpi , isl29018_acpi_match ) ;
2017-05-20 00:28:53 +03:00
# endif
2014-01-09 14:20:00 +04:00
2010-10-08 02:48:09 +04:00
static const struct i2c_device_id isl29018_id [ ] = {
2014-08-29 18:26:00 +04:00
{ " isl29018 " , isl29018 } ,
{ " isl29023 " , isl29023 } ,
{ " isl29035 " , isl29035 } ,
2010-10-08 02:48:09 +04:00
{ }
} ;
MODULE_DEVICE_TABLE ( i2c , isl29018_id ) ;
2011-12-23 06:44:43 +04:00
static const struct of_device_id isl29018_of_match [ ] = {
2012-04-24 10:11:38 +04:00
{ . compatible = " isil,isl29018 " , } ,
2014-08-29 18:26:00 +04:00
{ . compatible = " isil,isl29023 " , } ,
{ . compatible = " isil,isl29035 " , } ,
2011-12-23 06:44:43 +04:00
{ } ,
} ;
MODULE_DEVICE_TABLE ( of , isl29018_of_match ) ;
2010-10-08 02:48:09 +04:00
static struct i2c_driver isl29018_driver = {
. driver = {
. name = " isl29018 " ,
2014-01-09 14:20:00 +04:00
. acpi_match_table = ACPI_PTR ( isl29018_acpi_match ) ,
2022-01-30 22:31:17 +03:00
. pm = pm_sleep_ptr ( & isl29018_pm_ops ) ,
2011-12-23 06:44:43 +04:00
. of_match_table = isl29018_of_match ,
2010-10-08 02:48:09 +04:00
} ,
2023-05-15 23:50:48 +03:00
. probe = isl29018_probe ,
2010-10-08 02:48:09 +04:00
. id_table = isl29018_id ,
} ;
2011-11-16 13:13:38 +04:00
module_i2c_driver ( isl29018_driver ) ;
2010-10-08 02:48:09 +04:00
MODULE_DESCRIPTION ( " ISL29018 Ambient Light Sensor driver " ) ;
MODULE_LICENSE ( " GPL " ) ;