2014-09-16 07:54:00 +04:00
/*
* JSA1212 Ambient Light & Proximity Sensor Driver
*
* Copyright ( c ) 2014 , Intel Corporation .
*
* This program is free software ; you can redistribute it and / or modify it
* under the terms and conditions of the GNU General Public License ,
* version 2 , as published by the Free Software Foundation .
*
* This program is distributed in the hope it will be useful , but WITHOUT
* ANY WARRANTY ; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE . See the GNU General Public License for
* more details .
*
* JSA1212 I2C slave address : 0x44 ( ADDR tied to GND ) , 0x45 ( ADDR tied to VDD )
*
* TODO : Interrupt support , thresholds , range support .
*/
# include <linux/kernel.h>
# include <linux/slab.h>
# include <linux/module.h>
# include <linux/delay.h>
# include <linux/i2c.h>
# include <linux/mutex.h>
# include <linux/acpi.h>
# include <linux/regmap.h>
# include <linux/iio/iio.h>
# include <linux/iio/sysfs.h>
/* JSA1212 reg address */
# define JSA1212_CONF_REG 0x01
# define JSA1212_INT_REG 0x02
# define JSA1212_PXS_LT_REG 0x03
# define JSA1212_PXS_HT_REG 0x04
# define JSA1212_ALS_TH1_REG 0x05
# define JSA1212_ALS_TH2_REG 0x06
# define JSA1212_ALS_TH3_REG 0x07
# define JSA1212_PXS_DATA_REG 0x08
# define JSA1212_ALS_DT1_REG 0x09
# define JSA1212_ALS_DT2_REG 0x0A
# define JSA1212_ALS_RNG_REG 0x0B
# define JSA1212_MAX_REG 0x0C
/* JSA1212 reg masks */
# define JSA1212_CONF_MASK 0xFF
# define JSA1212_INT_MASK 0xFF
# define JSA1212_PXS_LT_MASK 0xFF
# define JSA1212_PXS_HT_MASK 0xFF
# define JSA1212_ALS_TH1_MASK 0xFF
# define JSA1212_ALS_TH2_LT_MASK 0x0F
# define JSA1212_ALS_TH2_HT_MASK 0xF0
# define JSA1212_ALS_TH3_MASK 0xFF
# define JSA1212_PXS_DATA_MASK 0xFF
# define JSA1212_ALS_DATA_MASK 0x0FFF
# define JSA1212_ALS_DT1_MASK 0xFF
# define JSA1212_ALS_DT2_MASK 0x0F
# define JSA1212_ALS_RNG_MASK 0x07
/* JSA1212 CONF REG bits */
# define JSA1212_CONF_PXS_MASK 0x80
# define JSA1212_CONF_PXS_ENABLE 0x80
# define JSA1212_CONF_PXS_DISABLE 0x00
# define JSA1212_CONF_ALS_MASK 0x04
# define JSA1212_CONF_ALS_ENABLE 0x04
# define JSA1212_CONF_ALS_DISABLE 0x00
# define JSA1212_CONF_IRDR_MASK 0x08
/* Proxmity sensing IRDR current sink settings */
# define JSA1212_CONF_IRDR_200MA 0x08
# define JSA1212_CONF_IRDR_100MA 0x00
# define JSA1212_CONF_PXS_SLP_MASK 0x70
# define JSA1212_CONF_PXS_SLP_0MS 0x70
# define JSA1212_CONF_PXS_SLP_12MS 0x60
# define JSA1212_CONF_PXS_SLP_50MS 0x50
# define JSA1212_CONF_PXS_SLP_75MS 0x40
# define JSA1212_CONF_PXS_SLP_100MS 0x30
# define JSA1212_CONF_PXS_SLP_200MS 0x20
# define JSA1212_CONF_PXS_SLP_400MS 0x10
# define JSA1212_CONF_PXS_SLP_800MS 0x00
/* JSA1212 INT REG bits */
# define JSA1212_INT_CTRL_MASK 0x01
# define JSA1212_INT_CTRL_EITHER 0x00
# define JSA1212_INT_CTRL_BOTH 0x01
# define JSA1212_INT_ALS_PRST_MASK 0x06
# define JSA1212_INT_ALS_PRST_1CONV 0x00
# define JSA1212_INT_ALS_PRST_4CONV 0x02
# define JSA1212_INT_ALS_PRST_8CONV 0x04
# define JSA1212_INT_ALS_PRST_16CONV 0x06
# define JSA1212_INT_ALS_FLAG_MASK 0x08
# define JSA1212_INT_ALS_FLAG_CLR 0x00
# define JSA1212_INT_PXS_PRST_MASK 0x60
# define JSA1212_INT_PXS_PRST_1CONV 0x00
# define JSA1212_INT_PXS_PRST_4CONV 0x20
# define JSA1212_INT_PXS_PRST_8CONV 0x40
# define JSA1212_INT_PXS_PRST_16CONV 0x60
# define JSA1212_INT_PXS_FLAG_MASK 0x80
# define JSA1212_INT_PXS_FLAG_CLR 0x00
/* JSA1212 ALS RNG REG bits */
# define JSA1212_ALS_RNG_0_2048 0x00
# define JSA1212_ALS_RNG_0_1024 0x01
# define JSA1212_ALS_RNG_0_512 0x02
# define JSA1212_ALS_RNG_0_256 0x03
# define JSA1212_ALS_RNG_0_128 0x04
/* JSA1212 INT threshold range */
# define JSA1212_ALS_TH_MIN 0x0000
# define JSA1212_ALS_TH_MAX 0x0FFF
# define JSA1212_PXS_TH_MIN 0x00
# define JSA1212_PXS_TH_MAX 0xFF
# define JSA1212_ALS_DELAY_MS 200
# define JSA1212_PXS_DELAY_MS 100
# define JSA1212_DRIVER_NAME "jsa1212"
# define JSA1212_REGMAP_NAME "jsa1212_regmap"
enum jsa1212_op_mode {
JSA1212_OPMODE_ALS_EN ,
JSA1212_OPMODE_PXS_EN ,
} ;
struct jsa1212_data {
struct i2c_client * client ;
struct mutex lock ;
u8 als_rng_idx ;
bool als_en ; /* ALS enable status */
bool pxs_en ; /* proximity enable status */
struct regmap * regmap ;
} ;
/* ALS range idx to val mapping */
static const int jsa1212_als_range_val [ ] = { 2048 , 1024 , 512 , 256 , 128 ,
128 , 128 , 128 } ;
/* Enables or disables ALS function based on status */
static int jsa1212_als_enable ( struct jsa1212_data * data , u8 status )
{
int ret ;
ret = regmap_update_bits ( data - > regmap , JSA1212_CONF_REG ,
JSA1212_CONF_ALS_MASK ,
status ) ;
if ( ret < 0 )
return ret ;
data - > als_en = ! ! status ;
return 0 ;
}
/* Enables or disables PXS function based on status */
static int jsa1212_pxs_enable ( struct jsa1212_data * data , u8 status )
{
int ret ;
ret = regmap_update_bits ( data - > regmap , JSA1212_CONF_REG ,
JSA1212_CONF_PXS_MASK ,
status ) ;
if ( ret < 0 )
return ret ;
data - > pxs_en = ! ! status ;
return 0 ;
}
static int jsa1212_read_als_data ( struct jsa1212_data * data ,
unsigned int * val )
{
int ret ;
__le16 als_data ;
ret = jsa1212_als_enable ( data , JSA1212_CONF_ALS_ENABLE ) ;
if ( ret < 0 )
return ret ;
/* Delay for data output */
msleep ( JSA1212_ALS_DELAY_MS ) ;
/* Read 12 bit data */
ret = regmap_bulk_read ( data - > regmap , JSA1212_ALS_DT1_REG , & als_data , 2 ) ;
if ( ret < 0 ) {
dev_err ( & data - > client - > dev , " als data read err \n " ) ;
goto als_data_read_err ;
}
* val = le16_to_cpu ( als_data ) ;
als_data_read_err :
return jsa1212_als_enable ( data , JSA1212_CONF_ALS_DISABLE ) ;
}
static int jsa1212_read_pxs_data ( struct jsa1212_data * data ,
unsigned int * val )
{
int ret ;
unsigned int pxs_data ;
ret = jsa1212_pxs_enable ( data , JSA1212_CONF_PXS_ENABLE ) ;
if ( ret < 0 )
return ret ;
/* Delay for data output */
msleep ( JSA1212_PXS_DELAY_MS ) ;
/* Read out all data */
ret = regmap_read ( data - > regmap , JSA1212_PXS_DATA_REG , & pxs_data ) ;
if ( ret < 0 ) {
dev_err ( & data - > client - > dev , " pxs data read err \n " ) ;
goto pxs_data_read_err ;
}
* val = pxs_data & JSA1212_PXS_DATA_MASK ;
pxs_data_read_err :
return jsa1212_pxs_enable ( data , JSA1212_CONF_PXS_DISABLE ) ;
}
static int jsa1212_read_raw ( struct iio_dev * indio_dev ,
struct iio_chan_spec const * chan ,
int * val , int * val2 , long mask )
{
int ret ;
struct jsa1212_data * data = iio_priv ( indio_dev ) ;
switch ( mask ) {
case IIO_CHAN_INFO_RAW :
mutex_lock ( & data - > lock ) ;
switch ( chan - > type ) {
case IIO_LIGHT :
ret = jsa1212_read_als_data ( data , val ) ;
break ;
case IIO_PROXIMITY :
ret = jsa1212_read_pxs_data ( data , val ) ;
break ;
default :
ret = - EINVAL ;
break ;
}
mutex_unlock ( & data - > lock ) ;
return ret < 0 ? ret : IIO_VAL_INT ;
case IIO_CHAN_INFO_SCALE :
switch ( chan - > type ) {
case IIO_LIGHT :
* val = jsa1212_als_range_val [ data - > als_rng_idx ] ;
* val2 = BIT ( 12 ) ; /* Max 12 bit value */
return IIO_VAL_FRACTIONAL ;
default :
break ;
}
break ;
default :
break ;
}
return - EINVAL ;
}
static const struct iio_chan_spec jsa1212_channels [ ] = {
{
. type = IIO_LIGHT ,
. info_mask_separate = BIT ( IIO_CHAN_INFO_RAW ) |
BIT ( IIO_CHAN_INFO_SCALE ) ,
} ,
{
. type = IIO_PROXIMITY ,
. info_mask_separate = BIT ( IIO_CHAN_INFO_RAW ) ,
}
} ;
static const struct iio_info jsa1212_info = {
. driver_module = THIS_MODULE ,
. read_raw = & jsa1212_read_raw ,
} ;
static int jsa1212_chip_init ( struct jsa1212_data * data )
{
int ret ;
ret = regmap_write ( data - > regmap , JSA1212_CONF_REG ,
( JSA1212_CONF_PXS_SLP_50MS |
JSA1212_CONF_IRDR_200MA ) ) ;
if ( ret < 0 )
return ret ;
ret = regmap_write ( data - > regmap , JSA1212_INT_REG ,
JSA1212_INT_ALS_PRST_4CONV ) ;
if ( ret < 0 )
return ret ;
data - > als_rng_idx = JSA1212_ALS_RNG_0_2048 ;
return 0 ;
}
static bool jsa1212_is_volatile_reg ( struct device * dev , unsigned int reg )
{
switch ( reg ) {
case JSA1212_PXS_DATA_REG :
case JSA1212_ALS_DT1_REG :
case JSA1212_ALS_DT2_REG :
case JSA1212_INT_REG :
return true ;
default :
return false ;
}
}
2015-02-24 12:41:49 +03:00
static const struct regmap_config jsa1212_regmap_config = {
2014-09-16 07:54:00 +04:00
. name = JSA1212_REGMAP_NAME ,
. reg_bits = 8 ,
. val_bits = 8 ,
. max_register = JSA1212_MAX_REG ,
. cache_type = REGCACHE_RBTREE ,
. volatile_reg = jsa1212_is_volatile_reg ,
} ;
static int jsa1212_probe ( struct i2c_client * client ,
const struct i2c_device_id * id )
{
struct jsa1212_data * data ;
struct iio_dev * indio_dev ;
struct regmap * regmap ;
int ret ;
indio_dev = devm_iio_device_alloc ( & client - > dev , sizeof ( * data ) ) ;
if ( ! indio_dev )
return - ENOMEM ;
regmap = devm_regmap_init_i2c ( client , & jsa1212_regmap_config ) ;
if ( IS_ERR ( regmap ) ) {
dev_err ( & client - > dev , " Regmap initialization failed. \n " ) ;
return PTR_ERR ( regmap ) ;
}
data = iio_priv ( indio_dev ) ;
i2c_set_clientdata ( client , indio_dev ) ;
data - > client = client ;
data - > regmap = regmap ;
mutex_init ( & data - > lock ) ;
ret = jsa1212_chip_init ( data ) ;
if ( ret < 0 )
return ret ;
indio_dev - > dev . parent = & client - > dev ;
indio_dev - > channels = jsa1212_channels ;
indio_dev - > num_channels = ARRAY_SIZE ( jsa1212_channels ) ;
indio_dev - > name = JSA1212_DRIVER_NAME ;
indio_dev - > modes = INDIO_DIRECT_MODE ;
indio_dev - > info = & jsa1212_info ;
ret = iio_device_register ( indio_dev ) ;
if ( ret < 0 )
dev_err ( & client - > dev , " %s: register device failed \n " , __func__ ) ;
return ret ;
}
/* power off the device */
static int jsa1212_power_off ( struct jsa1212_data * data )
{
int ret ;
mutex_lock ( & data - > lock ) ;
ret = regmap_update_bits ( data - > regmap , JSA1212_CONF_REG ,
JSA1212_CONF_ALS_MASK |
JSA1212_CONF_PXS_MASK ,
JSA1212_CONF_ALS_DISABLE |
JSA1212_CONF_PXS_DISABLE ) ;
if ( ret < 0 )
dev_err ( & data - > client - > dev , " power off cmd failed \n " ) ;
mutex_unlock ( & data - > lock ) ;
return ret ;
}
static int jsa1212_remove ( struct i2c_client * client )
{
struct iio_dev * indio_dev = i2c_get_clientdata ( client ) ;
struct jsa1212_data * data = iio_priv ( indio_dev ) ;
iio_device_unregister ( indio_dev ) ;
return jsa1212_power_off ( data ) ;
}
# ifdef CONFIG_PM_SLEEP
static int jsa1212_suspend ( struct device * dev )
{
struct jsa1212_data * data ;
data = iio_priv ( i2c_get_clientdata ( to_i2c_client ( dev ) ) ) ;
return jsa1212_power_off ( data ) ;
}
static int jsa1212_resume ( struct device * dev )
{
int ret = 0 ;
struct jsa1212_data * data ;
data = iio_priv ( i2c_get_clientdata ( to_i2c_client ( dev ) ) ) ;
mutex_lock ( & data - > lock ) ;
if ( data - > als_en ) {
ret = jsa1212_als_enable ( data , JSA1212_CONF_ALS_ENABLE ) ;
if ( ret < 0 ) {
dev_err ( dev , " als resume failed \n " ) ;
goto unlock_and_ret ;
}
}
if ( data - > pxs_en ) {
ret = jsa1212_pxs_enable ( data , JSA1212_CONF_PXS_ENABLE ) ;
if ( ret < 0 )
dev_err ( dev , " pxs resume failed \n " ) ;
}
unlock_and_ret :
mutex_unlock ( & data - > lock ) ;
return ret ;
}
static SIMPLE_DEV_PM_OPS ( jsa1212_pm_ops , jsa1212_suspend , jsa1212_resume ) ;
# define JSA1212_PM_OPS (&jsa1212_pm_ops)
# else
# define JSA1212_PM_OPS NULL
# endif
static const struct acpi_device_id jsa1212_acpi_match [ ] = {
{ " JSA1212 " , 0 } ,
{ } ,
} ;
MODULE_DEVICE_TABLE ( acpi , jsa1212_acpi_match ) ;
static const struct i2c_device_id jsa1212_id [ ] = {
{ JSA1212_DRIVER_NAME , 0 } ,
{ }
} ;
MODULE_DEVICE_TABLE ( i2c , jsa1212_id ) ;
static struct i2c_driver jsa1212_driver = {
. driver = {
. name = JSA1212_DRIVER_NAME ,
. pm = JSA1212_PM_OPS ,
. acpi_match_table = ACPI_PTR ( jsa1212_acpi_match ) ,
} ,
. probe = jsa1212_probe ,
. remove = jsa1212_remove ,
. id_table = jsa1212_id ,
} ;
module_i2c_driver ( jsa1212_driver ) ;
MODULE_AUTHOR ( " Sathya Kuppuswamy <sathyanarayanan.kuppuswamy@linux.intel.com> " ) ;
MODULE_DESCRIPTION ( " JSA1212 proximity/ambient light sensor driver " ) ;
MODULE_LICENSE ( " GPL v2 " ) ;