summaryrefslogtreecommitdiff
path: root/drivers/iio/light
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2015-02-15 19:30:39 (GMT)
committerLinus Torvalds <torvalds@linux-foundation.org>2015-02-15 19:30:39 (GMT)
commit46f7b635569731ff81a3b72d1bcd4415b293b637 (patch)
treee97e5e28d1768bb281116d92292851758ea20024 /drivers/iio/light
parent9682ec9692e5ac11c6caebd079324e727b19e7ce (diff)
parent533e80b1ea709577ec5cf73b8b566569bc711259 (diff)
downloadlinux-46f7b635569731ff81a3b72d1bcd4415b293b637.tar.xz
Merge tag 'staging-3.20-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/staging
Pull staging drivers patches from Greg KH: "Here's the big staging driver tree update for 3.20-rc1. Lots of little things in here, adding up to lots of overall cleanups. The IIO driver updates are also in here as they cross the staging tree boundry a lot. I2O has moved into staging as well, as a plan to drop it from the tree eventually as that's a dead subsystem. All of this has been in linux-next with no reported issues for a while" * tag 'staging-3.20-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/staging: (740 commits) staging: lustre: lustre: libcfs: define symbols as static staging: rtl8712: Do coding style cleanup staging: lustre: make obd_updatemax_lock static staging: rtl8188eu: core: switch with redundant cases staging: rtl8188eu: odm: conditional setting with no effect staging: rtl8188eu: odm: condition with no effect staging: ft1000: fix braces warning staging: sm7xxfb: fix remaining CamelCase staging: sm7xxfb: fix CamelCase staging: rtl8723au: multiple condition with no effect - if identical to else staging: sm7xxfb: make smtc_scr_info static staging/lustre/mdc: Initialize req in mdc_enqueue for !it case staging/lustre/clio: Do not allow group locks with gid 0 staging/lustre/llite: don't add to page cache upon failure staging/lustre/llite: Add exception entry check after radix_tree staging/lustre/libcfs: protect kkuc_groups from write access staging/lustre/fld: refer to MDT0 for fld lookup in some cases staging/lustre/llite: Solve a race to access lli_has_smd in read case staging/lustre/ptlrpc: hold rq_lock when modify rq_flags staging/lustre/lnet: portal spreading rotor should be unsigned ...
Diffstat (limited to 'drivers/iio/light')
-rw-r--r--drivers/iio/light/Kconfig24
-rw-r--r--drivers/iio/light/Makefile2
-rw-r--r--drivers/iio/light/cm32181.c2
-rw-r--r--drivers/iio/light/cm3232.c403
-rw-r--r--drivers/iio/light/hid-sensor-als.c9
-rw-r--r--drivers/iio/light/hid-sensor-prox.c10
-rw-r--r--drivers/iio/light/jsa1212.c471
-rw-r--r--drivers/iio/light/lm3533-als.c2
-rw-r--r--drivers/iio/light/tcs3414.c4
9 files changed, 906 insertions, 21 deletions
diff --git a/drivers/iio/light/Kconfig b/drivers/iio/light/Kconfig
index 5bea821..ae68c64 100644
--- a/drivers/iio/light/Kconfig
+++ b/drivers/iio/light/Kconfig
@@ -48,6 +48,17 @@ config CM32181
To compile this driver as a module, choose M here:
the module will be called cm32181.
+config CM3232
+ depends on I2C
+ tristate "CM3232 ambient light sensor"
+ help
+ Say Y here if you use cm3232.
+ This option enables ambient light sensor using
+ Capella Microsystems cm3232 device driver.
+
+ To compile this driver as a module, choose M here:
+ the module will be called cm3232.
+
config CM36651
depends on I2C
tristate "CM36651 driver"
@@ -95,6 +106,9 @@ config HID_SENSOR_ALS
Say yes here to build support for the HID SENSOR
Ambient light sensor.
+ To compile this driver as a module, choose M here: the
+ module will be called hid-sensor-als.
+
config HID_SENSOR_PROX
depends on HID_SENSOR_HUB
select IIO_BUFFER
@@ -109,6 +123,16 @@ config HID_SENSOR_PROX
To compile this driver as a module, choose M here: the
module will be called hid-sensor-prox.
+config JSA1212
+ tristate "JSA1212 ALS and proximity sensor driver"
+ depends on I2C
+ help
+ Say Y here if you want to build a IIO driver for JSA1212
+ proximity & ALS sensor device.
+
+ To compile this driver as a module, choose M here:
+ the module will be called jsa1212.
+
config SENSORS_LM3533
tristate "LM3533 ambient light sensor"
depends on MFD_LM3533
diff --git a/drivers/iio/light/Makefile b/drivers/iio/light/Makefile
index 47877a3..b12a516 100644
--- a/drivers/iio/light/Makefile
+++ b/drivers/iio/light/Makefile
@@ -7,11 +7,13 @@ obj-$(CONFIG_ADJD_S311) += adjd_s311.o
obj-$(CONFIG_AL3320A) += al3320a.o
obj-$(CONFIG_APDS9300) += apds9300.o
obj-$(CONFIG_CM32181) += cm32181.o
+obj-$(CONFIG_CM3232) += cm3232.o
obj-$(CONFIG_CM36651) += cm36651.o
obj-$(CONFIG_GP2AP020A00F) += gp2ap020a00f.o
obj-$(CONFIG_HID_SENSOR_ALS) += hid-sensor-als.o
obj-$(CONFIG_HID_SENSOR_PROX) += hid-sensor-prox.o
obj-$(CONFIG_ISL29125) += isl29125.o
+obj-$(CONFIG_JSA1212) += jsa1212.o
obj-$(CONFIG_SENSORS_LM3533) += lm3533-als.o
obj-$(CONFIG_LTR501) += ltr501.o
obj-$(CONFIG_SENSORS_TSL2563) += tsl2563.o
diff --git a/drivers/iio/light/cm32181.c b/drivers/iio/light/cm32181.c
index ad36b29..5d12ae54 100644
--- a/drivers/iio/light/cm32181.c
+++ b/drivers/iio/light/cm32181.c
@@ -169,7 +169,7 @@ static int cm32181_write_als_it(struct cm32181_chip *cm32181, int val)
* @cm32181: pointer of struct cm32181.
*
* Convert sensor raw data to lux. It depends on integration
- * time and claibscale variable.
+ * time and calibscale variable.
*
* Return: Positive value is lux, otherwise is error code.
*/
diff --git a/drivers/iio/light/cm3232.c b/drivers/iio/light/cm3232.c
new file mode 100644
index 0000000..90e3519
--- /dev/null
+++ b/drivers/iio/light/cm3232.c
@@ -0,0 +1,403 @@
+/*
+ * CM3232 Ambient Light Sensor
+ *
+ * Copyright (C) 2014-2015 Capella Microsystems Inc.
+ * Author: Kevin Tsai <ktsai@capellamicro.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, as published
+ * by the Free Software Foundation.
+ *
+ * IIO driver for CM3232 (7-bit I2C slave address 0x10).
+ */
+
+#include <linux/i2c.h>
+#include <linux/module.h>
+#include <linux/iio/iio.h>
+#include <linux/iio/sysfs.h>
+#include <linux/init.h>
+
+/* Registers Address */
+#define CM3232_REG_ADDR_CMD 0x00
+#define CM3232_REG_ADDR_ALS 0x50
+#define CM3232_REG_ADDR_ID 0x53
+
+#define CM3232_CMD_ALS_DISABLE BIT(0)
+
+#define CM3232_CMD_ALS_IT_SHIFT 2
+#define CM3232_CMD_ALS_IT_MASK (BIT(2) | BIT(3) | BIT(4))
+#define CM3232_CMD_ALS_IT_DEFAULT (0x01 << CM3232_CMD_ALS_IT_SHIFT)
+
+#define CM3232_CMD_ALS_RESET BIT(6)
+
+#define CM3232_CMD_DEFAULT CM3232_CMD_ALS_IT_DEFAULT
+
+#define CM3232_HW_ID 0x32
+#define CM3232_CALIBSCALE_DEFAULT 100000
+#define CM3232_CALIBSCALE_RESOLUTION 100000
+#define CM3232_MLUX_PER_LUX 1000
+
+#define CM3232_MLUX_PER_BIT_DEFAULT 64
+#define CM3232_MLUX_PER_BIT_BASE_IT 100000
+
+static const struct {
+ int val;
+ int val2;
+ u8 it;
+} cm3232_als_it_scales[] = {
+ {0, 100000, 0}, /* 0.100000 */
+ {0, 200000, 1}, /* 0.200000 */
+ {0, 400000, 2}, /* 0.400000 */
+ {0, 800000, 3}, /* 0.800000 */
+ {1, 600000, 4}, /* 1.600000 */
+ {3, 200000, 5}, /* 3.200000 */
+};
+
+struct cm3232_als_info {
+ u8 regs_cmd_default;
+ u8 hw_id;
+ int calibscale;
+ int mlux_per_bit;
+ int mlux_per_bit_base_it;
+};
+
+static struct cm3232_als_info cm3232_als_info_default = {
+ .regs_cmd_default = CM3232_CMD_DEFAULT,
+ .hw_id = CM3232_HW_ID,
+ .calibscale = CM3232_CALIBSCALE_DEFAULT,
+ .mlux_per_bit = CM3232_MLUX_PER_BIT_DEFAULT,
+ .mlux_per_bit_base_it = CM3232_MLUX_PER_BIT_BASE_IT,
+};
+
+struct cm3232_chip {
+ struct i2c_client *client;
+ struct cm3232_als_info *als_info;
+ u8 regs_cmd;
+ u16 regs_als;
+};
+
+/**
+ * cm3232_reg_init() - Initialize CM3232
+ * @chip: pointer of struct cm3232_chip.
+ *
+ * Check and initialize CM3232 ambient light sensor.
+ *
+ * Return: 0 for success; otherwise for error code.
+ */
+static int cm3232_reg_init(struct cm3232_chip *chip)
+{
+ struct i2c_client *client = chip->client;
+ s32 ret;
+
+ chip->als_info = &cm3232_als_info_default;
+
+ /* Identify device */
+ ret = i2c_smbus_read_word_data(client, CM3232_REG_ADDR_ID);
+ if (ret < 0) {
+ dev_err(&chip->client->dev, "Error reading addr_id\n");
+ return ret;
+ }
+
+ if ((ret & 0xFF) != chip->als_info->hw_id)
+ return -ENODEV;
+
+ /* Disable and reset device */
+ chip->regs_cmd = CM3232_CMD_ALS_DISABLE | CM3232_CMD_ALS_RESET;
+ ret = i2c_smbus_write_byte_data(client, CM3232_REG_ADDR_CMD,
+ chip->regs_cmd);
+ if (ret < 0) {
+ dev_err(&chip->client->dev, "Error writing reg_cmd\n");
+ return ret;
+ }
+
+ /* Register default value */
+ chip->regs_cmd = chip->als_info->regs_cmd_default;
+
+ /* Configure register */
+ ret = i2c_smbus_write_byte_data(client, CM3232_REG_ADDR_CMD,
+ chip->regs_cmd);
+ if (ret < 0)
+ dev_err(&chip->client->dev, "Error writing reg_cmd\n");
+
+ return 0;
+}
+
+/**
+ * cm3232_read_als_it() - Get sensor integration time
+ * @chip: pointer of struct cm3232_chip
+ * @val: pointer of int to load the integration (sec).
+ * @val2: pointer of int to load the integration time (microsecond).
+ *
+ * Report the current integration time.
+ *
+ * Return: IIO_VAL_INT_PLUS_MICRO for success, otherwise -EINVAL.
+ */
+static int cm3232_read_als_it(struct cm3232_chip *chip, int *val, int *val2)
+{
+ u16 als_it;
+ int i;
+
+ als_it = chip->regs_cmd;
+ als_it &= CM3232_CMD_ALS_IT_MASK;
+ als_it >>= CM3232_CMD_ALS_IT_SHIFT;
+ for (i = 0; i < ARRAY_SIZE(cm3232_als_it_scales); i++) {
+ if (als_it == cm3232_als_it_scales[i].it) {
+ *val = cm3232_als_it_scales[i].val;
+ *val2 = cm3232_als_it_scales[i].val2;
+ return IIO_VAL_INT_PLUS_MICRO;
+ }
+ }
+
+ return -EINVAL;
+}
+
+/**
+ * cm3232_write_als_it() - Write sensor integration time
+ * @chip: pointer of struct cm3232_chip.
+ * @val: integration time in second.
+ * @val2: integration time in microsecond.
+ *
+ * Convert integration time to sensor value.
+ *
+ * Return: i2c_smbus_write_byte_data command return value.
+ */
+static int cm3232_write_als_it(struct cm3232_chip *chip, int val, int val2)
+{
+ struct i2c_client *client = chip->client;
+ u16 als_it, cmd;
+ int i;
+ s32 ret;
+
+ for (i = 0; i < ARRAY_SIZE(cm3232_als_it_scales); i++) {
+ if (val == cm3232_als_it_scales[i].val &&
+ val2 == cm3232_als_it_scales[i].val2) {
+
+ als_it = cm3232_als_it_scales[i].it;
+ als_it <<= CM3232_CMD_ALS_IT_SHIFT;
+
+ cmd = chip->regs_cmd & ~CM3232_CMD_ALS_IT_MASK;
+ cmd |= als_it;
+ ret = i2c_smbus_write_byte_data(client,
+ CM3232_REG_ADDR_CMD,
+ cmd);
+ if (ret < 0)
+ return ret;
+ chip->regs_cmd = cmd;
+ return 0;
+ }
+ }
+ return -EINVAL;
+}
+
+/**
+ * cm3232_get_lux() - report current lux value
+ * @chip: pointer of struct cm3232_chip.
+ *
+ * Convert sensor data to lux. It depends on integration
+ * time and calibscale variable.
+ *
+ * Return: Zero or positive value is lux, otherwise error code.
+ */
+static int cm3232_get_lux(struct cm3232_chip *chip)
+{
+ struct i2c_client *client = chip->client;
+ struct cm3232_als_info *als_info = chip->als_info;
+ int ret;
+ int val, val2;
+ int als_it;
+ u64 lux;
+
+ /* Calculate mlux per bit based on als_it */
+ ret = cm3232_read_als_it(chip, &val, &val2);
+ if (ret < 0)
+ return -EINVAL;
+ als_it = val * 1000000 + val2;
+ lux = (__force u64)als_info->mlux_per_bit;
+ lux *= als_info->mlux_per_bit_base_it;
+ lux = div_u64(lux, als_it);
+
+ ret = i2c_smbus_read_word_data(client, CM3232_REG_ADDR_ALS);
+ if (ret < 0) {
+ dev_err(&client->dev, "Error reading reg_addr_als\n");
+ return ret;
+ }
+
+ chip->regs_als = (u16)ret;
+ lux *= chip->regs_als;
+ lux *= als_info->calibscale;
+ lux = div_u64(lux, CM3232_CALIBSCALE_RESOLUTION);
+ lux = div_u64(lux, CM3232_MLUX_PER_LUX);
+
+ if (lux > 0xFFFF)
+ lux = 0xFFFF;
+
+ return (int)lux;
+}
+
+static int cm3232_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int *val, int *val2, long mask)
+{
+ struct cm3232_chip *chip = iio_priv(indio_dev);
+ struct cm3232_als_info *als_info = chip->als_info;
+ int ret;
+
+ switch (mask) {
+ case IIO_CHAN_INFO_PROCESSED:
+ ret = cm3232_get_lux(chip);
+ if (ret < 0)
+ return ret;
+ *val = ret;
+ return IIO_VAL_INT;
+ case IIO_CHAN_INFO_CALIBSCALE:
+ *val = als_info->calibscale;
+ return IIO_VAL_INT;
+ case IIO_CHAN_INFO_INT_TIME:
+ return cm3232_read_als_it(chip, val, val2);
+ }
+
+ return -EINVAL;
+}
+
+static int cm3232_write_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int val, int val2, long mask)
+{
+ struct cm3232_chip *chip = iio_priv(indio_dev);
+ struct cm3232_als_info *als_info = chip->als_info;
+
+ switch (mask) {
+ case IIO_CHAN_INFO_CALIBSCALE:
+ als_info->calibscale = val;
+ return 0;
+ case IIO_CHAN_INFO_INT_TIME:
+ return cm3232_write_als_it(chip, val, val2);
+ }
+
+ return -EINVAL;
+}
+
+/**
+ * cm3232_get_it_available() - Get available ALS IT value
+ * @dev: pointer of struct device.
+ * @attr: pointer of struct device_attribute.
+ * @buf: pointer of return string buffer.
+ *
+ * Display the available integration time in second.
+ *
+ * Return: string length.
+ */
+static ssize_t cm3232_get_it_available(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ int i, len;
+
+ for (i = 0, len = 0; i < ARRAY_SIZE(cm3232_als_it_scales); i++)
+ len += scnprintf(buf + len, PAGE_SIZE - len, "%u.%06u ",
+ cm3232_als_it_scales[i].val,
+ cm3232_als_it_scales[i].val2);
+ return len + scnprintf(buf + len, PAGE_SIZE - len, "\n");
+}
+
+static const struct iio_chan_spec cm3232_channels[] = {
+ {
+ .type = IIO_LIGHT,
+ .info_mask_separate =
+ BIT(IIO_CHAN_INFO_PROCESSED) |
+ BIT(IIO_CHAN_INFO_CALIBSCALE) |
+ BIT(IIO_CHAN_INFO_INT_TIME),
+ }
+};
+
+static IIO_DEVICE_ATTR(in_illuminance_integration_time_available,
+ S_IRUGO, cm3232_get_it_available, NULL, 0);
+
+static struct attribute *cm3232_attributes[] = {
+ &iio_dev_attr_in_illuminance_integration_time_available.dev_attr.attr,
+ NULL,
+};
+
+static const struct attribute_group cm3232_attribute_group = {
+ .attrs = cm3232_attributes
+};
+
+static const struct iio_info cm3232_info = {
+ .driver_module = THIS_MODULE,
+ .read_raw = &cm3232_read_raw,
+ .write_raw = &cm3232_write_raw,
+ .attrs = &cm3232_attribute_group,
+};
+
+static int cm3232_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct cm3232_chip *chip;
+ struct iio_dev *indio_dev;
+ int ret;
+
+ indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*chip));
+ if (!indio_dev)
+ return -ENOMEM;
+
+ chip = iio_priv(indio_dev);
+ i2c_set_clientdata(client, indio_dev);
+ chip->client = client;
+
+ indio_dev->dev.parent = &client->dev;
+ indio_dev->channels = cm3232_channels;
+ indio_dev->num_channels = ARRAY_SIZE(cm3232_channels);
+ indio_dev->info = &cm3232_info;
+ indio_dev->name = id->name;
+ indio_dev->modes = INDIO_DIRECT_MODE;
+
+ ret = cm3232_reg_init(chip);
+ if (ret) {
+ dev_err(&client->dev,
+ "%s: register init failed\n",
+ __func__);
+ return ret;
+ }
+
+ return iio_device_register(indio_dev);
+}
+
+static int cm3232_remove(struct i2c_client *client)
+{
+ struct iio_dev *indio_dev = i2c_get_clientdata(client);
+
+ i2c_smbus_write_byte_data(client, CM3232_REG_ADDR_CMD,
+ CM3232_CMD_ALS_DISABLE);
+
+ iio_device_unregister(indio_dev);
+
+ return 0;
+}
+
+static const struct i2c_device_id cm3232_id[] = {
+ {"cm3232", 0},
+ {}
+};
+
+MODULE_DEVICE_TABLE(i2c, cm3232_id);
+
+static const struct of_device_id cm3232_of_match[] = {
+ {.compatible = "capella,cm3232"},
+ {}
+};
+
+static struct i2c_driver cm3232_driver = {
+ .driver = {
+ .name = "cm3232",
+ .owner = THIS_MODULE,
+ .of_match_table = of_match_ptr(cm3232_of_match),
+ },
+ .id_table = cm3232_id,
+ .probe = cm3232_probe,
+ .remove = cm3232_remove,
+};
+
+module_i2c_driver(cm3232_driver);
+
+MODULE_AUTHOR("Kevin Tsai <ktsai@capellamicro.com>");
+MODULE_DESCRIPTION("CM3232 ambient light sensor driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/iio/light/hid-sensor-als.c b/drivers/iio/light/hid-sensor-als.c
index a5283d7..948acfc 100644
--- a/drivers/iio/light/hid-sensor-als.c
+++ b/drivers/iio/light/hid-sensor-als.c
@@ -80,7 +80,6 @@ static int als_read_raw(struct iio_dev *indio_dev,
int report_id = -1;
u32 address;
int ret_type;
- s32 poll_value;
*val = 0;
*val2 = 0;
@@ -97,15 +96,8 @@ static int als_read_raw(struct iio_dev *indio_dev,
break;
}
if (report_id >= 0) {
- poll_value = hid_sensor_read_poll_value(
- &als_state->common_attributes);
- if (poll_value < 0)
- return -EINVAL;
-
hid_sensor_power_state(&als_state->common_attributes,
true);
- msleep_interruptible(poll_value * 2);
-
*val = sensor_hub_input_attr_get_raw_value(
als_state->common_attributes.hsdev,
HID_USAGE_SENSOR_ALS, address,
@@ -381,6 +373,7 @@ static struct platform_driver hid_als_platform_driver = {
.id_table = hid_als_ids,
.driver = {
.name = KBUILD_MODNAME,
+ .pm = &hid_sensor_pm_ops,
},
.probe = hid_als_probe,
.remove = hid_als_remove,
diff --git a/drivers/iio/light/hid-sensor-prox.c b/drivers/iio/light/hid-sensor-prox.c
index f5a5146..3ecf79e 100644
--- a/drivers/iio/light/hid-sensor-prox.c
+++ b/drivers/iio/light/hid-sensor-prox.c
@@ -75,7 +75,6 @@ static int prox_read_raw(struct iio_dev *indio_dev,
int report_id = -1;
u32 address;
int ret_type;
- s32 poll_value;
*val = 0;
*val2 = 0;
@@ -92,16 +91,8 @@ static int prox_read_raw(struct iio_dev *indio_dev,
break;
}
if (report_id >= 0) {
- poll_value = hid_sensor_read_poll_value(
- &prox_state->common_attributes);
- if (poll_value < 0)
- return -EINVAL;
-
hid_sensor_power_state(&prox_state->common_attributes,
true);
-
- msleep_interruptible(poll_value * 2);
-
*val = sensor_hub_input_attr_get_raw_value(
prox_state->common_attributes.hsdev,
HID_USAGE_SENSOR_PROX, address,
@@ -373,6 +364,7 @@ static struct platform_driver hid_prox_platform_driver = {
.id_table = hid_prox_ids,
.driver = {
.name = KBUILD_MODNAME,
+ .pm = &hid_sensor_pm_ops,
},
.probe = hid_prox_probe,
.remove = hid_prox_remove,
diff --git a/drivers/iio/light/jsa1212.c b/drivers/iio/light/jsa1212.c
new file mode 100644
index 0000000..29de7e7
--- /dev/null
+++ b/drivers/iio/light/jsa1212.c
@@ -0,0 +1,471 @@
+/*
+ * JSA1212 Ambient Light & Proximity Sensor Driver
+ *
+ * Copyright (c) 2014, Intel Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope 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.
+ *
+ * JSA1212 I2C slave address: 0x44(ADDR tied to GND), 0x45(ADDR tied to VDD)
+ *
+ * TODO: Interrupt support, thresholds, range support.
+ */
+
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/delay.h>
+#include <linux/i2c.h>
+#include <linux/mutex.h>
+#include <linux/acpi.h>
+#include <linux/regmap.h>
+#include <linux/iio/iio.h>
+#include <linux/iio/sysfs.h>
+
+/* JSA1212 reg address */
+#define JSA1212_CONF_REG 0x01
+#define JSA1212_INT_REG 0x02
+#define JSA1212_PXS_LT_REG 0x03
+#define JSA1212_PXS_HT_REG 0x04
+#define JSA1212_ALS_TH1_REG 0x05
+#define JSA1212_ALS_TH2_REG 0x06
+#define JSA1212_ALS_TH3_REG 0x07
+#define JSA1212_PXS_DATA_REG 0x08
+#define JSA1212_ALS_DT1_REG 0x09
+#define JSA1212_ALS_DT2_REG 0x0A
+#define JSA1212_ALS_RNG_REG 0x0B
+#define JSA1212_MAX_REG 0x0C
+
+/* JSA1212 reg masks */
+#define JSA1212_CONF_MASK 0xFF
+#define JSA1212_INT_MASK 0xFF
+#define JSA1212_PXS_LT_MASK 0xFF
+#define JSA1212_PXS_HT_MASK 0xFF
+#define JSA1212_ALS_TH1_MASK 0xFF
+#define JSA1212_ALS_TH2_LT_MASK 0x0F
+#define JSA1212_ALS_TH2_HT_MASK 0xF0
+#define JSA1212_ALS_TH3_MASK 0xFF
+#define JSA1212_PXS_DATA_MASK 0xFF
+#define JSA1212_ALS_DATA_MASK 0x0FFF
+#define JSA1212_ALS_DT1_MASK 0xFF
+#define JSA1212_ALS_DT2_MASK 0x0F
+#define JSA1212_ALS_RNG_MASK 0x07
+
+/* JSA1212 CONF REG bits */
+#define JSA1212_CONF_PXS_MASK 0x80
+#define JSA1212_CONF_PXS_ENABLE 0x80
+#define JSA1212_CONF_PXS_DISABLE 0x00
+#define JSA1212_CONF_ALS_MASK 0x04
+#define JSA1212_CONF_ALS_ENABLE 0x04
+#define JSA1212_CONF_ALS_DISABLE 0x00
+#define JSA1212_CONF_IRDR_MASK 0x08
+/* Proxmity sensing IRDR current sink settings */
+#define JSA1212_CONF_IRDR_200MA 0x08
+#define JSA1212_CONF_IRDR_100MA 0x00
+#define JSA1212_CONF_PXS_SLP_MASK 0x70
+#define JSA1212_CONF_PXS_SLP_0MS 0x70
+#define JSA1212_CONF_PXS_SLP_12MS 0x60
+#define JSA1212_CONF_PXS_SLP_50MS 0x50
+#define JSA1212_CONF_PXS_SLP_75MS 0x40
+#define JSA1212_CONF_PXS_SLP_100MS 0x30
+#define JSA1212_CONF_PXS_SLP_200MS 0x20
+#define JSA1212_CONF_PXS_SLP_400MS 0x10
+#define JSA1212_CONF_PXS_SLP_800MS 0x00
+
+/* JSA1212 INT REG bits */
+#define JSA1212_INT_CTRL_MASK 0x01
+#define JSA1212_INT_CTRL_EITHER 0x00
+#define JSA1212_INT_CTRL_BOTH 0x01
+#define JSA1212_INT_ALS_PRST_MASK 0x06
+#define JSA1212_INT_ALS_PRST_1CONV 0x00
+#define JSA1212_INT_ALS_PRST_4CONV 0x02
+#define JSA1212_INT_ALS_PRST_8CONV 0x04
+#define JSA1212_INT_ALS_PRST_16CONV 0x06
+#define JSA1212_INT_ALS_FLAG_MASK 0x08
+#define JSA1212_INT_ALS_FLAG_CLR 0x00
+#define JSA1212_INT_PXS_PRST_MASK 0x60
+#define JSA1212_INT_PXS_PRST_1CONV 0x00
+#define JSA1212_INT_PXS_PRST_4CONV 0x20
+#define JSA1212_INT_PXS_PRST_8CONV 0x40
+#define JSA1212_INT_PXS_PRST_16CONV 0x60
+#define JSA1212_INT_PXS_FLAG_MASK 0x80
+#define JSA1212_INT_PXS_FLAG_CLR 0x00
+
+/* JSA1212 ALS RNG REG bits */
+#define JSA1212_ALS_RNG_0_2048 0x00
+#define JSA1212_ALS_RNG_0_1024 0x01
+#define JSA1212_ALS_RNG_0_512 0x02
+#define JSA1212_ALS_RNG_0_256 0x03
+#define JSA1212_ALS_RNG_0_128 0x04
+
+/* JSA1212 INT threshold range */
+#define JSA1212_ALS_TH_MIN 0x0000
+#define JSA1212_ALS_TH_MAX 0x0FFF
+#define JSA1212_PXS_TH_MIN 0x00
+#define JSA1212_PXS_TH_MAX 0xFF
+
+#define JSA1212_ALS_DELAY_MS 200
+#define JSA1212_PXS_DELAY_MS 100
+
+#define JSA1212_DRIVER_NAME "jsa1212"
+#define JSA1212_REGMAP_NAME "jsa1212_regmap"
+
+enum jsa1212_op_mode {
+ JSA1212_OPMODE_ALS_EN,
+ JSA1212_OPMODE_PXS_EN,
+};
+
+struct jsa1212_data {
+ struct i2c_client *client;
+ struct mutex lock;
+ u8 als_rng_idx;
+ bool als_en; /* ALS enable status */
+ bool pxs_en; /* proximity enable status */
+ struct regmap *regmap;
+};
+
+/* ALS range idx to val mapping */
+static const int jsa1212_als_range_val[] = {2048, 1024, 512, 256, 128,
+ 128, 128, 128};
+
+/* Enables or disables ALS function based on status */
+static int jsa1212_als_enable(struct jsa1212_data *data, u8 status)
+{
+ int ret;
+
+ ret = regmap_update_bits(data->regmap, JSA1212_CONF_REG,
+ JSA1212_CONF_ALS_MASK,
+ status);
+ if (ret < 0)
+ return ret;
+
+ data->als_en = !!status;
+
+ return 0;
+}
+
+/* Enables or disables PXS function based on status */
+static int jsa1212_pxs_enable(struct jsa1212_data *data, u8 status)
+{
+ int ret;
+
+ ret = regmap_update_bits(data->regmap, JSA1212_CONF_REG,
+ JSA1212_CONF_PXS_MASK,
+ status);
+ if (ret < 0)
+ return ret;
+
+ data->pxs_en = !!status;
+
+ return 0;
+}
+
+static int jsa1212_read_als_data(struct jsa1212_data *data,
+ unsigned int *val)
+{
+ int ret;
+ __le16 als_data;
+
+ ret = jsa1212_als_enable(data, JSA1212_CONF_ALS_ENABLE);
+ if (ret < 0)
+ return ret;
+
+ /* Delay for data output */
+ msleep(JSA1212_ALS_DELAY_MS);
+
+ /* Read 12 bit data */
+ ret = regmap_bulk_read(data->regmap, JSA1212_ALS_DT1_REG, &als_data, 2);
+ if (ret < 0) {
+ dev_err(&data->client->dev, "als data read err\n");
+ goto als_data_read_err;
+ }
+
+ *val = le16_to_cpu(als_data);
+
+als_data_read_err:
+ return jsa1212_als_enable(data, JSA1212_CONF_ALS_DISABLE);
+}
+
+static int jsa1212_read_pxs_data(struct jsa1212_data *data,
+ unsigned int *val)
+{
+ int ret;
+ unsigned int pxs_data;
+
+ ret = jsa1212_pxs_enable(data, JSA1212_CONF_PXS_ENABLE);
+ if (ret < 0)
+ return ret;
+
+ /* Delay for data output */
+ msleep(JSA1212_PXS_DELAY_MS);
+
+ /* Read out all data */
+ ret = regmap_read(data->regmap, JSA1212_PXS_DATA_REG, &pxs_data);
+ if (ret < 0) {
+ dev_err(&data->client->dev, "pxs data read err\n");
+ goto pxs_data_read_err;
+ }
+
+ *val = pxs_data & JSA1212_PXS_DATA_MASK;
+
+pxs_data_read_err:
+ return jsa1212_pxs_enable(data, JSA1212_CONF_PXS_DISABLE);
+}
+
+static int jsa1212_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int *val, int *val2, long mask)
+{
+ int ret;
+ struct jsa1212_data *data = iio_priv(indio_dev);
+
+ switch (mask) {
+ case IIO_CHAN_INFO_RAW:
+ mutex_lock(&data->lock);
+ switch (chan->type) {
+ case IIO_LIGHT:
+ ret = jsa1212_read_als_data(data, val);
+ break;
+ case IIO_PROXIMITY:
+ ret = jsa1212_read_pxs_data(data, val);
+ break;
+ default:
+ ret = -EINVAL;
+ break;
+ }
+ mutex_unlock(&data->lock);
+ return ret < 0 ? ret : IIO_VAL_INT;
+ case IIO_CHAN_INFO_SCALE:
+ switch (chan->type) {
+ case IIO_LIGHT:
+ *val = jsa1212_als_range_val[data->als_rng_idx];
+ *val2 = BIT(12); /* Max 12 bit value */
+ return IIO_VAL_FRACTIONAL;
+ default:
+ break;
+ }
+ break;
+ default:
+ break;
+ }
+
+ return -EINVAL;
+}
+
+static const struct iio_chan_spec jsa1212_channels[] = {
+ {
+ .type = IIO_LIGHT,
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
+ BIT(IIO_CHAN_INFO_SCALE),
+ },
+ {
+ .type = IIO_PROXIMITY,
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
+ }
+};
+
+static const struct iio_info jsa1212_info = {
+ .driver_module = THIS_MODULE,
+ .read_raw = &jsa1212_read_raw,
+};
+
+static int jsa1212_chip_init(struct jsa1212_data *data)
+{
+ int ret;
+
+ ret = regmap_write(data->regmap, JSA1212_CONF_REG,
+ (JSA1212_CONF_PXS_SLP_50MS |
+ JSA1212_CONF_IRDR_200MA));
+ if (ret < 0)
+ return ret;
+
+ ret = regmap_write(data->regmap, JSA1212_INT_REG,
+ JSA1212_INT_ALS_PRST_4CONV);
+ if (ret < 0)
+ return ret;
+
+ data->als_rng_idx = JSA1212_ALS_RNG_0_2048;
+
+ return 0;
+}
+
+static bool jsa1212_is_volatile_reg(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case JSA1212_PXS_DATA_REG:
+ case JSA1212_ALS_DT1_REG:
+ case JSA1212_ALS_DT2_REG:
+ case JSA1212_INT_REG:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static struct regmap_config jsa1212_regmap_config = {
+ .name = JSA1212_REGMAP_NAME,
+ .reg_bits = 8,
+ .val_bits = 8,
+ .max_register = JSA1212_MAX_REG,
+ .cache_type = REGCACHE_RBTREE,
+ .volatile_reg = jsa1212_is_volatile_reg,
+};
+
+static int jsa1212_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct jsa1212_data *data;
+ struct iio_dev *indio_dev;
+ struct regmap *regmap;
+ int ret;
+
+ if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA))
+ return -ENODEV;
+
+ indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data));
+ if (!indio_dev)
+ return -ENOMEM;
+
+ regmap = devm_regmap_init_i2c(client, &jsa1212_regmap_config);
+ if (IS_ERR(regmap)) {
+ dev_err(&client->dev, "Regmap initialization failed.\n");
+ return PTR_ERR(regmap);
+ }
+
+ data = iio_priv(indio_dev);
+
+ i2c_set_clientdata(client, indio_dev);
+ data->client = client;
+ data->regmap = regmap;
+
+ mutex_init(&data->lock);
+
+ ret = jsa1212_chip_init(data);
+ if (ret < 0)
+ return ret;
+
+ indio_dev->dev.parent = &client->dev;
+ indio_dev->channels = jsa1212_channels;
+ indio_dev->num_channels = ARRAY_SIZE(jsa1212_channels);
+ indio_dev->name = JSA1212_DRIVER_NAME;
+ indio_dev->modes = INDIO_DIRECT_MODE;
+
+ indio_dev->info = &jsa1212_info;
+
+ ret = iio_device_register(indio_dev);
+ if (ret < 0)
+ dev_err(&client->dev, "%s: register device failed\n", __func__);
+
+ return ret;
+}
+
+ /* power off the device */
+static int jsa1212_power_off(struct jsa1212_data *data)
+{
+ int ret;
+
+ mutex_lock(&data->lock);
+
+ ret = regmap_update_bits(data->regmap, JSA1212_CONF_REG,
+ JSA1212_CONF_ALS_MASK |
+ JSA1212_CONF_PXS_MASK,
+ JSA1212_CONF_ALS_DISABLE |
+ JSA1212_CONF_PXS_DISABLE);
+
+ if (ret < 0)
+ dev_err(&data->client->dev, "power off cmd failed\n");
+
+ mutex_unlock(&data->lock);
+
+ return ret;
+}
+
+static int jsa1212_remove(struct i2c_client *client)
+{
+ struct iio_dev *indio_dev = i2c_get_clientdata(client);
+ struct jsa1212_data *data = iio_priv(indio_dev);
+
+ iio_device_unregister(indio_dev);
+
+ return jsa1212_power_off(data);
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int jsa1212_suspend(struct device *dev)
+{
+ struct jsa1212_data *data;
+
+ data = iio_priv(i2c_get_clientdata(to_i2c_client(dev)));
+
+ return jsa1212_power_off(data);
+}
+
+static int jsa1212_resume(struct device *dev)
+{
+ int ret = 0;
+ struct jsa1212_data *data;
+
+ data = iio_priv(i2c_get_clientdata(to_i2c_client(dev)));
+
+ mutex_lock(&data->lock);
+
+ if (data->als_en) {
+ ret = jsa1212_als_enable(data, JSA1212_CONF_ALS_ENABLE);
+ if (ret < 0) {
+ dev_err(dev, "als resume failed\n");
+ goto unlock_and_ret;
+ }
+ }
+
+ if (data->pxs_en) {
+ ret = jsa1212_pxs_enable(data, JSA1212_CONF_PXS_ENABLE);
+ if (ret < 0)
+ dev_err(dev, "pxs resume failed\n");
+ }
+
+unlock_and_ret:
+ mutex_unlock(&data->lock);
+ return ret;
+}
+
+static SIMPLE_DEV_PM_OPS(jsa1212_pm_ops, jsa1212_suspend, jsa1212_resume);
+
+#define JSA1212_PM_OPS (&jsa1212_pm_ops)
+#else
+#define JSA1212_PM_OPS NULL
+#endif
+
+static const struct acpi_device_id jsa1212_acpi_match[] = {
+ {"JSA1212", 0},
+ { },
+};
+MODULE_DEVICE_TABLE(acpi, jsa1212_acpi_match);
+
+static const struct i2c_device_id jsa1212_id[] = {
+ { JSA1212_DRIVER_NAME, 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, jsa1212_id);
+
+static struct i2c_driver jsa1212_driver = {
+ .driver = {
+ .name = JSA1212_DRIVER_NAME,
+ .pm = JSA1212_PM_OPS,
+ .owner = THIS_MODULE,
+ .acpi_match_table = ACPI_PTR(jsa1212_acpi_match),
+ },
+ .probe = jsa1212_probe,
+ .remove = jsa1212_remove,
+ .id_table = jsa1212_id,
+};
+module_i2c_driver(jsa1212_driver);
+
+MODULE_AUTHOR("Sathya Kuppuswamy <sathyanarayanan.kuppuswamy@linux.intel.com>");
+MODULE_DESCRIPTION("JSA1212 proximity/ambient light sensor driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/iio/light/lm3533-als.c b/drivers/iio/light/lm3533-als.c
index ae3c71b..076bc46 100644
--- a/drivers/iio/light/lm3533-als.c
+++ b/drivers/iio/light/lm3533-als.c
@@ -657,7 +657,7 @@ static ALS_HYSTERESIS_ATTR_RO(3);
#define ILLUMINANCE_ATTR_RO(_name) \
DEVICE_ATTR(in_illuminance0_##_name, S_IRUGO, show_##_name, NULL)
#define ILLUMINANCE_ATTR_RW(_name) \
- DEVICE_ATTR(in_illuminance0_##_name, S_IRUGO | S_IWUSR , \
+ DEVICE_ATTR(in_illuminance0_##_name, S_IRUGO | S_IWUSR, \
show_##_name, store_##_name)
/*
* ALS Zone threshold-event enable
diff --git a/drivers/iio/light/tcs3414.c b/drivers/iio/light/tcs3414.c
index a9e449b..71c2bde 100644
--- a/drivers/iio/light/tcs3414.c
+++ b/drivers/iio/light/tcs3414.c
@@ -149,8 +149,8 @@ static int tcs3414_read_raw(struct iio_dev *indio_dev,
*val = ret;
return IIO_VAL_INT;
case IIO_CHAN_INFO_SCALE:
- i = (data->gain & TCS3414_GAIN_MASK) >> TCS3414_GAIN_SHIFT;
- *val = tcs3414_scales[i][0];
+ i = (data->gain & TCS3414_GAIN_MASK) >> TCS3414_GAIN_SHIFT;
+ *val = tcs3414_scales[i][0];
*val2 = tcs3414_scales[i][1];
return IIO_VAL_INT_PLUS_MICRO;
case IIO_CHAN_INFO_INT_TIME: