* [PATCH 0/3] iio: add Intersil isl76683 light sensor support
@ 2017-11-07 15:36 Christoph Fritz
2017-11-07 15:36 ` [PATCH 1/3] iio: light: add support for Intersil isl76683 sensor Christoph Fritz
` (2 more replies)
0 siblings, 3 replies; 9+ messages in thread
From: Christoph Fritz @ 2017-11-07 15:36 UTC (permalink / raw)
To: Jonathan Cameron, Peter Meerwald-Stadler, Rob Herring
Cc: linux-iio, devicetree
This patchset adds initial support for isl76683 light sensor.
Christoph Fritz (3):
iio: light: add support for Intersil isl76683 sensor
dt-bindings: iio: add Intersil isl76683 light sensor bindings
iio: light: isl76683 add way to adjust irq threshold
.../ABI/testing/sysfs-bus-iio-light-isl76683 | 17 +
.../devicetree/bindings/iio/light/isl76683.txt | 26 +
drivers/iio/light/Kconfig | 12 +
drivers/iio/light/Makefile | 1 +
drivers/iio/light/isl76683.c | 743 +++++++++++++++++++++
5 files changed, 799 insertions(+)
create mode 100644 Documentation/ABI/testing/sysfs-bus-iio-light-isl76683
create mode 100644 Documentation/devicetree/bindings/iio/light/isl76683.txt
create mode 100644 drivers/iio/light/isl76683.c
--
2.1.4
^ permalink raw reply [flat|nested] 9+ messages in thread
* [PATCH 1/3] iio: light: add support for Intersil isl76683 sensor
2017-11-07 15:36 [PATCH 0/3] iio: add Intersil isl76683 light sensor support Christoph Fritz
@ 2017-11-07 15:36 ` Christoph Fritz
2017-11-07 16:28 ` Peter Meerwald-Stadler
2017-11-11 0:50 ` Jonathan Cameron
2017-11-07 15:36 ` [PATCH 2/3] dt-bindings: iio: add Intersil isl76683 light sensor bindings Christoph Fritz
2017-11-07 15:36 ` [PATCH 3/3] iio: light: isl76683 add way to adjust irq threshold Christoph Fritz
2 siblings, 2 replies; 9+ messages in thread
From: Christoph Fritz @ 2017-11-07 15:36 UTC (permalink / raw)
To: Jonathan Cameron, Peter Meerwald-Stadler, Rob Herring
Cc: linux-iio, devicetree
This patch adds support for Intersil isl76683 light sensor.
Signed-off-by: Christoph Fritz <chf.fritz@googlemail.com>
---
drivers/iio/light/Kconfig | 12 +
drivers/iio/light/Makefile | 1 +
drivers/iio/light/isl76683.c | 693 +++++++++++++++++++++++++++++++++++++++++++
3 files changed, 706 insertions(+)
create mode 100644 drivers/iio/light/isl76683.c
diff --git a/drivers/iio/light/Kconfig b/drivers/iio/light/Kconfig
index 2356ed9..4f0882c 100644
--- a/drivers/iio/light/Kconfig
+++ b/drivers/iio/light/Kconfig
@@ -194,6 +194,18 @@ config ISL29125
To compile this driver as a module, choose M here: the module will be
called isl29125.
+config ISL76683
+ tristate "Intersil ISL76683 light sensor"
+ depends on I2C
+ select IIO_BUFFER
+ select IIO_TRIGGERED_BUFFER
+ help
+ Say Y here if you want to build a driver for the Intersil ISL76683
+ light sensor for I2C.
+
+ To compile this driver as a module, choose M here: the module will be
+ called isl76683.
+
config HID_SENSOR_ALS
depends on HID_SENSOR_HUB
select IIO_BUFFER
diff --git a/drivers/iio/light/Makefile b/drivers/iio/light/Makefile
index fa32fa4..886a51f 100644
--- a/drivers/iio/light/Makefile
+++ b/drivers/iio/light/Makefile
@@ -22,6 +22,7 @@ obj-$(CONFIG_HID_SENSOR_PROX) += hid-sensor-prox.o
obj-$(CONFIG_SENSORS_ISL29018) += isl29018.o
obj-$(CONFIG_SENSORS_ISL29028) += isl29028.o
obj-$(CONFIG_ISL29125) += isl29125.o
+obj-$(CONFIG_ISL76683) += isl76683.o
obj-$(CONFIG_JSA1212) += jsa1212.o
obj-$(CONFIG_SENSORS_LM3533) += lm3533-als.o
obj-$(CONFIG_LTR501) += ltr501.o
diff --git a/drivers/iio/light/isl76683.c b/drivers/iio/light/isl76683.c
new file mode 100644
index 0000000..b730276
--- /dev/null
+++ b/drivers/iio/light/isl76683.c
@@ -0,0 +1,693 @@
+/*
+ * IIO driver for the light sensor ISL76683.
+ *
+ * Copyright (c) 2017 Christoph Fritz <chf.fritz@googlemail.com>
+ *
+ * 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.
+ *
+ * Datasheet:
+ * http://www.intersil.com/content/dam/Intersil/documents/isl7/isl76683.pdf
+ */
+
+#include <linux/module.h>
+#include <linux/i2c.h>
+#include <linux/pm_runtime.h>
+#include <linux/regmap.h>
+#include <linux/util_macros.h>
+#include <linux/iio/iio.h>
+#include <linux/iio/sysfs.h>
+#include <linux/iio/buffer.h>
+#include <linux/iio/trigger.h>
+#include <linux/iio/trigger_consumer.h>
+#include <linux/iio/triggered_buffer.h>
+
+#define ISL76683_REG_CMD 0x00
+#define ISL76683_REG_CTRL 0x01
+#define ISL76683_REG_THR_HI 0x02
+#define ISL76683_REG_THR_LO 0x03
+#define ISL76683_REG_SENSOR_L 0x04
+#define ISL76683_REG_SENSOR_M 0x05
+#define ISL76683_REG_CLEAR_INT 0x40
+#define ISL76683_REGMAP_MAX 0x40
+
+#define ISL76683_CMD_ENABLE BIT(7)
+#define ISL76683_CMD_PWRDWN BIT(6)
+#define ISL76683_WIDTH_MASK 0x3
+#define ISL76683_PHOTOD_SHFT 2
+#define ISL76683_PHOTOD_MASK (0x3 << ISL76683_PHOTOD_SHFT)
+#define ISL76683_INTPERS_MASK 0x3
+#define ISL76683_LUXRANGE_SHFT 2
+#define ISL76683_LUXRANGE_MASK (0x3 << ISL76683_LUXRANGE_SHFT)
+#define ISL76683_LUXRANGE_STR "1000 4000 16000 64000"
+
+enum isl76683_dmode {
+ ISL76683_DIODE_0 = 0,
+ ISL76683_DIODE_IR,
+ ISL76683_DIODE_DIFF,
+};
+
+enum isl76683_lux_range {
+ ISL76683_LUX_1000 = 0,
+ ISL76683_LUX_4000,
+ ISL76683_LUX_16000,
+ ISL76683_LUX_64000,
+};
+
+static const int isl76683_lux_ranges_available[] = {
+ 1000, 4000, 16000, 64000};
+
+#define ISL76683_LUX_RANGE_DEFAULT ISL76683_LUX_1000
+#define ISL76683_DIODE_MAX ISL76683_DIODE_DIFF
+#define ISL76683_DIODE_DEFAULT ISL76683_DIODE_0
+#define ISL76683_WIDTH_DEFAULT 0x0
+#define ISL76683_RESOLUTION_DEFAULT 16
+#define ISL76683_EXT_RESISTOR_DEFAULT 100
+#define ISL76683_KOHM_MIN 1
+#define ISL76683_KOHM_MAX 1000
+#define ISL76683_INTPERS_DEFAULT 0x0
+#define ISL76683_THR_DEFAULT 0x7f
+
+struct isl76683_chip {
+ enum isl76683_lux_range luxrange;
+ int external_resistor;
+ enum isl76683_dmode photodiode;
+ struct i2c_client *client;
+ struct regmap *rmp;
+ struct completion irq_complete;
+ struct iio_trigger *trig;
+ bool trig_enabled;
+ struct mutex lock;
+ __le16 *buffer;
+ s64 time_ns;
+ bool buffer_running;
+};
+
+static bool isl76683_readable_reg(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case ISL76683_REG_CMD:
+ case ISL76683_REG_CTRL:
+ case ISL76683_REG_THR_HI:
+ case ISL76683_REG_THR_LO:
+ case ISL76683_REG_SENSOR_L:
+ case ISL76683_REG_SENSOR_M:
+ case ISL76683_REG_CLEAR_INT:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static bool isl76683_writeable_reg(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case ISL76683_REG_CMD:
+ case ISL76683_REG_CTRL:
+ case ISL76683_REG_THR_HI:
+ case ISL76683_REG_THR_LO:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static bool isl76683_is_volatile_reg(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case ISL76683_REG_SENSOR_L:
+ case ISL76683_REG_SENSOR_M:
+ case ISL76683_REG_CLEAR_INT:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static const struct regmap_config isl76683_regmap_config = {
+ .reg_bits = 8,
+ .val_bits = 8,
+ .max_register = ISL76683_REGMAP_MAX,
+ .readable_reg = isl76683_readable_reg,
+ .writeable_reg = isl76683_writeable_reg,
+ .volatile_reg = isl76683_is_volatile_reg,
+ .cache_type = REGCACHE_RBTREE,
+};
+
+static int isl76683_set_config(struct isl76683_chip *chip)
+{
+ int ret;
+
+ ret = regmap_update_bits(chip->rmp, ISL76683_REG_CTRL,
+ ISL76683_LUXRANGE_MASK | ISL76683_INTPERS_MASK,
+ (chip->luxrange << ISL76683_LUXRANGE_SHFT) |
+ ISL76683_INTPERS_DEFAULT);
+ if (ret < 0)
+ return ret;
+
+ ret = regmap_update_bits(chip->rmp, ISL76683_REG_CMD,
+ ISL76683_PHOTOD_MASK | ISL76683_WIDTH_MASK,
+ (chip->photodiode << ISL76683_PHOTOD_SHFT) |
+ ISL76683_WIDTH_DEFAULT);
+ if (ret < 0)
+ return ret;
+
+ ret = regmap_write(chip->rmp, ISL76683_REG_THR_HI,
+ ISL76683_THR_DEFAULT);
+ if (ret < 0)
+ return ret;
+
+ ret = regmap_write(chip->rmp, ISL76683_REG_THR_LO,
+ ISL76683_THR_DEFAULT);
+ if (ret < 0)
+ return ret;
+
+ return 0;
+}
+
+static int isl76683_power(struct isl76683_chip *chip, bool on)
+{
+ int ret;
+
+ ret = regmap_update_bits(chip->rmp, ISL76683_REG_CMD,
+ ISL76683_CMD_ENABLE | ISL76683_CMD_PWRDWN,
+ 0x0);
+ if (ret < 0)
+ return ret;
+
+ ret = regmap_update_bits(chip->rmp, ISL76683_REG_CMD,
+ ISL76683_CMD_ENABLE | ISL76683_CMD_PWRDWN,
+ on ? ISL76683_CMD_ENABLE : ISL76683_CMD_PWRDWN);
+ if (ret < 0)
+ return ret;
+
+ return on ? isl76683_set_config(chip) : 0;
+}
+
+static int isl76683_reset(struct isl76683_chip *chip)
+{
+ int ret;
+
+ ret = isl76683_power(chip, false);
+ if (ret < 0)
+ return ret;
+
+ return isl76683_power(chip, true);
+}
+
+static int isl76683_read_lux(struct isl76683_chip *chip,
+ bool is_processed, int *val)
+{
+ unsigned int sensor_data, range, fsr;
+ __le16 sensor_raw;
+ int ret;
+
+ ret = regmap_bulk_read(chip->rmp, ISL76683_REG_SENSOR_L,
+ &sensor_raw, sizeof(sensor_raw));
+ if (ret)
+ return ret;
+
+ sensor_data = le16_to_cpu(sensor_raw);
+
+ if (!is_processed) {
+ *val = sensor_data;
+ return 0;
+ }
+
+ /* range values taken from datasheet (table 9) */
+ if (chip->luxrange == ISL76683_LUX_1000)
+ range = 973;
+ else if (chip->luxrange == ISL76683_LUX_4000)
+ range = 3892;
+ else if (chip->luxrange == ISL76683_LUX_16000)
+ range = 15568;
+ else if (chip->luxrange == ISL76683_LUX_64000)
+ range = 62272;
+ else
+ return -EINVAL;
+
+ /* equations from datasheet (EQ.3 and EQ.4) */
+ fsr = (100 * range) / chip->external_resistor;
+ *val = (fsr * sensor_data) / (1 << ISL76683_RESOLUTION_DEFAULT);
+
+ return 0;
+}
+
+static irqreturn_t isl76683_interrupt_handler(int irq, void *private)
+{
+ struct iio_dev *indio_dev = private;
+ struct isl76683_chip *chip = iio_priv(indio_dev);
+
+ chip->time_ns = iio_get_time_ns(indio_dev);
+
+ if (chip->trig_enabled)
+ iio_trigger_poll(chip->trig);
+
+ if (!completion_done(&chip->irq_complete))
+ complete(&chip->irq_complete);
+
+ return IRQ_HANDLED;
+}
+
+static inline int isl76683_start_measurement(struct isl76683_chip *chip)
+{
+ int dummy;
+
+ /* dummy read is clearing irq and triggers new measurement */
+ return regmap_read(chip->rmp, ISL76683_REG_CLEAR_INT, &dummy);
+}
+
+static int isl76683_singleshot_conversion(struct isl76683_chip *chip,
+ const struct iio_chan_spec *chan,
+ bool is_processed, int *val)
+{
+ long timeout;
+ int ret;
+
+ if (chip->buffer_running)
+ return -EAGAIN;
+
+ switch (chan->channel2) {
+ case IIO_MOD_LIGHT_BOTH:
+ chip->photodiode = ISL76683_DIODE_DIFF;
+ break;
+ case IIO_MOD_LIGHT_IR:
+ chip->photodiode = ISL76683_DIODE_IR;
+ break;
+ default:
+ chip->photodiode = ISL76683_DIODE_0;
+ }
+
+ ret = isl76683_set_config(chip);
+ if (ret)
+ return ret;
+
+ ret = isl76683_start_measurement(chip);
+ if (ret < 0)
+ return ret;
+
+ /* wait for measurement to complete */
+ timeout = wait_for_completion_interruptible_timeout(
+ &chip->irq_complete,
+ msecs_to_jiffies(5000));
+ if (timeout == 0) {
+ dev_err(&chip->client->dev, "measurement timed out\n");
+ return -ETIMEDOUT;
+ } else if (timeout < 0) {
+ dev_err(&chip->client->dev, "wait for measurement failed\n");
+ return -EINTR;
+ }
+
+ ret = isl76683_read_lux(chip, is_processed, val);
+ if (ret) {
+ dev_err(&chip->client->dev, "%s: Error %d reading lux\n",
+ __func__, ret);
+ return ret;
+ }
+
+ return IIO_VAL_INT;
+}
+
+static irqreturn_t isl76683_trigger_handler(int irq, void *p)
+{
+ struct iio_poll_func *pf = p;
+ struct iio_dev *indio_dev = pf->indio_dev;
+ struct isl76683_chip *chip = iio_priv(indio_dev);
+ __le16 buf;
+ int ret;
+
+ ret = regmap_bulk_read(chip->rmp, ISL76683_REG_SENSOR_L, &buf, 2);
+ if (ret)
+ return ret;
+
+ chip->buffer[0] = le16_to_cpu(buf);
+
+ iio_push_to_buffers_with_timestamp(indio_dev, chip->buffer,
+ chip->time_ns);
+
+ iio_trigger_notify_done(indio_dev->trig);
+
+ ret = isl76683_start_measurement(chip);
+ if (ret < 0)
+ return ret;
+
+ return IRQ_HANDLED;
+}
+
+static int isl76683_buffer_preenable(struct iio_dev *indio_dev)
+{
+ struct isl76683_chip *chip = iio_priv(indio_dev);
+
+ mutex_lock(&chip->lock);
+ chip->buffer_running = true;
+ mutex_unlock(&chip->lock);
+ chip->photodiode = indio_dev->channels[0].channel2;
+ return isl76683_set_config(chip);
+}
+
+static int isl76683_buffer_postdisable(struct iio_dev *indio_dev)
+{
+ struct isl76683_chip *chip = iio_priv(indio_dev);
+
+ chip->buffer_running = false;
+ return 0;
+}
+
+static const struct iio_buffer_setup_ops isl76683_buffer_setup_ops = {
+ .preenable = &isl76683_buffer_preenable,
+ .postdisable = &isl76683_buffer_postdisable,
+ .predisable = iio_triggered_buffer_predisable,
+ .postenable = iio_triggered_buffer_postenable,
+ .validate_scan_mask = &iio_validate_scan_mask_onehot,
+};
+
+static int isl76683_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int *val, int *val2, long mask)
+{
+ struct isl76683_chip *chip = iio_priv(indio_dev);
+ int ret;
+
+ switch (mask) {
+ case IIO_CHAN_INFO_RAW:
+ mutex_lock(&chip->lock);
+ ret = isl76683_singleshot_conversion(chip, chan, false, val);
+ mutex_unlock(&chip->lock);
+ return ret;
+ case IIO_CHAN_INFO_PROCESSED:
+ mutex_lock(&chip->lock);
+ ret = isl76683_singleshot_conversion(chip, chan, true, val);
+ mutex_unlock(&chip->lock);
+ return ret;
+ case IIO_CHAN_INFO_HARDWAREGAIN:
+ *val = isl76683_lux_ranges_available[chip->luxrange];
+ return IIO_VAL_INT;
+ }
+
+ return -EINVAL;
+}
+
+static int isl76683_write_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int val, int val2, long mask)
+{
+ struct isl76683_chip *chip = iio_priv(indio_dev);
+ int ret;
+
+ switch (mask) {
+ case IIO_CHAN_INFO_HARDWAREGAIN:
+ mutex_lock(&chip->lock);
+ chip->luxrange = find_closest(val,
+ isl76683_lux_ranges_available,
+ ARRAY_SIZE(isl76683_lux_ranges_available));
+ ret = isl76683_set_config(chip);
+ mutex_unlock(&chip->lock);
+ return ret;
+ }
+
+ return -EINVAL;
+}
+
+static IIO_CONST_ATTR(in_illuminance_hardwaregain_available,
+ ISL76683_LUXRANGE_STR);
+
+static struct attribute *isl76683_attributes[] = {
+ &iio_const_attr_in_illuminance_hardwaregain_available.dev_attr.attr,
+ NULL
+};
+
+static const struct attribute_group isl76683_attribute_group = {
+ .attrs = isl76683_attributes,
+};
+
+#define ISL76683_CHANNEL_2(_ch2, _si) { \
+ .type = IIO_LIGHT, \
+ .modified = 1, \
+ .channel2 = _ch2, \
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \
+ BIT(IIO_CHAN_INFO_PROCESSED), \
+ .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_HARDWAREGAIN), \
+ .scan_index = _si, \
+ .scan_type = { \
+ .sign = 'u', \
+ .realbits = 16, \
+ .storagebits = 16, \
+ .endianness = IIO_CPU, \
+ }, \
+}
+
+static const struct iio_chan_spec isl76683_channels[] = {
+ {
+ .type = IIO_LIGHT,
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
+ BIT(IIO_CHAN_INFO_PROCESSED),
+ .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_HARDWAREGAIN),
+ .scan_index = 0,
+ .scan_type = {
+ .sign = 'u',
+ .realbits = 16,
+ .storagebits = 16,
+ .endianness = IIO_CPU,
+ },
+ },
+ ISL76683_CHANNEL_2(IIO_MOD_LIGHT_IR, 1),
+ ISL76683_CHANNEL_2(IIO_MOD_LIGHT_BOTH, 2),
+ IIO_CHAN_SOFT_TIMESTAMP(3),
+};
+
+static int isl76683_update_scan_mode(struct iio_dev *indio_dev,
+ const unsigned long *scan_mask)
+{
+ struct isl76683_chip *chip = iio_priv(indio_dev);
+
+ kfree(chip->buffer);
+ chip->buffer = kzalloc(indio_dev->scan_bytes, GFP_KERNEL);
+ if (chip->buffer == NULL)
+ return -ENOMEM;
+
+ return 0;
+}
+
+static const struct iio_info isl76683_info = {
+ .update_scan_mode = isl76683_update_scan_mode,
+ .read_raw = isl76683_read_raw,
+ .write_raw = isl76683_write_raw,
+ .attrs = &isl76683_attribute_group,
+ .driver_module = THIS_MODULE,
+};
+
+static int isl76683_set_trigger_state(struct iio_trigger *trig, bool enable)
+{
+ struct isl76683_chip *chip = iio_trigger_get_drvdata(trig);
+ int ret;
+
+ if (enable) {
+ chip->trig_enabled = true;
+ ret = isl76683_start_measurement(chip);
+ if (ret < 0)
+ return ret;
+ } else
+ chip->trig_enabled = false;
+
+ return 0;
+}
+
+static const struct iio_trigger_ops isl76683_trigger_ops = {
+ .owner = THIS_MODULE,
+ .set_trigger_state = isl76683_set_trigger_state,
+ .validate_device = iio_trigger_validate_own_device,
+};
+
+static int isl76683_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct isl76683_chip *chip;
+ struct iio_dev *indio_dev;
+ struct device_node *np = client->dev.of_node;
+ int rs = ISL76683_EXT_RESISTOR_DEFAULT;
+ int v, 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;
+
+ if (np) {
+ ret = of_property_read_u32(np, "isil,external-resistor", &v);
+ if (ret || v < ISL76683_KOHM_MIN || v > ISL76683_KOHM_MAX)
+ dev_warn(&client->dev,
+ "assuming %i kOhm resistor\n", rs);
+ else
+ rs = v;
+ }
+
+ chip->luxrange = ISL76683_LUX_RANGE_DEFAULT;
+ chip->external_resistor = rs;
+ chip->photodiode = ISL76683_DIODE_DEFAULT;
+ chip->buffer_running = false;
+
+ chip->rmp = devm_regmap_init_i2c(client, &isl76683_regmap_config);
+ if (IS_ERR(chip->rmp)) {
+ ret = PTR_ERR(chip->rmp);
+ dev_err(&client->dev, "%s: Error %d initializing regmap\n",
+ __func__, ret);
+ return ret;
+ }
+
+ mutex_init(&chip->lock);
+
+ init_completion(&chip->irq_complete);
+
+ if (!client->irq) {
+ dev_err(&client->dev, "no interrupt configured\n");
+ return -EINVAL;
+ }
+
+ indio_dev->dev.parent = &client->dev;
+ indio_dev->info = &isl76683_info;
+ indio_dev->channels = isl76683_channels;
+ indio_dev->num_channels = ARRAY_SIZE(isl76683_channels);
+ indio_dev->name = id->name;
+ indio_dev->modes = INDIO_DIRECT_MODE;
+
+ chip->trig_enabled = false;
+ chip->trig = devm_iio_trigger_alloc(&client->dev, "%s-dev%d",
+ indio_dev->name, indio_dev->id);
+ if (!chip->trig)
+ return -ENOMEM;
+
+ chip->trig->ops = &isl76683_trigger_ops;
+ chip->trig->dev.parent = &client->dev;
+ iio_trigger_set_drvdata(chip->trig, chip);
+
+ ret = devm_request_irq(&client->dev, client->irq,
+ isl76683_interrupt_handler,
+ IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
+ "isl76683_event", indio_dev);
+ if (ret) {
+ dev_err(&client->dev, "irq request error\n");
+ return ret;
+ }
+
+ ret = devm_iio_trigger_register(&client->dev, chip->trig);
+ if (ret) {
+ dev_err(&client->dev, "iio_trigger register error\n");
+ return ret;
+ }
+
+ ret = isl76683_reset(chip);
+ if (ret) {
+ dev_err(&client->dev, "reset failed\n");
+ return ret;
+ }
+
+ ret = isl76683_read_lux(chip, false, &v);
+ if (ret) {
+ dev_err(&client->dev, "initial dummy readout failed\n");
+ return ret;
+ }
+
+ ret = devm_iio_triggered_buffer_setup(&client->dev, indio_dev, NULL,
+ &isl76683_trigger_handler, &isl76683_buffer_setup_ops);
+ if (ret)
+ return ret;
+
+ ret = devm_iio_device_register(&client->dev, indio_dev);
+ if (ret) {
+ dev_err(&client->dev,
+ "%s(): iio registration failed with error %d\n",
+ __func__, ret);
+ return ret;
+ }
+
+ return ret;
+}
+
+static int isl76683_remove(struct i2c_client *client)
+{
+ struct iio_dev *indio_dev = i2c_get_clientdata(client);
+ struct isl76683_chip *chip = iio_priv(indio_dev);
+
+ pm_runtime_disable(&client->dev);
+ pm_runtime_set_suspended(&client->dev);
+ isl76683_power(chip, false);
+ kfree(chip->buffer);
+
+ return 0;
+}
+
+#if defined(CONFIG_PM_SLEEP) || defined(CONFIG_PM)
+static int isl76683_runtime_suspend(struct device *dev)
+{
+ struct isl76683_chip *chip =
+ iio_priv(i2c_get_clientdata(to_i2c_client(dev)));
+ int ret;
+
+ mutex_lock(&chip->lock);
+ ret = isl76683_power(chip, false);
+ regcache_mark_dirty(chip->rmp);
+ mutex_unlock(&chip->lock);
+
+ return ret;
+}
+
+static int isl76683_runtime_resume(struct device *dev)
+{
+ struct isl76683_chip *chip =
+ iio_priv(i2c_get_clientdata(to_i2c_client(dev)));
+ int ret;
+
+ mutex_lock(&chip->lock);
+ ret = isl76683_power(chip, true);
+ mutex_unlock(&chip->lock);
+
+ return ret;
+}
+#endif
+
+static const struct dev_pm_ops isl76683_pm_ops = {
+ SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend,
+ pm_runtime_force_resume)
+ SET_RUNTIME_PM_OPS(isl76683_runtime_suspend,
+ isl76683_runtime_resume, NULL)
+};
+
+static const struct i2c_device_id isl76683_id[] = {
+ {"isl76683", 0},
+ {}
+};
+MODULE_DEVICE_TABLE(i2c, isl76683_id);
+
+static const struct of_device_id isl76683_of_match[] = {
+ { .compatible = "isil,isl76683", },
+ { },
+};
+MODULE_DEVICE_TABLE(of, isl76683_of_match);
+
+static struct i2c_driver isl76683_driver = {
+ .driver = {
+ .name = "isl76683",
+ .of_match_table = isl76683_of_match,
+ .pm = &isl76683_pm_ops,
+ },
+ .probe = isl76683_probe,
+ .remove = isl76683_remove,
+ .id_table = isl76683_id,
+};
+
+module_i2c_driver(isl76683_driver);
+
+MODULE_DESCRIPTION("ISL76683 Ambient Light Sensor driver");
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Christoph Fritz <chf.fritz@googlemail.com>");
--
2.1.4
^ permalink raw reply related [flat|nested] 9+ messages in thread
* [PATCH 2/3] dt-bindings: iio: add Intersil isl76683 light sensor bindings
2017-11-07 15:36 [PATCH 0/3] iio: add Intersil isl76683 light sensor support Christoph Fritz
2017-11-07 15:36 ` [PATCH 1/3] iio: light: add support for Intersil isl76683 sensor Christoph Fritz
@ 2017-11-07 15:36 ` Christoph Fritz
2017-11-10 21:26 ` Rob Herring
2017-11-07 15:36 ` [PATCH 3/3] iio: light: isl76683 add way to adjust irq threshold Christoph Fritz
2 siblings, 1 reply; 9+ messages in thread
From: Christoph Fritz @ 2017-11-07 15:36 UTC (permalink / raw)
To: Jonathan Cameron, Peter Meerwald-Stadler, Rob Herring
Cc: linux-iio, devicetree
This patch adds documentation of device tree bindings for Intersil
isl76683 light sensor.
Signed-off-by: Christoph Fritz <chf.fritz@googlemail.com>
---
.../devicetree/bindings/iio/light/isl76683.txt | 26 ++++++++++++++++++++++
1 file changed, 26 insertions(+)
create mode 100644 Documentation/devicetree/bindings/iio/light/isl76683.txt
diff --git a/Documentation/devicetree/bindings/iio/light/isl76683.txt b/Documentation/devicetree/bindings/iio/light/isl76683.txt
new file mode 100644
index 0000000..657f17d
--- /dev/null
+++ b/Documentation/devicetree/bindings/iio/light/isl76683.txt
@@ -0,0 +1,26 @@
+* ISL76683 ambient light sensor
+
+http://www.intersil.com/content/dam/Intersil/documents/isl7/isl76683.pdf
+
+Required properties:
+
+ - compatible: must be "isil,isl76683"
+ - reg: the I2C address of the sensor
+ - interrupt-parent: should be the phandle for the interrupt controller
+ - interrupts: interrupt mapping for GPIO IRQ
+
+Optional properties:
+
+ - isil,external-resistor: integer in kOhm of external resistor R_ext.
+ Valid values are from 1 to 1000.
+ If not supplied, 100 kOhm will be assumed.
+
+Example:
+
+isl76683@44 {
+ compatible = "isil,isl76683";
+ reg = <0x44>;
+ interrupt-parent = <&gpio1>;
+ interrupts = <20 IRQ_TYPE_LEVEL_LOW>;
+ isil,external-resistor = <50>;
+};
--
2.1.4
^ permalink raw reply related [flat|nested] 9+ messages in thread
* [PATCH 3/3] iio: light: isl76683 add way to adjust irq threshold
2017-11-07 15:36 [PATCH 0/3] iio: add Intersil isl76683 light sensor support Christoph Fritz
2017-11-07 15:36 ` [PATCH 1/3] iio: light: add support for Intersil isl76683 sensor Christoph Fritz
2017-11-07 15:36 ` [PATCH 2/3] dt-bindings: iio: add Intersil isl76683 light sensor bindings Christoph Fritz
@ 2017-11-07 15:36 ` Christoph Fritz
2 siblings, 0 replies; 9+ messages in thread
From: Christoph Fritz @ 2017-11-07 15:36 UTC (permalink / raw)
To: Jonathan Cameron, Peter Meerwald-Stadler, Rob Herring
Cc: linux-iio, devicetree
This patch adds sysfs read/write support for upper and lower irq
thresholds. So it's possible that only on certain lux ranges the
irq triggered measurement happens.
Signed-off-by: Christoph Fritz <chf.fritz@googlemail.com>
---
.../ABI/testing/sysfs-bus-iio-light-isl76683 | 17 +++++++
drivers/iio/light/isl76683.c | 58 ++++++++++++++++++++--
2 files changed, 71 insertions(+), 4 deletions(-)
create mode 100644 Documentation/ABI/testing/sysfs-bus-iio-light-isl76683
diff --git a/Documentation/ABI/testing/sysfs-bus-iio-light-isl76683 b/Documentation/ABI/testing/sysfs-bus-iio-light-isl76683
new file mode 100644
index 0000000..c7e08d7
--- /dev/null
+++ b/Documentation/ABI/testing/sysfs-bus-iio-light-isl76683
@@ -0,0 +1,17 @@
+What: /sys/bus/iio/devices/iio:deviceX/in_illuminance_threshold_low
+Date: November 2017
+KernelVersion: 4.15.0
+Contact: linux-iio@vger.kernel.org
+Description:
+ Raw value of lower threshold for the interrupt.
+ Reading returns 8-bit MSB data of a 16-bit threshold.
+ Writing 0...255 represents 8-bit MSB data of a 16-bit threshold.
+
+What: /sys/bus/iio/devices/iio:deviceX/in_illuminance_threshold_up
+Date: November 2017
+KernelVersion: 4.15.0
+Contact: linux-iio@vger.kernel.org
+Description:
+ Raw value of upper threshold for the interrupt.
+ Reading returns 8-bit MSB data of a 16-bit threshold.
+ Writing 0...255 represents 8-bit MSB data of a 16-bit threshold.
diff --git a/drivers/iio/light/isl76683.c b/drivers/iio/light/isl76683.c
index b730276..889fb85 100644
--- a/drivers/iio/light/isl76683.c
+++ b/drivers/iio/light/isl76683.c
@@ -74,11 +74,14 @@ static const int isl76683_lux_ranges_available[] = {
#define ISL76683_KOHM_MAX 1000
#define ISL76683_INTPERS_DEFAULT 0x0
#define ISL76683_THR_DEFAULT 0x7f
+#define ISL76683_THR_MAX 0xFF
struct isl76683_chip {
enum isl76683_lux_range luxrange;
int external_resistor;
enum isl76683_dmode photodiode;
+ int threshold_up;
+ int threshold_low;
struct i2c_client *client;
struct regmap *rmp;
struct completion irq_complete;
@@ -159,13 +162,11 @@ static int isl76683_set_config(struct isl76683_chip *chip)
if (ret < 0)
return ret;
- ret = regmap_write(chip->rmp, ISL76683_REG_THR_HI,
- ISL76683_THR_DEFAULT);
+ ret = regmap_write(chip->rmp, ISL76683_REG_THR_HI, chip->threshold_up);
if (ret < 0)
return ret;
- ret = regmap_write(chip->rmp, ISL76683_REG_THR_LO,
- ISL76683_THR_DEFAULT);
+ ret = regmap_write(chip->rmp, ISL76683_REG_THR_LO, chip->threshold_low);
if (ret < 0)
return ret;
@@ -415,11 +416,58 @@ static int isl76683_write_raw(struct iio_dev *indio_dev,
return -EINVAL;
}
+#define ISL76683_SYSFS_STORE(ident, _max) \
+static ssize_t in_illuminance_##ident##_store(struct device *dev, \
+ struct device_attribute *attr, \
+ const char *buf, size_t len) \
+{ \
+ struct iio_dev *indio_dev = dev_to_iio_dev(dev); \
+ struct isl76683_chip *chip = iio_priv(indio_dev); \
+ unsigned int val; \
+ int ret; \
+ \
+ if (kstrtouint(buf, 10, &val)) \
+ return -EINVAL; \
+ \
+ if (val > _max) \
+ return -EINVAL; \
+ \
+ mutex_lock(&chip->lock); \
+ chip->ident = val; \
+ ret = isl76683_set_config(chip); \
+ mutex_unlock(&chip->lock); \
+ \
+ if (ret) \
+ return -EIO; \
+ \
+ return len; \
+}
+
+ISL76683_SYSFS_STORE(threshold_low, ISL76683_THR_MAX)
+ISL76683_SYSFS_STORE(threshold_up, ISL76683_THR_MAX)
+
+#define ISL76683_SYSFS_SHOW(ident, show_val) \
+static ssize_t in_illuminance_##ident##_show(struct device *dev, \
+ struct device_attribute *attr, char *buf) \
+{ \
+ struct iio_dev *indio_dev = dev_to_iio_dev(dev); \
+ struct isl76683_chip *chip = iio_priv(indio_dev); \
+ \
+ return snprintf(buf, PAGE_SIZE, "%i\n", show_val); \
+}
+
+ISL76683_SYSFS_SHOW(threshold_up, chip->threshold_up)
+ISL76683_SYSFS_SHOW(threshold_low, chip->threshold_low)
+
static IIO_CONST_ATTR(in_illuminance_hardwaregain_available,
ISL76683_LUXRANGE_STR);
+static IIO_DEVICE_ATTR_RW(in_illuminance_threshold_up, 0);
+static IIO_DEVICE_ATTR_RW(in_illuminance_threshold_low, 0);
static struct attribute *isl76683_attributes[] = {
&iio_const_attr_in_illuminance_hardwaregain_available.dev_attr.attr,
+ &iio_dev_attr_in_illuminance_threshold_up.dev_attr.attr,
+ &iio_dev_attr_in_illuminance_threshold_low.dev_attr.attr,
NULL
};
@@ -535,6 +583,8 @@ static int isl76683_probe(struct i2c_client *client,
chip->luxrange = ISL76683_LUX_RANGE_DEFAULT;
chip->external_resistor = rs;
chip->photodiode = ISL76683_DIODE_DEFAULT;
+ chip->threshold_up = ISL76683_THR_DEFAULT;
+ chip->threshold_low = ISL76683_THR_DEFAULT;
chip->buffer_running = false;
chip->rmp = devm_regmap_init_i2c(client, &isl76683_regmap_config);
--
2.1.4
^ permalink raw reply related [flat|nested] 9+ messages in thread
* Re: [PATCH 1/3] iio: light: add support for Intersil isl76683 sensor
2017-11-07 15:36 ` [PATCH 1/3] iio: light: add support for Intersil isl76683 sensor Christoph Fritz
@ 2017-11-07 16:28 ` Peter Meerwald-Stadler
2017-11-10 15:55 ` Christoph Fritz
2017-11-11 0:50 ` Jonathan Cameron
1 sibling, 1 reply; 9+ messages in thread
From: Peter Meerwald-Stadler @ 2017-11-07 16:28 UTC (permalink / raw)
To: Christoph Fritz; +Cc: Jonathan Cameron, linux-iio
On Tue, 7 Nov 2017, Christoph Fritz wrote:
> This patch adds support for Intersil isl76683 light sensor.
nitpicking in subject: maybe 'Add support...', start uppercase
more comments below
> Signed-off-by: Christoph Fritz <chf.fritz@googlemail.com>
> ---
> drivers/iio/light/Kconfig | 12 +
> drivers/iio/light/Makefile | 1 +
> drivers/iio/light/isl76683.c | 693 +++++++++++++++++++++++++++++++++++++++++++
> 3 files changed, 706 insertions(+)
> create mode 100644 drivers/iio/light/isl76683.c
>
> diff --git a/drivers/iio/light/Kconfig b/drivers/iio/light/Kconfig
> index 2356ed9..4f0882c 100644
> --- a/drivers/iio/light/Kconfig
> +++ b/drivers/iio/light/Kconfig
> @@ -194,6 +194,18 @@ config ISL29125
> To compile this driver as a module, choose M here: the module will be
> called isl29125.
>
> +config ISL76683
> + tristate "Intersil ISL76683 light sensor"
> + depends on I2C
> + select IIO_BUFFER
> + select IIO_TRIGGERED_BUFFER
> + help
> + Say Y here if you want to build a driver for the Intersil ISL76683
> + light sensor for I2C.
> +
> + To compile this driver as a module, choose M here: the module will be
> + called isl76683.
> +
> config HID_SENSOR_ALS
> depends on HID_SENSOR_HUB
> select IIO_BUFFER
> diff --git a/drivers/iio/light/Makefile b/drivers/iio/light/Makefile
> index fa32fa4..886a51f 100644
> --- a/drivers/iio/light/Makefile
> +++ b/drivers/iio/light/Makefile
> @@ -22,6 +22,7 @@ obj-$(CONFIG_HID_SENSOR_PROX) += hid-sensor-prox.o
> obj-$(CONFIG_SENSORS_ISL29018) += isl29018.o
> obj-$(CONFIG_SENSORS_ISL29028) += isl29028.o
> obj-$(CONFIG_ISL29125) += isl29125.o
> +obj-$(CONFIG_ISL76683) += isl76683.o
> obj-$(CONFIG_JSA1212) += jsa1212.o
> obj-$(CONFIG_SENSORS_LM3533) += lm3533-als.o
> obj-$(CONFIG_LTR501) += ltr501.o
> diff --git a/drivers/iio/light/isl76683.c b/drivers/iio/light/isl76683.c
> new file mode 100644
> index 0000000..b730276
> --- /dev/null
> +++ b/drivers/iio/light/isl76683.c
> @@ -0,0 +1,693 @@
> +/*
> + * IIO driver for the light sensor ISL76683.
> + *
> + * Copyright (c) 2017 Christoph Fritz <chf.fritz@googlemail.com>
> + *
> + * 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.
> + *
> + * Datasheet:
> + * http://www.intersil.com/content/dam/Intersil/documents/isl7/isl76683.pdf
> + */
> +
> +#include <linux/module.h>
> +#include <linux/i2c.h>
> +#include <linux/pm_runtime.h>
> +#include <linux/regmap.h>
> +#include <linux/util_macros.h>
> +#include <linux/iio/iio.h>
> +#include <linux/iio/sysfs.h>
> +#include <linux/iio/buffer.h>
> +#include <linux/iio/trigger.h>
> +#include <linux/iio/trigger_consumer.h>
> +#include <linux/iio/triggered_buffer.h>
> +
> +#define ISL76683_REG_CMD 0x00
> +#define ISL76683_REG_CTRL 0x01
> +#define ISL76683_REG_THR_HI 0x02
> +#define ISL76683_REG_THR_LO 0x03
> +#define ISL76683_REG_SENSOR_L 0x04
maybe L -> LSB, M -> MSB to make it more clear
OTOH SENSOR_M is not really used, so could drop as well and maybe add a
comment to denote that data is 16-bit little endian
> +#define ISL76683_REG_SENSOR_M 0x05
> +#define ISL76683_REG_CLEAR_INT 0x40
> +#define ISL76683_REGMAP_MAX 0x40
> +
> +#define ISL76683_CMD_ENABLE BIT(7)
> +#define ISL76683_CMD_PWRDWN BIT(6)
> +#define ISL76683_WIDTH_MASK 0x3
> +#define ISL76683_PHOTOD_SHFT 2
> +#define ISL76683_PHOTOD_MASK (0x3 << ISL76683_PHOTOD_SHFT)
could use GENMASK()
> +#define ISL76683_INTPERS_MASK 0x3
> +#define ISL76683_LUXRANGE_SHFT 2
> +#define ISL76683_LUXRANGE_MASK (0x3 << ISL76683_LUXRANGE_SHFT)
> +#define ISL76683_LUXRANGE_STR "1000 4000 16000 64000"
> +
> +enum isl76683_dmode {
> + ISL76683_DIODE_0 = 0,
> + ISL76683_DIODE_IR,
> + ISL76683_DIODE_DIFF,
> +};
> +
> +enum isl76683_lux_range {
> + ISL76683_LUX_1000 = 0,
> + ISL76683_LUX_4000,
> + ISL76683_LUX_16000,
> + ISL76683_LUX_64000,
> +};
> +
> +static const int isl76683_lux_ranges_available[] = {
> + 1000, 4000, 16000, 64000};
> +
> +#define ISL76683_LUX_RANGE_DEFAULT ISL76683_LUX_1000
> +#define ISL76683_DIODE_MAX ISL76683_DIODE_DIFF
> +#define ISL76683_DIODE_DEFAULT ISL76683_DIODE_0
> +#define ISL76683_WIDTH_DEFAULT 0x0
> +#define ISL76683_RESOLUTION_DEFAULT 16
> +#define ISL76683_EXT_RESISTOR_DEFAULT 100
> +#define ISL76683_KOHM_MIN 1
> +#define ISL76683_KOHM_MAX 1000
> +#define ISL76683_INTPERS_DEFAULT 0x0
> +#define ISL76683_THR_DEFAULT 0x7f
> +
> +struct isl76683_chip {
> + enum isl76683_lux_range luxrange;
> + int external_resistor;
> + enum isl76683_dmode photodiode;
> + struct i2c_client *client;
> + struct regmap *rmp;
> + struct completion irq_complete;
> + struct iio_trigger *trig;
> + bool trig_enabled;
> + struct mutex lock;
> + __le16 *buffer;
isl76683_trigger_handler() stores the already endianness-converted value
into buffer, so type should be u16?
why do we need buffer at all?
> + s64 time_ns;
> + bool buffer_running;
> +};
> +
> +static bool isl76683_readable_reg(struct device *dev, unsigned int reg)
> +{
> + switch (reg) {
> + case ISL76683_REG_CMD:
> + case ISL76683_REG_CTRL:
> + case ISL76683_REG_THR_HI:
> + case ISL76683_REG_THR_LO:
> + case ISL76683_REG_SENSOR_L:
> + case ISL76683_REG_SENSOR_M:
> + case ISL76683_REG_CLEAR_INT:
> + return true;
> + default:
> + return false;
> + }
> +}
> +
> +static bool isl76683_writeable_reg(struct device *dev, unsigned int reg)
> +{
> + switch (reg) {
> + case ISL76683_REG_CMD:
> + case ISL76683_REG_CTRL:
> + case ISL76683_REG_THR_HI:
> + case ISL76683_REG_THR_LO:
> + return true;
> + default:
> + return false;
> + }
> +}
> +
> +static bool isl76683_is_volatile_reg(struct device *dev, unsigned int reg)
> +{
> + switch (reg) {
> + case ISL76683_REG_SENSOR_L:
> + case ISL76683_REG_SENSOR_M:
> + case ISL76683_REG_CLEAR_INT:
> + return true;
> + default:
> + return false;
> + }
> +}
> +
> +static const struct regmap_config isl76683_regmap_config = {
> + .reg_bits = 8,
> + .val_bits = 8,
> + .max_register = ISL76683_REGMAP_MAX,
> + .readable_reg = isl76683_readable_reg,
> + .writeable_reg = isl76683_writeable_reg,
> + .volatile_reg = isl76683_is_volatile_reg,
> + .cache_type = REGCACHE_RBTREE,
> +};
> +
> +static int isl76683_set_config(struct isl76683_chip *chip)
> +{
> + int ret;
> +
> + ret = regmap_update_bits(chip->rmp, ISL76683_REG_CTRL,
> + ISL76683_LUXRANGE_MASK | ISL76683_INTPERS_MASK,
> + (chip->luxrange << ISL76683_LUXRANGE_SHFT) |
> + ISL76683_INTPERS_DEFAULT);
> + if (ret < 0)
> + return ret;
> +
> + ret = regmap_update_bits(chip->rmp, ISL76683_REG_CMD,
> + ISL76683_PHOTOD_MASK | ISL76683_WIDTH_MASK,
> + (chip->photodiode << ISL76683_PHOTOD_SHFT) |
> + ISL76683_WIDTH_DEFAULT);
> + if (ret < 0)
> + return ret;
> +
> + ret = regmap_write(chip->rmp, ISL76683_REG_THR_HI,
> + ISL76683_THR_DEFAULT);
> + if (ret < 0)
> + return ret;
> +
> + ret = regmap_write(chip->rmp, ISL76683_REG_THR_LO,
> + ISL76683_THR_DEFAULT);
could just
return regmap_write(...);
> + if (ret < 0)
> +
> + return 0;
> +}
> +
> +static int isl76683_power(struct isl76683_chip *chip, bool on)
> +{
> + int ret;
> +
> + ret = regmap_update_bits(chip->rmp, ISL76683_REG_CMD,
> + ISL76683_CMD_ENABLE | ISL76683_CMD_PWRDWN,
> + 0x0);
> + if (ret < 0)
> + return ret;
why are these bits first set to 0 and then bits are written with a
separate command?
is this a requirement of the chip to clear and set?
> +
> + ret = regmap_update_bits(chip->rmp, ISL76683_REG_CMD,
> + ISL76683_CMD_ENABLE | ISL76683_CMD_PWRDWN,
> + on ? ISL76683_CMD_ENABLE : ISL76683_CMD_PWRDWN);
> + if (ret < 0)
> + return ret;
> +
> + return on ? isl76683_set_config(chip) : 0;
> +}
> +
> +static int isl76683_reset(struct isl76683_chip *chip)
> +{
> + int ret;
> +
> + ret = isl76683_power(chip, false);
> + if (ret < 0)
> + return ret;
is an explicit delay needed here? just curious...
> +
> + return isl76683_power(chip, true);
> +}
> +
> +static int isl76683_read_lux(struct isl76683_chip *chip,
> + bool is_processed, int *val)
> +{
> + unsigned int sensor_data, range, fsr;
> + __le16 sensor_raw;
> + int ret;
> +
> + ret = regmap_bulk_read(chip->rmp, ISL76683_REG_SENSOR_L,
> + &sensor_raw, sizeof(sensor_raw));
> + if (ret)
> + return ret;
> +
> + sensor_data = le16_to_cpu(sensor_raw);
> +
> + if (!is_processed) {
in this case sensor_data is probably NOT in lux?
function name read_lux() is misleading?
> + *val = sensor_data;
> + return 0;
> + }
> +
> + /* range values taken from datasheet (table 9) */
> + if (chip->luxrange == ISL76683_LUX_1000)
> + range = 973;
> + else if (chip->luxrange == ISL76683_LUX_4000)
> + range = 3892;
> + else if (chip->luxrange == ISL76683_LUX_16000)
> + range = 15568;
> + else if (chip->luxrange == ISL76683_LUX_64000)
> + range = 62272;
> + else
> + return -EINVAL;
> +
> + /* equations from datasheet (EQ.3 and EQ.4) */
> + fsr = (100 * range) / chip->external_resistor;
> + *val = (fsr * sensor_data) / (1 << ISL76683_RESOLUTION_DEFAULT);
> +
> + return 0;
> +}
> +
> +static irqreturn_t isl76683_interrupt_handler(int irq, void *private)
> +{
> + struct iio_dev *indio_dev = private;
> + struct isl76683_chip *chip = iio_priv(indio_dev);
> +
> + chip->time_ns = iio_get_time_ns(indio_dev);
> +
> + if (chip->trig_enabled)
> + iio_trigger_poll(chip->trig);
> +
> + if (!completion_done(&chip->irq_complete))
> + complete(&chip->irq_complete);
> +
> + return IRQ_HANDLED;
> +}
> +
> +static inline int isl76683_start_measurement(struct isl76683_chip *chip)
> +{
> + int dummy;
> +
> + /* dummy read is clearing irq and triggers new measurement */
> + return regmap_read(chip->rmp, ISL76683_REG_CLEAR_INT, &dummy);
> +}
> +
> +static int isl76683_singleshot_conversion(struct isl76683_chip *chip,
> + const struct iio_chan_spec *chan,
> + bool is_processed, int *val)
> +{
> + long timeout;
> + int ret;
> +
> + if (chip->buffer_running)
> + return -EAGAIN;
> +
> + switch (chan->channel2) {
> + case IIO_MOD_LIGHT_BOTH:
> + chip->photodiode = ISL76683_DIODE_DIFF;
> + break;
> + case IIO_MOD_LIGHT_IR:
> + chip->photodiode = ISL76683_DIODE_IR;
> + break;
> + default:
> + chip->photodiode = ISL76683_DIODE_0;
> + }
> +
> + ret = isl76683_set_config(chip);
> + if (ret)
> + return ret;
> +
> + ret = isl76683_start_measurement(chip);
> + if (ret < 0)
> + return ret;
> +
> + /* wait for measurement to complete */
> + timeout = wait_for_completion_interruptible_timeout(
> + &chip->irq_complete,
> + msecs_to_jiffies(5000));
> + if (timeout == 0) {
> + dev_err(&chip->client->dev, "measurement timed out\n");
> + return -ETIMEDOUT;
> + } else if (timeout < 0) {
> + dev_err(&chip->client->dev, "wait for measurement failed\n");
> + return -EINTR;
> + }
> +
> + ret = isl76683_read_lux(chip, is_processed, val);
> + if (ret) {
> + dev_err(&chip->client->dev, "%s: Error %d reading lux\n",
> + __func__, ret);
> + return ret;
> + }
> +
> + return IIO_VAL_INT;
> +}
> +
> +static irqreturn_t isl76683_trigger_handler(int irq, void *p)
> +{
> + struct iio_poll_func *pf = p;
> + struct iio_dev *indio_dev = pf->indio_dev;
> + struct isl76683_chip *chip = iio_priv(indio_dev);
> + __le16 buf;
> + int ret;
> +
> + ret = regmap_bulk_read(chip->rmp, ISL76683_REG_SENSOR_L, &buf, 2);
sizeof(buf)
> + if (ret)
> + return ret;
> +
> + chip->buffer[0] = le16_to_cpu(buf);
> +
> + iio_push_to_buffers_with_timestamp(indio_dev, chip->buffer,
> + chip->time_ns);
> +
> + iio_trigger_notify_done(indio_dev->trig);
> +
> + ret = isl76683_start_measurement(chip);
> + if (ret < 0)
> + return ret;
I think even if this fails IRQ_HANDLED should be returned
> +
> + return IRQ_HANDLED;
> +}
> +
> +static int isl76683_buffer_preenable(struct iio_dev *indio_dev)
> +{
> + struct isl76683_chip *chip = iio_priv(indio_dev);
> +
> + mutex_lock(&chip->lock);
> + chip->buffer_running = true;
> + mutex_unlock(&chip->lock);
should set_config() be also protected by the mutex?
> + chip->photodiode = indio_dev->channels[0].channel2;
> + return isl76683_set_config(chip);
> +}
> +
> +static int isl76683_buffer_postdisable(struct iio_dev *indio_dev)
> +{
> + struct isl76683_chip *chip = iio_priv(indio_dev);
> +
> + chip->buffer_running = false;
> + return 0;
> +}
> +
> +static const struct iio_buffer_setup_ops isl76683_buffer_setup_ops = {
> + .preenable = &isl76683_buffer_preenable,
> + .postdisable = &isl76683_buffer_postdisable,
> + .predisable = iio_triggered_buffer_predisable,
> + .postenable = iio_triggered_buffer_postenable,
> + .validate_scan_mask = &iio_validate_scan_mask_onehot,
> +};
> +
> +static int isl76683_read_raw(struct iio_dev *indio_dev,
> + struct iio_chan_spec const *chan,
> + int *val, int *val2, long mask)
> +{
> + struct isl76683_chip *chip = iio_priv(indio_dev);
> + int ret;
> +
> + switch (mask) {
> + case IIO_CHAN_INFO_RAW:
> + mutex_lock(&chip->lock);
> + ret = isl76683_singleshot_conversion(chip, chan, false, val);
> + mutex_unlock(&chip->lock);
> + return ret;
> + case IIO_CHAN_INFO_PROCESSED:
> + mutex_lock(&chip->lock);
> + ret = isl76683_singleshot_conversion(chip, chan, true, val);
> + mutex_unlock(&chip->lock);
> + return ret;
> + case IIO_CHAN_INFO_HARDWAREGAIN:
use _SCALE?
> + *val = isl76683_lux_ranges_available[chip->luxrange];
> + return IIO_VAL_INT;
> + }
> +
> + return -EINVAL;
> +}
> +
> +static int isl76683_write_raw(struct iio_dev *indio_dev,
> + struct iio_chan_spec const *chan,
> + int val, int val2, long mask)
> +{
> + struct isl76683_chip *chip = iio_priv(indio_dev);
> + int ret;
> +
> + switch (mask) {
> + case IIO_CHAN_INFO_HARDWAREGAIN:
> + mutex_lock(&chip->lock);
> + chip->luxrange = find_closest(val,
> + isl76683_lux_ranges_available,
> + ARRAY_SIZE(isl76683_lux_ranges_available));
> + ret = isl76683_set_config(chip);
> + mutex_unlock(&chip->lock);
> + return ret;
> + }
> +
> + return -EINVAL;
> +}
> +
> +static IIO_CONST_ATTR(in_illuminance_hardwaregain_available,
> + ISL76683_LUXRANGE_STR);
> +
> +static struct attribute *isl76683_attributes[] = {
> + &iio_const_attr_in_illuminance_hardwaregain_available.dev_attr.attr,
> + NULL
> +};
> +
> +static const struct attribute_group isl76683_attribute_group = {
> + .attrs = isl76683_attributes,
> +};
> +
> +#define ISL76683_CHANNEL_2(_ch2, _si) { \
why _2?
> + .type = IIO_LIGHT, \
> + .modified = 1, \
> + .channel2 = _ch2, \
> + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \
> + BIT(IIO_CHAN_INFO_PROCESSED), \
for INFO_RAW the measurement is not in lux, hence IIO_INTENSITY should be
used? why have _RAW and _PROCESSED?
the computation in the _PROCESSED path should ideally go into the _SCALE
factor
> + .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_HARDWAREGAIN), \
I think this should be _SCALE rather
> + .scan_index = _si, \
> + .scan_type = { \
> + .sign = 'u', \
> + .realbits = 16, \
> + .storagebits = 16, \
> + .endianness = IIO_CPU, \
you can/should specify IIO_LE and save the endianness conversion in
isl76683_trigger_handler()
> + }, \
> +}
> +
> +static const struct iio_chan_spec isl76683_channels[] = {
> + {
> + .type = IIO_LIGHT,
> + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
> + BIT(IIO_CHAN_INFO_PROCESSED),
> + .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_HARDWAREGAIN),
> + .scan_index = 0,
> + .scan_type = {
> + .sign = 'u',
> + .realbits = 16,
> + .storagebits = 16,
> + .endianness = IIO_CPU,
> + },
> + },
> + ISL76683_CHANNEL_2(IIO_MOD_LIGHT_IR, 1),
> + ISL76683_CHANNEL_2(IIO_MOD_LIGHT_BOTH, 2),
> + IIO_CHAN_SOFT_TIMESTAMP(3),
> +};
> +
> +static int isl76683_update_scan_mode(struct iio_dev *indio_dev,
> + const unsigned long *scan_mask)
> +{
> + struct isl76683_chip *chip = iio_priv(indio_dev);
> +
> + kfree(chip->buffer);
> + chip->buffer = kzalloc(indio_dev->scan_bytes, GFP_KERNEL);
most drivers do static allocation of the buffer, i.e. 2 bytes + 6 bytes
padding + 8 bytes timestamp
> + if (chip->buffer == NULL)
> + return -ENOMEM;
> +
> + return 0;
> +}
> +
> +static const struct iio_info isl76683_info = {
> + .update_scan_mode = isl76683_update_scan_mode,
> + .read_raw = isl76683_read_raw,
> + .write_raw = isl76683_write_raw,
> + .attrs = &isl76683_attribute_group,
> + .driver_module = THIS_MODULE,
THIS_MODULE not needed anymore
> +};
> +
> +static int isl76683_set_trigger_state(struct iio_trigger *trig, bool enable)
> +{
> + struct isl76683_chip *chip = iio_trigger_get_drvdata(trig);
> + int ret;
> +
> + if (enable) {
> + chip->trig_enabled = true;
> + ret = isl76683_start_measurement(chip);
> + if (ret < 0)
> + return ret;
> + } else
> + chip->trig_enabled = false;
> +
> + return 0;
> +}
> +
> +static const struct iio_trigger_ops isl76683_trigger_ops = {
> + .owner = THIS_MODULE,
> + .set_trigger_state = isl76683_set_trigger_state,
> + .validate_device = iio_trigger_validate_own_device,
> +};
> +
> +static int isl76683_probe(struct i2c_client *client,
> + const struct i2c_device_id *id)
> +{
> + struct isl76683_chip *chip;
> + struct iio_dev *indio_dev;
> + struct device_node *np = client->dev.of_node;
> + int rs = ISL76683_EXT_RESISTOR_DEFAULT;
> + int v, 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;
> +
> + if (np) {
> + ret = of_property_read_u32(np, "isil,external-resistor", &v);
> + if (ret || v < ISL76683_KOHM_MIN || v > ISL76683_KOHM_MAX)
> + dev_warn(&client->dev,
> + "assuming %i kOhm resistor\n", rs);
I'd rather fail if the value is incorrect (matter of taste)
> + else
> + rs = v;
> + }
> +
> + chip->luxrange = ISL76683_LUX_RANGE_DEFAULT;
> + chip->external_resistor = rs;
> + chip->photodiode = ISL76683_DIODE_DEFAULT;
> + chip->buffer_running = false;
> +
> + chip->rmp = devm_regmap_init_i2c(client, &isl76683_regmap_config);
> + if (IS_ERR(chip->rmp)) {
> + ret = PTR_ERR(chip->rmp);
> + dev_err(&client->dev, "%s: Error %d initializing regmap\n",
> + __func__, ret);
> + return ret;
> + }
> +
> + mutex_init(&chip->lock);
> +
> + init_completion(&chip->irq_complete);
> +
> + if (!client->irq) {
> + dev_err(&client->dev, "no interrupt configured\n");
> + return -EINVAL;
> + }
> +
> + indio_dev->dev.parent = &client->dev;
> + indio_dev->info = &isl76683_info;
> + indio_dev->channels = isl76683_channels;
> + indio_dev->num_channels = ARRAY_SIZE(isl76683_channels);
> + indio_dev->name = id->name;
> + indio_dev->modes = INDIO_DIRECT_MODE;
> +
> + chip->trig_enabled = false;
> + chip->trig = devm_iio_trigger_alloc(&client->dev, "%s-dev%d",
> + indio_dev->name, indio_dev->id);
> + if (!chip->trig)
> + return -ENOMEM;
> +
> + chip->trig->ops = &isl76683_trigger_ops;
> + chip->trig->dev.parent = &client->dev;
> + iio_trigger_set_drvdata(chip->trig, chip);
> +
> + ret = devm_request_irq(&client->dev, client->irq,
> + isl76683_interrupt_handler,
> + IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
> + "isl76683_event", indio_dev);
> + if (ret) {
> + dev_err(&client->dev, "irq request error\n");
> + return ret;
> + }
> +
> + ret = devm_iio_trigger_register(&client->dev, chip->trig);
> + if (ret) {
> + dev_err(&client->dev, "iio_trigger register error\n");
> + return ret;
> + }
> +
> + ret = isl76683_reset(chip);
> + if (ret) {
> + dev_err(&client->dev, "reset failed\n");
> + return ret;
> + }
> +
> + ret = isl76683_read_lux(chip, false, &v);
> + if (ret) {
> + dev_err(&client->dev, "initial dummy readout failed\n");
> + return ret;
> + }
> +
> + ret = devm_iio_triggered_buffer_setup(&client->dev, indio_dev, NULL,
> + &isl76683_trigger_handler, &isl76683_buffer_setup_ops);
> + if (ret)
> + return ret;
> +
> + ret = devm_iio_device_register(&client->dev, indio_dev);
> + if (ret) {
> + dev_err(&client->dev,
> + "%s(): iio registration failed with error %d\n",
> + __func__, ret);
> + return ret;
> + }
> +
> + return ret;
> +}
> +
> +static int isl76683_remove(struct i2c_client *client)
> +{
> + struct iio_dev *indio_dev = i2c_get_clientdata(client);
> + struct isl76683_chip *chip = iio_priv(indio_dev);
> +
> + pm_runtime_disable(&client->dev);
> + pm_runtime_set_suspended(&client->dev);
> + isl76683_power(chip, false);
> + kfree(chip->buffer);
> +
> + return 0;
> +}
> +
> +#if defined(CONFIG_PM_SLEEP) || defined(CONFIG_PM)
> +static int isl76683_runtime_suspend(struct device *dev)
> +{
> + struct isl76683_chip *chip =
> + iio_priv(i2c_get_clientdata(to_i2c_client(dev)));
> + int ret;
> +
> + mutex_lock(&chip->lock);
> + ret = isl76683_power(chip, false);
> + regcache_mark_dirty(chip->rmp);
> + mutex_unlock(&chip->lock);
> +
> + return ret;
> +}
> +
> +static int isl76683_runtime_resume(struct device *dev)
> +{
> + struct isl76683_chip *chip =
> + iio_priv(i2c_get_clientdata(to_i2c_client(dev)));
> + int ret;
> +
> + mutex_lock(&chip->lock);
> + ret = isl76683_power(chip, true);
> + mutex_unlock(&chip->lock);
> +
> + return ret;
> +}
> +#endif
> +
> +static const struct dev_pm_ops isl76683_pm_ops = {
> + SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend,
> + pm_runtime_force_resume)
> + SET_RUNTIME_PM_OPS(isl76683_runtime_suspend,
> + isl76683_runtime_resume, NULL)
> +};
> +
> +static const struct i2c_device_id isl76683_id[] = {
> + {"isl76683", 0},
> + {}
> +};
> +MODULE_DEVICE_TABLE(i2c, isl76683_id);
> +
> +static const struct of_device_id isl76683_of_match[] = {
> + { .compatible = "isil,isl76683", },
> + { },
> +};
> +MODULE_DEVICE_TABLE(of, isl76683_of_match);
> +
> +static struct i2c_driver isl76683_driver = {
> + .driver = {
> + .name = "isl76683",
> + .of_match_table = isl76683_of_match,
> + .pm = &isl76683_pm_ops,
> + },
> + .probe = isl76683_probe,
> + .remove = isl76683_remove,
> + .id_table = isl76683_id,
> +};
> +
> +module_i2c_driver(isl76683_driver);
> +
> +MODULE_DESCRIPTION("ISL76683 Ambient Light Sensor driver");
> +MODULE_LICENSE("GPL");
> +MODULE_AUTHOR("Christoph Fritz <chf.fritz@googlemail.com>");
>
--
Peter Meerwald-Stadler
Mobile: +43 664 24 44 418
^ permalink raw reply [flat|nested] 9+ messages in thread
* Re: [PATCH 1/3] iio: light: add support for Intersil isl76683 sensor
2017-11-07 16:28 ` Peter Meerwald-Stadler
@ 2017-11-10 15:55 ` Christoph Fritz
0 siblings, 0 replies; 9+ messages in thread
From: Christoph Fritz @ 2017-11-10 15:55 UTC (permalink / raw)
To: Peter Meerwald-Stadler; +Cc: Jonathan Cameron, linux-iio
Hi Peter,
thanks for your input, please see my comments and questions below.
On Tue, 2017-11-07 at 17:28 +0100, Peter Meerwald-Stadler wrote:
> On Tue, 7 Nov 2017, Christoph Fritz wrote:
>
> > This patch adds support for Intersil isl76683 light sensor.
>
> nitpicking in subject: maybe 'Add support...', start uppercase
> more comments below
OK
>
> > Signed-off-by: Christoph Fritz <chf.fritz@googlemail.com>
> > ---
> > drivers/iio/light/Kconfig | 12 +
> > drivers/iio/light/Makefile | 1 +
> > drivers/iio/light/isl76683.c | 693 +++++++++++++++++++++++++++++++++++++++++++
> > 3 files changed, 706 insertions(+)
> > create mode 100644 drivers/iio/light/isl76683.c
> >
> > diff --git a/drivers/iio/light/Kconfig b/drivers/iio/light/Kconfig
> > index 2356ed9..4f0882c 100644
> > --- a/drivers/iio/light/Kconfig
> > +++ b/drivers/iio/light/Kconfig
> > @@ -194,6 +194,18 @@ config ISL29125
> > To compile this driver as a module, choose M here: the module will be
> > called isl29125.
> >
> > +config ISL76683
> > + tristate "Intersil ISL76683 light sensor"
> > + depends on I2C
> > + select IIO_BUFFER
> > + select IIO_TRIGGERED_BUFFER
> > + help
> > + Say Y here if you want to build a driver for the Intersil ISL76683
> > + light sensor for I2C.
> > +
> > + To compile this driver as a module, choose M here: the module will be
> > + called isl76683.
> > +
> > config HID_SENSOR_ALS
> > depends on HID_SENSOR_HUB
> > select IIO_BUFFER
> > diff --git a/drivers/iio/light/Makefile b/drivers/iio/light/Makefile
> > index fa32fa4..886a51f 100644
> > --- a/drivers/iio/light/Makefile
> > +++ b/drivers/iio/light/Makefile
> > @@ -22,6 +22,7 @@ obj-$(CONFIG_HID_SENSOR_PROX) += hid-sensor-prox.o
> > obj-$(CONFIG_SENSORS_ISL29018) += isl29018.o
> > obj-$(CONFIG_SENSORS_ISL29028) += isl29028.o
> > obj-$(CONFIG_ISL29125) += isl29125.o
> > +obj-$(CONFIG_ISL76683) += isl76683.o
> > obj-$(CONFIG_JSA1212) += jsa1212.o
> > obj-$(CONFIG_SENSORS_LM3533) += lm3533-als.o
> > obj-$(CONFIG_LTR501) += ltr501.o
> > diff --git a/drivers/iio/light/isl76683.c b/drivers/iio/light/isl76683.c
> > new file mode 100644
> > index 0000000..b730276
> > --- /dev/null
> > +++ b/drivers/iio/light/isl76683.c
> > @@ -0,0 +1,693 @@
> > +/*
> > + * IIO driver for the light sensor ISL76683.
> > + *
> > + * Copyright (c) 2017 Christoph Fritz <chf.fritz@googlemail.com>
> > + *
> > + * 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.
> > + *
> > + * Datasheet:
> > + * http://www.intersil.com/content/dam/Intersil/documents/isl7/isl76683.pdf
> > + */
> > +
> > +#include <linux/module.h>
> > +#include <linux/i2c.h>
> > +#include <linux/pm_runtime.h>
> > +#include <linux/regmap.h>
> > +#include <linux/util_macros.h>
> > +#include <linux/iio/iio.h>
> > +#include <linux/iio/sysfs.h>
> > +#include <linux/iio/buffer.h>
> > +#include <linux/iio/trigger.h>
> > +#include <linux/iio/trigger_consumer.h>
> > +#include <linux/iio/triggered_buffer.h>
> > +
> > +#define ISL76683_REG_CMD 0x00
> > +#define ISL76683_REG_CTRL 0x01
> > +#define ISL76683_REG_THR_HI 0x02
> > +#define ISL76683_REG_THR_LO 0x03
> > +#define ISL76683_REG_SENSOR_L 0x04
>
> maybe L -> LSB, M -> MSB to make it more clear
> OTOH SENSOR_M is not really used, so could drop as well and maybe add a
> comment to denote that data is 16-bit little endian
I'll change it to ISL76683_REG_SENSOR_LSB and _MSB
ISL76683_REG_SENSOR_MSB is used in regmap readable and volatile
definition and is read by
regmap_bulk_read(chip->rmp, ISL76683_REG_SENSOR_LSB,
&sensor_raw, sizeof(sensor_raw));
because sizeof(sensor_raw) is 2
>
> > +#define ISL76683_REG_SENSOR_M 0x05
> > +#define ISL76683_REG_CLEAR_INT 0x40
> > +#define ISL76683_REGMAP_MAX 0x40
> > +
> > +#define ISL76683_CMD_ENABLE BIT(7)
> > +#define ISL76683_CMD_PWRDWN BIT(6)
> > +#define ISL76683_WIDTH_MASK 0x3
> > +#define ISL76683_PHOTOD_SHFT 2
> > +#define ISL76683_PHOTOD_MASK (0x3 << ISL76683_PHOTOD_SHFT)
>
> could use GENMASK()
OK
>
> > +#define ISL76683_INTPERS_MASK 0x3
> > +#define ISL76683_LUXRANGE_SHFT 2
> > +#define ISL76683_LUXRANGE_MASK (0x3 << ISL76683_LUXRANGE_SHFT)
> > +#define ISL76683_LUXRANGE_STR "1000 4000 16000 64000"
> > +
> > +enum isl76683_dmode {
> > + ISL76683_DIODE_0 = 0,
> > + ISL76683_DIODE_IR,
> > + ISL76683_DIODE_DIFF,
> > +};
> > +
> > +enum isl76683_lux_range {
> > + ISL76683_LUX_1000 = 0,
> > + ISL76683_LUX_4000,
> > + ISL76683_LUX_16000,
> > + ISL76683_LUX_64000,
> > +};
> > +
> > +static const int isl76683_lux_ranges_available[] = {
> > + 1000, 4000, 16000, 64000};
> > +
> > +#define ISL76683_LUX_RANGE_DEFAULT ISL76683_LUX_1000
> > +#define ISL76683_DIODE_MAX ISL76683_DIODE_DIFF
> > +#define ISL76683_DIODE_DEFAULT ISL76683_DIODE_0
> > +#define ISL76683_WIDTH_DEFAULT 0x0
> > +#define ISL76683_RESOLUTION_DEFAULT 16
> > +#define ISL76683_EXT_RESISTOR_DEFAULT 100
> > +#define ISL76683_KOHM_MIN 1
> > +#define ISL76683_KOHM_MAX 1000
> > +#define ISL76683_INTPERS_DEFAULT 0x0
> > +#define ISL76683_THR_DEFAULT 0x7f
> > +
> > +struct isl76683_chip {
> > + enum isl76683_lux_range luxrange;
> > + int external_resistor;
> > + enum isl76683_dmode photodiode;
> > + struct i2c_client *client;
> > + struct regmap *rmp;
> > + struct completion irq_complete;
> > + struct iio_trigger *trig;
> > + bool trig_enabled;
> > + struct mutex lock;
> > + __le16 *buffer;
>
> isl76683_trigger_handler() stores the already endianness-converted value
> into buffer, so type should be u16?
OK
> why do we need buffer at all?
to keep the buffer data small in either case when
scan_elements/timestamp is used or not, see isl76683_update_scan_mode()
> > + s64 time_ns;
> > + bool buffer_running;
> > +};
> > +
> > +static bool isl76683_readable_reg(struct device *dev, unsigned int reg)
> > +{
> > + switch (reg) {
> > + case ISL76683_REG_CMD:
> > + case ISL76683_REG_CTRL:
> > + case ISL76683_REG_THR_HI:
> > + case ISL76683_REG_THR_LO:
> > + case ISL76683_REG_SENSOR_L:
> > + case ISL76683_REG_SENSOR_M:
> > + case ISL76683_REG_CLEAR_INT:
> > + return true;
> > + default:
> > + return false;
> > + }
> > +}
> > +
> > +static bool isl76683_writeable_reg(struct device *dev, unsigned int reg)
> > +{
> > + switch (reg) {
> > + case ISL76683_REG_CMD:
> > + case ISL76683_REG_CTRL:
> > + case ISL76683_REG_THR_HI:
> > + case ISL76683_REG_THR_LO:
> > + return true;
> > + default:
> > + return false;
> > + }
> > +}
> > +
> > +static bool isl76683_is_volatile_reg(struct device *dev, unsigned int reg)
> > +{
> > + switch (reg) {
> > + case ISL76683_REG_SENSOR_L:
> > + case ISL76683_REG_SENSOR_M:
> > + case ISL76683_REG_CLEAR_INT:
> > + return true;
> > + default:
> > + return false;
> > + }
> > +}
> > +
> > +static const struct regmap_config isl76683_regmap_config = {
> > + .reg_bits = 8,
> > + .val_bits = 8,
> > + .max_register = ISL76683_REGMAP_MAX,
> > + .readable_reg = isl76683_readable_reg,
> > + .writeable_reg = isl76683_writeable_reg,
> > + .volatile_reg = isl76683_is_volatile_reg,
> > + .cache_type = REGCACHE_RBTREE,
> > +};
> > +
> > +static int isl76683_set_config(struct isl76683_chip *chip)
> > +{
> > + int ret;
> > +
> > + ret = regmap_update_bits(chip->rmp, ISL76683_REG_CTRL,
> > + ISL76683_LUXRANGE_MASK | ISL76683_INTPERS_MASK,
> > + (chip->luxrange << ISL76683_LUXRANGE_SHFT) |
> > + ISL76683_INTPERS_DEFAULT);
> > + if (ret < 0)
> > + return ret;
> > +
> > + ret = regmap_update_bits(chip->rmp, ISL76683_REG_CMD,
> > + ISL76683_PHOTOD_MASK | ISL76683_WIDTH_MASK,
> > + (chip->photodiode << ISL76683_PHOTOD_SHFT) |
> > + ISL76683_WIDTH_DEFAULT);
> > + if (ret < 0)
> > + return ret;
> > +
> > + ret = regmap_write(chip->rmp, ISL76683_REG_THR_HI,
> > + ISL76683_THR_DEFAULT);
> > + if (ret < 0)
> > + return ret;
> > +
> > + ret = regmap_write(chip->rmp, ISL76683_REG_THR_LO,
> > + ISL76683_THR_DEFAULT);
>
> could just
> return regmap_write(...);
ok, but then it looks like this because of 80 chars max per line:
return regmap_write(chip->rmp, ISL76683_REG_THR_LO,
chip->threshold_low);
}
>
> > + if (ret < 0)
> > +
> > + return 0;
> > +}
> > +
> > +static int isl76683_power(struct isl76683_chip *chip, bool on)
> > +{
> > + int ret;
> > +
> > + ret = regmap_update_bits(chip->rmp, ISL76683_REG_CMD,
> > + ISL76683_CMD_ENABLE | ISL76683_CMD_PWRDWN,
> > + 0x0);
> > + if (ret < 0)
> > + return ret;
>
> why are these bits first set to 0 and then bits are written with a
> separate command?
> is this a requirement of the chip to clear and set?
yes, that's the "proper" way as the data-sheet (page 9, bullet 2.)
recommends.
>
> > +
> > + ret = regmap_update_bits(chip->rmp, ISL76683_REG_CMD,
> > + ISL76683_CMD_ENABLE | ISL76683_CMD_PWRDWN,
> > + on ? ISL76683_CMD_ENABLE : ISL76683_CMD_PWRDWN);
> > + if (ret < 0)
> > + return ret;
> > +
> > + return on ? isl76683_set_config(chip) : 0;
> > +}
> > +
> > +static int isl76683_reset(struct isl76683_chip *chip)
> > +{
> > + int ret;
> > +
> > + ret = isl76683_power(chip, false);
> > + if (ret < 0)
> > + return ret;
>
> is an explicit delay needed here? just curious...
nope, haven't found any info in the data-sheet which would recommend any
delay and here it works fine for me
>
> > +
> > + return isl76683_power(chip, true);
> > +}
> > +
> > +static int isl76683_read_lux(struct isl76683_chip *chip,
> > + bool is_processed, int *val)
> > +{
> > + unsigned int sensor_data, range, fsr;
> > + __le16 sensor_raw;
> > + int ret;
> > +
> > + ret = regmap_bulk_read(chip->rmp, ISL76683_REG_SENSOR_L,
> > + &sensor_raw, sizeof(sensor_raw));
> > + if (ret)
> > + return ret;
> > +
> > + sensor_data = le16_to_cpu(sensor_raw);
> > +
> > + if (!is_processed) {
>
> in this case sensor_data is probably NOT in lux?
> function name read_lux() is misleading?
is isl76683_get_sensordata() better?
>
> > + *val = sensor_data;
> > + return 0;
> > + }
> > +
> > + /* range values taken from datasheet (table 9) */
> > + if (chip->luxrange == ISL76683_LUX_1000)
> > + range = 973;
> > + else if (chip->luxrange == ISL76683_LUX_4000)
> > + range = 3892;
> > + else if (chip->luxrange == ISL76683_LUX_16000)
> > + range = 15568;
> > + else if (chip->luxrange == ISL76683_LUX_64000)
> > + range = 62272;
> > + else
> > + return -EINVAL;
> > +
> > + /* equations from datasheet (EQ.3 and EQ.4) */
> > + fsr = (100 * range) / chip->external_resistor;
> > + *val = (fsr * sensor_data) / (1 << ISL76683_RESOLUTION_DEFAULT);
> > +
> > + return 0;
> > +}
<snip>
> > +
> > +static irqreturn_t isl76683_trigger_handler(int irq, void *p)
> > +{
> > + struct iio_poll_func *pf = p;
> > + struct iio_dev *indio_dev = pf->indio_dev;
> > + struct isl76683_chip *chip = iio_priv(indio_dev);
> > + __le16 buf;
> > + int ret;
> > +
> > + ret = regmap_bulk_read(chip->rmp, ISL76683_REG_SENSOR_L, &buf, 2);
>
> sizeof(buf)
OK
I used 2 to fit into 80 chars
>
> > + if (ret)
> > + return ret;
> > +
> > + chip->buffer[0] = le16_to_cpu(buf);
> > +
> > + iio_push_to_buffers_with_timestamp(indio_dev, chip->buffer,
> > + chip->time_ns);
> > +
> > + iio_trigger_notify_done(indio_dev->trig);
> > +
> > + ret = isl76683_start_measurement(chip);
> > + if (ret < 0)
> > + return ret;
>
> I think even if this fails IRQ_HANDLED should be returned
OK
>
> > +
> > + return IRQ_HANDLED;
> > +}
> > +
> > +static int isl76683_buffer_preenable(struct iio_dev *indio_dev)
> > +{
> > + struct isl76683_chip *chip = iio_priv(indio_dev);
> > +
> > + mutex_lock(&chip->lock);
> > + chip->buffer_running = true;
> > + mutex_unlock(&chip->lock);
>
> should set_config() be also protected by the mutex?
OK
>
> > + chip->photodiode = indio_dev->channels[0].channel2;
> > + return isl76683_set_config(chip);
> > +}
> > +
> > +static int isl76683_buffer_postdisable(struct iio_dev *indio_dev)
> > +{
> > + struct isl76683_chip *chip = iio_priv(indio_dev);
> > +
> > + chip->buffer_running = false;
> > + return 0;
> > +}
> > +
> > +static const struct iio_buffer_setup_ops isl76683_buffer_setup_ops = {
> > + .preenable = &isl76683_buffer_preenable,
> > + .postdisable = &isl76683_buffer_postdisable,
> > + .predisable = iio_triggered_buffer_predisable,
> > + .postenable = iio_triggered_buffer_postenable,
> > + .validate_scan_mask = &iio_validate_scan_mask_onehot,
> > +};
> > +
> > +static int isl76683_read_raw(struct iio_dev *indio_dev,
> > + struct iio_chan_spec const *chan,
> > + int *val, int *val2, long mask)
> > +{
> > + struct isl76683_chip *chip = iio_priv(indio_dev);
> > + int ret;
> > +
> > + switch (mask) {
> > + case IIO_CHAN_INFO_RAW:
> > + mutex_lock(&chip->lock);
> > + ret = isl76683_singleshot_conversion(chip, chan, false, val);
> > + mutex_unlock(&chip->lock);
> > + return ret;
> > + case IIO_CHAN_INFO_PROCESSED:
> > + mutex_lock(&chip->lock);
> > + ret = isl76683_singleshot_conversion(chip, chan, true, val);
> > + mutex_unlock(&chip->lock);
> > + return ret;
> > + case IIO_CHAN_INFO_HARDWAREGAIN:
>
> use _SCALE?
OK
>
> > + *val = isl76683_lux_ranges_available[chip->luxrange];
> > + return IIO_VAL_INT;
> > + }
> > +
> > + return -EINVAL;
> > +}
> > +
> > +static int isl76683_write_raw(struct iio_dev *indio_dev,
> > + struct iio_chan_spec const *chan,
> > + int val, int val2, long mask)
> > +{
> > + struct isl76683_chip *chip = iio_priv(indio_dev);
> > + int ret;
> > +
> > + switch (mask) {
> > + case IIO_CHAN_INFO_HARDWAREGAIN:
> > + mutex_lock(&chip->lock);
> > + chip->luxrange = find_closest(val,
> > + isl76683_lux_ranges_available,
> > + ARRAY_SIZE(isl76683_lux_ranges_available));
> > + ret = isl76683_set_config(chip);
> > + mutex_unlock(&chip->lock);
> > + return ret;
> > + }
> > +
> > + return -EINVAL;
> > +}
> > +
> > +static IIO_CONST_ATTR(in_illuminance_hardwaregain_available,
> > + ISL76683_LUXRANGE_STR);
> > +
> > +static struct attribute *isl76683_attributes[] = {
> > + &iio_const_attr_in_illuminance_hardwaregain_available.dev_attr.attr,
> > + NULL
> > +};
> > +
> > +static const struct attribute_group isl76683_attribute_group = {
> > + .attrs = isl76683_attributes,
> > +};
> > +
> > +#define ISL76683_CHANNEL_2(_ch2, _si) { \
>
> why _2?
to be more clear about what the purpose here is: "setting channel 2".
But obviously this doesn't work :) ... so I'll change it to
ISL76683_CHANNEL
>
> > + .type = IIO_LIGHT, \
> > + .modified = 1, \
> > + .channel2 = _ch2, \
> > + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \
> > + BIT(IIO_CHAN_INFO_PROCESSED), \
>
> for INFO_RAW the measurement is not in lux, hence IIO_INTENSITY should be
> used? why have _RAW and _PROCESSED?
>
> the computation in the _PROCESSED path should ideally go into the _SCALE
> factor
>
>
> > + .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_HARDWAREGAIN), \
>
> I think this should be _SCALE rather
>
> > + .scan_index = _si, \
> > + .scan_type = { \
> > + .sign = 'u', \
> > + .realbits = 16, \
> > + .storagebits = 16, \
> > + .endianness = IIO_CPU, \
>
> you can/should specify IIO_LE and save the endianness conversion in
> isl76683_trigger_handler()
So buffer should still be __le16* and not u16*?
>
> > + }, \
> > +}
> > +
> > +static const struct iio_chan_spec isl76683_channels[] = {
> > + {
> > + .type = IIO_LIGHT,
> > + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
> > + BIT(IIO_CHAN_INFO_PROCESSED),
> > + .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_HARDWAREGAIN),
> > + .scan_index = 0,
> > + .scan_type = {
> > + .sign = 'u',
> > + .realbits = 16,
> > + .storagebits = 16,
> > + .endianness = IIO_CPU,
> > + },
> > + },
> > + ISL76683_CHANNEL_2(IIO_MOD_LIGHT_IR, 1),
> > + ISL76683_CHANNEL_2(IIO_MOD_LIGHT_BOTH, 2),
> > + IIO_CHAN_SOFT_TIMESTAMP(3),
> > +};
> > +
> > +static int isl76683_update_scan_mode(struct iio_dev *indio_dev,
> > + const unsigned long *scan_mask)
> > +{
> > + struct isl76683_chip *chip = iio_priv(indio_dev);
> > +
> > + kfree(chip->buffer);
> > + chip->buffer = kzalloc(indio_dev->scan_bytes, GFP_KERNEL);
>
> most drivers do static allocation of the buffer, i.e. 2 bytes + 6 bytes
> padding + 8 bytes timestamp
is this a "have to"?
>
> > + if (chip->buffer == NULL)
> > + return -ENOMEM;
> > +
> > + return 0;
> > +}
> > +
> > +static const struct iio_info isl76683_info = {
> > + .update_scan_mode = isl76683_update_scan_mode,
> > + .read_raw = isl76683_read_raw,
> > + .write_raw = isl76683_write_raw,
> > + .attrs = &isl76683_attribute_group,
> > + .driver_module = THIS_MODULE,
>
> THIS_MODULE not needed anymore
OK
>
> > +};
> > +
> > +static int isl76683_set_trigger_state(struct iio_trigger *trig, bool enable)
> > +{
> > + struct isl76683_chip *chip = iio_trigger_get_drvdata(trig);
> > + int ret;
> > +
> > + if (enable) {
> > + chip->trig_enabled = true;
> > + ret = isl76683_start_measurement(chip);
> > + if (ret < 0)
> > + return ret;
> > + } else
> > + chip->trig_enabled = false;
> > +
> > + return 0;
> > +}
> > +
> > +static const struct iio_trigger_ops isl76683_trigger_ops = {
> > + .owner = THIS_MODULE,
> > + .set_trigger_state = isl76683_set_trigger_state,
> > + .validate_device = iio_trigger_validate_own_device,
> > +};
> > +
> > +static int isl76683_probe(struct i2c_client *client,
> > + const struct i2c_device_id *id)
> > +{
> > + struct isl76683_chip *chip;
> > + struct iio_dev *indio_dev;
> > + struct device_node *np = client->dev.of_node;
> > + int rs = ISL76683_EXT_RESISTOR_DEFAULT;
> > + int v, 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;
> > +
> > + if (np) {
> > + ret = of_property_read_u32(np, "isil,external-resistor", &v);
> > + if (ret || v < ISL76683_KOHM_MIN || v > ISL76683_KOHM_MAX)
> > + dev_warn(&client->dev,
> > + "assuming %i kOhm resistor\n", rs);
>
> I'd rather fail if the value is incorrect (matter of taste)
it's only for IIO_CHAN_INFO_PROCESSED, so I like it to let it slide:
Okay for you?
<snip>
^ permalink raw reply [flat|nested] 9+ messages in thread
* Re: [PATCH 2/3] dt-bindings: iio: add Intersil isl76683 light sensor bindings
2017-11-07 15:36 ` [PATCH 2/3] dt-bindings: iio: add Intersil isl76683 light sensor bindings Christoph Fritz
@ 2017-11-10 21:26 ` Rob Herring
2017-11-14 14:20 ` Christoph Fritz
0 siblings, 1 reply; 9+ messages in thread
From: Rob Herring @ 2017-11-10 21:26 UTC (permalink / raw)
To: Christoph Fritz
Cc: Jonathan Cameron, Peter Meerwald-Stadler, linux-iio, devicetree
On Tue, Nov 07, 2017 at 04:36:22PM +0100, Christoph Fritz wrote:
> This patch adds documentation of device tree bindings for Intersil
> isl76683 light sensor.
>
> Signed-off-by: Christoph Fritz <chf.fritz@googlemail.com>
> ---
> .../devicetree/bindings/iio/light/isl76683.txt | 26 ++++++++++++++++++++++
> 1 file changed, 26 insertions(+)
> create mode 100644 Documentation/devicetree/bindings/iio/light/isl76683.txt
>
> diff --git a/Documentation/devicetree/bindings/iio/light/isl76683.txt b/Documentation/devicetree/bindings/iio/light/isl76683.txt
> new file mode 100644
> index 0000000..657f17d
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/iio/light/isl76683.txt
> @@ -0,0 +1,26 @@
> +* ISL76683 ambient light sensor
> +
> +http://www.intersil.com/content/dam/Intersil/documents/isl7/isl76683.pdf
> +
> +Required properties:
> +
> + - compatible: must be "isil,isl76683"
> + - reg: the I2C address of the sensor
> + - interrupt-parent: should be the phandle for the interrupt controller
> + - interrupts: interrupt mapping for GPIO IRQ
> +
> +Optional properties:
> +
> + - isil,external-resistor: integer in kOhm of external resistor R_ext.
> + Valid values are from 1 to 1000.
> + If not supplied, 100 kOhm will be assumed.
Add an unit suffix as defined in property-units.txt.
> +
> +Example:
> +
> +isl76683@44 {
> + compatible = "isil,isl76683";
> + reg = <0x44>;
> + interrupt-parent = <&gpio1>;
> + interrupts = <20 IRQ_TYPE_LEVEL_LOW>;
> + isil,external-resistor = <50>;
> +};
> --
> 2.1.4
>
^ permalink raw reply [flat|nested] 9+ messages in thread
* Re: [PATCH 1/3] iio: light: add support for Intersil isl76683 sensor
2017-11-07 15:36 ` [PATCH 1/3] iio: light: add support for Intersil isl76683 sensor Christoph Fritz
2017-11-07 16:28 ` Peter Meerwald-Stadler
@ 2017-11-11 0:50 ` Jonathan Cameron
1 sibling, 0 replies; 9+ messages in thread
From: Jonathan Cameron @ 2017-11-11 0:50 UTC (permalink / raw)
To: Christoph Fritz
Cc: Jonathan Cameron, Peter Meerwald-Stadler, Rob Herring, linux-iio,
devicetree
On Tue, 7 Nov 2017 16:36:21 +0100
Christoph Fritz <chf.fritz@googlemail.com> wrote:
> This patch adds support for Intersil isl76683 light sensor.
>
> Signed-off-by: Christoph Fritz <chf.fritz@googlemail.com>
Sorry, bit of a rushed review as boarding about to start for a transatlantic flight...
Anyhow, a few comments inline in addition to Peter's. I'll take a proper look at V2.
Jonathan
> ---
> drivers/iio/light/Kconfig | 12 +
> drivers/iio/light/Makefile | 1 +
> drivers/iio/light/isl76683.c | 693 +++++++++++++++++++++++++++++++++++++++++++
> 3 files changed, 706 insertions(+)
> create mode 100644 drivers/iio/light/isl76683.c
>
> diff --git a/drivers/iio/light/Kconfig b/drivers/iio/light/Kconfig
> index 2356ed9..4f0882c 100644
> --- a/drivers/iio/light/Kconfig
> +++ b/drivers/iio/light/Kconfig
> @@ -194,6 +194,18 @@ config ISL29125
> To compile this driver as a module, choose M here: the module will be
> called isl29125.
>
> +config ISL76683
> + tristate "Intersil ISL76683 light sensor"
> + depends on I2C
> + select IIO_BUFFER
> + select IIO_TRIGGERED_BUFFER
> + help
> + Say Y here if you want to build a driver for the Intersil ISL76683
> + light sensor for I2C.
> +
> + To compile this driver as a module, choose M here: the module will be
> + called isl76683.
> +
> config HID_SENSOR_ALS
> depends on HID_SENSOR_HUB
> select IIO_BUFFER
> diff --git a/drivers/iio/light/Makefile b/drivers/iio/light/Makefile
> index fa32fa4..886a51f 100644
> --- a/drivers/iio/light/Makefile
> +++ b/drivers/iio/light/Makefile
> @@ -22,6 +22,7 @@ obj-$(CONFIG_HID_SENSOR_PROX) += hid-sensor-prox.o
> obj-$(CONFIG_SENSORS_ISL29018) += isl29018.o
> obj-$(CONFIG_SENSORS_ISL29028) += isl29028.o
> obj-$(CONFIG_ISL29125) += isl29125.o
> +obj-$(CONFIG_ISL76683) += isl76683.o
> obj-$(CONFIG_JSA1212) += jsa1212.o
> obj-$(CONFIG_SENSORS_LM3533) += lm3533-als.o
> obj-$(CONFIG_LTR501) += ltr501.o
> diff --git a/drivers/iio/light/isl76683.c b/drivers/iio/light/isl76683.c
> new file mode 100644
> index 0000000..b730276
> --- /dev/null
> +++ b/drivers/iio/light/isl76683.c
> @@ -0,0 +1,693 @@
> +/*
> + * IIO driver for the light sensor ISL76683.
> + *
> + * Copyright (c) 2017 Christoph Fritz <chf.fritz@googlemail.com>
> + *
> + * 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.
> + *
> + * Datasheet:
> + * http://www.intersil.com/content/dam/Intersil/documents/isl7/isl76683.pdf
> + */
> +
> +#include <linux/module.h>
> +#include <linux/i2c.h>
> +#include <linux/pm_runtime.h>
> +#include <linux/regmap.h>
> +#include <linux/util_macros.h>
> +#include <linux/iio/iio.h>
> +#include <linux/iio/sysfs.h>
> +#include <linux/iio/buffer.h>
> +#include <linux/iio/trigger.h>
> +#include <linux/iio/trigger_consumer.h>
> +#include <linux/iio/triggered_buffer.h>
> +
> +#define ISL76683_REG_CMD 0x00
> +#define ISL76683_REG_CTRL 0x01
> +#define ISL76683_REG_THR_HI 0x02
> +#define ISL76683_REG_THR_LO 0x03
> +#define ISL76683_REG_SENSOR_L 0x04
> +#define ISL76683_REG_SENSOR_M 0x05
> +#define ISL76683_REG_CLEAR_INT 0x40
> +#define ISL76683_REGMAP_MAX 0x40
> +
> +#define ISL76683_CMD_ENABLE BIT(7)
> +#define ISL76683_CMD_PWRDWN BIT(6)
> +#define ISL76683_WIDTH_MASK 0x3
> +#define ISL76683_PHOTOD_SHFT 2
> +#define ISL76683_PHOTOD_MASK (0x3 << ISL76683_PHOTOD_SHFT)
> +#define ISL76683_INTPERS_MASK 0x3
> +#define ISL76683_LUXRANGE_SHFT 2
> +#define ISL76683_LUXRANGE_MASK (0x3 << ISL76683_LUXRANGE_SHFT)
> +#define ISL76683_LUXRANGE_STR "1000 4000 16000 64000"
> +
> +enum isl76683_dmode {
> + ISL76683_DIODE_0 = 0,
> + ISL76683_DIODE_IR,
> + ISL76683_DIODE_DIFF,
> +};
> +
> +enum isl76683_lux_range {
> + ISL76683_LUX_1000 = 0,
> + ISL76683_LUX_4000,
> + ISL76683_LUX_16000,
> + ISL76683_LUX_64000,
> +};
> +
> +static const int isl76683_lux_ranges_available[] = {
> + 1000, 4000, 16000, 64000};
> +
> +#define ISL76683_LUX_RANGE_DEFAULT ISL76683_LUX_1000
> +#define ISL76683_DIODE_MAX ISL76683_DIODE_DIFF
> +#define ISL76683_DIODE_DEFAULT ISL76683_DIODE_0
> +#define ISL76683_WIDTH_DEFAULT 0x0
> +#define ISL76683_RESOLUTION_DEFAULT 16
> +#define ISL76683_EXT_RESISTOR_DEFAULT 100
> +#define ISL76683_KOHM_MIN 1
> +#define ISL76683_KOHM_MAX 1000
> +#define ISL76683_INTPERS_DEFAULT 0x0
> +#define ISL76683_THR_DEFAULT 0x7f
> +
> +struct isl76683_chip {
> + enum isl76683_lux_range luxrange;
> + int external_resistor;
> + enum isl76683_dmode photodiode;
> + struct i2c_client *client;
> + struct regmap *rmp;
> + struct completion irq_complete;
> + struct iio_trigger *trig;
> + bool trig_enabled;
> + struct mutex lock;
> + __le16 *buffer;
> + s64 time_ns;
> + bool buffer_running;
> +};
> +
> +static bool isl76683_readable_reg(struct device *dev, unsigned int reg)
> +{
> + switch (reg) {
> + case ISL76683_REG_CMD:
> + case ISL76683_REG_CTRL:
> + case ISL76683_REG_THR_HI:
> + case ISL76683_REG_THR_LO:
> + case ISL76683_REG_SENSOR_L:
> + case ISL76683_REG_SENSOR_M:
> + case ISL76683_REG_CLEAR_INT:
> + return true;
> + default:
> + return false;
> + }
> +}
> +
> +static bool isl76683_writeable_reg(struct device *dev, unsigned int reg)
> +{
> + switch (reg) {
> + case ISL76683_REG_CMD:
> + case ISL76683_REG_CTRL:
> + case ISL76683_REG_THR_HI:
> + case ISL76683_REG_THR_LO:
> + return true;
> + default:
> + return false;
> + }
> +}
> +
> +static bool isl76683_is_volatile_reg(struct device *dev, unsigned int reg)
> +{
> + switch (reg) {
> + case ISL76683_REG_SENSOR_L:
> + case ISL76683_REG_SENSOR_M:
> + case ISL76683_REG_CLEAR_INT:
> + return true;
> + default:
> + return false;
> + }
> +}
> +
> +static const struct regmap_config isl76683_regmap_config = {
> + .reg_bits = 8,
> + .val_bits = 8,
> + .max_register = ISL76683_REGMAP_MAX,
> + .readable_reg = isl76683_readable_reg,
> + .writeable_reg = isl76683_writeable_reg,
> + .volatile_reg = isl76683_is_volatile_reg,
> + .cache_type = REGCACHE_RBTREE,
> +};
> +
> +static int isl76683_set_config(struct isl76683_chip *chip)
> +{
> + int ret;
> +
> + ret = regmap_update_bits(chip->rmp, ISL76683_REG_CTRL,
> + ISL76683_LUXRANGE_MASK | ISL76683_INTPERS_MASK,
> + (chip->luxrange << ISL76683_LUXRANGE_SHFT) |
> + ISL76683_INTPERS_DEFAULT);
> + if (ret < 0)
> + return ret;
> +
> + ret = regmap_update_bits(chip->rmp, ISL76683_REG_CMD,
> + ISL76683_PHOTOD_MASK | ISL76683_WIDTH_MASK,
> + (chip->photodiode << ISL76683_PHOTOD_SHFT) |
> + ISL76683_WIDTH_DEFAULT);
> + if (ret < 0)
> + return ret;
> +
> + ret = regmap_write(chip->rmp, ISL76683_REG_THR_HI,
> + ISL76683_THR_DEFAULT);
> + if (ret < 0)
> + return ret;
> +
> + ret = regmap_write(chip->rmp, ISL76683_REG_THR_LO,
> + ISL76683_THR_DEFAULT);
> + if (ret < 0)
> + return ret;
> +
> + return 0;
> +}
> +
> +static int isl76683_power(struct isl76683_chip *chip, bool on)
> +{
> + int ret;
> +
> + ret = regmap_update_bits(chip->rmp, ISL76683_REG_CMD,
> + ISL76683_CMD_ENABLE | ISL76683_CMD_PWRDWN,
> + 0x0);
> + if (ret < 0)
> + return ret;
> +
> + ret = regmap_update_bits(chip->rmp, ISL76683_REG_CMD,
> + ISL76683_CMD_ENABLE | ISL76683_CMD_PWRDWN,
> + on ? ISL76683_CMD_ENABLE : ISL76683_CMD_PWRDWN);
> + if (ret < 0)
> + return ret;
> +
> + return on ? isl76683_set_config(chip) : 0;
> +}
> +
> +static int isl76683_reset(struct isl76683_chip *chip)
> +{
> + int ret;
> +
> + ret = isl76683_power(chip, false);
> + if (ret < 0)
> + return ret;
> +
> + return isl76683_power(chip, true);
> +}
> +
> +static int isl76683_read_lux(struct isl76683_chip *chip,
> + bool is_processed, int *val)
> +{
> + unsigned int sensor_data, range, fsr;
> + __le16 sensor_raw;
> + int ret;
> +
> + ret = regmap_bulk_read(chip->rmp, ISL76683_REG_SENSOR_L,
> + &sensor_raw, sizeof(sensor_raw));
> + if (ret)
> + return ret;
> +
> + sensor_data = le16_to_cpu(sensor_raw);
> +
> + if (!is_processed) {
> + *val = sensor_data;
> + return 0;
> + }
> +
> + /* range values taken from datasheet (table 9) */
> + if (chip->luxrange == ISL76683_LUX_1000)
> + range = 973;
> + else if (chip->luxrange == ISL76683_LUX_4000)
> + range = 3892;
> + else if (chip->luxrange == ISL76683_LUX_16000)
> + range = 15568;
> + else if (chip->luxrange == ISL76683_LUX_64000)
> + range = 62272;
> + else
> + return -EINVAL;
> +
> + /* equations from datasheet (EQ.3 and EQ.4) */
> + fsr = (100 * range) / chip->external_resistor;
> + *val = (fsr * sensor_data) / (1 << ISL76683_RESOLUTION_DEFAULT);
> +
> + return 0;
> +}
> +
> +static irqreturn_t isl76683_interrupt_handler(int irq, void *private)
> +{
> + struct iio_dev *indio_dev = private;
> + struct isl76683_chip *chip = iio_priv(indio_dev);
> +
> + chip->time_ns = iio_get_time_ns(indio_dev);
> +
> + if (chip->trig_enabled)
> + iio_trigger_poll(chip->trig);
> +
> + if (!completion_done(&chip->irq_complete))
> + complete(&chip->irq_complete);
> +
> + return IRQ_HANDLED;
> +}
> +
> +static inline int isl76683_start_measurement(struct isl76683_chip *chip)
> +{
> + int dummy;
> +
> + /* dummy read is clearing irq and triggers new measurement */
> + return regmap_read(chip->rmp, ISL76683_REG_CLEAR_INT, &dummy);
> +}
> +
> +static int isl76683_singleshot_conversion(struct isl76683_chip *chip,
> + const struct iio_chan_spec *chan,
> + bool is_processed, int *val)
> +{
> + long timeout;
> + int ret;
> +
> + if (chip->buffer_running)
Don't reinvent the wheel. (this is racy btw).
iio_claim_direct_mode etc will do this in a non racy fashion for you.
> + return -EAGAIN;
> +
> + switch (chan->channel2) {
> + case IIO_MOD_LIGHT_BOTH:
> + chip->photodiode = ISL76683_DIODE_DIFF;
> + break;
> + case IIO_MOD_LIGHT_IR:
> + chip->photodiode = ISL76683_DIODE_IR;
> + break;
> + default:
> + chip->photodiode = ISL76683_DIODE_0;
> + }
> +
> + ret = isl76683_set_config(chip);
> + if (ret)
> + return ret;
> +
> + ret = isl76683_start_measurement(chip);
> + if (ret < 0)
> + return ret;
> +
> + /* wait for measurement to complete */
> + timeout = wait_for_completion_interruptible_timeout(
> + &chip->irq_complete,
> + msecs_to_jiffies(5000));
> + if (timeout == 0) {
> + dev_err(&chip->client->dev, "measurement timed out\n");
> + return -ETIMEDOUT;
> + } else if (timeout < 0) {
> + dev_err(&chip->client->dev, "wait for measurement failed\n");
> + return -EINTR;
> + }
> +
> + ret = isl76683_read_lux(chip, is_processed, val);
> + if (ret) {
> + dev_err(&chip->client->dev, "%s: Error %d reading lux\n",
> + __func__, ret);
> + return ret;
> + }
> +
> + return IIO_VAL_INT;
> +}
> +
> +static irqreturn_t isl76683_trigger_handler(int irq, void *p)
> +{
> + struct iio_poll_func *pf = p;
> + struct iio_dev *indio_dev = pf->indio_dev;
> + struct isl76683_chip *chip = iio_priv(indio_dev);
> + __le16 buf;
> + int ret;
> +
> + ret = regmap_bulk_read(chip->rmp, ISL76683_REG_SENSOR_L, &buf, 2);
> + if (ret)
> + return ret;
> +
> + chip->buffer[0] = le16_to_cpu(buf);
> +
> + iio_push_to_buffers_with_timestamp(indio_dev, chip->buffer,
> + chip->time_ns);
> +
> + iio_trigger_notify_done(indio_dev->trig);
> +
> + ret = isl76683_start_measurement(chip);
> + if (ret < 0)
> + return ret;
> +
> + return IRQ_HANDLED;
> +}
> +
> +static int isl76683_buffer_preenable(struct iio_dev *indio_dev)
> +{
> + struct isl76683_chip *chip = iio_priv(indio_dev);
> +
> + mutex_lock(&chip->lock);
> + chip->buffer_running = true;
> + mutex_unlock(&chip->lock);
> + chip->photodiode = indio_dev->channels[0].channel2;
> + return isl76683_set_config(chip);
> +}
> +
> +static int isl76683_buffer_postdisable(struct iio_dev *indio_dev)
> +{
> + struct isl76683_chip *chip = iio_priv(indio_dev);
> +
> + chip->buffer_running = false;
> + return 0;
> +}
> +
> +static const struct iio_buffer_setup_ops isl76683_buffer_setup_ops = {
> + .preenable = &isl76683_buffer_preenable,
> + .postdisable = &isl76683_buffer_postdisable,
> + .predisable = iio_triggered_buffer_predisable,
> + .postenable = iio_triggered_buffer_postenable,
> + .validate_scan_mask = &iio_validate_scan_mask_onehot,
> +};
> +
> +static int isl76683_read_raw(struct iio_dev *indio_dev,
> + struct iio_chan_spec const *chan,
> + int *val, int *val2, long mask)
> +{
> + struct isl76683_chip *chip = iio_priv(indio_dev);
> + int ret;
> +
> + switch (mask) {
> + case IIO_CHAN_INFO_RAW:
> + mutex_lock(&chip->lock);
> + ret = isl76683_singleshot_conversion(chip, chan, false, val);
> + mutex_unlock(&chip->lock);
> + return ret;
> + case IIO_CHAN_INFO_PROCESSED:
> + mutex_lock(&chip->lock);
> + ret = isl76683_singleshot_conversion(chip, chan, true, val);
> + mutex_unlock(&chip->lock);
> + return ret;
> + case IIO_CHAN_INFO_HARDWAREGAIN:
> + *val = isl76683_lux_ranges_available[chip->luxrange];
> + return IIO_VAL_INT;
> + }
> +
> + return -EINVAL;
> +}
> +
> +static int isl76683_write_raw(struct iio_dev *indio_dev,
> + struct iio_chan_spec const *chan,
> + int val, int val2, long mask)
> +{
> + struct isl76683_chip *chip = iio_priv(indio_dev);
> + int ret;
> +
> + switch (mask) {
> + case IIO_CHAN_INFO_HARDWAREGAIN:
> + mutex_lock(&chip->lock);
> + chip->luxrange = find_closest(val,
> + isl76683_lux_ranges_available,
> + ARRAY_SIZE(isl76683_lux_ranges_available));
> + ret = isl76683_set_config(chip);
> + mutex_unlock(&chip->lock);
> + return ret;
> + }
> +
> + return -EINVAL;
> +}
> +
> +static IIO_CONST_ATTR(in_illuminance_hardwaregain_available,
> + ISL76683_LUXRANGE_STR);
> +
> +static struct attribute *isl76683_attributes[] = {
> + &iio_const_attr_in_illuminance_hardwaregain_available.dev_attr.attr,
> + NULL
> +};
> +
> +static const struct attribute_group isl76683_attribute_group = {
> + .attrs = isl76683_attributes,
> +};
> +
> +#define ISL76683_CHANNEL_2(_ch2, _si) { \
> + .type = IIO_LIGHT, \
> + .modified = 1, \
> + .channel2 = _ch2, \
> + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \
> + BIT(IIO_CHAN_INFO_PROCESSED), \
> + .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_HARDWAREGAIN), \
> + .scan_index = _si, \
> + .scan_type = { \
> + .sign = 'u', \
> + .realbits = 16, \
> + .storagebits = 16, \
> + .endianness = IIO_CPU, \
> + }, \
> +}
> +
> +static const struct iio_chan_spec isl76683_channels[] = {
> + {
> + .type = IIO_LIGHT,
> + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
> + BIT(IIO_CHAN_INFO_PROCESSED),
> + .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_HARDWAREGAIN),
> + .scan_index = 0,
> + .scan_type = {
> + .sign = 'u',
> + .realbits = 16,
> + .storagebits = 16,
> + .endianness = IIO_CPU,
> + },
> + },
> + ISL76683_CHANNEL_2(IIO_MOD_LIGHT_IR, 1),
> + ISL76683_CHANNEL_2(IIO_MOD_LIGHT_BOTH, 2),
> + IIO_CHAN_SOFT_TIMESTAMP(3),
> +};
> +
> +static int isl76683_update_scan_mode(struct iio_dev *indio_dev,
> + const unsigned long *scan_mask)
> +{
> + struct isl76683_chip *chip = iio_priv(indio_dev);
> +
> + kfree(chip->buffer);
> + chip->buffer = kzalloc(indio_dev->scan_bytes, GFP_KERNEL);
> + if (chip->buffer == NULL)
> + return -ENOMEM;
I would be lazy and just make the buffer big enough for all cases.
You will want to play the __cachline_aligned game to make it
dma safe at the end of the chip structure.
> +
> + return 0;
> +}
> +
> +static const struct iio_info isl76683_info = {
> + .update_scan_mode = isl76683_update_scan_mode,
> + .read_raw = isl76683_read_raw,
> + .write_raw = isl76683_write_raw,
> + .attrs = &isl76683_attribute_group,
> + .driver_module = THIS_MODULE,
This will not work with latest IIO tree (or linux-next)
Just drop setting this field as it no longer exists.
> +};
> +
> +static int isl76683_set_trigger_state(struct iio_trigger *trig, bool enable)
> +{
> + struct isl76683_chip *chip = iio_trigger_get_drvdata(trig);
> + int ret;
> +
> + if (enable) {
> + chip->trig_enabled = true;
This is unusual enough that I'd like a comment here on how you would end
up in a state where this is necessary.
> + ret = isl76683_start_measurement(chip);
> + if (ret < 0)
> + return ret;
> + } else
> + chip->trig_enabled = false;
> +
> + return 0;
> +}
> +
> +static const struct iio_trigger_ops isl76683_trigger_ops = {
> + .owner = THIS_MODULE,
> + .set_trigger_state = isl76683_set_trigger_state,
> + .validate_device = iio_trigger_validate_own_device,
> +};
> +
> +static int isl76683_probe(struct i2c_client *client,
> + const struct i2c_device_id *id)
> +{
> + struct isl76683_chip *chip;
> + struct iio_dev *indio_dev;
> + struct device_node *np = client->dev.of_node;
> + int rs = ISL76683_EXT_RESISTOR_DEFAULT;
> + int v, 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;
> +
> + if (np) {
> + ret = of_property_read_u32(np, "isil,external-resistor", &v);
> + if (ret || v < ISL76683_KOHM_MIN || v > ISL76683_KOHM_MAX)
> + dev_warn(&client->dev,
> + "assuming %i kOhm resistor\n", rs);
> + else
> + rs = v;
> + }
> +
> + chip->luxrange = ISL76683_LUX_RANGE_DEFAULT;
> + chip->external_resistor = rs;
> + chip->photodiode = ISL76683_DIODE_DEFAULT;
> + chip->buffer_running = false;
> +
> + chip->rmp = devm_regmap_init_i2c(client, &isl76683_regmap_config);
> + if (IS_ERR(chip->rmp)) {
> + ret = PTR_ERR(chip->rmp);
> + dev_err(&client->dev, "%s: Error %d initializing regmap\n",
> + __func__, ret);
> + return ret;
> + }
> +
> + mutex_init(&chip->lock);
> +
> + init_completion(&chip->irq_complete);
> +
> + if (!client->irq) {
> + dev_err(&client->dev, "no interrupt configured\n");
> + return -EINVAL;
> + }
> +
> + indio_dev->dev.parent = &client->dev;
> + indio_dev->info = &isl76683_info;
> + indio_dev->channels = isl76683_channels;
> + indio_dev->num_channels = ARRAY_SIZE(isl76683_channels);
> + indio_dev->name = id->name;
> + indio_dev->modes = INDIO_DIRECT_MODE;
> +
> + chip->trig_enabled = false;
> + chip->trig = devm_iio_trigger_alloc(&client->dev, "%s-dev%d",
> + indio_dev->name, indio_dev->id);
> + if (!chip->trig)
> + return -ENOMEM;
> +
> + chip->trig->ops = &isl76683_trigger_ops;
> + chip->trig->dev.parent = &client->dev;
> + iio_trigger_set_drvdata(chip->trig, chip);
> +
> + ret = devm_request_irq(&client->dev, client->irq,
> + isl76683_interrupt_handler,
> + IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
> + "isl76683_event", indio_dev);
> + if (ret) {
> + dev_err(&client->dev, "irq request error\n");
> + return ret;
> + }
> +
> + ret = devm_iio_trigger_register(&client->dev, chip->trig);
> + if (ret) {
> + dev_err(&client->dev, "iio_trigger register error\n");
> + return ret;
> + }
> +
> + ret = isl76683_reset(chip);
> + if (ret) {
> + dev_err(&client->dev, "reset failed\n");
> + return ret;
> + }
> +
> + ret = isl76683_read_lux(chip, false, &v);
> + if (ret) {
> + dev_err(&client->dev, "initial dummy readout failed\n");
> + return ret;
> + }
> +
> + ret = devm_iio_triggered_buffer_setup(&client->dev, indio_dev, NULL,
> + &isl76683_trigger_handler, &isl76683_buffer_setup_ops);
> + if (ret)
> + return ret;
> +
> + ret = devm_iio_device_register(&client->dev, indio_dev);
> + if (ret) {
> + dev_err(&client->dev,
> + "%s(): iio registration failed with error %d\n",
> + __func__, ret);
> + return ret;
Drop this return ret.
> + }
> +
> + return ret;
> +}
> +
> +static int isl76683_remove(struct i2c_client *client)
> +{
> + struct iio_dev *indio_dev = i2c_get_clientdata(client);
> + struct isl76683_chip *chip = iio_priv(indio_dev);
> +
> + pm_runtime_disable(&client->dev);
> + pm_runtime_set_suspended(&client->dev);
> + isl76683_power(chip, false);
> + kfree(chip->buffer);
> +
> + return 0;
> +}
> +
> +#if defined(CONFIG_PM_SLEEP) || defined(CONFIG_PM)
> +static int isl76683_runtime_suspend(struct device *dev)
> +{
> + struct isl76683_chip *chip =
> + iio_priv(i2c_get_clientdata(to_i2c_client(dev)));
> + int ret;
> +
> + mutex_lock(&chip->lock);
> + ret = isl76683_power(chip, false);
> + regcache_mark_dirty(chip->rmp);
> + mutex_unlock(&chip->lock);
> +
> + return ret;
> +}
> +
> +static int isl76683_runtime_resume(struct device *dev)
> +{
> + struct isl76683_chip *chip =
> + iio_priv(i2c_get_clientdata(to_i2c_client(dev)));
> + int ret;
> +
> + mutex_lock(&chip->lock);
> + ret = isl76683_power(chip, true);
> + mutex_unlock(&chip->lock);
> +
> + return ret;
> +}
> +#endif
> +
> +static const struct dev_pm_ops isl76683_pm_ops = {
> + SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend,
> + pm_runtime_force_resume)
> + SET_RUNTIME_PM_OPS(isl76683_runtime_suspend,
> + isl76683_runtime_resume, NULL)
> +};
> +
> +static const struct i2c_device_id isl76683_id[] = {
> + {"isl76683", 0},
> + {}
> +};
> +MODULE_DEVICE_TABLE(i2c, isl76683_id);
> +
> +static const struct of_device_id isl76683_of_match[] = {
> + { .compatible = "isil,isl76683", },
> + { },
> +};
> +MODULE_DEVICE_TABLE(of, isl76683_of_match);
> +
> +static struct i2c_driver isl76683_driver = {
> + .driver = {
> + .name = "isl76683",
> + .of_match_table = isl76683_of_match,
> + .pm = &isl76683_pm_ops,
> + },
> + .probe = isl76683_probe,
> + .remove = isl76683_remove,
> + .id_table = isl76683_id,
> +};
> +
> +module_i2c_driver(isl76683_driver);
> +
> +MODULE_DESCRIPTION("ISL76683 Ambient Light Sensor driver");
> +MODULE_LICENSE("GPL");
> +MODULE_AUTHOR("Christoph Fritz <chf.fritz@googlemail.com>");
^ permalink raw reply [flat|nested] 9+ messages in thread
* Re: [PATCH 2/3] dt-bindings: iio: add Intersil isl76683 light sensor bindings
2017-11-10 21:26 ` Rob Herring
@ 2017-11-14 14:20 ` Christoph Fritz
0 siblings, 0 replies; 9+ messages in thread
From: Christoph Fritz @ 2017-11-14 14:20 UTC (permalink / raw)
To: Rob Herring
Cc: Jonathan Cameron, Peter Meerwald-Stadler, linux-iio, devicetree
Hi Rob
On Fri, 2017-11-10 at 15:26 -0600, Rob Herring wrote:
> On Tue, Nov 07, 2017 at 04:36:22PM +0100, Christoph Fritz wrote:
> > +Optional properties:
> > +
> > + - isil,external-resistor: integer in kOhm of external resistor R_ext.
> > + Valid values are from 1 to 1000.
> > + If not supplied, 100 kOhm will be assumed.
>
> Add an unit suffix as defined in property-units.txt.
Thanks, I'll use "isil,external-resistor-ohms" as defined in
property-units.txt.
What bothers me a bit is that there is no "-kilo-ohms" suffix but a
"-micro-ohms" which is pretty uncommon. Even the only use of suffix
"-micro-ohms" could have been expressed easily as "-ohms" while being
more readable:
qcom,external-resistor-micro-ohms = <10000>;
Due to stable dt-api I suppose this cannot be changed and/or extended
with "-kilo-ohms", right?
Thanks
-- Christoph
^ permalink raw reply [flat|nested] 9+ messages in thread
end of thread, other threads:[~2017-11-14 14:20 UTC | newest]
Thread overview: 9+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2017-11-07 15:36 [PATCH 0/3] iio: add Intersil isl76683 light sensor support Christoph Fritz
2017-11-07 15:36 ` [PATCH 1/3] iio: light: add support for Intersil isl76683 sensor Christoph Fritz
2017-11-07 16:28 ` Peter Meerwald-Stadler
2017-11-10 15:55 ` Christoph Fritz
2017-11-11 0:50 ` Jonathan Cameron
2017-11-07 15:36 ` [PATCH 2/3] dt-bindings: iio: add Intersil isl76683 light sensor bindings Christoph Fritz
2017-11-10 21:26 ` Rob Herring
2017-11-14 14:20 ` Christoph Fritz
2017-11-07 15:36 ` [PATCH 3/3] iio: light: isl76683 add way to adjust irq threshold Christoph Fritz
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).