summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorXiaofei Zhu <zhuxf@marvell.com>2015-06-18 11:02:45 +0800
committerTim Wang <wangtt@marvell.com>2015-06-23 17:14:24 +0800
commit67a8b009685a78e8366c51fbe19d134dc38ce826 (patch)
treec8c01a3e3ef865080100c1c004135b4c370d6407
parentbe5d79ca9f257d76a58fc747fe4ccbfcebd49dbb (diff)
downloadmarvell-67a8b009685a78e8366c51fbe19d134dc38ce826.tar.gz
hwmon: ltr55x: add the ltr55x driver code
add the ltr55x driver code Change-Id: Ib7277fec8cf61e6feb1bc2c7de280d496f0f6cd7 Signed-off-by: Xiaofei Zhu <zhuxf@marvell.com>
-rw-r--r--drivers/hwmon/Kconfig8
-rw-r--r--drivers/hwmon/Makefile2
-rwxr-xr-xdrivers/hwmon/ltr55x.c1760
-rwxr-xr-xdrivers/hwmon/ltr55x.h83
4 files changed, 1852 insertions, 1 deletions
diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig
index d92721f4086..42733114579 100644
--- a/drivers/hwmon/Kconfig
+++ b/drivers/hwmon/Kconfig
@@ -1671,6 +1671,14 @@ config SENSORS_LTR558_I2C
Say Y here if you have a ltr558 and ltr553 device on the board and use I2C
communication, else say N.
+config SENSORS_LTR55X_I2C
+ tristate "LTR 55X proximity device with I2C bus"
+ depends on I2C
+ default n
+ help
+ Say Y here if you have a ltr55X device on the board and use I2C
+ communication, else say N.
+
config SENSOR_EPL2182
tristate "EPL 2182 proximity and ambient light sensor"
depends on I2C
diff --git a/drivers/hwmon/Makefile b/drivers/hwmon/Makefile
index a50def777bb..ece5cc0bf7d 100644
--- a/drivers/hwmon/Makefile
+++ b/drivers/hwmon/Makefile
@@ -154,7 +154,7 @@ obj-$(CONFIG_SENSORS_TSL2772) += tsl277x.o
obj-$(CONFIG_SENSORS_MC3XXX) += mc3xxx.o
obj-$(CONFIG_SENSORS_LTR558_I2C) += ltr_558als.o
obj-$(CONFIG_SENSOR_EPL2182) += epl2182.o
-
+obj-$(CONFIG_SENSORS_LTR55X_I2C) += ltr55x.o
obj-$(CONFIG_PMBUS) += pmbus/
obj-y += bmi160/
diff --git a/drivers/hwmon/ltr55x.c b/drivers/hwmon/ltr55x.c
new file mode 100755
index 00000000000..c5c29daabe8
--- /dev/null
+++ b/drivers/hwmon/ltr55x.c
@@ -0,0 +1,1760 @@
+/* ltr55x.c
+ * LTR-On LTR-55x Proxmity and Light sensor driver
+ *
+ * Copyright (C) 2011 Lite-On Technology Corp (Singapore)
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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.
+ *
+ */
+/*
+ * 2011-11-18 Thundersoft porting to MSM7x27A platform.
+ * 2011-05-01 Lite-On created base driver.
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/i2c.h>
+#include <linux/mutex.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <linux/input.h>
+#include <linux/string.h>
+#include <linux/irq.h>
+#include <linux/fs.h>
+#include <linux/errno.h>
+#include <linux/device.h>
+#include <linux/miscdevice.h>
+#include <linux/platform_device.h>
+#include <linux/wakelock.h>
+#include <linux/workqueue.h>
+#include <linux/uaccess.h>
+#include <linux/ioctl.h>
+#include <asm/atomic.h>
+#include <linux/regulator/consumer.h>
+#include <linux/of_gpio.h>
+#include "ltr55x.h"
+
+#define SENSOR_NAME "proximity"
+#define LTR55x_DRV_NAME "ltr55x"
+#define LTR55x_MANUFAC_ID 0x05
+#define VENDOR_NAME "lite-on"
+#define LTR55x_SENSOR_NAME "ltr55xals"
+#define DRIVER_VERSION "1.0"
+#define LTR55x_PS_CALIBERATE
+#define LIGHT_SENSOR_NAME "ltr55x_light";
+#define PROXIMITY_SENSOR_NAME "ltr55x_proximity";
+
+#define DEVICE_ATTR2(_name, _mode, _show, _store) \
+struct device_attribute dev_attr2_##_name = __ATTR(_name, _mode, _show, _store)
+
+struct ltr55x_data
+{
+
+ struct i2c_client *client;
+ struct input_dev *input_dev_als;
+ struct input_dev *input_dev_ps;
+ /* pinctrl data*/
+ struct pinctrl *pinctrl;
+ struct pinctrl_state *pin_default;
+ struct pinctrl_state *pin_sleep;
+ struct ltr55x_platform_data *platform_data;
+ /* regulator data */
+ bool power_state;
+ struct regulator *vdd;
+ struct regulator *vio;
+ /* interrupt type is level-style */
+ struct mutex lockw;
+ struct mutex op_lock;
+ struct delayed_work ps_work;
+ struct delayed_work als_work;
+ u8 ps_open_state;
+ u8 als_open_state;
+ u16 irq;
+ u32 ps_state;
+ u32 last_lux;
+#ifdef LTR55x_PS_CALIBERATE
+ bool cali_update;
+#endif
+ u32 dynamic_noise;
+};
+
+struct ltr55x_reg
+{
+ const char *name;
+ u8 addr;
+ u16 defval;
+ u16 curval;
+};
+
+enum ltr55x_reg_tbl
+{
+ REG_ALS_CONTR,
+ REG_PS_CONTR,
+ REG_ALS_PS_STATUS,
+ REG_INTERRUPT,
+ REG_PS_LED,
+ REG_PS_N_PULSES,
+ REG_PS_MEAS_RATE,
+ REG_ALS_MEAS_RATE,
+ REG_MANUFACTURER_ID,
+ REG_INTERRUPT_PERSIST,
+ REG_PS_THRES_LOW,
+ REG_PS_THRES_UP,
+ REG_ALS_THRES_LOW,
+ REG_ALS_THRES_UP,
+ REG_ALS_DATA_CH1,
+ REG_ALS_DATA_CH0,
+ REG_PS_DATA
+};
+
+/*static int ltr55x_als_set_enable(struct sensors_classdev *sensors_cdev,
+ unsigned int enable);*/
+/*static int ltr55x_ps_set_enable(struct sensors_classdev *sensors_cdev,
+ unsigned int enable);*/
+static int ltr55x_als_set_poll_delay(struct ltr55x_data *data,unsigned long delay);
+
+static ssize_t ltr55x_ps_self_caliberate(struct ltr55x_data *data);
+
+static struct ltr55x_reg reg_tbl[] = {
+ {
+ .name = "ALS_CONTR",
+ .addr = 0x80,
+ .defval = 0x00,
+ .curval = 0x19,
+ },
+ {
+ .name = "PS_CONTR",
+ .addr = 0x81,
+ .defval = 0x00,
+ .curval = 0x03,
+ },
+ {
+ .name = "ALS_PS_STATUS",
+ .addr = 0x8c,
+ .defval = 0x00,
+ .curval = 0x00,
+ },
+ {
+ .name = "INTERRUPT",
+ .addr = 0x8f,
+ .defval = 0x00,
+ .curval = 0x01,
+ },
+ {
+ .name = "PS_LED",
+ .addr = 0x82,
+ .defval = 0x7f,
+ .curval = 0x7f,
+ },
+ {
+ .name = "PS_N_PULSES",
+ .addr = 0x83,
+ .defval = 0x01,
+ .curval = 0x06,
+ },
+ {
+ .name = "PS_MEAS_RATE",
+ .addr = 0x84,
+ .defval = 0x02,
+ .curval = 0x00,
+ },
+ {
+ .name = "ALS_MEAS_RATE",
+ .addr = 0x85,
+ // .defval = 0x03,
+ // .curval = 0x03,
+ .defval = 0x03,
+ .curval = 0x02, //200ms
+ },
+ {
+ .name = "MANUFACTURER_ID",
+ .addr = 0x87,
+ .defval = 0x05,
+ .curval = 0x05,
+ },
+ {
+ .name = "INTERRUPT_PERSIST",
+ .addr = 0x9e,
+ .defval = 0x00,
+ .curval = 0x23,
+ },
+ {
+ .name = "PS_THRES_LOW",
+ .addr = 0x92,
+ .defval = 0x0000,
+ .curval = 0x0000,
+ },
+ {
+ .name = "PS_THRES_UP",
+ .addr = 0x90,
+ .defval = 0x07ff,
+ .curval = 0x0000,
+ },
+ {
+ .name = "ALS_THRES_LOW",
+ .addr = 0x99,
+ .defval = 0x0000,
+ .curval = 0x0000,
+ },
+ {
+ .name = "ALS_THRES_UP",
+ .addr = 0x97,
+ .defval = 0xffff,
+ //.defval = 0x0000,
+ .curval = 0x0000,
+ },
+ {
+ .name = "ALS_DATA_CH1",
+ .addr = 0x88,
+ .defval = 0x0000,
+ .curval = 0x0000,
+ },
+ {
+ .name = "ALS_DATA_CH0",
+ .addr = 0x8a,
+ .defval = 0x0000,
+ .curval = 0x0000,
+ },
+ {
+ .name = "PS_DATA",
+ .addr = 0x8d,
+ .defval = 0x0000,
+ .curval = 0x0000,
+ },
+};
+
+static int ltr55x_ps_read(struct i2c_client *client)
+{
+ int psval_lo, psval_hi, psdata;
+ psval_lo = i2c_smbus_read_byte_data(client, LTR55x_PS_DATA_0);
+ if(psval_lo < 0)
+ {
+ psdata = psval_lo;
+ goto out;
+ }
+ psval_hi = i2c_smbus_read_byte_data(client, LTR55x_PS_DATA_1);
+ if(psval_hi < 0)
+ {
+ psdata = psval_hi;
+ goto out;
+ }
+
+ psdata = ((psval_hi & 7)* 256) + psval_lo;
+ printk("ps_rawdata = %d\n", psdata);
+
+ out:
+ return psdata;
+}
+
+
+static int ltr55x_chip_reset(struct i2c_client *client)
+{
+ int ret;
+
+ ret = i2c_smbus_write_byte_data(client, LTR55x_ALS_CONTR, MODE_ALS_StdBy);//als standby mode
+ ret = i2c_smbus_write_byte_data(client, LTR55x_PS_CONTR, MODE_PS_StdBy);//ps standby mode
+ ret = i2c_smbus_write_byte_data(client, LTR55x_ALS_CONTR, 0x02);//reset
+ if(ret < 0)
+ printk("%s reset chip fail\n",__func__);
+
+ return ret;
+}
+
+static void ltr55x_set_ps_threshold(struct i2c_client *client, u8 addr, u16 value)
+{
+ i2c_smbus_write_byte_data(client, addr, (value & 0xff));
+ i2c_smbus_write_byte_data(client, addr+1, (value >> 8));
+}
+
+static int ltr55x_ps_enable(struct i2c_client *client, int on)
+{
+ struct ltr55x_data *data = i2c_get_clientdata(client);
+ int ret=0;
+ int contr_data;
+
+ if(on)
+ {
+ ltr55x_set_ps_threshold(client, LTR55x_PS_THRES_LOW_0, 0);
+ ltr55x_set_ps_threshold(client, LTR55x_PS_THRES_UP_0, data->platform_data->prox_threshold);
+ ret = i2c_smbus_write_byte_data(client, LTR55x_PS_CONTR, reg_tbl[REG_PS_CONTR].curval);
+ //ret = i2c_smbus_write_byte_data(client, LTR55x_INTERRUPT, reg_tbl[REG_INTERRUPT].curval);//enable irq
+ if(ret<0)
+ {
+ pr_err("%s: enable=(%d) failed!\n", __func__, on);
+ return ret;
+ }
+ contr_data = i2c_smbus_read_byte_data(client, LTR55x_PS_CONTR);
+ if(contr_data != reg_tbl[REG_PS_CONTR].curval)
+ {
+ //data->ps_open_state = 1;
+ pr_err("%s: enable=(%d) failed!\n", __func__, on);
+ return -EFAULT;
+ }
+
+ msleep(WAKEUP_DELAY);
+ data->ps_state = 1;
+ input_report_abs(data->input_dev_ps, ABS_DISTANCE, data->ps_state);
+ ltr55x_ps_self_caliberate(data);
+ }
+ else
+ {
+ //ret = i2c_smbus_write_byte_data(client, LTR55x_INTERRUPT, reg_tbl[REG_INTERRUPT].defval);//disable irq
+ ret = i2c_smbus_write_byte_data(client, LTR55x_PS_CONTR, MODE_PS_StdBy);
+ if(ret<0)
+ {
+ pr_err("%s: enable=(%d) failed!\n", __func__, on);
+ return ret;
+ }
+ //data->ps_open_state = 0;
+ contr_data = i2c_smbus_read_byte_data(client, LTR55x_PS_CONTR);
+ if(contr_data != reg_tbl[REG_PS_CONTR].defval)
+ {
+ //data->ps_open_state = 1;
+ pr_err("%s: enable=(%d) failed!\n", __func__, on);
+ return -EFAULT;
+ }
+ }
+ data->ps_open_state = on;
+ pr_err("%s: enable=(%d) OK\n", __func__, on);
+ return ret;
+}
+
+/*
+ * Absent Light Sensor Congfig
+ */
+static int ltr55x_als_enable(struct i2c_client *client, int on)
+{
+ struct ltr55x_data *data = i2c_get_clientdata(client);
+ int ret;
+ if(on)
+ {
+ ret = i2c_smbus_write_byte_data(client, LTR55x_ALS_CONTR, reg_tbl[REG_ALS_CONTR].curval);
+ msleep(WAKEUP_DELAY);
+ ret |= i2c_smbus_read_byte_data(client, LTR55x_ALS_DATA_CH0_1);
+ cancel_delayed_work_sync(&data->als_work);
+ schedule_delayed_work(&data->als_work, msecs_to_jiffies(data->platform_data->als_poll_interval));
+ }
+ else
+ {
+ cancel_delayed_work_sync(&data->als_work);
+ ret = i2c_smbus_write_byte_data(client, LTR55x_ALS_CONTR, MODE_ALS_StdBy);
+ }
+ data->als_open_state = on;
+ pr_err("%s: enable=(%d) ret=%d\n", __func__, on, ret);
+ return ret;
+}
+
+static int ltr55x_als_read(struct i2c_client *client)
+{
+ int alsval_ch0_lo, alsval_ch0_hi, alsval_ch0;
+ int alsval_ch1_lo, alsval_ch1_hi, alsval_ch1;
+ int luxdata;
+ int ch1_co, ch0_co, ratio;
+ alsval_ch1_lo = i2c_smbus_read_byte_data(client, LTR55x_ALS_DATA_CH1_0);
+ alsval_ch1_hi = i2c_smbus_read_byte_data(client, LTR55x_ALS_DATA_CH1_1);
+ if(alsval_ch1_lo < 0 || alsval_ch1_hi < 0)
+ return -1;
+ alsval_ch1 = (alsval_ch1_hi << 8) + alsval_ch1_lo;
+ alsval_ch0_lo = i2c_smbus_read_byte_data(client, LTR55x_ALS_DATA_CH0_0);
+ alsval_ch0_hi = i2c_smbus_read_byte_data(client, LTR55x_ALS_DATA_CH0_1);
+ if(alsval_ch0_lo < 0 || alsval_ch0_hi < 0)
+ return -1;
+ alsval_ch0 = (alsval_ch0_hi << 8) + alsval_ch0_lo;
+
+ if((alsval_ch0 + alsval_ch1) == 0)
+ {
+ ratio = 1000;
+ }
+ else
+ {
+ ratio = alsval_ch1 * 1000 / (alsval_ch1 + alsval_ch0);
+ }
+
+ if(ratio < 450)
+ {
+ ch0_co = 17743;
+ ch1_co = -11059;
+ }
+ else if((ratio >= 450) && (ratio < 640))
+ {
+ ch0_co = 42785;
+ ch1_co = 19548;
+ }
+ else if((ratio >= 640) && (ratio < 850))
+ {
+ ch0_co = 5926;
+ ch1_co = -1185;
+ }
+ else if(ratio >= 850)
+ {
+ ch0_co = 0;
+ ch1_co = 0;
+ }
+ luxdata = (alsval_ch0 * ch0_co - alsval_ch1 * ch1_co) / 10000;
+ return luxdata;
+}
+
+static void ltr55x_ps_work_func(struct work_struct *work)
+{
+ struct ltr55x_data *data = container_of(work, struct ltr55x_data, ps_work.work);
+ struct i2c_client *client=data->client;
+ int als_ps_status;
+ int psdata;
+ static int val_temp = 1;
+ static u32 ps_state_last = 1; // xuke @ 20140828 Far as default.
+ mutex_lock(&data->op_lock);
+
+ als_ps_status = i2c_smbus_read_byte_data(client, LTR55x_ALS_PS_STATUS);
+ printk("%s ps_open_state=%d, als_ps_status=0x%x\n",__func__,data->ps_open_state,als_ps_status);
+ if(als_ps_status < 0)
+ goto workout;
+ /* Here should check data status,ignore interrupt status. */
+ /* Bit 0: PS Data
+ * Bit 1: PS interrupt
+ * Bit 2: ASL Data
+ * Bit 3: ASL interrupt
+ * Bit 4: ASL Gain 0: ALS measurement data is in dynamic range 2 (2 to 64k lux)
+ * 1: ALS measurement data is in dynamic range 1 (0.01 to 320 lux)
+ */
+ if((data->ps_open_state == 1) && (als_ps_status & 0x02))
+ {
+ psdata = ltr55x_ps_read(client);
+ printk("%s ps data=%d(0x%x), prox_threshold=%d, prox_hsyteresis_threshold=%d\n",__func__,psdata,psdata,data->platform_data->prox_threshold,data->platform_data->prox_hsyteresis_threshold);
+ if(psdata > data->platform_data->prox_threshold)
+ {
+ data->ps_state = 0; //near
+ ltr55x_set_ps_threshold(client, LTR55x_PS_THRES_LOW_0, data->platform_data->prox_hsyteresis_threshold);
+ ltr55x_set_ps_threshold(client, LTR55x_PS_THRES_UP_0, 0x07ff);
+ val_temp = 0;
+ }
+ else if(psdata < data->platform_data->prox_hsyteresis_threshold)
+ {
+ data->ps_state = 1; //far
+ ltr55x_set_ps_threshold(client, LTR55x_PS_THRES_LOW_0, 0);
+ ltr55x_set_ps_threshold(client, LTR55x_PS_THRES_UP_0, data->platform_data->prox_threshold);
+ val_temp = 1;
+ }
+ else
+ {
+ data->ps_state = val_temp;
+ }
+
+ if(ps_state_last != data->ps_state)
+ {
+ input_report_abs(data->input_dev_ps, ABS_DISTANCE, data->ps_state);
+ input_sync(data->input_dev_ps);
+ printk("%s, report ABS_DISTANCE=%s\n",__func__, data->ps_state ? "far" : "near");
+ if(data->ps_state == 1 && data->dynamic_noise > 20 && psdata < (data->dynamic_noise - 50))
+ {
+ data->dynamic_noise = psdata;
+ if(psdata < 50)
+ {
+ data->platform_data->prox_threshold = psdata+150;
+ data->platform_data->prox_hsyteresis_threshold = psdata+100;
+ }
+ else if(psdata < 100)
+ {
+ data->platform_data->prox_threshold = psdata+160;
+ data->platform_data->prox_hsyteresis_threshold = psdata+110;
+ }
+ else if(psdata < 600)
+ {
+ data->platform_data->prox_threshold = psdata+160;
+ data->platform_data->prox_hsyteresis_threshold = psdata+120;
+ }
+ else if(psdata < 1500)
+ {
+ data->platform_data->prox_threshold = psdata+200;
+ data->platform_data->prox_hsyteresis_threshold = psdata+150;
+ }
+ else if(psdata < 1650)
+ {
+ data->platform_data->prox_threshold = psdata+400;
+ data->platform_data->prox_hsyteresis_threshold = psdata+160;
+ }
+ else
+ {
+ pr_err("ltr55x the proximity sensor rubber or structure is error!\n");
+ }
+ pr_info("ltr55x self calibrate when state far : noise = %d , thd_val_low = %d , htd_val_high = %d \n", psdata, data->platform_data->prox_hsyteresis_threshold, data->platform_data->prox_threshold);
+ ltr55x_set_ps_threshold(data->client, LTR55x_PS_THRES_LOW_0, 0);
+ ltr55x_set_ps_threshold(data->client, LTR55x_PS_THRES_UP_0, data->platform_data->prox_threshold);
+ }
+
+ ps_state_last = data->ps_state; // xuke @ 20140828 Report ABS value only if the state changed.
+ }
+ else
+ printk("%s, ps_state still %s\n", __func__, data->ps_state ? "far" : "near");
+ }
+ workout:
+ enable_irq(data->irq);
+ mutex_unlock(&data->op_lock);
+}
+
+static void ltr55x_als_work_func(struct work_struct *work)
+{
+ struct ltr55x_data *data = container_of(work, struct ltr55x_data, als_work.work);
+ struct i2c_client *client=data->client;
+ int als_ps_status;
+ int als_data;
+
+ mutex_lock(&data->op_lock);
+
+ //printk("%s\n",__func__);
+
+ if(!data->als_open_state)
+ goto workout;
+
+ als_ps_status = i2c_smbus_read_byte_data(client, LTR55x_ALS_PS_STATUS);
+ if(als_ps_status < 0)
+ goto workout;
+
+
+ if((data->als_open_state == 1) && (als_ps_status & 0x04))
+ {
+ als_data = ltr55x_als_read(client);
+ if(als_data > 50000)
+ als_data = 50000;
+
+ //printk("%s als data %d\n",__func__,als_data);
+ if((als_data >= 0) && (als_data != data->last_lux))
+ {
+ data->last_lux = als_data;
+ input_report_abs(data->input_dev_als, ABS_MISC, als_data);
+ input_sync(data->input_dev_als);
+ }
+ }
+
+ schedule_delayed_work(&data->als_work, msecs_to_jiffies(data->platform_data->als_poll_interval));
+ workout:
+ mutex_unlock(&data->op_lock);
+}
+
+static irqreturn_t ltr55x_irq_handler(int irq, void *arg)
+{
+ struct ltr55x_data *data = (struct ltr55x_data *)arg;
+
+ printk("%s\n",__func__);
+ if(NULL == data)
+ return IRQ_HANDLED;
+ disable_irq_nosync(data->irq);
+ schedule_delayed_work(&data->ps_work, 0);
+ return IRQ_HANDLED;
+}
+
+static int ltr55x_gpio_irq(struct ltr55x_data *data)
+{
+ struct device_node *np = data->client->dev.of_node;
+ int err = 0;
+
+ data->platform_data->int_gpio = of_get_named_gpio_flags(np, "irq-gpios", 0, &data->platform_data->irq_gpio_flags);
+ if(data->platform_data->int_gpio < 0)
+ return -EIO;
+
+ if(gpio_is_valid(data->platform_data->int_gpio))
+ {
+ err = gpio_request(data->platform_data->int_gpio, "ltr55x_irq_gpio");
+ if(err)
+ {
+ printk( "%s irq gpio request failed\n",__func__);
+ return -EINTR;
+ }
+
+ err = gpio_direction_input(data->platform_data->int_gpio);
+ if(err)
+ {
+ printk("%s set_direction for irq gpio failed\n",__func__);
+ return -EIO;
+ }
+ }
+
+ data->irq = data->client->irq = gpio_to_irq(data->platform_data->int_gpio);
+
+ if(request_irq(data->irq, ltr55x_irq_handler, IRQF_TRIGGER_FALLING|IRQF_TRIGGER_RISING/*IRQF_DISABLED|IRQ_TYPE_EDGE_FALLING*/,
+ LTR55x_DRV_NAME, data))
+ {
+ printk("%s Could not allocate ltr55x_INT !\n", __func__);
+ return -EINTR;
+ }
+
+ irq_set_irq_wake(data->irq, 1);
+
+ printk(KERN_INFO "%s: INT No. %d", __func__, data->irq);
+ return 0;
+}
+
+
+static void ltr55x_gpio_irq_free(struct ltr55x_data *data)
+{
+ free_irq(data->irq, data);
+ gpio_free(data->platform_data->int_gpio);
+}
+
+static ssize_t ltr55x_show_enable_ps(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct ltr55x_data *data = dev_get_drvdata(dev);
+
+ return sprintf(buf, "%u\n", data->ps_open_state);
+}
+
+static ssize_t ltr55x_store_enable_ps(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t size)
+{
+ /* If proximity work,then ALS must be enable */
+ unsigned long val;
+ char *after;
+ struct ltr55x_data *data = dev_get_drvdata(dev);
+
+ val = simple_strtoul(buf, &after, 10);
+
+ printk(KERN_INFO "enable 55x PS sensor -> %ld\n", val);
+
+ mutex_lock(&data->lockw);
+
+ ltr55x_ps_enable(data->client, (int)val);
+
+ mutex_unlock(&data->lockw);
+
+ return size;
+}
+
+static ssize_t ltr55x_show_enable_ps_for_input_device(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct input_dev *input = to_input_dev(dev);
+
+ struct ltr55x_data *data = input_get_drvdata(input);
+
+ return sprintf(buf, "%u\n", data->ps_open_state);
+}
+
+static ssize_t ltr55x_store_enable_ps_for_input_device(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t size)
+{
+ /* If proximity work,then ALS must be enable */
+ unsigned long val;
+ char *after;
+
+ struct input_dev *input = to_input_dev(dev);
+
+ struct ltr55x_data *data = input_get_drvdata(input);
+
+ val = simple_strtoul(buf, &after, 10);
+
+ printk(KERN_INFO "enable 55x PS sensor -> %ld\n", val);
+
+ mutex_lock(&data->lockw);
+
+ ltr55x_ps_enable(data->client, (int)val);
+
+ mutex_unlock(&data->lockw);
+
+ return size;
+}
+
+static ssize_t ltr55x_show_poll_delay_als(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct ltr55x_data *data = dev_get_drvdata(dev);
+
+ return sprintf(buf, "%u\n", data->platform_data->als_poll_interval);
+}
+
+static ssize_t ltr55x_store_poll_delay_als(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t size)
+{
+ /* If proximity work,then ALS must be enable */
+ unsigned long val;
+ char *after;
+ struct ltr55x_data *data = dev_get_drvdata(dev);
+
+ val = simple_strtoul(buf, &after, 10);
+
+ printk(KERN_INFO "set 55x ALS sensor poll delay -> %ld\n", val);
+
+ mutex_lock(&data->lockw);
+ ltr55x_als_set_poll_delay(data,val);
+ mutex_unlock(&data->lockw);
+
+ return size;
+}
+
+static ssize_t ltr55x_show_enable_als(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct ltr55x_data *data = dev_get_drvdata(dev);
+
+ return sprintf(buf, "%u\n", data->als_open_state);
+}
+
+static ssize_t ltr55x_store_enable_als(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t size)
+{
+ /* If proximity work,then ALS must be enable */
+ unsigned long val;
+ char *after;
+ struct ltr55x_data *data = dev_get_drvdata(dev);
+
+ val = simple_strtoul(buf, &after, 10);
+
+ printk(KERN_INFO "enable 55x ALS sensor -> %ld\n", val);
+
+ mutex_lock(&data->lockw);
+ ltr55x_als_enable(data->client, (int)val);
+ mutex_unlock(&data->lockw);
+ return size;
+}
+
+static ssize_t ltr55x_show_enable_als_for_input_device(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+
+ struct input_dev *input = to_input_dev(dev);
+
+ struct ltr55x_data *data = input_get_drvdata(input);
+
+ return sprintf(buf, "%u\n", data->als_open_state);
+}
+
+static ssize_t ltr55x_store_enable_als_for_input_device(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t size)
+{
+ /* If proximity work,then ALS must be enable */
+ unsigned long val;
+
+ char *after;
+
+ struct input_dev *input = to_input_dev(dev);
+
+ struct ltr55x_data *data = input_get_drvdata(input);
+
+ val = simple_strtoul(buf, &after, 10);
+
+ printk(KERN_INFO "enable 55x ALS sensor -> %ld\n", val);
+
+ mutex_lock(&data->lockw);
+ ltr55x_als_enable(data->client, (int)val);
+ mutex_unlock(&data->lockw);
+ return size;
+}
+
+
+static ssize_t ltr55x_driver_info_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ return sprintf(buf, "Chip: %s %s\nVersion: %s\n",VENDOR_NAME, LTR55x_SENSOR_NAME, DRIVER_VERSION);
+}
+
+static ssize_t ltr55x_show_debug_regs(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ u8 val,high,low;
+ int i;
+ char *after;
+ struct ltr55x_data *data = dev_get_drvdata(dev);
+ struct i2c_client *client = data->client;
+ after = buf;
+ after += sprintf(after, "%-17s%5s%14s%16s\n", "Register Name", "address", "default", "current");
+ for(i = 0; i < sizeof(reg_tbl)/sizeof(reg_tbl[0]); i++)
+ {
+ if(reg_tbl[i].name == NULL || reg_tbl[i].addr == 0)
+ {
+ break;
+ }
+ if(i < 10)
+ {
+ val = i2c_smbus_read_byte_data(client, reg_tbl[i].addr);
+ after += sprintf(after, "%-20s0x%02x\t 0x%02x\t\t 0x%02x\n", reg_tbl[i].name, reg_tbl[i].addr, reg_tbl[i].defval, val);
+ }
+ else
+ {
+ low = i2c_smbus_read_byte_data(client, reg_tbl[i].addr);
+ high = i2c_smbus_read_byte_data(client, reg_tbl[i].addr+1);
+ after += sprintf(after, "%-20s0x%02x\t0x%04x\t\t0x%04x\n", reg_tbl[i].name, reg_tbl[i].addr, reg_tbl[i].defval,(high << 8) + low);
+ }
+ }
+ after += sprintf(after, "\nYou can echo '0xaa=0xbb' to set the value 0xbb to the register of address 0xaa.\n ");
+
+ return(after - buf);
+}
+
+static ssize_t ltr55x_store_debug_regs(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t size)
+{
+ /* If proximity work,then ALS must be enable */
+ char *after, direct;
+ u8 addr, val;
+ struct ltr55x_data *data = dev_get_drvdata(dev);
+ struct i2c_client *client = data->client;
+
+ addr = simple_strtoul(buf, &after, 16);
+ direct = *after;
+ val = simple_strtoul((after+1), &after, 16);
+
+ if(!((addr >= 0x80 && addr <= 0x93)
+ || (addr >= 0x97 && addr <= 0x9e)))
+ return -EINVAL;
+
+ mutex_lock(&data->lockw);
+ if(direct == '=')
+ i2c_smbus_write_byte_data(client, addr, val);
+ else
+ printk("%s: register(0x%02x) is: 0x%02x\n", __func__, addr, i2c_smbus_read_byte_data(client, addr));
+ mutex_unlock(&data->lockw);
+
+ return(after - buf);
+}
+
+static ssize_t ltr55x_show_adc_data(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct ltr55x_data *data = dev_get_drvdata(dev);
+ struct i2c_client *client = data->client;
+ u8 high,low;
+ char *after;
+
+ after = buf;
+
+ low = i2c_smbus_read_byte_data(client, LTR55x_PS_DATA_0);
+ high = i2c_smbus_read_byte_data(client, LTR55x_PS_DATA_1);
+ if(low < 0 || high < 0)
+ after += sprintf(after, "Failed to read PS adc data.\n");
+ else
+ after += sprintf(after, "%d\n", (high << 8) + low);
+
+ return(after - buf);
+}
+
+static ssize_t ltr55x_show_lux_data(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ int lux;
+ struct ltr55x_data *data = dev_get_drvdata(dev);
+ struct i2c_client *client = data->client;
+
+ lux = ltr55x_als_read(client);
+
+ return sprintf(buf, "%d\n", lux);
+}
+
+#ifdef LTR55x_PS_CALIBERATE
+static ssize_t ltr55x_show_ps_threshold(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct ltr55x_data *data = dev_get_drvdata(dev);
+
+ return sprintf(buf, "%u\n", data->platform_data->prox_threshold);
+}
+
+static ssize_t ltr55x_store_ps_threshold(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t size)
+{
+ /* If proximity work,then ALS must be enable */
+ unsigned long val;
+ char *after;
+ struct ltr55x_data *data = dev_get_drvdata(dev);
+
+ val = simple_strtoul(buf, &after, 10);
+
+ printk(KERN_INFO "ltr55x PS prox_threshold -> %ld\n", val);
+
+ mutex_lock(&data->lockw);
+ data->platform_data->prox_threshold = val;
+ if(data->ps_state == 1)
+ {
+ ltr55x_set_ps_threshold(data->client, LTR55x_PS_THRES_LOW_0, 0);
+ ltr55x_set_ps_threshold(data->client, LTR55x_PS_THRES_UP_0, data->platform_data->prox_threshold);
+ }
+ mutex_unlock(&data->lockw);
+
+ return size;
+}
+
+static ssize_t ltr55x_show_ps_hsyteresis_threshold(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct ltr55x_data *data = dev_get_drvdata(dev);
+
+ return sprintf(buf, "%u\n", data->platform_data->prox_hsyteresis_threshold);
+}
+
+static ssize_t ltr55x_store_ps_hsyteresis_threshold(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t size)
+{
+ /* If proximity work,then ALS must be enable */
+ unsigned long val;
+ char *after;
+ struct ltr55x_data *data = dev_get_drvdata(dev);
+
+ val = simple_strtoul(buf, &after, 10);
+
+ printk(KERN_INFO "ltr55x PS prox_hsyteresis_threshold -> %ld\n", val);
+
+ mutex_lock(&data->lockw);
+ data->platform_data->prox_hsyteresis_threshold = val;
+ if(data->ps_state == 0)
+ {
+ ltr55x_set_ps_threshold(data->client, LTR55x_PS_THRES_LOW_0, data->platform_data->prox_hsyteresis_threshold);
+ ltr55x_set_ps_threshold(data->client, LTR55x_PS_THRES_UP_0, 0x07ff);
+ }
+ mutex_unlock(&data->lockw);
+
+ return size;
+}
+
+static ssize_t ltr55x_show_ps_cali_update_flag(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct ltr55x_data *data = dev_get_drvdata(dev);
+
+ return sprintf(buf, "%u\n", data->cali_update);
+}
+
+static ssize_t ltr55x_store_ps_cali_update_flag(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t size)
+{
+ struct ltr55x_data *data = dev_get_drvdata(dev);
+
+ mutex_lock(&data->lockw);
+ data->cali_update = 0;
+ mutex_unlock(&data->lockw);
+
+ return size;
+}
+#endif
+
+static DEVICE_ATTR(debug_regs, S_IRUGO | S_IWUGO, ltr55x_show_debug_regs,ltr55x_store_debug_regs);
+static DEVICE_ATTR(enable_als_sensor, S_IRUGO | S_IWUGO, ltr55x_show_enable_als,ltr55x_store_enable_als);
+static DEVICE_ATTR(enable, S_IRUGO | S_IWUGO, ltr55x_show_enable_ps,ltr55x_store_enable_ps);
+static DEVICE_ATTR(poll_delay, S_IRUGO | S_IWUGO, ltr55x_show_poll_delay_als,ltr55x_store_poll_delay_als);
+static DEVICE_ATTR(info, S_IRUGO, ltr55x_driver_info_show, NULL);
+static DEVICE_ATTR(raw_adc, S_IRUGO, ltr55x_show_adc_data, NULL);
+static DEVICE_ATTR(lux_adc, S_IRUGO, ltr55x_show_lux_data, NULL);
+#ifdef LTR55x_PS_CALIBERATE
+static DEVICE_ATTR(cali_param_1, S_IRUGO | S_IWUGO, ltr55x_show_ps_threshold,ltr55x_store_ps_threshold);
+static DEVICE_ATTR(cali_param_2, S_IRUGO | S_IWUGO, ltr55x_show_ps_hsyteresis_threshold,ltr55x_store_ps_hsyteresis_threshold);
+static DEVICE_ATTR(cali_update, S_IRUGO | S_IWUGO, ltr55x_show_ps_cali_update_flag,ltr55x_store_ps_cali_update_flag);
+#endif
+
+static DEVICE_ATTR(active, S_IRUGO | S_IWUGO,ltr55x_show_enable_als_for_input_device, ltr55x_store_enable_als_for_input_device);
+static DEVICE_ATTR2(active, S_IRUGO | S_IWUGO,ltr55x_show_enable_ps_for_input_device, ltr55x_store_enable_ps_for_input_device);
+static struct attribute *ltr55x_als_attributes[] = {
+ &dev_attr_active.attr,
+ NULL
+};
+static const struct attribute_group ltr55x_als_attr_group = {
+.attrs = ltr55x_als_attributes,
+};
+static struct attribute *ltr55x_ps_attributes[] = {
+ &dev_attr2_active.attr,
+ NULL
+};
+static const struct attribute_group ltr55x_ps_attr_group = {
+ .attrs = ltr55x_ps_attributes,
+};
+
+
+static struct attribute *ltr55x_attributes[] = {
+ &dev_attr_enable.attr,
+ &dev_attr_info.attr,
+ &dev_attr_enable_als_sensor.attr,
+ &dev_attr_poll_delay.attr,
+ &dev_attr_debug_regs.attr,
+ &dev_attr_raw_adc.attr,
+ &dev_attr_lux_adc.attr,
+#ifdef LTR55x_PS_CALIBERATE
+ &dev_attr_cali_param_1.attr,
+ &dev_attr_cali_param_2.attr,
+ &dev_attr_cali_update.attr,
+#endif
+ NULL,
+};
+
+static const struct attribute_group ltr55x_attr_group = {
+ .attrs = ltr55x_attributes,
+};
+
+static int ltr55x_als_set_poll_delay(struct ltr55x_data *data,unsigned long delay)
+{
+ //mutex_lock(&data->op_lock);
+ if(delay < 1)
+ delay = 1;
+ if(delay > 1000)
+ delay = 1000;
+
+ if(data->platform_data->als_poll_interval != delay)
+ {
+ data->platform_data->als_poll_interval = delay;
+ }
+
+ if(!data->als_open_state)
+ return -ESRCH;
+
+ pr_info("%s poll_interval=%d",__func__,data->platform_data->als_poll_interval);
+ //cancel_delayed_work_sync(&data->als_work);
+ //schedule_delayed_work(&data->als_work,msecs_to_jiffies(data->platform_data->als_poll_interval));
+ //mutex_unlock(&data->op_lock);
+ return 0;
+}
+
+#ifdef LTR55x_PS_CALIBERATE
+static ssize_t ltr55x_ps_self_caliberate(struct ltr55x_data *data)
+{
+ struct ltr55x_platform_data *pdata = data->platform_data;
+ int i=0;
+ int ps;
+ int data_total=0;
+ int noise = 0;
+ int count = 5;
+ int max = 0;
+
+ if(!data)
+ {
+ pr_err("ltr55x_data is null!!\n");
+ return -EFAULT;
+ }
+
+ // wait for register to be stable
+ msleep(15);
+
+ for(i = 0; i < count; i++)
+ {
+ // wait for ps value be stable
+
+ msleep(15);
+
+ ps = ltr55x_ps_read(data->client);
+ if(ps < 0)
+ {
+ i--;
+ continue;
+ }
+
+ if(ps & 0x8000)
+ {
+ noise = 0;
+ break;
+ }
+ else
+ {
+ noise = ps;
+ }
+
+ data_total += ps;
+
+ if(max++ > 10)
+ {
+ pr_err("ltr55x read data error!\n");
+ return -EFAULT;
+ }
+ }
+
+ noise = data_total/count;
+ data->dynamic_noise = noise;
+
+ if(noise < 50)
+ {
+ pdata->prox_threshold = noise+150;
+ pdata->prox_hsyteresis_threshold = noise+100;
+ }
+ else if(noise < 100)
+ {
+ pdata->prox_threshold = noise+160;
+ pdata->prox_hsyteresis_threshold = noise+110;
+ }
+ else if(noise < 600)
+ {
+ pdata->prox_threshold = noise+160;
+ pdata->prox_hsyteresis_threshold = noise+120;
+ }
+ else if(noise < 1500)
+ {
+ pdata->prox_threshold = noise+200;
+ pdata->prox_hsyteresis_threshold = noise+150;
+ }
+ else if(noise < 1650)
+ {
+ pdata->prox_threshold = noise+400;
+ pdata->prox_hsyteresis_threshold = noise+160;
+ }
+ else
+ {
+ pr_err("ltr55x the proximity sensor rubber or structure is error!\n");
+ return -EAGAIN;
+ }
+
+ if(data->ps_state == 1)
+ {
+ ltr55x_set_ps_threshold(data->client, LTR55x_PS_THRES_LOW_0, 0);
+ ltr55x_set_ps_threshold(data->client, LTR55x_PS_THRES_UP_0, data->platform_data->prox_threshold);
+ }
+ else if(data->ps_state == 0)
+ {
+ ltr55x_set_ps_threshold(data->client, LTR55x_PS_THRES_LOW_0, data->platform_data->prox_hsyteresis_threshold);
+ ltr55x_set_ps_threshold(data->client, LTR55x_PS_THRES_UP_0, 0x07ff);
+ }
+
+ data->cali_update = true;
+
+ printk("%s : noise = %d , thd_val_low = %d , htd_val_high = %d \n",__func__, noise, pdata->prox_hsyteresis_threshold, pdata->prox_threshold);
+ return 0;
+}
+#endif
+
+static int ltr55x_suspend(struct device *dev)
+{
+ struct ltr55x_data *data = dev_get_drvdata(dev);
+ int ret = 0;
+
+ printk("%s\n", __func__);
+ mutex_lock(&data->lockw);
+#if 1 // xuke @ 20140828 Can not suspend p-sensor with kernel PM.
+// None.
+#else
+ ret = ltr55x_ps_enable(data->client, 0);
+#endif
+ if(data->als_open_state == 1)
+ ret |= ltr55x_als_enable(data->client, 0);
+ mutex_unlock(&data->lockw);
+ return ret;
+}
+
+static int ltr55x_resume(struct device *dev)
+{
+ struct ltr55x_data *data = dev_get_drvdata(dev);
+ int ret = 0;
+
+ printk("%s\n", __func__);
+ mutex_lock(&data->lockw);
+ if(data->als_open_state == 1)
+ ret = ltr55x_als_enable(data->client, 1);
+#if 1 // xuke @ 20140828 No need to resume p-sensor with kernel PM.
+// None.
+#else
+ if(data->ps_open_state == 1)
+ ret = ltr55x_ps_enable(data->client, 1);
+#endif
+ mutex_unlock(&data->lockw);
+ return ret;
+}
+
+static int ltr55x_check_chip_id(struct i2c_client *client)
+{
+ int id;
+
+ id = i2c_smbus_read_byte_data(client, LTR55x_MANUFACTURER_ID);
+ printk("%s read the LTR55x_MANUFAC_ID is 0x%x\n", __func__, id);
+ if(id != LTR55x_MANUFAC_ID)
+ {
+ return -EINVAL;
+ }
+ return 0;
+}
+
+int ltr55x_device_init(struct i2c_client *client)
+{
+ int retval = 0;
+ int i;
+
+ retval = i2c_smbus_write_byte_data(client, LTR55x_ALS_CONTR, 0x02);//reset chip
+ if(retval < 0)
+ {
+ printk("%s i2c_smbus_write_byte_data(LTR55x_ALS_CONTR, 0x02); ERROR !!!.\n",__func__);
+ }
+
+ msleep(WAKEUP_DELAY);
+ for(i = 2; i < sizeof(reg_tbl)/sizeof(reg_tbl[0]); i++)
+ {
+ if(reg_tbl[i].name == NULL || reg_tbl[i].addr == 0)
+ {
+ break;
+ }
+ if(reg_tbl[i].defval != reg_tbl[i].curval)
+ {
+ if(i < 10)
+ {
+ retval = i2c_smbus_write_byte_data(client, reg_tbl[i].addr, reg_tbl[i].curval);
+ printk("___CAOYI____ ltr55x write 0x%x to addr:0x%x\n", reg_tbl[i].curval,reg_tbl[i].addr);//add by caoyi
+ printk("___CAOYI____ write is OK(0) or Error (-x) ? = return is %d\n", i2c_smbus_write_byte_data(client, reg_tbl[i].addr, reg_tbl[i].curval));
+ }
+ else
+ {
+ retval = i2c_smbus_write_byte_data(client, reg_tbl[i].addr, reg_tbl[i].curval & 0xff);
+ printk("___CAOYI____ ltr55x write 0x%x to addr:0x%x\n", reg_tbl[i].curval & 0xff,reg_tbl[i].addr);//add by caoyi
+ retval = i2c_smbus_write_byte_data(client, reg_tbl[i].addr + 1, reg_tbl[i].curval >> 8);
+ printk("___CAOYI____ ltr55x write 0x%x to addr:0x%x\n", reg_tbl[i].curval >> 8,reg_tbl[i].addr);//add by caoyi
+ }
+ }
+ }
+
+ printk("___CAOYI______ read addr 0x9e is 0x%x\n",i2c_smbus_read_byte_data(client, 0x9e)); //add by caoyi
+ printk("___CAOYI______ read addr 0x90 is 0x%x\n",i2c_smbus_read_byte_data(client, 0x90)); //add by caoyi
+
+
+ printk("___CAOYI______|||| read is OK(+x) or Error (-x) ? = return is %d\n", i2c_smbus_read_byte_data(client, 0x1a));
+ printk("___CAOYI______|||| write is OK(0) or Error (-x) ? = return is %d\n", i2c_smbus_write_byte_data(client, 0x1b,0x88));
+ printk("___CAOYI______|||| write is OK(0) or Error (-x) ? = return is %d\n", i2c_smbus_write_byte_data(client, 0x11,0x88));
+
+ return retval;
+}
+
+static int sensor_regulator_configure(struct ltr55x_data *data, bool on)
+{
+ int rc;
+
+ if(!on)
+ {
+ if(regulator_count_voltages(data->vdd) > 0)
+ regulator_set_voltage(data->vdd, 0, LTR55x_VDD_MAX_UV);
+ regulator_put(data->vdd);
+
+ if(regulator_count_voltages(data->vio) > 0)
+ regulator_set_voltage(data->vio, 0, LTR55x_VIO_MAX_UV);
+ regulator_put(data->vio);
+ }
+ else
+ {
+ data->vdd = regulator_get(&data->client->dev, "vdd");
+ if(IS_ERR(data->vdd))
+ {
+ rc = PTR_ERR(data->vdd);
+ dev_err(&data->client->dev,"Regulator get failed vdd rc=%d\n", rc);
+ return rc;
+ }
+
+ if(regulator_count_voltages(data->vdd) > 0)
+ {
+ rc = regulator_set_voltage(data->vdd, LTR55x_VDD_MIN_UV, LTR55x_VDD_MAX_UV);
+ if(rc)
+ {
+ dev_err(&data->client->dev,"Regulator set failed vdd rc=%d\n",rc);
+ goto reg_vdd_put;
+ }
+ }
+
+ data->vio = regulator_get(&data->client->dev, "vio");
+ if(IS_ERR(data->vio))
+ {
+ rc = PTR_ERR(data->vio);
+ dev_err(&data->client->dev,"Regulator get failed vio rc=%d\n", rc);
+ goto reg_vdd_set;
+ }
+
+ if(regulator_count_voltages(data->vio) > 0)
+ {
+ rc = regulator_set_voltage(data->vio, LTR55x_VIO_MIN_UV, LTR55x_VIO_MAX_UV);
+ if(rc)
+ {
+ dev_err(&data->client->dev, "Regulator set failed vio rc=%d\n", rc);
+ goto reg_vio_put;
+ }
+ }
+ }
+
+ return 0;
+ reg_vio_put:
+ regulator_put(data->vio);
+
+ reg_vdd_set:
+ if(regulator_count_voltages(data->vdd) > 0)
+ regulator_set_voltage(data->vdd, 0, LTR55x_VDD_MAX_UV);
+ reg_vdd_put:
+ regulator_put(data->vdd);
+ return rc;
+}
+
+static int sensor_regulator_power_on(struct ltr55x_data *data, bool on)
+{
+ int rc = 0;
+
+ if(!on)
+ {
+ rc = regulator_disable(data->vdd);
+ if(rc)
+ {
+ dev_err(&data->client->dev, "Regulator vdd disable failed rc=%d\n", rc);
+ return rc;
+ }
+
+ rc = regulator_disable(data->vio);
+ if(rc)
+ {
+ dev_err(&data->client->dev, "Regulator vio disable failed rc=%d\n", rc);
+ rc = regulator_enable(data->vdd);
+ dev_err(&data->client->dev, "Regulator vio re-enabled rc=%d\n", rc);
+ /*
+ * Successfully re-enable regulator.
+ * Enter poweron delay and returns error.
+ */
+ if(!rc)
+ {
+ rc = -EBUSY;
+ goto enable_delay;
+ }
+ }
+ return rc;
+ }
+ else
+ {
+ rc = regulator_enable(data->vdd);
+ if(rc)
+ {
+ dev_err(&data->client->dev, "Regulator vdd enable failed rc=%d\n", rc);
+ return rc;
+ }
+
+ rc = regulator_enable(data->vio);
+ if(rc)
+ {
+ dev_err(&data->client->dev, "Regulator vio enable failed rc=%d\n", rc);
+ regulator_disable(data->vdd);
+ return rc;
+ }
+ }
+
+ enable_delay:
+ msleep(130);
+ dev_dbg(&data->client->dev, "Sensor regulator power on =%d\n", on);
+ return rc;
+}
+
+static int sensor_platform_hw_power_onoff(struct ltr55x_data *data, bool on)
+{
+ int err = 0;
+
+ if(data->power_state != on)
+ {
+ if(on)
+ {
+ if(!IS_ERR_OR_NULL(data->pinctrl))
+ {
+ err = pinctrl_select_state(data->pinctrl, data->pin_default);
+ if(err)
+ {
+ dev_err(&data->client->dev, "Can't select pinctrl state on=%d\n", on);
+ goto power_out;
+ }
+ }
+
+ err = sensor_regulator_configure(data, true);
+ if(err)
+ {
+ dev_err(&data->client->dev, "unable to configure regulator on=%d\n", on);
+ goto power_out;
+ }
+
+ err = sensor_regulator_power_on(data, true);
+ if(err)
+ {
+ dev_err(&data->client->dev, "Can't configure regulator on=%d\n", on);
+ goto power_out;
+ }
+
+ data->power_state = true;
+ }
+ else
+ {
+ if(!IS_ERR_OR_NULL(data->pinctrl))
+ {
+ err = pinctrl_select_state(data->pinctrl, data->pin_sleep);
+ if(err)
+ {
+ dev_err(&data->client->dev, "Can't select pinctrl state on=%d\n", on);
+ goto power_out;
+ }
+ }
+
+ err = sensor_regulator_power_on(data, false);
+ if(err)
+ {
+ dev_err(&data->client->dev, "Can't configure regulator on=%d\n", on);
+ goto power_out;
+ }
+
+ err = sensor_regulator_configure(data, false);
+ if(err)
+ {
+ dev_err(&data->client->dev, "unable to configure regulator on=%d\n", on);
+ goto power_out;
+ }
+
+ data->power_state = false;
+ }
+ }
+ power_out:
+ return err;
+}
+#if 0
+static int ltr55x_pinctrl_init(struct ltr55x_data *data)
+{
+ struct i2c_client *client = data->client;
+
+ data->pinctrl = devm_pinctrl_get(&client->dev);
+ if(IS_ERR_OR_NULL(data->pinctrl))
+ {
+ dev_err(&client->dev, "Failed to get pinctrl\n");
+ return PTR_ERR(data->pinctrl);
+ }
+ data->pin_default = pinctrl_lookup_state(data->pinctrl, "default");
+ if(IS_ERR_OR_NULL(data->pin_default))
+ {
+ dev_err(&client->dev, "Failed to look up default state\n");
+ return PTR_ERR(data->pin_default);
+ }
+
+ data->pin_sleep = pinctrl_lookup_state(data->pinctrl, "sleep");
+ if(IS_ERR_OR_NULL(data->pin_sleep))
+ {
+ dev_err(&client->dev, "Failed to look up sleep state\n");
+ return PTR_ERR(data->pin_sleep);
+ }
+
+
+ return 0;
+}
+#endif
+
+static int ltr55x_parse_dt(struct device *dev, struct ltr55x_data *data)
+{
+ struct ltr55x_platform_data *pdata = data->platform_data;
+ struct device_node *np = dev->of_node;
+ unsigned int tmp;
+ int rc = 0;
+
+ /* ps tuning data*/
+ rc = of_property_read_u32(np, "ps-threshold", &tmp);
+ if(rc)
+ {
+ dev_err(dev, "Unable to read ps threshold\n");
+ return rc;
+ }
+ pdata->prox_threshold = tmp;
+
+ rc = of_property_read_u32(np, "ps-hysteresis-threshold", &tmp);
+ if(rc)
+ {
+ dev_err(dev, "Unable to read ps hysteresis threshold\n");
+ return rc;
+ }
+ pdata->prox_hsyteresis_threshold = tmp;
+
+ rc = of_property_read_u32(np, "als-polling-time", &tmp);
+ if(rc)
+ {
+ dev_err(dev, "Unable to read ps hysteresis threshold\n");
+ return rc;
+ }
+ pdata->als_poll_interval = tmp;
+
+ return 0;
+}
+
+int ltr55x_probe(struct i2c_client *client, const struct i2c_device_id *id)
+{
+ struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent);
+ struct ltr55x_data *data;
+ struct ltr55x_platform_data *pdata;
+ int ret = 0;
+
+ /* check i2c*/
+ if(!i2c_check_functionality(adapter,I2C_FUNC_SMBUS_WRITE_BYTE | I2C_FUNC_SMBUS_READ_BYTE_DATA))
+ {
+ dev_err(&client->dev, "+++++++++++++++++++++++++++++++++++++++LTR-55xALS functionality check failed.\n");
+ return -EIO;
+ }
+
+ /* platform data memory allocation*/
+ if(client->dev.of_node)
+ {
+ pdata = devm_kzalloc(&client->dev,sizeof(struct ltr55x_platform_data),GFP_KERNEL);
+ if(!pdata)
+ {
+ dev_err(&client->dev, "+++++++++++++++++++++++++++++++++++++++LTR-55xALSFailed to allocate memory\n");
+ return -ENOMEM;
+ }
+
+ client->dev.platform_data = pdata;
+ }
+ else
+ {
+ pdata = client->dev.platform_data;
+ if(!pdata)
+ {
+ dev_err(&client->dev, "+++++++++++++++++++++++++++++++++++++++No platform data\n");
+ return -ENODEV;
+ }
+ }
+
+ /* data memory allocation */
+ data = kzalloc(sizeof(struct ltr55x_data), GFP_KERNEL);
+ if(!data)
+ {
+ dev_err(&client->dev, "+++++++++++++++++++++++++++++++++++++++kzalloc failed\n");
+ ret = -ENOMEM;
+ goto exit_kfree_pdata;
+ }
+ data->client = client;
+ data->platform_data = pdata;
+ ret = ltr55x_parse_dt(&client->dev, data);
+ if(ret)
+ {
+ dev_err(&client->dev, "+++++++++++++++++++++++++++++++++++++++can't parse platform data\n");
+ ret = -EFAULT;
+ goto exit_kfree_data;
+ }
+
+ #if 0
+ /* pinctrl initialization */
+ ret = ltr55x_pinctrl_init(data);
+ if(ret)
+ {
+ ret = -EFAULT;
+ dev_err(&client->dev, "+++++++++++++++++++++++++++++++++++++++Can't initialize pinctrl\n");
+ goto exit_kfree_data;
+ }
+ #endif
+
+ /* power initialization */
+ ret = sensor_platform_hw_power_onoff(data, true);
+ if(ret)
+ {
+ ret = -ENOEXEC;
+ dev_err(&client->dev,"+++++++++++++++++++++++++++++++++++++++power on fail\n");
+ goto exit_kfree_data;
+ }
+ /* set client data as ltr55x_data*/
+ i2c_set_clientdata(client, data);
+
+
+ ret = ltr55x_check_chip_id(client);
+ if(ret)
+ {
+ ret = -ENXIO;
+ dev_err(&client->dev,"+++++++++++++++++++++++++++++++++++++++the manufacture id is not match\n");
+ goto exit_power_off;
+ }
+
+
+ ret = ltr55x_device_init(client);
+ if(ret)
+ {
+ ret = -ENXIO;
+ dev_err(&client->dev,"+++++++++++++++++++++++++++++++++++++++device init failed\n");
+ goto exit_power_off;
+ }
+
+ /* request gpio and irq */
+ ret = ltr55x_gpio_irq(data);
+ if(ret)
+ {
+ ret = -ENXIO;
+ dev_err(&client->dev,"+++++++++++++++++++++++++++++++++++++++gpio_irq failed\n");
+ goto exit_chip_reset;
+ }
+ /* Register Input Device */
+ data->input_dev_als = input_allocate_device();
+ if(!data->input_dev_als)
+ {
+ ret = -ENOMEM;
+ dev_err(&client->dev,"+++++++++++++++++++++++++++++++++++++++Failed to allocate input device als\n");
+ goto exit_free_irq;
+ }
+ data->input_dev_ps = input_allocate_device();
+ if(!data->input_dev_ps)
+ {
+ ret = -ENOMEM;
+ dev_err(&client->dev,"+++++++++++++++++++++++++++++++++++++++Failed to allocate input device ps\n");
+ goto exit_free_dev_als;
+ }
+ set_bit(EV_ABS, data->input_dev_als->evbit);
+ set_bit(EV_ABS, data->input_dev_ps->evbit);
+ input_set_abs_params(data->input_dev_als, ABS_MISC, 0, 65535, 0, 0);
+ input_set_abs_params(data->input_dev_ps, ABS_DISTANCE, 0, 1, 0, 0);
+ data->input_dev_als->name = LIGHT_SENSOR_NAME ;
+ data->input_dev_ps->name = PROXIMITY_SENSOR_NAME;
+ data->input_dev_als->id.bustype = BUS_I2C;
+ data->input_dev_als->dev.parent =&data->client->dev;
+ data->input_dev_ps->id.bustype = BUS_I2C;
+ data->input_dev_ps->dev.parent =&data->client->dev;
+ input_set_drvdata(data->input_dev_als, data);
+ input_set_drvdata(data->input_dev_ps, data);
+
+ ret = input_register_device(data->input_dev_als);
+ if(ret)
+ {
+ ret = -ENOMEM;
+ dev_err(&client->dev,"Unable to register input device als: %s\n",data->input_dev_als->name);
+ goto exit_free_dev_ps;
+ }
+
+ ret = input_register_device(data->input_dev_ps);
+ if(ret)
+ {
+ ret = -ENOMEM;
+ dev_err(&client->dev,"Unable to register input device ps: %s\n",data->input_dev_ps->name);
+ goto exit_unregister_dev_als;
+ }
+ printk("%s input device success.\n",__func__);
+
+ /* init delayed works */
+ INIT_DELAYED_WORK(&data->ps_work, ltr55x_ps_work_func);
+ INIT_DELAYED_WORK(&data->als_work, ltr55x_als_work_func);
+
+ /* init mutex */
+ mutex_init(&data->lockw);
+ mutex_init(&data->op_lock);
+
+ /* create sysfs group */
+ ret = sysfs_create_group(&client->dev.kobj, &ltr55x_attr_group);
+ if(ret)
+ {
+ ret = -EROFS;
+ dev_err(&client->dev,"+++++++++++++++++++Unable to creat sysfs group\n");
+ goto exit_unregister_dev_ps;
+ }
+
+ /* Register sysfs hooks in input devices */
+ ret = sysfs_create_group(&data->input_dev_als->dev.kobj, &ltr55x_als_attr_group);
+ if (ret)
+ {
+ dev_err(&client->dev,"+++++++++++++++++++Unable to creat sysfs group for als in input node\n");
+ goto exit_unregister_dev_ps;
+ }
+
+ ret = sysfs_create_group(&data->input_dev_ps->dev.kobj, &ltr55x_ps_attr_group);
+ if (ret)
+ {
+ dev_err(&client->dev,"+++++++++++++++++++Unable to creat sysfs group for ps in input node\n");
+ goto exit_unregister_dev_ps;
+ }
+
+ data->dynamic_noise = 0;
+
+ dev_dbg(&client->dev,"probe succece\n");
+ return 0;
+ #if 0
+ exit_remove_sysfs_group:
+ sysfs_remove_group(&client->dev.kobj, &ltr55x_attr_group);
+ sysfs_remove_group(&data->input_dev_ps->dev.kobj, &ltr55x_ps_attr_group);
+ sysfs_remove_group(&data->input_dev_als->dev.kobj, &ltr55x_als_attr_group);
+ #endif
+ exit_unregister_dev_ps:
+ input_unregister_device(data->input_dev_ps);
+ exit_unregister_dev_als:
+ input_unregister_device(data->input_dev_als);
+ exit_free_dev_ps:
+ if(data->input_dev_ps)
+ input_free_device(data->input_dev_ps);
+ exit_free_dev_als:
+ if(data->input_dev_als)
+ input_free_device(data->input_dev_als);
+ exit_free_irq:
+ ltr55x_gpio_irq_free(data);
+ exit_chip_reset:
+ ltr55x_chip_reset(client);
+ exit_power_off:
+ sensor_platform_hw_power_onoff(data, false);
+ exit_kfree_data:
+ kfree(data);
+ exit_kfree_pdata:
+ if(pdata && (client->dev.of_node))
+ devm_kfree(&client->dev, pdata);
+ data->platform_data= NULL;
+
+ return ret;
+}
+
+static int ltr55x_remove(struct i2c_client *client)
+{
+ struct ltr55x_data *data = i2c_get_clientdata(client);
+ struct ltr55x_platform_data *pdata=data->platform_data;
+
+ if(data == NULL || pdata == NULL)
+ return 0;
+
+ ltr55x_ps_enable(client, 0);
+ ltr55x_als_enable(client, 0);
+ input_unregister_device(data->input_dev_als);
+ input_unregister_device(data->input_dev_ps);
+
+ input_free_device(data->input_dev_als);
+ input_free_device(data->input_dev_ps);
+
+ ltr55x_gpio_irq_free(data);
+
+ sysfs_remove_group(&client->dev.kobj, &ltr55x_attr_group);
+
+ cancel_delayed_work_sync(&data->ps_work);
+ cancel_delayed_work_sync(&data->als_work);
+
+ if(pdata && (client->dev.of_node))
+ devm_kfree(&client->dev, pdata);
+ pdata = NULL;
+
+ kfree(data);
+ data = NULL;
+
+ return 0;
+}
+
+static struct i2c_device_id ltr55x_id[] = {
+ {"ltr55x", 0},
+ {}
+};
+
+static struct of_device_id ltr_match_table[] = {
+ { .compatible = "LITEON,ltr_55x",},
+ {},
+};
+
+MODULE_DEVICE_TABLE(i2c, ltr55x_id);
+static SIMPLE_DEV_PM_OPS(ltr55x_pm_ops, ltr55x_suspend, ltr55x_resume);
+static struct i2c_driver ltr55x_driver = {
+ .driver = {
+ .name = LTR55x_DRV_NAME,
+ .owner = THIS_MODULE,
+ .pm = &ltr55x_pm_ops,
+ .of_match_table = ltr_match_table,
+ },
+ .probe = ltr55x_probe,
+ .remove = ltr55x_remove,
+ .id_table = ltr55x_id,
+};
+
+static int ltr55x_driver_init(void)
+{
+ pr_info("Driver ltr55x0 init.\n");
+ return i2c_add_driver(&ltr55x_driver);
+};
+
+static void ltr55x_driver_exit(void)
+{
+ pr_info("Unload ltr55x module...\n");
+ i2c_del_driver(&ltr55x_driver);
+}
+
+module_init(ltr55x_driver_init);
+module_exit(ltr55x_driver_exit);
+MODULE_AUTHOR("Lite-On Technology Corp.");
+MODULE_DESCRIPTION("Lite-On LTR-55x Proximity and Light Sensor Driver");
+MODULE_LICENSE("GPL v2");
+MODULE_VERSION("1.0");
+
diff --git a/drivers/hwmon/ltr55x.h b/drivers/hwmon/ltr55x.h
new file mode 100755
index 00000000000..f2d8a845e05
--- /dev/null
+++ b/drivers/hwmon/ltr55x.h
@@ -0,0 +1,83 @@
+/* Lite-On LTR-55xALS Linux Driver
+*
+* Copyright (C) 2011 Lite-On Technology Corp (Singapore)
+*
+* 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.
+*
+*/
+#ifndef _LTR55x_H
+#define _LTR55x_H
+
+struct ltr55x_platform_data {
+ unsigned int prox_threshold;
+ unsigned int prox_hsyteresis_threshold;
+
+ unsigned int als_poll_interval;
+
+ unsigned int int_gpio;
+ unsigned int irq_gpio_flags;
+};
+
+/* POWER SUPPLY VOLTAGE RANGE */
+#define LTR55x_VDD_MIN_UV 2000000
+#define LTR55x_VDD_MAX_UV 3300000
+#define LTR55x_VIO_MIN_UV 1750000
+#define LTR55x_VIO_MAX_UV 1950000
+
+/* LTR-55x Registers */
+#define LTR55x_ALS_CONTR 0x80
+#define LTR55x_PS_CONTR 0x81
+#define LTR55x_PS_LED 0x82
+#define LTR55x_PS_N_PULSES 0x83
+#define LTR55x_PS_MEAS_RATE 0x84
+#define LTR55x_ALS_MEAS_RATE 0x85
+#define LTR55x_MANUFACTURER_ID 0x87
+
+#define LTR55x_INTERRUPT 0x8F
+#define LTR55x_PS_THRES_UP_0 0x90
+#define LTR55x_PS_THRES_UP_1 0x91
+#define LTR55x_PS_THRES_LOW_0 0x92
+#define LTR55x_PS_THRES_LOW_1 0x93
+
+#define LTR55x_ALS_THRES_UP_0 0x97
+#define LTR55x_ALS_THRES_UP_1 0x98
+#define LTR55x_ALS_THRES_LOW_0 0x99
+#define LTR55x_ALS_THRES_LOW_1 0x9A
+#define LTR55x_INTERRUPT_PERSIST 0x9E
+
+/* 55x's Read Only Registers */
+#define LTR55x_ALS_DATA_CH1_0 0x88
+#define LTR55x_ALS_DATA_CH1_1 0x89
+#define LTR55x_ALS_DATA_CH0_0 0x8A
+#define LTR55x_ALS_DATA_CH0_1 0x8B
+#define LTR55x_ALS_PS_STATUS 0x8C
+#define LTR55x_PS_DATA_0 0x8D
+#define LTR55x_PS_DATA_1 0x8E
+
+/* Basic Operating Modes */
+#define MODE_ALS_ON_Range1 0x0B
+#define MODE_ALS_ON_Range2 0x03
+#define MODE_ALS_StdBy 0x00
+#define MODE_PS_ON_Gain1 0x03
+#define MODE_PS_ON_Gain2 0x07
+#define MODE_PS_ON_Gain4 0x0B
+#define MODE_PS_ON_Gain8 0x0C
+#define MODE_PS_StdBy 0x00
+
+#define PS_RANGE1 1
+#define PS_RANGE2 2
+#define PS_RANGE4 4
+#define PS_RANGE8 8
+#define ALS_RANGE1_320 1
+#define ALS_RANGE2_64K 2
+
+#define PS_DETECTED_THRES 200 //0x00ff//0x00E0
+#define PS_UNDETECTED_THRES 180 //0x00ef//0x0046
+
+/* Power On response time in ms */
+#define PON_DELAY 600
+#define WAKEUP_DELAY 10
+
+#endif