blob: fa3109b5c68da0d8b8783ffa3d24f386b24d1dd4 [file] [log] [blame]
/*
* si1143.c - Linux kernel modules for ambient light sensor and proximity
*
* Copyright (C) 2010 Nest Labs, Inc
*
* 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., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include <linux/module.h>
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/i2c.h>
#include <linux/mutex.h>
#include <linux/gpio.h>
#include <linux/interrupt.h>
#include <linux/delay.h>
#include <linux/workqueue.h>
#include <linux/si1143.h>
#define SI1143_DRV_NAME "si1143"
#define DRIVER_VERSION "1.0"
/*
* Defines
*/
#define SI1143_PART_ID 0x00
#define SI1143_PART_ID_VAL 0x43
#define SI1143_REV_ID 0x01
#define SI1143_SEQ_ID 0x02
#define SI1143_INT_CFG 0x03
#define SI1143_IRQ_ENABLE 0x04
#define SI1143_IRQ_MODE1 0x05
#define SI1143_IRQ_MODE2 0x06
#define SI1143_HW_KEY 0x07
#define SI1143_MEAS_RATE 0x08
#define SI1143_ALS_RATE 0x09
#define SI1143_PS_RATE 0x0A
#define SI1143_ALS_LOW_TH 0x0B
#define SI1143_ALS_HIGH_TH 0x0D
#define SI1143_PS_LED21 0x0F
#define SI1143_PS_LED3 0x10
#define SI1143_PS1_TH 0x11
#define SI1143_PARAM_WR 0x17
#define SI1143_COMMAND 0x18
#define SI1143_RESPONSE 0x20
#define SI1143_IRQ_STATUS 0x21
#define SI1143_PS1_DATA0 0x26
#define SI1143_PS1_DATA1 0x27
#define SI1143_ALS_VIS_DATA0 0x22
#define SI1143_ALS_VIS_DATA1 0x23
#define SI1143_ALS_IR_DATA0 0x24
#define SI1143_ALS_IR_DATA1 0x25
#define SI1143_PARAM_RD 0x2E
//Prox/ALS commands
#define SI1143_PSALS_FORCE 0x07
#define SI1143_PS_FORCE 0x05
#define SI1143_ALS_FORCE 0x06
#define SI1143_PS_AUTO 0x0D
#define SI1143_PARAM_SET 0xA0
#define SI1143_PARAM_QUERY 0x80
//Prox/ALS parameters
#define SI1143_CHLIST 0x01
#define SI1143_PSLED12_SELECT 0x02
#define SI1143_PS_ADC_GAIN 0x0B
#define SI1143_ALS_VIS_ADC_GAIN 0x11
#define SI1143_TH_LOW 0
#define SI1143_TH_HIGH 1
#define SI1143_ALS_VIS 0
#define SI1143_ALS_IR 1
#define SI1143_DEFAULT_PS_ADC_GAIN 2
#define SI1143_MAX_PS_ADC_GAIN 4 // could be 5, but that might get unsafe and damage the part
#define SI1143_DEFAULT_ALS_VS_ADC_GAIN 3
#define SI1143_ALS_IE 0x01
#define SI1143_PS1_IE 0x04
#define SI1143_PS1_IM 0x30
#define SI1143_ALS_VIS_IM 0x01
#define SI1143_ALS_IR_IM 0x03
#define SI1143_ALS_IM_MASK 0x03
/*
* Structs
*/
struct si1143_data {
struct i2c_client *client;
struct mutex update_lock;
struct delayed_work dwork;
spinlock_t lock;
unsigned int power_state : 1;
unsigned int operating_mode : 1;
unsigned int als_interrupt_spectrum;
unsigned int irq;
};
/*
* Global data
*/
/*
* Management functions
*/
static ssize_t __si1143_show_light(struct i2c_client *client, char *buf)
{
u8 visiblelight[2];
int err;
err = i2c_smbus_write_byte_data(client, SI1143_COMMAND, SI1143_ALS_FORCE);
if (err < 0)
return err;
mdelay(20);
err = i2c_smbus_read_i2c_block_data(client, SI1143_ALS_VIS_DATA0,
2, visiblelight);
if (err < 0)
return err;
return sprintf(buf, "%d\n", (visiblelight[1]<< 8) | visiblelight[0] );
}
static ssize_t si1143_show_light(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct i2c_client *client = to_i2c_client(dev);
struct si1143_data *data = i2c_get_clientdata(client);
int ret;
mutex_lock(&data->update_lock);
ret = __si1143_show_light(client, buf);
mutex_unlock(&data->update_lock);
return ret;
}
static ssize_t __si1143_show_ir(struct i2c_client *client, char *buf)
{
u8 infrared[2];
int err;
err = i2c_smbus_write_byte_data(client, SI1143_COMMAND, SI1143_ALS_FORCE);
if (err < 0)
return err;
mdelay(20);
// Read the PS1_DATA0
err = i2c_smbus_read_i2c_block_data(client, SI1143_ALS_IR_DATA0,
2, infrared);
if (err < 0)
return err;
return sprintf(buf, "%d\n", (infrared[1]<< 8) | infrared[0] );
}
static ssize_t si1143_show_ir(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct i2c_client *client = to_i2c_client(dev);
struct si1143_data *data = i2c_get_clientdata(client);
int ret;
mutex_lock(&data->update_lock);
ret = __si1143_show_ir(client, buf);
mutex_unlock(&data->update_lock);
return ret;
}
static ssize_t __si1143_show_prox(struct i2c_client *client, char *buf)
{
u8 proximity[2];
int err;
err = i2c_smbus_write_byte_data(client, SI1143_COMMAND, SI1143_PS_FORCE);
if (err < 0)
return err;
mdelay(20);
// Read the PS1_DATA0
err = i2c_smbus_read_i2c_block_data(client, SI1143_PS1_DATA0,
2, proximity);
if (err < 0)
return err;
return sprintf(buf, "%d\n", (proximity[1]<< 8) | proximity[0] );
}
static ssize_t si1143_show_prox(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct i2c_client *client = to_i2c_client(dev);
struct si1143_data *data = i2c_get_clientdata(client);
int ret;
mutex_lock(&data->update_lock);
ret = __si1143_show_prox(client, buf);
mutex_unlock(&data->update_lock);
return ret;
}
static ssize_t __si1143_get_prox_th(struct i2c_client *client, char *buf)
{
int err;
u8 threshold;
err = i2c_smbus_read_i2c_block_data(client, SI1143_PS1_TH,
1, &threshold);
if (err < 0)
return err;
return sprintf(buf, "%d\n", (threshold << 8) );
}
static ssize_t si1143_get_prox_th(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct i2c_client *client = to_i2c_client(dev);
struct si1143_data *data = i2c_get_clientdata(client);
int ret;
mutex_lock(&data->update_lock);
ret = __si1143_get_prox_th(client, buf);
mutex_unlock(&data->update_lock);
return ret;
}
static ssize_t __si1143_set_prox_th(struct i2c_client *client, u32 threshold)
{
int err;
u8 device_th = (u8) ((threshold & 0xFF00)>> 8);
u8 interrupt_val=0;
err = i2c_smbus_write_byte_data(client, SI1143_PS1_TH, device_th);
if (err < 0)
return err;
err = i2c_smbus_read_i2c_block_data(client, SI1143_IRQ_ENABLE, 1, &interrupt_val);
if (err < 0)
return err;
// Set the IRQ_ENABLE register
err = i2c_smbus_write_byte_data(client, SI1143_IRQ_ENABLE, interrupt_val | SI1143_PS1_IE);
if (err < 0)
return err;
err = i2c_smbus_read_i2c_block_data(client, SI1143_IRQ_MODE1, 1, &interrupt_val);
if (err < 0)
return err;
// Set the IRQ_MODE1 register
err = i2c_smbus_write_byte_data(client, SI1143_IRQ_MODE1, interrupt_val | SI1143_PS1_IM);
if (err < 0)
return err;
err = i2c_smbus_write_byte_data(client, SI1143_MEAS_RATE, 0xb9);//DF);
if (err < 0)
return err;
err = i2c_smbus_write_byte_data(client, SI1143_PS_RATE, 0x08);
if (err < 0)
return err;
err = i2c_smbus_write_byte_data(client, SI1143_COMMAND, SI1143_PS_AUTO);
if (err < 0)
return err;
else
return 0;
}
static ssize_t si1143_set_prox_th(struct device *dev,
struct device_attribute *attr, const char *buf, size_t len)
{
struct i2c_client *client = to_i2c_client(dev);
struct si1143_data *data = i2c_get_clientdata(client);
int ret;
u32 threshold;
sscanf(buf, "%u", &threshold);
mutex_lock(&data->update_lock);
ret = __si1143_set_prox_th(client, threshold);
mutex_unlock(&data->update_lock);
if(ret < 0)
{
return ret;
}
else
{
return strnlen(buf, PAGE_SIZE);
}
}
static ssize_t __si1143_get_prox_gain(struct i2c_client *client, char *buf)
{
int err;
u8 gain;
err = i2c_smbus_write_byte_data(client, SI1143_COMMAND, SI1143_PARAM_QUERY | SI1143_PS_ADC_GAIN);
if (err < 0)
return err;
err = i2c_smbus_read_i2c_block_data(client, SI1143_PARAM_RD, 1, &gain);
if (err < 0)
return err;
return sprintf(buf, "%d\n", gain );
}
static ssize_t si1143_get_prox_gain(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct i2c_client *client = to_i2c_client(dev);
struct si1143_data *data = i2c_get_clientdata(client);
int ret;
mutex_lock(&data->update_lock);
ret = __si1143_get_prox_gain(client, buf);
mutex_unlock(&data->update_lock);
return ret;
}
static ssize_t __si1143_set_prox_gain(struct i2c_client *client, u8 gain)
{
int err;
err = i2c_smbus_write_byte_data(client, SI1143_PARAM_WR, min(gain, (u8) SI1143_MAX_PS_ADC_GAIN) );
if (err < 0)
return err;
err = i2c_smbus_write_byte_data(client, SI1143_COMMAND, SI1143_PARAM_SET | SI1143_PS_ADC_GAIN);
if (err < 0)
return err;
else
return 0;
}
static ssize_t si1143_set_prox_gain(struct device *dev,
struct device_attribute *attr, const char *buf, size_t len)
{
struct i2c_client *client = to_i2c_client(dev);
struct si1143_data *data = i2c_get_clientdata(client);
int ret;
u32 gain;
sscanf(buf, "%u", &gain);
mutex_lock(&data->update_lock);
ret = __si1143_set_prox_gain(client, gain);
mutex_unlock(&data->update_lock);
if(ret < 0)
{
return ret;
}
else
{
return strnlen(buf, PAGE_SIZE);
}
}
static ssize_t __si1143_get_als_spectrum(struct i2c_client *client, char *buf)
{
int err;
u8 interrupts;
err = i2c_smbus_read_i2c_block_data(client, SI1143_IRQ_MODE1,
1, &interrupts);
if (err < 0)
return err;
if ((interrupts & SI1143_ALS_IR_IM) == SI1143_ALS_IR_IM)
return sprintf(buf, "infrared\n");
else
return sprintf(buf, "visible\n");
}
static ssize_t si1143_get_als_spectrum(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct i2c_client *client = to_i2c_client(dev);
struct si1143_data *data = i2c_get_clientdata(client);
int ret;
mutex_lock(&data->update_lock);
ret = __si1143_get_als_spectrum(client, buf);
mutex_unlock(&data->update_lock);
return ret;
}
static ssize_t __si1143_set_als_spectrum(struct i2c_client *client, u32 visible)
{
int err;
u8 interrupt_val = 0;
err = i2c_smbus_read_i2c_block_data(client, SI1143_IRQ_MODE1,
1, &interrupt_val);
if (err < 0)
return err;
if (visible)
{
interrupt_val &= ~SI1143_ALS_IM_MASK;
interrupt_val |= SI1143_ALS_VIS_IM;
}
else
{
interrupt_val &= ~SI1143_ALS_IM_MASK;
interrupt_val |= SI1143_ALS_IR_IM;
}
err = i2c_smbus_write_byte_data(client, SI1143_IRQ_MODE1, interrupt_val);
if (err < 0)
return err;
err = i2c_smbus_read_i2c_block_data(client, SI1143_IRQ_ENABLE, 1, &interrupt_val);
if (err < 0)
return err;
// Set the IRQ_ENABLE register
err = i2c_smbus_write_byte_data(client, SI1143_IRQ_ENABLE, interrupt_val | SI1143_ALS_IE);
if (err < 0)
return err;
else
return 0;
}
static ssize_t si1143_set_als_spectrum(struct device *dev,
struct device_attribute *attr, const char *buf, size_t len)
{
struct i2c_client *client = to_i2c_client(dev);
struct si1143_data *data = i2c_get_clientdata(client);
int ret;
u32 visible = 0;
sscanf(buf, "%u", &visible);
mutex_lock(&data->update_lock);
ret = __si1143_set_als_spectrum(client, visible);
mutex_unlock(&data->update_lock);
if(ret < 0)
{
return ret;
}
else
{
return strnlen(buf, PAGE_SIZE);
}
}
static ssize_t __si1143_get_als_th(struct i2c_client *client, char *buf, int limit)
{
int err;
u8 threshold;
if(limit == SI1143_TH_HIGH)
err = i2c_smbus_read_i2c_block_data(client, SI1143_ALS_HIGH_TH, 1, &threshold);
else
err = i2c_smbus_read_i2c_block_data(client, SI1143_ALS_LOW_TH, 1, &threshold);
if (err < 0)
return err;
return sprintf(buf, "%d\n", (threshold << 8) );
}
static ssize_t si1143_get_als_th_high(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct i2c_client *client = to_i2c_client(dev);
struct si1143_data *data = i2c_get_clientdata(client);
int ret;
mutex_lock(&data->update_lock);
ret = __si1143_get_als_th(client, buf, SI1143_TH_HIGH);
mutex_unlock(&data->update_lock);
return ret;
}
static ssize_t si1143_get_als_th_low(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct i2c_client *client = to_i2c_client(dev);
struct si1143_data *data = i2c_get_clientdata(client);
int ret;
mutex_lock(&data->update_lock);
ret = __si1143_get_als_th(client, buf, SI1143_TH_LOW);
mutex_unlock(&data->update_lock);
return ret;
}
static ssize_t __si1143_set_als_th(struct i2c_client *client, u32 threshold, int limit)
{
int err;
u8 device_th = (u8) ((threshold & 0xFF00)>> 8);
if(limit == SI1143_TH_HIGH)
err = i2c_smbus_write_byte_data(client, SI1143_ALS_HIGH_TH, device_th);
else
err = i2c_smbus_write_byte_data(client, SI1143_ALS_LOW_TH, device_th);
if (err < 0)
return err;
else
return 0;
}
static ssize_t si1143_set_als_th_high(struct device *dev,
struct device_attribute *attr, const char *buf, size_t len)
{
struct i2c_client *client = to_i2c_client(dev);
struct si1143_data *data = i2c_get_clientdata(client);
int ret;
u32 threshold;
sscanf(buf, "%u", &threshold);
mutex_lock(&data->update_lock);
ret = __si1143_set_als_th(client, threshold, SI1143_TH_HIGH);
mutex_unlock(&data->update_lock);
if(ret < 0)
{
return ret;
}
else
{
return strnlen(buf, PAGE_SIZE);
}
}
static ssize_t si1143_set_als_th_low(struct device *dev,
struct device_attribute *attr, const char *buf, size_t len)
{
struct i2c_client *client = to_i2c_client(dev);
struct si1143_data *data = i2c_get_clientdata(client);
int ret;
u32 threshold;
sscanf(buf, "%u", &threshold);
mutex_lock(&data->update_lock);
ret = __si1143_set_als_th(client, threshold, SI1143_TH_LOW);
mutex_unlock(&data->update_lock);
if(ret < 0)
{
return ret;
}
else
{
return strnlen(buf, PAGE_SIZE);
}
}
static DEVICE_ATTR(proximity, S_IRUGO, si1143_show_prox, NULL);
static DEVICE_ATTR(illuminance_visible, S_IRUGO, si1143_show_light, NULL);
static DEVICE_ATTR(illuminance_infrared, S_IRUGO, si1143_show_ir, NULL);
static DEVICE_ATTR(proximity_threshold, (S_IRUGO | S_IWUGO ), si1143_get_prox_th, si1143_set_prox_th);
static DEVICE_ATTR(illuminance_threshold_high, (S_IRUGO | S_IWUGO ), si1143_get_als_th_high, si1143_set_als_th_high);
static DEVICE_ATTR(illuminance_threshold_low, (S_IRUGO | S_IWUGO ), si1143_get_als_th_low, si1143_set_als_th_low);
static DEVICE_ATTR(illuminance_interrupt_spectrum, (S_IRUGO | S_IWUGO ), si1143_get_als_spectrum, si1143_set_als_spectrum);
static DEVICE_ATTR(proximity_adc_gain, (S_IRUGO | S_IWUGO ), si1143_get_prox_gain, si1143_set_prox_gain);
static struct attribute *si1143_attributes[] = {
&dev_attr_proximity.attr,
&dev_attr_illuminance_visible.attr,
&dev_attr_illuminance_infrared.attr,
&dev_attr_proximity_threshold.attr,
&dev_attr_illuminance_threshold_high.attr,
&dev_attr_illuminance_threshold_low.attr,
&dev_attr_illuminance_interrupt_spectrum.attr,
&dev_attr_proximity_adc_gain.attr,
NULL
};
static const struct attribute_group si1143_attr_group = {
.attrs = si1143_attributes,
};
/*
* Initialization function
*/
static int si1143_init_client(struct i2c_client *client, struct si1143_platform_data *platform_data)
{
int err;
err = i2c_smbus_read_byte_data(client, SI1143_PART_ID);
if (err < 0)
return err;
if (err != SI1143_PART_ID_VAL)
{
return -ENODEV;
}
// Send the 0x017 to HW_KEY to enter Standby mode
err = i2c_smbus_write_byte_data(client, SI1143_HW_KEY, 0x17);
if (err < 0)
{
return err;
}
// Set the INT_CFG register
// level, High-Z
err = i2c_smbus_write_byte_data(client, SI1143_INT_CFG, 0x01);
if (err < 0)
{
return err;
}
// Set the IRQ_ENABLE register
err = i2c_smbus_write_byte_data(client, SI1143_IRQ_ENABLE, 0x00);
if (err < 0)
{
return err;
}
// Set the IRQ_MODE1 register
err = i2c_smbus_write_byte_data(client, SI1143_IRQ_MODE1, 0x00);
if (err < 0)
{
return err;
}
// Set the PS_LED21 register (SET LED CURRENT)
// LED1_I set to 100%
err = i2c_smbus_write_byte_data(client, SI1143_PS_LED21,
(platform_data->proximity_tx_led1 ? 0x0F : 0) |
(platform_data->proximity_tx_led2 ? 0xF0 : 0) );
if (err < 0)
{
return err;
}
err = i2c_smbus_write_byte_data(client, SI1143_PS_LED3,
(platform_data->proximity_tx_led3 ? 0x0F : 0) );
if (err < 0)
{
return err;
}
// set PS1 in CHLIST to enable LED1 measurement
// first set the value of PARAM_WR reg
// turn on ALS_IR, ALS_VIS and PS1 measurement
err = i2c_smbus_write_byte_data(client, SI1143_PARAM_WR, 0x31);
if (err < 0)
{
return err;
}
// next send PARAM_SET command
// turn on PS1 measurement
err = i2c_smbus_write_byte_data(client, SI1143_COMMAND, SI1143_PARAM_SET | SI1143_CHLIST);
if (err < 0)
{
return err;
}
err = i2c_smbus_write_byte_data(client, SI1143_PARAM_WR,
(platform_data->proximity_tx_led1 ? 0x01 : 0) |
(platform_data->proximity_tx_led2 ? 0x02 : 0) |
(platform_data->proximity_tx_led3 ? 0x04 : 0) );
if (err < 0)
{
return err;
}
// next send PARAM_SET command
// turn on PS1 measurement
err = i2c_smbus_write_byte_data(client, SI1143_COMMAND, SI1143_PARAM_SET | SI1143_PSLED12_SELECT);
if (err < 0)
{
return err;
}
// first set the value of PARAM_WR reg
err = i2c_smbus_write_byte_data(client, SI1143_PARAM_WR, SI1143_DEFAULT_PS_ADC_GAIN);
if (err < 0)
{
return err;
}
// next send PARAM_SET command
// set the PS_ADC_GAIN value
err = i2c_smbus_write_byte_data(client, SI1143_COMMAND, SI1143_PARAM_SET | SI1143_PS_ADC_GAIN);
if (err < 0)
{
return err;
}
// first set the value of PARAM_WR reg
err = i2c_smbus_write_byte_data(client, SI1143_PARAM_WR, SI1143_DEFAULT_ALS_VS_ADC_GAIN);
if (err < 0)
{
return err;
}
// next send PARAM_SET command
// set the ALS_VIS_ADC_GAIN value
err = i2c_smbus_write_byte_data(client, SI1143_COMMAND, SI1143_PARAM_SET | SI1143_ALS_VIS_ADC_GAIN);
if (err < 0)
{
return err;
}
return 0;
}
static void si1143_i2c_reschedule_work(struct si1143_data *data,
unsigned long delay)
{
unsigned long flags;
spin_lock_irqsave(&data->lock, flags);
/*
* If work is already scheduled then subsequent schedules will not
* change the scheduled time that's why we have to cancel it first.
*/
__cancel_delayed_work(&data->dwork);
schedule_delayed_work(&data->dwork, delay);
spin_unlock_irqrestore(&data->lock, flags);
}
static irqreturn_t si1143_irq(int irq, void *dev_id)
{
struct si1143_data *data = dev_id;
si1143_i2c_reschedule_work(data, 0);
return IRQ_HANDLED;
}
static void si1143_i2c_work_handler(struct work_struct *work)
{
struct si1143_data *data = container_of(work, struct si1143_data, dwork.work);
struct i2c_client *client = data->client;
int results;
//Get the interrupts over i2c
results = i2c_smbus_read_byte_data(client, SI1143_IRQ_STATUS);
//Process interrupts
//printk("si1143 irq handler 0x%X\n", results);
//Clear the interrupts
i2c_smbus_write_byte_data(client, SI1143_IRQ_STATUS, results);
}
/*
* I2C init/probing/exit functions
*/
static struct i2c_driver si1143_driver;
static int __devinit si1143_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent);
struct si1143_data *data;
int err = 0;
struct si1143_platform_data *platform_data;
if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_WRITE_BYTE
| I2C_FUNC_SMBUS_READ_BYTE_DATA)) {
err = -EIO;
goto exit;
}
data = kzalloc(sizeof(struct si1143_data), GFP_KERNEL);
if (!data) {
err = -ENOMEM;
goto exit;
}
data->client = client;
i2c_set_clientdata(client, data);
/* Check platform data */
platform_data = client->dev.platform_data;
mutex_init(&data->update_lock);
data->als_interrupt_spectrum=0;
data->irq = gpio_to_irq(client->irq);
/* request the GPIOs */
err = gpio_request(client->irq, SI1143_DRV_NAME);
if (err) {
dev_err(&client->dev, "unable to request GPIO %d\n",
client->irq);
goto exit_kfree;
}
/* request the IRQs */
err = request_irq(data->irq, &si1143_irq,
IORESOURCE_IRQ_HIGHEDGE | IORESOURCE_IRQ_LOWEDGE,
SI1143_DRV_NAME, data);
if (err) {
dev_err(&client->dev, "unable to request IRQ %d\n",
data->irq);
gpio_free(client->irq);
goto exit_kfree;
}
/* Initialize the SI1143 chip */
err = si1143_init_client(client, platform_data);
if (err)
{
goto exit_kfree;
}
INIT_DELAYED_WORK(&data->dwork, si1143_i2c_work_handler);
spin_lock_init(&data->lock);
/* Register sysfs hooks */
err = sysfs_create_group(&client->dev.kobj, &si1143_attr_group);
if (err)
{
goto exit_kfree;
}
dev_info(&client->dev, "support ver. %s enabled\n", DRIVER_VERSION);
return 0;
exit_kfree:
kfree(data);
exit:
return err;
}
static int __devexit si1143_remove(struct i2c_client *client)
{
sysfs_remove_group(&client->dev.kobj, &si1143_attr_group);
/* Power down the device */
// si1143_set_power_state(client, 0);
kfree(i2c_get_clientdata(client));
return 0;
}
#ifdef CONFIG_PM
static int si1143_suspend(struct i2c_client *client, pm_message_t mesg)
{
struct si1143_data *data = i2c_get_clientdata(client);
cancel_delayed_work_sync(&data->dwork);
return 0;//si1143_set_power_state(client, 0);
}
static int si1143_resume(struct i2c_client *client)
{
return 0;//si1143_set_power_state(client, 1);
}
#else
#define si1143_suspend NULL
#define si1143_resume NULL
#endif /* CONFIG_PM */
static const struct i2c_device_id si1143_id[] = {
{ "si1143", 0 },
{ }
};
MODULE_DEVICE_TABLE(i2c, si1143_id);
static struct i2c_driver si1143_driver = {
.driver = {
.name = SI1143_DRV_NAME,
.owner = THIS_MODULE,
},
.suspend = si1143_suspend,
.resume = si1143_resume,
.probe = si1143_probe,
.remove = __devexit_p(si1143_remove),
.id_table = si1143_id,
};
static int __init si1143_init(void)
{
return i2c_add_driver(&si1143_driver);
}
static void __exit si1143_exit(void)
{
i2c_del_driver(&si1143_driver);
}
MODULE_AUTHOR("Nest Labs, Inc");
MODULE_DESCRIPTION("SI1143 ambient light and proximity sensor driver");
MODULE_LICENSE("GPL");
MODULE_VERSION(DRIVER_VERSION);
module_init(si1143_init);
module_exit(si1143_exit);