summaryrefslogtreecommitdiff
path: root/drivers
diff options
context:
space:
mode:
Diffstat (limited to 'drivers')
-rw-r--r--drivers/input/misc/Kconfig6
-rw-r--r--drivers/input/misc/Makefile1
-rw-r--r--drivers/input/misc/cm3218.c365
-rw-r--r--drivers/staging/iio/light/Kconfig6
-rw-r--r--drivers/staging/iio/light/Makefile1
-rw-r--r--drivers/staging/iio/light/cm3218.c576
6 files changed, 583 insertions, 372 deletions
diff --git a/drivers/input/misc/Kconfig b/drivers/input/misc/Kconfig
index c97a656f708e..6db78298398d 100644
--- a/drivers/input/misc/Kconfig
+++ b/drivers/input/misc/Kconfig
@@ -684,12 +684,6 @@ config INPUT_CAPELLA_CM3217
help
Say Y here to enable the CM3217 Ambient Light Sensor.
-config INPUT_CAPELLA_CM3218
- tristate "CM3218 light sensor"
- depends on I2C
- help
- Say Y here to enable the CM3218 Ambient Light Sensor.
-
source "drivers/input/misc/compass/Kconfig"
source "drivers/input/misc/mpu/Kconfig"
diff --git a/drivers/input/misc/Makefile b/drivers/input/misc/Makefile
index 9426a48ffb12..2594cf7e693e 100644
--- a/drivers/input/misc/Makefile
+++ b/drivers/input/misc/Makefile
@@ -21,7 +21,6 @@ obj-$(CONFIG_INPUT_ATLAS_BTNS) += atlas_btns.o
obj-$(CONFIG_INPUT_BFIN_ROTARY) += bfin_rotary.o
obj-$(CONFIG_INPUT_BMA150) += bma150.o
obj-$(CONFIG_INPUT_CAPELLA_CM3217) += cm3217.o
-obj-$(CONFIG_INPUT_CAPELLA_CM3218) += cm3218.o
obj-$(CONFIG_INPUT_CM109) += cm109.o
obj-$(CONFIG_INPUT_CMA3000) += cma3000_d0x.o
obj-$(CONFIG_INPUT_CMA3000_I2C) += cma3000_d0x_i2c.o
diff --git a/drivers/input/misc/cm3218.c b/drivers/input/misc/cm3218.c
deleted file mode 100644
index e564317afe89..000000000000
--- a/drivers/input/misc/cm3218.c
+++ /dev/null
@@ -1,365 +0,0 @@
-/* drivers/input/misc/cm3218.c - cm3218 Ambient Light Sensor driver
- *
- * Copyright (c) 2012-2013, NVIDIA CORPORATION. All Rights Reserved.
- *
- * 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.
- *
- */
-
-#include <linux/delay.h>
-#include <linux/i2c.h>
-#include <linux/input.h>
-#include <linux/module.h>
-#include <linux/platform_device.h>
-#include <linux/lightsensor.h>
-#include <linux/workqueue.h>
-#include <linux/miscdevice.h>
-#include <linux/errno.h>
-#include <linux/err.h>
-#include <linux/slab.h>
-#include <linux/uaccess.h>
-#include <linux/wakelock.h>
-#include <linux/jiffies.h>
-#include <asm/mach-types.h>
-#include <asm/setup.h>
-#include <linux/regulator/consumer.h>
-
-#define ALS_CMD_SET 0x00
-#define ALS_CMD_DATA 0x04
-#define ALS_CMD_INIT_VALUE 0x0005;
-#define ALS_POWER_MASK 0x0001
-#define ALS_POWER_ON_VAL 0x0000
-#define IS_ALS_POWER_ON \
- (((chip_info->als_set_reg) & ALS_POWER_MASK) == ALS_POWER_ON_VAL)
-#define ALS_POWER_ENABLE ((chip_info->als_set_reg) &= 0xFFFE)
-#define ALS_POWER_DISABLE ((chip_info->als_set_reg) |= 0x0001)
-
-#define LS_POLLING_DELAY 1000 /* mSec */
-#define REGULATOR_LATENCY 1
-
-static void report_do_work(struct work_struct *w);
-static DECLARE_DELAYED_WORK(report_work, report_do_work);
-
-struct cm3218_info {
- struct class *cls;
- struct i2c_client *client;
- struct input_dev *inp_dev;
- struct workqueue_struct *wq;
- struct regulator *vdd_sensor;
- int polling_delay;
- u16 als_set_reg;
- struct mutex lock;
- bool is_als_on_before_suspend;
- int shutdown_complete;
-};
-
-static struct cm3218_info *chip_info;
-
-static inline int cm3218_read(void)
-{
- int ret = 0;
- mutex_lock(&chip_info->lock);
- if (chip_info && chip_info->shutdown_complete) {
- mutex_unlock(&chip_info->lock);
- return -ENODEV;
- }
-
- ret = i2c_smbus_read_word_data(chip_info->client, ALS_CMD_DATA);
- mutex_unlock(&chip_info->lock);
- return ret;
-}
-
-static inline int cm3218_write(void)
-{
- int ret = 0;
- mutex_lock(&chip_info->lock);
- if (chip_info && chip_info->shutdown_complete) {
- mutex_unlock(&chip_info->lock);
- return -ENODEV;
- }
- ret = i2c_smbus_write_word_data(chip_info->client,
- ALS_CMD_SET, chip_info->als_set_reg);
- mutex_unlock(&chip_info->lock);
- return ret;
-}
-
-
-static void report_alsensor_input_event(void)
-{
- int ret = 0;
-
- ret = cm3218_read();
- if (ret < 0) {
- pr_err("[CM3218] %s: Error read value=%d\n", __func__, ret);
- return;
- }
-
- mutex_lock(&chip_info->lock);
- input_report_abs(chip_info->inp_dev, ABS_MISC, ret);
- input_sync(chip_info->inp_dev);
- mutex_unlock(&chip_info->lock);
-}
-
-static void report_do_work(struct work_struct *work)
-{
- report_alsensor_input_event();
- queue_delayed_work(chip_info->wq,
- &report_work,
- chip_info->polling_delay);
-}
-
-static int cm3218_power(int on)
-{
- int ret = 0;
- if (on && !IS_ALS_POWER_ON) {
- regulator_enable(chip_info->vdd_sensor);
- msleep(REGULATOR_LATENCY);
- ALS_POWER_ENABLE;
- ret = cm3218_write();
- if (ret < 0)
- return ret;
- chip_info->inp_dev->enabled = true;
- queue_delayed_work(chip_info->wq, &report_work, 0);
- } else if (!on && IS_ALS_POWER_ON) {
- ALS_POWER_DISABLE;
- ret = cm3218_write();
- if (ret < 0)
- return ret;
- chip_info->inp_dev->enabled = false;
- cancel_delayed_work(&report_work);
- regulator_disable(chip_info->vdd_sensor);
- }
- return 0;
-}
-
-static int cm3218_enable(struct input_dev *dev)
-{
- pr_debug("[LS][CM3218] %s\n", __func__);
- if (chip_info->is_als_on_before_suspend) {
- cm3218_power(true);
- } else {
- regulator_enable(chip_info->vdd_sensor);
- msleep(REGULATOR_LATENCY);
- cm3218_write();
- regulator_disable(chip_info->vdd_sensor);
- }
- return 0;
-}
-
-static int cm3218_disable(struct input_dev *dev)
-{
- pr_debug("[LS][CM3218] %s\n", __func__);
- chip_info->is_als_on_before_suspend = IS_ALS_POWER_ON;
- if (IS_ALS_POWER_ON)
- cm3218_power(false);
- return 0;
-}
-
-static int lightsensor_open(struct inode *inode, struct file *file)
-{
- pr_debug("[CM3218] %s\n", __func__);
- return 0;
-}
-
-static int lightsensor_release(struct inode *inode, struct file *file)
-{
- pr_debug("[CM3218] %s\n", __func__);
- return 0;
-}
-
-static long lightsensor_ioctl(struct file *file, unsigned int cmd,
- unsigned long arg) {
- int rc = 0;
- int val;
- unsigned long delay;
-
- switch (cmd) {
- case LIGHTSENSOR_IOCTL_ENABLE:
- if (get_user(val, (unsigned long __user *)arg)) {
- rc = -EFAULT;
- break;
- }
- pr_debug("[CM3218] %s LIGHTSENSOR_IOCTL_ENABLE, value = %d\n",
- __func__, val);
- rc = cm3218_power(val);
- break;
-
- case LIGHTSENSOR_IOCTL_GET_ENABLED:
- val = IS_ALS_POWER_ON;
- pr_debug("[CM3218] %s LIGHTSENSOR_IOCTL_GET_ENABLED, enabled %d\n",
- __func__, val);
- rc = put_user(val, (unsigned long __user *)arg);
- break;
-
- case LIGHTSENSOR_IOCTL_SET_DELAY:
- if (get_user(delay, (unsigned long __user *)arg)) {
- rc = -EFAULT;
- break;
- }
- pr_debug("[CM3218] %s LIGHTSENSOR_IOCTL_SET_DELAY, delay %ld\n",
- __func__, delay);
- delay = delay / 1000000;
- chip_info->polling_delay = msecs_to_jiffies(delay);
- break;
-
- default:
- pr_err("[CM3218 error]%s: invalid cmd %d\n",
- __func__, _IOC_NR(cmd));
- rc = -EINVAL;
- }
-
- return rc;
-}
-
-static const struct file_operations lightsensor_fops = {
- .owner = THIS_MODULE,
- .open = lightsensor_open,
- .release = lightsensor_release,
- .unlocked_ioctl = lightsensor_ioctl,
-};
-
-static struct miscdevice lightsensor_misc = {
- .minor = MISC_DYNAMIC_MINOR,
- .name = "lightsensor",
- .fops = &lightsensor_fops,
-};
-
-
-static int cm3218_probe(struct i2c_client *client,
- const struct i2c_device_id *id)
-{
- int ret = 0;
-
- chip_info =
- kzalloc(sizeof(struct cm3218_info), GFP_KERNEL);
-
- if (!chip_info)
- return -ENOMEM;
- chip_info->vdd_sensor = regulator_get(NULL, "vdd_sensor_2v85");
-
- if (IS_ERR_OR_NULL(chip_info->vdd_sensor)) {
- pr_err("[CM3218 error]%s: regulator_get failed\n",
- __func__);
- return PTR_ERR(chip_info->vdd_sensor);
- }
- i2c_set_clientdata(client, chip_info);
- chip_info->client = client;
-
- mutex_init(&chip_info->lock);
-
- chip_info->inp_dev = input_allocate_device();
- if (!chip_info->inp_dev) {
- pr_err("[CM3218 error]%s: could not allocate input device\n",
- __func__);
- ret = -ENOMEM;
- goto err_input_allocate;
- }
-
- chip_info->inp_dev->name = "cm3218-ls";
- chip_info->inp_dev->enable = cm3218_enable;
- chip_info->inp_dev->disable = cm3218_disable;
- chip_info->inp_dev->enabled = false;
-
- set_bit(EV_ABS, chip_info->inp_dev->evbit);
- input_set_abs_params(chip_info->inp_dev, ABS_MISC, 0, 9, 0, 0);
-
- ret = input_register_device(chip_info->inp_dev);
- if (ret < 0) {
- pr_err("[CM3218 error]%s:can not register input device\n",
- __func__);
- goto err_input_register_device;
- }
-
- ret = misc_register(&lightsensor_misc);
- if (ret < 0) {
- pr_err("[CM3218 error]%s:can not register misc device\n",
- __func__);
- goto err_misc_register;
- }
-
- chip_info->wq = create_singlethread_workqueue("cm3218_wq");
- if (!chip_info->wq) {
- pr_err("[CM3218 error]%s: can't create workqueue\n", __func__);
- ret = -ENOMEM;
- goto err_create_singlethread_workqueue;
- }
-
- chip_info->cls = class_create(THIS_MODULE, "optical_sensors");
- if (IS_ERR(chip_info->cls)) {
- ret = PTR_ERR(chip_info->cls);
- chip_info->cls = NULL;
- goto err_class_create;
- }
-
- chip_info->polling_delay = msecs_to_jiffies(LS_POLLING_DELAY);
- chip_info->als_set_reg = ALS_CMD_INIT_VALUE;
- regulator_enable(chip_info->vdd_sensor);
- msleep(REGULATOR_LATENCY);
- cm3218_write();
- regulator_disable(chip_info->vdd_sensor);
- chip_info->shutdown_complete = 0;
- pr_info("[CM3218] probe success");
- return ret;
-
-
-err_class_create:
-err_create_singlethread_workqueue:
- misc_deregister(&lightsensor_misc);
-err_misc_register:
- input_unregister_device(chip_info->inp_dev);
-err_input_register_device:
- input_free_device(chip_info->inp_dev);
-err_input_allocate:
- mutex_destroy(&chip_info->lock);
- kfree(chip_info);
- return ret;
-}
-
-static void cm3218_shutdown(struct i2c_client *client)
-{
- mutex_lock(&chip_info->lock);
- if (IS_ALS_POWER_ON) {
- cancel_delayed_work_sync(&report_work);
- }
- chip_info->shutdown_complete = 1;
- mutex_unlock(&chip_info->lock);
-}
-
-static const struct i2c_device_id cm3218_id[] = {
- {"cm3218", 0},
- {}
-};
-
-static struct i2c_driver cm3218_driver = {
- .id_table = cm3218_id,
- .probe = cm3218_probe,
- .driver = {
- .name = "cm3218",
- .owner = THIS_MODULE,
- },
- .shutdown = cm3218_shutdown,
-};
-
-static int __init cm3218_init(void)
-{
- return i2c_add_driver(&cm3218_driver);
-}
-
-static void __exit cm3218_exit(void)
-{
- i2c_del_driver(&cm3218_driver);
-}
-
-module_init(cm3218_init);
-module_exit(cm3218_exit);
-
-MODULE_LICENSE("GPL");
-MODULE_DESCRIPTION("CM3218 Driver");
-MODULE_AUTHOR("Sri Krishna chowdary <schowdary@nvidia.com>");
diff --git a/drivers/staging/iio/light/Kconfig b/drivers/staging/iio/light/Kconfig
index 46d4ad1ede98..498a8d1a18ce 100644
--- a/drivers/staging/iio/light/Kconfig
+++ b/drivers/staging/iio/light/Kconfig
@@ -56,4 +56,10 @@ config SENSORS_MAX44005
If you say yes here you get support for ambient light, RGB, IR,
Proximity and temperature sensing from MAX44005.
+config SENSORS_CM3218
+ tristate "CM3218 Ambient light sensor"
+ depends on I2C
+ help
+ Say Y here to enable the CM3218 Ambient Light Sensor.
+
endmenu
diff --git a/drivers/staging/iio/light/Makefile b/drivers/staging/iio/light/Makefile
index 9d5d81b220dc..6c307be905d1 100644
--- a/drivers/staging/iio/light/Makefile
+++ b/drivers/staging/iio/light/Makefile
@@ -3,6 +3,7 @@
#
GCOV_PROFILE := y
+obj-$(CONFIG_SENSORS_CM3218) += cm3218.o
obj-$(CONFIG_SENSORS_ISL29018) += isl29018.o
obj-$(CONFIG_SENSORS_ISL29028) += isl29028.o
obj-$(CONFIG_TSL2583) += tsl2583.o
diff --git a/drivers/staging/iio/light/cm3218.c b/drivers/staging/iio/light/cm3218.c
new file mode 100644
index 000000000000..0ccf88038b78
--- /dev/null
+++ b/drivers/staging/iio/light/cm3218.c
@@ -0,0 +1,576 @@
+/* drivers/input/misc/cm3218.c - cm3218 Ambient Light Sensor driver
+ *
+ * Copyright (c) 2012-2013, NVIDIA CORPORATION. All Rights Reserved.
+ *
+ * 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.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/i2c.h>
+#include <linux/err.h>
+#include <linux/mutex.h>
+#include <linux/slab.h>
+#include <linux/regmap.h>
+#include <linux/delay.h>
+#include <linux/pm_runtime.h>
+#include <linux/notifier.h>
+#include <linux/regulator/consumer.h>
+#include <linux/iio/iio.h>
+#include <linux/iio/sysfs.h>
+
+
+#define CM3218_REG_CONFIGURE 0x00
+#define CM3218_REG_ALS_DATA 0x04
+#define CM3218_REG_MAX 0x04
+
+
+#define CONFIGURE_ALS_MASK 0x01
+#define CONFIGURE_ALS_EN 0x00
+
+#define CONFIGURE_SHDN_MASK CONFIGURE_ALS_MASK
+#define CONFIGURE_SHDN_EN 0x01
+
+#define LS_POLLING_DELAY 1000 /* mSec */
+#define I2C_MAX_TIMEOUT msecs_to_jiffies(20) /* 20 mSec */
+
+enum {
+ CHIP_POWER_OFF,
+ CHIP_POWER_ON_ALS_OFF,
+ CHIP_POWER_ON_ALS_ON,
+};
+
+struct cm3218_chip {
+ struct i2c_client *client;
+ struct i2c_device_id *id;
+ struct mutex lock;
+ struct regulator_bulk_data *consumers;
+ struct notifier_block regulator_nb;
+ wait_queue_head_t i2c_wait_queue;
+ int i2c_xfer_ready;
+ struct regmap *regmap;
+
+ u8 als_state;
+ bool is_als_on_before_suspend;
+ int shutdown_complete;
+};
+
+/* regulators used by the device */
+static struct regulator_bulk_data cm3218_consumers[] = {
+ {
+ .supply = "vdd",
+ },
+};
+
+/* device's regmap configuration for i2c communication */
+/* non cacheable registers*/
+bool cm3218_volatile_reg(struct device *dev, unsigned int reg)
+{
+ return reg == CM3218_REG_ALS_DATA;
+}
+
+static const struct reg_default cm3218_reg_defaults = {
+ .reg = 0x00,
+ .def = 0x0400,
+};
+
+/* TODO * in linux-next we have to add
+ * val_format_endian to take care of endianness
+ * use regmap_access_table instead of volatile_reg call backs
+ */
+static const struct regmap_config cm3218_regmap_config = {
+ .reg_bits = 8,
+ .val_bits = 16,
+ .volatile_reg = &cm3218_volatile_reg,
+ .max_register = CM3218_REG_MAX,
+ .reg_defaults = &cm3218_reg_defaults,
+ .num_reg_defaults = 1,
+ .cache_type = REGCACHE_RBTREE,
+};
+
+/* device's read/write functionality and a helper */
+static void change_endianness_16(int *val)
+{
+ u8 *buf = val;
+ u8 temp = buf[0];
+ buf[0] = buf[1];
+ buf[1] = temp;
+}
+
+static int _cm3218_register_read(struct cm3218_chip *chip, int reg, int *val)
+{
+ int ret;
+ int temp;
+
+ if (!chip->regmap)
+ return -ENODEV;
+
+ ret = wait_event_timeout(chip->i2c_wait_queue,
+ chip->i2c_xfer_ready, I2C_MAX_TIMEOUT);
+ if (!ret) {
+ dev_err(&chip->client->dev, "idname:%s func:%s line:%d " \
+ "error_msg: device not ready for i2c xfer\n",
+ chip->id->name, __func__, __LINE__);
+ return -ETIMEDOUT;
+ }
+
+ mutex_lock(&chip->lock);
+ ret = regmap_read(chip->regmap, reg, val);
+ if (ret)
+ dev_err(&chip->client->dev, "idname:%s func:%s line:%d " \
+ "error_msg:regmap_read fails\n",
+ chip->id->name, __func__, __LINE__);
+
+ change_endianness_16(val);
+/*
+ temp = i2c_smbus_read_word_data(chip->client, CM3218_REG_ALS_DATA);
+ dev_err(&chip->client->dev, "idname:%s func:%s line:%d " \
+ "als_i2c_data = %d\n",
+ chip->id->name, __func__, __LINE__, temp);
+*/ mutex_unlock(&chip->lock);
+ return ret;
+}
+
+static int _cm3218_register_write(struct cm3218_chip *chip, int reg, int mask,
+ int val)
+{
+ int ret, value;
+
+ if (!chip->regmap)
+ return -ENODEV;
+
+ ret = wait_event_timeout(chip->i2c_wait_queue,
+ chip->i2c_xfer_ready, I2C_MAX_TIMEOUT);
+ if (!ret) {
+ dev_err(&chip->client->dev, "idname:%s func:%s line:%d " \
+ "error_msg: device not ready for i2c xfer\n",
+ chip->id->name, __func__, __LINE__);
+ return -ETIMEDOUT;
+ }
+
+ mutex_lock(&chip->lock);
+ change_endianness_16(&val);
+ ret = regmap_update_bits(chip->regmap, reg, mask, val);
+ if (ret)
+ dev_err(&chip->client->dev, "idname:%s func:%s line:%d " \
+ "error_msg:regmap_write fails\n",
+ chip->id->name, __func__, __LINE__);
+ mutex_unlock(&chip->lock);
+
+ return ret;
+}
+
+/* device's registration with iio to facilitate user operations */
+static ssize_t cm3218_chan_regulator_enable(struct iio_dev *indio_dev,
+ uintptr_t private, struct iio_chan_spec const *chan, char *buf)
+{
+ u8 enable;
+ int ret = 0;
+ struct cm3218_chip *chip = iio_priv(indio_dev);
+
+ if (kstrtou8(buf, 10, &enable))
+ return -EINVAL;
+
+ if ((enable != 0) && (enable != 1))
+ return -EINVAL;
+
+ if (chan->type != IIO_LIGHT)
+ return -EINVAL;
+
+ if (enable == (chip->als_state != CHIP_POWER_OFF))
+ return 1;
+
+ if (!chip->consumers)
+ goto success;
+
+ if (enable)
+ ret = regulator_bulk_enable(1, chip->consumers);
+ else
+ ret = regulator_bulk_disable(1, chip->consumers);
+
+ if (ret) {
+ dev_err(&chip->client->dev, "idname:%s func:%s line:%d " \
+ "error_msg:_cm3218_register_read fails\n",
+ chip->id->name, __func__, __LINE__);
+ goto fail;
+ }
+
+success:
+ chip->als_state = enable;
+fail:
+ return ret ? ret : 1;
+}
+
+static ssize_t cm3218_chan_enable(struct iio_dev *indio_dev, uintptr_t private,
+ struct iio_chan_spec const *chan, char *buf)
+{
+ u8 enable;
+ int ret;
+ struct cm3218_chip *chip = iio_priv(indio_dev);
+
+ if (kstrtou8(buf, 10, &enable))
+ return -EINVAL;
+
+ if ((enable != 0) && (enable != 1))
+ return -EINVAL;
+
+ if (chip->als_state == CHIP_POWER_OFF) {
+ dev_err(&chip->client->dev, "idname:%s func:%s line:%d " \
+ "error_msg:please enable regulator first\n",
+ chip->id->name, __func__, __LINE__);
+ return -EINVAL;
+ }
+
+ if (!((enable && (chip->als_state == CHIP_POWER_ON_ALS_OFF)) ||
+ (!enable && (chip->als_state == CHIP_POWER_ON_ALS_ON))))
+ return -EINVAL;
+
+ if (chan->type != IIO_LIGHT)
+ return -EINVAL;
+
+ /* a small optimization*/
+ if (enable == (chip->als_state - 1))
+ goto success;
+
+ if (enable) {
+ ret = _cm3218_register_write(chip, CM3218_REG_CONFIGURE,
+ CONFIGURE_ALS_MASK,
+ CONFIGURE_ALS_EN);
+ if (ret)
+ return ret;
+ } else {
+ ret = _cm3218_register_write(chip, CM3218_REG_CONFIGURE,
+ CONFIGURE_ALS_MASK,
+ !CONFIGURE_ALS_EN);
+ if (ret)
+ return ret;
+ }
+
+success:
+ /* a small optimization*/
+ chip->als_state = enable + 1;
+ return ret ? ret : 1;
+}
+
+
+/* chan_regulator_enable is used to enable regulators used by
+ * particular channel.
+ * chan_enable actually configures various registers to activate
+ * a particular channel.
+ */
+static const struct iio_chan_spec_ext_info cm3218_ext_info[] = {
+ {
+ .name = "regulator_enable",
+ .write = cm3218_chan_regulator_enable,
+ }, {
+ .name = "enable",
+ .write = cm3218_chan_enable,
+ },
+ NULL,
+};
+
+static const struct iio_chan_spec cm3218_channels[] = {
+ {
+ .type = IIO_LIGHT,
+ .ext_info = cm3218_ext_info,
+ },
+};
+
+static int cm3218_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan, int *val, int *val2, long mask)
+{
+ struct cm3218_chip *chip = iio_priv(indio_dev);
+ int ret;
+ int value;
+
+ if (chip->als_state != CHIP_POWER_ON_ALS_ON)
+ return -EINVAL;
+
+ if (chan->type != IIO_LIGHT)
+ return -EINVAL;
+
+ ret = _cm3218_register_read(chip, CM3218_REG_ALS_DATA, &value);
+ if (ret)
+ dev_err(&chip->client->dev, "idname:%s func:%s line:%d " \
+ "error_msg:_cm3218_register_read fails\n",
+ chip->id->name, __func__, __LINE__);
+
+ if (!ret) {
+ *val = value;
+ ret = IIO_VAL_INT;
+ }
+ return ret;
+}
+
+/* read_raw is used to report a channel's data to user
+ * in non SI units
+ */
+static const struct iio_info cm3218_iio_info = {
+ .driver_module = THIS_MODULE,
+ .read_raw = &cm3218_read_raw,
+};
+
+/* chip's power management helpers */
+static int cm3218_activate_standby_mode(struct cm3218_chip *chip)
+{
+ int ret;
+ ret = _cm3218_register_write(chip, CM3218_REG_CONFIGURE,
+ CONFIGURE_SHDN_MASK,
+ CONFIGURE_SHDN_EN);
+ return 0;
+}
+
+/* this detects the regulator enable/disable event and puts
+ * the device to low power state if this device does not use the regulator */
+static int cm3218_power_manager(struct notifier_block *regulator_nb,
+ unsigned long event, void *v)
+{
+ struct cm3218_chip *chip;
+
+ chip = container_of(regulator_nb, struct cm3218_chip, regulator_nb);
+
+ if (event & (REGULATOR_EVENT_POST_ENABLE |
+ REGULATOR_EVENT_OUT_POSTCHANGE)) {
+ chip->i2c_xfer_ready = 1;
+ cm3218_activate_standby_mode(chip);
+ } else if (event & (REGULATOR_EVENT_DISABLE |
+ REGULATOR_EVENT_FORCE_DISABLE)) {
+ chip->i2c_xfer_ready = 0;
+ }
+ return NOTIFY_OK;
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int cm3218_suspend(struct device *dev)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ struct iio_dev *indio_dev = i2c_get_clientdata(client);
+ struct cm3218_chip *chip = iio_priv(indio_dev);
+ int ret = 0;
+
+ if (!chip->consumers)
+ return 0;
+
+
+ /* assumes all other devices stop using this regulator */
+ if (chip->als_state != CHIP_POWER_OFF)
+ ret = regulator_bulk_disable(1, chip->consumers);
+
+ if (ret) {
+ dev_err(&chip->client->dev, "idname:%s func:%s line:%d " \
+ "error_msg:regulator_bulk_disable fails" \
+ "\ncm3218_suspend fails\n",
+ chip->id->name, __func__, __LINE__);
+ return ret;
+ }
+
+ if (!ret && regulator_is_enabled(chip->consumers[0].consumer))
+ ret = cm3218_activate_standby_mode(chip);
+ if (ret)
+ dev_err(&chip->client->dev, "idname:%s func:%s line:%d " \
+ "error_msg:cm3218_activate_standby_mode fails" \
+ "\ncm3218_suspend fails\n",
+ chip->id->name, __func__, __LINE__);
+ return ret;
+}
+
+static int cm3218_resume(struct device *dev)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ struct iio_dev *indio_dev = i2c_get_clientdata(client);
+ struct cm3218_chip *chip = iio_priv(indio_dev);
+ int ret = 0;
+ int temp;
+
+ if (chip->als_state == CHIP_POWER_OFF)
+ return 0;
+
+ ret = regulator_bulk_enable(1, cm3218_consumers);
+ if (ret) {
+ dev_err(&chip->client->dev, "idname:%s func:%s line:%d " \
+ "error_msg:regulator_bulk_enable fails" \
+ "\ncm3218_suspend fails\n",
+ chip->id->name, __func__, __LINE__);
+ return ret;
+ }
+
+ mutex_lock(&chip->lock);
+ if (chip->als_state == CHIP_POWER_ON_ALS_ON)
+ ret = _cm3218_register_write(chip, CM3218_REG_CONFIGURE,
+ CONFIGURE_ALS_MASK,
+ CONFIGURE_ALS_EN);
+ if (ret) {
+ dev_err(&chip->client->dev, "idname:%s func:%s line:%d " \
+ "error_msg:_cm3218_register_write fails" \
+ "\ncm3218_suspend fails\n",
+ chip->id->name, __func__, __LINE__);
+ /* fall back to CHIP_POWER_OFF, we don't need partial resume */
+ do {
+ temp = regulator_bulk_disable(1, cm3218_consumers);
+ } while (temp);
+ }
+ mutex_unlock(&chip->lock);
+ return ret;
+}
+
+static SIMPLE_DEV_PM_OPS(cm3218_pm_ops, cm3218_suspend, cm3218_resume);
+#define CM3218_PM_OPS (&cm3218_pm_ops)
+#else
+#define CM3218_PM_OPS NULL
+#endif
+
+
+/* device's i2c registration */
+static int cm3218_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ int ret = 0;
+ struct cm3218_chip *chip;
+ struct iio_dev *indio_dev;
+ struct regmap *regmap;
+ u8 *buf;
+
+ indio_dev = iio_device_alloc(sizeof(*chip));
+ if (indio_dev == NULL) {
+ dev_err(&client->dev, "idname:%s func:%s line:%d " \
+ "error_msg:iio_device_alloc fails\n",
+ id->name, __func__, __LINE__);
+ return -ENOMEM;
+ }
+ chip = iio_priv(indio_dev);
+
+ i2c_set_clientdata(client, indio_dev);
+ chip->client = client;
+ mutex_init(&chip->lock);
+
+ regmap = devm_regmap_init_i2c(client, &cm3218_regmap_config);
+ if (IS_ERR_OR_NULL(regmap)) {
+ dev_err(&client->dev, "idname:%s func:%s line:%d " \
+ "error_msg:devm_regmap_init_i2c fails\n",
+ id->name, __func__, __LINE__);
+ return -ENOMEM;
+ }
+ chip->regmap = regmap;
+
+ ret = devm_regulator_bulk_get(&client->dev,
+ ARRAY_SIZE(cm3218_consumers),
+ cm3218_consumers);
+ if (ret)
+ dev_err(&client->dev, "idname:%s func:%s line:%d " \
+ "error_msg:regulator_get fails\n",
+ id->name, __func__, __LINE__);
+ else
+ chip->consumers = cm3218_consumers;
+
+ if (chip->consumers) {
+ chip->regulator_nb.notifier_call = cm3218_power_manager;
+ ret = regulator_register_notifier(chip->consumers[0].consumer,
+ &chip->regulator_nb);
+ if (ret)
+ dev_err(&client->dev, "idname:%s func:%s line:%d " \
+ "error_msg:regulator_register_notifier fails\n",
+ id->name, __func__, __LINE__);
+ }
+
+ indio_dev->info = &cm3218_iio_info;
+ indio_dev->channels = cm3218_channels;
+ indio_dev->num_channels = 1;
+ indio_dev->name = id->name;
+ indio_dev->dev.parent = &client->dev;
+ indio_dev->modes = INDIO_DIRECT_MODE;
+ ret = iio_device_register(indio_dev);
+ if (ret) {
+ dev_err(&client->dev, "idname:%s func:%s line:%d " \
+ "error_msg:iio_device_register fails\n",
+ id->name, __func__, __LINE__);
+ goto err_iio_free;
+ }
+
+ init_waitqueue_head(&chip->i2c_wait_queue);
+ chip->als_state = 0;
+ if (regulator_is_enabled(chip->consumers[0].consumer)) {
+ chip->i2c_xfer_ready = 1;
+ cm3218_activate_standby_mode(chip);
+ }
+
+ dev_info(&client->dev, "idname:%s func:%s line:%d " \
+ "probe success\n",
+ id->name, __func__, __LINE__);
+
+ return 0;
+
+err_iio_free:
+ mutex_destroy(&chip->lock);
+ iio_device_free(indio_dev);
+ return ret;
+}
+
+static void cm3218_shutdown(struct i2c_client *client)
+{
+ struct iio_dev *indio_dev = i2c_get_clientdata(client);
+ struct cm3218_chip *chip = iio_priv(indio_dev);
+ mutex_lock(&chip->lock);
+ chip->shutdown_complete = 1;
+ mutex_unlock(&chip->lock);
+}
+
+static int __devexit cm3218_remove(struct i2c_client *client)
+{
+ struct iio_dev *indio_dev = i2c_get_clientdata(client);
+ struct cm3218_chip *chip = iio_priv(indio_dev);
+
+ mutex_destroy(&chip->lock);
+ iio_device_unregister(indio_dev);
+ iio_device_free(indio_dev);
+ return 0;
+}
+
+static const struct i2c_device_id cm3218_id[] = {
+ {"cm3218", 0},
+ {}
+};
+MODULE_DEVICE_TABLE(i2c, cm3218_id);
+
+static const struct of_device_id cm3218_of_match[] = {
+ { .compatible = "capella,cm3218", },
+ { },
+};
+MODULE_DEVICE_TABLE(of, cm3218_of_match);
+
+static struct i2c_driver cm3218_driver = {
+ .class = I2C_CLASS_HWMON,
+ .driver = {
+ .name = "cm3218",
+ .owner = THIS_MODULE,
+ .of_match_table = cm3218_of_match,
+ .pm = CM3218_PM_OPS,
+ },
+ .id_table = cm3218_id,
+ .probe = cm3218_probe,
+ .remove = __devexit_p(cm3218_remove),
+ .shutdown = cm3218_shutdown,
+};
+
+static int __init cm3218_init(void)
+{
+ return i2c_add_driver(&cm3218_driver);
+}
+
+static void __exit cm3218_exit(void)
+{
+ i2c_del_driver(&cm3218_driver);
+}
+
+module_init(cm3218_init);
+module_exit(cm3218_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("CM3218 Driver");
+MODULE_AUTHOR("Sri Krishna chowdary <schowdary@nvidia.com>");