Mullet: Changes for New Light Sensor
Integrated OPT3001 light sensor
Bug: 68972256
Change-Id: I9c584b56733ce46b528e751854e34eb52b0f41c9
diff --git a/arch/arm/boot/dts/qcom/apq8009-deacon-discreet.dtsi b/arch/arm/boot/dts/qcom/apq8009-deacon-discreet.dtsi
index 8e028df..a842dd4 100644
--- a/arch/arm/boot/dts/qcom/apq8009-deacon-discreet.dtsi
+++ b/arch/arm/boot/dts/qcom/apq8009-deacon-discreet.dtsi
@@ -63,6 +63,11 @@
};
};
+ ti_opt3001: opt3001@44 {
+ compatible = "ti,opt3001";
+ reg = <0x44>;
+ };
+
};
i2c@78b8000 { /* BLSP1 QUP4 */
diff --git a/arch/arm/configs/msm8909w-perf_defconfig b/arch/arm/configs/msm8909w-perf_defconfig
index afd6dcf..e433b2a 100644
--- a/arch/arm/configs/msm8909w-perf_defconfig
+++ b/arch/arm/configs/msm8909w-perf_defconfig
@@ -505,6 +505,7 @@
CONFIG_QMI_ENCDEC=y
CONFIG_IIO=y
CONFIG_TSL2584=y
+CONFIG_OPT3001=y
CONFIG_INPUT_DRV260X_HAPTICS=y
CONFIG_FRAME_WARN=2048
CONFIG_PRIMA=y
diff --git a/arch/arm/configs/msm8909w_defconfig b/arch/arm/configs/msm8909w_defconfig
index 78e4e93..29c02b2 100644
--- a/arch/arm/configs/msm8909w_defconfig
+++ b/arch/arm/configs/msm8909w_defconfig
@@ -537,6 +537,7 @@
CONFIG_CRYPTO_SHA256_ARM=y
CONFIG_IIO=y
CONFIG_TSL2584=y
+CONFIG_OPT3001=y
CONFIG_INPUT_DRV260X_HAPTICS=y
CONFIG_FRAME_WARN=2048
CONFIG_PRIMA=y
diff --git a/drivers/iio/light/Kconfig b/drivers/iio/light/Kconfig
index abacafe..8743940 100644
--- a/drivers/iio/light/Kconfig
+++ b/drivers/iio/light/Kconfig
@@ -189,6 +189,13 @@
Provides support for the AMS-TAOS tsl2584tsv device.
Access ALS data via iio, sysfs.
+config OPT3001
+ tristate "Texas Instruments OPT3001 Light Sensor`"
+ depends on IIO && I2C
+ help
+ Provides support for the exas Instruments OPT3001 device.
+ Acces ALS data via iio, sysfs.
+
config VCNL4000
tristate "VCNL4000 combined ALS and proximity sensor"
depends on I2C
diff --git a/drivers/iio/light/Makefile b/drivers/iio/light/Makefile
index 8e9f5de..8ae045e 100644
--- a/drivers/iio/light/Makefile
+++ b/drivers/iio/light/Makefile
@@ -19,4 +19,5 @@
obj-$(CONFIG_TCS3472) += tcs3472.o
obj-$(CONFIG_TSL4531) += tsl4531.o
obj-$(CONFIG_TSL2584) += tsl2584.o
+obj-$(CONFIG_OPT3001) += opt3001.o
obj-$(CONFIG_VCNL4000) += vcnl4000.o
diff --git a/drivers/iio/light/opt3001.c b/drivers/iio/light/opt3001.c
new file mode 100644
index 0000000..e8f7f39
--- /dev/null
+++ b/drivers/iio/light/opt3001.c
@@ -0,0 +1,1235 @@
+/**
+ * opt3001.c - Texas Instruments OPT3001 Light Sensor
+ *
+ * Copyright (C) 2014 Texas Instruments Incorporated - http://www.ti.com
+ *
+ * Author: Andreas Dannenberg <dannenberg@ti.com>
+ * Based on previous work from: Felipe Balbi <balbi@ti.com>
+ *
+ * This program is free software: you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 of the License
+ * as published by the Free Software Foundation.
+ *
+ * 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.
+ */
+
+#include <linux/bitops.h>
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/i2c.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/slab.h>
+#include <linux/types.h>
+
+#include <linux/iio/events.h>
+#include <linux/iio/iio.h>
+#include <linux/iio/sysfs.h>
+#include <linux/input.h>
+#include <linux/sensors.h>
+#include <linux/workqueue.h>
+#include <asm/uaccess.h>
+#include <linux/fs.h>
+
+#define OPT3001_RESULT 0x00
+#define OPT3001_CONFIGURATION 0x01
+#define OPT3001_LOW_LIMIT 0x02
+#define OPT3001_HIGH_LIMIT 0x03
+#define OPT3001_MANUFACTURER_ID 0x7e
+#define OPT3001_DEVICE_ID 0x7f
+
+#define OPT3001_CONFIGURATION_RN_MASK (0xf << 12)
+#define OPT3001_CONFIGURATION_RN_AUTO (0xc << 12)
+
+#define OPT3001_CONFIGURATION_CT BIT(11)
+
+#define OPT3001_CONFIGURATION_M_MASK (3 << 9)
+#define OPT3001_CONFIGURATION_M_SHUTDOWN (0 << 9)
+#define OPT3001_CONFIGURATION_M_SINGLE (1 << 9)
+#define OPT3001_CONFIGURATION_M_CONTINUOUS (2 << 9) /* also 3 << 9 */
+
+#define OPT3001_CONFIGURATION_OVF BIT(8)
+#define OPT3001_CONFIGURATION_CRF BIT(7)
+#define OPT3001_CONFIGURATION_FH BIT(6)
+#define OPT3001_CONFIGURATION_FL BIT(5)
+#define OPT3001_CONFIGURATION_L BIT(4)
+#define OPT3001_CONFIGURATION_POL BIT(3)
+#define OPT3001_CONFIGURATION_ME BIT(2)
+
+#define OPT3001_CONFIGURATION_FC_MASK (3 << 0)
+
+/* The end-of-conversion enable is located in the low-limit register */
+#define OPT3001_LOW_LIMIT_EOC_ENABLE 0xc000
+
+#define OPT3001_REG_EXPONENT(n) ((n) >> 12)
+#define OPT3001_REG_MANTISSA(n) ((n) & 0xfff)
+
+#define OPT3001_INT_TIME_LONG 800000
+#define OPT3001_INT_TIME_SHORT 100000
+
+/*
+ * Time to wait for conversion result to be ready. The device datasheet
+ * sect. 6.5 states results are ready after total integration time plus 3ms.
+ * This results in worst-case max values of 113ms or 883ms, respectively.
+ * Add some slack to be on the safe side.
+ */
+#define OPT3001_RESULT_READY_SHORT 150
+#define OPT3001_RESULT_READY_LONG 1000
+
+/* OPT3001 polling rate in ms */
+#define OPT3001_LS_MIN_POLL_DELAY 1
+#define OPT3001_LS_MAX_POLL_DELAY 1000
+#define OPT3001_LS_DEFAULT_POLL_DELAY 100
+
+#define CAL_LUX 600
+#define CAL_FILE "/persist/sensors/lightsensor_calibration.dat"
+
+void opt3001_get_cal_data(struct iio_dev *iio);
+int opt3001_get_pure_lux(struct iio_dev *iio);
+
+struct opt3001_info {
+ bool enabled;
+ int poll_delay;
+};
+struct opt3001_setting {
+ int caliberated_value;
+};
+
+struct opt3001 {
+ struct i2c_client *client;
+ struct device *dev;
+
+ struct mutex lock;
+ bool ok_to_ignore_lock;
+ bool result_ready;
+ wait_queue_head_t result_ready_queue;
+ u16 result;
+
+ u32 int_time;
+ u32 mode;
+
+ u16 high_thresh_mantissa;
+ u16 low_thresh_mantissa;
+
+ u8 high_thresh_exp;
+ u8 low_thresh_exp;
+
+ bool use_irq;
+ struct sensors_classdev als_cdev;
+ struct opt3001_info *pdata;
+ struct mutex io_lock;
+ struct input_dev *ls_input_dev;
+ struct hrtimer als_timer;
+ struct work_struct als_work;
+ struct workqueue_struct *als_wq;
+ struct opt3001_setting setting;
+ int flush_count;
+ bool cal_status;
+
+};
+
+struct opt3001_scale {
+ int val;
+ int val2;
+};
+
+static const struct opt3001_scale opt3001_scales[] = {
+ {
+ .val = 40,
+ .val2 = 950000,
+ },
+ {
+ .val = 81,
+ .val2 = 900000,
+ },
+ {
+ .val = 163,
+ .val2 = 800000,
+ },
+ {
+ .val = 327,
+ .val2 = 600000,
+ },
+ {
+ .val = 655,
+ .val2 = 200000,
+ },
+ {
+ .val = 1310,
+ .val2 = 400000,
+ },
+ {
+ .val = 2620,
+ .val2 = 800000,
+ },
+ {
+ .val = 5241,
+ .val2 = 600000,
+ },
+ {
+ .val = 10483,
+ .val2 = 200000,
+ },
+ {
+ .val = 20966,
+ .val2 = 400000,
+ },
+ {
+ .val = 83865,
+ .val2 = 600000,
+ },
+};
+
+static struct sensors_classdev sensors_light_cdev = {
+ .name = "opt3001-light",
+ .vendor = "TI",
+ .version = 1,
+ .handle = SENSORS_LIGHT_HANDLE,
+ .type = SENSOR_TYPE_LIGHT,
+ .max_range = "800",
+ .resolution = "0.0125",
+ .sensor_power = "0.15",
+ .min_delay = 0,
+ .fifo_reserved_event_count = 0,
+ .fifo_max_event_count = 0,
+ .enabled = 0,
+ .delay_msec = OPT3001_LS_DEFAULT_POLL_DELAY,
+ .sensors_enable = NULL,
+ .sensors_poll_delay = NULL,
+};
+
+static int opt3001_find_scale(const struct opt3001 *opt, int val,
+ int val2, u8 *exponent)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(opt3001_scales); i++) {
+ const struct opt3001_scale *scale = &opt3001_scales[i];
+
+ /*
+ * Combine the integer and micro parts for comparison
+ * purposes. Use milli lux precision to avoid 32-bit integer
+ * overflows.
+ */
+ if ((val * 1000 + val2 / 1000) <=
+ (scale->val * 1000 + scale->val2 / 1000)) {
+ *exponent = i;
+ return 0;
+ }
+ }
+
+ return -EINVAL;
+}
+
+static void opt3001_to_iio_ret(struct opt3001 *opt, u8 exponent,
+ u16 mantissa, int *val, int *val2)
+{
+ int lux;
+
+ lux = 10 * (mantissa << exponent);
+ *val = lux / 1000;
+ *val2 = (lux - (*val * 1000)) * 1000;
+}
+
+static void opt3001_set_mode(struct opt3001 *opt, u16 *reg, u16 mode)
+{
+ *reg &= ~OPT3001_CONFIGURATION_M_MASK;
+ *reg |= mode;
+ opt->mode = mode;
+}
+
+static IIO_CONST_ATTR_INT_TIME_AVAIL("0.1 0.8");
+
+static ssize_t opt3001_als_trim_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct iio_dev *iio = dev_to_iio_dev(dev);
+ struct opt3001 *als_data = iio_priv(iio);
+
+ return sprintf(buf, "%d\n", als_data->setting.caliberated_value);
+}
+
+static ssize_t opt3001_als_trim_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t len)
+{
+ struct iio_dev *iio = dev_to_iio_dev(dev);
+ struct opt3001 *als_data = iio_priv(iio);
+ int value;
+
+ if (kstrtoint(buf, 0, &value))
+ return -EINVAL;
+
+ if (value)
+ als_data->setting.caliberated_value = value;
+
+ return len;
+}
+
+static ssize_t opt3001_do_calibrate(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t len)
+{
+ struct iio_dev *iio = dev_to_iio_dev(dev);
+ struct opt3001 *opt = iio_priv(iio);
+ int value;
+ int ret;
+ u16 reg;
+
+ ret = i2c_smbus_read_word_swapped(opt->client, OPT3001_CONFIGURATION);
+ if (ret < 0) {
+ dev_err(opt->dev, "failed to read register %02x with 800ms, hence fall back to 100ms\n",
+ OPT3001_CONFIGURATION);
+ }
+
+ if (kstrtoint(buf, 0, &value))
+ return -EINVAL;
+
+ if (value == 1) {
+ /* Set time as 800ms for proper caliberation data */
+ reg = ret;
+ reg |= OPT3001_CONFIGURATION_CT;
+ ret = i2c_smbus_write_word_swapped(opt->client, OPT3001_CONFIGURATION, reg);
+ if(ret < 0)
+ pr_err("Time set for 800ms failed\n");
+ opt->int_time = OPT3001_INT_TIME_LONG;
+ msleep(100);
+ opt3001_get_cal_data(iio);
+ /* Set time to 100ms read lux value */
+ reg &= ~OPT3001_CONFIGURATION_CT;
+ i2c_smbus_write_word_swapped(opt->client, OPT3001_CONFIGURATION, reg);
+ ret = i2c_smbus_write_word_swapped(opt->client, OPT3001_CONFIGURATION, reg);
+ if(ret < 0)
+ pr_err("Time set for 100ms failed\n");
+ opt->int_time = OPT3001_INT_TIME_SHORT;
+ }
+
+ return len;
+}
+
+static ssize_t opt3001_lux_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct iio_dev *iio = dev_to_iio_dev(dev);
+
+ return sprintf(buf, "%d\n", opt3001_get_pure_lux(iio));
+}
+
+static DEVICE_ATTR(illuminance0_input, S_IRUGO, opt3001_lux_show, NULL);
+static DEVICE_ATTR(illuminance0_calibrate, S_IWUSR, NULL, opt3001_do_calibrate);
+static DEVICE_ATTR(illuminance0_calibbias, S_IRUGO | S_IWUSR,
+ opt3001_als_trim_show, opt3001_als_trim_store);
+
+static struct attribute *opt3001_attributes[] = {
+ &iio_const_attr_integration_time_available.dev_attr.attr,
+ &dev_attr_illuminance0_calibrate.attr,
+ &dev_attr_illuminance0_calibbias.attr,
+ &dev_attr_illuminance0_input.attr,
+ NULL
+};
+
+static const struct attribute_group opt3001_attribute_group = {
+ .attrs = opt3001_attributes,
+};
+
+static const struct iio_event_spec opt3001_event_spec[] = {
+ {
+ .type = IIO_EV_TYPE_THRESH,
+ .dir = IIO_EV_DIR_RISING,
+ .mask_separate = BIT(IIO_EV_INFO_VALUE) |
+ BIT(IIO_EV_INFO_ENABLE),
+ },
+ {
+ .type = IIO_EV_TYPE_THRESH,
+ .dir = IIO_EV_DIR_FALLING,
+ .mask_separate = BIT(IIO_EV_INFO_VALUE) |
+ BIT(IIO_EV_INFO_ENABLE),
+ },
+};
+
+static const struct iio_chan_spec opt3001_channels[] = {
+ {
+ .type = IIO_LIGHT,
+ .info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED) |
+ BIT(IIO_CHAN_INFO_INT_TIME),
+ .event_spec = opt3001_event_spec,
+ .num_event_specs = ARRAY_SIZE(opt3001_event_spec),
+ },
+ IIO_CHAN_SOFT_TIMESTAMP(1),
+};
+
+static int opt3001_get_lux(struct opt3001 *opt, int *val, int *val2)
+{
+ int ret;
+ u16 mantissa;
+ u16 reg;
+ u8 exponent;
+ u16 value;
+ long timeout;
+
+ if (opt->use_irq) {
+ /*
+ * Enable the end-of-conversion interrupt mechanism. Note that
+ * doing so will overwrite the low-level limit value however we
+ * will restore this value later on.
+ */
+ ret = i2c_smbus_write_word_swapped(opt->client,
+ OPT3001_LOW_LIMIT,
+ OPT3001_LOW_LIMIT_EOC_ENABLE);
+ if (ret < 0) {
+ dev_err(opt->dev, "failed to write register %02x\n",
+ OPT3001_LOW_LIMIT);
+ return ret;
+ }
+
+ /* Allow IRQ to access the device despite lock being set */
+ opt->ok_to_ignore_lock = true;
+ }
+
+ /* Reset data-ready indicator flag */
+ opt->result_ready = false;
+
+ /* Configure for single-conversion mode and start a new conversion */
+ ret = i2c_smbus_read_word_swapped(opt->client, OPT3001_CONFIGURATION);
+ if (ret < 0) {
+ dev_err(opt->dev, "failed to read register %02x\n",
+ OPT3001_CONFIGURATION);
+ goto err;
+ }
+
+ reg = ret;
+ opt3001_set_mode(opt, ®, OPT3001_CONFIGURATION_M_SINGLE);
+
+ ret = i2c_smbus_write_word_swapped(opt->client, OPT3001_CONFIGURATION,
+ reg);
+ if (ret < 0) {
+ dev_err(opt->dev, "failed to write register %02x\n",
+ OPT3001_CONFIGURATION);
+ goto err;
+ }
+
+ if (opt->use_irq) {
+ /* Wait for the IRQ to indicate the conversion is complete */
+ ret = wait_event_timeout(opt->result_ready_queue,
+ opt->result_ready,
+ msecs_to_jiffies(OPT3001_RESULT_READY_LONG));
+ } else {
+ /* Sleep for result ready time */
+ timeout = (opt->int_time == OPT3001_INT_TIME_SHORT) ?
+ OPT3001_RESULT_READY_SHORT : OPT3001_RESULT_READY_LONG;
+ msleep(timeout);
+
+ /* Check result ready flag */
+ ret = i2c_smbus_read_word_swapped(opt->client,
+ OPT3001_CONFIGURATION);
+ if (ret < 0) {
+ dev_err(opt->dev, "failed to read register %02x\n",
+ OPT3001_CONFIGURATION);
+ goto err;
+ }
+
+ if (!(ret & OPT3001_CONFIGURATION_CRF)) {
+ ret = -ETIMEDOUT;
+ goto err;
+ }
+
+ /* Obtain value */
+ ret = i2c_smbus_read_word_swapped(opt->client, OPT3001_RESULT);
+ if (ret < 0) {
+ dev_err(opt->dev, "failed to read register %02x\n",
+ OPT3001_RESULT);
+ goto err;
+ }
+ opt->result = ret;
+ opt->result_ready = true;
+ }
+
+err:
+ if (opt->use_irq)
+ /* Disallow IRQ to access the device while lock is active */
+ opt->ok_to_ignore_lock = false;
+
+ if (ret == 0)
+ return -ETIMEDOUT;
+ else if (ret < 0)
+ return ret;
+
+ if (opt->use_irq) {
+ /*
+ * Disable the end-of-conversion interrupt mechanism by
+ * restoring the low-level limit value (clearing
+ * OPT3001_LOW_LIMIT_EOC_ENABLE). Note that selectively clearing
+ * those enable bits would affect the actual limit value due to
+ * bit-overlap and therefore can't be done.
+ */
+ value = (opt->low_thresh_exp << 12) | opt->low_thresh_mantissa;
+ ret = i2c_smbus_write_word_swapped(opt->client,
+ OPT3001_LOW_LIMIT,
+ value);
+ if (ret < 0) {
+ dev_err(opt->dev, "failed to write register %02x\n",
+ OPT3001_LOW_LIMIT);
+ return ret;
+ }
+ }
+
+ exponent = OPT3001_REG_EXPONENT(opt->result);
+ mantissa = OPT3001_REG_MANTISSA(opt->result);
+
+ opt3001_to_iio_ret(opt, exponent, mantissa, val, val2);
+
+ return IIO_VAL_INT_PLUS_MICRO;
+}
+
+void opt3001_get_cal_data(struct iio_dev *iio)
+{
+ struct opt3001 *als_data = iio_priv(iio);
+ int als_lux_msb = 0;
+ int als_lux_lsb = 0;
+ int cal_lux = 0;
+
+ if(als_data->pdata->enabled)
+ hrtimer_cancel(&als_data->als_timer);
+
+ mutex_lock(&als_data->io_lock);
+ opt3001_get_lux(als_data, &als_lux_lsb, &als_lux_msb);
+ cal_lux = (als_lux_lsb * 100) + (als_lux_msb / 10000);
+ als_data->setting.caliberated_value = cal_lux;
+ mutex_unlock(&als_data->io_lock);
+ if(als_data->pdata->enabled)
+ hrtimer_start(&als_data->als_timer,
+ ns_to_ktime(als_data->pdata->poll_delay * NSEC_PER_MSEC),
+ HRTIMER_MODE_REL);
+}
+
+int opt3001_get_pure_lux(struct iio_dev *iio)
+{
+ struct opt3001 *ps_data = iio_priv(iio);
+ int als_lux_msb = 0;
+ int als_lux_lsb = 0;
+ int actual_lux = 0;
+ int als_lux = 0;
+
+ mutex_lock(&ps_data->io_lock);
+ opt3001_get_lux(ps_data, &als_lux_lsb, &als_lux_msb);
+ actual_lux = (als_lux_lsb * 100) + (als_lux_msb / 10000);
+ /* Caliberated lux = ((Actual Lux * 600) / Lux value received in 600 lux) */
+ if(ps_data->setting.caliberated_value)
+ als_lux = ((actual_lux * 600) / ps_data->setting.caliberated_value);
+ else
+ als_lux = actual_lux;
+
+ mutex_unlock(&ps_data->io_lock);
+
+ return als_lux;
+}
+
+static int opt3001_get_int_time(struct opt3001 *opt, int *val, int *val2)
+{
+ *val = 0;
+ *val2 = opt->int_time;
+
+ return IIO_VAL_INT_PLUS_MICRO;
+}
+
+static int opt3001_set_int_time(struct opt3001 *opt, int time)
+{
+ int ret;
+ u16 reg;
+
+ ret = i2c_smbus_read_word_swapped(opt->client, OPT3001_CONFIGURATION);
+ if (ret < 0) {
+ dev_err(opt->dev, "failed to read register %02x\n",
+ OPT3001_CONFIGURATION);
+ return ret;
+ }
+
+ reg = ret;
+
+ switch (time) {
+ case OPT3001_INT_TIME_SHORT:
+ reg &= ~OPT3001_CONFIGURATION_CT;
+ opt->int_time = OPT3001_INT_TIME_SHORT;
+ break;
+ case OPT3001_INT_TIME_LONG:
+ reg |= OPT3001_CONFIGURATION_CT;
+ opt->int_time = OPT3001_INT_TIME_LONG;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return i2c_smbus_write_word_swapped(opt->client, OPT3001_CONFIGURATION,
+ reg);
+}
+
+static int opt3001_read_raw(struct iio_dev *iio,
+ struct iio_chan_spec const *chan, int *val, int *val2,
+ long mask)
+{
+ struct opt3001 *opt = iio_priv(iio);
+ int ret;
+
+ if (opt->mode == OPT3001_CONFIGURATION_M_CONTINUOUS)
+ return -EBUSY;
+
+ if (chan->type != IIO_LIGHT)
+ return -EINVAL;
+
+ mutex_lock(&opt->lock);
+
+ switch (mask) {
+ case IIO_CHAN_INFO_PROCESSED:
+ ret = opt3001_get_lux(opt, val, val2);
+ break;
+ case IIO_CHAN_INFO_INT_TIME:
+ ret = opt3001_get_int_time(opt, val, val2);
+ break;
+ default:
+ ret = -EINVAL;
+ }
+
+ mutex_unlock(&opt->lock);
+
+ return ret;
+}
+
+static int opt3001_write_raw(struct iio_dev *iio,
+ struct iio_chan_spec const *chan, int val, int val2,
+ long mask)
+{
+ struct opt3001 *opt = iio_priv(iio);
+ int ret;
+
+ if (opt->mode == OPT3001_CONFIGURATION_M_CONTINUOUS)
+ return -EBUSY;
+
+ if (chan->type != IIO_LIGHT)
+ return -EINVAL;
+
+ if (mask != IIO_CHAN_INFO_INT_TIME)
+ return -EINVAL;
+
+ if (val != 0)
+ return -EINVAL;
+
+ mutex_lock(&opt->lock);
+ ret = opt3001_set_int_time(opt, val2);
+ mutex_unlock(&opt->lock);
+
+ return ret;
+}
+
+static int opt3001_read_event_value(struct iio_dev *iio,
+ const struct iio_chan_spec *chan, enum iio_event_type type,
+ enum iio_event_direction dir, enum iio_event_info info,
+ int *val, int *val2)
+{
+ struct opt3001 *opt = iio_priv(iio);
+ int ret = IIO_VAL_INT_PLUS_MICRO;
+
+ mutex_lock(&opt->lock);
+
+ switch (dir) {
+ case IIO_EV_DIR_RISING:
+ opt3001_to_iio_ret(opt, opt->high_thresh_exp,
+ opt->high_thresh_mantissa, val, val2);
+ break;
+ case IIO_EV_DIR_FALLING:
+ opt3001_to_iio_ret(opt, opt->low_thresh_exp,
+ opt->low_thresh_mantissa, val, val2);
+ break;
+ default:
+ ret = -EINVAL;
+ }
+
+ mutex_unlock(&opt->lock);
+
+ return ret;
+}
+
+static int opt3001_write_event_value(struct iio_dev *iio,
+ const struct iio_chan_spec *chan, enum iio_event_type type,
+ enum iio_event_direction dir, enum iio_event_info info,
+ int val, int val2)
+{
+ struct opt3001 *opt = iio_priv(iio);
+ int ret;
+
+ u16 mantissa;
+ u16 value;
+ u16 reg;
+
+ u8 exponent;
+
+ if (val < 0)
+ return -EINVAL;
+
+ mutex_lock(&opt->lock);
+
+ ret = opt3001_find_scale(opt, val, val2, &exponent);
+ if (ret < 0) {
+ dev_err(opt->dev, "can't find scale for %d.%06u\n", val, val2);
+ goto err;
+ }
+
+ mantissa = (((val * 1000) + (val2 / 1000)) / 10) >> exponent;
+ value = (exponent << 12) | mantissa;
+
+ switch (dir) {
+ case IIO_EV_DIR_RISING:
+ reg = OPT3001_HIGH_LIMIT;
+ opt->high_thresh_mantissa = mantissa;
+ opt->high_thresh_exp = exponent;
+ break;
+ case IIO_EV_DIR_FALLING:
+ reg = OPT3001_LOW_LIMIT;
+ opt->low_thresh_mantissa = mantissa;
+ opt->low_thresh_exp = exponent;
+ break;
+ default:
+ ret = -EINVAL;
+ goto err;
+ }
+
+ ret = i2c_smbus_write_word_swapped(opt->client, reg, value);
+ if (ret < 0) {
+ dev_err(opt->dev, "failed to write register %02x\n", reg);
+ goto err;
+ }
+
+err:
+ mutex_unlock(&opt->lock);
+
+ return ret;
+}
+
+static int opt3001_read_event_config(struct iio_dev *iio,
+ const struct iio_chan_spec *chan, enum iio_event_type type,
+ enum iio_event_direction dir)
+{
+ struct opt3001 *opt = iio_priv(iio);
+
+ return opt->mode == OPT3001_CONFIGURATION_M_CONTINUOUS;
+}
+
+static int opt3001_write_event_config(struct iio_dev *iio,
+ const struct iio_chan_spec *chan, enum iio_event_type type,
+ enum iio_event_direction dir, int state)
+{
+ struct opt3001 *opt = iio_priv(iio);
+ int ret;
+ u16 mode;
+ u16 reg;
+
+ if (state && opt->mode == OPT3001_CONFIGURATION_M_CONTINUOUS)
+ return 0;
+
+ if (!state && opt->mode == OPT3001_CONFIGURATION_M_SHUTDOWN)
+ return 0;
+
+ mutex_lock(&opt->lock);
+
+ mode = state ? OPT3001_CONFIGURATION_M_CONTINUOUS
+ : OPT3001_CONFIGURATION_M_SHUTDOWN;
+
+ ret = i2c_smbus_read_word_swapped(opt->client, OPT3001_CONFIGURATION);
+ if (ret < 0) {
+ dev_err(opt->dev, "failed to read register %02x\n",
+ OPT3001_CONFIGURATION);
+ goto err;
+ }
+
+ reg = ret;
+ opt3001_set_mode(opt, ®, mode);
+
+ ret = i2c_smbus_write_word_swapped(opt->client, OPT3001_CONFIGURATION,
+ reg);
+ if (ret < 0) {
+ dev_err(opt->dev, "failed to write register %02x\n",
+ OPT3001_CONFIGURATION);
+ goto err;
+ }
+
+err:
+ mutex_unlock(&opt->lock);
+
+ return ret;
+}
+
+static const struct iio_info opt3001_info = {
+ .driver_module = THIS_MODULE,
+ .attrs = &opt3001_attribute_group,
+ .read_raw = opt3001_read_raw,
+ .write_raw = opt3001_write_raw,
+ .read_event_value = opt3001_read_event_value,
+ .write_event_value = opt3001_write_event_value,
+ .read_event_config = opt3001_read_event_config,
+ .write_event_config = opt3001_write_event_config,
+};
+
+static int opt3001_read_id(struct opt3001 *opt)
+{
+ char manufacturer[2];
+ u16 device_id;
+ int ret;
+
+ ret = i2c_smbus_read_word_swapped(opt->client, OPT3001_MANUFACTURER_ID);
+ if (ret < 0) {
+ dev_err(opt->dev, "failed to read register %02x\n",
+ OPT3001_MANUFACTURER_ID);
+ return ret;
+ }
+
+ manufacturer[0] = ret >> 8;
+ manufacturer[1] = ret & 0xff;
+
+ ret = i2c_smbus_read_word_swapped(opt->client, OPT3001_DEVICE_ID);
+ if (ret < 0) {
+ dev_err(opt->dev, "failed to read register %02x\n",
+ OPT3001_DEVICE_ID);
+ return ret;
+ }
+
+ device_id = ret;
+
+ dev_info(opt->dev, "Found %c%c OPT%04x\n", manufacturer[0],
+ manufacturer[1], device_id);
+
+ return 0;
+}
+
+static int opt3001_configure(struct opt3001 *opt)
+{
+ int ret;
+ u16 reg;
+
+ ret = i2c_smbus_read_word_swapped(opt->client, OPT3001_CONFIGURATION);
+ if (ret < 0) {
+ dev_err(opt->dev, "failed to read register %02x\n",
+ OPT3001_CONFIGURATION);
+ return ret;
+ }
+
+ reg = ret;
+
+ /* Enable automatic full-scale setting mode */
+ reg &= ~OPT3001_CONFIGURATION_RN_MASK;
+ reg &= ~OPT3001_CONFIGURATION_CT;
+ reg |= OPT3001_CONFIGURATION_RN_AUTO;
+
+ /* Reflect status of the device's integration time setting */
+ if (reg & OPT3001_CONFIGURATION_CT)
+ opt->int_time = OPT3001_INT_TIME_LONG;
+ else
+ opt->int_time = OPT3001_INT_TIME_SHORT;
+
+ /* Ensure device is in shutdown initially */
+ opt3001_set_mode(opt, ®, OPT3001_CONFIGURATION_M_SHUTDOWN);
+
+ /* Configure for latched window-style comparison operation */
+ reg |= OPT3001_CONFIGURATION_L;
+ reg &= ~OPT3001_CONFIGURATION_POL;
+ reg &= ~OPT3001_CONFIGURATION_ME;
+ reg &= ~OPT3001_CONFIGURATION_FC_MASK;
+
+ ret = i2c_smbus_write_word_swapped(opt->client, OPT3001_CONFIGURATION,
+ reg);
+ if (ret < 0) {
+ dev_err(opt->dev, "failed to write register %02x\n",
+ OPT3001_CONFIGURATION);
+ return ret;
+ }
+
+ ret = i2c_smbus_read_word_swapped(opt->client, OPT3001_LOW_LIMIT);
+ if (ret < 0) {
+ dev_err(opt->dev, "failed to read register %02x\n",
+ OPT3001_LOW_LIMIT);
+ return ret;
+ }
+
+ opt->low_thresh_mantissa = OPT3001_REG_MANTISSA(ret);
+ opt->low_thresh_exp = OPT3001_REG_EXPONENT(ret);
+
+ ret = i2c_smbus_read_word_swapped(opt->client, OPT3001_HIGH_LIMIT);
+ if (ret < 0) {
+ dev_err(opt->dev, "failed to read register %02x\n",
+ OPT3001_HIGH_LIMIT);
+ return ret;
+ }
+
+ opt->high_thresh_mantissa = OPT3001_REG_MANTISSA(ret);
+ opt->high_thresh_exp = OPT3001_REG_EXPONENT(ret);
+
+ return 0;
+}
+
+static irqreturn_t opt3001_irq(int irq, void *_iio)
+{
+ struct iio_dev *iio = _iio;
+ struct opt3001 *opt = iio_priv(iio);
+ int ret;
+
+ if (!opt->ok_to_ignore_lock)
+ mutex_lock(&opt->lock);
+
+ ret = i2c_smbus_read_word_swapped(opt->client, OPT3001_CONFIGURATION);
+ if (ret < 0) {
+ dev_err(opt->dev, "failed to read register %02x\n",
+ OPT3001_CONFIGURATION);
+ goto out;
+ }
+
+ if ((ret & OPT3001_CONFIGURATION_M_MASK) ==
+ OPT3001_CONFIGURATION_M_CONTINUOUS) {
+ if (ret & OPT3001_CONFIGURATION_FH)
+ iio_push_event(iio,
+ IIO_UNMOD_EVENT_CODE(IIO_LIGHT, 0,
+ IIO_EV_TYPE_THRESH,
+ IIO_EV_DIR_RISING),
+ iio_get_time_ns());
+ if (ret & OPT3001_CONFIGURATION_FL)
+ iio_push_event(iio,
+ IIO_UNMOD_EVENT_CODE(IIO_LIGHT, 0,
+ IIO_EV_TYPE_THRESH,
+ IIO_EV_DIR_FALLING),
+ iio_get_time_ns());
+ } else if (ret & OPT3001_CONFIGURATION_CRF) {
+ ret = i2c_smbus_read_word_swapped(opt->client, OPT3001_RESULT);
+ if (ret < 0) {
+ dev_err(opt->dev, "failed to read register %02x\n",
+ OPT3001_RESULT);
+ goto out;
+ }
+ opt->result = ret;
+ opt->result_ready = true;
+ wake_up(&opt->result_ready_queue);
+ }
+
+out:
+ if (!opt->ok_to_ignore_lock)
+ mutex_unlock(&opt->lock);
+
+ return IRQ_HANDLED;
+}
+
+static enum hrtimer_restart opt3001_als_timer_func(struct hrtimer *timer)
+{
+ struct opt3001 *ps_data = container_of(timer, struct opt3001, als_timer);
+ queue_work(ps_data->als_wq, &ps_data->als_work);
+ hrtimer_forward_now(&ps_data->als_timer, ns_to_ktime(ps_data->pdata->poll_delay * NSEC_PER_MSEC));
+ return HRTIMER_RESTART;
+}
+
+static void opt3001_als_work_func(struct work_struct *work)
+{
+ struct opt3001 *ps_data = container_of(work, struct opt3001, als_work);
+ int als_lux_msb = 0;
+ int als_lux_lsb = 0;
+ int actual_lux = 0;
+ int als_lux = 0;
+ ktime_t timestamp;
+
+ timestamp = ktime_get_boottime();
+
+ mutex_lock(&ps_data->io_lock);
+ opt3001_get_lux(ps_data, &als_lux_lsb, &als_lux_msb);
+ actual_lux = (als_lux_lsb * 100) + (als_lux_msb / 10000);
+ /* Caliberated lux = ((Actual Lux * 600) / Lux value received in 600 lux) */
+ if(ps_data->setting.caliberated_value)
+ als_lux = ((actual_lux * 600) / ps_data->setting.caliberated_value);
+ else
+ als_lux = actual_lux;
+
+ input_report_abs(ps_data->ls_input_dev, ABS_MISC, als_lux);
+ input_event(ps_data->ls_input_dev, EV_SYN, SYN_TIME_SEC,
+ ktime_to_timespec(timestamp).tv_sec);
+ input_event(ps_data->ls_input_dev, EV_SYN, SYN_TIME_NSEC,
+ ktime_to_timespec(timestamp).tv_nsec);
+ input_sync(ps_data->ls_input_dev);
+ mutex_unlock(&ps_data->io_lock);
+}
+
+
+static int opt3001_als_poll_delay_set(struct sensors_classdev *sensors_cdev,
+ unsigned int delay_msec)
+{
+ struct opt3001 *als_data = container_of(sensors_cdev,
+ struct opt3001, als_cdev);
+
+ if ((delay_msec < OPT3001_LS_MIN_POLL_DELAY) ||
+ (delay_msec > OPT3001_LS_MAX_POLL_DELAY))
+ return -EINVAL;
+ als_data->pdata->poll_delay = delay_msec;
+
+ return 0;
+}
+
+
+static int opt3001_als_flush(struct sensors_classdev *sensors_cdev)
+{
+ struct opt3001 *als_data = container_of(sensors_cdev,
+ struct opt3001, als_cdev);
+
+ input_event(als_data->ls_input_dev, EV_SYN, SYN_CONFIG,
+ als_data->flush_count++);
+ input_sync(als_data->ls_input_dev);
+
+ return 0;
+}
+
+static int opt3001_als_get_cal_data(struct opt3001 *als_data)
+{
+ struct file *filp;
+ char read_data[8];
+ unsigned int read_size = 0;
+ int value;
+ mm_segment_t fs;
+
+ fs = get_fs();
+ set_fs(get_ds());
+ filp=filp_open(CAL_FILE, O_RDONLY, 0);
+ if(IS_ERR(filp)) {
+ pr_err("Failed to open %s\n", CAL_FILE);
+ return -EIO;
+ }
+
+ memset(read_data, 0, sizeof(read_data));
+
+ read_size = filp->f_op->read(filp, read_data, 8, &filp->f_pos);
+ if (kstrtoint(read_data, 0, &value))
+ return -EINVAL;
+
+ if (value)
+ als_data->setting.caliberated_value = value;
+
+ set_fs(fs);
+ filp_close(filp, NULL);
+
+ return 0;
+}
+
+static int opt3001_als_set_enable(struct sensors_classdev *sensors_cdev,
+ unsigned int enable)
+{
+ struct opt3001 *als_data = container_of(sensors_cdev,
+ struct opt3001, als_cdev);
+ if ((enable != 0) && (enable != 1)) {
+ pr_err("%s: invalid value(%d)\n", __func__, enable);
+ return -EINVAL;
+ }
+
+ if(!als_data->cal_status) {
+ /* Read Caliberated data and set */
+ if(opt3001_als_get_cal_data(als_data))
+ pr_err("lux calibration failed\n");
+
+ als_data->cal_status = true;
+ }
+
+ if (enable) {
+ als_data->pdata->enabled = true;
+ hrtimer_start(&als_data->als_timer, ns_to_ktime(als_data->pdata->poll_delay * NSEC_PER_MSEC), HRTIMER_MODE_REL);
+ } else {
+ als_data->pdata->enabled = false;
+ hrtimer_cancel(&als_data->als_timer);
+ }
+
+ return 0;
+}
+
+
+static int lightsensor_setup(struct opt3001 *opt)
+{
+ int ret;
+
+ opt->ls_input_dev = devm_input_allocate_device(&opt->client->dev);
+ if (!opt->ls_input_dev) {
+ pr_err(
+ "%s: could not allocate ls input device\n",
+ __func__);
+ return -ENOMEM;
+ }
+ opt->ls_input_dev->name = "opt3001-light";
+ opt->ls_input_dev->id.bustype = BUS_I2C;
+ set_bit(EV_ABS, opt->ls_input_dev->evbit);
+
+ input_set_abs_params(opt->ls_input_dev, ABS_MISC, 0, 65535, 0, 0);
+
+ ret = input_register_device(opt->ls_input_dev);
+ if (ret < 0) {
+ pr_err("%s: can not register ls input device\n",
+ __func__);
+ goto err_free_ls_input_device;
+ }
+
+ input_set_drvdata(opt->ls_input_dev, opt);
+
+err_free_ls_input_device:
+ return ret;
+}
+
+static int opt3001_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct device *dev = &client->dev;
+
+ struct iio_dev *iio;
+ struct opt3001 *opt;
+ struct opt3001_info *plat_data;
+ int irq = client->irq;
+ int ret;
+
+ iio = devm_iio_device_alloc(dev, sizeof(*opt));
+ if (!iio)
+ return -ENOMEM;
+
+ opt = iio_priv(iio);
+ opt->client = client;
+ opt->dev = dev;
+
+ mutex_init(&opt->lock);
+ init_waitqueue_head(&opt->result_ready_queue);
+ i2c_set_clientdata(client, iio);
+
+ ret = opt3001_read_id(opt);
+ if (ret)
+ return ret;
+
+ ret = opt3001_configure(opt);
+ if (ret)
+ return ret;
+
+ iio->name = client->name;
+ iio->channels = opt3001_channels;
+ iio->num_channels = ARRAY_SIZE(opt3001_channels);
+ iio->dev.parent = dev;
+ iio->modes = INDIO_DIRECT_MODE;
+ iio->info = &opt3001_info;
+
+ ret = devm_iio_device_register(dev, iio);
+ if (ret) {
+ dev_err(dev, "failed to register IIO device\n");
+ return ret;
+ }
+
+ /* Make use of INT pin only if valid IRQ no. is given */
+ if (irq > 0) {
+ ret = request_threaded_irq(irq, NULL, opt3001_irq,
+ IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
+ "opt3001", iio);
+ if (ret) {
+ dev_err(dev, "failed to request IRQ #%d\n", irq);
+ return ret;
+ }
+ opt->use_irq = true;
+ } else {
+ dev_dbg(opt->dev, "enabling interrupt-less operation\n");
+ }
+
+ plat_data = devm_kzalloc(dev,
+ sizeof(struct opt3001_info), GFP_KERNEL);
+ if (!plat_data) {
+ dev_err(dev, "Failed to allocate memory\n");
+ return -ENOMEM;
+ }
+
+ plat_data->enabled = 0;
+ plat_data->poll_delay = OPT3001_LS_DEFAULT_POLL_DELAY;
+ opt->pdata = plat_data;
+
+ mutex_init(&opt->io_lock);
+
+ ret = lightsensor_setup(opt);
+ if (ret < 0) {
+ pr_err("%s: lightsensor_setup error!!\n", __func__);
+ return -1;
+ }
+
+
+ opt->setting.caliberated_value = 0;
+ opt->als_cdev = sensors_light_cdev;
+ opt->als_cdev.sensors_enable = opt3001_als_set_enable;
+ opt->als_cdev.sensors_poll_delay = opt3001_als_poll_delay_set;
+ opt->als_cdev.sensors_flush = opt3001_als_flush;
+ opt->als_wq = create_singlethread_workqueue("als_wq");
+ INIT_WORK(&opt->als_work, opt3001_als_work_func);
+ hrtimer_init(&opt->als_timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
+ opt->als_timer.function = opt3001_als_timer_func;
+
+ ret = sensors_classdev_register(&opt->ls_input_dev->dev,
+ &opt->als_cdev);
+ if (ret) {
+ pr_err("%s: ERROR: Failed to register sensor class\n", __func__);
+ return -1;
+ }
+
+ dev_info(opt->dev, "OPT3001 Light sensor driver probe: Success ");
+ return 0;
+}
+
+static int opt3001_remove(struct i2c_client *client)
+{
+ struct iio_dev *iio = i2c_get_clientdata(client);
+ struct opt3001 *opt = iio_priv(iio);
+ int ret;
+ u16 reg;
+
+ if (opt->use_irq)
+ free_irq(client->irq, iio);
+
+ ret = i2c_smbus_read_word_swapped(opt->client, OPT3001_CONFIGURATION);
+ if (ret < 0) {
+ dev_err(opt->dev, "failed to read register %02x\n",
+ OPT3001_CONFIGURATION);
+ return ret;
+ }
+
+ reg = ret;
+ opt3001_set_mode(opt, ®, OPT3001_CONFIGURATION_M_SHUTDOWN);
+
+ ret = i2c_smbus_write_word_swapped(opt->client, OPT3001_CONFIGURATION,
+ reg);
+ if (ret < 0) {
+ dev_err(opt->dev, "failed to write register %02x\n",
+ OPT3001_CONFIGURATION);
+ return ret;
+ }
+
+ return 0;
+}
+
+static const struct i2c_device_id opt3001_id[] = {
+ { "opt3001", 0 },
+ { } /* Terminating Entry */
+};
+MODULE_DEVICE_TABLE(i2c, opt3001_id);
+
+static const struct of_device_id opt3001_of_match[] = {
+ { .compatible = "ti,opt3001" },
+ { }
+};
+MODULE_DEVICE_TABLE(of, opt3001_of_match);
+
+static struct i2c_driver opt3001_driver = {
+ .probe = opt3001_probe,
+ .remove = opt3001_remove,
+ .id_table = opt3001_id,
+
+ .driver = {
+ .name = "opt3001",
+ .of_match_table = of_match_ptr(opt3001_of_match),
+ },
+};
+
+module_i2c_driver(opt3001_driver);
+
+MODULE_LICENSE("GPL v2");
+MODULE_AUTHOR("Andreas Dannenberg <dannenberg@ti.com>");
+MODULE_DESCRIPTION("Texas Instruments OPT3001 Light Sensor Driver");
diff --git a/drivers/iio/light/tsl2584.c b/drivers/iio/light/tsl2584.c
index 87150c6..e7ce050 100644
--- a/drivers/iio/light/tsl2584.c
+++ b/drivers/iio/light/tsl2584.c
@@ -1060,7 +1060,13 @@
taos_defaults(chip);
/* Make sure the chip is on */
- taos_chip_on(indio_dev);
+ //taos_chip_on(indio_dev);
+ if(taos_chip_on(indio_dev) < 0) {
+ dev_err(&clientp->dev, "I2C Slave Failed\n");
+ devm_iio_device_unregister(indio_dev->dev.parent, indio_dev);
+ devm_iio_device_free(&clientp->dev, indio_dev);
+ return -ENOMEM;
+ }
plat_data = devm_kzalloc(&clientp->dev,