summaryrefslogtreecommitdiff
path: root/drivers/staging
diff options
context:
space:
mode:
authorLaxman Dewangan <ldewangan@nvidia.com>2011-03-22 18:30:01 +0530
committerDan Willemsen <dwillemsen@nvidia.com>2011-11-30 21:48:55 -0800
commitbe4407a28cf8732b201fdcec9fc617dfb8ec3e2f (patch)
tree55a5dd3dcce9f5cd230c641d4bd502113fe17212 /drivers/staging
parent5c74cd877c7fa6d527a3bbe4bb74f7c8a1c55243 (diff)
iio:isl29028: Adding driver for ISL29028
Adding the complete driver for the ISL29028 to get the proximity, ALS and IR sensing. The reading of these data is provided through sysfs interface. Bug 876339 Original-Change-Id: I25661d0ef54e1053ccf47a716c89607ca51a9862 Reviewed-on: http://git-master/r/23155 Change-Id: I14114e30937fb2e5d308b77a76f2c6f634bf907c (cherry picked from commit b441479716b41ac91cc398c803692e5eb005a704) Reviewed-on: http://git-master/r/52594 Tested-by: Sachin Nikam <snikam@nvidia.com> Reviewed-by: Bharat Nihalani <bnihalani@nvidia.com> Reviewed-by: Joseph Lehrer <jlehrer@nvidia.com> Tested-by: Joseph Lehrer <jlehrer@nvidia.com> Rebase-Id: Re9322c535586f4190c8bd24c2135b0487bef8083
Diffstat (limited to 'drivers/staging')
-rw-r--r--drivers/staging/iio/light/Kconfig29
-rw-r--r--drivers/staging/iio/light/Makefile2
-rw-r--r--drivers/staging/iio/light/isl29028.c1249
3 files changed, 1271 insertions, 9 deletions
diff --git a/drivers/staging/iio/light/Kconfig b/drivers/staging/iio/light/Kconfig
index 1ad2d56c8ba8..92ee85fc85d6 100644
--- a/drivers/staging/iio/light/Kconfig
+++ b/drivers/staging/iio/light/Kconfig
@@ -4,15 +4,26 @@
comment "Light sensors"
config SENSORS_ISL29018
- tristate "ISL 29018 light and proximity sensor"
- depends on I2C
- default n
- help
- If you say yes here you get support for ambient light sensing and
- proximity infrared sensing from Intersil ISL29018.
- This driver will provide the measurements of ambient light intensity
- in lux, proximity infrared sensing and normal infrared sensing.
- Data from sensor is accessible via sysfs.
+ tristate "ISL 29018 light and proximity sensor"
+ depends on I2C
+ default n
+ help
+ If you say yes here you get support for ambient light sensing and
+ proximity infrared sensing from Intersil ISL29018.
+ This driver will provide the measurements of ambient light intensity
+ in lux, proximity infrared sensing and normal infrared sensing.
+ Data from sensor is accessible via sysfs.
+
+config SENSORS_ISL29028
+ tristate "ISL 29028 light and proximity sensor"
+ depends on I2C
+ default n
+ help
+ If you say yes here you get support for ambient light sensing and
+ proximity ir sensing from intersil ISL29028.
+ This driver will provide the measurements of ambient light intensity
+ in lux, proximity infrared sensing and normal infrared sensing.
+ Data from sensor is accessible via sysfs.
config SENSORS_TSL2563
tristate "TAOS TSL2560, TSL2561, TSL2562 and TSL2563 ambient light sensors"
diff --git a/drivers/staging/iio/light/Makefile b/drivers/staging/iio/light/Makefile
index 3011fbfa8dc2..6f4e5b0a665b 100644
--- a/drivers/staging/iio/light/Makefile
+++ b/drivers/staging/iio/light/Makefile
@@ -5,3 +5,5 @@
obj-$(CONFIG_SENSORS_TSL2563) += tsl2563.o
obj-$(CONFIG_SENSORS_ISL29018) += isl29018.o
obj-$(CONFIG_TSL2583) += tsl2583.o
+obj-$(CONFIG_SENSORS_ISL29028) += isl29028.o
+
diff --git a/drivers/staging/iio/light/isl29028.c b/drivers/staging/iio/light/isl29028.c
new file mode 100644
index 000000000000..49caa612482a
--- /dev/null
+++ b/drivers/staging/iio/light/isl29028.c
@@ -0,0 +1,1249 @@
+/*
+ * A iio driver for the light sensor ISL 29028.
+ *
+ * IIO Light driver for monitoring ambient light intensity in lux and proximity
+ * ir.
+ *
+ * Copyright (c) 2011, NVIDIA Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#include <linux/module.h>
+#include <linux/i2c.h>
+#include <linux/err.h>
+#include <linux/mutex.h>
+#include <linux/delay.h>
+#include <linux/slab.h>
+#include <linux/completion.h>
+#include <linux/interrupt.h>
+#include "../iio.h"
+
+#define CONVERSION_TIME_MS 100
+
+#define ISL29028_REG_ADD_CONFIGURE 0x01
+
+#define CONFIGURE_PROX_EN_MASK (1 << 7)
+#define CONFIGURE_PROX_EN_SH 7
+
+#define CONFIGURE_PROX_SLP_SH 4
+#define CONFIGURE_PROX_SLP_MASK (7 << CONFIGURE_PROX_SLP_SH)
+
+#define CONFIGURE_PROX_DRIVE (1 << 3)
+
+#define CONFIGURE_ALS_EN 1
+#define CONFIGURE_ALS_DIS 0
+#define CONFIGURE_ALS_EN_SH 2
+#define CONFIGURE_ALS_EN_MASK (1 << CONFIGURE_ALS_EN_SH)
+
+
+#define CONFIGURE_ALS_RANGE_LOW_LUX 0
+#define CONFIGURE_ALS_RANGE_HIGH_LUX 1
+
+#define CONFIGURE_ALS_IR_MODE_MASK 1
+#define CONFIGURE_ALS_IR_MODE_SH 0
+#define CONFIGURE_ALS_IR_MODE_IR 1
+#define CONFIGURE_ALS_IR_MODE_ALS 0
+
+#define ISL29028_REG_ADD_INTERRUPT 0x02
+#define INTERRUPT_PROX_FLAG_MASK (1 << 7)
+#define INTERRUPT_PROX_FLAG_SH 7
+#define INTERRUPT_PROX_FLAG_EN 1
+#define INTERRUPT_PROX_FLAG_DIS 0
+
+#define INTERRUPT_PROX_PERSIST_SH 5
+#define INTERRUPT_PROX_PERSIST_MASK (3 << 5)
+
+#define INTERRUPT_ALS_FLAG_MASK (1 << 3)
+#define INTERRUPT_ALS_FLAG_SH 3
+#define INTERRUPT_ALS_FLAG_EN 1
+#define INTERRUPT_ALS_FLAG_DIS 0
+
+#define INTERRUPT_ALS_PERSIST_SH 1
+#define INTERRUPT_ALS_PERSIST_MASK (3 << 1)
+
+#define ISL29028_REG_ADD_PROX_LOW_THRES 0x03
+#define ISL29028_REG_ADD_PROX_HIGH_THRES 0x04
+
+#define ISL29028_REG_ADD_ALSIR_LOW_THRES 0x05
+#define ISL29028_REG_ADD_ALSIR_LH_THRES 0x06
+#define ISL29028_REG_ADD_ALSIR_LH_THRES_L_SH 0
+#define ISL29028_REG_ADD_ALSIR_LH_THRES_H_SH 4
+#define ISL29028_REG_ADD_ALSIR_HIGH_THRES 0x07
+
+#define ISL29028_REG_ADD_PROX_DATA 0x08
+#define ISL29028_REG_ADD_ALSIR_L 0x09
+#define ISL29028_REG_ADD_ALSIR_U 0x0A
+
+#define ISL29028_REG_ADD_TEST1_MODE 0x0E
+#define ISL29028_REG_ADD_TEST2_MODE 0x0F
+
+#define ISL29028_MAX_REGS ISL29028_REG_ADD_TEST2_MODE
+
+enum {
+ MODE_NONE = 0,
+ MODE_ALS,
+ MODE_IR
+};
+
+struct isl29028_chip {
+ struct iio_dev *indio_dev;
+ struct i2c_client *client;
+ struct mutex lock;
+ int irq;
+
+ int prox_period;
+ int prox_low_thres;
+ int prox_high_thres;
+ int prox_persist;
+ bool is_prox_enable;
+ int prox_reading;
+
+ int als_high_thres;
+ int als_low_thres;
+ int als_persist;
+ int als_range;
+ int als_reading;
+ int als_ir_mode;
+
+ int ir_high_thres;
+ int ir_low_thres;
+ int ir_reading;
+
+ bool is_int_enable;
+ bool is_proxim_int_waiting;
+ bool is_als_int_waiting;
+ struct completion prox_completion;
+ struct completion als_completion;
+ u8 reg_cache[ISL29028_MAX_REGS];
+};
+
+static bool isl29028_write_data(struct i2c_client *client, u8 reg,
+ u8 val, u8 mask, u8 shift)
+{
+ u8 regval;
+ int ret = 0;
+ struct isl29028_chip *chip = i2c_get_clientdata(client);
+
+ regval = chip->reg_cache[reg];
+ regval &= ~mask;
+ regval |= val << shift;
+
+ ret = i2c_smbus_write_byte_data(client, reg, regval);
+ if (ret) {
+ dev_err(&client->dev, "Write to device reg %d fails status "
+ "%x\n", reg, ret);
+ return false;
+ }
+ chip->reg_cache[reg] = regval;
+ return true;
+}
+
+static bool isl29018_set_proxim_period(struct i2c_client *client,
+ bool is_enable, int period)
+{
+ int prox_period[] = {0, 12, 50, 75, 100, 200, 400, 800};
+ int i;
+ int sel;
+ bool st;
+ if (period < 12)
+ sel = 7;
+ else {
+ for (i = 1; i < ARRAY_SIZE(prox_period) - 1; ++i) {
+ if ((prox_period[i] <= period) &&
+ period < prox_period[i + 1])
+ break;
+ }
+ sel = 7 - i;
+ }
+
+ if (!is_enable) {
+ dev_dbg(&client->dev, "Disabling proximity sensing\n");
+ st = isl29028_write_data(client, ISL29028_REG_ADD_CONFIGURE,
+ 0, CONFIGURE_PROX_EN_MASK, CONFIGURE_PROX_EN_SH);
+ } else {
+ dev_dbg(&client->dev, "Enabling proximity sensing with period "
+ "of %d ms sel %d period %d\n", prox_period[7 - sel],
+ sel, period);
+ st = isl29028_write_data(client, ISL29028_REG_ADD_CONFIGURE,
+ sel, CONFIGURE_PROX_SLP_MASK, CONFIGURE_PROX_SLP_SH);
+ if (st)
+ st = isl29028_write_data(client,
+ ISL29028_REG_ADD_CONFIGURE, 1,
+ CONFIGURE_PROX_EN_MASK, CONFIGURE_PROX_EN_SH);
+ }
+ return st;
+}
+
+static bool isl29018_set_proxim_persist(struct i2c_client *client,
+ bool is_enable, int persist)
+{
+ int prox_perstant[] = {1, 4, 8, 16};
+ int i;
+ int sel;
+ bool st;
+ if (is_enable) {
+ for (i = 0; i < ARRAY_SIZE(prox_perstant) - 1; ++i) {
+ if ((prox_perstant[i] <= persist) &&
+ persist < prox_perstant[i+1])
+ break;
+ }
+ sel = i;
+ }
+
+ if (is_enable) {
+ dev_dbg(&client->dev, "Enabling proximity threshold interrupt\n");
+ st = isl29028_write_data(client, ISL29028_REG_ADD_INTERRUPT,
+ sel, INTERRUPT_PROX_PERSIST_MASK,
+ INTERRUPT_PROX_PERSIST_SH);
+ if (st)
+ st = isl29028_write_data(client,
+ ISL29028_REG_ADD_INTERRUPT,
+ INTERRUPT_PROX_FLAG_EN,
+ INTERRUPT_PROX_FLAG_MASK,
+ INTERRUPT_PROX_FLAG_SH);
+ } else {
+ st = isl29028_write_data(client,
+ ISL29028_REG_ADD_INTERRUPT, INTERRUPT_PROX_FLAG_DIS,
+ INTERRUPT_PROX_FLAG_MASK, INTERRUPT_PROX_FLAG_SH);
+ }
+ return st;
+}
+
+static bool isl29018_set_als_persist(struct i2c_client *client, bool is_enable,
+ int persist)
+{
+ int prox_perstant[] = {1, 4, 8, 16};
+ int i;
+ int sel;
+ bool st;
+ if (is_enable) {
+ for (i = 0; i < ARRAY_SIZE(prox_perstant) - 1; ++i) {
+ if ((prox_perstant[i] <= persist) &&
+ persist < prox_perstant[i+1])
+ break;
+ }
+ sel = i;
+ }
+
+ if (is_enable) {
+ dev_dbg(&client->dev, "Enabling als threshold interrupt\n");
+ st = isl29028_write_data(client, ISL29028_REG_ADD_INTERRUPT,
+ sel, INTERRUPT_ALS_PERSIST_MASK,
+ INTERRUPT_ALS_PERSIST_SH);
+ if (st)
+ st = isl29028_write_data(client,
+ ISL29028_REG_ADD_INTERRUPT,
+ INTERRUPT_ALS_FLAG_EN,
+ INTERRUPT_ALS_FLAG_MASK,
+ INTERRUPT_ALS_FLAG_SH);
+ } else {
+ st = isl29028_write_data(client,
+ ISL29028_REG_ADD_INTERRUPT, INTERRUPT_ALS_FLAG_DIS,
+ INTERRUPT_ALS_FLAG_MASK, INTERRUPT_ALS_FLAG_SH);
+ }
+ return st;
+}
+
+static bool isl29018_set_proxim_high_threshold(struct i2c_client *client, u8 th)
+{
+ return isl29028_write_data(client, ISL29028_REG_ADD_PROX_HIGH_THRES,
+ th, 0xFF, 0);
+}
+
+static bool isl29018_set_proxim_low_threshold(struct i2c_client *client, u8 th)
+{
+ return isl29028_write_data(client, ISL29028_REG_ADD_PROX_LOW_THRES,
+ th, 0xFF, 0);
+}
+
+static bool isl29018_set_irals_high_threshold(struct i2c_client *client,
+ u32 als)
+{
+ bool st;
+ st = isl29028_write_data(client, ISL29028_REG_ADD_ALSIR_HIGH_THRES,
+ (als >> 4) & 0xFF, 0xFF, 0);
+ if (st)
+ st = isl29028_write_data(client,
+ ISL29028_REG_ADD_ALSIR_LH_THRES, als & 0xF,
+ 0xF << ISL29028_REG_ADD_ALSIR_LH_THRES_H_SH,
+ ISL29028_REG_ADD_ALSIR_LH_THRES_H_SH);
+ return st;
+}
+
+static bool isl29018_set_irals_low_threshold(struct i2c_client *client, u32 als)
+{
+ bool st;
+ st = isl29028_write_data(client,
+ ISL29028_REG_ADD_ALSIR_LH_THRES, (als >> 8) & 0xF,
+ 0xF << ISL29028_REG_ADD_ALSIR_LH_THRES_L_SH,
+ ISL29028_REG_ADD_ALSIR_LH_THRES_L_SH);
+ if (st)
+ st = isl29028_write_data(client,
+ ISL29028_REG_ADD_ALSIR_LOW_THRES,
+ als & 0xFF, 0xFF, 0);
+ return st;
+}
+
+static bool isl29018_set_als_ir_mode(struct i2c_client *client, bool is_enable,
+ bool is_als)
+{
+ struct isl29028_chip *chip = i2c_get_clientdata(client);
+ bool st;
+ if (is_enable) {
+ if (is_als) {
+ dev_dbg(&client->dev, "Enabling ALS mode\n");
+ st = isl29028_write_data(client,
+ ISL29028_REG_ADD_CONFIGURE,
+ CONFIGURE_ALS_IR_MODE_ALS,
+ CONFIGURE_ALS_IR_MODE_MASK,
+ CONFIGURE_ALS_IR_MODE_SH);
+ if (st)
+ st = isl29018_set_irals_high_threshold(client,
+ chip->als_high_thres);
+ if (st)
+ st = isl29018_set_irals_low_threshold(client,
+ chip->als_low_thres);
+ } else {
+ dev_dbg(&client->dev, "Enabling IR mode\n");
+ st = isl29028_write_data(client,
+ ISL29028_REG_ADD_CONFIGURE,
+ CONFIGURE_ALS_IR_MODE_IR,
+ CONFIGURE_ALS_IR_MODE_MASK,
+ CONFIGURE_ALS_IR_MODE_SH);
+ if (st)
+ st = isl29018_set_irals_high_threshold(client,
+ chip->ir_high_thres);
+ if (st)
+ st = isl29018_set_irals_low_threshold(client,
+ chip->ir_low_thres);
+ }
+ if (st)
+ st = isl29028_write_data(client,
+ ISL29028_REG_ADD_CONFIGURE,
+ CONFIGURE_ALS_EN,
+ CONFIGURE_ALS_EN_MASK,
+ CONFIGURE_ALS_EN_SH);
+ } else {
+ st = isl29028_write_data(client,
+ ISL29028_REG_ADD_CONFIGURE,
+ CONFIGURE_ALS_DIS,
+ CONFIGURE_ALS_EN_MASK,
+ CONFIGURE_ALS_EN_SH);
+ }
+ return st;
+}
+
+static bool isl29028_read_als_ir(struct i2c_client *client, int *als_ir)
+{
+ s32 lsb;
+ s32 msb;
+
+ lsb = i2c_smbus_read_byte_data(client, ISL29028_REG_ADD_ALSIR_L);
+ if (lsb < 0) {
+ dev_err(&client->dev, "Error in reading register %d, error %d\n",
+ ISL29028_REG_ADD_ALSIR_L, lsb);
+ return false;
+ }
+
+ msb = i2c_smbus_read_byte_data(client, ISL29028_REG_ADD_ALSIR_U);
+ if (msb < 0) {
+ dev_err(&client->dev, "Error in reading register %d, error %d\n",
+ ISL29028_REG_ADD_ALSIR_U, lsb);
+ return false;
+ }
+ *als_ir = ((msb & 0xF) << 8) | (lsb & 0xFF);
+ return true;
+}
+
+static bool isl29028_read_proxim(struct i2c_client *client, int *prox)
+{
+ s32 data;
+
+ data = i2c_smbus_read_byte_data(client, ISL29028_REG_ADD_PROX_DATA);
+ if (data < 0) {
+ dev_err(&client->dev, "Error in reading register %d, error %d\n",
+ ISL29028_REG_ADD_PROX_DATA, data);
+ return false;
+ }
+ *prox = (int)data;
+ return true;
+}
+
+/* Sysfs interface */
+/* proximity period */
+static ssize_t show_prox_period(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct iio_dev *indio_dev = dev_get_drvdata(dev);
+ struct isl29028_chip *chip = indio_dev->dev_data;
+
+ dev_vdbg(dev, "%s()\n", __func__);
+ return sprintf(buf, "%d\n", chip->prox_period);
+}
+
+static ssize_t store_prox_period(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ struct iio_dev *indio_dev = dev_get_drvdata(dev);
+ struct isl29028_chip *chip = indio_dev->dev_data;
+ struct i2c_client *client = chip->client;
+ bool st;
+ unsigned long lval;
+
+ dev_vdbg(dev, "%s()\n", __func__);
+
+ if (strict_strtoul(buf, 10, &lval))
+ return -EINVAL;
+
+ mutex_lock(&chip->lock);
+ st = isl29018_set_proxim_period(client, chip->is_prox_enable,
+ (int)lval);
+ if (st)
+ chip->prox_period = (int)lval;
+ else
+ dev_err(dev, "Error in setting the proximity period\n");
+
+ mutex_unlock(&chip->lock);
+ return count;
+}
+
+/* proximity enable/disable */
+static ssize_t show_prox_enable(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct iio_dev *indio_dev = dev_get_drvdata(dev);
+ struct isl29028_chip *chip = indio_dev->dev_data;
+
+ dev_vdbg(dev, "%s()\n", __func__);
+ if (chip->is_prox_enable)
+ return sprintf(buf, "1\n");
+ else
+ return sprintf(buf, "0\n");
+}
+
+static ssize_t store_prox_enable(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ struct iio_dev *indio_dev = dev_get_drvdata(dev);
+ struct isl29028_chip *chip = indio_dev->dev_data;
+ struct i2c_client *client = chip->client;
+ bool st;
+ unsigned long lval;
+
+ dev_vdbg(dev, "%s()\n", __func__);
+
+ if (strict_strtoul(buf, 10, &lval))
+ return -EINVAL;
+ if ((lval != 1) && (lval != 0)) {
+ dev_err(dev, "illegal value %lu\n", lval);
+ return -EINVAL;
+ }
+
+ mutex_lock(&chip->lock);
+ if (lval == 1)
+ st = isl29018_set_proxim_period(client, true,
+ chip->prox_period);
+ else
+ st = isl29018_set_proxim_period(client, false,
+ chip->prox_period);
+ if (st)
+ chip->is_prox_enable = (lval) ? true : false;
+ else
+ dev_err(dev, "Error in enabling proximity\n");
+
+ mutex_unlock(&chip->lock);
+ return count;
+}
+
+/* als/ir enable/disable */
+static ssize_t show_als_ir_mode(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct iio_dev *indio_dev = dev_get_drvdata(dev);
+ struct isl29028_chip *chip = indio_dev->dev_data;
+
+ dev_vdbg(dev, "%s()\n", __func__);
+ return sprintf(buf, "Current Mode: %d [0:None, 1:ALS, 2:IR]\n",
+ chip->als_ir_mode);
+}
+
+static ssize_t store_als_ir_mode(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ struct iio_dev *indio_dev = dev_get_drvdata(dev);
+ struct isl29028_chip *chip = indio_dev->dev_data;
+ struct i2c_client *client = chip->client;
+ bool st;
+ unsigned long lval;
+
+ dev_vdbg(dev, "%s()\n", __func__);
+
+ if (strict_strtoul(buf, 10, &lval))
+ return -EINVAL;
+ if (lval > 2) {
+ dev_err(dev, "illegal value %lu\n", lval);
+ return -EINVAL;
+ }
+
+ mutex_lock(&chip->lock);
+ if (lval == 0)
+ st = isl29018_set_als_ir_mode(client, false, false);
+ else if (lval == 1)
+ st = isl29018_set_als_ir_mode(client, true, true);
+ else
+ st = isl29018_set_als_ir_mode(client, true, false);
+ if (st)
+ chip->als_ir_mode = (int)lval;
+ else
+ dev_err(dev, "Error in enabling als/ir mode\n");
+
+ mutex_unlock(&chip->lock);
+ return count;
+}
+
+/* Proximity low thresholds */
+static ssize_t show_proxim_low_threshold(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct iio_dev *indio_dev = dev_get_drvdata(dev);
+ struct isl29028_chip *chip = indio_dev->dev_data;
+
+ dev_vdbg(dev, "%s()\n", __func__);
+ return sprintf(buf, "%d\n", chip->prox_low_thres);
+}
+
+static ssize_t store_proxim_low_threshold(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ struct iio_dev *indio_dev = dev_get_drvdata(dev);
+ struct isl29028_chip *chip = indio_dev->dev_data;
+ struct i2c_client *client = chip->client;
+ bool st;
+ unsigned long lval;
+
+ dev_vdbg(dev, "%s()\n", __func__);
+
+ if (strict_strtoul(buf, 10, &lval))
+ return -EINVAL;
+
+ if ((lval > 0xFF) || (lval < 0x0)) {
+ dev_err(dev, "The threshold is not supported\n");
+ return -EINVAL;
+ }
+
+ mutex_lock(&chip->lock);
+ st = isl29018_set_proxim_low_threshold(client, (u8)lval);
+ if (st)
+ chip->prox_low_thres = (int)lval;
+ else
+ dev_err(dev, "Error in setting proximity low threshold\n");
+
+ mutex_unlock(&chip->lock);
+ return count;
+}
+
+/* Proximity high thresholds */
+static ssize_t show_proxim_high_threshold(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct iio_dev *indio_dev = dev_get_drvdata(dev);
+ struct isl29028_chip *chip = indio_dev->dev_data;
+
+ dev_vdbg(dev, "%s()\n", __func__);
+ return sprintf(buf, "%d\n", chip->prox_high_thres);
+}
+
+static ssize_t store_proxim_high_threshold(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ struct iio_dev *indio_dev = dev_get_drvdata(dev);
+ struct isl29028_chip *chip = indio_dev->dev_data;
+ struct i2c_client *client = chip->client;
+ bool st;
+ unsigned long lval;
+
+ dev_vdbg(dev, "%s()\n", __func__);
+
+ if (strict_strtoul(buf, 10, &lval))
+ return -EINVAL;
+
+ if ((lval > 0xFF) || (lval < 0x0)) {
+ dev_err(dev, "The threshold is not supported\n");
+ return -EINVAL;
+ }
+
+ mutex_lock(&chip->lock);
+ st = isl29018_set_proxim_high_threshold(client, (u8)lval);
+ if (st)
+ chip->prox_high_thres = (int)lval;
+ else
+ dev_err(dev, "Error in setting proximity high threshold\n");
+
+ mutex_unlock(&chip->lock);
+ return count;
+}
+
+/* als low thresholds */
+static ssize_t show_als_low_threshold(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct iio_dev *indio_dev = dev_get_drvdata(dev);
+ struct isl29028_chip *chip = indio_dev->dev_data;
+
+ dev_vdbg(dev, "%s()\n", __func__);
+ return sprintf(buf, "%d\n", chip->als_low_thres);
+}
+
+static ssize_t store_als_low_threshold(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ struct iio_dev *indio_dev = dev_get_drvdata(dev);
+ struct isl29028_chip *chip = indio_dev->dev_data;
+ struct i2c_client *client = chip->client;
+ bool st;
+ unsigned long lval;
+
+ dev_vdbg(dev, "%s()\n", __func__);
+
+ if (strict_strtoul(buf, 10, &lval))
+ return -EINVAL;
+
+ if ((lval > 0xFFFF) || (lval < 0x0)) {
+ dev_err(dev, "The ALS threshold is not supported\n");
+ return -EINVAL;
+ }
+
+ mutex_lock(&chip->lock);
+ if (chip->als_ir_mode == MODE_ALS) {
+ st = isl29018_set_irals_low_threshold(client, (int)lval);
+ if (st)
+ chip->als_low_thres = (int)lval;
+ else
+ dev_err(dev, "Error in setting als low threshold\n");
+ } else
+ chip->als_low_thres = (int)lval;
+ mutex_unlock(&chip->lock);
+ return count;
+}
+
+/* Als high thresholds */
+static ssize_t show_als_high_threshold(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct iio_dev *indio_dev = dev_get_drvdata(dev);
+ struct isl29028_chip *chip = indio_dev->dev_data;
+
+ dev_vdbg(dev, "%s()\n", __func__);
+ return sprintf(buf, "%d\n", chip->als_high_thres);
+}
+
+static ssize_t store_als_high_threshold(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ struct iio_dev *indio_dev = dev_get_drvdata(dev);
+ struct isl29028_chip *chip = indio_dev->dev_data;
+ struct i2c_client *client = chip->client;
+ bool st;
+ unsigned long lval;
+
+ dev_vdbg(dev, "%s()\n", __func__);
+
+ if (strict_strtoul(buf, 10, &lval))
+ return -EINVAL;
+
+ if ((lval > 0xFFFF) || (lval < 0x0)) {
+ dev_err(dev, "The als threshold is not supported\n");
+ return -EINVAL;
+ }
+
+ mutex_lock(&chip->lock);
+ if (chip->als_ir_mode == MODE_ALS) {
+ st = isl29018_set_irals_high_threshold(client, (int)lval);
+ if (st)
+ chip->als_high_thres = (int)lval;
+ else
+ dev_err(dev, "Error in setting als high threshold\n");
+ } else
+ chip->als_high_thres = (int)lval;
+ mutex_unlock(&chip->lock);
+ return count;
+}
+
+/* IR low thresholds */
+static ssize_t show_ir_low_threshold(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct iio_dev *indio_dev = dev_get_drvdata(dev);
+ struct isl29028_chip *chip = indio_dev->dev_data;
+
+ dev_vdbg(dev, "%s()\n", __func__);
+ return sprintf(buf, "%d\n", chip->ir_low_thres);
+}
+
+static ssize_t store_ir_low_threshold(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ struct iio_dev *indio_dev = dev_get_drvdata(dev);
+ struct isl29028_chip *chip = indio_dev->dev_data;
+ struct i2c_client *client = chip->client;
+ bool st;
+ unsigned long lval;
+
+ dev_vdbg(dev, "%s()\n", __func__);
+
+ if (strict_strtoul(buf, 10, &lval))
+ return -EINVAL;
+
+ if ((lval > 0xFFFF) || (lval < 0x0)) {
+ dev_err(dev, "The IR threshold is not supported\n");
+ return -EINVAL;
+ }
+
+ mutex_lock(&chip->lock);
+ if (chip->als_ir_mode == MODE_IR) {
+ st = isl29018_set_irals_low_threshold(client, (int)lval);
+ if (st)
+ chip->ir_low_thres = (int)lval;
+ else
+ dev_err(dev, "Error in setting als low threshold\n");
+ } else
+ chip->ir_low_thres = (int)lval;
+ mutex_unlock(&chip->lock);
+ return count;
+}
+
+/* IR high thresholds */
+static ssize_t show_ir_high_threshold(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct iio_dev *indio_dev = dev_get_drvdata(dev);
+ struct isl29028_chip *chip = indio_dev->dev_data;
+
+ dev_vdbg(dev, "%s()\n", __func__);
+ return sprintf(buf, "%d\n", chip->ir_high_thres);
+}
+
+static ssize_t store_ir_high_threshold(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ struct iio_dev *indio_dev = dev_get_drvdata(dev);
+ struct isl29028_chip *chip = indio_dev->dev_data;
+ struct i2c_client *client = chip->client;
+ bool st;
+ unsigned long lval;
+
+ dev_vdbg(dev, "%s()\n", __func__);
+
+ if (strict_strtoul(buf, 10, &lval))
+ return -EINVAL;
+
+ if ((lval > 0xFFFF) || (lval < 0x0)) {
+ dev_err(dev, "The als threshold is not supported\n");
+ return -EINVAL;
+ }
+
+ mutex_lock(&chip->lock);
+ if (chip->als_ir_mode == MODE_IR) {
+ st = isl29018_set_irals_high_threshold(client, (int)lval);
+ if (st)
+ chip->ir_high_thres = (int)lval;
+ else
+ dev_err(dev, "Error in setting als high threshold\n");
+ } else
+ chip->ir_high_thres = (int)lval;
+ mutex_unlock(&chip->lock);
+ return count;
+}
+
+/* Proximity persist */
+static ssize_t show_proxim_persist(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct iio_dev *indio_dev = dev_get_drvdata(dev);
+ struct isl29028_chip *chip = indio_dev->dev_data;
+
+ dev_vdbg(dev, "%s()\n", __func__);
+ return sprintf(buf, "%d\n", chip->prox_persist);
+}
+
+static ssize_t store_proxim_persist(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ struct iio_dev *indio_dev = dev_get_drvdata(dev);
+ struct isl29028_chip *chip = indio_dev->dev_data;
+ unsigned long lval;
+
+ dev_vdbg(dev, "%s()\n", __func__);
+
+ if (strict_strtoul(buf, 10, &lval))
+ return -EINVAL;
+
+ if ((lval > 16) || (lval < 0x0)) {
+ dev_err(dev, "The proximity persist is not supported\n");
+ return -EINVAL;
+ }
+
+ mutex_lock(&chip->lock);
+ chip->prox_persist = (int)lval;
+ mutex_unlock(&chip->lock);
+ return count;
+}
+
+/* als/ir persist */
+static ssize_t show_als_persist(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct iio_dev *indio_dev = dev_get_drvdata(dev);
+ struct isl29028_chip *chip = indio_dev->dev_data;
+
+ dev_vdbg(dev, "%s()\n", __func__);
+ return sprintf(buf, "%d\n", chip->als_persist);
+}
+
+static ssize_t store_als_persist(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ struct iio_dev *indio_dev = dev_get_drvdata(dev);
+ struct isl29028_chip *chip = indio_dev->dev_data;
+ unsigned long lval;
+
+ dev_vdbg(dev, "%s()\n", __func__);
+
+ if (strict_strtoul(buf, 10, &lval))
+ return -EINVAL;
+
+ if ((lval > 16) || (lval < 0x0)) {
+ dev_err(dev, "The als persist is not supported\n");
+ return -EINVAL;
+ }
+
+ mutex_lock(&chip->lock);
+ chip->als_persist = (int)lval;
+ mutex_unlock(&chip->lock);
+ return count;
+}
+
+/* Display proxim data */
+static ssize_t show_proxim_data(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct iio_dev *indio_dev = dev_get_drvdata(dev);
+ struct isl29028_chip *chip = indio_dev->dev_data;
+ int prox_data;
+ bool st;
+ ssize_t buf_count = 0;
+
+ dev_vdbg(dev, "%s()\n", __func__);
+ mutex_lock(&chip->lock);
+
+ if (chip->is_prox_enable) {
+ st = isl29028_read_proxim(chip->client, &prox_data);
+ if (st) {
+ buf_count = sprintf(buf, "%d\n", prox_data);
+ chip->prox_reading = prox_data;
+ }
+ } else
+ buf_count = sprintf(buf, "%d\n", chip->prox_reading);
+
+ mutex_unlock(&chip->lock);
+ return buf_count;
+}
+
+/* Display als data */
+static ssize_t show_als_data(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct iio_dev *indio_dev = dev_get_drvdata(dev);
+ struct isl29028_chip *chip = indio_dev->dev_data;
+ int als_ir_data;
+ bool st;
+ ssize_t buf_count = 0;
+
+ dev_vdbg(dev, "%s()\n", __func__);
+ mutex_lock(&chip->lock);
+
+ if (chip->als_ir_mode == MODE_ALS) {
+ st = isl29028_read_als_ir(chip->client, &als_ir_data);
+ if (st) {
+ buf_count = sprintf(buf, "%d\n", als_ir_data);
+ chip->als_reading = als_ir_data;
+ }
+ } else
+ buf_count = sprintf(buf, "%d\n", chip->als_reading);
+ mutex_unlock(&chip->lock);
+ return buf_count;
+}
+
+/* Display IR data */
+static ssize_t show_ir_data(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct iio_dev *indio_dev = dev_get_drvdata(dev);
+ struct isl29028_chip *chip = indio_dev->dev_data;
+ int als_ir_data;
+ bool st;
+ ssize_t buf_count = 0;
+
+ dev_vdbg(dev, "%s()\n", __func__);
+ mutex_lock(&chip->lock);
+
+ if (chip->als_ir_mode == MODE_IR) {
+ st = isl29028_read_als_ir(chip->client, &als_ir_data);
+ if (st) {
+ buf_count = sprintf(buf, "%d\n", als_ir_data);
+ chip->ir_reading = als_ir_data;
+ }
+ } else
+ buf_count = sprintf(buf, "%d\n", chip->ir_reading);
+ mutex_unlock(&chip->lock);
+ return buf_count;
+}
+
+/* Wait for the proximity threshold interrupt*/
+static ssize_t show_wait_proxim_int(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct iio_dev *indio_dev = dev_get_drvdata(dev);
+ struct isl29028_chip *chip = indio_dev->dev_data;
+ struct i2c_client *client = chip->client;
+ bool st;
+
+ dev_vdbg(dev, "%s()\n", __func__);
+ if (!chip->is_int_enable) {
+ dev_err(dev, "%s() Interrupt mode not supported\n", __func__);
+ return sprintf(buf, "error\n");
+ }
+
+ mutex_lock(&chip->lock);
+ st = isl29018_set_proxim_persist(client, true, chip->prox_persist);
+ if (!st) {
+ dev_err(dev, "%s() Error in configuration\n", __func__);
+ mutex_unlock(&chip->lock);
+ return sprintf(buf, "error\n");
+ }
+
+ chip->is_proxim_int_waiting = true;
+ mutex_unlock(&chip->lock);
+ wait_for_completion(&chip->prox_completion);
+ mutex_lock(&chip->lock);
+ chip->is_proxim_int_waiting = false;
+ isl29018_set_proxim_persist(client, false, chip->prox_persist);
+ mutex_unlock(&chip->lock);
+ return sprintf(buf, "done\n");
+}
+
+/* Wait for the als/ir interrupt*/
+static ssize_t show_wait_als_ir_int(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct iio_dev *indio_dev = dev_get_drvdata(dev);
+ struct isl29028_chip *chip = indio_dev->dev_data;
+ struct i2c_client *client = chip->client;
+ bool st;
+
+ dev_vdbg(dev, "%s()\n", __func__);
+ if (!chip->is_int_enable) {
+ dev_err(dev, "%s() Interrupt mode not supported\n", __func__);
+ return sprintf(buf, "error\n");
+ }
+
+ mutex_lock(&chip->lock);
+
+ st = isl29018_set_als_persist(client, true, chip->als_persist);
+ if (!st) {
+ dev_err(dev, "%s() Error in als ir int configuration\n",
+ __func__);
+ mutex_unlock(&chip->lock);
+ return sprintf(buf, "error\n");
+ }
+
+ chip->is_als_int_waiting = true;
+ mutex_unlock(&chip->lock);
+ wait_for_completion(&chip->als_completion);
+ mutex_lock(&chip->lock);
+ chip->is_als_int_waiting = false;
+ st = isl29018_set_als_persist(client, false, chip->als_persist);
+ mutex_unlock(&chip->lock);
+ return sprintf(buf, "done\n");
+}
+
+/* Read name */
+static ssize_t show_name(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct iio_dev *indio_dev = dev_get_drvdata(dev);
+ struct isl29028_chip *chip = indio_dev->dev_data;
+ return sprintf(buf, "%s\n", chip->client->name);
+}
+
+static IIO_DEVICE_ATTR(proximity_low_threshold, S_IRUGO | S_IWUSR,
+ show_proxim_low_threshold, store_proxim_low_threshold, 0);
+static IIO_DEVICE_ATTR(proximity_high_threshold, S_IRUGO | S_IWUSR,
+ show_proxim_high_threshold, store_proxim_high_threshold, 0);
+static IIO_DEVICE_ATTR(proximity_persist, S_IRUGO | S_IWUSR,
+ show_proxim_persist, store_proxim_persist, 0);
+static IIO_DEVICE_ATTR(proximity_period, S_IRUGO | S_IWUSR,
+ show_prox_period, store_prox_period, 0);
+static IIO_DEVICE_ATTR(proximity_enable, S_IRUGO | S_IWUSR,
+ show_prox_enable, store_prox_enable, 0);
+static IIO_DEVICE_ATTR(wait_proxim_thres, S_IRUGO,
+ show_wait_proxim_int, NULL, 0);
+static IIO_DEVICE_ATTR(proximity_value, S_IRUGO,
+ show_proxim_data, NULL, 0);
+
+static IIO_DEVICE_ATTR(als_low_threshold, S_IRUGO | S_IWUSR,
+ show_als_low_threshold, store_als_low_threshold, 0);
+static IIO_DEVICE_ATTR(als_high_threshold, S_IRUGO | S_IWUSR,
+ show_als_high_threshold, store_als_high_threshold, 0);
+static IIO_DEVICE_ATTR(als_persist, S_IRUGO | S_IWUSR,
+ show_als_persist, store_als_persist, 0);
+static IIO_DEVICE_ATTR(als_ir_mode, S_IRUGO | S_IWUSR,
+ show_als_ir_mode, store_als_ir_mode, 0);
+static IIO_DEVICE_ATTR(als_value, S_IRUGO,
+ show_als_data, NULL, 0);
+static IIO_DEVICE_ATTR(wait_als_ir_thres, S_IRUGO,
+ show_wait_als_ir_int, NULL, 0);
+
+static IIO_DEVICE_ATTR(ir_value, S_IRUGO,
+ show_ir_data, NULL, 0);
+static IIO_DEVICE_ATTR(ir_low_threshold, S_IRUGO | S_IWUSR,
+ show_ir_low_threshold, store_ir_low_threshold, 0);
+static IIO_DEVICE_ATTR(ir_high_threshold, S_IRUGO | S_IWUSR,
+ show_ir_high_threshold, store_ir_high_threshold, 0);
+
+static IIO_DEVICE_ATTR(name, S_IRUGO, show_name, NULL, 0);
+
+static struct attribute *isl29028_attributes[] = {
+ &iio_dev_attr_name.dev_attr.attr,
+
+ &iio_dev_attr_ir_value.dev_attr.attr,
+
+ &iio_dev_attr_als_low_threshold.dev_attr.attr,
+ &iio_dev_attr_als_high_threshold.dev_attr.attr,
+ &iio_dev_attr_als_persist.dev_attr.attr,
+ &iio_dev_attr_als_ir_mode.dev_attr.attr,
+ &iio_dev_attr_als_value.dev_attr.attr,
+ &iio_dev_attr_wait_als_ir_thres.dev_attr.attr,
+ &iio_dev_attr_ir_low_threshold.dev_attr.attr,
+ &iio_dev_attr_ir_high_threshold.dev_attr.attr,
+
+ &iio_dev_attr_proximity_low_threshold.dev_attr.attr,
+ &iio_dev_attr_proximity_high_threshold.dev_attr.attr,
+ &iio_dev_attr_proximity_enable.dev_attr.attr,
+ &iio_dev_attr_proximity_period.dev_attr.attr,
+ &iio_dev_attr_proximity_persist.dev_attr.attr,
+ &iio_dev_attr_proximity_value.dev_attr.attr,
+ &iio_dev_attr_wait_proxim_thres.dev_attr.attr,
+ NULL
+};
+
+static const struct attribute_group isl29108_group = {
+ .attrs = isl29028_attributes,
+};
+
+static int isl29028_chip_init(struct i2c_client *client)
+{
+ struct isl29028_chip *chip = i2c_get_clientdata(client);
+ int i;
+ bool st;
+
+ for (i = 0; i < ARRAY_SIZE(chip->reg_cache); i++)
+ chip->reg_cache[i] = 0;
+
+ chip->is_prox_enable = 0;
+ chip->prox_low_thres = 0;
+ chip->prox_high_thres = 0xFF;
+ chip->prox_period = 0;
+ chip->prox_reading = 0;
+
+ chip->als_low_thres = 0;
+ chip->als_high_thres = 0xFFF;
+ chip->als_range = 0;
+ chip->als_reading = 0;
+ chip->als_ir_mode = 0;
+
+ chip->ir_high_thres = 0xFFF;
+ chip->ir_low_thres = 0;
+ chip->ir_reading = 0;
+
+ chip->is_int_enable = false;
+ chip->prox_persist = 1;
+ chip->als_persist = 1;
+ chip->is_proxim_int_waiting = false;
+ chip->is_als_int_waiting = false;
+
+ st = isl29028_write_data(client, ISL29028_REG_ADD_TEST1_MODE,
+ 0x0, 0xFF, 0);
+ if (st)
+ st = isl29028_write_data(client, ISL29028_REG_ADD_TEST2_MODE,
+ 0x0, 0xFF, 0);
+ if (st)
+ st = isl29028_write_data(client, ISL29028_REG_ADD_CONFIGURE,
+ 0x0, 0xFF, 0);
+ if (st)
+ msleep(1);
+ if (!st) {
+ dev_err(&client->dev, "%s(): fails\n", __func__);
+ return -ENODEV;
+ }
+ return 0;
+}
+
+static irqreturn_t threshold_isr(int irq, void *irq_data)
+{
+ struct isl29028_chip *chip = (struct isl29028_chip *)irq_data;
+ s32 int_reg;
+ struct i2c_client *client = chip->client;
+
+ int_reg = i2c_smbus_read_byte_data(client, ISL29028_REG_ADD_INTERRUPT);
+ if (int_reg < 0) {
+ dev_err(&client->dev, "Error in reading register %d, error %d\n",
+ ISL29028_REG_ADD_INTERRUPT, int_reg);
+ return IRQ_HANDLED;
+ }
+
+ if (int_reg & INTERRUPT_PROX_FLAG_MASK) {
+ /* Write 0 to clear */
+ isl29028_write_data(client,
+ ISL29028_REG_ADD_INTERRUPT, INTERRUPT_PROX_FLAG_DIS,
+ INTERRUPT_PROX_FLAG_MASK, INTERRUPT_PROX_FLAG_SH);
+ if (chip->is_proxim_int_waiting)
+ complete(&chip->prox_completion);
+ }
+
+ if (int_reg & INTERRUPT_ALS_FLAG_MASK) {
+ /* Write 0 to clear */
+ isl29028_write_data(client,
+ ISL29028_REG_ADD_INTERRUPT, INTERRUPT_ALS_FLAG_DIS,
+ INTERRUPT_ALS_FLAG_MASK, INTERRUPT_ALS_FLAG_SH);
+ if (chip->is_als_int_waiting)
+ complete(&chip->als_completion);
+ }
+
+ return IRQ_HANDLED;
+}
+
+static int __devinit isl29028_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct isl29028_chip *chip;
+ int err;
+
+ dev_dbg(&client->dev, "%s() called\n", __func__);
+
+ chip = kzalloc(sizeof(struct isl29028_chip), GFP_KERNEL);
+ if (!chip) {
+ dev_err(&client->dev, "Memory allocation fails\n");
+ err = -ENOMEM;
+ goto exit;
+ }
+
+ i2c_set_clientdata(client, chip);
+ chip->client = client;
+ chip->irq = client->irq;
+
+ mutex_init(&chip->lock);
+
+ err = isl29028_chip_init(client);
+ if (err)
+ goto exit_free;
+
+ init_completion(&chip->prox_completion);
+ init_completion(&chip->als_completion);
+
+ if (chip->irq > 0) {
+ err = request_threaded_irq(chip->irq, NULL, threshold_isr,
+ IRQF_SHARED, "ISL29028", chip);
+ if (err) {
+ dev_err(&client->dev, "Unable to register irq %d; "
+ "error %d\n", chip->irq, err);
+ goto exit_free;
+ }
+ }
+
+ chip->is_int_enable = true;
+ chip->indio_dev = iio_allocate_device();
+ if (!chip->indio_dev) {
+ dev_err(&client->dev, "iio allocation fails\n");
+ goto exit_irq;
+ }
+
+ chip->indio_dev->attrs = &isl29108_group;
+ chip->indio_dev->dev.parent = &client->dev;
+ chip->indio_dev->dev_data = (void *)(chip);
+ chip->indio_dev->driver_module = THIS_MODULE;
+ chip->indio_dev->modes = INDIO_DIRECT_MODE;
+ err = iio_device_register(chip->indio_dev);
+ if (err) {
+ dev_err(&client->dev, "iio registration fails\n");
+ goto exit_iio_free;
+ }
+ dev_dbg(&client->dev, "%s() success\n", __func__);
+ return 0;
+
+exit_iio_free:
+ iio_free_device(chip->indio_dev);
+exit_irq:
+ if (chip->irq > 0)
+ free_irq(chip->irq, chip);
+exit_free:
+ kfree(chip);
+exit:
+ return err;
+}
+
+static int __devexit isl29028_remove(struct i2c_client *client)
+{
+ struct isl29028_chip *chip = i2c_get_clientdata(client);
+
+ dev_dbg(&client->dev, "%s()\n", __func__);
+ iio_device_unregister(chip->indio_dev);
+ if (chip->irq > 0)
+ free_irq(chip->irq, chip);
+ kfree(chip);
+ return 0;
+}
+
+static const struct i2c_device_id isl29028_id[] = {
+ {"isl29028", 0},
+ {}
+};
+
+MODULE_DEVICE_TABLE(i2c, isl29028_id);
+
+static struct i2c_driver isl29028_driver = {
+ .class = I2C_CLASS_HWMON,
+ .driver = {
+ .name = "isl29028",
+ .owner = THIS_MODULE,
+ },
+ .probe = isl29028_probe,
+ .remove = __devexit_p(isl29028_remove),
+ .id_table = isl29028_id,
+};
+
+static int __init isl29028_init(void)
+{
+ return i2c_add_driver(&isl29028_driver);
+}
+
+static void __exit isl29028_exit(void)
+{
+ i2c_del_driver(&isl29028_driver);
+}
+
+module_init(isl29028_init);
+module_exit(isl29028_exit);