2019-05-29 16:57:44 -07:00
// SPDX-License-Identifier: GPL-2.0-only
2014-10-09 07:57:00 +01:00
/*
* AL3320A - Dyna Image Ambient Light Sensor
*
* Copyright ( c ) 2014 , Intel Corporation .
*
* IIO driver for AL3320A ( 7 - bit I2C slave address 0x1C ) .
*
* TODO : interrupt support , thresholds
2020-02-11 20:11:59 +01:00
* When the driver will get support for interrupt handling , then interrupt
* will need to be disabled before turning sensor OFF in order to avoid
* potential races with the interrupt handling .
2014-10-09 07:57:00 +01:00
*/
2020-02-11 20:11:57 +01:00
# include <linux/bitfield.h>
2014-10-09 07:57:00 +01:00
# include <linux/i2c.h>
2020-02-11 20:11:57 +01:00
# include <linux/module.h>
2020-02-11 20:12:01 +01:00
# include <linux/of.h>
2014-10-09 07:57:00 +01:00
# include <linux/iio/iio.h>
# include <linux/iio/sysfs.h>
# define AL3320A_DRV_NAME "al3320a"
# define AL3320A_REG_CONFIG 0x00
# define AL3320A_REG_STATUS 0x01
# define AL3320A_REG_INT 0x02
# define AL3320A_REG_WAIT 0x06
# define AL3320A_REG_CONFIG_RANGE 0x07
# define AL3320A_REG_PERSIST 0x08
# define AL3320A_REG_MEAN_TIME 0x09
# define AL3320A_REG_ADUMMY 0x0A
# define AL3320A_REG_DATA_LOW 0x22
# define AL3320A_REG_LOW_THRESH_LOW 0x30
# define AL3320A_REG_LOW_THRESH_HIGH 0x31
# define AL3320A_REG_HIGH_THRESH_LOW 0x32
# define AL3320A_REG_HIGH_THRESH_HIGH 0x33
# define AL3320A_CONFIG_DISABLE 0x00
# define AL3320A_CONFIG_ENABLE 0x01
2020-02-11 20:11:57 +01:00
# define AL3320A_GAIN_MASK GENMASK(2, 1)
2014-10-09 07:57:00 +01:00
/* chip params default values */
# define AL3320A_DEFAULT_MEAN_TIME 4
# define AL3320A_DEFAULT_WAIT_TIME 0 /* no waiting */
# define AL3320A_SCALE_AVAILABLE "0.512 0.128 0.032 0.01"
enum al3320a_range {
AL3320A_RANGE_1 , /* 33.28 Klx */
AL3320A_RANGE_2 , /* 8.32 Klx */
AL3320A_RANGE_3 , /* 2.08 Klx */
AL3320A_RANGE_4 /* 0.65 Klx */
} ;
static const int al3320a_scales [ ] [ 2 ] = {
{ 0 , 512000 } , { 0 , 128000 } , { 0 , 32000 } , { 0 , 10000 }
} ;
struct al3320a_data {
struct i2c_client * client ;
} ;
static const struct iio_chan_spec al3320a_channels [ ] = {
{
. type = IIO_LIGHT ,
. info_mask_separate = BIT ( IIO_CHAN_INFO_RAW ) |
BIT ( IIO_CHAN_INFO_SCALE ) ,
}
} ;
static IIO_CONST_ATTR ( in_illuminance_scale_available , AL3320A_SCALE_AVAILABLE ) ;
static struct attribute * al3320a_attributes [ ] = {
& iio_const_attr_in_illuminance_scale_available . dev_attr . attr ,
NULL ,
} ;
static const struct attribute_group al3320a_attribute_group = {
. attrs = al3320a_attributes ,
} ;
2020-02-11 20:11:59 +01:00
static int al3320a_set_pwr ( struct i2c_client * client , bool pwr )
{
u8 val = pwr ? AL3320A_CONFIG_ENABLE : AL3320A_CONFIG_DISABLE ;
return i2c_smbus_write_byte_data ( client , AL3320A_REG_CONFIG , val ) ;
}
2020-02-11 20:12:00 +01:00
static void al3320a_set_pwr_off ( void * _data )
{
struct al3320a_data * data = _data ;
al3320a_set_pwr ( data - > client , false ) ;
}
2014-10-09 07:57:00 +01:00
static int al3320a_init ( struct al3320a_data * data )
{
int ret ;
2020-02-11 20:11:59 +01:00
ret = al3320a_set_pwr ( data - > client , true ) ;
2014-10-09 07:57:00 +01:00
if ( ret < 0 )
return ret ;
ret = i2c_smbus_write_byte_data ( data - > client , AL3320A_REG_CONFIG_RANGE ,
2020-02-11 20:11:57 +01:00
FIELD_PREP ( AL3320A_GAIN_MASK ,
AL3320A_RANGE_3 ) ) ;
2014-10-09 07:57:00 +01:00
if ( ret < 0 )
return ret ;
ret = i2c_smbus_write_byte_data ( data - > client , AL3320A_REG_MEAN_TIME ,
AL3320A_DEFAULT_MEAN_TIME ) ;
if ( ret < 0 )
return ret ;
ret = i2c_smbus_write_byte_data ( data - > client , AL3320A_REG_WAIT ,
AL3320A_DEFAULT_WAIT_TIME ) ;
if ( ret < 0 )
return ret ;
return 0 ;
}
static int al3320a_read_raw ( struct iio_dev * indio_dev ,
struct iio_chan_spec const * chan , int * val ,
int * val2 , long mask )
{
struct al3320a_data * data = iio_priv ( indio_dev ) ;
int ret ;
switch ( mask ) {
case IIO_CHAN_INFO_RAW :
/*
* ALS ADC value is stored in two adjacent registers :
* - low byte of output is stored at AL3320A_REG_DATA_LOW
* - high byte of output is stored at AL3320A_REG_DATA_LOW + 1
*/
ret = i2c_smbus_read_word_data ( data - > client ,
AL3320A_REG_DATA_LOW ) ;
if ( ret < 0 )
return ret ;
* val = ret ;
return IIO_VAL_INT ;
case IIO_CHAN_INFO_SCALE :
ret = i2c_smbus_read_byte_data ( data - > client ,
AL3320A_REG_CONFIG_RANGE ) ;
if ( ret < 0 )
return ret ;
2020-02-11 20:11:57 +01:00
ret = FIELD_GET ( AL3320A_GAIN_MASK , ret ) ;
2014-10-09 07:57:00 +01:00
* val = al3320a_scales [ ret ] [ 0 ] ;
* val2 = al3320a_scales [ ret ] [ 1 ] ;
return IIO_VAL_INT_PLUS_MICRO ;
}
return - EINVAL ;
}
static int al3320a_write_raw ( struct iio_dev * indio_dev ,
struct iio_chan_spec const * chan , int val ,
int val2 , long mask )
{
struct al3320a_data * data = iio_priv ( indio_dev ) ;
int i ;
switch ( mask ) {
case IIO_CHAN_INFO_SCALE :
for ( i = 0 ; i < ARRAY_SIZE ( al3320a_scales ) ; i + + ) {
2020-02-11 20:11:57 +01:00
if ( val ! = al3320a_scales [ i ] [ 0 ] | |
val2 ! = al3320a_scales [ i ] [ 1 ] )
continue ;
return i2c_smbus_write_byte_data ( data - > client ,
2014-10-09 07:57:00 +01:00
AL3320A_REG_CONFIG_RANGE ,
2020-02-11 20:11:57 +01:00
FIELD_PREP ( AL3320A_GAIN_MASK , i ) ) ;
2014-10-09 07:57:00 +01:00
}
break ;
}
return - EINVAL ;
}
static const struct iio_info al3320a_info = {
. read_raw = al3320a_read_raw ,
. write_raw = al3320a_write_raw ,
. attrs = & al3320a_attribute_group ,
} ;
2022-11-18 23:37:29 +01:00
static int al3320a_probe ( struct i2c_client * client )
2014-10-09 07:57:00 +01:00
{
struct al3320a_data * data ;
struct iio_dev * indio_dev ;
int ret ;
indio_dev = devm_iio_device_alloc ( & client - > dev , sizeof ( * data ) ) ;
if ( ! indio_dev )
return - ENOMEM ;
data = iio_priv ( indio_dev ) ;
i2c_set_clientdata ( client , indio_dev ) ;
data - > client = client ;
indio_dev - > info = & al3320a_info ;
indio_dev - > name = AL3320A_DRV_NAME ;
indio_dev - > channels = al3320a_channels ;
indio_dev - > num_channels = ARRAY_SIZE ( al3320a_channels ) ;
indio_dev - > modes = INDIO_DIRECT_MODE ;
ret = al3320a_init ( data ) ;
if ( ret < 0 ) {
dev_err ( & client - > dev , " al3320a chip init failed \n " ) ;
return ret ;
}
2020-02-11 20:12:00 +01:00
ret = devm_add_action_or_reset ( & client - > dev ,
al3320a_set_pwr_off ,
data ) ;
if ( ret < 0 )
return ret ;
return devm_iio_device_register ( & client - > dev , indio_dev ) ;
2014-10-09 07:57:00 +01:00
}
2022-06-21 21:27:03 +01:00
static int al3320a_suspend ( struct device * dev )
2020-02-11 20:11:59 +01:00
{
return al3320a_set_pwr ( to_i2c_client ( dev ) , false ) ;
}
2022-06-21 21:27:03 +01:00
static int al3320a_resume ( struct device * dev )
2020-02-11 20:11:59 +01:00
{
return al3320a_set_pwr ( to_i2c_client ( dev ) , true ) ;
}
2022-06-21 21:27:03 +01:00
static DEFINE_SIMPLE_DEV_PM_OPS ( al3320a_pm_ops , al3320a_suspend ,
al3320a_resume ) ;
2020-02-11 20:11:59 +01:00
2014-10-09 07:57:00 +01:00
static const struct i2c_device_id al3320a_id [ ] = {
{ " al3320a " , 0 } ,
{ }
} ;
MODULE_DEVICE_TABLE ( i2c , al3320a_id ) ;
2020-02-11 20:12:01 +01:00
static const struct of_device_id al3320a_of_match [ ] = {
{ . compatible = " dynaimage,al3320a " , } ,
{ } ,
} ;
MODULE_DEVICE_TABLE ( of , al3320a_of_match ) ;
2014-10-09 07:57:00 +01:00
static struct i2c_driver al3320a_driver = {
. driver = {
. name = AL3320A_DRV_NAME ,
2020-02-11 20:12:01 +01:00
. of_match_table = al3320a_of_match ,
2022-06-21 21:27:03 +01:00
. pm = pm_sleep_ptr ( & al3320a_pm_ops ) ,
2014-10-09 07:57:00 +01:00
} ,
2022-11-18 23:37:29 +01:00
. probe_new = al3320a_probe ,
2014-10-09 07:57:00 +01:00
. id_table = al3320a_id ,
} ;
module_i2c_driver ( al3320a_driver ) ;
MODULE_AUTHOR ( " Daniel Baluta <daniel.baluta@intel.com> " ) ;
MODULE_DESCRIPTION ( " AL3320A Ambient Light Sensor driver " ) ;
MODULE_LICENSE ( " GPL v2 " ) ;