* [PATCH v2 0/2] ZOPT2210 ambient light and UVB sensor driver
@ 2017-11-05 23:24 Peter Meerwald-Stadler
2017-11-05 23:24 ` [PATCH v2 1/2] iio: light: Add driver for IDT ZOPT2201 ambient light and UVB sensor Peter Meerwald-Stadler
2017-11-05 23:25 ` [PATCH v2 2/2] Documentation: iio: Fix typo Peter Meerwald-Stadler
0 siblings, 2 replies; 6+ messages in thread
From: Peter Meerwald-Stadler @ 2017-11-05 23:24 UTC (permalink / raw)
To: Jonathan Cameron; +Cc: linux-iio
Driver for 20-bit ALS and UV B sensor with I2C interface exposing
the following API:
in_uvindex_input
in_illuminance_raw
in_illuminance_scale
in_illuminance_scale_available
in_intensity_uv_raw
in_intensity_uv_scale
in_intensity_uv_scale_available
integration_time
integration_time_available
v2: (thanks Jonathan)
* remove duplicate new lines as suggested
* split write functions as suggested
* reformatting error path as suggested
* drop _mode enum and use register address directly instead
* the intensity_uv channel now reports in SI units: W/m^2 (as suggested),
the illuminance channel reports in lux (after application of _SCALE)
* add unrelated patch to fix a typo in documentation
Peter Meerwald-Stadler (2):
iio: light: Add driver for IDT ZOPT2201 ambient light and UVB sensor
Documentation: iio: Fix typo
Documentation/ABI/testing/sysfs-bus-iio | 2 +-
drivers/iio/light/Kconfig | 10 +
drivers/iio/light/Makefile | 1 +
drivers/iio/light/zopt2201.c | 568 ++++++++++++++++++++++++++++++++
4 files changed, 580 insertions(+), 1 deletion(-)
create mode 100644 drivers/iio/light/zopt2201.c
--
2.7.4
^ permalink raw reply [flat|nested] 6+ messages in thread
* [PATCH v2 1/2] iio: light: Add driver for IDT ZOPT2201 ambient light and UVB sensor
2017-11-05 23:24 [PATCH v2 0/2] ZOPT2210 ambient light and UVB sensor driver Peter Meerwald-Stadler
@ 2017-11-05 23:24 ` Peter Meerwald-Stadler
2017-11-11 0:26 ` Jonathan Cameron
2017-11-05 23:25 ` [PATCH v2 2/2] Documentation: iio: Fix typo Peter Meerwald-Stadler
1 sibling, 1 reply; 6+ messages in thread
From: Peter Meerwald-Stadler @ 2017-11-05 23:24 UTC (permalink / raw)
To: Jonathan Cameron; +Cc: linux-iio, Peter Meerwald-Stadler
Driver for 20-bit ALS and UV B sensor with I2C interface exposing
the following API:
in_uvindex_input
in_illuminance_raw
in_illuminance_scale
in_illuminance_scale_available
in_intensity_uv_raw
in_intensity_uv_scale
in_intensity_uv_scale_available
integration_time
integration_time_available
Signed-off-by: Peter Meerwald-Stadler <pmeerw@pmeerw.net>
---
drivers/iio/light/Kconfig | 10 +
drivers/iio/light/Makefile | 1 +
drivers/iio/light/zopt2201.c | 568 +++++++++++++++++++++++++++++++++++++++++++
3 files changed, 579 insertions(+)
create mode 100644 drivers/iio/light/zopt2201.c
diff --git a/drivers/iio/light/Kconfig b/drivers/iio/light/Kconfig
index 2356ed9..6a5835f 100644
--- a/drivers/iio/light/Kconfig
+++ b/drivers/iio/light/Kconfig
@@ -425,4 +425,14 @@ config VL6180
To compile this driver as a module, choose M here: the
module will be called vl6180.
+config ZOPT2201
+ tristate "ZOPT2201 ALS and UV B sensor"
+ depends on I2C
+ help
+ Say Y here if you want to build a driver for the IDT
+ ZOPT2201 ambient light and UV B sensor.
+
+ To compile this driver as a module, choose M here: the
+ module will be called zopt2201.
+
endmenu
diff --git a/drivers/iio/light/Makefile b/drivers/iio/light/Makefile
index fa32fa4..d99abdf 100644
--- a/drivers/iio/light/Makefile
+++ b/drivers/iio/light/Makefile
@@ -40,3 +40,4 @@ obj-$(CONFIG_US5182D) += us5182d.o
obj-$(CONFIG_VCNL4000) += vcnl4000.o
obj-$(CONFIG_VEML6070) += veml6070.o
obj-$(CONFIG_VL6180) += vl6180.o
+obj-$(CONFIG_ZOPT2201) += zopt2201.o
diff --git a/drivers/iio/light/zopt2201.c b/drivers/iio/light/zopt2201.c
new file mode 100644
index 0000000..041ac9e
--- /dev/null
+++ b/drivers/iio/light/zopt2201.c
@@ -0,0 +1,568 @@
+/*
+ * zopt2201.c - Support for IDT ZOPT2201 ambient light and UV B sensor
+ *
+ * Copyright 2017 Peter Meerwald-Stadler <pmeerw@pmeerw.net>
+ *
+ * This file is subject to the terms and conditions of version 2 of
+ * the GNU General Public License. See the file COPYING in the main
+ * directory of this archive for more details.
+ *
+ * Datasheet: https://www.idt.com/document/dst/zopt2201-datasheet
+ * 7-bit I2C slave addresses 0x53 (default) or 0x52 (programmed)
+ *
+ * TODO: interrupt support, ALS/UVB raw mode
+ */
+
+#include <linux/module.h>
+#include <linux/i2c.h>
+#include <linux/mutex.h>
+#include <linux/err.h>
+#include <linux/delay.h>
+
+#include <linux/iio/iio.h>
+#include <linux/iio/sysfs.h>
+
+#define ZOPT2201_DRV_NAME "zopt2201"
+
+/* Registers */
+#define ZOPT2201_MAIN_CTRL 0x00
+#define ZOPT2201_LS_MEAS_RATE 0x04
+#define ZOPT2201_LS_GAIN 0x05
+#define ZOPT2201_PART_ID 0x06
+#define ZOPT2201_MAIN_STATUS 0x07
+#define ZOPT2201_ALS_DATA 0x0d /* LSB first, 13 to 20 bits */
+#define ZOPT2201_UVB_DATA 0x10 /* LSB first, 13 to 20 bits */
+#define ZOPT2201_UV_COMP_DATA 0x13 /* LSB first, 13 to 20 bits */
+#define ZOPT2201_COMP_DATA 0x16 /* LSB first, 13 to 20 bits */
+#define ZOPT2201_INT_CFG 0x19
+#define ZOPT2201_INT_PST 0x1a
+
+#define ZOPT2201_MAIN_CTRL_LS_MODE BIT(3) /* 0 .. ALS, 1 .. UV B */
+#define ZOPT2201_MAIN_CTRL_LS_EN BIT(1)
+
+/* Values for ZOPT2201_LS_MEAS_RATE resolution / bit width */
+#define ZOPT2201_MEAS_RES_20BIT 0 /* takes 400 ms */
+#define ZOPT2201_MEAS_RES_19BIT 1 /* takes 200 ms */
+#define ZOPT2201_MEAS_RES_18BIT 2 /* takes 100 ms, default */
+#define ZOPT2201_MEAS_RES_17BIT 3 /* takes 50 ms */
+#define ZOPT2201_MEAS_RES_16BIT 4 /* takes 25 ms */
+#define ZOPT2201_MEAS_RES_13BIT 5 /* takes 3.125 ms */
+#define ZOPT2201_MEAS_RES_SHIFT 4
+
+/* Values for ZOPT2201_LS_MEAS_RATE measurement rate */
+#define ZOPT2201_MEAS_FREQ_25MS 0
+#define ZOPT2201_MEAS_FREQ_50MS 1
+#define ZOPT2201_MEAS_FREQ_100MS 2 /* default */
+#define ZOPT2201_MEAS_FREQ_200MS 3
+#define ZOPT2201_MEAS_FREQ_500MS 4
+#define ZOPT2201_MEAS_FREQ_1000MS 5
+#define ZOPT2201_MEAS_FREQ_2000MS 6
+
+/* Values for ZOPT2201_LS_GAIN */
+#define ZOPT2201_LS_GAIN_1 0
+#define ZOPT2201_LS_GAIN_3 1
+#define ZOPT2201_LS_GAIN_6 2
+#define ZOPT2201_LS_GAIN_9 3
+#define ZOPT2201_LS_GAIN_18 4
+
+/* Values for ZOPT2201_MAIN_STATUS */
+#define ZOPT2201_MAIN_STATUS_POWERON BIT(5)
+#define ZOPT2201_MAIN_STATUS_INT BIT(4)
+#define ZOPT2201_MAIN_STATUS_DRDY BIT(3)
+
+#define ZOPT2201_PART_NUMBER 0xb2
+
+struct zopt2201_data {
+ struct i2c_client *client;
+ struct mutex lock;
+ u8 gain;
+ u8 res;
+ u8 rate;
+};
+
+static const struct {
+ unsigned int gain; /* gain factor */
+ unsigned int scale; /* micro lux per count */
+} zopt2201_gain_als[] = {
+ { 1, 19200000 },
+ { 3, 6400000 },
+ { 6, 3200000 },
+ { 9, 2133333 },
+ { 18, 1066666 },
+};
+
+static const struct {
+ unsigned int gain; /* gain factor */
+ unsigned int scale; /* micro W/m2 per count */
+} zopt2201_gain_uvb[] = {
+ { 1, 460800 },
+ { 3, 153600 },
+ { 6, 76800 },
+ { 9, 51200 },
+ { 18, 25600 },
+};
+
+static const struct {
+ unsigned int bits; /* sensor resolution in bits */
+ unsigned long us; /* measurement time in micro seconds */
+} zopt2201_resolution[] = {
+ { 20, 400000 },
+ { 19, 200000 },
+ { 18, 100000 },
+ { 17, 50000 },
+ { 16, 25000 },
+ { 13, 3125 },
+};
+
+static const struct {
+ unsigned int scale, uscale; /* scale factor as integer + micro */
+ u8 gain; /* gain register value */
+ u8 res; /* resolution register value */
+} zopt2201_scale_als[] = {
+ { 19, 200000, 0, 5 },
+ { 6, 400000, 1, 5 },
+ { 3, 200000, 2, 5 },
+ { 2, 400000, 0, 4 },
+ { 2, 133333, 3, 5 },
+ { 1, 200000, 0, 3 },
+ { 1, 66666, 4, 5 },
+ { 0, 800000, 1, 4 },
+ { 0, 600000, 0, 2 },
+ { 0, 400000, 2, 4 },
+ { 0, 300000, 0, 1 },
+ { 0, 266666, 3, 4 },
+ { 0, 200000, 2, 3 },
+ { 0, 150000, 0, 0 },
+ { 0, 133333, 4, 4 },
+ { 0, 100000, 2, 2 },
+ { 0, 66666, 4, 3 },
+ { 0, 50000, 2, 1 },
+ { 0, 33333, 4, 2 },
+ { 0, 25000, 2, 0 },
+ { 0, 16666, 4, 1 },
+ { 0, 8333, 4, 0 },
+};
+
+static const struct {
+ unsigned int scale, uscale; /* scale factor as integer + micro */
+ u8 gain; /* gain register value */
+ u8 res; /* resolution register value */
+} zopt2201_scale_uvb[] = {
+ { 0, 460800, 0, 5 },
+ { 0, 153600, 1, 5 },
+ { 0, 76800, 2, 5 },
+ { 0, 57600, 0, 4 },
+ { 0, 51200, 3, 5 },
+ { 0, 28800, 0, 3 },
+ { 0, 25600, 4, 5 },
+ { 0, 19200, 1, 4 },
+ { 0, 14400, 0, 2 },
+ { 0, 9600, 2, 4 },
+ { 0, 7200, 0, 1 },
+ { 0, 6400, 3, 4 },
+ { 0, 4800, 2, 3 },
+ { 0, 3600, 0, 0 },
+ { 0, 3200, 4, 4 },
+ { 0, 2400, 2, 2 },
+ { 0, 1600, 4, 3 },
+ { 0, 1200, 2, 1 },
+ { 0, 800, 4, 2 },
+ { 0, 600, 2, 0 },
+ { 0, 400, 4, 1 },
+ { 0, 200, 4, 0 },
+};
+
+static int zopt2201_enable_mode(struct zopt2201_data *data, bool uvb_mode)
+{
+ u8 out = ZOPT2201_MAIN_CTRL_LS_EN;
+
+ if (uvb_mode)
+ out |= ZOPT2201_MAIN_CTRL_LS_MODE;
+
+ return i2c_smbus_write_byte_data(data->client, ZOPT2201_MAIN_CTRL, out);
+}
+
+static int zopt2201_read(struct zopt2201_data *data, u8 reg)
+{
+ struct i2c_client *client = data->client;
+ int tries = 10;
+ u8 buf[3];
+ int ret;
+
+ mutex_lock(&data->lock);
+ ret = zopt2201_enable_mode(data, reg == ZOPT2201_UVB_DATA);
+ if (ret < 0)
+ goto fail;
+
+ while (tries--) {
+ unsigned long t = zopt2201_resolution[data->res].us;
+
+ if (t <= 20000)
+ usleep_range(t, t + 1000);
+ else
+ msleep(t / 1000);
+ ret = i2c_smbus_read_byte_data(client, ZOPT2201_MAIN_STATUS);
+ if (ret < 0)
+ goto fail;
+ if (ret & ZOPT2201_MAIN_STATUS_DRDY)
+ break;
+ }
+
+ if (tries < 0) {
+ ret = -ETIMEDOUT;
+ goto fail;
+ }
+
+ ret = i2c_smbus_read_i2c_block_data(client, reg, sizeof(buf), buf);
+ if (ret < 0)
+ goto fail;
+
+ ret = i2c_smbus_write_byte_data(client, ZOPT2201_MAIN_CTRL, 0x00);
+ if (ret < 0)
+ goto fail;
+ mutex_unlock(&data->lock);
+
+ return (buf[2] << 16) | (buf[1] << 8) | buf[0];
+
+fail:
+ mutex_unlock(&data->lock);
+ return ret;
+}
+
+static const struct iio_chan_spec zopt2201_channels[] = {
+ {
+ .type = IIO_LIGHT,
+ .address = ZOPT2201_ALS_DATA,
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
+ BIT(IIO_CHAN_INFO_SCALE),
+ .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_INT_TIME),
+ },
+ {
+ .type = IIO_INTENSITY,
+ .modified = 1,
+ .channel2 = IIO_MOD_LIGHT_UV,
+ .address = ZOPT2201_UVB_DATA,
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
+ BIT(IIO_CHAN_INFO_SCALE),
+ .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_INT_TIME),
+ },
+ {
+ .type = IIO_UVINDEX,
+ .info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED),
+ },
+};
+
+static int zopt2201_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int *val, int *val2, long mask)
+{
+ struct zopt2201_data *data = iio_priv(indio_dev);
+ u64 tmp;
+ int ret;
+
+ switch (mask) {
+ case IIO_CHAN_INFO_RAW:
+ ret = zopt2201_read(data, chan->address);
+ if (ret < 0)
+ return ret;
+ *val = ret;
+ return IIO_VAL_INT;
+ case IIO_CHAN_INFO_PROCESSED:
+ ret = zopt2201_read(data, ZOPT2201_UVB_DATA);
+ if (ret < 0)
+ return ret;
+ *val = ret * 18 *
+ (1 << (20 - zopt2201_resolution[data->res].bits)) /
+ zopt2201_gain_uvb[data->gain].gain;
+ return IIO_VAL_INT;
+ case IIO_CHAN_INFO_SCALE:
+ switch (chan->address) {
+ case ZOPT2201_ALS_DATA:
+ *val = zopt2201_gain_als[data->gain].scale;
+ break;
+ case ZOPT2201_UVB_DATA:
+ *val = zopt2201_gain_uvb[data->gain].scale;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ *val2 = 1000000;
+ *val2 *= (1 << (zopt2201_resolution[data->res].bits - 13));
+ tmp = div_s64(*val * 1000000ULL, *val2);
+ *val = div_s64_rem(tmp, 1000000, val2);
+
+ return IIO_VAL_INT_PLUS_MICRO;
+ case IIO_CHAN_INFO_INT_TIME:
+ *val = 0;
+ *val2 = zopt2201_resolution[data->res].us;
+ return IIO_VAL_INT_PLUS_MICRO;
+ default:
+ return -EINVAL;
+ }
+}
+
+static int zopt2201_set_resolution(struct zopt2201_data *data, u8 res)
+{
+ int ret;
+
+ ret = i2c_smbus_write_byte_data(data->client, ZOPT2201_LS_MEAS_RATE,
+ (res << ZOPT2201_MEAS_RES_SHIFT) |
+ data->rate);
+ if (ret < 0)
+ return ret;
+
+ data->res = res;
+
+ return 0;
+}
+
+static int zopt2201_write_resolution(struct zopt2201_data *data,
+ int val, int val2)
+{
+ int i, ret;
+
+ if (val != 0)
+ return -EINVAL;
+
+ for (i = 0; i < ARRAY_SIZE(zopt2201_resolution); i++)
+ if (val2 == zopt2201_resolution[i].us) {
+ mutex_lock(&data->lock);
+ ret = zopt2201_set_resolution(data, i);
+ mutex_unlock(&data->lock);
+ return ret;
+ }
+
+ return -EINVAL;
+}
+
+static int zopt2201_set_gain(struct zopt2201_data *data, u8 gain)
+{
+ int ret;
+
+ ret = i2c_smbus_write_byte_data(data->client, ZOPT2201_LS_GAIN, gain);
+ if (ret < 0)
+ return ret;
+
+ data->gain = gain;
+
+ return 0;
+}
+
+static int zopt2201_write_scale_als_by_idx(struct zopt2201_data *data, int idx)
+{
+ int ret;
+
+ mutex_lock(&data->lock);
+ ret = zopt2201_set_resolution(data, zopt2201_scale_als[idx].res);
+ if (ret < 0)
+ goto unlock;
+
+ ret = zopt2201_set_gain(data, zopt2201_scale_als[idx].gain);
+
+unlock:
+ mutex_unlock(&data->lock);
+ return ret;
+}
+
+static int zopt2201_write_scale_als(struct zopt2201_data *data,
+ int val, int val2)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(zopt2201_scale_als); i++)
+ if (val == zopt2201_scale_als[i].scale &&
+ val2 == zopt2201_scale_als[i].uscale) {
+ return zopt2201_write_scale_als_by_idx(data, i);
+ }
+
+ return -EINVAL;
+}
+
+static int zopt2201_write_scale_uvb_by_idx(struct zopt2201_data *data, int idx)
+{
+ int ret;
+
+ mutex_lock(&data->lock);
+ ret = zopt2201_set_resolution(data, zopt2201_scale_als[idx].res);
+ if (ret < 0)
+ goto unlock;
+
+ ret = zopt2201_set_gain(data, zopt2201_scale_als[idx].gain);
+
+unlock:
+ mutex_unlock(&data->lock);
+ return ret;
+}
+
+static int zopt2201_write_scale_uvb(struct zopt2201_data *data,
+ int val, int val2)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(zopt2201_scale_uvb); i++)
+ if (val == zopt2201_scale_uvb[i].scale &&
+ val2 == zopt2201_scale_uvb[i].uscale)
+ return zopt2201_write_scale_uvb_by_idx(data, i);
+
+ return -EINVAL;
+}
+
+static int zopt2201_write_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int val, int val2, long mask)
+{
+ struct zopt2201_data *data = iio_priv(indio_dev);
+
+ switch (mask) {
+ case IIO_CHAN_INFO_INT_TIME:
+ return zopt2201_write_resolution(data, val, val2);
+ case IIO_CHAN_INFO_SCALE:
+ switch (chan->address) {
+ case ZOPT2201_ALS_DATA:
+ return zopt2201_write_scale_als(data, val, val2);
+ case ZOPT2201_UVB_DATA:
+ return zopt2201_write_scale_uvb(data, val, val2);
+ default:
+ return -EINVAL;
+ }
+ }
+
+ return -EINVAL;
+}
+
+static ssize_t zopt2201_show_int_time_available(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ size_t len = 0;
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(zopt2201_resolution); i++)
+ len += scnprintf(buf + len, PAGE_SIZE - len, "0.%06lu ",
+ zopt2201_resolution[i].us);
+ buf[len - 1] = '\n';
+
+ return len;
+}
+
+static IIO_DEV_ATTR_INT_TIME_AVAIL(zopt2201_show_int_time_available);
+
+static ssize_t zopt2201_show_als_scale_avail(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ ssize_t len = 0;
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(zopt2201_scale_als); i++)
+ len += scnprintf(buf + len, PAGE_SIZE - len, "%d.%06u ",
+ zopt2201_scale_als[i].scale,
+ zopt2201_scale_als[i].uscale);
+ buf[len - 1] = '\n';
+
+ return len;
+}
+
+static ssize_t zopt2201_show_uvb_scale_avail(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ ssize_t len = 0;
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(zopt2201_scale_uvb); i++)
+ len += scnprintf(buf + len, PAGE_SIZE - len, "%d.%06u ",
+ zopt2201_scale_uvb[i].scale,
+ zopt2201_scale_uvb[i].uscale);
+ buf[len - 1] = '\n';
+
+ return len;
+}
+
+static IIO_DEVICE_ATTR(in_illuminance_scale_available, 0444,
+ zopt2201_show_als_scale_avail, NULL, 0);
+static IIO_DEVICE_ATTR(in_intensity_uv_scale_available, 0444,
+ zopt2201_show_uvb_scale_avail, NULL, 0);
+
+static struct attribute *zopt2201_attributes[] = {
+ &iio_dev_attr_integration_time_available.dev_attr.attr,
+ &iio_dev_attr_in_illuminance_scale_available.dev_attr.attr,
+ &iio_dev_attr_in_intensity_uv_scale_available.dev_attr.attr,
+ NULL
+};
+
+static const struct attribute_group zopt2201_attribute_group = {
+ .attrs = zopt2201_attributes,
+};
+
+static const struct iio_info zopt2201_info = {
+ .read_raw = zopt2201_read_raw,
+ .write_raw = zopt2201_write_raw,
+ .attrs = &zopt2201_attribute_group,
+};
+
+static int zopt2201_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct zopt2201_data *data;
+ struct iio_dev *indio_dev;
+ int ret;
+
+ if (!i2c_check_functionality(client->adapter,
+ I2C_FUNC_SMBUS_READ_I2C_BLOCK))
+ return -EOPNOTSUPP;
+
+ ret = i2c_smbus_read_byte_data(client, ZOPT2201_PART_ID);
+ if (ret < 0)
+ return ret;
+ if (ret != ZOPT2201_PART_NUMBER)
+ return -ENODEV;
+
+ indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data));
+ if (!indio_dev)
+ return -ENOMEM;
+
+ data = iio_priv(indio_dev);
+ i2c_set_clientdata(client, indio_dev);
+ data->client = client;
+ mutex_init(&data->lock);
+
+ indio_dev->dev.parent = &client->dev;
+ indio_dev->info = &zopt2201_info;
+ indio_dev->channels = zopt2201_channels;
+ indio_dev->num_channels = ARRAY_SIZE(zopt2201_channels);
+ indio_dev->name = ZOPT2201_DRV_NAME;
+ indio_dev->modes = INDIO_DIRECT_MODE;
+
+ data->rate = ZOPT2201_MEAS_FREQ_100MS;
+ ret = zopt2201_set_resolution(data, ZOPT2201_MEAS_RES_18BIT);
+ if (ret < 0)
+ return ret;
+
+ ret = zopt2201_set_gain(data, ZOPT2201_LS_GAIN_3);
+ if (ret < 0)
+ return ret;
+
+ return devm_iio_device_register(&client->dev, indio_dev);
+}
+
+static const struct i2c_device_id zopt2201_id[] = {
+ { "zopt2201", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, zopt2201_id);
+
+static struct i2c_driver zopt2201_driver = {
+ .driver = {
+ .name = ZOPT2201_DRV_NAME,
+ },
+ .probe = zopt2201_probe,
+ .id_table = zopt2201_id,
+};
+
+module_i2c_driver(zopt2201_driver);
+
+MODULE_AUTHOR("Peter Meerwald-Stadler <pmeerw@pmeerw.net>");
+MODULE_DESCRIPTION("IDT ZOPT2201 ambient light and UV B sensor driver");
+MODULE_LICENSE("GPL");
--
2.7.4
^ permalink raw reply related [flat|nested] 6+ messages in thread
* [PATCH v2 2/2] Documentation: iio: Fix typo
2017-11-05 23:24 [PATCH v2 0/2] ZOPT2210 ambient light and UVB sensor driver Peter Meerwald-Stadler
2017-11-05 23:24 ` [PATCH v2 1/2] iio: light: Add driver for IDT ZOPT2201 ambient light and UVB sensor Peter Meerwald-Stadler
@ 2017-11-05 23:25 ` Peter Meerwald-Stadler
2017-11-19 14:55 ` Jonathan Cameron
1 sibling, 1 reply; 6+ messages in thread
From: Peter Meerwald-Stadler @ 2017-11-05 23:25 UTC (permalink / raw)
To: Jonathan Cameron; +Cc: linux-iio, Peter Meerwald-Stadler
Signed-off-by: Peter Meerwald-Stadler <pmeerw@pmeerw.net>
---
Documentation/ABI/testing/sysfs-bus-iio | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/Documentation/ABI/testing/sysfs-bus-iio b/Documentation/ABI/testing/sysfs-bus-iio
index 2e3f919..a478740 100644
--- a/Documentation/ABI/testing/sysfs-bus-iio
+++ b/Documentation/ABI/testing/sysfs-bus-iio
@@ -1290,7 +1290,7 @@ KernelVersion: 3.4
Contact: linux-iio@vger.kernel.org
Description:
Unit-less light intensity. Modifiers both and ir indicate
- that measurements contains visible and infrared light
+ that measurements contain visible and infrared light
components or just infrared light, respectively. Modifier uv indicates
that measurements contain ultraviolet light components.
--
2.7.4
^ permalink raw reply related [flat|nested] 6+ messages in thread
* Re: [PATCH v2 1/2] iio: light: Add driver for IDT ZOPT2201 ambient light and UVB sensor
2017-11-05 23:24 ` [PATCH v2 1/2] iio: light: Add driver for IDT ZOPT2201 ambient light and UVB sensor Peter Meerwald-Stadler
@ 2017-11-11 0:26 ` Jonathan Cameron
2017-11-19 14:54 ` Jonathan Cameron
0 siblings, 1 reply; 6+ messages in thread
From: Jonathan Cameron @ 2017-11-11 0:26 UTC (permalink / raw)
To: Peter Meerwald-Stadler; +Cc: Jonathan Cameron, linux-iio
On Mon, 6 Nov 2017 00:24:59 +0100
Peter Meerwald-Stadler <pmeerw@pmeerw.net> wrote:
> Driver for 20-bit ALS and UV B sensor with I2C interface exposing
> the following API:
> in_uvindex_input
> in_illuminance_raw
> in_illuminance_scale
> in_illuminance_scale_available
> in_intensity_uv_raw
> in_intensity_uv_scale
> in_intensity_uv_scale_available
> integration_time
> integration_time_available
>
> Signed-off-by: Peter Meerwald-Stadler <pmeerw@pmeerw.net>
Looks good. I'll pick this up when I'm next on the right computer rather than
my work laptop.
Thanks,
Jonathan
> ---
> drivers/iio/light/Kconfig | 10 +
> drivers/iio/light/Makefile | 1 +
> drivers/iio/light/zopt2201.c | 568 +++++++++++++++++++++++++++++++++++++++++++
> 3 files changed, 579 insertions(+)
> create mode 100644 drivers/iio/light/zopt2201.c
>
> diff --git a/drivers/iio/light/Kconfig b/drivers/iio/light/Kconfig
> index 2356ed9..6a5835f 100644
> --- a/drivers/iio/light/Kconfig
> +++ b/drivers/iio/light/Kconfig
> @@ -425,4 +425,14 @@ config VL6180
> To compile this driver as a module, choose M here: the
> module will be called vl6180.
>
> +config ZOPT2201
> + tristate "ZOPT2201 ALS and UV B sensor"
> + depends on I2C
> + help
> + Say Y here if you want to build a driver for the IDT
> + ZOPT2201 ambient light and UV B sensor.
> +
> + To compile this driver as a module, choose M here: the
> + module will be called zopt2201.
> +
> endmenu
> diff --git a/drivers/iio/light/Makefile b/drivers/iio/light/Makefile
> index fa32fa4..d99abdf 100644
> --- a/drivers/iio/light/Makefile
> +++ b/drivers/iio/light/Makefile
> @@ -40,3 +40,4 @@ obj-$(CONFIG_US5182D) += us5182d.o
> obj-$(CONFIG_VCNL4000) += vcnl4000.o
> obj-$(CONFIG_VEML6070) += veml6070.o
> obj-$(CONFIG_VL6180) += vl6180.o
> +obj-$(CONFIG_ZOPT2201) += zopt2201.o
> diff --git a/drivers/iio/light/zopt2201.c b/drivers/iio/light/zopt2201.c
> new file mode 100644
> index 0000000..041ac9e
> --- /dev/null
> +++ b/drivers/iio/light/zopt2201.c
> @@ -0,0 +1,568 @@
> +/*
> + * zopt2201.c - Support for IDT ZOPT2201 ambient light and UV B sensor
> + *
> + * Copyright 2017 Peter Meerwald-Stadler <pmeerw@pmeerw.net>
> + *
> + * This file is subject to the terms and conditions of version 2 of
> + * the GNU General Public License. See the file COPYING in the main
> + * directory of this archive for more details.
> + *
> + * Datasheet: https://www.idt.com/document/dst/zopt2201-datasheet
> + * 7-bit I2C slave addresses 0x53 (default) or 0x52 (programmed)
> + *
> + * TODO: interrupt support, ALS/UVB raw mode
> + */
> +
> +#include <linux/module.h>
> +#include <linux/i2c.h>
> +#include <linux/mutex.h>
> +#include <linux/err.h>
> +#include <linux/delay.h>
> +
> +#include <linux/iio/iio.h>
> +#include <linux/iio/sysfs.h>
> +
> +#define ZOPT2201_DRV_NAME "zopt2201"
> +
> +/* Registers */
> +#define ZOPT2201_MAIN_CTRL 0x00
> +#define ZOPT2201_LS_MEAS_RATE 0x04
> +#define ZOPT2201_LS_GAIN 0x05
> +#define ZOPT2201_PART_ID 0x06
> +#define ZOPT2201_MAIN_STATUS 0x07
> +#define ZOPT2201_ALS_DATA 0x0d /* LSB first, 13 to 20 bits */
> +#define ZOPT2201_UVB_DATA 0x10 /* LSB first, 13 to 20 bits */
> +#define ZOPT2201_UV_COMP_DATA 0x13 /* LSB first, 13 to 20 bits */
> +#define ZOPT2201_COMP_DATA 0x16 /* LSB first, 13 to 20 bits */
> +#define ZOPT2201_INT_CFG 0x19
> +#define ZOPT2201_INT_PST 0x1a
> +
> +#define ZOPT2201_MAIN_CTRL_LS_MODE BIT(3) /* 0 .. ALS, 1 .. UV B */
> +#define ZOPT2201_MAIN_CTRL_LS_EN BIT(1)
> +
> +/* Values for ZOPT2201_LS_MEAS_RATE resolution / bit width */
> +#define ZOPT2201_MEAS_RES_20BIT 0 /* takes 400 ms */
> +#define ZOPT2201_MEAS_RES_19BIT 1 /* takes 200 ms */
> +#define ZOPT2201_MEAS_RES_18BIT 2 /* takes 100 ms, default */
> +#define ZOPT2201_MEAS_RES_17BIT 3 /* takes 50 ms */
> +#define ZOPT2201_MEAS_RES_16BIT 4 /* takes 25 ms */
> +#define ZOPT2201_MEAS_RES_13BIT 5 /* takes 3.125 ms */
> +#define ZOPT2201_MEAS_RES_SHIFT 4
> +
> +/* Values for ZOPT2201_LS_MEAS_RATE measurement rate */
> +#define ZOPT2201_MEAS_FREQ_25MS 0
> +#define ZOPT2201_MEAS_FREQ_50MS 1
> +#define ZOPT2201_MEAS_FREQ_100MS 2 /* default */
> +#define ZOPT2201_MEAS_FREQ_200MS 3
> +#define ZOPT2201_MEAS_FREQ_500MS 4
> +#define ZOPT2201_MEAS_FREQ_1000MS 5
> +#define ZOPT2201_MEAS_FREQ_2000MS 6
> +
> +/* Values for ZOPT2201_LS_GAIN */
> +#define ZOPT2201_LS_GAIN_1 0
> +#define ZOPT2201_LS_GAIN_3 1
> +#define ZOPT2201_LS_GAIN_6 2
> +#define ZOPT2201_LS_GAIN_9 3
> +#define ZOPT2201_LS_GAIN_18 4
> +
> +/* Values for ZOPT2201_MAIN_STATUS */
> +#define ZOPT2201_MAIN_STATUS_POWERON BIT(5)
> +#define ZOPT2201_MAIN_STATUS_INT BIT(4)
> +#define ZOPT2201_MAIN_STATUS_DRDY BIT(3)
> +
> +#define ZOPT2201_PART_NUMBER 0xb2
> +
> +struct zopt2201_data {
> + struct i2c_client *client;
> + struct mutex lock;
> + u8 gain;
> + u8 res;
> + u8 rate;
> +};
> +
> +static const struct {
> + unsigned int gain; /* gain factor */
> + unsigned int scale; /* micro lux per count */
> +} zopt2201_gain_als[] = {
> + { 1, 19200000 },
> + { 3, 6400000 },
> + { 6, 3200000 },
> + { 9, 2133333 },
> + { 18, 1066666 },
> +};
> +
> +static const struct {
> + unsigned int gain; /* gain factor */
> + unsigned int scale; /* micro W/m2 per count */
> +} zopt2201_gain_uvb[] = {
> + { 1, 460800 },
> + { 3, 153600 },
> + { 6, 76800 },
> + { 9, 51200 },
> + { 18, 25600 },
> +};
> +
> +static const struct {
> + unsigned int bits; /* sensor resolution in bits */
> + unsigned long us; /* measurement time in micro seconds */
> +} zopt2201_resolution[] = {
> + { 20, 400000 },
> + { 19, 200000 },
> + { 18, 100000 },
> + { 17, 50000 },
> + { 16, 25000 },
> + { 13, 3125 },
> +};
> +
> +static const struct {
> + unsigned int scale, uscale; /* scale factor as integer + micro */
> + u8 gain; /* gain register value */
> + u8 res; /* resolution register value */
> +} zopt2201_scale_als[] = {
> + { 19, 200000, 0, 5 },
> + { 6, 400000, 1, 5 },
> + { 3, 200000, 2, 5 },
> + { 2, 400000, 0, 4 },
> + { 2, 133333, 3, 5 },
> + { 1, 200000, 0, 3 },
> + { 1, 66666, 4, 5 },
> + { 0, 800000, 1, 4 },
> + { 0, 600000, 0, 2 },
> + { 0, 400000, 2, 4 },
> + { 0, 300000, 0, 1 },
> + { 0, 266666, 3, 4 },
> + { 0, 200000, 2, 3 },
> + { 0, 150000, 0, 0 },
> + { 0, 133333, 4, 4 },
> + { 0, 100000, 2, 2 },
> + { 0, 66666, 4, 3 },
> + { 0, 50000, 2, 1 },
> + { 0, 33333, 4, 2 },
> + { 0, 25000, 2, 0 },
> + { 0, 16666, 4, 1 },
> + { 0, 8333, 4, 0 },
> +};
> +
> +static const struct {
> + unsigned int scale, uscale; /* scale factor as integer + micro */
> + u8 gain; /* gain register value */
> + u8 res; /* resolution register value */
> +} zopt2201_scale_uvb[] = {
> + { 0, 460800, 0, 5 },
> + { 0, 153600, 1, 5 },
> + { 0, 76800, 2, 5 },
> + { 0, 57600, 0, 4 },
> + { 0, 51200, 3, 5 },
> + { 0, 28800, 0, 3 },
> + { 0, 25600, 4, 5 },
> + { 0, 19200, 1, 4 },
> + { 0, 14400, 0, 2 },
> + { 0, 9600, 2, 4 },
> + { 0, 7200, 0, 1 },
> + { 0, 6400, 3, 4 },
> + { 0, 4800, 2, 3 },
> + { 0, 3600, 0, 0 },
> + { 0, 3200, 4, 4 },
> + { 0, 2400, 2, 2 },
> + { 0, 1600, 4, 3 },
> + { 0, 1200, 2, 1 },
> + { 0, 800, 4, 2 },
> + { 0, 600, 2, 0 },
> + { 0, 400, 4, 1 },
> + { 0, 200, 4, 0 },
> +};
> +
> +static int zopt2201_enable_mode(struct zopt2201_data *data, bool uvb_mode)
> +{
> + u8 out = ZOPT2201_MAIN_CTRL_LS_EN;
> +
> + if (uvb_mode)
> + out |= ZOPT2201_MAIN_CTRL_LS_MODE;
> +
> + return i2c_smbus_write_byte_data(data->client, ZOPT2201_MAIN_CTRL, out);
> +}
> +
> +static int zopt2201_read(struct zopt2201_data *data, u8 reg)
> +{
> + struct i2c_client *client = data->client;
> + int tries = 10;
> + u8 buf[3];
> + int ret;
> +
> + mutex_lock(&data->lock);
> + ret = zopt2201_enable_mode(data, reg == ZOPT2201_UVB_DATA);
> + if (ret < 0)
> + goto fail;
> +
> + while (tries--) {
> + unsigned long t = zopt2201_resolution[data->res].us;
> +
> + if (t <= 20000)
> + usleep_range(t, t + 1000);
> + else
> + msleep(t / 1000);
> + ret = i2c_smbus_read_byte_data(client, ZOPT2201_MAIN_STATUS);
> + if (ret < 0)
> + goto fail;
> + if (ret & ZOPT2201_MAIN_STATUS_DRDY)
> + break;
> + }
> +
> + if (tries < 0) {
> + ret = -ETIMEDOUT;
> + goto fail;
> + }
> +
> + ret = i2c_smbus_read_i2c_block_data(client, reg, sizeof(buf), buf);
> + if (ret < 0)
> + goto fail;
> +
> + ret = i2c_smbus_write_byte_data(client, ZOPT2201_MAIN_CTRL, 0x00);
> + if (ret < 0)
> + goto fail;
> + mutex_unlock(&data->lock);
> +
> + return (buf[2] << 16) | (buf[1] << 8) | buf[0];
> +
> +fail:
> + mutex_unlock(&data->lock);
> + return ret;
> +}
> +
> +static const struct iio_chan_spec zopt2201_channels[] = {
> + {
> + .type = IIO_LIGHT,
> + .address = ZOPT2201_ALS_DATA,
> + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
> + BIT(IIO_CHAN_INFO_SCALE),
> + .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_INT_TIME),
> + },
> + {
> + .type = IIO_INTENSITY,
> + .modified = 1,
> + .channel2 = IIO_MOD_LIGHT_UV,
> + .address = ZOPT2201_UVB_DATA,
> + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
> + BIT(IIO_CHAN_INFO_SCALE),
> + .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_INT_TIME),
> + },
> + {
> + .type = IIO_UVINDEX,
> + .info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED),
> + },
> +};
> +
> +static int zopt2201_read_raw(struct iio_dev *indio_dev,
> + struct iio_chan_spec const *chan,
> + int *val, int *val2, long mask)
> +{
> + struct zopt2201_data *data = iio_priv(indio_dev);
> + u64 tmp;
> + int ret;
> +
> + switch (mask) {
> + case IIO_CHAN_INFO_RAW:
> + ret = zopt2201_read(data, chan->address);
> + if (ret < 0)
> + return ret;
> + *val = ret;
> + return IIO_VAL_INT;
> + case IIO_CHAN_INFO_PROCESSED:
> + ret = zopt2201_read(data, ZOPT2201_UVB_DATA);
> + if (ret < 0)
> + return ret;
> + *val = ret * 18 *
> + (1 << (20 - zopt2201_resolution[data->res].bits)) /
> + zopt2201_gain_uvb[data->gain].gain;
> + return IIO_VAL_INT;
> + case IIO_CHAN_INFO_SCALE:
> + switch (chan->address) {
> + case ZOPT2201_ALS_DATA:
> + *val = zopt2201_gain_als[data->gain].scale;
> + break;
> + case ZOPT2201_UVB_DATA:
> + *val = zopt2201_gain_uvb[data->gain].scale;
> + break;
> + default:
> + return -EINVAL;
> + }
> +
> + *val2 = 1000000;
> + *val2 *= (1 << (zopt2201_resolution[data->res].bits - 13));
> + tmp = div_s64(*val * 1000000ULL, *val2);
> + *val = div_s64_rem(tmp, 1000000, val2);
> +
> + return IIO_VAL_INT_PLUS_MICRO;
> + case IIO_CHAN_INFO_INT_TIME:
> + *val = 0;
> + *val2 = zopt2201_resolution[data->res].us;
> + return IIO_VAL_INT_PLUS_MICRO;
> + default:
> + return -EINVAL;
> + }
> +}
> +
> +static int zopt2201_set_resolution(struct zopt2201_data *data, u8 res)
> +{
> + int ret;
> +
> + ret = i2c_smbus_write_byte_data(data->client, ZOPT2201_LS_MEAS_RATE,
> + (res << ZOPT2201_MEAS_RES_SHIFT) |
> + data->rate);
> + if (ret < 0)
> + return ret;
> +
> + data->res = res;
> +
> + return 0;
> +}
> +
> +static int zopt2201_write_resolution(struct zopt2201_data *data,
> + int val, int val2)
> +{
> + int i, ret;
> +
> + if (val != 0)
> + return -EINVAL;
> +
> + for (i = 0; i < ARRAY_SIZE(zopt2201_resolution); i++)
> + if (val2 == zopt2201_resolution[i].us) {
> + mutex_lock(&data->lock);
> + ret = zopt2201_set_resolution(data, i);
> + mutex_unlock(&data->lock);
> + return ret;
> + }
> +
> + return -EINVAL;
> +}
> +
> +static int zopt2201_set_gain(struct zopt2201_data *data, u8 gain)
> +{
> + int ret;
> +
> + ret = i2c_smbus_write_byte_data(data->client, ZOPT2201_LS_GAIN, gain);
> + if (ret < 0)
> + return ret;
> +
> + data->gain = gain;
> +
> + return 0;
> +}
> +
> +static int zopt2201_write_scale_als_by_idx(struct zopt2201_data *data, int idx)
> +{
> + int ret;
> +
> + mutex_lock(&data->lock);
> + ret = zopt2201_set_resolution(data, zopt2201_scale_als[idx].res);
> + if (ret < 0)
> + goto unlock;
> +
> + ret = zopt2201_set_gain(data, zopt2201_scale_als[idx].gain);
> +
> +unlock:
> + mutex_unlock(&data->lock);
> + return ret;
> +}
> +
> +static int zopt2201_write_scale_als(struct zopt2201_data *data,
> + int val, int val2)
> +{
> + int i;
> +
> + for (i = 0; i < ARRAY_SIZE(zopt2201_scale_als); i++)
> + if (val == zopt2201_scale_als[i].scale &&
> + val2 == zopt2201_scale_als[i].uscale) {
> + return zopt2201_write_scale_als_by_idx(data, i);
> + }
> +
> + return -EINVAL;
> +}
> +
> +static int zopt2201_write_scale_uvb_by_idx(struct zopt2201_data *data, int idx)
> +{
> + int ret;
> +
> + mutex_lock(&data->lock);
> + ret = zopt2201_set_resolution(data, zopt2201_scale_als[idx].res);
> + if (ret < 0)
> + goto unlock;
> +
> + ret = zopt2201_set_gain(data, zopt2201_scale_als[idx].gain);
> +
> +unlock:
> + mutex_unlock(&data->lock);
> + return ret;
> +}
> +
> +static int zopt2201_write_scale_uvb(struct zopt2201_data *data,
> + int val, int val2)
> +{
> + int i;
> +
> + for (i = 0; i < ARRAY_SIZE(zopt2201_scale_uvb); i++)
> + if (val == zopt2201_scale_uvb[i].scale &&
> + val2 == zopt2201_scale_uvb[i].uscale)
> + return zopt2201_write_scale_uvb_by_idx(data, i);
> +
> + return -EINVAL;
> +}
> +
> +static int zopt2201_write_raw(struct iio_dev *indio_dev,
> + struct iio_chan_spec const *chan,
> + int val, int val2, long mask)
> +{
> + struct zopt2201_data *data = iio_priv(indio_dev);
> +
> + switch (mask) {
> + case IIO_CHAN_INFO_INT_TIME:
> + return zopt2201_write_resolution(data, val, val2);
> + case IIO_CHAN_INFO_SCALE:
> + switch (chan->address) {
> + case ZOPT2201_ALS_DATA:
> + return zopt2201_write_scale_als(data, val, val2);
> + case ZOPT2201_UVB_DATA:
> + return zopt2201_write_scale_uvb(data, val, val2);
> + default:
> + return -EINVAL;
> + }
> + }
> +
> + return -EINVAL;
> +}
> +
> +static ssize_t zopt2201_show_int_time_available(struct device *dev,
> + struct device_attribute *attr,
> + char *buf)
> +{
> + size_t len = 0;
> + int i;
> +
> + for (i = 0; i < ARRAY_SIZE(zopt2201_resolution); i++)
> + len += scnprintf(buf + len, PAGE_SIZE - len, "0.%06lu ",
> + zopt2201_resolution[i].us);
> + buf[len - 1] = '\n';
> +
> + return len;
> +}
> +
> +static IIO_DEV_ATTR_INT_TIME_AVAIL(zopt2201_show_int_time_available);
> +
> +static ssize_t zopt2201_show_als_scale_avail(struct device *dev,
> + struct device_attribute *attr,
> + char *buf)
> +{
> + ssize_t len = 0;
> + int i;
> +
> + for (i = 0; i < ARRAY_SIZE(zopt2201_scale_als); i++)
> + len += scnprintf(buf + len, PAGE_SIZE - len, "%d.%06u ",
> + zopt2201_scale_als[i].scale,
> + zopt2201_scale_als[i].uscale);
> + buf[len - 1] = '\n';
> +
> + return len;
> +}
> +
> +static ssize_t zopt2201_show_uvb_scale_avail(struct device *dev,
> + struct device_attribute *attr,
> + char *buf)
> +{
> + ssize_t len = 0;
> + int i;
> +
> + for (i = 0; i < ARRAY_SIZE(zopt2201_scale_uvb); i++)
> + len += scnprintf(buf + len, PAGE_SIZE - len, "%d.%06u ",
> + zopt2201_scale_uvb[i].scale,
> + zopt2201_scale_uvb[i].uscale);
> + buf[len - 1] = '\n';
> +
> + return len;
> +}
> +
> +static IIO_DEVICE_ATTR(in_illuminance_scale_available, 0444,
> + zopt2201_show_als_scale_avail, NULL, 0);
> +static IIO_DEVICE_ATTR(in_intensity_uv_scale_available, 0444,
> + zopt2201_show_uvb_scale_avail, NULL, 0);
> +
> +static struct attribute *zopt2201_attributes[] = {
> + &iio_dev_attr_integration_time_available.dev_attr.attr,
> + &iio_dev_attr_in_illuminance_scale_available.dev_attr.attr,
> + &iio_dev_attr_in_intensity_uv_scale_available.dev_attr.attr,
> + NULL
> +};
> +
> +static const struct attribute_group zopt2201_attribute_group = {
> + .attrs = zopt2201_attributes,
> +};
> +
> +static const struct iio_info zopt2201_info = {
> + .read_raw = zopt2201_read_raw,
> + .write_raw = zopt2201_write_raw,
> + .attrs = &zopt2201_attribute_group,
> +};
> +
> +static int zopt2201_probe(struct i2c_client *client,
> + const struct i2c_device_id *id)
> +{
> + struct zopt2201_data *data;
> + struct iio_dev *indio_dev;
> + int ret;
> +
> + if (!i2c_check_functionality(client->adapter,
> + I2C_FUNC_SMBUS_READ_I2C_BLOCK))
> + return -EOPNOTSUPP;
> +
> + ret = i2c_smbus_read_byte_data(client, ZOPT2201_PART_ID);
> + if (ret < 0)
> + return ret;
> + if (ret != ZOPT2201_PART_NUMBER)
> + return -ENODEV;
> +
> + indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data));
> + if (!indio_dev)
> + return -ENOMEM;
> +
> + data = iio_priv(indio_dev);
> + i2c_set_clientdata(client, indio_dev);
> + data->client = client;
> + mutex_init(&data->lock);
> +
> + indio_dev->dev.parent = &client->dev;
> + indio_dev->info = &zopt2201_info;
> + indio_dev->channels = zopt2201_channels;
> + indio_dev->num_channels = ARRAY_SIZE(zopt2201_channels);
> + indio_dev->name = ZOPT2201_DRV_NAME;
> + indio_dev->modes = INDIO_DIRECT_MODE;
> +
> + data->rate = ZOPT2201_MEAS_FREQ_100MS;
> + ret = zopt2201_set_resolution(data, ZOPT2201_MEAS_RES_18BIT);
> + if (ret < 0)
> + return ret;
> +
> + ret = zopt2201_set_gain(data, ZOPT2201_LS_GAIN_3);
> + if (ret < 0)
> + return ret;
> +
> + return devm_iio_device_register(&client->dev, indio_dev);
> +}
> +
> +static const struct i2c_device_id zopt2201_id[] = {
> + { "zopt2201", 0 },
> + { }
> +};
> +MODULE_DEVICE_TABLE(i2c, zopt2201_id);
> +
> +static struct i2c_driver zopt2201_driver = {
> + .driver = {
> + .name = ZOPT2201_DRV_NAME,
> + },
> + .probe = zopt2201_probe,
> + .id_table = zopt2201_id,
> +};
> +
> +module_i2c_driver(zopt2201_driver);
> +
> +MODULE_AUTHOR("Peter Meerwald-Stadler <pmeerw@pmeerw.net>");
> +MODULE_DESCRIPTION("IDT ZOPT2201 ambient light and UV B sensor driver");
> +MODULE_LICENSE("GPL");
^ permalink raw reply [flat|nested] 6+ messages in thread
* Re: [PATCH v2 1/2] iio: light: Add driver for IDT ZOPT2201 ambient light and UVB sensor
2017-11-11 0:26 ` Jonathan Cameron
@ 2017-11-19 14:54 ` Jonathan Cameron
0 siblings, 0 replies; 6+ messages in thread
From: Jonathan Cameron @ 2017-11-19 14:54 UTC (permalink / raw)
To: Jonathan Cameron; +Cc: Peter Meerwald-Stadler, linux-iio
On Sat, 11 Nov 2017 00:26:04 +0000
Jonathan Cameron <Jonathan.Cameron@huawei.com> wrote:
> On Mon, 6 Nov 2017 00:24:59 +0100
> Peter Meerwald-Stadler <pmeerw@pmeerw.net> wrote:
>
> > Driver for 20-bit ALS and UV B sensor with I2C interface exposing
> > the following API:
> > in_uvindex_input
> > in_illuminance_raw
> > in_illuminance_scale
> > in_illuminance_scale_available
> > in_intensity_uv_raw
> > in_intensity_uv_scale
> > in_intensity_uv_scale_available
> > integration_time
> > integration_time_available
> >
> > Signed-off-by: Peter Meerwald-Stadler <pmeerw@pmeerw.net>
> Looks good. I'll pick this up when I'm next on the right computer rather than
> my work laptop.
Applied to the togreg branch of iio.git and pushed out as testing
for the autobuilders to play with it.
Thanks,
Jonathan
>
> Thanks,
>
> Jonathan
>
> > ---
> > drivers/iio/light/Kconfig | 10 +
> > drivers/iio/light/Makefile | 1 +
> > drivers/iio/light/zopt2201.c | 568 +++++++++++++++++++++++++++++++++++++++++++
> > 3 files changed, 579 insertions(+)
> > create mode 100644 drivers/iio/light/zopt2201.c
> >
> > diff --git a/drivers/iio/light/Kconfig b/drivers/iio/light/Kconfig
> > index 2356ed9..6a5835f 100644
> > --- a/drivers/iio/light/Kconfig
> > +++ b/drivers/iio/light/Kconfig
> > @@ -425,4 +425,14 @@ config VL6180
> > To compile this driver as a module, choose M here: the
> > module will be called vl6180.
> >
> > +config ZOPT2201
> > + tristate "ZOPT2201 ALS and UV B sensor"
> > + depends on I2C
> > + help
> > + Say Y here if you want to build a driver for the IDT
> > + ZOPT2201 ambient light and UV B sensor.
> > +
> > + To compile this driver as a module, choose M here: the
> > + module will be called zopt2201.
> > +
> > endmenu
> > diff --git a/drivers/iio/light/Makefile b/drivers/iio/light/Makefile
> > index fa32fa4..d99abdf 100644
> > --- a/drivers/iio/light/Makefile
> > +++ b/drivers/iio/light/Makefile
> > @@ -40,3 +40,4 @@ obj-$(CONFIG_US5182D) += us5182d.o
> > obj-$(CONFIG_VCNL4000) += vcnl4000.o
> > obj-$(CONFIG_VEML6070) += veml6070.o
> > obj-$(CONFIG_VL6180) += vl6180.o
> > +obj-$(CONFIG_ZOPT2201) += zopt2201.o
> > diff --git a/drivers/iio/light/zopt2201.c b/drivers/iio/light/zopt2201.c
> > new file mode 100644
> > index 0000000..041ac9e
> > --- /dev/null
> > +++ b/drivers/iio/light/zopt2201.c
> > @@ -0,0 +1,568 @@
> > +/*
> > + * zopt2201.c - Support for IDT ZOPT2201 ambient light and UV B sensor
> > + *
> > + * Copyright 2017 Peter Meerwald-Stadler <pmeerw@pmeerw.net>
> > + *
> > + * This file is subject to the terms and conditions of version 2 of
> > + * the GNU General Public License. See the file COPYING in the main
> > + * directory of this archive for more details.
> > + *
> > + * Datasheet: https://www.idt.com/document/dst/zopt2201-datasheet
> > + * 7-bit I2C slave addresses 0x53 (default) or 0x52 (programmed)
> > + *
> > + * TODO: interrupt support, ALS/UVB raw mode
> > + */
> > +
> > +#include <linux/module.h>
> > +#include <linux/i2c.h>
> > +#include <linux/mutex.h>
> > +#include <linux/err.h>
> > +#include <linux/delay.h>
> > +
> > +#include <linux/iio/iio.h>
> > +#include <linux/iio/sysfs.h>
> > +
> > +#define ZOPT2201_DRV_NAME "zopt2201"
> > +
> > +/* Registers */
> > +#define ZOPT2201_MAIN_CTRL 0x00
> > +#define ZOPT2201_LS_MEAS_RATE 0x04
> > +#define ZOPT2201_LS_GAIN 0x05
> > +#define ZOPT2201_PART_ID 0x06
> > +#define ZOPT2201_MAIN_STATUS 0x07
> > +#define ZOPT2201_ALS_DATA 0x0d /* LSB first, 13 to 20 bits */
> > +#define ZOPT2201_UVB_DATA 0x10 /* LSB first, 13 to 20 bits */
> > +#define ZOPT2201_UV_COMP_DATA 0x13 /* LSB first, 13 to 20 bits */
> > +#define ZOPT2201_COMP_DATA 0x16 /* LSB first, 13 to 20 bits */
> > +#define ZOPT2201_INT_CFG 0x19
> > +#define ZOPT2201_INT_PST 0x1a
> > +
> > +#define ZOPT2201_MAIN_CTRL_LS_MODE BIT(3) /* 0 .. ALS, 1 .. UV B */
> > +#define ZOPT2201_MAIN_CTRL_LS_EN BIT(1)
> > +
> > +/* Values for ZOPT2201_LS_MEAS_RATE resolution / bit width */
> > +#define ZOPT2201_MEAS_RES_20BIT 0 /* takes 400 ms */
> > +#define ZOPT2201_MEAS_RES_19BIT 1 /* takes 200 ms */
> > +#define ZOPT2201_MEAS_RES_18BIT 2 /* takes 100 ms, default */
> > +#define ZOPT2201_MEAS_RES_17BIT 3 /* takes 50 ms */
> > +#define ZOPT2201_MEAS_RES_16BIT 4 /* takes 25 ms */
> > +#define ZOPT2201_MEAS_RES_13BIT 5 /* takes 3.125 ms */
> > +#define ZOPT2201_MEAS_RES_SHIFT 4
> > +
> > +/* Values for ZOPT2201_LS_MEAS_RATE measurement rate */
> > +#define ZOPT2201_MEAS_FREQ_25MS 0
> > +#define ZOPT2201_MEAS_FREQ_50MS 1
> > +#define ZOPT2201_MEAS_FREQ_100MS 2 /* default */
> > +#define ZOPT2201_MEAS_FREQ_200MS 3
> > +#define ZOPT2201_MEAS_FREQ_500MS 4
> > +#define ZOPT2201_MEAS_FREQ_1000MS 5
> > +#define ZOPT2201_MEAS_FREQ_2000MS 6
> > +
> > +/* Values for ZOPT2201_LS_GAIN */
> > +#define ZOPT2201_LS_GAIN_1 0
> > +#define ZOPT2201_LS_GAIN_3 1
> > +#define ZOPT2201_LS_GAIN_6 2
> > +#define ZOPT2201_LS_GAIN_9 3
> > +#define ZOPT2201_LS_GAIN_18 4
> > +
> > +/* Values for ZOPT2201_MAIN_STATUS */
> > +#define ZOPT2201_MAIN_STATUS_POWERON BIT(5)
> > +#define ZOPT2201_MAIN_STATUS_INT BIT(4)
> > +#define ZOPT2201_MAIN_STATUS_DRDY BIT(3)
> > +
> > +#define ZOPT2201_PART_NUMBER 0xb2
> > +
> > +struct zopt2201_data {
> > + struct i2c_client *client;
> > + struct mutex lock;
> > + u8 gain;
> > + u8 res;
> > + u8 rate;
> > +};
> > +
> > +static const struct {
> > + unsigned int gain; /* gain factor */
> > + unsigned int scale; /* micro lux per count */
> > +} zopt2201_gain_als[] = {
> > + { 1, 19200000 },
> > + { 3, 6400000 },
> > + { 6, 3200000 },
> > + { 9, 2133333 },
> > + { 18, 1066666 },
> > +};
> > +
> > +static const struct {
> > + unsigned int gain; /* gain factor */
> > + unsigned int scale; /* micro W/m2 per count */
> > +} zopt2201_gain_uvb[] = {
> > + { 1, 460800 },
> > + { 3, 153600 },
> > + { 6, 76800 },
> > + { 9, 51200 },
> > + { 18, 25600 },
> > +};
> > +
> > +static const struct {
> > + unsigned int bits; /* sensor resolution in bits */
> > + unsigned long us; /* measurement time in micro seconds */
> > +} zopt2201_resolution[] = {
> > + { 20, 400000 },
> > + { 19, 200000 },
> > + { 18, 100000 },
> > + { 17, 50000 },
> > + { 16, 25000 },
> > + { 13, 3125 },
> > +};
> > +
> > +static const struct {
> > + unsigned int scale, uscale; /* scale factor as integer + micro */
> > + u8 gain; /* gain register value */
> > + u8 res; /* resolution register value */
> > +} zopt2201_scale_als[] = {
> > + { 19, 200000, 0, 5 },
> > + { 6, 400000, 1, 5 },
> > + { 3, 200000, 2, 5 },
> > + { 2, 400000, 0, 4 },
> > + { 2, 133333, 3, 5 },
> > + { 1, 200000, 0, 3 },
> > + { 1, 66666, 4, 5 },
> > + { 0, 800000, 1, 4 },
> > + { 0, 600000, 0, 2 },
> > + { 0, 400000, 2, 4 },
> > + { 0, 300000, 0, 1 },
> > + { 0, 266666, 3, 4 },
> > + { 0, 200000, 2, 3 },
> > + { 0, 150000, 0, 0 },
> > + { 0, 133333, 4, 4 },
> > + { 0, 100000, 2, 2 },
> > + { 0, 66666, 4, 3 },
> > + { 0, 50000, 2, 1 },
> > + { 0, 33333, 4, 2 },
> > + { 0, 25000, 2, 0 },
> > + { 0, 16666, 4, 1 },
> > + { 0, 8333, 4, 0 },
> > +};
> > +
> > +static const struct {
> > + unsigned int scale, uscale; /* scale factor as integer + micro */
> > + u8 gain; /* gain register value */
> > + u8 res; /* resolution register value */
> > +} zopt2201_scale_uvb[] = {
> > + { 0, 460800, 0, 5 },
> > + { 0, 153600, 1, 5 },
> > + { 0, 76800, 2, 5 },
> > + { 0, 57600, 0, 4 },
> > + { 0, 51200, 3, 5 },
> > + { 0, 28800, 0, 3 },
> > + { 0, 25600, 4, 5 },
> > + { 0, 19200, 1, 4 },
> > + { 0, 14400, 0, 2 },
> > + { 0, 9600, 2, 4 },
> > + { 0, 7200, 0, 1 },
> > + { 0, 6400, 3, 4 },
> > + { 0, 4800, 2, 3 },
> > + { 0, 3600, 0, 0 },
> > + { 0, 3200, 4, 4 },
> > + { 0, 2400, 2, 2 },
> > + { 0, 1600, 4, 3 },
> > + { 0, 1200, 2, 1 },
> > + { 0, 800, 4, 2 },
> > + { 0, 600, 2, 0 },
> > + { 0, 400, 4, 1 },
> > + { 0, 200, 4, 0 },
> > +};
> > +
> > +static int zopt2201_enable_mode(struct zopt2201_data *data, bool uvb_mode)
> > +{
> > + u8 out = ZOPT2201_MAIN_CTRL_LS_EN;
> > +
> > + if (uvb_mode)
> > + out |= ZOPT2201_MAIN_CTRL_LS_MODE;
> > +
> > + return i2c_smbus_write_byte_data(data->client, ZOPT2201_MAIN_CTRL, out);
> > +}
> > +
> > +static int zopt2201_read(struct zopt2201_data *data, u8 reg)
> > +{
> > + struct i2c_client *client = data->client;
> > + int tries = 10;
> > + u8 buf[3];
> > + int ret;
> > +
> > + mutex_lock(&data->lock);
> > + ret = zopt2201_enable_mode(data, reg == ZOPT2201_UVB_DATA);
> > + if (ret < 0)
> > + goto fail;
> > +
> > + while (tries--) {
> > + unsigned long t = zopt2201_resolution[data->res].us;
> > +
> > + if (t <= 20000)
> > + usleep_range(t, t + 1000);
> > + else
> > + msleep(t / 1000);
> > + ret = i2c_smbus_read_byte_data(client, ZOPT2201_MAIN_STATUS);
> > + if (ret < 0)
> > + goto fail;
> > + if (ret & ZOPT2201_MAIN_STATUS_DRDY)
> > + break;
> > + }
> > +
> > + if (tries < 0) {
> > + ret = -ETIMEDOUT;
> > + goto fail;
> > + }
> > +
> > + ret = i2c_smbus_read_i2c_block_data(client, reg, sizeof(buf), buf);
> > + if (ret < 0)
> > + goto fail;
> > +
> > + ret = i2c_smbus_write_byte_data(client, ZOPT2201_MAIN_CTRL, 0x00);
> > + if (ret < 0)
> > + goto fail;
> > + mutex_unlock(&data->lock);
> > +
> > + return (buf[2] << 16) | (buf[1] << 8) | buf[0];
> > +
> > +fail:
> > + mutex_unlock(&data->lock);
> > + return ret;
> > +}
> > +
> > +static const struct iio_chan_spec zopt2201_channels[] = {
> > + {
> > + .type = IIO_LIGHT,
> > + .address = ZOPT2201_ALS_DATA,
> > + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
> > + BIT(IIO_CHAN_INFO_SCALE),
> > + .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_INT_TIME),
> > + },
> > + {
> > + .type = IIO_INTENSITY,
> > + .modified = 1,
> > + .channel2 = IIO_MOD_LIGHT_UV,
> > + .address = ZOPT2201_UVB_DATA,
> > + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
> > + BIT(IIO_CHAN_INFO_SCALE),
> > + .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_INT_TIME),
> > + },
> > + {
> > + .type = IIO_UVINDEX,
> > + .info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED),
> > + },
> > +};
> > +
> > +static int zopt2201_read_raw(struct iio_dev *indio_dev,
> > + struct iio_chan_spec const *chan,
> > + int *val, int *val2, long mask)
> > +{
> > + struct zopt2201_data *data = iio_priv(indio_dev);
> > + u64 tmp;
> > + int ret;
> > +
> > + switch (mask) {
> > + case IIO_CHAN_INFO_RAW:
> > + ret = zopt2201_read(data, chan->address);
> > + if (ret < 0)
> > + return ret;
> > + *val = ret;
> > + return IIO_VAL_INT;
> > + case IIO_CHAN_INFO_PROCESSED:
> > + ret = zopt2201_read(data, ZOPT2201_UVB_DATA);
> > + if (ret < 0)
> > + return ret;
> > + *val = ret * 18 *
> > + (1 << (20 - zopt2201_resolution[data->res].bits)) /
> > + zopt2201_gain_uvb[data->gain].gain;
> > + return IIO_VAL_INT;
> > + case IIO_CHAN_INFO_SCALE:
> > + switch (chan->address) {
> > + case ZOPT2201_ALS_DATA:
> > + *val = zopt2201_gain_als[data->gain].scale;
> > + break;
> > + case ZOPT2201_UVB_DATA:
> > + *val = zopt2201_gain_uvb[data->gain].scale;
> > + break;
> > + default:
> > + return -EINVAL;
> > + }
> > +
> > + *val2 = 1000000;
> > + *val2 *= (1 << (zopt2201_resolution[data->res].bits - 13));
> > + tmp = div_s64(*val * 1000000ULL, *val2);
> > + *val = div_s64_rem(tmp, 1000000, val2);
> > +
> > + return IIO_VAL_INT_PLUS_MICRO;
> > + case IIO_CHAN_INFO_INT_TIME:
> > + *val = 0;
> > + *val2 = zopt2201_resolution[data->res].us;
> > + return IIO_VAL_INT_PLUS_MICRO;
> > + default:
> > + return -EINVAL;
> > + }
> > +}
> > +
> > +static int zopt2201_set_resolution(struct zopt2201_data *data, u8 res)
> > +{
> > + int ret;
> > +
> > + ret = i2c_smbus_write_byte_data(data->client, ZOPT2201_LS_MEAS_RATE,
> > + (res << ZOPT2201_MEAS_RES_SHIFT) |
> > + data->rate);
> > + if (ret < 0)
> > + return ret;
> > +
> > + data->res = res;
> > +
> > + return 0;
> > +}
> > +
> > +static int zopt2201_write_resolution(struct zopt2201_data *data,
> > + int val, int val2)
> > +{
> > + int i, ret;
> > +
> > + if (val != 0)
> > + return -EINVAL;
> > +
> > + for (i = 0; i < ARRAY_SIZE(zopt2201_resolution); i++)
> > + if (val2 == zopt2201_resolution[i].us) {
> > + mutex_lock(&data->lock);
> > + ret = zopt2201_set_resolution(data, i);
> > + mutex_unlock(&data->lock);
> > + return ret;
> > + }
> > +
> > + return -EINVAL;
> > +}
> > +
> > +static int zopt2201_set_gain(struct zopt2201_data *data, u8 gain)
> > +{
> > + int ret;
> > +
> > + ret = i2c_smbus_write_byte_data(data->client, ZOPT2201_LS_GAIN, gain);
> > + if (ret < 0)
> > + return ret;
> > +
> > + data->gain = gain;
> > +
> > + return 0;
> > +}
> > +
> > +static int zopt2201_write_scale_als_by_idx(struct zopt2201_data *data, int idx)
> > +{
> > + int ret;
> > +
> > + mutex_lock(&data->lock);
> > + ret = zopt2201_set_resolution(data, zopt2201_scale_als[idx].res);
> > + if (ret < 0)
> > + goto unlock;
> > +
> > + ret = zopt2201_set_gain(data, zopt2201_scale_als[idx].gain);
> > +
> > +unlock:
> > + mutex_unlock(&data->lock);
> > + return ret;
> > +}
> > +
> > +static int zopt2201_write_scale_als(struct zopt2201_data *data,
> > + int val, int val2)
> > +{
> > + int i;
> > +
> > + for (i = 0; i < ARRAY_SIZE(zopt2201_scale_als); i++)
> > + if (val == zopt2201_scale_als[i].scale &&
> > + val2 == zopt2201_scale_als[i].uscale) {
> > + return zopt2201_write_scale_als_by_idx(data, i);
> > + }
> > +
> > + return -EINVAL;
> > +}
> > +
> > +static int zopt2201_write_scale_uvb_by_idx(struct zopt2201_data *data, int idx)
> > +{
> > + int ret;
> > +
> > + mutex_lock(&data->lock);
> > + ret = zopt2201_set_resolution(data, zopt2201_scale_als[idx].res);
> > + if (ret < 0)
> > + goto unlock;
> > +
> > + ret = zopt2201_set_gain(data, zopt2201_scale_als[idx].gain);
> > +
> > +unlock:
> > + mutex_unlock(&data->lock);
> > + return ret;
> > +}
> > +
> > +static int zopt2201_write_scale_uvb(struct zopt2201_data *data,
> > + int val, int val2)
> > +{
> > + int i;
> > +
> > + for (i = 0; i < ARRAY_SIZE(zopt2201_scale_uvb); i++)
> > + if (val == zopt2201_scale_uvb[i].scale &&
> > + val2 == zopt2201_scale_uvb[i].uscale)
> > + return zopt2201_write_scale_uvb_by_idx(data, i);
> > +
> > + return -EINVAL;
> > +}
> > +
> > +static int zopt2201_write_raw(struct iio_dev *indio_dev,
> > + struct iio_chan_spec const *chan,
> > + int val, int val2, long mask)
> > +{
> > + struct zopt2201_data *data = iio_priv(indio_dev);
> > +
> > + switch (mask) {
> > + case IIO_CHAN_INFO_INT_TIME:
> > + return zopt2201_write_resolution(data, val, val2);
> > + case IIO_CHAN_INFO_SCALE:
> > + switch (chan->address) {
> > + case ZOPT2201_ALS_DATA:
> > + return zopt2201_write_scale_als(data, val, val2);
> > + case ZOPT2201_UVB_DATA:
> > + return zopt2201_write_scale_uvb(data, val, val2);
> > + default:
> > + return -EINVAL;
> > + }
> > + }
> > +
> > + return -EINVAL;
> > +}
> > +
> > +static ssize_t zopt2201_show_int_time_available(struct device *dev,
> > + struct device_attribute *attr,
> > + char *buf)
> > +{
> > + size_t len = 0;
> > + int i;
> > +
> > + for (i = 0; i < ARRAY_SIZE(zopt2201_resolution); i++)
> > + len += scnprintf(buf + len, PAGE_SIZE - len, "0.%06lu ",
> > + zopt2201_resolution[i].us);
> > + buf[len - 1] = '\n';
> > +
> > + return len;
> > +}
> > +
> > +static IIO_DEV_ATTR_INT_TIME_AVAIL(zopt2201_show_int_time_available);
> > +
> > +static ssize_t zopt2201_show_als_scale_avail(struct device *dev,
> > + struct device_attribute *attr,
> > + char *buf)
> > +{
> > + ssize_t len = 0;
> > + int i;
> > +
> > + for (i = 0; i < ARRAY_SIZE(zopt2201_scale_als); i++)
> > + len += scnprintf(buf + len, PAGE_SIZE - len, "%d.%06u ",
> > + zopt2201_scale_als[i].scale,
> > + zopt2201_scale_als[i].uscale);
> > + buf[len - 1] = '\n';
> > +
> > + return len;
> > +}
> > +
> > +static ssize_t zopt2201_show_uvb_scale_avail(struct device *dev,
> > + struct device_attribute *attr,
> > + char *buf)
> > +{
> > + ssize_t len = 0;
> > + int i;
> > +
> > + for (i = 0; i < ARRAY_SIZE(zopt2201_scale_uvb); i++)
> > + len += scnprintf(buf + len, PAGE_SIZE - len, "%d.%06u ",
> > + zopt2201_scale_uvb[i].scale,
> > + zopt2201_scale_uvb[i].uscale);
> > + buf[len - 1] = '\n';
> > +
> > + return len;
> > +}
> > +
> > +static IIO_DEVICE_ATTR(in_illuminance_scale_available, 0444,
> > + zopt2201_show_als_scale_avail, NULL, 0);
> > +static IIO_DEVICE_ATTR(in_intensity_uv_scale_available, 0444,
> > + zopt2201_show_uvb_scale_avail, NULL, 0);
> > +
> > +static struct attribute *zopt2201_attributes[] = {
> > + &iio_dev_attr_integration_time_available.dev_attr.attr,
> > + &iio_dev_attr_in_illuminance_scale_available.dev_attr.attr,
> > + &iio_dev_attr_in_intensity_uv_scale_available.dev_attr.attr,
> > + NULL
> > +};
> > +
> > +static const struct attribute_group zopt2201_attribute_group = {
> > + .attrs = zopt2201_attributes,
> > +};
> > +
> > +static const struct iio_info zopt2201_info = {
> > + .read_raw = zopt2201_read_raw,
> > + .write_raw = zopt2201_write_raw,
> > + .attrs = &zopt2201_attribute_group,
> > +};
> > +
> > +static int zopt2201_probe(struct i2c_client *client,
> > + const struct i2c_device_id *id)
> > +{
> > + struct zopt2201_data *data;
> > + struct iio_dev *indio_dev;
> > + int ret;
> > +
> > + if (!i2c_check_functionality(client->adapter,
> > + I2C_FUNC_SMBUS_READ_I2C_BLOCK))
> > + return -EOPNOTSUPP;
> > +
> > + ret = i2c_smbus_read_byte_data(client, ZOPT2201_PART_ID);
> > + if (ret < 0)
> > + return ret;
> > + if (ret != ZOPT2201_PART_NUMBER)
> > + return -ENODEV;
> > +
> > + indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data));
> > + if (!indio_dev)
> > + return -ENOMEM;
> > +
> > + data = iio_priv(indio_dev);
> > + i2c_set_clientdata(client, indio_dev);
> > + data->client = client;
> > + mutex_init(&data->lock);
> > +
> > + indio_dev->dev.parent = &client->dev;
> > + indio_dev->info = &zopt2201_info;
> > + indio_dev->channels = zopt2201_channels;
> > + indio_dev->num_channels = ARRAY_SIZE(zopt2201_channels);
> > + indio_dev->name = ZOPT2201_DRV_NAME;
> > + indio_dev->modes = INDIO_DIRECT_MODE;
> > +
> > + data->rate = ZOPT2201_MEAS_FREQ_100MS;
> > + ret = zopt2201_set_resolution(data, ZOPT2201_MEAS_RES_18BIT);
> > + if (ret < 0)
> > + return ret;
> > +
> > + ret = zopt2201_set_gain(data, ZOPT2201_LS_GAIN_3);
> > + if (ret < 0)
> > + return ret;
> > +
> > + return devm_iio_device_register(&client->dev, indio_dev);
> > +}
> > +
> > +static const struct i2c_device_id zopt2201_id[] = {
> > + { "zopt2201", 0 },
> > + { }
> > +};
> > +MODULE_DEVICE_TABLE(i2c, zopt2201_id);
> > +
> > +static struct i2c_driver zopt2201_driver = {
> > + .driver = {
> > + .name = ZOPT2201_DRV_NAME,
> > + },
> > + .probe = zopt2201_probe,
> > + .id_table = zopt2201_id,
> > +};
> > +
> > +module_i2c_driver(zopt2201_driver);
> > +
> > +MODULE_AUTHOR("Peter Meerwald-Stadler <pmeerw@pmeerw.net>");
> > +MODULE_DESCRIPTION("IDT ZOPT2201 ambient light and UV B sensor driver");
> > +MODULE_LICENSE("GPL");
>
> --
> To unsubscribe from this list: send the line "unsubscribe linux-iio" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply [flat|nested] 6+ messages in thread
* Re: [PATCH v2 2/2] Documentation: iio: Fix typo
2017-11-05 23:25 ` [PATCH v2 2/2] Documentation: iio: Fix typo Peter Meerwald-Stadler
@ 2017-11-19 14:55 ` Jonathan Cameron
0 siblings, 0 replies; 6+ messages in thread
From: Jonathan Cameron @ 2017-11-19 14:55 UTC (permalink / raw)
To: Peter Meerwald-Stadler; +Cc: linux-iio
On Mon, 6 Nov 2017 00:25:00 +0100
Peter Meerwald-Stadler <pmeerw@pmeerw.net> wrote:
> Signed-off-by: Peter Meerwald-Stadler <pmeerw@pmeerw.net>
Applied
> ---
> Documentation/ABI/testing/sysfs-bus-iio | 2 +-
> 1 file changed, 1 insertion(+), 1 deletion(-)
>
> diff --git a/Documentation/ABI/testing/sysfs-bus-iio b/Documentation/ABI/testing/sysfs-bus-iio
> index 2e3f919..a478740 100644
> --- a/Documentation/ABI/testing/sysfs-bus-iio
> +++ b/Documentation/ABI/testing/sysfs-bus-iio
> @@ -1290,7 +1290,7 @@ KernelVersion: 3.4
> Contact: linux-iio@vger.kernel.org
> Description:
> Unit-less light intensity. Modifiers both and ir indicate
> - that measurements contains visible and infrared light
> + that measurements contain visible and infrared light
> components or just infrared light, respectively. Modifier uv indicates
> that measurements contain ultraviolet light components.
>
^ permalink raw reply [flat|nested] 6+ messages in thread
end of thread, other threads:[~2017-11-19 14:55 UTC | newest]
Thread overview: 6+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2017-11-05 23:24 [PATCH v2 0/2] ZOPT2210 ambient light and UVB sensor driver Peter Meerwald-Stadler
2017-11-05 23:24 ` [PATCH v2 1/2] iio: light: Add driver for IDT ZOPT2201 ambient light and UVB sensor Peter Meerwald-Stadler
2017-11-11 0:26 ` Jonathan Cameron
2017-11-19 14:54 ` Jonathan Cameron
2017-11-05 23:25 ` [PATCH v2 2/2] Documentation: iio: Fix typo Peter Meerwald-Stadler
2017-11-19 14:55 ` Jonathan Cameron
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).