blob: 851a0fa177b6bafaabac7a0dd97ac24d39340db6 [file] [log] [blame]
/*
* Copyright (C) 2011-2014 Freescale Semiconductor, Inc. All Rights Reserved.
*/
/*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
* This program is distributed in the hope that 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.
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include <linux/module.h>
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/i2c.h>
#include <linux/input.h>
#include <linux/interrupt.h>
#include <linux/irq.h>
#include <linux/mutex.h>
#include <linux/delay.h>
#include <linux/of.h>
#include <linux/regulator/consumer.h>
#include <linux/isl29023.h>
#define ISL29023_DRV_NAME "isl29023"
#define DRIVER_VERSION "1.0"
#define ISL29023_COMMAND1 (0x00)
#define ISL29023_MODE_SHIFT (5)
#define ISL29023_MODE_MASK (0x7 << ISL29023_MODE_SHIFT)
#define ISL29023_INT_FLAG_SHIFT (2)
#define ISL29023_INT_FLAG_MASK (0x1 << ISL29023_INT_FLAG_SHIFT)
#define ISL29023_INT_PERSISTS_SHIFT (0)
#define ISL29023_INT_PERSISTS_MASK (0x3 << ISL29023_INT_PERSISTS_SHIFT)
#define ISL29023_COMMAND2 (0x01)
#define ISL29023_RES_SHIFT (2)
#define ISL29023_RES_MASK (0x3 << ISL29023_RES_SHIFT)
#define ISL29023_RANGE_SHIFT (0)
#define ISL29023_RANGE_MASK (0x3 << ISL29023_RANGE_SHIFT)
#define ISL29023_REG_LSB_SENSOR (0x02)
#define ISL29023_REG_MSB_SENSOR (0x03)
#define ISL29023_REG_IRQ_TH_LO_LSB (0x04)
#define ISL29023_REG_IRQ_TH_LO_MSB (0x05)
#define ISL29023_REG_IRQ_TH_HI_LSB (0x06)
#define ISL29023_REG_IRQ_TH_HI_MSB (0x07)
#define ISL29023_NUM_CACHABLE_REGS 8
#define DEF_RANGE 2
#define DEFAULT_REGISTOR_VAL 499
struct isl29023_data {
struct i2c_client *client;
struct mutex lock;
struct input_dev *input;
struct work_struct work;
struct workqueue_struct *workqueue;
char phys[32];
u8 reg_cache[ISL29023_NUM_CACHABLE_REGS];
u8 mode_before_suspend;
u8 mode_before_interrupt;
u16 rext;
};
static int gain_range[] = {
1000, 4000, 16000, 64000
};
/*
* register access helpers
*/
static int __isl29023_read_reg(struct i2c_client *client,
u32 reg, u8 mask, u8 shift)
{
struct isl29023_data *data = i2c_get_clientdata(client);
return (data->reg_cache[reg] & mask) >> shift;
}
static int __isl29023_write_reg(struct i2c_client *client,
u32 reg, u8 mask, u8 shift, u8 val)
{
struct isl29023_data *data = i2c_get_clientdata(client);
int ret = 0;
u8 tmp;
if (reg >= ISL29023_NUM_CACHABLE_REGS)
return -EINVAL;
mutex_lock(&data->lock);
tmp = data->reg_cache[reg];
tmp &= ~mask;
tmp |= val << shift;
ret = i2c_smbus_write_byte_data(client, reg, tmp);
if (!ret)
data->reg_cache[reg] = tmp;
mutex_unlock(&data->lock);
return ret;
}
/*
* internally used functions
*/
static int isl29023_get_int_persists(struct i2c_client *client)
{
return __isl29023_read_reg(client, ISL29023_COMMAND1,
ISL29023_INT_PERSISTS_MASK, ISL29023_INT_PERSISTS_SHIFT);
}
static int isl29023_set_int_persists(struct i2c_client *client,
int int_persists)
{
return __isl29023_write_reg(client, ISL29023_COMMAND1,
ISL29023_INT_PERSISTS_MASK, ISL29023_INT_PERSISTS_SHIFT,
int_persists);
}
/*
* interrupt flag
*/
static int isl29023_get_int_flag(struct i2c_client *client)
{
return __isl29023_read_reg(client, ISL29023_COMMAND1,
ISL29023_INT_FLAG_MASK, ISL29023_INT_FLAG_SHIFT);
}
static int isl29023_set_int_flag(struct i2c_client *client, int flag)
{
return __isl29023_write_reg(client, ISL29023_COMMAND1,
ISL29023_INT_FLAG_MASK, ISL29023_INT_FLAG_SHIFT, flag);
}
/*
* interrupt lt
*/
static int isl29023_get_int_lt(struct i2c_client *client)
{
struct isl29023_data *data = i2c_get_clientdata(client);
int lsb, msb, lt;
mutex_lock(&data->lock);
lsb = i2c_smbus_read_byte_data(client, ISL29023_REG_IRQ_TH_LO_LSB);
if (lsb < 0) {
mutex_unlock(&data->lock);
return lsb;
}
msb = i2c_smbus_read_byte_data(client, ISL29023_REG_IRQ_TH_LO_MSB);
mutex_unlock(&data->lock);
if (msb < 0)
return msb;
lt = ((msb << 8) | lsb);
return lt;
}
static int isl29023_set_int_lt(struct i2c_client *client, int lt)
{
int ret = 0;
struct isl29023_data *data = i2c_get_clientdata(client);
mutex_lock(&data->lock);
ret = i2c_smbus_write_byte_data(client, ISL29023_REG_IRQ_TH_LO_LSB,
lt & 0xff);
if (ret < 0) {
mutex_unlock(&data->lock);
return ret;
}
ret = i2c_smbus_write_byte_data(client, ISL29023_REG_IRQ_TH_LO_MSB,
(lt >> 8) & 0xff);
if (ret < 0) {
mutex_unlock(&data->lock);
return ret;
}
data->reg_cache[ISL29023_REG_IRQ_TH_LO_MSB] = (lt >> 8) & 0xff;
data->reg_cache[ISL29023_REG_IRQ_TH_LO_LSB] = lt & 0xff;
mutex_unlock(&data->lock);
return ret;
}
/*
* interrupt ht
*/
static int isl29023_get_int_ht(struct i2c_client *client)
{
struct isl29023_data *data = i2c_get_clientdata(client);
int lsb, msb, ht;
mutex_lock(&data->lock);
lsb = i2c_smbus_read_byte_data(client, ISL29023_REG_IRQ_TH_HI_LSB);
if (lsb < 0) {
mutex_unlock(&data->lock);
return lsb;
}
msb = i2c_smbus_read_byte_data(client, ISL29023_REG_IRQ_TH_HI_MSB);
mutex_unlock(&data->lock);
if (msb < 0)
return msb;
ht = ((msb << 8) | lsb);
return ht;
}
static int isl29023_set_int_ht(struct i2c_client *client, int ht)
{
int ret = 0;
struct isl29023_data *data = i2c_get_clientdata(client);
mutex_lock(&data->lock);
ret = i2c_smbus_write_byte_data(client, ISL29023_REG_IRQ_TH_HI_LSB,
ht & 0xff);
if (ret < 0) {
mutex_unlock(&data->lock);
return ret;
}
ret = i2c_smbus_write_byte_data(client, ISL29023_REG_IRQ_TH_HI_MSB,
(ht >> 8) & 0xff);
if (ret < 0) {
mutex_unlock(&data->lock);
return ret;
}
data->reg_cache[ISL29023_REG_IRQ_TH_HI_MSB] = (ht >> 8) & 0xff;
data->reg_cache[ISL29023_REG_IRQ_TH_HI_LSB] = ht & 0xff;
mutex_unlock(&data->lock);
return ret;
}
/*
* range
*/
static int isl29023_get_range(struct i2c_client *client)
{
return __isl29023_read_reg(client, ISL29023_COMMAND2,
ISL29023_RANGE_MASK, ISL29023_RANGE_SHIFT);
}
static int isl29023_set_range(struct i2c_client *client, int range)
{
return __isl29023_write_reg(client, ISL29023_COMMAND2,
ISL29023_RANGE_MASK, ISL29023_RANGE_SHIFT, range);
}
/*
* resolution
*/
static int isl29023_get_resolution(struct i2c_client *client)
{
return __isl29023_read_reg(client, ISL29023_COMMAND2,
ISL29023_RES_MASK, ISL29023_RES_SHIFT);
}
static int isl29023_set_resolution(struct i2c_client *client, int res)
{
return __isl29023_write_reg(client, ISL29023_COMMAND2,
ISL29023_RES_MASK, ISL29023_RES_SHIFT, res);
}
/*
* mode
*/
static int isl29023_get_mode(struct i2c_client *client)
{
return __isl29023_read_reg(client, ISL29023_COMMAND1,
ISL29023_MODE_MASK, ISL29023_MODE_SHIFT);
}
static int isl29023_set_mode(struct i2c_client *client, int mode)
{
return __isl29023_write_reg(client, ISL29023_COMMAND1,
ISL29023_MODE_MASK, ISL29023_MODE_SHIFT, mode);
}
/*
* power_state
*/
static int isl29023_set_power_state(struct i2c_client *client, int state)
{
return __isl29023_write_reg(client, ISL29023_COMMAND1,
ISL29023_MODE_MASK, ISL29023_MODE_SHIFT,
state ?
ISL29023_ALS_ONCE_MODE : ISL29023_PD_MODE);
}
static int isl29023_get_power_state(struct i2c_client *client)
{
struct isl29023_data *data = i2c_get_clientdata(client);
u8 cmdreg = data->reg_cache[ISL29023_COMMAND1];
if (cmdreg & ISL29023_MODE_MASK)
return 1;
else
return 0;
}
static int isl29023_get_adc_value(struct i2c_client *client)
{
struct isl29023_data *data = i2c_get_clientdata(client);
int lsb, msb, range, bitdepth;
mutex_lock(&data->lock);
lsb = i2c_smbus_read_byte_data(client, ISL29023_REG_LSB_SENSOR);
if (lsb < 0) {
mutex_unlock(&data->lock);
return lsb;
}
msb = i2c_smbus_read_byte_data(client, ISL29023_REG_MSB_SENSOR);
mutex_unlock(&data->lock);
if (msb < 0)
return msb;
range = isl29023_get_range(client);
bitdepth = (4 - isl29023_get_resolution(client)) * 4;
return (((msb << 8) | lsb) * ((gain_range[range] * 499) / data->rext))
>> bitdepth;
}
static int isl29023_get_int_lt_value(struct i2c_client *client)
{
struct isl29023_data *data = i2c_get_clientdata(client);
int lsb, msb, range, bitdepth;
mutex_lock(&data->lock);
lsb = i2c_smbus_read_byte_data(client, ISL29023_REG_IRQ_TH_LO_LSB);
if (lsb < 0) {
mutex_unlock(&data->lock);
return lsb;
}
msb = i2c_smbus_read_byte_data(client, ISL29023_REG_IRQ_TH_LO_MSB);
mutex_unlock(&data->lock);
if (msb < 0)
return msb;
range = isl29023_get_range(client);
bitdepth = (4 - isl29023_get_resolution(client)) * 4;
return (((msb << 8) | lsb) * ((gain_range[range] * 499) / data->rext))
>> bitdepth;
}
static int isl29023_get_int_ht_value(struct i2c_client *client)
{
struct isl29023_data *data = i2c_get_clientdata(client);
int lsb, msb, range, bitdepth;
mutex_lock(&data->lock);
lsb = i2c_smbus_read_byte_data(client, ISL29023_REG_IRQ_TH_HI_LSB);
if (lsb < 0) {
mutex_unlock(&data->lock);
return lsb;
}
msb = i2c_smbus_read_byte_data(client, ISL29023_REG_IRQ_TH_HI_MSB);
mutex_unlock(&data->lock);
if (msb < 0)
return msb;
range = isl29023_get_range(client);
bitdepth = (4 - isl29023_get_resolution(client)) * 4;
return (((msb << 8) | lsb) * ((gain_range[range] * 499) / data->rext))
>> bitdepth;
}
/*
* sysfs layer
*/
/*
* interrupt persists
*/
static ssize_t isl29023_show_int_persists(struct device *dev,
struct device_attribute *attr,
char *buf)
{
struct i2c_client *client = to_i2c_client(dev);
return sprintf(buf, "%i\n", isl29023_get_int_persists(client));
}
static ssize_t isl29023_store_int_persists(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
struct i2c_client *client = to_i2c_client(dev);
unsigned long val;
int ret;
if ((kstrtoul(buf, 10, &val) < 0) ||
(val > ISL29023_INT_PERSISTS_16))
return -EINVAL;
ret = isl29023_set_int_persists(client, val);
if (ret < 0)
return ret;
return count;
}
static DEVICE_ATTR(int_persists, S_IWUSR | S_IRUGO,
isl29023_show_int_persists, isl29023_store_int_persists);
/*
*interrupt flag
*/
static ssize_t isl29023_show_int_flag(struct device *dev,
struct device_attribute *attr,
char *buf)
{
struct i2c_client *client = to_i2c_client(dev);
return sprintf(buf, "%i\n", isl29023_get_int_flag(client));
}
static ssize_t isl29023_store_int_flag(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
struct i2c_client *client = to_i2c_client(dev);
unsigned long val;
int ret;
if ((kstrtoul(buf, 10, &val) < 0) || (val > 1))
return -EINVAL;
ret = isl29023_set_int_flag(client, val);
if (ret < 0)
return ret;
return count;
}
static DEVICE_ATTR(int_flag, S_IWUSR | S_IRUGO,
isl29023_show_int_flag, isl29023_store_int_flag);
/*
* interrupt lt
*/
static ssize_t isl29023_show_int_lt(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct i2c_client *client = to_i2c_client(dev);
return sprintf(buf, "%i\n", isl29023_get_int_lt(client));
}
static ssize_t isl29023_store_int_lt(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
struct i2c_client *client = to_i2c_client(dev);
unsigned long val;
int ret;
if ((kstrtoul(buf, 16, &val) < 0) || (val > 0xffff))
return -EINVAL;
ret = isl29023_set_int_lt(client, val);
if (ret < 0)
return ret;
return count;
}
static DEVICE_ATTR(int_lt, S_IWUSR | S_IRUGO,
isl29023_show_int_lt, isl29023_store_int_lt);
/*
*interrupt ht
*/
static ssize_t isl29023_show_int_ht(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct i2c_client *client = to_i2c_client(dev);
return sprintf(buf, "%i\n", isl29023_get_int_ht(client));
}
static ssize_t isl29023_store_int_ht(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
struct i2c_client *client = to_i2c_client(dev);
unsigned long val;
int ret;
if ((kstrtoul(buf, 16, &val) < 0) || (val > 0xffff))
return -EINVAL;
ret = isl29023_set_int_ht(client, val);
if (ret < 0)
return ret;
return count;
}
static DEVICE_ATTR(int_ht, S_IWUSR | S_IRUGO,
isl29023_show_int_ht, isl29023_store_int_ht);
/*
* range
*/
static ssize_t isl29023_show_range(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct i2c_client *client = to_i2c_client(dev);
return sprintf(buf, "%i\n", isl29023_get_range(client));
}
static ssize_t isl29023_store_range(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
struct i2c_client *client = to_i2c_client(dev);
unsigned long val;
int ret;
if ((kstrtoul(buf, 10, &val) < 0) || (val > ISL29023_RANGE_64K))
return -EINVAL;
ret = isl29023_set_range(client, val);
if (ret < 0)
return ret;
return count;
}
static DEVICE_ATTR(range, S_IWUSR | S_IRUGO,
isl29023_show_range, isl29023_store_range);
/*
* resolution
*/
static ssize_t isl29023_show_resolution(struct device *dev,
struct device_attribute *attr,
char *buf)
{
struct i2c_client *client = to_i2c_client(dev);
return sprintf(buf, "%d\n", isl29023_get_resolution(client));
}
static ssize_t isl29023_store_resolution(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
struct i2c_client *client = to_i2c_client(dev);
unsigned long val;
int ret;
if ((kstrtoul(buf, 10, &val) < 0) || (val > ISL29023_RES_4))
return -EINVAL;
ret = isl29023_set_resolution(client, val);
if (ret < 0)
return ret;
return count;
}
static DEVICE_ATTR(resolution, S_IWUSR | S_IRUGO,
isl29023_show_resolution, isl29023_store_resolution);
/*
*mode
*/
static ssize_t isl29023_show_mode(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct i2c_client *client = to_i2c_client(dev);
return sprintf(buf, "%d\n", isl29023_get_mode(client));
}
static ssize_t isl29023_store_mode(struct device *dev,
struct device_attribute *attr, const char *buf, size_t count)
{
struct i2c_client *client = to_i2c_client(dev);
unsigned long val;
int ret;
if ((kstrtoul(buf, 10, &val) < 0) ||
(val > ISL29023_IR_CONT_MODE))
return -EINVAL;
/* clear the interrupt flag */
i2c_smbus_read_byte_data(client, ISL29023_COMMAND1);
ret = isl29023_set_mode(client, val);
if (ret < 0)
return ret;
return count;
}
static DEVICE_ATTR(mode, S_IWUSR | S_IRUGO,
isl29023_show_mode, isl29023_store_mode);
/*
*power state
*/
static ssize_t isl29023_show_power_state(struct device *dev,
struct device_attribute *attr,
char *buf)
{
struct i2c_client *client = to_i2c_client(dev);
return sprintf(buf, "%d\n", isl29023_get_power_state(client));
}
static ssize_t isl29023_store_power_state(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
struct i2c_client *client = to_i2c_client(dev);
unsigned long val;
int ret;
if ((kstrtoul(buf, 10, &val) < 0) || (val > 1))
return -EINVAL;
ret = isl29023_set_power_state(client, val);
return ret ? ret : count;
}
static DEVICE_ATTR(power_state, S_IWUSR | S_IRUGO,
isl29023_show_power_state, isl29023_store_power_state);
/*
* lux
*/
static ssize_t isl29023_show_lux(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct i2c_client *client = to_i2c_client(dev);
/* No LUX data if not operational */
if (!isl29023_get_power_state(client))
return -EBUSY;
return sprintf(buf, "%d\n", isl29023_get_adc_value(client));
}
static DEVICE_ATTR(lux, S_IRUGO, isl29023_show_lux, NULL);
/*
* lux interrupt low threshold
*/
static ssize_t isl29023_show_int_lt_lux(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct i2c_client *client = to_i2c_client(dev);
/* No LUX data if not operational */
if (isl29023_get_mode(client) != ISL29023_ALS_ONCE_MODE &&
isl29023_get_mode(client) != ISL29023_ALS_CONT_MODE)
return -EIO;
return sprintf(buf, "%d\n", isl29023_get_int_lt_value(client));
}
static ssize_t isl29023_store_int_lt_lux(struct device *dev,
struct device_attribute *attr, const char *buf, size_t count)
{
struct i2c_client *client = to_i2c_client(dev);
struct isl29023_data *data = i2c_get_clientdata(client);
unsigned long val, lux_data;
int range, bitdepth, ret;
u8 lsb, msb;
if ((kstrtoul(buf, 10, &val) < 0))
return -EINVAL;
/* No LUX data if not operational */
if (isl29023_get_mode(client) != ISL29023_ALS_ONCE_MODE &&
isl29023_get_mode(client) != ISL29023_ALS_CONT_MODE)
return -EIO;
if (val > (gain_range[isl29023_get_range(client)]*499/data->rext))
return -EINVAL;
range = isl29023_get_range(client);
bitdepth = (4 - isl29023_get_resolution(client)) * 4;
lux_data = ((unsigned long)(val << bitdepth)) /
((gain_range[range] * 499) / data->rext);
lux_data &= 0xffff;
msb = lux_data >> 8;
lsb = lux_data & 0xff;
mutex_lock(&data->lock);
ret = i2c_smbus_write_byte_data(client, ISL29023_REG_IRQ_TH_LO_LSB,
lsb);
if (ret < 0) {
mutex_unlock(&data->lock);
return ret;
}
ret = i2c_smbus_write_byte_data(client, ISL29023_REG_IRQ_TH_LO_MSB,
msb);
if (ret < 0) {
mutex_unlock(&data->lock);
return ret;
}
data->reg_cache[ISL29023_REG_IRQ_TH_LO_MSB] = msb;
data->reg_cache[ISL29023_REG_IRQ_TH_LO_LSB] = lsb;
mutex_unlock(&data->lock);
return count;
}
static DEVICE_ATTR(int_lt_lux, S_IWUSR | S_IRUGO,
isl29023_show_int_lt_lux, isl29023_store_int_lt_lux);
/*
* lux interrupt high threshold
*/
static ssize_t isl29023_show_int_ht_lux(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct i2c_client *client = to_i2c_client(dev);
/* No LUX data if not operational */
if (isl29023_get_mode(client) != ISL29023_ALS_ONCE_MODE &&
isl29023_get_mode(client) != ISL29023_ALS_CONT_MODE)
return -EIO;
return sprintf(buf, "%d\n", isl29023_get_int_ht_value(client));
}
static ssize_t isl29023_store_int_ht_lux(struct device *dev,
struct device_attribute *attr, const char *buf, size_t count)
{
struct i2c_client *client = to_i2c_client(dev);
struct isl29023_data *data = i2c_get_clientdata(client);
unsigned long val, lux_data;
int range, bitdepth, ret;
u8 lsb, msb;
if ((kstrtoul(buf, 10, &val) < 0))
return -EINVAL;
/* No LUX data if not operational */
if (isl29023_get_mode(client) != ISL29023_ALS_ONCE_MODE &&
isl29023_get_mode(client) != ISL29023_ALS_CONT_MODE)
return -EIO;
if (val > (gain_range[isl29023_get_range(client)]*499/data->rext))
return -EINVAL;
range = isl29023_get_range(client);
bitdepth = (4 - isl29023_get_resolution(client)) * 4;
lux_data = ((unsigned long)(val << bitdepth)) /
((gain_range[range] * 499) / data->rext);
lux_data &= 0xffff;
msb = lux_data >> 8;
lsb = lux_data & 0xff;
mutex_lock(&data->lock);
ret = i2c_smbus_write_byte_data(client, ISL29023_REG_IRQ_TH_HI_LSB,
lsb);
if (ret < 0) {
mutex_unlock(&data->lock);
return ret;
}
ret = i2c_smbus_write_byte_data(client, ISL29023_REG_IRQ_TH_HI_MSB,
msb);
if (ret < 0) {
mutex_unlock(&data->lock);
return ret;
}
data->reg_cache[ISL29023_REG_IRQ_TH_HI_MSB] = msb;
data->reg_cache[ISL29023_REG_IRQ_TH_HI_LSB] = lsb;
mutex_unlock(&data->lock);
return count;
}
static DEVICE_ATTR(int_ht_lux, S_IWUSR | S_IRUGO,
isl29023_show_int_ht_lux, isl29023_store_int_ht_lux);
static struct attribute *isl29023_attributes[] = {
&dev_attr_int_persists.attr,
&dev_attr_range.attr,
&dev_attr_resolution.attr,
&dev_attr_mode.attr,
&dev_attr_power_state.attr,
&dev_attr_lux.attr,
&dev_attr_int_lt_lux.attr,
&dev_attr_int_ht_lux.attr,
&dev_attr_int_lt.attr,
&dev_attr_int_ht.attr,
&dev_attr_int_flag.attr,
NULL
};
static const struct attribute_group isl29023_attr_group = {
.attrs = isl29023_attributes,
};
static int isl29023_init_client(struct i2c_client *client)
{
struct isl29023_data *data = i2c_get_clientdata(client);
int i;
/* read all the registers once to fill the cache.
* if one of the reads fails, we consider the init failed */
for (i = 0; i < ARRAY_SIZE(data->reg_cache); i++) {
int v = i2c_smbus_read_byte_data(client, i);
if (v < 0)
return -ENODEV;
data->reg_cache[i] = v;
}
/* set defaults */
isl29023_set_int_persists(client, ISL29023_INT_PERSISTS_8);
isl29023_set_int_ht(client, 0xffff);
isl29023_set_int_lt(client, 0x0);
isl29023_set_range(client, ISL29023_RANGE_16K);
isl29023_set_resolution(client, ISL29023_RES_16);
isl29023_set_mode(client, ISL29023_ALS_ONCE_MODE);
isl29023_set_int_flag(client, 0);
isl29023_set_power_state(client, 0);
return 0;
}
static void isl29023_work(struct work_struct *work)
{
struct isl29023_data *data =
container_of(work, struct isl29023_data, work);
struct i2c_client *client = data->client;
int lux;
/* Clear interrupt flag */
isl29023_set_int_flag(client, 0);
data->mode_before_interrupt = isl29023_get_mode(client);
lux = isl29023_get_adc_value(client);
/* To clear the interrpt status */
isl29023_set_power_state(client, ISL29023_PD_MODE);
isl29023_set_mode(client, data->mode_before_interrupt);
msleep(100);
input_report_abs(data->input, ABS_MISC, lux);
input_sync(data->input);
}
static irqreturn_t isl29023_irq_handler(int irq, void *handle)
{
struct isl29023_data *data = handle;
int cmd_1;
cmd_1 = i2c_smbus_read_byte_data(data->client, ISL29023_COMMAND1);
if (!(cmd_1 & ISL29023_INT_FLAG_MASK))
return IRQ_NONE;
queue_work(data->workqueue, &data->work);
return IRQ_HANDLED;
}
/*
* I2C layer
*/
static int isl29023_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent);
struct isl29023_data *data;
struct input_dev *input_dev;
int err = 0;
struct regulator *vdd = NULL;
u32 rext = 0;
struct device_node *of_node = client->dev.of_node;
struct irq_data *irq_data = irq_get_irq_data(client->irq);
u32 irq_flag;
bool shared_irq;
vdd = devm_regulator_get(&client->dev, "vdd");
if (!IS_ERR(vdd)) {
err = regulator_enable(vdd);
if (err) {
dev_err(&client->dev, "vdd set voltage error\n");
return err;
}
}
err = of_property_read_u32(of_node, "rext", &rext);
if (err || rext == 0)
rext = DEFAULT_REGISTOR_VAL;
shared_irq = of_property_read_bool(of_node, "shared-interrupt");
if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE))
return -EIO;
data = kzalloc(sizeof(struct isl29023_data), GFP_KERNEL);
if (!data)
return -ENOMEM;
data->client = client;
data->rext = (u16)rext;
snprintf(data->phys, sizeof(data->phys),
"%s", dev_name(&client->dev));
i2c_set_clientdata(client, data);
mutex_init(&data->lock);
/* initialize the ISL29023 chip */
err = isl29023_init_client(client);
if (err)
goto exit_kfree;
/* register sysfs hooks */
err = sysfs_create_group(&client->dev.kobj, &isl29023_attr_group);
if (err)
goto exit_kfree;
input_dev = input_allocate_device();
if (!input_dev) {
err = -ENOMEM;
goto exit_kfree;
}
data->input = input_dev;
input_dev->name = "isl29023 light sensor";
input_dev->id.bustype = BUS_I2C;
input_dev->phys = data->phys;
__set_bit(EV_ABS, input_dev->evbit);
input_set_abs_params(input_dev, ABS_MISC, 0,
gain_range[DEF_RANGE]*499/data->rext, 0, 0);
err = input_register_device(input_dev);
if (err)
goto exit_free_input;
irq_flag = irqd_get_trigger_type(irq_data);
irq_flag |= IRQF_ONESHOT;
if (shared_irq)
irq_flag |= IRQF_SHARED;
err = request_threaded_irq(client->irq, NULL,
isl29023_irq_handler, irq_flag,
client->dev.driver->name, data);
if (err < 0) {
dev_err(&client->dev, "failed to register irq %d!\n",
client->irq);
goto exit_free_input;
}
data->workqueue = create_singlethread_workqueue("isl29023");
INIT_WORK(&data->work, isl29023_work);
if (data->workqueue == NULL) {
dev_err(&client->dev, "couldn't create workqueue\n");
err = -ENOMEM;
goto exit_free_interrupt;
}
dev_info(&client->dev, "driver version %s enabled\n", DRIVER_VERSION);
return 0;
exit_free_interrupt:
free_irq(client->irq, data);
exit_free_input:
input_free_device(input_dev);
exit_kfree:
kfree(data);
return err;
}
static int isl29023_remove(struct i2c_client *client)
{
struct isl29023_data *data = i2c_get_clientdata(client);
cancel_work_sync(&data->work);
destroy_workqueue(data->workqueue);
free_irq(client->irq, data);
input_unregister_device(data->input);
input_free_device(data->input);
sysfs_remove_group(&client->dev.kobj, &isl29023_attr_group);
isl29023_set_power_state(client, 0);
kfree(i2c_get_clientdata(client));
return 0;
}
#ifdef CONFIG_PM_SLEEP
static int isl29023_suspend(struct device *dev)
{
struct i2c_client *client = to_i2c_client(dev);
struct isl29023_data *data = i2c_get_clientdata(client);
data->mode_before_suspend = isl29023_get_mode(client);
return isl29023_set_power_state(client, ISL29023_PD_MODE);
}
static int isl29023_resume(struct device *dev)
{
int i;
struct i2c_client *client = to_i2c_client(dev);
struct isl29023_data *data = i2c_get_clientdata(client);
/* restore registers from cache */
for (i = 0; i < ARRAY_SIZE(data->reg_cache); i++)
if (i2c_smbus_write_byte_data(client, i, data->reg_cache[i]))
return -EIO;
return isl29023_set_mode(client, data->mode_before_suspend);
}
#endif
static SIMPLE_DEV_PM_OPS(isl29023_pm_ops, isl29023_suspend, isl29023_resume);
static const struct i2c_device_id isl29023_id[] = {
{ ISL29023_DRV_NAME, 0 },
{}
};
MODULE_DEVICE_TABLE(i2c, isl29023_id);
static struct i2c_driver isl29023_driver = {
.driver = {
.name = ISL29023_DRV_NAME,
.owner = THIS_MODULE,
.pm = &isl29023_pm_ops,
},
.probe = isl29023_probe,
.remove = isl29023_remove,
.id_table = isl29023_id,
};
static int __init isl29023_init(void)
{
return i2c_add_driver(&isl29023_driver);
}
static void __exit isl29023_exit(void)
{
i2c_del_driver(&isl29023_driver);
}
MODULE_AUTHOR("Freescale Semiconductor, Inc.");
MODULE_DESCRIPTION("ISL29023 ambient light sensor driver");
MODULE_LICENSE("GPL");
MODULE_VERSION(DRIVER_VERSION);
module_init(isl29023_init);
module_exit(isl29023_exit);