Update Linux to v5.10.109
Sourced from [1]
[1] https://cdn.kernel.org/pub/linux/kernel/v5.x/linux-5.10.109.tar.xz
Change-Id: I19bca9fc6762d4e63bcf3e4cba88bbe560d9c76c
Signed-off-by: Olivier Deprez <olivier.deprez@arm.com>
diff --git a/drivers/iio/light/Kconfig b/drivers/iio/light/Kconfig
index 42b64b8..917f9be 100644
--- a/drivers/iio/light/Kconfig
+++ b/drivers/iio/light/Kconfig
@@ -32,6 +32,27 @@
This driver can also be built as a module. If so, the module
will be called adjd_s311.
+config ADUX1020
+ tristate "ADUX1020 photometric sensor"
+ select REGMAP_I2C
+ depends on I2C
+ help
+ Say Y here if you want to build a driver for the Analog Devices
+ ADUX1020 photometric sensor.
+
+ To compile this driver as a module, choose M here: the
+ module will be called adux1020.
+
+config AL3010
+ tristate "AL3010 ambient light sensor"
+ depends on I2C
+ help
+ Say Y here if you want to build a driver for the Dyna Image AL3010
+ ambient light sensor.
+
+ To compile this driver as a module, choose M here: the
+ module will be called al3010.
+
config AL3320A
tristate "AL3320A ambient light sensor"
depends on I2C
@@ -65,6 +86,21 @@
To compile this driver as a module, choose M here: the
module will be called apds9960
+config AS73211
+ tristate "AMS AS73211 XYZ color sensor"
+ depends on I2C
+ select IIO_BUFFER
+ select IIO_TRIGGERED_BUFFER
+ help
+ If you say yes here you get support for the AMS AS73211
+ JENCOLOR(R) Digital XYZ Sensor.
+
+ For triggered measurements, you will need an additional trigger driver
+ like IIO_HRTIMER_TRIGGER or IIO_SYSFS_TRIGGER.
+
+ This driver can also be built as a module. If so, the module
+ will be called as73211.
+
config BH1750
tristate "ROHM BH1750 ambient light sensor"
depends on I2C
@@ -148,6 +184,17 @@
To compile this driver as a module, choose M here:
the module will be called cros_ec_light_prox.
+config GP2AP002
+ tristate "Sharp GP2AP002 Proximity/ALS sensor"
+ depends on I2C
+ select REGMAP
+ help
+ Say Y here if you have a Sharp GP2AP002 proximity/ALS combo-chip
+ hooked to an I2C bus.
+
+ To compile this driver as a module, choose M here: the
+ module will be called gp2ap002.
+
config GP2AP020A00F
tristate "Sharp GP2AP020A00F Proximity/ALS sensor"
depends on I2C
@@ -162,6 +209,16 @@
To compile this driver as a module, choose M here: the
module will be called gp2ap020a00f.
+config IQS621_ALS
+ tristate "Azoteq IQS621/622 ambient light sensors"
+ depends on MFD_IQS62X || COMPILE_TEST
+ help
+ Say Y here if you want to build support for the Azoteq IQS621
+ and IQS622 ambient light sensors.
+
+ To compile this driver as a module, choose M here: the module
+ will be called iqs621-als.
+
config SENSORS_ISL29018
tristate "Intersil 29018 light and proximity sensor"
depends on I2C
@@ -199,7 +256,6 @@
config HID_SENSOR_ALS
depends on HID_SENSOR_HUB
select IIO_BUFFER
- select IIO_TRIGGERED_BUFFER
select HID_SENSOR_IIO_COMMON
select HID_SENSOR_IIO_TRIGGER
tristate "HID ALS"
@@ -213,7 +269,6 @@
config HID_SENSOR_PROX
depends on HID_SENSOR_HUB
select IIO_BUFFER
- select IIO_TRIGGERED_BUFFER
select HID_SENSOR_IIO_COMMON
select HID_SENSOR_IIO_TRIGGER
tristate "HID PROX"
@@ -474,6 +529,8 @@
config VCNL4000
tristate "VCNL4000/4010/4020/4200 combined ALS and proximity sensor"
+ select IIO_BUFFER
+ select IIO_TRIGGERED_BUFFER
depends on I2C
help
Say Y here if you want to build a driver for the Vishay VCNL4000,
@@ -497,6 +554,17 @@
To compile this driver as a module, choose M here: the
module will be called vcnl4035.
+config VEML6030
+ tristate "VEML6030 ambient light sensor"
+ select REGMAP_I2C
+ depends on I2C
+ help
+ Say Y here if you want to build a driver for the Vishay VEML6030
+ ambient light sensor (ALS).
+
+ To compile this driver as a module, choose M here: the
+ module will be called veml6030.
+
config VEML6070
tristate "VEML6070 UV A light sensor"
depends on I2C
diff --git a/drivers/iio/light/Makefile b/drivers/iio/light/Makefile
index 00d1f9b..ea376de 100644
--- a/drivers/iio/light/Makefile
+++ b/drivers/iio/light/Makefile
@@ -6,9 +6,12 @@
# When adding new entries keep the list in alphabetical order
obj-$(CONFIG_ACPI_ALS) += acpi-als.o
obj-$(CONFIG_ADJD_S311) += adjd_s311.o
+obj-$(CONFIG_ADUX1020) += adux1020.o
+obj-$(CONFIG_AL3010) += al3010.o
obj-$(CONFIG_AL3320A) += al3320a.o
obj-$(CONFIG_APDS9300) += apds9300.o
obj-$(CONFIG_APDS9960) += apds9960.o
+obj-$(CONFIG_AS73211) += as73211.o
obj-$(CONFIG_BH1750) += bh1750.o
obj-$(CONFIG_BH1780) += bh1780.o
obj-$(CONFIG_CM32181) += cm32181.o
@@ -17,9 +20,11 @@
obj-$(CONFIG_CM3605) += cm3605.o
obj-$(CONFIG_CM36651) += cm36651.o
obj-$(CONFIG_IIO_CROS_EC_LIGHT_PROX) += cros_ec_light_prox.o
+obj-$(CONFIG_GP2AP002) += gp2ap002.o
obj-$(CONFIG_GP2AP020A00F) += gp2ap020a00f.o
obj-$(CONFIG_HID_SENSOR_ALS) += hid-sensor-als.o
obj-$(CONFIG_HID_SENSOR_PROX) += hid-sensor-prox.o
+obj-$(CONFIG_IQS621_ALS) += iqs621-als.o
obj-$(CONFIG_SENSORS_ISL29018) += isl29018.o
obj-$(CONFIG_SENSORS_ISL29028) += isl29028.o
obj-$(CONFIG_ISL29125) += isl29125.o
@@ -48,6 +53,7 @@
obj-$(CONFIG_US5182D) += us5182d.o
obj-$(CONFIG_VCNL4000) += vcnl4000.o
obj-$(CONFIG_VCNL4035) += vcnl4035.o
+obj-$(CONFIG_VEML6030) += veml6030.o
obj-$(CONFIG_VEML6070) += veml6070.o
obj-$(CONFIG_VL6180) += vl6180.o
obj-$(CONFIG_ZOPT2201) += zopt2201.o
diff --git a/drivers/iio/light/acpi-als.c b/drivers/iio/light/acpi-als.c
index 1eafd0b..2be7180 100644
--- a/drivers/iio/light/acpi-als.c
+++ b/drivers/iio/light/acpi-als.c
@@ -178,7 +178,6 @@
mutex_init(&als->lock);
indio_dev->name = ACPI_ALS_DEVICE_NAME;
- indio_dev->dev.parent = &device->dev;
indio_dev->info = &acpi_als_info;
indio_dev->modes = INDIO_BUFFER_SOFTWARE;
indio_dev->channels = acpi_als_channels;
diff --git a/drivers/iio/light/adjd_s311.c b/drivers/iio/light/adjd_s311.c
index d3269cd..17dac8d 100644
--- a/drivers/iio/light/adjd_s311.c
+++ b/drivers/iio/light/adjd_s311.c
@@ -259,7 +259,6 @@
i2c_set_clientdata(client, indio_dev);
data->client = client;
- indio_dev->dev.parent = &client->dev;
indio_dev->info = &adjd_s311_info;
indio_dev->name = ADJD_S311_DRV_NAME;
indio_dev->channels = adjd_s311_channels;
diff --git a/drivers/iio/light/adux1020.c b/drivers/iio/light/adux1020.c
new file mode 100644
index 0000000..9aa2869
--- /dev/null
+++ b/drivers/iio/light/adux1020.c
@@ -0,0 +1,848 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * adux1020.c - Support for Analog Devices ADUX1020 photometric sensor
+ *
+ * Copyright (C) 2019 Linaro Ltd.
+ * Author: Manivannan Sadhasivam <manivannan.sadhasivam@linaro.org>
+ *
+ * TODO: Triggered buffer support
+ */
+
+#include <linux/bitfield.h>
+#include <linux/delay.h>
+#include <linux/err.h>
+#include <linux/i2c.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/regmap.h>
+
+#include <linux/iio/iio.h>
+#include <linux/iio/sysfs.h>
+#include <linux/iio/events.h>
+
+#define ADUX1020_REGMAP_NAME "adux1020_regmap"
+#define ADUX1020_DRV_NAME "adux1020"
+
+/* System registers */
+#define ADUX1020_REG_CHIP_ID 0x08
+#define ADUX1020_REG_SLAVE_ADDRESS 0x09
+
+#define ADUX1020_REG_SW_RESET 0x0f
+#define ADUX1020_REG_INT_ENABLE 0x1c
+#define ADUX1020_REG_INT_POLARITY 0x1d
+#define ADUX1020_REG_PROX_TH_ON1 0x2a
+#define ADUX1020_REG_PROX_TH_OFF1 0x2b
+#define ADUX1020_REG_PROX_TYPE 0x2f
+#define ADUX1020_REG_TEST_MODES_3 0x32
+#define ADUX1020_REG_FORCE_MODE 0x33
+#define ADUX1020_REG_FREQUENCY 0x40
+#define ADUX1020_REG_LED_CURRENT 0x41
+#define ADUX1020_REG_OP_MODE 0x45
+#define ADUX1020_REG_INT_MASK 0x48
+#define ADUX1020_REG_INT_STATUS 0x49
+#define ADUX1020_REG_DATA_BUFFER 0x60
+
+/* Chip ID bits */
+#define ADUX1020_CHIP_ID_MASK GENMASK(11, 0)
+#define ADUX1020_CHIP_ID 0x03fc
+
+#define ADUX1020_SW_RESET BIT(1)
+#define ADUX1020_FIFO_FLUSH BIT(15)
+#define ADUX1020_OP_MODE_MASK GENMASK(3, 0)
+#define ADUX1020_DATA_OUT_MODE_MASK GENMASK(7, 4)
+#define ADUX1020_DATA_OUT_PROX_I FIELD_PREP(ADUX1020_DATA_OUT_MODE_MASK, 1)
+
+#define ADUX1020_MODE_INT_MASK GENMASK(7, 0)
+#define ADUX1020_INT_ENABLE 0x2094
+#define ADUX1020_INT_DISABLE 0x2090
+#define ADUX1020_PROX_INT_ENABLE 0x00f0
+#define ADUX1020_PROX_ON1_INT BIT(0)
+#define ADUX1020_PROX_OFF1_INT BIT(1)
+#define ADUX1020_FIFO_INT_ENABLE 0x7f
+#define ADUX1020_MODE_INT_DISABLE 0xff
+#define ADUX1020_MODE_INT_STATUS_MASK GENMASK(7, 0)
+#define ADUX1020_FIFO_STATUS_MASK GENMASK(15, 8)
+#define ADUX1020_INT_CLEAR 0xff
+#define ADUX1020_PROX_TYPE BIT(15)
+
+#define ADUX1020_INT_PROX_ON1 BIT(0)
+#define ADUX1020_INT_PROX_OFF1 BIT(1)
+
+#define ADUX1020_FORCE_CLOCK_ON 0x0f4f
+#define ADUX1020_FORCE_CLOCK_RESET 0x0040
+#define ADUX1020_ACTIVE_4_STATE 0x0008
+
+#define ADUX1020_PROX_FREQ_MASK GENMASK(7, 4)
+#define ADUX1020_PROX_FREQ(x) FIELD_PREP(ADUX1020_PROX_FREQ_MASK, x)
+
+#define ADUX1020_LED_CURRENT_MASK GENMASK(3, 0)
+#define ADUX1020_LED_PIREF_EN BIT(12)
+
+/* Operating modes */
+enum adux1020_op_modes {
+ ADUX1020_MODE_STANDBY,
+ ADUX1020_MODE_PROX_I,
+ ADUX1020_MODE_PROX_XY,
+ ADUX1020_MODE_GEST,
+ ADUX1020_MODE_SAMPLE,
+ ADUX1020_MODE_FORCE = 0x0e,
+ ADUX1020_MODE_IDLE = 0x0f,
+};
+
+struct adux1020_data {
+ struct i2c_client *client;
+ struct iio_dev *indio_dev;
+ struct mutex lock;
+ struct regmap *regmap;
+};
+
+struct adux1020_mode_data {
+ u8 bytes;
+ u8 buf_len;
+ u16 int_en;
+};
+
+static const struct adux1020_mode_data adux1020_modes[] = {
+ [ADUX1020_MODE_PROX_I] = {
+ .bytes = 2,
+ .buf_len = 1,
+ .int_en = ADUX1020_PROX_INT_ENABLE,
+ },
+};
+
+static const struct regmap_config adux1020_regmap_config = {
+ .name = ADUX1020_REGMAP_NAME,
+ .reg_bits = 8,
+ .val_bits = 16,
+ .max_register = 0x6F,
+ .cache_type = REGCACHE_NONE,
+};
+
+static const struct reg_sequence adux1020_def_conf[] = {
+ { 0x000c, 0x000f },
+ { 0x0010, 0x1010 },
+ { 0x0011, 0x004c },
+ { 0x0012, 0x5f0c },
+ { 0x0013, 0xada5 },
+ { 0x0014, 0x0080 },
+ { 0x0015, 0x0000 },
+ { 0x0016, 0x0600 },
+ { 0x0017, 0x0000 },
+ { 0x0018, 0x2693 },
+ { 0x0019, 0x0004 },
+ { 0x001a, 0x4280 },
+ { 0x001b, 0x0060 },
+ { 0x001c, 0x2094 },
+ { 0x001d, 0x0020 },
+ { 0x001e, 0x0001 },
+ { 0x001f, 0x0100 },
+ { 0x0020, 0x0320 },
+ { 0x0021, 0x0A13 },
+ { 0x0022, 0x0320 },
+ { 0x0023, 0x0113 },
+ { 0x0024, 0x0000 },
+ { 0x0025, 0x2412 },
+ { 0x0026, 0x2412 },
+ { 0x0027, 0x0022 },
+ { 0x0028, 0x0000 },
+ { 0x0029, 0x0300 },
+ { 0x002a, 0x0700 },
+ { 0x002b, 0x0600 },
+ { 0x002c, 0x6000 },
+ { 0x002d, 0x4000 },
+ { 0x002e, 0x0000 },
+ { 0x002f, 0x0000 },
+ { 0x0030, 0x0000 },
+ { 0x0031, 0x0000 },
+ { 0x0032, 0x0040 },
+ { 0x0033, 0x0008 },
+ { 0x0034, 0xE400 },
+ { 0x0038, 0x8080 },
+ { 0x0039, 0x8080 },
+ { 0x003a, 0x2000 },
+ { 0x003b, 0x1f00 },
+ { 0x003c, 0x2000 },
+ { 0x003d, 0x2000 },
+ { 0x003e, 0x0000 },
+ { 0x0040, 0x8069 },
+ { 0x0041, 0x1f2f },
+ { 0x0042, 0x4000 },
+ { 0x0043, 0x0000 },
+ { 0x0044, 0x0008 },
+ { 0x0046, 0x0000 },
+ { 0x0048, 0x00ef },
+ { 0x0049, 0x0000 },
+ { 0x0045, 0x0000 },
+};
+
+static const int adux1020_rates[][2] = {
+ { 0, 100000 },
+ { 0, 200000 },
+ { 0, 500000 },
+ { 1, 0 },
+ { 2, 0 },
+ { 5, 0 },
+ { 10, 0 },
+ { 20, 0 },
+ { 50, 0 },
+ { 100, 0 },
+ { 190, 0 },
+ { 450, 0 },
+ { 820, 0 },
+ { 1400, 0 },
+};
+
+static const int adux1020_led_currents[][2] = {
+ { 0, 25000 },
+ { 0, 40000 },
+ { 0, 55000 },
+ { 0, 70000 },
+ { 0, 85000 },
+ { 0, 100000 },
+ { 0, 115000 },
+ { 0, 130000 },
+ { 0, 145000 },
+ { 0, 160000 },
+ { 0, 175000 },
+ { 0, 190000 },
+ { 0, 205000 },
+ { 0, 220000 },
+ { 0, 235000 },
+ { 0, 250000 },
+};
+
+static int adux1020_flush_fifo(struct adux1020_data *data)
+{
+ int ret;
+
+ /* Force Idle mode */
+ ret = regmap_write(data->regmap, ADUX1020_REG_FORCE_MODE,
+ ADUX1020_ACTIVE_4_STATE);
+ if (ret < 0)
+ return ret;
+
+ ret = regmap_update_bits(data->regmap, ADUX1020_REG_OP_MODE,
+ ADUX1020_OP_MODE_MASK, ADUX1020_MODE_FORCE);
+ if (ret < 0)
+ return ret;
+
+ ret = regmap_update_bits(data->regmap, ADUX1020_REG_OP_MODE,
+ ADUX1020_OP_MODE_MASK, ADUX1020_MODE_IDLE);
+ if (ret < 0)
+ return ret;
+
+ /* Flush FIFO */
+ ret = regmap_write(data->regmap, ADUX1020_REG_TEST_MODES_3,
+ ADUX1020_FORCE_CLOCK_ON);
+ if (ret < 0)
+ return ret;
+
+ ret = regmap_write(data->regmap, ADUX1020_REG_INT_STATUS,
+ ADUX1020_FIFO_FLUSH);
+ if (ret < 0)
+ return ret;
+
+ return regmap_write(data->regmap, ADUX1020_REG_TEST_MODES_3,
+ ADUX1020_FORCE_CLOCK_RESET);
+}
+
+static int adux1020_read_fifo(struct adux1020_data *data, u16 *buf, u8 buf_len)
+{
+ unsigned int regval;
+ int i, ret;
+
+ /* Enable 32MHz clock */
+ ret = regmap_write(data->regmap, ADUX1020_REG_TEST_MODES_3,
+ ADUX1020_FORCE_CLOCK_ON);
+ if (ret < 0)
+ return ret;
+
+ for (i = 0; i < buf_len; i++) {
+ ret = regmap_read(data->regmap, ADUX1020_REG_DATA_BUFFER,
+ ®val);
+ if (ret < 0)
+ return ret;
+
+ buf[i] = regval;
+ }
+
+ /* Set 32MHz clock to be controlled by internal state machine */
+ return regmap_write(data->regmap, ADUX1020_REG_TEST_MODES_3,
+ ADUX1020_FORCE_CLOCK_RESET);
+}
+
+static int adux1020_set_mode(struct adux1020_data *data,
+ enum adux1020_op_modes mode)
+{
+ int ret;
+
+ /* Switch to standby mode before changing the mode */
+ ret = regmap_write(data->regmap, ADUX1020_REG_OP_MODE,
+ ADUX1020_MODE_STANDBY);
+ if (ret < 0)
+ return ret;
+
+ /* Set data out and switch to the desired mode */
+ switch (mode) {
+ case ADUX1020_MODE_PROX_I:
+ ret = regmap_update_bits(data->regmap, ADUX1020_REG_OP_MODE,
+ ADUX1020_DATA_OUT_MODE_MASK,
+ ADUX1020_DATA_OUT_PROX_I);
+ if (ret < 0)
+ return ret;
+
+ ret = regmap_update_bits(data->regmap, ADUX1020_REG_OP_MODE,
+ ADUX1020_OP_MODE_MASK,
+ ADUX1020_MODE_PROX_I);
+ if (ret < 0)
+ return ret;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int adux1020_measure(struct adux1020_data *data,
+ enum adux1020_op_modes mode,
+ u16 *val)
+{
+ unsigned int status;
+ int ret, tries = 50;
+
+ /* Disable INT pin as polling is going to be used */
+ ret = regmap_write(data->regmap, ADUX1020_REG_INT_ENABLE,
+ ADUX1020_INT_DISABLE);
+ if (ret < 0)
+ return ret;
+
+ /* Enable mode interrupt */
+ ret = regmap_update_bits(data->regmap, ADUX1020_REG_INT_MASK,
+ ADUX1020_MODE_INT_MASK,
+ adux1020_modes[mode].int_en);
+ if (ret < 0)
+ return ret;
+
+ while (tries--) {
+ ret = regmap_read(data->regmap, ADUX1020_REG_INT_STATUS,
+ &status);
+ if (ret < 0)
+ return ret;
+
+ status &= ADUX1020_FIFO_STATUS_MASK;
+ if (status >= adux1020_modes[mode].bytes)
+ break;
+ msleep(20);
+ }
+
+ if (tries < 0)
+ return -EIO;
+
+ ret = adux1020_read_fifo(data, val, adux1020_modes[mode].buf_len);
+ if (ret < 0)
+ return ret;
+
+ /* Clear mode interrupt */
+ ret = regmap_write(data->regmap, ADUX1020_REG_INT_STATUS,
+ (~adux1020_modes[mode].int_en));
+ if (ret < 0)
+ return ret;
+
+ /* Disable mode interrupts */
+ return regmap_update_bits(data->regmap, ADUX1020_REG_INT_MASK,
+ ADUX1020_MODE_INT_MASK,
+ ADUX1020_MODE_INT_DISABLE);
+}
+
+static int adux1020_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int *val, int *val2, long mask)
+{
+ struct adux1020_data *data = iio_priv(indio_dev);
+ u16 buf[3];
+ int ret = -EINVAL;
+ unsigned int regval;
+
+ mutex_lock(&data->lock);
+
+ switch (mask) {
+ case IIO_CHAN_INFO_RAW:
+ switch (chan->type) {
+ case IIO_PROXIMITY:
+ ret = adux1020_set_mode(data, ADUX1020_MODE_PROX_I);
+ if (ret < 0)
+ goto fail;
+
+ ret = adux1020_measure(data, ADUX1020_MODE_PROX_I, buf);
+ if (ret < 0)
+ goto fail;
+
+ *val = buf[0];
+ ret = IIO_VAL_INT;
+ break;
+ default:
+ break;
+ }
+ break;
+ case IIO_CHAN_INFO_PROCESSED:
+ switch (chan->type) {
+ case IIO_CURRENT:
+ ret = regmap_read(data->regmap,
+ ADUX1020_REG_LED_CURRENT, ®val);
+ if (ret < 0)
+ goto fail;
+
+ regval = regval & ADUX1020_LED_CURRENT_MASK;
+
+ *val = adux1020_led_currents[regval][0];
+ *val2 = adux1020_led_currents[regval][1];
+
+ ret = IIO_VAL_INT_PLUS_MICRO;
+ break;
+ default:
+ break;
+ }
+ break;
+ case IIO_CHAN_INFO_SAMP_FREQ:
+ switch (chan->type) {
+ case IIO_PROXIMITY:
+ ret = regmap_read(data->regmap, ADUX1020_REG_FREQUENCY,
+ ®val);
+ if (ret < 0)
+ goto fail;
+
+ regval = FIELD_GET(ADUX1020_PROX_FREQ_MASK, regval);
+
+ *val = adux1020_rates[regval][0];
+ *val2 = adux1020_rates[regval][1];
+
+ ret = IIO_VAL_INT_PLUS_MICRO;
+ break;
+ default:
+ break;
+ }
+ break;
+ default:
+ break;
+ }
+
+fail:
+ mutex_unlock(&data->lock);
+
+ return ret;
+};
+
+static inline int adux1020_find_index(const int array[][2], int count, int val,
+ int val2)
+{
+ int i;
+
+ for (i = 0; i < count; i++)
+ if (val == array[i][0] && val2 == array[i][1])
+ return i;
+
+ return -EINVAL;
+}
+
+static int adux1020_write_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int val, int val2, long mask)
+{
+ struct adux1020_data *data = iio_priv(indio_dev);
+ int i, ret = -EINVAL;
+
+ mutex_lock(&data->lock);
+
+ switch (mask) {
+ case IIO_CHAN_INFO_SAMP_FREQ:
+ if (chan->type == IIO_PROXIMITY) {
+ i = adux1020_find_index(adux1020_rates,
+ ARRAY_SIZE(adux1020_rates),
+ val, val2);
+ if (i < 0) {
+ ret = i;
+ goto fail;
+ }
+
+ ret = regmap_update_bits(data->regmap,
+ ADUX1020_REG_FREQUENCY,
+ ADUX1020_PROX_FREQ_MASK,
+ ADUX1020_PROX_FREQ(i));
+ }
+ break;
+ case IIO_CHAN_INFO_PROCESSED:
+ if (chan->type == IIO_CURRENT) {
+ i = adux1020_find_index(adux1020_led_currents,
+ ARRAY_SIZE(adux1020_led_currents),
+ val, val2);
+ if (i < 0) {
+ ret = i;
+ goto fail;
+ }
+
+ ret = regmap_update_bits(data->regmap,
+ ADUX1020_REG_LED_CURRENT,
+ ADUX1020_LED_CURRENT_MASK, i);
+ }
+ break;
+ default:
+ break;
+ }
+
+fail:
+ mutex_unlock(&data->lock);
+
+ return ret;
+}
+
+static int adux1020_write_event_config(struct iio_dev *indio_dev,
+ const struct iio_chan_spec *chan,
+ enum iio_event_type type,
+ enum iio_event_direction dir, int state)
+{
+ struct adux1020_data *data = iio_priv(indio_dev);
+ int ret, mask;
+
+ mutex_lock(&data->lock);
+
+ ret = regmap_write(data->regmap, ADUX1020_REG_INT_ENABLE,
+ ADUX1020_INT_ENABLE);
+ if (ret < 0)
+ goto fail;
+
+ ret = regmap_write(data->regmap, ADUX1020_REG_INT_POLARITY, 0);
+ if (ret < 0)
+ goto fail;
+
+ switch (chan->type) {
+ case IIO_PROXIMITY:
+ if (dir == IIO_EV_DIR_RISING)
+ mask = ADUX1020_PROX_ON1_INT;
+ else
+ mask = ADUX1020_PROX_OFF1_INT;
+
+ if (state)
+ state = 0;
+ else
+ state = mask;
+
+ ret = regmap_update_bits(data->regmap, ADUX1020_REG_INT_MASK,
+ mask, state);
+ if (ret < 0)
+ goto fail;
+
+ /*
+ * Trigger proximity interrupt when the intensity is above
+ * or below threshold
+ */
+ ret = regmap_update_bits(data->regmap, ADUX1020_REG_PROX_TYPE,
+ ADUX1020_PROX_TYPE,
+ ADUX1020_PROX_TYPE);
+ if (ret < 0)
+ goto fail;
+
+ /* Set proximity mode */
+ ret = adux1020_set_mode(data, ADUX1020_MODE_PROX_I);
+ break;
+ default:
+ ret = -EINVAL;
+ break;
+ }
+
+fail:
+ mutex_unlock(&data->lock);
+
+ return ret;
+}
+
+static int adux1020_read_event_config(struct iio_dev *indio_dev,
+ const struct iio_chan_spec *chan,
+ enum iio_event_type type,
+ enum iio_event_direction dir)
+{
+ struct adux1020_data *data = iio_priv(indio_dev);
+ int ret, mask;
+ unsigned int regval;
+
+ switch (chan->type) {
+ case IIO_PROXIMITY:
+ if (dir == IIO_EV_DIR_RISING)
+ mask = ADUX1020_PROX_ON1_INT;
+ else
+ mask = ADUX1020_PROX_OFF1_INT;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ ret = regmap_read(data->regmap, ADUX1020_REG_INT_MASK, ®val);
+ if (ret < 0)
+ return ret;
+
+ return !(regval & mask);
+}
+
+static int adux1020_read_thresh(struct iio_dev *indio_dev,
+ const struct iio_chan_spec *chan,
+ enum iio_event_type type,
+ enum iio_event_direction dir,
+ enum iio_event_info info, int *val, int *val2)
+{
+ struct adux1020_data *data = iio_priv(indio_dev);
+ u8 reg;
+ int ret;
+ unsigned int regval;
+
+ switch (chan->type) {
+ case IIO_PROXIMITY:
+ if (dir == IIO_EV_DIR_RISING)
+ reg = ADUX1020_REG_PROX_TH_ON1;
+ else
+ reg = ADUX1020_REG_PROX_TH_OFF1;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ ret = regmap_read(data->regmap, reg, ®val);
+ if (ret < 0)
+ return ret;
+
+ *val = regval;
+
+ return IIO_VAL_INT;
+}
+
+static int adux1020_write_thresh(struct iio_dev *indio_dev,
+ const struct iio_chan_spec *chan,
+ enum iio_event_type type,
+ enum iio_event_direction dir,
+ enum iio_event_info info, int val, int val2)
+{
+ struct adux1020_data *data = iio_priv(indio_dev);
+ u8 reg;
+
+ switch (chan->type) {
+ case IIO_PROXIMITY:
+ if (dir == IIO_EV_DIR_RISING)
+ reg = ADUX1020_REG_PROX_TH_ON1;
+ else
+ reg = ADUX1020_REG_PROX_TH_OFF1;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ /* Full scale threshold value is 0-65535 */
+ if (val < 0 || val > 65535)
+ return -EINVAL;
+
+ return regmap_write(data->regmap, reg, val);
+}
+
+static const struct iio_event_spec adux1020_proximity_event[] = {
+ {
+ .type = IIO_EV_TYPE_THRESH,
+ .dir = IIO_EV_DIR_RISING,
+ .mask_separate = BIT(IIO_EV_INFO_VALUE) |
+ BIT(IIO_EV_INFO_ENABLE),
+ },
+ {
+ .type = IIO_EV_TYPE_THRESH,
+ .dir = IIO_EV_DIR_FALLING,
+ .mask_separate = BIT(IIO_EV_INFO_VALUE) |
+ BIT(IIO_EV_INFO_ENABLE),
+ },
+};
+
+static const struct iio_chan_spec adux1020_channels[] = {
+ {
+ .type = IIO_PROXIMITY,
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
+ BIT(IIO_CHAN_INFO_SAMP_FREQ),
+ .event_spec = adux1020_proximity_event,
+ .num_event_specs = ARRAY_SIZE(adux1020_proximity_event),
+ },
+ {
+ .type = IIO_CURRENT,
+ .info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED),
+ .extend_name = "led",
+ .output = 1,
+ },
+};
+
+static IIO_CONST_ATTR_SAMP_FREQ_AVAIL(
+ "0.1 0.2 0.5 1 2 5 10 20 50 100 190 450 820 1400");
+
+static struct attribute *adux1020_attributes[] = {
+ &iio_const_attr_sampling_frequency_available.dev_attr.attr,
+ NULL
+};
+
+static const struct attribute_group adux1020_attribute_group = {
+ .attrs = adux1020_attributes,
+};
+
+static const struct iio_info adux1020_info = {
+ .attrs = &adux1020_attribute_group,
+ .read_raw = adux1020_read_raw,
+ .write_raw = adux1020_write_raw,
+ .read_event_config = adux1020_read_event_config,
+ .write_event_config = adux1020_write_event_config,
+ .read_event_value = adux1020_read_thresh,
+ .write_event_value = adux1020_write_thresh,
+};
+
+static irqreturn_t adux1020_interrupt_handler(int irq, void *private)
+{
+ struct iio_dev *indio_dev = private;
+ struct adux1020_data *data = iio_priv(indio_dev);
+ int ret, status;
+
+ ret = regmap_read(data->regmap, ADUX1020_REG_INT_STATUS, &status);
+ if (ret < 0)
+ return IRQ_HANDLED;
+
+ status &= ADUX1020_MODE_INT_STATUS_MASK;
+
+ if (status & ADUX1020_INT_PROX_ON1) {
+ iio_push_event(indio_dev,
+ IIO_UNMOD_EVENT_CODE(IIO_PROXIMITY, 0,
+ IIO_EV_TYPE_THRESH,
+ IIO_EV_DIR_RISING),
+ iio_get_time_ns(indio_dev));
+ }
+
+ if (status & ADUX1020_INT_PROX_OFF1) {
+ iio_push_event(indio_dev,
+ IIO_UNMOD_EVENT_CODE(IIO_PROXIMITY, 0,
+ IIO_EV_TYPE_THRESH,
+ IIO_EV_DIR_FALLING),
+ iio_get_time_ns(indio_dev));
+ }
+
+ regmap_update_bits(data->regmap, ADUX1020_REG_INT_STATUS,
+ ADUX1020_MODE_INT_MASK, ADUX1020_INT_CLEAR);
+
+ return IRQ_HANDLED;
+}
+
+static int adux1020_chip_init(struct adux1020_data *data)
+{
+ struct i2c_client *client = data->client;
+ int ret;
+ unsigned int val;
+
+ ret = regmap_read(data->regmap, ADUX1020_REG_CHIP_ID, &val);
+ if (ret < 0)
+ return ret;
+
+ if ((val & ADUX1020_CHIP_ID_MASK) != ADUX1020_CHIP_ID) {
+ dev_err(&client->dev, "invalid chip id 0x%04x\n", val);
+ return -ENODEV;
+ }
+
+ dev_dbg(&client->dev, "Detected ADUX1020 with chip id: 0x%04x\n", val);
+
+ ret = regmap_update_bits(data->regmap, ADUX1020_REG_SW_RESET,
+ ADUX1020_SW_RESET, ADUX1020_SW_RESET);
+ if (ret < 0)
+ return ret;
+
+ /* Load default configuration */
+ ret = regmap_multi_reg_write(data->regmap, adux1020_def_conf,
+ ARRAY_SIZE(adux1020_def_conf));
+ if (ret < 0)
+ return ret;
+
+ ret = adux1020_flush_fifo(data);
+ if (ret < 0)
+ return ret;
+
+ /* Use LED_IREF for proximity mode */
+ ret = regmap_update_bits(data->regmap, ADUX1020_REG_LED_CURRENT,
+ ADUX1020_LED_PIREF_EN, 0);
+ if (ret < 0)
+ return ret;
+
+ /* Mask all interrupts */
+ return regmap_update_bits(data->regmap, ADUX1020_REG_INT_MASK,
+ ADUX1020_MODE_INT_MASK, ADUX1020_MODE_INT_DISABLE);
+}
+
+static int adux1020_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct adux1020_data *data;
+ struct iio_dev *indio_dev;
+ int ret;
+
+ indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data));
+ if (!indio_dev)
+ return -ENOMEM;
+
+ indio_dev->info = &adux1020_info;
+ indio_dev->name = ADUX1020_DRV_NAME;
+ indio_dev->channels = adux1020_channels;
+ indio_dev->num_channels = ARRAY_SIZE(adux1020_channels);
+ indio_dev->modes = INDIO_DIRECT_MODE;
+
+ data = iio_priv(indio_dev);
+
+ data->regmap = devm_regmap_init_i2c(client, &adux1020_regmap_config);
+ if (IS_ERR(data->regmap)) {
+ dev_err(&client->dev, "regmap initialization failed.\n");
+ return PTR_ERR(data->regmap);
+ }
+
+ data->client = client;
+ data->indio_dev = indio_dev;
+ mutex_init(&data->lock);
+
+ ret = adux1020_chip_init(data);
+ if (ret)
+ return ret;
+
+ if (client->irq) {
+ ret = devm_request_threaded_irq(&client->dev, client->irq,
+ NULL, adux1020_interrupt_handler,
+ IRQF_TRIGGER_HIGH | IRQF_ONESHOT,
+ ADUX1020_DRV_NAME, indio_dev);
+ if (ret) {
+ dev_err(&client->dev, "irq request error %d\n", -ret);
+ return ret;
+ }
+ }
+
+ return devm_iio_device_register(&client->dev, indio_dev);
+}
+
+static const struct i2c_device_id adux1020_id[] = {
+ { "adux1020", 0 },
+ {}
+};
+MODULE_DEVICE_TABLE(i2c, adux1020_id);
+
+static const struct of_device_id adux1020_of_match[] = {
+ { .compatible = "adi,adux1020" },
+ { }
+};
+MODULE_DEVICE_TABLE(of, adux1020_of_match);
+
+static struct i2c_driver adux1020_driver = {
+ .driver = {
+ .name = ADUX1020_DRV_NAME,
+ .of_match_table = adux1020_of_match,
+ },
+ .probe = adux1020_probe,
+ .id_table = adux1020_id,
+};
+module_i2c_driver(adux1020_driver);
+
+MODULE_AUTHOR("Manivannan Sadhasivam <manivannan.sadhasivam@linaro.org>");
+MODULE_DESCRIPTION("ADUX1020 photometric sensor");
+MODULE_LICENSE("GPL");
diff --git a/drivers/iio/light/al3010.c b/drivers/iio/light/al3010.c
new file mode 100644
index 0000000..b4e9924
--- /dev/null
+++ b/drivers/iio/light/al3010.c
@@ -0,0 +1,241 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * AL3010 - Dyna Image Ambient Light Sensor
+ *
+ * Copyright (c) 2014, Intel Corporation.
+ * Copyright (c) 2016, Dyna-Image Corp.
+ * Copyright (c) 2020, David Heidelberg, Michał Mirosław, Dmitry Osipenko
+ *
+ * IIO driver for AL3010 (7-bit I2C slave address 0x1C).
+ *
+ * TODO: interrupt support, thresholds
+ * When the driver will get support for interrupt handling, then interrupt
+ * will need to be disabled before turning sensor OFF in order to avoid
+ * potential races with the interrupt handling.
+ */
+
+#include <linux/bitfield.h>
+#include <linux/i2c.h>
+#include <linux/module.h>
+#include <linux/of.h>
+
+#include <linux/iio/iio.h>
+#include <linux/iio/sysfs.h>
+
+#define AL3010_DRV_NAME "al3010"
+
+#define AL3010_REG_SYSTEM 0x00
+#define AL3010_REG_DATA_LOW 0x0c
+#define AL3010_REG_CONFIG 0x10
+
+#define AL3010_CONFIG_DISABLE 0x00
+#define AL3010_CONFIG_ENABLE 0x01
+
+#define AL3010_GAIN_MASK GENMASK(6,4)
+
+#define AL3010_SCALE_AVAILABLE "1.1872 0.2968 0.0742 0.018"
+
+enum al3xxxx_range {
+ AL3XXX_RANGE_1, /* 77806 lx */
+ AL3XXX_RANGE_2, /* 19542 lx */
+ AL3XXX_RANGE_3, /* 4863 lx */
+ AL3XXX_RANGE_4 /* 1216 lx */
+};
+
+static const int al3010_scales[][2] = {
+ {0, 1187200}, {0, 296800}, {0, 74200}, {0, 18600}
+};
+
+struct al3010_data {
+ struct i2c_client *client;
+};
+
+static const struct iio_chan_spec al3010_channels[] = {
+ {
+ .type = IIO_LIGHT,
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
+ BIT(IIO_CHAN_INFO_SCALE),
+ }
+};
+
+static IIO_CONST_ATTR(in_illuminance_scale_available, AL3010_SCALE_AVAILABLE);
+
+static struct attribute *al3010_attributes[] = {
+ &iio_const_attr_in_illuminance_scale_available.dev_attr.attr,
+ NULL,
+};
+
+static const struct attribute_group al3010_attribute_group = {
+ .attrs = al3010_attributes,
+};
+
+static int al3010_set_pwr(struct i2c_client *client, bool pwr)
+{
+ u8 val = pwr ? AL3010_CONFIG_ENABLE : AL3010_CONFIG_DISABLE;
+ return i2c_smbus_write_byte_data(client, AL3010_REG_SYSTEM, val);
+}
+
+static void al3010_set_pwr_off(void *_data)
+{
+ struct al3010_data *data = _data;
+
+ al3010_set_pwr(data->client, false);
+}
+
+static int al3010_init(struct al3010_data *data)
+{
+ int ret;
+
+ ret = al3010_set_pwr(data->client, true);
+
+ if (ret < 0)
+ return ret;
+
+ ret = i2c_smbus_write_byte_data(data->client, AL3010_REG_CONFIG,
+ FIELD_PREP(AL3010_GAIN_MASK,
+ AL3XXX_RANGE_3));
+ if (ret < 0)
+ return ret;
+
+ return 0;
+}
+
+static int al3010_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan, int *val,
+ int *val2, long mask)
+{
+ struct al3010_data *data = iio_priv(indio_dev);
+ int ret;
+
+ switch (mask) {
+ case IIO_CHAN_INFO_RAW:
+ /*
+ * ALS ADC value is stored in two adjacent registers:
+ * - low byte of output is stored at AL3010_REG_DATA_LOW
+ * - high byte of output is stored at AL3010_REG_DATA_LOW + 1
+ */
+ ret = i2c_smbus_read_word_data(data->client,
+ AL3010_REG_DATA_LOW);
+ if (ret < 0)
+ return ret;
+ *val = ret;
+ return IIO_VAL_INT;
+ case IIO_CHAN_INFO_SCALE:
+ ret = i2c_smbus_read_byte_data(data->client,
+ AL3010_REG_CONFIG);
+ if (ret < 0)
+ return ret;
+
+ ret = FIELD_GET(AL3010_GAIN_MASK, ret);
+ *val = al3010_scales[ret][0];
+ *val2 = al3010_scales[ret][1];
+
+ return IIO_VAL_INT_PLUS_MICRO;
+ }
+ return -EINVAL;
+}
+
+static int al3010_write_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan, int val,
+ int val2, long mask)
+{
+ struct al3010_data *data = iio_priv(indio_dev);
+ int i;
+
+ switch (mask) {
+ case IIO_CHAN_INFO_SCALE:
+ for (i = 0; i < ARRAY_SIZE(al3010_scales); i++) {
+ if (val != al3010_scales[i][0] ||
+ val2 != al3010_scales[i][1])
+ continue;
+
+ return i2c_smbus_write_byte_data(data->client,
+ AL3010_REG_CONFIG,
+ FIELD_PREP(AL3010_GAIN_MASK, i));
+ }
+ break;
+ }
+ return -EINVAL;
+}
+
+static const struct iio_info al3010_info = {
+ .read_raw = al3010_read_raw,
+ .write_raw = al3010_write_raw,
+ .attrs = &al3010_attribute_group,
+};
+
+static int al3010_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct al3010_data *data;
+ struct iio_dev *indio_dev;
+ int ret;
+
+ 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;
+
+ indio_dev->info = &al3010_info;
+ indio_dev->name = AL3010_DRV_NAME;
+ indio_dev->channels = al3010_channels;
+ indio_dev->num_channels = ARRAY_SIZE(al3010_channels);
+ indio_dev->modes = INDIO_DIRECT_MODE;
+
+ ret = al3010_init(data);
+ if (ret < 0) {
+ dev_err(&client->dev, "al3010 chip init failed\n");
+ return ret;
+ }
+
+ ret = devm_add_action_or_reset(&client->dev,
+ al3010_set_pwr_off,
+ data);
+ if (ret < 0)
+ return ret;
+
+ return devm_iio_device_register(&client->dev, indio_dev);
+}
+
+static int __maybe_unused al3010_suspend(struct device *dev)
+{
+ return al3010_set_pwr(to_i2c_client(dev), false);
+}
+
+static int __maybe_unused al3010_resume(struct device *dev)
+{
+ return al3010_set_pwr(to_i2c_client(dev), true);
+}
+
+static SIMPLE_DEV_PM_OPS(al3010_pm_ops, al3010_suspend, al3010_resume);
+
+static const struct i2c_device_id al3010_id[] = {
+ {"al3010", },
+ {}
+};
+MODULE_DEVICE_TABLE(i2c, al3010_id);
+
+static const struct of_device_id al3010_of_match[] = {
+ { .compatible = "dynaimage,al3010", },
+ {},
+};
+MODULE_DEVICE_TABLE(of, al3010_of_match);
+
+static struct i2c_driver al3010_driver = {
+ .driver = {
+ .name = AL3010_DRV_NAME,
+ .of_match_table = al3010_of_match,
+ .pm = &al3010_pm_ops,
+ },
+ .probe = al3010_probe,
+ .id_table = al3010_id,
+};
+module_i2c_driver(al3010_driver);
+
+MODULE_AUTHOR("Daniel Baluta <daniel.baluta@nxp.com>");
+MODULE_AUTHOR("David Heidelberg <david@ixit.cz>");
+MODULE_DESCRIPTION("AL3010 Ambient Light Sensor driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/iio/light/al3320a.c b/drivers/iio/light/al3320a.c
index a21aa99..cc1407c 100644
--- a/drivers/iio/light/al3320a.c
+++ b/drivers/iio/light/al3320a.c
@@ -7,11 +7,15 @@
* IIO driver for AL3320A (7-bit I2C slave address 0x1C).
*
* TODO: interrupt support, thresholds
+ * When the driver will get support for interrupt handling, then interrupt
+ * will need to be disabled before turning sensor OFF in order to avoid
+ * potential races with the interrupt handling.
*/
-#include <linux/module.h>
-#include <linux/init.h>
+#include <linux/bitfield.h>
#include <linux/i2c.h>
+#include <linux/module.h>
+#include <linux/of.h>
#include <linux/iio/iio.h>
#include <linux/iio/sysfs.h>
@@ -36,8 +40,7 @@
#define AL3320A_CONFIG_DISABLE 0x00
#define AL3320A_CONFIG_ENABLE 0x01
-#define AL3320A_GAIN_SHIFT 1
-#define AL3320A_GAIN_MASK (BIT(2) | BIT(1))
+#define AL3320A_GAIN_MASK GENMASK(2, 1)
/* chip params default values */
#define AL3320A_DEFAULT_MEAN_TIME 4
@@ -79,18 +82,31 @@
.attrs = al3320a_attributes,
};
+static int al3320a_set_pwr(struct i2c_client *client, bool pwr)
+{
+ u8 val = pwr ? AL3320A_CONFIG_ENABLE : AL3320A_CONFIG_DISABLE;
+ return i2c_smbus_write_byte_data(client, AL3320A_REG_CONFIG, val);
+}
+
+static void al3320a_set_pwr_off(void *_data)
+{
+ struct al3320a_data *data = _data;
+
+ al3320a_set_pwr(data->client, false);
+}
+
static int al3320a_init(struct al3320a_data *data)
{
int ret;
- /* power on */
- ret = i2c_smbus_write_byte_data(data->client, AL3320A_REG_CONFIG,
- AL3320A_CONFIG_ENABLE);
+ ret = al3320a_set_pwr(data->client, true);
+
if (ret < 0)
return ret;
ret = i2c_smbus_write_byte_data(data->client, AL3320A_REG_CONFIG_RANGE,
- AL3320A_RANGE_3 << AL3320A_GAIN_SHIFT);
+ FIELD_PREP(AL3320A_GAIN_MASK,
+ AL3320A_RANGE_3));
if (ret < 0)
return ret;
@@ -133,7 +149,7 @@
if (ret < 0)
return ret;
- ret = (ret & AL3320A_GAIN_MASK) >> AL3320A_GAIN_SHIFT;
+ ret = FIELD_GET(AL3320A_GAIN_MASK, ret);
*val = al3320a_scales[ret][0];
*val2 = al3320a_scales[ret][1];
@@ -152,11 +168,13 @@
switch (mask) {
case IIO_CHAN_INFO_SCALE:
for (i = 0; i < ARRAY_SIZE(al3320a_scales); i++) {
- if (val == al3320a_scales[i][0] &&
- val2 == al3320a_scales[i][1])
- return i2c_smbus_write_byte_data(data->client,
+ if (val != al3320a_scales[i][0] ||
+ val2 != al3320a_scales[i][1])
+ continue;
+
+ return i2c_smbus_write_byte_data(data->client,
AL3320A_REG_CONFIG_RANGE,
- i << AL3320A_GAIN_SHIFT);
+ FIELD_PREP(AL3320A_GAIN_MASK, i));
}
break;
}
@@ -184,7 +202,6 @@
i2c_set_clientdata(client, indio_dev);
data->client = client;
- indio_dev->dev.parent = &client->dev;
indio_dev->info = &al3320a_info;
indio_dev->name = AL3320A_DRV_NAME;
indio_dev->channels = al3320a_channels;
@@ -196,27 +213,47 @@
dev_err(&client->dev, "al3320a chip init failed\n");
return ret;
}
+
+ ret = devm_add_action_or_reset(&client->dev,
+ al3320a_set_pwr_off,
+ data);
+ if (ret < 0)
+ return ret;
+
return devm_iio_device_register(&client->dev, indio_dev);
}
-static int al3320a_remove(struct i2c_client *client)
+static int __maybe_unused al3320a_suspend(struct device *dev)
{
- return i2c_smbus_write_byte_data(client, AL3320A_REG_CONFIG,
- AL3320A_CONFIG_DISABLE);
+ return al3320a_set_pwr(to_i2c_client(dev), false);
}
+static int __maybe_unused al3320a_resume(struct device *dev)
+{
+ return al3320a_set_pwr(to_i2c_client(dev), true);
+}
+
+static SIMPLE_DEV_PM_OPS(al3320a_pm_ops, al3320a_suspend, al3320a_resume);
+
static const struct i2c_device_id al3320a_id[] = {
{"al3320a", 0},
{}
};
MODULE_DEVICE_TABLE(i2c, al3320a_id);
+static const struct of_device_id al3320a_of_match[] = {
+ { .compatible = "dynaimage,al3320a", },
+ {},
+};
+MODULE_DEVICE_TABLE(of, al3320a_of_match);
+
static struct i2c_driver al3320a_driver = {
.driver = {
.name = AL3320A_DRV_NAME,
+ .of_match_table = al3320a_of_match,
+ .pm = &al3320a_pm_ops,
},
.probe = al3320a_probe,
- .remove = al3320a_remove,
.id_table = al3320a_id,
};
diff --git a/drivers/iio/light/apds9300.c b/drivers/iio/light/apds9300.c
index 856b6c4..baaf202 100644
--- a/drivers/iio/light/apds9300.c
+++ b/drivers/iio/light/apds9300.c
@@ -419,7 +419,6 @@
mutex_init(&data->mutex);
- indio_dev->dev.parent = &client->dev;
indio_dev->channels = apds9300_channels;
indio_dev->num_channels = ARRAY_SIZE(apds9300_channels);
indio_dev->name = APDS9300_DRV_NAME;
diff --git a/drivers/iio/light/apds9960.c b/drivers/iio/light/apds9960.c
index c5dfb9a..9afb3fc 100644
--- a/drivers/iio/light/apds9960.c
+++ b/drivers/iio/light/apds9960.c
@@ -15,7 +15,6 @@
#include <linux/mutex.h>
#include <linux/err.h>
#include <linux/irq.h>
-#include <linux/gpio.h>
#include <linux/i2c.h>
#include <linux/pm_runtime.h>
#include <linux/regmap.h>
@@ -24,7 +23,6 @@
#include <linux/iio/events.h>
#include <linux/iio/kfifo_buf.h>
#include <linux/iio/sysfs.h>
-#include <linux/of_gpio.h>
#define APDS9960_REGMAP_NAME "apds9960_regmap"
#define APDS9960_DRV_NAME "apds9960"
@@ -1003,7 +1001,6 @@
iio_device_attach_buffer(indio_dev, buffer);
- indio_dev->dev.parent = &client->dev;
indio_dev->info = &apds9960_info;
indio_dev->name = APDS9960_DRV_NAME;
indio_dev->channels = apds9960_channels;
diff --git a/drivers/iio/light/as73211.c b/drivers/iio/light/as73211.c
new file mode 100644
index 0000000..7b32dfa
--- /dev/null
+++ b/drivers/iio/light/as73211.c
@@ -0,0 +1,800 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Support for AMS AS73211 JENCOLOR(R) Digital XYZ Sensor
+ *
+ * Author: Christian Eggers <ceggers@arri.de>
+ *
+ * Copyright (c) 2020 ARRI Lighting
+ *
+ * Color light sensor with 16-bit channels for x, y, z and temperature);
+ * 7-bit I2C slave address 0x74 .. 0x77.
+ *
+ * Datasheet: https://ams.com/documents/20143/36005/AS73211_DS000556_3-01.pdf
+ */
+
+#include <linux/bitfield.h>
+#include <linux/completion.h>
+#include <linux/delay.h>
+#include <linux/i2c.h>
+#include <linux/iio/buffer.h>
+#include <linux/iio/iio.h>
+#include <linux/iio/sysfs.h>
+#include <linux/iio/trigger_consumer.h>
+#include <linux/iio/triggered_buffer.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/pm.h>
+
+#define HZ_PER_KHZ 1000
+
+#define AS73211_DRV_NAME "as73211"
+
+/* AS73211 configuration registers */
+#define AS73211_REG_OSR 0x0
+#define AS73211_REG_AGEN 0x2
+#define AS73211_REG_CREG1 0x6
+#define AS73211_REG_CREG2 0x7
+#define AS73211_REG_CREG3 0x8
+
+/* AS73211 output register bank */
+#define AS73211_OUT_OSR_STATUS 0
+#define AS73211_OUT_TEMP 1
+#define AS73211_OUT_MRES1 2
+#define AS73211_OUT_MRES2 3
+#define AS73211_OUT_MRES3 4
+
+#define AS73211_OSR_SS BIT(7)
+#define AS73211_OSR_PD BIT(6)
+#define AS73211_OSR_SW_RES BIT(3)
+#define AS73211_OSR_DOS_MASK GENMASK(2, 0)
+#define AS73211_OSR_DOS_CONFIG FIELD_PREP(AS73211_OSR_DOS_MASK, 0x2)
+#define AS73211_OSR_DOS_MEASURE FIELD_PREP(AS73211_OSR_DOS_MASK, 0x3)
+
+#define AS73211_AGEN_DEVID_MASK GENMASK(7, 4)
+#define AS73211_AGEN_DEVID(x) FIELD_PREP(AS73211_AGEN_DEVID_MASK, (x))
+#define AS73211_AGEN_MUT_MASK GENMASK(3, 0)
+#define AS73211_AGEN_MUT(x) FIELD_PREP(AS73211_AGEN_MUT_MASK, (x))
+
+#define AS73211_CREG1_GAIN_MASK GENMASK(7, 4)
+#define AS73211_CREG1_GAIN_1 11
+#define AS73211_CREG1_TIME_MASK GENMASK(3, 0)
+
+#define AS73211_CREG3_CCLK_MASK GENMASK(1, 0)
+
+#define AS73211_OSR_STATUS_OUTCONVOF BIT(15)
+#define AS73211_OSR_STATUS_MRESOF BIT(14)
+#define AS73211_OSR_STATUS_ADCOF BIT(13)
+#define AS73211_OSR_STATUS_LDATA BIT(12)
+#define AS73211_OSR_STATUS_NDATA BIT(11)
+#define AS73211_OSR_STATUS_NOTREADY BIT(10)
+
+#define AS73211_SAMPLE_FREQ_BASE 1024000
+
+#define AS73211_SAMPLE_TIME_NUM 15
+#define AS73211_SAMPLE_TIME_MAX_MS BIT(AS73211_SAMPLE_TIME_NUM - 1)
+
+/* Available sample frequencies are 1.024MHz multiplied by powers of two. */
+static const int as73211_samp_freq_avail[] = {
+ AS73211_SAMPLE_FREQ_BASE * 1,
+ AS73211_SAMPLE_FREQ_BASE * 2,
+ AS73211_SAMPLE_FREQ_BASE * 4,
+ AS73211_SAMPLE_FREQ_BASE * 8,
+};
+
+static const int as73211_hardwaregain_avail[] = {
+ 1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024, 2048,
+};
+
+/**
+ * struct as73211_data - Instance data for one AS73211
+ * @client: I2C client.
+ * @osr: Cached Operational State Register.
+ * @creg1: Cached Configuration Register 1.
+ * @creg2: Cached Configuration Register 2.
+ * @creg3: Cached Configuration Register 3.
+ * @mutex: Keeps cached registers in sync with the device.
+ * @completion: Completion to wait for interrupt.
+ * @int_time_avail: Available integration times (depend on sampling frequency).
+ */
+struct as73211_data {
+ struct i2c_client *client;
+ u8 osr;
+ u8 creg1;
+ u8 creg2;
+ u8 creg3;
+ struct mutex mutex;
+ struct completion completion;
+ int int_time_avail[AS73211_SAMPLE_TIME_NUM * 2];
+};
+
+#define AS73211_COLOR_CHANNEL(_color, _si, _addr) { \
+ .type = IIO_INTENSITY, \
+ .modified = 1, \
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE), \
+ .info_mask_shared_by_type = \
+ BIT(IIO_CHAN_INFO_SAMP_FREQ) | \
+ BIT(IIO_CHAN_INFO_HARDWAREGAIN) | \
+ BIT(IIO_CHAN_INFO_INT_TIME), \
+ .info_mask_shared_by_type_available = \
+ BIT(IIO_CHAN_INFO_SAMP_FREQ) | \
+ BIT(IIO_CHAN_INFO_HARDWAREGAIN) | \
+ BIT(IIO_CHAN_INFO_INT_TIME), \
+ .channel2 = IIO_MOD_##_color, \
+ .address = _addr, \
+ .scan_index = _si, \
+ .scan_type = { \
+ .sign = 'u', \
+ .realbits = 16, \
+ .storagebits = 16, \
+ .endianness = IIO_LE, \
+ }, \
+}
+
+#define AS73211_OFFSET_TEMP_INT (-66)
+#define AS73211_OFFSET_TEMP_MICRO 900000
+#define AS73211_SCALE_TEMP_INT 0
+#define AS73211_SCALE_TEMP_MICRO 50000
+
+#define AS73211_SCALE_X 277071108 /* nW/m^2 */
+#define AS73211_SCALE_Y 298384270 /* nW/m^2 */
+#define AS73211_SCALE_Z 160241927 /* nW/m^2 */
+
+/* Channel order MUST match devices result register order */
+#define AS73211_SCAN_INDEX_TEMP 0
+#define AS73211_SCAN_INDEX_X 1
+#define AS73211_SCAN_INDEX_Y 2
+#define AS73211_SCAN_INDEX_Z 3
+#define AS73211_SCAN_INDEX_TS 4
+
+#define AS73211_SCAN_MASK_COLOR ( \
+ BIT(AS73211_SCAN_INDEX_X) | \
+ BIT(AS73211_SCAN_INDEX_Y) | \
+ BIT(AS73211_SCAN_INDEX_Z))
+
+#define AS73211_SCAN_MASK_ALL ( \
+ BIT(AS73211_SCAN_INDEX_TEMP) | \
+ AS73211_SCAN_MASK_COLOR)
+
+static const struct iio_chan_spec as73211_channels[] = {
+ {
+ .type = IIO_TEMP,
+ .info_mask_separate =
+ BIT(IIO_CHAN_INFO_RAW) |
+ BIT(IIO_CHAN_INFO_OFFSET) |
+ BIT(IIO_CHAN_INFO_SCALE),
+ .address = AS73211_OUT_TEMP,
+ .scan_index = AS73211_SCAN_INDEX_TEMP,
+ .scan_type = {
+ .sign = 'u',
+ .realbits = 16,
+ .storagebits = 16,
+ .endianness = IIO_LE,
+ }
+ },
+ AS73211_COLOR_CHANNEL(X, AS73211_SCAN_INDEX_X, AS73211_OUT_MRES1),
+ AS73211_COLOR_CHANNEL(Y, AS73211_SCAN_INDEX_Y, AS73211_OUT_MRES2),
+ AS73211_COLOR_CHANNEL(Z, AS73211_SCAN_INDEX_Z, AS73211_OUT_MRES3),
+ IIO_CHAN_SOFT_TIMESTAMP(AS73211_SCAN_INDEX_TS),
+};
+
+static unsigned int as73211_integration_time_1024cyc(struct as73211_data *data)
+{
+ /*
+ * Return integration time in units of 1024 clock cycles. Integration time
+ * in CREG1 is in powers of 2 (x 1024 cycles).
+ */
+ return BIT(FIELD_GET(AS73211_CREG1_TIME_MASK, data->creg1));
+}
+
+static unsigned int as73211_integration_time_us(struct as73211_data *data,
+ unsigned int integration_time_1024cyc)
+{
+ /*
+ * f_samp is configured in CREG3 in powers of 2 (x 1.024 MHz)
+ * t_cycl is configured in CREG1 in powers of 2 (x 1024 cycles)
+ * t_int_us = 1 / (f_samp) * t_cycl * US_PER_SEC
+ * = 1 / (2^CREG3_CCLK * 1,024,000) * 2^CREG1_CYCLES * 1,024 * US_PER_SEC
+ * = 2^(-CREG3_CCLK) * 2^CREG1_CYCLES * 1,000
+ * In order to get rid of negative exponents, we extend the "fraction"
+ * by 2^3 (CREG3_CCLK,max = 3)
+ * t_int_us = 2^(3-CREG3_CCLK) * 2^CREG1_CYCLES * 125
+ */
+ return BIT(3 - FIELD_GET(AS73211_CREG3_CCLK_MASK, data->creg3)) *
+ integration_time_1024cyc * 125;
+}
+
+static void as73211_integration_time_calc_avail(struct as73211_data *data)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(data->int_time_avail) / 2; i++) {
+ unsigned int time_us = as73211_integration_time_us(data, BIT(i));
+
+ data->int_time_avail[i * 2 + 0] = time_us / USEC_PER_SEC;
+ data->int_time_avail[i * 2 + 1] = time_us % USEC_PER_SEC;
+ }
+}
+
+static unsigned int as73211_gain(struct as73211_data *data)
+{
+ /* gain can be calculated from CREG1 as 2^(11 - CREG1_GAIN) */
+ return BIT(AS73211_CREG1_GAIN_1 - FIELD_GET(AS73211_CREG1_GAIN_MASK, data->creg1));
+}
+
+/* must be called with as73211_data::mutex held. */
+static int as73211_req_data(struct as73211_data *data)
+{
+ unsigned int time_us = as73211_integration_time_us(data,
+ as73211_integration_time_1024cyc(data));
+ struct device *dev = &data->client->dev;
+ union i2c_smbus_data smbus_data;
+ u16 osr_status;
+ int ret;
+
+ if (data->client->irq)
+ reinit_completion(&data->completion);
+
+ /*
+ * During measurement, there should be no traffic on the i2c bus as the
+ * electrical noise would disturb the measurement process.
+ */
+ i2c_lock_bus(data->client->adapter, I2C_LOCK_SEGMENT);
+
+ data->osr &= ~AS73211_OSR_DOS_MASK;
+ data->osr |= AS73211_OSR_DOS_MEASURE | AS73211_OSR_SS;
+
+ smbus_data.byte = data->osr;
+ ret = __i2c_smbus_xfer(data->client->adapter, data->client->addr,
+ data->client->flags, I2C_SMBUS_WRITE,
+ AS73211_REG_OSR, I2C_SMBUS_BYTE_DATA, &smbus_data);
+ if (ret < 0) {
+ i2c_unlock_bus(data->client->adapter, I2C_LOCK_SEGMENT);
+ return ret;
+ }
+
+ /*
+ * Reset AS73211_OSR_SS (is self clearing) in order to avoid unintentional
+ * triggering of further measurements later.
+ */
+ data->osr &= ~AS73211_OSR_SS;
+
+ /*
+ * Add 33% extra margin for the timeout. fclk,min = fclk,typ - 27%.
+ */
+ time_us += time_us / 3;
+ if (data->client->irq) {
+ ret = wait_for_completion_timeout(&data->completion, usecs_to_jiffies(time_us));
+ if (!ret) {
+ dev_err(dev, "timeout waiting for READY IRQ\n");
+ i2c_unlock_bus(data->client->adapter, I2C_LOCK_SEGMENT);
+ return -ETIMEDOUT;
+ }
+ } else {
+ /* Wait integration time */
+ usleep_range(time_us, 2 * time_us);
+ }
+
+ i2c_unlock_bus(data->client->adapter, I2C_LOCK_SEGMENT);
+
+ ret = i2c_smbus_read_word_data(data->client, AS73211_OUT_OSR_STATUS);
+ if (ret < 0)
+ return ret;
+
+ osr_status = ret;
+ if (osr_status != (AS73211_OSR_DOS_MEASURE | AS73211_OSR_STATUS_NDATA)) {
+ if (osr_status & AS73211_OSR_SS) {
+ dev_err(dev, "%s() Measurement has not stopped\n", __func__);
+ return -ETIME;
+ }
+ if (osr_status & AS73211_OSR_STATUS_NOTREADY) {
+ dev_err(dev, "%s() Data is not ready\n", __func__);
+ return -ENODATA;
+ }
+ if (!(osr_status & AS73211_OSR_STATUS_NDATA)) {
+ dev_err(dev, "%s() No new data available\n", __func__);
+ return -ENODATA;
+ }
+ if (osr_status & AS73211_OSR_STATUS_LDATA) {
+ dev_err(dev, "%s() Result buffer overrun\n", __func__);
+ return -ENOBUFS;
+ }
+ if (osr_status & AS73211_OSR_STATUS_ADCOF) {
+ dev_err(dev, "%s() ADC overflow\n", __func__);
+ return -EOVERFLOW;
+ }
+ if (osr_status & AS73211_OSR_STATUS_MRESOF) {
+ dev_err(dev, "%s() Measurement result overflow\n", __func__);
+ return -EOVERFLOW;
+ }
+ if (osr_status & AS73211_OSR_STATUS_OUTCONVOF) {
+ dev_err(dev, "%s() Timer overflow\n", __func__);
+ return -EOVERFLOW;
+ }
+ dev_err(dev, "%s() Unexpected status value\n", __func__);
+ return -EIO;
+ }
+
+ return 0;
+}
+
+static int as73211_read_raw(struct iio_dev *indio_dev, struct iio_chan_spec const *chan,
+ int *val, int *val2, long mask)
+{
+ struct as73211_data *data = iio_priv(indio_dev);
+
+ switch (mask) {
+ case IIO_CHAN_INFO_RAW: {
+ int ret;
+
+ ret = iio_device_claim_direct_mode(indio_dev);
+ if (ret < 0)
+ return ret;
+
+ ret = as73211_req_data(data);
+ if (ret < 0) {
+ iio_device_release_direct_mode(indio_dev);
+ return ret;
+ }
+
+ ret = i2c_smbus_read_word_data(data->client, chan->address);
+ iio_device_release_direct_mode(indio_dev);
+ if (ret < 0)
+ return ret;
+
+ *val = ret;
+ return IIO_VAL_INT;
+ }
+ case IIO_CHAN_INFO_OFFSET:
+ *val = AS73211_OFFSET_TEMP_INT;
+ *val2 = AS73211_OFFSET_TEMP_MICRO;
+ return IIO_VAL_INT_PLUS_MICRO;
+
+ case IIO_CHAN_INFO_SCALE:
+ switch (chan->type) {
+ case IIO_TEMP:
+ *val = AS73211_SCALE_TEMP_INT;
+ *val2 = AS73211_SCALE_TEMP_MICRO;
+ return IIO_VAL_INT_PLUS_MICRO;
+
+ case IIO_INTENSITY: {
+ unsigned int scale;
+
+ switch (chan->channel2) {
+ case IIO_MOD_X:
+ scale = AS73211_SCALE_X;
+ break;
+ case IIO_MOD_Y:
+ scale = AS73211_SCALE_Y;
+ break;
+ case IIO_MOD_Z:
+ scale = AS73211_SCALE_Z;
+ break;
+ default:
+ return -EINVAL;
+ }
+ scale /= as73211_gain(data);
+ scale /= as73211_integration_time_1024cyc(data);
+ *val = scale;
+ return IIO_VAL_INT;
+
+ default:
+ return -EINVAL;
+ }}
+
+ case IIO_CHAN_INFO_SAMP_FREQ:
+ /* f_samp is configured in CREG3 in powers of 2 (x 1.024 MHz) */
+ *val = BIT(FIELD_GET(AS73211_CREG3_CCLK_MASK, data->creg3)) *
+ AS73211_SAMPLE_FREQ_BASE;
+ return IIO_VAL_INT;
+
+ case IIO_CHAN_INFO_HARDWAREGAIN:
+ *val = as73211_gain(data);
+ return IIO_VAL_INT;
+
+ case IIO_CHAN_INFO_INT_TIME: {
+ unsigned int time_us;
+
+ mutex_lock(&data->mutex);
+ time_us = as73211_integration_time_us(data, as73211_integration_time_1024cyc(data));
+ mutex_unlock(&data->mutex);
+ *val = time_us / USEC_PER_SEC;
+ *val2 = time_us % USEC_PER_SEC;
+ return IIO_VAL_INT_PLUS_MICRO;
+
+ default:
+ return -EINVAL;
+ }}
+}
+
+static int as73211_read_avail(struct iio_dev *indio_dev, struct iio_chan_spec const *chan,
+ const int **vals, int *type, int *length, long mask)
+{
+ struct as73211_data *data = iio_priv(indio_dev);
+
+ switch (mask) {
+ case IIO_CHAN_INFO_SAMP_FREQ:
+ *length = ARRAY_SIZE(as73211_samp_freq_avail);
+ *vals = as73211_samp_freq_avail;
+ *type = IIO_VAL_INT;
+ return IIO_AVAIL_LIST;
+
+ case IIO_CHAN_INFO_HARDWAREGAIN:
+ *length = ARRAY_SIZE(as73211_hardwaregain_avail);
+ *vals = as73211_hardwaregain_avail;
+ *type = IIO_VAL_INT;
+ return IIO_AVAIL_LIST;
+
+ case IIO_CHAN_INFO_INT_TIME:
+ *length = ARRAY_SIZE(data->int_time_avail);
+ *vals = data->int_time_avail;
+ *type = IIO_VAL_INT_PLUS_MICRO;
+ return IIO_AVAIL_LIST;
+
+ default:
+ return -EINVAL;
+ }
+}
+
+static int _as73211_write_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan __always_unused,
+ int val, int val2, long mask)
+{
+ struct as73211_data *data = iio_priv(indio_dev);
+ int ret;
+
+ switch (mask) {
+ case IIO_CHAN_INFO_SAMP_FREQ: {
+ int reg_bits, freq_kHz = val / HZ_PER_KHZ; /* 1024, 2048, ... */
+
+ /* val must be 1024 * 2^x */
+ if (val < 0 || (freq_kHz * HZ_PER_KHZ) != val ||
+ !is_power_of_2(freq_kHz) || val2)
+ return -EINVAL;
+
+ /* f_samp is configured in CREG3 in powers of 2 (x 1.024 MHz (=2^10)) */
+ reg_bits = ilog2(freq_kHz) - 10;
+ if (!FIELD_FIT(AS73211_CREG3_CCLK_MASK, reg_bits))
+ return -EINVAL;
+
+ data->creg3 &= ~AS73211_CREG3_CCLK_MASK;
+ data->creg3 |= FIELD_PREP(AS73211_CREG3_CCLK_MASK, reg_bits);
+ as73211_integration_time_calc_avail(data);
+
+ ret = i2c_smbus_write_byte_data(data->client, AS73211_REG_CREG3, data->creg3);
+ if (ret < 0)
+ return ret;
+
+ return 0;
+ }
+ case IIO_CHAN_INFO_HARDWAREGAIN: {
+ unsigned int reg_bits;
+
+ if (val < 0 || !is_power_of_2(val) || val2)
+ return -EINVAL;
+
+ /* gain can be calculated from CREG1 as 2^(11 - CREG1_GAIN) */
+ reg_bits = AS73211_CREG1_GAIN_1 - ilog2(val);
+ if (!FIELD_FIT(AS73211_CREG1_GAIN_MASK, reg_bits))
+ return -EINVAL;
+
+ data->creg1 &= ~AS73211_CREG1_GAIN_MASK;
+ data->creg1 |= FIELD_PREP(AS73211_CREG1_GAIN_MASK, reg_bits);
+
+ ret = i2c_smbus_write_byte_data(data->client, AS73211_REG_CREG1, data->creg1);
+ if (ret < 0)
+ return ret;
+
+ return 0;
+ }
+ case IIO_CHAN_INFO_INT_TIME: {
+ int val_us = val * USEC_PER_SEC + val2;
+ int time_ms;
+ int reg_bits;
+
+ /* f_samp is configured in CREG3 in powers of 2 (x 1.024 MHz) */
+ int f_samp_1_024mhz = BIT(FIELD_GET(AS73211_CREG3_CCLK_MASK, data->creg3));
+
+ /*
+ * time_ms = time_us * US_PER_MS * f_samp_1_024mhz / MHZ_PER_HZ
+ * = time_us * f_samp_1_024mhz / 1000
+ */
+ time_ms = (val_us * f_samp_1_024mhz) / 1000; /* 1 ms, 2 ms, ... (power of two) */
+ if (time_ms < 0 || !is_power_of_2(time_ms) || time_ms > AS73211_SAMPLE_TIME_MAX_MS)
+ return -EINVAL;
+
+ reg_bits = ilog2(time_ms);
+ if (!FIELD_FIT(AS73211_CREG1_TIME_MASK, reg_bits))
+ return -EINVAL; /* not possible due to previous tests */
+
+ data->creg1 &= ~AS73211_CREG1_TIME_MASK;
+ data->creg1 |= FIELD_PREP(AS73211_CREG1_TIME_MASK, reg_bits);
+
+ ret = i2c_smbus_write_byte_data(data->client, AS73211_REG_CREG1, data->creg1);
+ if (ret < 0)
+ return ret;
+
+ return 0;
+
+ default:
+ return -EINVAL;
+ }}
+}
+
+static int as73211_write_raw(struct iio_dev *indio_dev, struct iio_chan_spec const *chan,
+ int val, int val2, long mask)
+{
+ struct as73211_data *data = iio_priv(indio_dev);
+ int ret;
+
+ mutex_lock(&data->mutex);
+
+ ret = iio_device_claim_direct_mode(indio_dev);
+ if (ret < 0)
+ goto error_unlock;
+
+ /* Need to switch to config mode ... */
+ if ((data->osr & AS73211_OSR_DOS_MASK) != AS73211_OSR_DOS_CONFIG) {
+ data->osr &= ~AS73211_OSR_DOS_MASK;
+ data->osr |= AS73211_OSR_DOS_CONFIG;
+
+ ret = i2c_smbus_write_byte_data(data->client, AS73211_REG_OSR, data->osr);
+ if (ret < 0)
+ goto error_release;
+ }
+
+ ret = _as73211_write_raw(indio_dev, chan, val, val2, mask);
+
+error_release:
+ iio_device_release_direct_mode(indio_dev);
+error_unlock:
+ mutex_unlock(&data->mutex);
+ return ret;
+}
+
+static irqreturn_t as73211_ready_handler(int irq __always_unused, void *priv)
+{
+ struct as73211_data *data = iio_priv(priv);
+
+ complete(&data->completion);
+
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t as73211_trigger_handler(int irq __always_unused, void *p)
+{
+ struct iio_poll_func *pf = p;
+ struct iio_dev *indio_dev = pf->indio_dev;
+ struct as73211_data *data = iio_priv(indio_dev);
+ struct {
+ __le16 chan[4];
+ s64 ts __aligned(8);
+ } scan;
+ int data_result, ret;
+
+ mutex_lock(&data->mutex);
+
+ data_result = as73211_req_data(data);
+ if (data_result < 0 && data_result != -EOVERFLOW)
+ goto done; /* don't push any data for errors other than EOVERFLOW */
+
+ if (*indio_dev->active_scan_mask == AS73211_SCAN_MASK_ALL) {
+ /* Optimization for reading all (color + temperature) channels */
+ u8 addr = as73211_channels[0].address;
+ struct i2c_msg msgs[] = {
+ {
+ .addr = data->client->addr,
+ .flags = 0,
+ .len = 1,
+ .buf = &addr,
+ },
+ {
+ .addr = data->client->addr,
+ .flags = I2C_M_RD,
+ .len = sizeof(scan.chan),
+ .buf = (u8 *)&scan.chan,
+ },
+ };
+
+ ret = i2c_transfer(data->client->adapter, msgs, ARRAY_SIZE(msgs));
+ if (ret < 0)
+ goto done;
+ } else {
+ /* Optimization for reading only color channels */
+
+ /* AS73211 starts reading at address 2 */
+ ret = i2c_master_recv(data->client,
+ (char *)&scan.chan[1], 3 * sizeof(scan.chan[1]));
+ if (ret < 0)
+ goto done;
+ }
+
+ if (data_result) {
+ /*
+ * Saturate all channels (in case of overflows). Temperature channel
+ * is not affected by overflows.
+ */
+ scan.chan[1] = cpu_to_le16(U16_MAX);
+ scan.chan[2] = cpu_to_le16(U16_MAX);
+ scan.chan[3] = cpu_to_le16(U16_MAX);
+ }
+
+ iio_push_to_buffers_with_timestamp(indio_dev, &scan, iio_get_time_ns(indio_dev));
+
+done:
+ mutex_unlock(&data->mutex);
+ iio_trigger_notify_done(indio_dev->trig);
+
+ return IRQ_HANDLED;
+}
+
+static const struct iio_info as73211_info = {
+ .read_raw = as73211_read_raw,
+ .read_avail = as73211_read_avail,
+ .write_raw = as73211_write_raw,
+};
+
+static int as73211_power(struct iio_dev *indio_dev, bool state)
+{
+ struct as73211_data *data = iio_priv(indio_dev);
+ int ret;
+
+ mutex_lock(&data->mutex);
+
+ if (state)
+ data->osr &= ~AS73211_OSR_PD;
+ else
+ data->osr |= AS73211_OSR_PD;
+
+ ret = i2c_smbus_write_byte_data(data->client, AS73211_REG_OSR, data->osr);
+
+ mutex_unlock(&data->mutex);
+
+ if (ret < 0)
+ return ret;
+
+ return 0;
+}
+
+static void as73211_power_disable(void *data)
+{
+ struct iio_dev *indio_dev = data;
+
+ as73211_power(indio_dev, false);
+}
+
+static int as73211_probe(struct i2c_client *client)
+{
+ struct device *dev = &client->dev;
+ struct as73211_data *data;
+ struct iio_dev *indio_dev;
+ int ret;
+
+ indio_dev = devm_iio_device_alloc(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->mutex);
+ init_completion(&data->completion);
+
+ indio_dev->info = &as73211_info;
+ indio_dev->name = AS73211_DRV_NAME;
+ indio_dev->channels = as73211_channels;
+ indio_dev->num_channels = ARRAY_SIZE(as73211_channels);
+ indio_dev->modes = INDIO_DIRECT_MODE;
+
+ ret = i2c_smbus_read_byte_data(data->client, AS73211_REG_OSR);
+ if (ret < 0)
+ return ret;
+ data->osr = ret;
+
+ /* reset device */
+ data->osr |= AS73211_OSR_SW_RES;
+ ret = i2c_smbus_write_byte_data(data->client, AS73211_REG_OSR, data->osr);
+ if (ret < 0)
+ return ret;
+
+ ret = i2c_smbus_read_byte_data(data->client, AS73211_REG_OSR);
+ if (ret < 0)
+ return ret;
+ data->osr = ret;
+
+ /*
+ * Reading AGEN is only possible after reset (AGEN is not available if
+ * device is in measurement mode).
+ */
+ ret = i2c_smbus_read_byte_data(data->client, AS73211_REG_AGEN);
+ if (ret < 0)
+ return ret;
+
+ /* At the time of writing this driver, only DEVID 2 and MUT 1 are known. */
+ if ((ret & AS73211_AGEN_DEVID_MASK) != AS73211_AGEN_DEVID(2) ||
+ (ret & AS73211_AGEN_MUT_MASK) != AS73211_AGEN_MUT(1))
+ return -ENODEV;
+
+ ret = i2c_smbus_read_byte_data(data->client, AS73211_REG_CREG1);
+ if (ret < 0)
+ return ret;
+ data->creg1 = ret;
+
+ ret = i2c_smbus_read_byte_data(data->client, AS73211_REG_CREG2);
+ if (ret < 0)
+ return ret;
+ data->creg2 = ret;
+
+ ret = i2c_smbus_read_byte_data(data->client, AS73211_REG_CREG3);
+ if (ret < 0)
+ return ret;
+ data->creg3 = ret;
+ as73211_integration_time_calc_avail(data);
+
+ ret = as73211_power(indio_dev, true);
+ if (ret < 0)
+ return ret;
+
+ ret = devm_add_action_or_reset(dev, as73211_power_disable, indio_dev);
+ if (ret)
+ return ret;
+
+ ret = devm_iio_triggered_buffer_setup(dev, indio_dev, NULL, as73211_trigger_handler, NULL);
+ if (ret)
+ return ret;
+
+ if (client->irq) {
+ ret = devm_request_threaded_irq(&client->dev, client->irq,
+ NULL,
+ as73211_ready_handler,
+ IRQF_ONESHOT,
+ client->name, indio_dev);
+ if (ret)
+ return ret;
+ }
+
+ return devm_iio_device_register(dev, indio_dev);
+}
+
+static int __maybe_unused as73211_suspend(struct device *dev)
+{
+ struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev));
+
+ return as73211_power(indio_dev, false);
+}
+
+static int __maybe_unused as73211_resume(struct device *dev)
+{
+ struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev));
+
+ return as73211_power(indio_dev, true);
+}
+
+static SIMPLE_DEV_PM_OPS(as73211_pm_ops, as73211_suspend, as73211_resume);
+
+static const struct of_device_id as73211_of_match[] = {
+ { .compatible = "ams,as73211" },
+ { }
+};
+MODULE_DEVICE_TABLE(of, as73211_of_match);
+
+static const struct i2c_device_id as73211_id[] = {
+ { "as73211", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, as73211_id);
+
+static struct i2c_driver as73211_driver = {
+ .driver = {
+ .name = AS73211_DRV_NAME,
+ .of_match_table = as73211_of_match,
+ .pm = &as73211_pm_ops,
+ },
+ .probe_new = as73211_probe,
+ .id_table = as73211_id,
+};
+module_i2c_driver(as73211_driver);
+
+MODULE_AUTHOR("Christian Eggers <ceggers@arri.de>");
+MODULE_DESCRIPTION("AS73211 XYZ True Color Sensor driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/iio/light/bh1750.c b/drivers/iio/light/bh1750.c
index adb5ab9..48484b9 100644
--- a/drivers/iio/light/bh1750.c
+++ b/drivers/iio/light/bh1750.c
@@ -254,7 +254,6 @@
return ret;
mutex_init(&data->lock);
- indio_dev->dev.parent = &client->dev;
indio_dev->info = &bh1750_info;
indio_dev->name = id->name;
indio_dev->channels = bh1750_channels;
diff --git a/drivers/iio/light/bh1780.c b/drivers/iio/light/bh1780.c
index a836100..abbf2e6 100644
--- a/drivers/iio/light/bh1780.c
+++ b/drivers/iio/light/bh1780.c
@@ -13,7 +13,7 @@
#include <linux/platform_device.h>
#include <linux/delay.h>
#include <linux/module.h>
-#include <linux/of.h>
+#include <linux/mod_devicetable.h>
#include <linux/pm_runtime.h>
#include <linux/iio/iio.h>
#include <linux/iio/sysfs.h>
@@ -185,7 +185,6 @@
pm_runtime_use_autosuspend(&client->dev);
pm_runtime_put(&client->dev);
- indio_dev->dev.parent = &client->dev;
indio_dev->info = &bh1780_info;
indio_dev->name = "bh1780";
indio_dev->channels = bh1780_channels;
@@ -273,13 +272,11 @@
MODULE_DEVICE_TABLE(i2c, bh1780_id);
-#ifdef CONFIG_OF
static const struct of_device_id of_bh1780_match[] = {
{ .compatible = "rohm,bh1780gli", },
{},
};
MODULE_DEVICE_TABLE(of, of_bh1780_match);
-#endif
static struct i2c_driver bh1780_driver = {
.probe = bh1780_probe,
@@ -288,7 +285,7 @@
.driver = {
.name = "bh1780",
.pm = &bh1780_dev_pm_ops,
- .of_match_table = of_match_ptr(of_bh1780_match),
+ .of_match_table = of_bh1780_match,
},
};
diff --git a/drivers/iio/light/cm32181.c b/drivers/iio/light/cm32181.c
index 5f4fb56..9764994 100644
--- a/drivers/iio/light/cm32181.c
+++ b/drivers/iio/light/cm32181.c
@@ -4,11 +4,13 @@
* Author: Kevin Tsai <ktsai@capellamicro.com>
*/
+#include <linux/acpi.h>
#include <linux/delay.h>
#include <linux/err.h>
#include <linux/i2c.h>
#include <linux/mutex.h>
#include <linux/module.h>
+#include <linux/mod_devicetable.h>
#include <linux/interrupt.h>
#include <linux/regulator/consumer.h>
#include <linux/iio/iio.h>
@@ -18,17 +20,24 @@
/* Registers Address */
#define CM32181_REG_ADDR_CMD 0x00
+#define CM32181_REG_ADDR_WH 0x01
+#define CM32181_REG_ADDR_WL 0x02
+#define CM32181_REG_ADDR_TEST 0x03
#define CM32181_REG_ADDR_ALS 0x04
#define CM32181_REG_ADDR_STATUS 0x06
#define CM32181_REG_ADDR_ID 0x07
/* Number of Configurable Registers */
-#define CM32181_CONF_REG_NUM 0x01
+#define CM32181_CONF_REG_NUM 4
/* CMD register */
-#define CM32181_CMD_ALS_ENABLE 0x00
-#define CM32181_CMD_ALS_DISABLE 0x01
-#define CM32181_CMD_ALS_INT_EN 0x02
+#define CM32181_CMD_ALS_DISABLE BIT(0)
+#define CM32181_CMD_ALS_INT_EN BIT(1)
+#define CM32181_CMD_ALS_THRES_WINDOW BIT(2)
+
+#define CM32181_CMD_ALS_PERS_SHIFT 4
+#define CM32181_CMD_ALS_PERS_MASK (0x03 << CM32181_CMD_ALS_PERS_SHIFT)
+#define CM32181_CMD_ALS_PERS_DEFAULT (0x01 << CM32181_CMD_ALS_PERS_SHIFT)
#define CM32181_CMD_ALS_IT_SHIFT 6
#define CM32181_CMD_ALS_IT_MASK (0x0F << CM32181_CMD_ALS_IT_SHIFT)
@@ -38,27 +47,133 @@
#define CM32181_CMD_ALS_SM_MASK (0x03 << CM32181_CMD_ALS_SM_SHIFT)
#define CM32181_CMD_ALS_SM_DEFAULT (0x01 << CM32181_CMD_ALS_SM_SHIFT)
-#define CM32181_MLUX_PER_BIT 5 /* ALS_SM=01 IT=800ms */
-#define CM32181_MLUX_PER_BIT_BASE_IT 800000 /* Based on IT=800ms */
-#define CM32181_CALIBSCALE_DEFAULT 1000
-#define CM32181_CALIBSCALE_RESOLUTION 1000
-#define MLUX_PER_LUX 1000
+#define CM32181_LUX_PER_BIT 500 /* ALS_SM=01 IT=800ms */
+#define CM32181_LUX_PER_BIT_RESOLUTION 100000
+#define CM32181_LUX_PER_BIT_BASE_IT 800000 /* Based on IT=800ms */
+#define CM32181_CALIBSCALE_DEFAULT 100000
+#define CM32181_CALIBSCALE_RESOLUTION 100000
-static const u8 cm32181_reg[CM32181_CONF_REG_NUM] = {
- CM32181_REG_ADDR_CMD,
+#define SMBUS_ALERT_RESPONSE_ADDRESS 0x0c
+
+/* CPM0 Index 0: device-id (3218 or 32181), 1: Unknown, 2: init_regs_bitmap */
+#define CPM0_REGS_BITMAP 2
+#define CPM0_HEADER_SIZE 3
+
+/* CPM1 Index 0: lux_per_bit, 1: calibscale, 2: resolution (100000) */
+#define CPM1_LUX_PER_BIT 0
+#define CPM1_CALIBSCALE 1
+#define CPM1_SIZE 3
+
+/* CM3218 Family */
+static const int cm3218_als_it_bits[] = { 0, 1, 2, 3 };
+static const int cm3218_als_it_values[] = { 100000, 200000, 400000, 800000 };
+
+/* CM32181 Family */
+static const int cm32181_als_it_bits[] = { 12, 8, 0, 1, 2, 3 };
+static const int cm32181_als_it_values[] = {
+ 25000, 50000, 100000, 200000, 400000, 800000
};
-static const int als_it_bits[] = {12, 8, 0, 1, 2, 3};
-static const int als_it_value[] = {25000, 50000, 100000, 200000, 400000,
- 800000};
-
struct cm32181_chip {
struct i2c_client *client;
+ struct device *dev;
struct mutex lock;
u16 conf_regs[CM32181_CONF_REG_NUM];
+ unsigned long init_regs_bitmap;
int calibscale;
+ int lux_per_bit;
+ int lux_per_bit_base_it;
+ int num_als_it;
+ const int *als_it_bits;
+ const int *als_it_values;
};
+static int cm32181_read_als_it(struct cm32181_chip *cm32181, int *val2);
+
+#ifdef CONFIG_ACPI
+/**
+ * cm32181_acpi_get_cpm() - Get CPM object from ACPI
+ * @dev: pointer of struct device.
+ * @obj_name: pointer of ACPI object name.
+ * @values: pointer of array for return elements.
+ * @count: maximum size of return array.
+ *
+ * Convert ACPI CPM table to array.
+ *
+ * Return: -ENODEV for fail. Otherwise is number of elements.
+ */
+static int cm32181_acpi_get_cpm(struct device *dev, char *obj_name,
+ u64 *values, int count)
+{
+ struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL };
+ union acpi_object *cpm, *elem;
+ acpi_handle handle;
+ acpi_status status;
+ int i;
+
+ handle = ACPI_HANDLE(dev);
+ if (!handle)
+ return -ENODEV;
+
+ status = acpi_evaluate_object(handle, obj_name, NULL, &buffer);
+ if (ACPI_FAILURE(status)) {
+ dev_err(dev, "object %s not found\n", obj_name);
+ return -ENODEV;
+ }
+
+ cpm = buffer.pointer;
+ if (cpm->package.count > count)
+ dev_warn(dev, "%s table contains %u values, only using first %d values\n",
+ obj_name, cpm->package.count, count);
+
+ count = min_t(int, cpm->package.count, count);
+ for (i = 0; i < count; i++) {
+ elem = &(cpm->package.elements[i]);
+ values[i] = elem->integer.value;
+ }
+
+ kfree(buffer.pointer);
+
+ return count;
+}
+
+static void cm32181_acpi_parse_cpm_tables(struct cm32181_chip *cm32181)
+{
+ u64 vals[CPM0_HEADER_SIZE + CM32181_CONF_REG_NUM];
+ struct device *dev = cm32181->dev;
+ int i, count;
+
+ count = cm32181_acpi_get_cpm(dev, "CPM0", vals, ARRAY_SIZE(vals));
+ if (count <= CPM0_HEADER_SIZE)
+ return;
+
+ count -= CPM0_HEADER_SIZE;
+
+ cm32181->init_regs_bitmap = vals[CPM0_REGS_BITMAP];
+ cm32181->init_regs_bitmap &= GENMASK(count - 1, 0);
+ for_each_set_bit(i, &cm32181->init_regs_bitmap, count)
+ cm32181->conf_regs[i] = vals[CPM0_HEADER_SIZE + i];
+
+ count = cm32181_acpi_get_cpm(dev, "CPM1", vals, ARRAY_SIZE(vals));
+ if (count != CPM1_SIZE)
+ return;
+
+ cm32181->lux_per_bit = vals[CPM1_LUX_PER_BIT];
+
+ /* Check for uncalibrated devices */
+ if (vals[CPM1_CALIBSCALE] == CM32181_CALIBSCALE_DEFAULT)
+ return;
+
+ cm32181->calibscale = vals[CPM1_CALIBSCALE];
+ /* CPM1 lux_per_bit is for the current it value */
+ cm32181_read_als_it(cm32181, &cm32181->lux_per_bit_base_it);
+}
+#else
+static void cm32181_acpi_parse_cpm_tables(struct cm32181_chip *cm32181)
+{
+}
+#endif /* CONFIG_ACPI */
+
/**
* cm32181_reg_init() - Initialize CM32181 registers
* @cm32181: pointer of struct cm32181.
@@ -78,18 +193,37 @@
return ret;
/* check device ID */
- if ((ret & 0xFF) != 0x81)
+ switch (ret & 0xFF) {
+ case 0x18: /* CM3218 */
+ cm32181->num_als_it = ARRAY_SIZE(cm3218_als_it_bits);
+ cm32181->als_it_bits = cm3218_als_it_bits;
+ cm32181->als_it_values = cm3218_als_it_values;
+ break;
+ case 0x81: /* CM32181 */
+ case 0x82: /* CM32182, fully compat. with CM32181 */
+ cm32181->num_als_it = ARRAY_SIZE(cm32181_als_it_bits);
+ cm32181->als_it_bits = cm32181_als_it_bits;
+ cm32181->als_it_values = cm32181_als_it_values;
+ break;
+ default:
return -ENODEV;
+ }
/* Default Values */
- cm32181->conf_regs[CM32181_REG_ADDR_CMD] = CM32181_CMD_ALS_ENABLE |
+ cm32181->conf_regs[CM32181_REG_ADDR_CMD] =
CM32181_CMD_ALS_IT_DEFAULT | CM32181_CMD_ALS_SM_DEFAULT;
+ cm32181->init_regs_bitmap = BIT(CM32181_REG_ADDR_CMD);
cm32181->calibscale = CM32181_CALIBSCALE_DEFAULT;
+ cm32181->lux_per_bit = CM32181_LUX_PER_BIT;
+ cm32181->lux_per_bit_base_it = CM32181_LUX_PER_BIT_BASE_IT;
+
+ if (ACPI_HANDLE(cm32181->dev))
+ cm32181_acpi_parse_cpm_tables(cm32181);
/* Initialize registers*/
- for (i = 0; i < CM32181_CONF_REG_NUM; i++) {
- ret = i2c_smbus_write_word_data(client, cm32181_reg[i],
- cm32181->conf_regs[i]);
+ for_each_set_bit(i, &cm32181->init_regs_bitmap, CM32181_CONF_REG_NUM) {
+ ret = i2c_smbus_write_word_data(client, i,
+ cm32181->conf_regs[i]);
if (ret < 0)
return ret;
}
@@ -102,7 +236,7 @@
* @cm32181: pointer of struct cm32181
* @val2: pointer of int to load the als_it value.
*
- * Report the current integartion time by millisecond.
+ * Report the current integration time in milliseconds.
*
* Return: IIO_VAL_INT_PLUS_MICRO for success, otherwise -EINVAL.
*/
@@ -114,9 +248,9 @@
als_it = cm32181->conf_regs[CM32181_REG_ADDR_CMD];
als_it &= CM32181_CMD_ALS_IT_MASK;
als_it >>= CM32181_CMD_ALS_IT_SHIFT;
- for (i = 0; i < ARRAY_SIZE(als_it_bits); i++) {
- if (als_it == als_it_bits[i]) {
- *val2 = als_it_value[i];
+ for (i = 0; i < cm32181->num_als_it; i++) {
+ if (als_it == cm32181->als_it_bits[i]) {
+ *val2 = cm32181->als_it_values[i];
return IIO_VAL_INT_PLUS_MICRO;
}
}
@@ -139,14 +273,14 @@
u16 als_it;
int ret, i, n;
- n = ARRAY_SIZE(als_it_value);
+ n = cm32181->num_als_it;
for (i = 0; i < n; i++)
- if (val <= als_it_value[i])
+ if (val <= cm32181->als_it_values[i])
break;
if (i >= n)
i = n - 1;
- als_it = als_it_bits[i];
+ als_it = cm32181->als_it_bits[i];
als_it <<= CM32181_CMD_ALS_IT_SHIFT;
mutex_lock(&cm32181->lock);
@@ -175,15 +309,15 @@
struct i2c_client *client = cm32181->client;
int ret;
int als_it;
- unsigned long lux;
+ u64 lux;
ret = cm32181_read_als_it(cm32181, &als_it);
if (ret < 0)
return -EINVAL;
- lux = CM32181_MLUX_PER_BIT;
- lux *= CM32181_MLUX_PER_BIT_BASE_IT;
- lux /= als_it;
+ lux = cm32181->lux_per_bit;
+ lux *= cm32181->lux_per_bit_base_it;
+ lux = div_u64(lux, als_it);
ret = i2c_smbus_read_word_data(client, CM32181_REG_ADDR_ALS);
if (ret < 0)
@@ -191,8 +325,8 @@
lux *= ret;
lux *= cm32181->calibscale;
- lux /= CM32181_CALIBSCALE_RESOLUTION;
- lux /= MLUX_PER_LUX;
+ lux = div_u64(lux, CM32181_CALIBSCALE_RESOLUTION);
+ lux = div_u64(lux, CM32181_LUX_PER_BIT_RESOLUTION);
if (lux > 0xFFFF)
lux = 0xFFFF;
@@ -258,11 +392,12 @@
static ssize_t cm32181_get_it_available(struct device *dev,
struct device_attribute *attr, char *buf)
{
+ struct cm32181_chip *cm32181 = iio_priv(dev_to_iio_dev(dev));
int i, n, len;
- n = ARRAY_SIZE(als_it_value);
+ n = cm32181->num_als_it;
for (i = 0, len = 0; i < n; i++)
- len += sprintf(buf + len, "0.%06u ", als_it_value[i]);
+ len += sprintf(buf + len, "0.%06u ", cm32181->als_it_values[i]);
return len + sprintf(buf + len, "\n");
}
@@ -294,70 +429,85 @@
.attrs = &cm32181_attribute_group,
};
-static int cm32181_probe(struct i2c_client *client,
- const struct i2c_device_id *id)
+static int cm32181_probe(struct i2c_client *client)
{
+ struct device *dev = &client->dev;
struct cm32181_chip *cm32181;
struct iio_dev *indio_dev;
int ret;
- indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*cm32181));
- if (!indio_dev) {
- dev_err(&client->dev, "devm_iio_device_alloc failed\n");
+ indio_dev = devm_iio_device_alloc(dev, sizeof(*cm32181));
+ if (!indio_dev)
return -ENOMEM;
+
+ /*
+ * Some ACPI systems list 2 I2C resources for the CM3218 sensor, the
+ * SMBus Alert Response Address (ARA, 0x0c) and the actual I2C address.
+ * Detect this and take the following step to deal with it:
+ * 1. When a SMBus Alert capable sensor has an Alert asserted, it will
+ * not respond on its actual I2C address. Read a byte from the ARA
+ * to clear any pending Alerts.
+ * 2. Create a "dummy" client for the actual I2C address and
+ * use that client to communicate with the sensor.
+ */
+ if (ACPI_HANDLE(dev) && client->addr == SMBUS_ALERT_RESPONSE_ADDRESS) {
+ struct i2c_board_info board_info = { .type = "dummy" };
+
+ i2c_smbus_read_byte(client);
+
+ client = i2c_acpi_new_device(dev, 1, &board_info);
+ if (IS_ERR(client))
+ return PTR_ERR(client);
}
cm32181 = iio_priv(indio_dev);
- i2c_set_clientdata(client, indio_dev);
cm32181->client = client;
+ cm32181->dev = dev;
mutex_init(&cm32181->lock);
- indio_dev->dev.parent = &client->dev;
indio_dev->channels = cm32181_channels;
indio_dev->num_channels = ARRAY_SIZE(cm32181_channels);
indio_dev->info = &cm32181_info;
- indio_dev->name = id->name;
+ indio_dev->name = dev_name(dev);
indio_dev->modes = INDIO_DIRECT_MODE;
ret = cm32181_reg_init(cm32181);
if (ret) {
- dev_err(&client->dev,
- "%s: register init failed\n",
- __func__);
+ dev_err(dev, "%s: register init failed\n", __func__);
return ret;
}
- ret = devm_iio_device_register(&client->dev, indio_dev);
+ ret = devm_iio_device_register(dev, indio_dev);
if (ret) {
- dev_err(&client->dev,
- "%s: regist device failed\n",
- __func__);
+ dev_err(dev, "%s: regist device failed\n", __func__);
return ret;
}
return 0;
}
-static const struct i2c_device_id cm32181_id[] = {
- { "cm32181", 0 },
- { }
-};
-
-MODULE_DEVICE_TABLE(i2c, cm32181_id);
-
static const struct of_device_id cm32181_of_match[] = {
+ { .compatible = "capella,cm3218" },
{ .compatible = "capella,cm32181" },
{ }
};
MODULE_DEVICE_TABLE(of, cm32181_of_match);
+#ifdef CONFIG_ACPI
+static const struct acpi_device_id cm32181_acpi_match[] = {
+ { "CPLM3218", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(acpi, cm32181_acpi_match);
+#endif
+
static struct i2c_driver cm32181_driver = {
.driver = {
.name = "cm32181",
- .of_match_table = of_match_ptr(cm32181_of_match),
+ .acpi_match_table = ACPI_PTR(cm32181_acpi_match),
+ .of_match_table = cm32181_of_match,
},
- .id_table = cm32181_id,
- .probe = cm32181_probe,
+ .probe_new = cm32181_probe,
};
module_i2c_driver(cm32181_driver);
diff --git a/drivers/iio/light/cm3232.c b/drivers/iio/light/cm3232.c
index cd3cfb7..18a4103 100644
--- a/drivers/iio/light/cm3232.c
+++ b/drivers/iio/light/cm3232.c
@@ -10,6 +10,7 @@
#include <linux/i2c.h>
#include <linux/module.h>
+#include <linux/mod_devicetable.h>
#include <linux/iio/iio.h>
#include <linux/iio/sysfs.h>
#include <linux/init.h>
@@ -339,7 +340,6 @@
i2c_set_clientdata(client, indio_dev);
chip->client = client;
- indio_dev->dev.parent = &client->dev;
indio_dev->channels = cm3232_channels;
indio_dev->num_channels = ARRAY_SIZE(cm3232_channels);
indio_dev->info = &cm3232_info;
@@ -418,7 +418,7 @@
static struct i2c_driver cm3232_driver = {
.driver = {
.name = "cm3232",
- .of_match_table = of_match_ptr(cm3232_of_match),
+ .of_match_table = cm3232_of_match,
#ifdef CONFIG_PM_SLEEP
.pm = &cm3232_pm_ops,
#endif
diff --git a/drivers/iio/light/cm3323.c b/drivers/iio/light/cm3323.c
index 0443861..6d1b0ff 100644
--- a/drivers/iio/light/cm3323.c
+++ b/drivers/iio/light/cm3323.c
@@ -231,7 +231,6 @@
mutex_init(&data->mutex);
- indio_dev->dev.parent = &client->dev;
indio_dev->info = &cm3323_info;
indio_dev->name = CM3323_DRV_NAME;
indio_dev->channels = cm3323_channels;
diff --git a/drivers/iio/light/cm3605.c b/drivers/iio/light/cm3605.c
index 964ede4..4c83953 100644
--- a/drivers/iio/light/cm3605.c
+++ b/drivers/iio/light/cm3605.c
@@ -239,7 +239,6 @@
led_trigger_register_simple("cm3605", &cm3605->led);
led_trigger_event(cm3605->led, LED_FULL);
- indio_dev->dev.parent = dev;
indio_dev->info = &cm3605_info;
indio_dev->name = "cm3605";
indio_dev->channels = cm3605_channels;
diff --git a/drivers/iio/light/cm36651.c b/drivers/iio/light/cm36651.c
index 1019d62..fd83a19 100644
--- a/drivers/iio/light/cm36651.c
+++ b/drivers/iio/light/cm36651.c
@@ -532,7 +532,7 @@
int state)
{
struct cm36651_data *cm36651 = iio_priv(indio_dev);
- int cmd, ret = -EINVAL;
+ int cmd, ret;
mutex_lock(&cm36651->lock);
@@ -662,7 +662,6 @@
}
mutex_init(&cm36651->lock);
- indio_dev->dev.parent = &client->dev;
indio_dev->channels = cm36651_channels;
indio_dev->num_channels = ARRAY_SIZE(cm36651_channels);
indio_dev->info = &cm36651_info;
diff --git a/drivers/iio/light/cros_ec_light_prox.c b/drivers/iio/light/cros_ec_light_prox.c
index c5263b5..75d6b5f 100644
--- a/drivers/iio/light/cros_ec_light_prox.c
+++ b/drivers/iio/light/cros_ec_light_prox.c
@@ -14,7 +14,6 @@
#include <linux/iio/triggered_buffer.h>
#include <linux/iio/trigger_consumer.h>
#include <linux/kernel.h>
-#include <linux/mfd/cros_ec.h>
#include <linux/module.h>
#include <linux/platform_data/cros_ec_commands.h>
#include <linux/platform_data/cros_ec_proto.h>
@@ -146,8 +145,11 @@
break;
case IIO_CHAN_INFO_CALIBSCALE:
st->core.param.cmd = MOTIONSENSE_CMD_SENSOR_RANGE;
- st->core.param.sensor_range.data = (val << 16) | (val2 / 100);
+ st->core.curr_range = (val << 16) | (val2 / 100);
+ st->core.param.sensor_range.data = st->core.curr_range;
ret = cros_ec_motion_send_host_cmd(&st->core, 0);
+ if (ret == 0)
+ st->core.range_updated = true;
break;
default:
ret = cros_ec_sensors_core_write(&st->core, chan, val, val2,
@@ -169,22 +171,19 @@
static int cros_ec_light_prox_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
- struct cros_ec_dev *ec_dev = dev_get_drvdata(dev->parent);
struct iio_dev *indio_dev;
struct cros_ec_light_prox_state *state;
struct iio_chan_spec *channel;
int ret;
- if (!ec_dev || !ec_dev->ec_dev) {
- dev_warn(dev, "No CROS EC device found.\n");
- return -EINVAL;
- }
-
indio_dev = devm_iio_device_alloc(dev, sizeof(*state));
if (!indio_dev)
return -ENOMEM;
- ret = cros_ec_sensors_core_init(pdev, indio_dev, true);
+ ret = cros_ec_sensors_core_init(pdev, indio_dev, true,
+ cros_ec_sensors_capture,
+ cros_ec_sensors_push_data,
+ true);
if (ret)
return ret;
@@ -196,8 +195,7 @@
/* Common part */
channel->info_mask_shared_by_all =
- BIT(IIO_CHAN_INFO_SAMP_FREQ) |
- BIT(IIO_CHAN_INFO_FREQUENCY);
+ BIT(IIO_CHAN_INFO_SAMP_FREQ);
channel->info_mask_shared_by_all_available =
BIT(IIO_CHAN_INFO_SAMP_FREQ);
channel->scan_type.realbits = CROS_EC_SENSOR_BITS;
@@ -243,11 +241,6 @@
state->core.read_ec_sensors_data = cros_ec_sensors_read_cmd;
- ret = devm_iio_triggered_buffer_setup(dev, indio_dev, NULL,
- cros_ec_sensors_capture, NULL);
- if (ret)
- return ret;
-
return devm_iio_device_register(dev, indio_dev);
}
diff --git a/drivers/iio/light/gp2ap002.c b/drivers/iio/light/gp2ap002.c
new file mode 100644
index 0000000..040d842
--- /dev/null
+++ b/drivers/iio/light/gp2ap002.c
@@ -0,0 +1,733 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * These are the two Sharp GP2AP002 variants supported by this driver:
+ * GP2AP002A00F Ambient Light and Proximity Sensor
+ * GP2AP002S00F Proximity Sensor
+ *
+ * Copyright (C) 2020 Linaro Ltd.
+ * Author: Linus Walleij <linus.walleij@linaro.org>
+ *
+ * Based partly on the code in Sony Ericssons GP2AP00200F driver by
+ * Courtney Cavin and Oskar Andero in drivers/input/misc/gp2ap002a00f.c
+ * Based partly on a Samsung misc driver submitted by
+ * Donggeun Kim & Minkyu Kang in 2011:
+ * https://lore.kernel.org/lkml/1315556546-7445-1-git-send-email-dg77.kim@samsung.com/
+ * Based partly on a submission by
+ * Jonathan Bakker and Paweł Chmiel in january 2019:
+ * https://lore.kernel.org/linux-input/20190125175045.22576-1-pawel.mikolaj.chmiel@gmail.com/
+ * Based partly on code from the Samsung GT-S7710 by <mjchen@sta.samsung.com>
+ * Based partly on the code in LG Electronics GP2AP00200F driver by
+ * Kenobi Lee <sungyoung.lee@lge.com> and EunYoung Cho <ey.cho@lge.com>
+ */
+#include <linux/module.h>
+#include <linux/i2c.h>
+#include <linux/regmap.h>
+#include <linux/iio/iio.h>
+#include <linux/iio/sysfs.h>
+#include <linux/iio/events.h>
+#include <linux/iio/consumer.h> /* To get our ADC channel */
+#include <linux/iio/types.h> /* To deal with our ADC channel */
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/regulator/consumer.h>
+#include <linux/pm_runtime.h>
+#include <linux/interrupt.h>
+#include <linux/bits.h>
+#include <linux/math64.h>
+#include <linux/pm.h>
+
+#define GP2AP002_PROX_CHANNEL 0
+#define GP2AP002_ALS_CHANNEL 1
+
+/* ------------------------------------------------------------------------ */
+/* ADDRESS SYMBOL DATA Init R/W */
+/* D7 D6 D5 D4 D3 D2 D1 D0 */
+/* ------------------------------------------------------------------------ */
+/* 0 PROX X X X X X X X VO H'00 R */
+/* 1 GAIN X X X X LED0 X X X H'00 W */
+/* 2 HYS HYSD HYSC1 HYSC0 X HYSF3 HYSF2 HYSF1 HYSF0 H'00 W */
+/* 3 CYCLE X X CYCL2 CYCL1 CYCL0 OSC2 X X H'00 W */
+/* 4 OPMOD X X X ASD X X VCON SSD H'00 W */
+/* 6 CON X X X OCON1 OCON0 X X X H'00 W */
+/* ------------------------------------------------------------------------ */
+/* VO :Proximity sensing result(0: no detection, 1: detection) */
+/* LED0 :Select switch for LED driver's On-registence(0:2x higher, 1:normal)*/
+/* HYSD/HYSF :Adjusts the receiver sensitivity */
+/* OSC :Select switch internal clocl frequency hoppling(0:effective) */
+/* CYCL :Determine the detection cycle(typically 8ms, up to 128x) */
+/* SSD :Software Shutdown function(0:shutdown, 1:operating) */
+/* VCON :VOUT output method control(0:normal, 1:interrupt) */
+/* ASD :Select switch for analog sleep function(0:ineffective, 1:effective)*/
+/* OCON :Select switch for enabling/disabling VOUT (00:enable, 11:disable) */
+
+#define GP2AP002_PROX 0x00
+#define GP2AP002_GAIN 0x01
+#define GP2AP002_HYS 0x02
+#define GP2AP002_CYCLE 0x03
+#define GP2AP002_OPMOD 0x04
+#define GP2AP002_CON 0x06
+
+#define GP2AP002_PROX_VO_DETECT BIT(0)
+
+/* Setting this bit to 0 means 2x higher LED resistance */
+#define GP2AP002_GAIN_LED_NORMAL BIT(3)
+
+/*
+ * These bits adjusts the proximity sensitivity, determining characteristics
+ * of the detection distance and its hysteresis.
+ */
+#define GP2AP002_HYS_HYSD_SHIFT 7
+#define GP2AP002_HYS_HYSD_MASK BIT(7)
+#define GP2AP002_HYS_HYSC_SHIFT 5
+#define GP2AP002_HYS_HYSC_MASK GENMASK(6, 5)
+#define GP2AP002_HYS_HYSF_SHIFT 0
+#define GP2AP002_HYS_HYSF_MASK GENMASK(3, 0)
+#define GP2AP002_HYS_MASK (GP2AP002_HYS_HYSD_MASK | \
+ GP2AP002_HYS_HYSC_MASK | \
+ GP2AP002_HYS_HYSF_MASK)
+
+/*
+ * These values determine the detection cycle response time
+ * 0: 8ms, 1: 16ms, 2: 32ms, 3: 64ms, 4: 128ms,
+ * 5: 256ms, 6: 512ms, 7: 1024ms
+ */
+#define GP2AP002_CYCLE_CYCL_SHIFT 3
+#define GP2AP002_CYCLE_CYCL_MASK GENMASK(5, 3)
+
+/*
+ * Select switch for internal clock frequency hopping
+ * 0: effective,
+ * 1: ineffective
+ */
+#define GP2AP002_CYCLE_OSC_EFFECTIVE 0
+#define GP2AP002_CYCLE_OSC_INEFFECTIVE BIT(2)
+#define GP2AP002_CYCLE_OSC_MASK BIT(2)
+
+/* Analog sleep effective */
+#define GP2AP002_OPMOD_ASD BIT(4)
+/* Enable chip */
+#define GP2AP002_OPMOD_SSD_OPERATING BIT(0)
+/* IRQ mode */
+#define GP2AP002_OPMOD_VCON_IRQ BIT(1)
+#define GP2AP002_OPMOD_MASK (BIT(0) | BIT(1) | BIT(4))
+
+/*
+ * Select switch for enabling/disabling Vout pin
+ * 0: enable
+ * 2: force to go Low
+ * 3: force to go High
+ */
+#define GP2AP002_CON_OCON_SHIFT 3
+#define GP2AP002_CON_OCON_ENABLE (0x0 << GP2AP002_CON_OCON_SHIFT)
+#define GP2AP002_CON_OCON_LOW (0x2 << GP2AP002_CON_OCON_SHIFT)
+#define GP2AP002_CON_OCON_HIGH (0x3 << GP2AP002_CON_OCON_SHIFT)
+#define GP2AP002_CON_OCON_MASK (0x3 << GP2AP002_CON_OCON_SHIFT)
+
+/**
+ * struct gp2ap002 - GP2AP002 state
+ * @map: regmap pointer for the i2c regmap
+ * @dev: pointer to parent device
+ * @vdd: regulator controlling VDD
+ * @vio: regulator controlling VIO
+ * @alsout: IIO ADC channel to convert the ALSOUT signal
+ * @hys_far: hysteresis control from device tree
+ * @hys_close: hysteresis control from device tree
+ * @is_gp2ap002s00f: this is the GP2AP002F variant of the chip
+ * @irq: the IRQ line used by this device
+ * @enabled: we cannot read the status of the hardware so we need to
+ * keep track of whether the event is enabled using this state variable
+ */
+struct gp2ap002 {
+ struct regmap *map;
+ struct device *dev;
+ struct regulator *vdd;
+ struct regulator *vio;
+ struct iio_channel *alsout;
+ u8 hys_far;
+ u8 hys_close;
+ bool is_gp2ap002s00f;
+ int irq;
+ bool enabled;
+};
+
+static irqreturn_t gp2ap002_prox_irq(int irq, void *d)
+{
+ struct iio_dev *indio_dev = d;
+ struct gp2ap002 *gp2ap002 = iio_priv(indio_dev);
+ u64 ev;
+ int val;
+ int ret;
+
+ if (!gp2ap002->enabled)
+ goto err_retrig;
+
+ ret = regmap_read(gp2ap002->map, GP2AP002_PROX, &val);
+ if (ret) {
+ dev_err(gp2ap002->dev, "error reading proximity\n");
+ goto err_retrig;
+ }
+
+ if (val & GP2AP002_PROX_VO_DETECT) {
+ /* Close */
+ dev_dbg(gp2ap002->dev, "close\n");
+ ret = regmap_write(gp2ap002->map, GP2AP002_HYS,
+ gp2ap002->hys_far);
+ if (ret)
+ dev_err(gp2ap002->dev,
+ "error setting up proximity hysteresis\n");
+ ev = IIO_UNMOD_EVENT_CODE(IIO_PROXIMITY, GP2AP002_PROX_CHANNEL,
+ IIO_EV_TYPE_THRESH, IIO_EV_DIR_RISING);
+ } else {
+ /* Far */
+ dev_dbg(gp2ap002->dev, "far\n");
+ ret = regmap_write(gp2ap002->map, GP2AP002_HYS,
+ gp2ap002->hys_close);
+ if (ret)
+ dev_err(gp2ap002->dev,
+ "error setting up proximity hysteresis\n");
+ ev = IIO_UNMOD_EVENT_CODE(IIO_PROXIMITY, GP2AP002_PROX_CHANNEL,
+ IIO_EV_TYPE_THRESH, IIO_EV_DIR_FALLING);
+ }
+ iio_push_event(indio_dev, ev, iio_get_time_ns(indio_dev));
+
+ /*
+ * After changing hysteresis, we need to wait for one detection
+ * cycle to see if anything changed, or we will just trigger the
+ * previous interrupt again. A detection cycle depends on the CYCLE
+ * register, we are hard-coding ~8 ms in probe() so wait some more
+ * than this, 20-30 ms.
+ */
+ usleep_range(20000, 30000);
+
+err_retrig:
+ ret = regmap_write(gp2ap002->map, GP2AP002_CON,
+ GP2AP002_CON_OCON_ENABLE);
+ if (ret)
+ dev_err(gp2ap002->dev, "error setting up VOUT control\n");
+
+ return IRQ_HANDLED;
+}
+
+/*
+ * This array maps current and lux.
+ *
+ * Ambient light sensing range is 3 to 55000 lux.
+ *
+ * This mapping is based on the following formula.
+ * illuminance = 10 ^ (current[mA] / 10)
+ *
+ * When the ADC measures 0, return 0 lux.
+ */
+static const u16 gp2ap002_illuminance_table[] = {
+ 0, 1, 1, 2, 2, 3, 4, 5, 6, 8, 10, 12, 16, 20, 25, 32, 40, 50, 63, 79,
+ 100, 126, 158, 200, 251, 316, 398, 501, 631, 794, 1000, 1259, 1585,
+ 1995, 2512, 3162, 3981, 5012, 6310, 7943, 10000, 12589, 15849, 19953,
+ 25119, 31623, 39811, 50119,
+};
+
+static int gp2ap002_get_lux(struct gp2ap002 *gp2ap002)
+{
+ int ret, res;
+ u16 lux;
+
+ ret = iio_read_channel_processed(gp2ap002->alsout, &res);
+ if (ret < 0)
+ return ret;
+
+ dev_dbg(gp2ap002->dev, "read %d mA from ADC\n", res);
+
+ /* ensure we don't under/overflow */
+ res = clamp(res, 0, (int)ARRAY_SIZE(gp2ap002_illuminance_table) - 1);
+ lux = gp2ap002_illuminance_table[res];
+
+ return (int)lux;
+}
+
+static int gp2ap002_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int *val, int *val2, long mask)
+{
+ struct gp2ap002 *gp2ap002 = iio_priv(indio_dev);
+ int ret;
+
+ pm_runtime_get_sync(gp2ap002->dev);
+
+ switch (mask) {
+ case IIO_CHAN_INFO_RAW:
+ switch (chan->type) {
+ case IIO_LIGHT:
+ ret = gp2ap002_get_lux(gp2ap002);
+ if (ret < 0)
+ return ret;
+ *val = ret;
+ ret = IIO_VAL_INT;
+ goto out;
+ default:
+ ret = -EINVAL;
+ goto out;
+ }
+ default:
+ ret = -EINVAL;
+ }
+
+out:
+ pm_runtime_mark_last_busy(gp2ap002->dev);
+ pm_runtime_put_autosuspend(gp2ap002->dev);
+
+ return ret;
+}
+
+static int gp2ap002_init(struct gp2ap002 *gp2ap002)
+{
+ int ret;
+
+ /* Set up the IR LED resistance */
+ ret = regmap_write(gp2ap002->map, GP2AP002_GAIN,
+ GP2AP002_GAIN_LED_NORMAL);
+ if (ret) {
+ dev_err(gp2ap002->dev, "error setting up LED gain\n");
+ return ret;
+ }
+ ret = regmap_write(gp2ap002->map, GP2AP002_HYS, gp2ap002->hys_far);
+ if (ret) {
+ dev_err(gp2ap002->dev,
+ "error setting up proximity hysteresis\n");
+ return ret;
+ }
+
+ /* Disable internal frequency hopping */
+ ret = regmap_write(gp2ap002->map, GP2AP002_CYCLE,
+ GP2AP002_CYCLE_OSC_INEFFECTIVE);
+ if (ret) {
+ dev_err(gp2ap002->dev,
+ "error setting up internal frequency hopping\n");
+ return ret;
+ }
+
+ /* Enable chip and IRQ, disable analog sleep */
+ ret = regmap_write(gp2ap002->map, GP2AP002_OPMOD,
+ GP2AP002_OPMOD_SSD_OPERATING |
+ GP2AP002_OPMOD_VCON_IRQ);
+ if (ret) {
+ dev_err(gp2ap002->dev, "error setting up operation mode\n");
+ return ret;
+ }
+
+ /* Interrupt on VOUT enabled */
+ ret = regmap_write(gp2ap002->map, GP2AP002_CON,
+ GP2AP002_CON_OCON_ENABLE);
+ if (ret)
+ dev_err(gp2ap002->dev, "error setting up VOUT control\n");
+
+ return ret;
+}
+
+static int gp2ap002_read_event_config(struct iio_dev *indio_dev,
+ const struct iio_chan_spec *chan,
+ enum iio_event_type type,
+ enum iio_event_direction dir)
+{
+ struct gp2ap002 *gp2ap002 = iio_priv(indio_dev);
+
+ /*
+ * We just keep track of this internally, as it is not possible to
+ * query the hardware.
+ */
+ return gp2ap002->enabled;
+}
+
+static int gp2ap002_write_event_config(struct iio_dev *indio_dev,
+ const struct iio_chan_spec *chan,
+ enum iio_event_type type,
+ enum iio_event_direction dir,
+ int state)
+{
+ struct gp2ap002 *gp2ap002 = iio_priv(indio_dev);
+
+ if (state) {
+ /*
+ * This will bring the regulators up (unless they are on
+ * already) and reintialize the sensor by using runtime_pm
+ * callbacks.
+ */
+ pm_runtime_get_sync(gp2ap002->dev);
+ gp2ap002->enabled = true;
+ } else {
+ pm_runtime_mark_last_busy(gp2ap002->dev);
+ pm_runtime_put_autosuspend(gp2ap002->dev);
+ gp2ap002->enabled = false;
+ }
+
+ return 0;
+}
+
+static const struct iio_info gp2ap002_info = {
+ .read_raw = gp2ap002_read_raw,
+ .read_event_config = gp2ap002_read_event_config,
+ .write_event_config = gp2ap002_write_event_config,
+};
+
+static const struct iio_event_spec gp2ap002_events[] = {
+ {
+ .type = IIO_EV_TYPE_THRESH,
+ .dir = IIO_EV_DIR_EITHER,
+ .mask_separate = BIT(IIO_EV_INFO_ENABLE),
+ },
+};
+
+static const struct iio_chan_spec gp2ap002_channels[] = {
+ {
+ .type = IIO_PROXIMITY,
+ .event_spec = gp2ap002_events,
+ .num_event_specs = ARRAY_SIZE(gp2ap002_events),
+ },
+ {
+ .type = IIO_LIGHT,
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
+ .channel = GP2AP002_ALS_CHANNEL,
+ },
+};
+
+/*
+ * We need a special regmap because this hardware expects to
+ * write single bytes to registers but read a 16bit word on some
+ * variants and discard the lower 8 bits so combine
+ * i2c_smbus_read_word_data() with i2c_smbus_write_byte_data()
+ * selectively like this.
+ */
+static int gp2ap002_regmap_i2c_read(void *context, unsigned int reg,
+ unsigned int *val)
+{
+ struct device *dev = context;
+ struct i2c_client *i2c = to_i2c_client(dev);
+ int ret;
+
+ ret = i2c_smbus_read_word_data(i2c, reg);
+ if (ret < 0)
+ return ret;
+
+ *val = (ret >> 8) & 0xFF;
+
+ return 0;
+}
+
+static int gp2ap002_regmap_i2c_write(void *context, unsigned int reg,
+ unsigned int val)
+{
+ struct device *dev = context;
+ struct i2c_client *i2c = to_i2c_client(dev);
+
+ return i2c_smbus_write_byte_data(i2c, reg, val);
+}
+
+static struct regmap_bus gp2ap002_regmap_bus = {
+ .reg_read = gp2ap002_regmap_i2c_read,
+ .reg_write = gp2ap002_regmap_i2c_write,
+};
+
+static int gp2ap002_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct gp2ap002 *gp2ap002;
+ struct iio_dev *indio_dev;
+ struct device *dev = &client->dev;
+ enum iio_chan_type ch_type;
+ static const struct regmap_config config = {
+ .reg_bits = 8,
+ .val_bits = 8,
+ .max_register = GP2AP002_CON,
+ };
+ struct regmap *regmap;
+ int num_chan;
+ const char *compat;
+ u8 val;
+ int ret;
+
+ indio_dev = devm_iio_device_alloc(dev, sizeof(*gp2ap002));
+ if (!indio_dev)
+ return -ENOMEM;
+ i2c_set_clientdata(client, indio_dev);
+
+ gp2ap002 = iio_priv(indio_dev);
+ gp2ap002->dev = dev;
+
+ /*
+ * Check the device compatible like this makes it possible to use
+ * ACPI PRP0001 for registering the sensor using device tree
+ * properties.
+ */
+ ret = device_property_read_string(dev, "compatible", &compat);
+ if (ret) {
+ dev_err(dev, "cannot check compatible\n");
+ return ret;
+ }
+ gp2ap002->is_gp2ap002s00f = !strcmp(compat, "sharp,gp2ap002s00f");
+
+ regmap = devm_regmap_init(dev, &gp2ap002_regmap_bus, dev, &config);
+ if (IS_ERR(regmap)) {
+ dev_err(dev, "Failed to register i2c regmap %d\n",
+ (int)PTR_ERR(regmap));
+ return PTR_ERR(regmap);
+ }
+ gp2ap002->map = regmap;
+
+ /*
+ * The hysteresis settings are coded into the device tree as values
+ * to be written into the hysteresis register. The datasheet defines
+ * modes "A", "B1" and "B2" with fixed values to be use but vendor
+ * code trees for actual devices are tweaking these values and refer to
+ * modes named things like "B1.5". To be able to support any devices,
+ * we allow passing an arbitrary hysteresis setting for "near" and
+ * "far".
+ */
+
+ /* Check the device tree for the IR LED hysteresis */
+ ret = device_property_read_u8(dev, "sharp,proximity-far-hysteresis",
+ &val);
+ if (ret) {
+ dev_err(dev, "failed to obtain proximity far setting\n");
+ return ret;
+ }
+ dev_dbg(dev, "proximity far setting %02x\n", val);
+ gp2ap002->hys_far = val;
+
+ ret = device_property_read_u8(dev, "sharp,proximity-close-hysteresis",
+ &val);
+ if (ret) {
+ dev_err(dev, "failed to obtain proximity close setting\n");
+ return ret;
+ }
+ dev_dbg(dev, "proximity close setting %02x\n", val);
+ gp2ap002->hys_close = val;
+
+ /* The GP2AP002A00F has a light sensor too */
+ if (!gp2ap002->is_gp2ap002s00f) {
+ gp2ap002->alsout = devm_iio_channel_get(dev, "alsout");
+ if (IS_ERR(gp2ap002->alsout)) {
+ if (PTR_ERR(gp2ap002->alsout) == -ENODEV) {
+ dev_err(dev, "no ADC, deferring...\n");
+ return -EPROBE_DEFER;
+ }
+ dev_err(dev, "failed to get ALSOUT ADC channel\n");
+ return PTR_ERR(gp2ap002->alsout);
+ }
+ ret = iio_get_channel_type(gp2ap002->alsout, &ch_type);
+ if (ret < 0)
+ return ret;
+ if (ch_type != IIO_CURRENT) {
+ dev_err(dev,
+ "wrong type of IIO channel specified for ALSOUT\n");
+ return -EINVAL;
+ }
+ }
+
+ gp2ap002->vdd = devm_regulator_get(dev, "vdd");
+ if (IS_ERR(gp2ap002->vdd)) {
+ dev_err(dev, "failed to get VDD regulator\n");
+ return PTR_ERR(gp2ap002->vdd);
+ }
+ gp2ap002->vio = devm_regulator_get(dev, "vio");
+ if (IS_ERR(gp2ap002->vio)) {
+ dev_err(dev, "failed to get VIO regulator\n");
+ return PTR_ERR(gp2ap002->vio);
+ }
+
+ /* Operating voltage 2.4V .. 3.6V according to datasheet */
+ ret = regulator_set_voltage(gp2ap002->vdd, 2400000, 3600000);
+ if (ret) {
+ dev_err(dev, "failed to sett VDD voltage\n");
+ return ret;
+ }
+
+ /* VIO should be between 1.65V and VDD */
+ ret = regulator_get_voltage(gp2ap002->vdd);
+ if (ret < 0) {
+ dev_err(dev, "failed to get VDD voltage\n");
+ return ret;
+ }
+ ret = regulator_set_voltage(gp2ap002->vio, 1650000, ret);
+ if (ret) {
+ dev_err(dev, "failed to set VIO voltage\n");
+ return ret;
+ }
+
+ ret = regulator_enable(gp2ap002->vdd);
+ if (ret) {
+ dev_err(dev, "failed to enable VDD regulator\n");
+ return ret;
+ }
+ ret = regulator_enable(gp2ap002->vio);
+ if (ret) {
+ dev_err(dev, "failed to enable VIO regulator\n");
+ goto out_disable_vdd;
+ }
+
+ msleep(20);
+
+ /*
+ * Initialize the device and signal to runtime PM that now we are
+ * definitely up and using power.
+ */
+ ret = gp2ap002_init(gp2ap002);
+ if (ret) {
+ dev_err(dev, "initialization failed\n");
+ goto out_disable_vio;
+ }
+ pm_runtime_get_noresume(dev);
+ pm_runtime_set_active(dev);
+ pm_runtime_enable(dev);
+ gp2ap002->enabled = false;
+
+ ret = devm_request_threaded_irq(dev, client->irq, NULL,
+ gp2ap002_prox_irq, IRQF_ONESHOT,
+ "gp2ap002", indio_dev);
+ if (ret) {
+ dev_err(dev, "unable to request IRQ\n");
+ goto out_put_pm;
+ }
+ gp2ap002->irq = client->irq;
+
+ /*
+ * As the device takes 20 ms + regulator delay to come up with a fresh
+ * measurement after power-on, do not shut it down unnecessarily.
+ * Set autosuspend to a one second.
+ */
+ pm_runtime_set_autosuspend_delay(dev, 1000);
+ pm_runtime_use_autosuspend(dev);
+ pm_runtime_put(dev);
+
+ indio_dev->info = &gp2ap002_info;
+ indio_dev->name = "gp2ap002";
+ indio_dev->channels = gp2ap002_channels;
+ /* Skip light channel for the proximity-only sensor */
+ num_chan = ARRAY_SIZE(gp2ap002_channels);
+ if (gp2ap002->is_gp2ap002s00f)
+ num_chan--;
+ indio_dev->num_channels = num_chan;
+ indio_dev->modes = INDIO_DIRECT_MODE;
+
+ ret = iio_device_register(indio_dev);
+ if (ret)
+ goto out_disable_pm;
+ dev_dbg(dev, "Sharp GP2AP002 probed successfully\n");
+
+ return 0;
+
+out_put_pm:
+ pm_runtime_put_noidle(dev);
+out_disable_pm:
+ pm_runtime_disable(dev);
+out_disable_vio:
+ regulator_disable(gp2ap002->vio);
+out_disable_vdd:
+ regulator_disable(gp2ap002->vdd);
+ return ret;
+}
+
+static int gp2ap002_remove(struct i2c_client *client)
+{
+ struct iio_dev *indio_dev = i2c_get_clientdata(client);
+ struct gp2ap002 *gp2ap002 = iio_priv(indio_dev);
+ struct device *dev = &client->dev;
+
+ pm_runtime_get_sync(dev);
+ pm_runtime_put_noidle(dev);
+ pm_runtime_disable(dev);
+ iio_device_unregister(indio_dev);
+ regulator_disable(gp2ap002->vio);
+ regulator_disable(gp2ap002->vdd);
+
+ return 0;
+}
+
+static int __maybe_unused gp2ap002_runtime_suspend(struct device *dev)
+{
+ struct iio_dev *indio_dev = dev_get_drvdata(dev);
+ struct gp2ap002 *gp2ap002 = iio_priv(indio_dev);
+ int ret;
+
+ /* Deactivate the IRQ */
+ disable_irq(gp2ap002->irq);
+
+ /* Disable chip and IRQ, everything off */
+ ret = regmap_write(gp2ap002->map, GP2AP002_OPMOD, 0x00);
+ if (ret) {
+ dev_err(gp2ap002->dev, "error setting up operation mode\n");
+ return ret;
+ }
+ /*
+ * As these regulators may be shared, at least we are now in
+ * sleep even if the regulators aren't really turned off.
+ */
+ regulator_disable(gp2ap002->vio);
+ regulator_disable(gp2ap002->vdd);
+
+ return 0;
+}
+
+static int __maybe_unused gp2ap002_runtime_resume(struct device *dev)
+{
+ struct iio_dev *indio_dev = dev_get_drvdata(dev);
+ struct gp2ap002 *gp2ap002 = iio_priv(indio_dev);
+ int ret;
+
+ ret = regulator_enable(gp2ap002->vdd);
+ if (ret) {
+ dev_err(dev, "failed to enable VDD regulator in resume path\n");
+ return ret;
+ }
+ ret = regulator_enable(gp2ap002->vio);
+ if (ret) {
+ dev_err(dev, "failed to enable VIO regulator in resume path\n");
+ return ret;
+ }
+
+ msleep(20);
+
+ ret = gp2ap002_init(gp2ap002);
+ if (ret) {
+ dev_err(dev, "re-initialization failed\n");
+ return ret;
+ }
+
+ /* Re-activate the IRQ */
+ enable_irq(gp2ap002->irq);
+
+ return 0;
+}
+
+static const struct dev_pm_ops gp2ap002_dev_pm_ops = {
+ SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend,
+ pm_runtime_force_resume)
+ SET_RUNTIME_PM_OPS(gp2ap002_runtime_suspend,
+ gp2ap002_runtime_resume, NULL)
+};
+
+static const struct i2c_device_id gp2ap002_id_table[] = {
+ { "gp2ap002", 0 },
+ { },
+};
+MODULE_DEVICE_TABLE(i2c, gp2ap002_id_table);
+
+static const struct of_device_id gp2ap002_of_match[] = {
+ { .compatible = "sharp,gp2ap002a00f" },
+ { .compatible = "sharp,gp2ap002s00f" },
+ { },
+};
+MODULE_DEVICE_TABLE(of, gp2ap002_of_match);
+
+static struct i2c_driver gp2ap002_driver = {
+ .driver = {
+ .name = "gp2ap002",
+ .of_match_table = gp2ap002_of_match,
+ .pm = &gp2ap002_dev_pm_ops,
+ },
+ .probe = gp2ap002_probe,
+ .remove = gp2ap002_remove,
+ .id_table = gp2ap002_id_table,
+};
+module_i2c_driver(gp2ap002_driver);
+
+MODULE_AUTHOR("Linus Walleij <linus.walleij@linaro.org>");
+MODULE_DESCRIPTION("GP2AP002 ambient light and proximity sensor driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/iio/light/gp2ap020a00f.c b/drivers/iio/light/gp2ap020a00f.c
index 4d70c5b..e2850c1 100644
--- a/drivers/iio/light/gp2ap020a00f.c
+++ b/drivers/iio/light/gp2ap020a00f.c
@@ -38,8 +38,8 @@
#include <linux/irq.h>
#include <linux/irq_work.h>
#include <linux/module.h>
+#include <linux/mod_devicetable.h>
#include <linux/mutex.h>
-#include <linux/of.h>
#include <linux/regmap.h>
#include <linux/regulator/consumer.h>
#include <linux/slab.h>
@@ -1420,12 +1420,8 @@
goto error_unlock;
data->buffer = kmalloc(indio_dev->scan_bytes, GFP_KERNEL);
- if (!data->buffer) {
+ if (!data->buffer)
err = -ENOMEM;
- goto error_unlock;
- }
-
- err = iio_triggered_buffer_postenable(indio_dev);
error_unlock:
mutex_unlock(&data->lock);
@@ -1436,14 +1432,10 @@
static int gp2ap020a00f_buffer_predisable(struct iio_dev *indio_dev)
{
struct gp2ap020a00f_data *data = iio_priv(indio_dev);
- int i, err;
+ int i, err = 0;
mutex_lock(&data->lock);
- err = iio_triggered_buffer_predisable(indio_dev);
- if (err < 0)
- goto error_unlock;
-
for_each_set_bit(i, indio_dev->active_scan_mask,
indio_dev->masklength) {
switch (i) {
@@ -1465,7 +1457,6 @@
if (err == 0)
kfree(data->buffer);
-error_unlock:
mutex_unlock(&data->lock);
return err;
@@ -1526,7 +1517,6 @@
init_waitqueue_head(&data->data_ready_queue);
mutex_init(&data->lock);
- indio_dev->dev.parent = &client->dev;
indio_dev->channels = gp2ap020a00f_channels;
indio_dev->num_channels = ARRAY_SIZE(gp2ap020a00f_channels);
indio_dev->info = &gp2ap020a00f_info;
@@ -1616,18 +1606,16 @@
MODULE_DEVICE_TABLE(i2c, gp2ap020a00f_id);
-#ifdef CONFIG_OF
static const struct of_device_id gp2ap020a00f_of_match[] = {
{ .compatible = "sharp,gp2ap020a00f" },
{ }
};
MODULE_DEVICE_TABLE(of, gp2ap020a00f_of_match);
-#endif
static struct i2c_driver gp2ap020a00f_driver = {
.driver = {
.name = GP2A_I2C_NAME,
- .of_match_table = of_match_ptr(gp2ap020a00f_of_match),
+ .of_match_table = gp2ap020a00f_of_match,
},
.probe = gp2ap020a00f_probe,
.remove = gp2ap020a00f_remove,
diff --git a/drivers/iio/light/hid-sensor-als.c b/drivers/iio/light/hid-sensor-als.c
index b6cd299..a21c827 100644
--- a/drivers/iio/light/hid-sensor-als.c
+++ b/drivers/iio/light/hid-sensor-als.c
@@ -14,8 +14,6 @@
#include <linux/iio/iio.h>
#include <linux/iio/sysfs.h>
#include <linux/iio/buffer.h>
-#include <linux/iio/trigger_consumer.h>
-#include <linux/iio/triggered_buffer.h>
#include "../common/hid-sensors/hid-sensor-trigger.h"
enum {
@@ -303,23 +301,17 @@
indio_dev->num_channels =
ARRAY_SIZE(als_channels);
- indio_dev->dev.parent = &pdev->dev;
indio_dev->info = &als_info;
indio_dev->name = name;
indio_dev->modes = INDIO_DIRECT_MODE;
- ret = iio_triggered_buffer_setup(indio_dev, &iio_pollfunc_store_time,
- NULL, NULL);
- if (ret) {
- dev_err(&pdev->dev, "failed to initialize trigger buffer\n");
- goto error_free_dev_mem;
- }
atomic_set(&als_state->common_attributes.data_ready, 0);
+
ret = hid_sensor_setup_trigger(indio_dev, name,
&als_state->common_attributes);
if (ret < 0) {
dev_err(&pdev->dev, "trigger setup failed\n");
- goto error_unreg_buffer_funcs;
+ goto error_free_dev_mem;
}
ret = iio_device_register(indio_dev);
@@ -343,9 +335,7 @@
error_iio_unreg:
iio_device_unregister(indio_dev);
error_remove_trigger:
- hid_sensor_remove_trigger(&als_state->common_attributes);
-error_unreg_buffer_funcs:
- iio_triggered_buffer_cleanup(indio_dev);
+ hid_sensor_remove_trigger(indio_dev, &als_state->common_attributes);
error_free_dev_mem:
kfree(indio_dev->channels);
return ret;
@@ -360,8 +350,7 @@
sensor_hub_remove_callback(hsdev, HID_USAGE_SENSOR_ALS);
iio_device_unregister(indio_dev);
- hid_sensor_remove_trigger(&als_state->common_attributes);
- iio_triggered_buffer_cleanup(indio_dev);
+ hid_sensor_remove_trigger(indio_dev, &als_state->common_attributes);
kfree(indio_dev->channels);
return 0;
diff --git a/drivers/iio/light/hid-sensor-prox.c b/drivers/iio/light/hid-sensor-prox.c
index 70da928..e9e00ce 100644
--- a/drivers/iio/light/hid-sensor-prox.c
+++ b/drivers/iio/light/hid-sensor-prox.c
@@ -14,8 +14,6 @@
#include <linux/iio/iio.h>
#include <linux/iio/sysfs.h>
#include <linux/iio/buffer.h>
-#include <linux/iio/trigger_consumer.h>
-#include <linux/iio/triggered_buffer.h>
#include "../common/hid-sensors/hid-sensor-trigger.h"
#define CHANNEL_SCAN_INDEX_PRESENCE 0
@@ -290,23 +288,17 @@
}
indio_dev->num_channels = ARRAY_SIZE(prox_channels);
- indio_dev->dev.parent = &pdev->dev;
indio_dev->info = &prox_info;
indio_dev->name = name;
indio_dev->modes = INDIO_DIRECT_MODE;
- ret = iio_triggered_buffer_setup(indio_dev, &iio_pollfunc_store_time,
- NULL, NULL);
- if (ret) {
- dev_err(&pdev->dev, "failed to initialize trigger buffer\n");
- goto error_free_dev_mem;
- }
atomic_set(&prox_state->common_attributes.data_ready, 0);
+
ret = hid_sensor_setup_trigger(indio_dev, name,
&prox_state->common_attributes);
if (ret) {
dev_err(&pdev->dev, "trigger setup failed\n");
- goto error_unreg_buffer_funcs;
+ goto error_free_dev_mem;
}
ret = iio_device_register(indio_dev);
@@ -330,9 +322,7 @@
error_iio_unreg:
iio_device_unregister(indio_dev);
error_remove_trigger:
- hid_sensor_remove_trigger(&prox_state->common_attributes);
-error_unreg_buffer_funcs:
- iio_triggered_buffer_cleanup(indio_dev);
+ hid_sensor_remove_trigger(indio_dev, &prox_state->common_attributes);
error_free_dev_mem:
kfree(indio_dev->channels);
return ret;
@@ -347,8 +337,7 @@
sensor_hub_remove_callback(hsdev, HID_USAGE_SENSOR_PROX);
iio_device_unregister(indio_dev);
- hid_sensor_remove_trigger(&prox_state->common_attributes);
- iio_triggered_buffer_cleanup(indio_dev);
+ hid_sensor_remove_trigger(indio_dev, &prox_state->common_attributes);
kfree(indio_dev->channels);
return 0;
diff --git a/drivers/iio/light/iqs621-als.c b/drivers/iio/light/iqs621-als.c
new file mode 100644
index 0000000..004ea89
--- /dev/null
+++ b/drivers/iio/light/iqs621-als.c
@@ -0,0 +1,618 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Azoteq IQS621/622 Ambient Light Sensors
+ *
+ * Copyright (C) 2019 Jeff LaBundy <jeff@labundy.com>
+ */
+
+#include <linux/device.h>
+#include <linux/iio/events.h>
+#include <linux/iio/iio.h>
+#include <linux/kernel.h>
+#include <linux/mfd/iqs62x.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/notifier.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+
+#define IQS621_ALS_FLAGS_LIGHT BIT(7)
+#define IQS621_ALS_FLAGS_RANGE GENMASK(3, 0)
+
+#define IQS621_ALS_UI_OUT 0x17
+
+#define IQS621_ALS_THRESH_DARK 0x80
+#define IQS621_ALS_THRESH_LIGHT 0x81
+
+#define IQS622_IR_RANGE 0x15
+#define IQS622_IR_FLAGS 0x16
+#define IQS622_IR_FLAGS_TOUCH BIT(1)
+#define IQS622_IR_FLAGS_PROX BIT(0)
+
+#define IQS622_IR_UI_OUT 0x17
+
+#define IQS622_IR_THRESH_PROX 0x91
+#define IQS622_IR_THRESH_TOUCH 0x92
+
+struct iqs621_als_private {
+ struct iqs62x_core *iqs62x;
+ struct iio_dev *indio_dev;
+ struct notifier_block notifier;
+ struct mutex lock;
+ bool light_en;
+ bool range_en;
+ bool prox_en;
+ u8 als_flags;
+ u8 ir_flags_mask;
+ u8 ir_flags;
+ u8 thresh_light;
+ u8 thresh_dark;
+ u8 thresh_prox;
+};
+
+static int iqs621_als_init(struct iqs621_als_private *iqs621_als)
+{
+ struct iqs62x_core *iqs62x = iqs621_als->iqs62x;
+ unsigned int event_mask = 0;
+ int ret;
+
+ switch (iqs621_als->ir_flags_mask) {
+ case IQS622_IR_FLAGS_TOUCH:
+ ret = regmap_write(iqs62x->regmap, IQS622_IR_THRESH_TOUCH,
+ iqs621_als->thresh_prox);
+ break;
+
+ case IQS622_IR_FLAGS_PROX:
+ ret = regmap_write(iqs62x->regmap, IQS622_IR_THRESH_PROX,
+ iqs621_als->thresh_prox);
+ break;
+
+ default:
+ ret = regmap_write(iqs62x->regmap, IQS621_ALS_THRESH_LIGHT,
+ iqs621_als->thresh_light);
+ if (ret)
+ return ret;
+
+ ret = regmap_write(iqs62x->regmap, IQS621_ALS_THRESH_DARK,
+ iqs621_als->thresh_dark);
+ }
+
+ if (ret)
+ return ret;
+
+ if (iqs621_als->light_en || iqs621_als->range_en)
+ event_mask |= iqs62x->dev_desc->als_mask;
+
+ if (iqs621_als->prox_en)
+ event_mask |= iqs62x->dev_desc->ir_mask;
+
+ return regmap_update_bits(iqs62x->regmap, IQS620_GLBL_EVENT_MASK,
+ event_mask, 0);
+}
+
+static int iqs621_als_notifier(struct notifier_block *notifier,
+ unsigned long event_flags, void *context)
+{
+ struct iqs62x_event_data *event_data = context;
+ struct iqs621_als_private *iqs621_als;
+ struct iio_dev *indio_dev;
+ bool light_new, light_old;
+ bool prox_new, prox_old;
+ u8 range_new, range_old;
+ s64 timestamp;
+ int ret;
+
+ iqs621_als = container_of(notifier, struct iqs621_als_private,
+ notifier);
+ indio_dev = iqs621_als->indio_dev;
+ timestamp = iio_get_time_ns(indio_dev);
+
+ mutex_lock(&iqs621_als->lock);
+
+ if (event_flags & BIT(IQS62X_EVENT_SYS_RESET)) {
+ ret = iqs621_als_init(iqs621_als);
+ if (ret) {
+ dev_err(indio_dev->dev.parent,
+ "Failed to re-initialize device: %d\n", ret);
+ ret = NOTIFY_BAD;
+ } else {
+ ret = NOTIFY_OK;
+ }
+
+ goto err_mutex;
+ }
+
+ if (!iqs621_als->light_en && !iqs621_als->range_en &&
+ !iqs621_als->prox_en) {
+ ret = NOTIFY_DONE;
+ goto err_mutex;
+ }
+
+ /* IQS621 only */
+ light_new = event_data->als_flags & IQS621_ALS_FLAGS_LIGHT;
+ light_old = iqs621_als->als_flags & IQS621_ALS_FLAGS_LIGHT;
+
+ if (iqs621_als->light_en && light_new && !light_old)
+ iio_push_event(indio_dev,
+ IIO_UNMOD_EVENT_CODE(IIO_LIGHT, 0,
+ IIO_EV_TYPE_THRESH,
+ IIO_EV_DIR_RISING),
+ timestamp);
+ else if (iqs621_als->light_en && !light_new && light_old)
+ iio_push_event(indio_dev,
+ IIO_UNMOD_EVENT_CODE(IIO_LIGHT, 0,
+ IIO_EV_TYPE_THRESH,
+ IIO_EV_DIR_FALLING),
+ timestamp);
+
+ /* IQS621 and IQS622 */
+ range_new = event_data->als_flags & IQS621_ALS_FLAGS_RANGE;
+ range_old = iqs621_als->als_flags & IQS621_ALS_FLAGS_RANGE;
+
+ if (iqs621_als->range_en && (range_new > range_old))
+ iio_push_event(indio_dev,
+ IIO_UNMOD_EVENT_CODE(IIO_INTENSITY, 0,
+ IIO_EV_TYPE_CHANGE,
+ IIO_EV_DIR_RISING),
+ timestamp);
+ else if (iqs621_als->range_en && (range_new < range_old))
+ iio_push_event(indio_dev,
+ IIO_UNMOD_EVENT_CODE(IIO_INTENSITY, 0,
+ IIO_EV_TYPE_CHANGE,
+ IIO_EV_DIR_FALLING),
+ timestamp);
+
+ /* IQS622 only */
+ prox_new = event_data->ir_flags & iqs621_als->ir_flags_mask;
+ prox_old = iqs621_als->ir_flags & iqs621_als->ir_flags_mask;
+
+ if (iqs621_als->prox_en && prox_new && !prox_old)
+ iio_push_event(indio_dev,
+ IIO_UNMOD_EVENT_CODE(IIO_PROXIMITY, 0,
+ IIO_EV_TYPE_THRESH,
+ IIO_EV_DIR_RISING),
+ timestamp);
+ else if (iqs621_als->prox_en && !prox_new && prox_old)
+ iio_push_event(indio_dev,
+ IIO_UNMOD_EVENT_CODE(IIO_PROXIMITY, 0,
+ IIO_EV_TYPE_THRESH,
+ IIO_EV_DIR_FALLING),
+ timestamp);
+
+ iqs621_als->als_flags = event_data->als_flags;
+ iqs621_als->ir_flags = event_data->ir_flags;
+ ret = NOTIFY_OK;
+
+err_mutex:
+ mutex_unlock(&iqs621_als->lock);
+
+ return ret;
+}
+
+static void iqs621_als_notifier_unregister(void *context)
+{
+ struct iqs621_als_private *iqs621_als = context;
+ struct iio_dev *indio_dev = iqs621_als->indio_dev;
+ int ret;
+
+ ret = blocking_notifier_chain_unregister(&iqs621_als->iqs62x->nh,
+ &iqs621_als->notifier);
+ if (ret)
+ dev_err(indio_dev->dev.parent,
+ "Failed to unregister notifier: %d\n", ret);
+}
+
+static int iqs621_als_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int *val, int *val2, long mask)
+{
+ struct iqs621_als_private *iqs621_als = iio_priv(indio_dev);
+ struct iqs62x_core *iqs62x = iqs621_als->iqs62x;
+ int ret;
+ __le16 val_buf;
+
+ switch (chan->type) {
+ case IIO_INTENSITY:
+ ret = regmap_read(iqs62x->regmap, chan->address, val);
+ if (ret)
+ return ret;
+
+ *val &= IQS621_ALS_FLAGS_RANGE;
+ return IIO_VAL_INT;
+
+ case IIO_PROXIMITY:
+ case IIO_LIGHT:
+ ret = regmap_raw_read(iqs62x->regmap, chan->address, &val_buf,
+ sizeof(val_buf));
+ if (ret)
+ return ret;
+
+ *val = le16_to_cpu(val_buf);
+ return IIO_VAL_INT;
+
+ default:
+ return -EINVAL;
+ }
+}
+
+static int iqs621_als_read_event_config(struct iio_dev *indio_dev,
+ const struct iio_chan_spec *chan,
+ enum iio_event_type type,
+ enum iio_event_direction dir)
+{
+ struct iqs621_als_private *iqs621_als = iio_priv(indio_dev);
+ int ret;
+
+ mutex_lock(&iqs621_als->lock);
+
+ switch (chan->type) {
+ case IIO_LIGHT:
+ ret = iqs621_als->light_en;
+ break;
+
+ case IIO_INTENSITY:
+ ret = iqs621_als->range_en;
+ break;
+
+ case IIO_PROXIMITY:
+ ret = iqs621_als->prox_en;
+ break;
+
+ default:
+ ret = -EINVAL;
+ }
+
+ mutex_unlock(&iqs621_als->lock);
+
+ return ret;
+}
+
+static int iqs621_als_write_event_config(struct iio_dev *indio_dev,
+ const struct iio_chan_spec *chan,
+ enum iio_event_type type,
+ enum iio_event_direction dir,
+ int state)
+{
+ struct iqs621_als_private *iqs621_als = iio_priv(indio_dev);
+ struct iqs62x_core *iqs62x = iqs621_als->iqs62x;
+ unsigned int val;
+ int ret;
+
+ mutex_lock(&iqs621_als->lock);
+
+ ret = regmap_read(iqs62x->regmap, iqs62x->dev_desc->als_flags, &val);
+ if (ret)
+ goto err_mutex;
+ iqs621_als->als_flags = val;
+
+ switch (chan->type) {
+ case IIO_LIGHT:
+ ret = regmap_update_bits(iqs62x->regmap, IQS620_GLBL_EVENT_MASK,
+ iqs62x->dev_desc->als_mask,
+ iqs621_als->range_en || state ? 0 :
+ 0xFF);
+ if (!ret)
+ iqs621_als->light_en = state;
+ break;
+
+ case IIO_INTENSITY:
+ ret = regmap_update_bits(iqs62x->regmap, IQS620_GLBL_EVENT_MASK,
+ iqs62x->dev_desc->als_mask,
+ iqs621_als->light_en || state ? 0 :
+ 0xFF);
+ if (!ret)
+ iqs621_als->range_en = state;
+ break;
+
+ case IIO_PROXIMITY:
+ ret = regmap_read(iqs62x->regmap, IQS622_IR_FLAGS, &val);
+ if (ret)
+ goto err_mutex;
+ iqs621_als->ir_flags = val;
+
+ ret = regmap_update_bits(iqs62x->regmap, IQS620_GLBL_EVENT_MASK,
+ iqs62x->dev_desc->ir_mask,
+ state ? 0 : 0xFF);
+ if (!ret)
+ iqs621_als->prox_en = state;
+ break;
+
+ default:
+ ret = -EINVAL;
+ }
+
+err_mutex:
+ mutex_unlock(&iqs621_als->lock);
+
+ return ret;
+}
+
+static int iqs621_als_read_event_value(struct iio_dev *indio_dev,
+ const struct iio_chan_spec *chan,
+ enum iio_event_type type,
+ enum iio_event_direction dir,
+ enum iio_event_info info,
+ int *val, int *val2)
+{
+ struct iqs621_als_private *iqs621_als = iio_priv(indio_dev);
+ int ret = IIO_VAL_INT;
+
+ mutex_lock(&iqs621_als->lock);
+
+ switch (dir) {
+ case IIO_EV_DIR_RISING:
+ *val = iqs621_als->thresh_light * 16;
+ break;
+
+ case IIO_EV_DIR_FALLING:
+ *val = iqs621_als->thresh_dark * 4;
+ break;
+
+ case IIO_EV_DIR_EITHER:
+ if (iqs621_als->ir_flags_mask == IQS622_IR_FLAGS_TOUCH)
+ *val = iqs621_als->thresh_prox * 4;
+ else
+ *val = iqs621_als->thresh_prox;
+ break;
+
+ default:
+ ret = -EINVAL;
+ }
+
+ mutex_unlock(&iqs621_als->lock);
+
+ return ret;
+}
+
+static int iqs621_als_write_event_value(struct iio_dev *indio_dev,
+ const struct iio_chan_spec *chan,
+ enum iio_event_type type,
+ enum iio_event_direction dir,
+ enum iio_event_info info,
+ int val, int val2)
+{
+ struct iqs621_als_private *iqs621_als = iio_priv(indio_dev);
+ struct iqs62x_core *iqs62x = iqs621_als->iqs62x;
+ unsigned int thresh_reg, thresh_val;
+ u8 ir_flags_mask, *thresh_cache;
+ int ret = -EINVAL;
+
+ mutex_lock(&iqs621_als->lock);
+
+ switch (dir) {
+ case IIO_EV_DIR_RISING:
+ thresh_reg = IQS621_ALS_THRESH_LIGHT;
+ thresh_val = val / 16;
+
+ thresh_cache = &iqs621_als->thresh_light;
+ ir_flags_mask = 0;
+ break;
+
+ case IIO_EV_DIR_FALLING:
+ thresh_reg = IQS621_ALS_THRESH_DARK;
+ thresh_val = val / 4;
+
+ thresh_cache = &iqs621_als->thresh_dark;
+ ir_flags_mask = 0;
+ break;
+
+ case IIO_EV_DIR_EITHER:
+ /*
+ * The IQS622 supports two detection thresholds, both measured
+ * in the same arbitrary units reported by read_raw: proximity
+ * (0 through 255 in steps of 1), and touch (0 through 1020 in
+ * steps of 4).
+ *
+ * Based on the single detection threshold chosen by the user,
+ * select the hardware threshold that gives the best trade-off
+ * between range and resolution.
+ *
+ * By default, the close-range (but coarse) touch threshold is
+ * chosen during probe.
+ */
+ switch (val) {
+ case 0 ... 255:
+ thresh_reg = IQS622_IR_THRESH_PROX;
+ thresh_val = val;
+
+ ir_flags_mask = IQS622_IR_FLAGS_PROX;
+ break;
+
+ case 256 ... 1020:
+ thresh_reg = IQS622_IR_THRESH_TOUCH;
+ thresh_val = val / 4;
+
+ ir_flags_mask = IQS622_IR_FLAGS_TOUCH;
+ break;
+
+ default:
+ goto err_mutex;
+ }
+
+ thresh_cache = &iqs621_als->thresh_prox;
+ break;
+
+ default:
+ goto err_mutex;
+ }
+
+ if (thresh_val > 0xFF)
+ goto err_mutex;
+
+ ret = regmap_write(iqs62x->regmap, thresh_reg, thresh_val);
+ if (ret)
+ goto err_mutex;
+
+ *thresh_cache = thresh_val;
+ iqs621_als->ir_flags_mask = ir_flags_mask;
+
+err_mutex:
+ mutex_unlock(&iqs621_als->lock);
+
+ return ret;
+}
+
+static const struct iio_info iqs621_als_info = {
+ .read_raw = &iqs621_als_read_raw,
+ .read_event_config = iqs621_als_read_event_config,
+ .write_event_config = iqs621_als_write_event_config,
+ .read_event_value = iqs621_als_read_event_value,
+ .write_event_value = iqs621_als_write_event_value,
+};
+
+static const struct iio_event_spec iqs621_als_range_events[] = {
+ {
+ .type = IIO_EV_TYPE_CHANGE,
+ .dir = IIO_EV_DIR_EITHER,
+ .mask_separate = BIT(IIO_EV_INFO_ENABLE),
+ },
+};
+
+static const struct iio_event_spec iqs621_als_light_events[] = {
+ {
+ .type = IIO_EV_TYPE_THRESH,
+ .dir = IIO_EV_DIR_EITHER,
+ .mask_separate = BIT(IIO_EV_INFO_ENABLE),
+ },
+ {
+ .type = IIO_EV_TYPE_THRESH,
+ .dir = IIO_EV_DIR_RISING,
+ .mask_separate = BIT(IIO_EV_INFO_VALUE),
+ },
+ {
+ .type = IIO_EV_TYPE_THRESH,
+ .dir = IIO_EV_DIR_FALLING,
+ .mask_separate = BIT(IIO_EV_INFO_VALUE),
+ },
+};
+
+static const struct iio_chan_spec iqs621_als_channels[] = {
+ {
+ .type = IIO_INTENSITY,
+ .address = IQS621_ALS_FLAGS,
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
+ .event_spec = iqs621_als_range_events,
+ .num_event_specs = ARRAY_SIZE(iqs621_als_range_events),
+ },
+ {
+ .type = IIO_LIGHT,
+ .address = IQS621_ALS_UI_OUT,
+ .info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED),
+ .event_spec = iqs621_als_light_events,
+ .num_event_specs = ARRAY_SIZE(iqs621_als_light_events),
+ },
+};
+
+static const struct iio_event_spec iqs622_als_prox_events[] = {
+ {
+ .type = IIO_EV_TYPE_THRESH,
+ .dir = IIO_EV_DIR_EITHER,
+ .mask_separate = BIT(IIO_EV_INFO_ENABLE) |
+ BIT(IIO_EV_INFO_VALUE),
+ },
+};
+
+static const struct iio_chan_spec iqs622_als_channels[] = {
+ {
+ .type = IIO_INTENSITY,
+ .channel2 = IIO_MOD_LIGHT_BOTH,
+ .address = IQS622_ALS_FLAGS,
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
+ .event_spec = iqs621_als_range_events,
+ .num_event_specs = ARRAY_SIZE(iqs621_als_range_events),
+ .modified = true,
+ },
+ {
+ .type = IIO_INTENSITY,
+ .channel2 = IIO_MOD_LIGHT_IR,
+ .address = IQS622_IR_RANGE,
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
+ .modified = true,
+ },
+ {
+ .type = IIO_PROXIMITY,
+ .address = IQS622_IR_UI_OUT,
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
+ .event_spec = iqs622_als_prox_events,
+ .num_event_specs = ARRAY_SIZE(iqs622_als_prox_events),
+ },
+};
+
+static int iqs621_als_probe(struct platform_device *pdev)
+{
+ struct iqs62x_core *iqs62x = dev_get_drvdata(pdev->dev.parent);
+ struct iqs621_als_private *iqs621_als;
+ struct iio_dev *indio_dev;
+ unsigned int val;
+ int ret;
+
+ indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(*iqs621_als));
+ if (!indio_dev)
+ return -ENOMEM;
+
+ iqs621_als = iio_priv(indio_dev);
+ iqs621_als->iqs62x = iqs62x;
+ iqs621_als->indio_dev = indio_dev;
+
+ if (iqs62x->dev_desc->prod_num == IQS622_PROD_NUM) {
+ ret = regmap_read(iqs62x->regmap, IQS622_IR_THRESH_TOUCH,
+ &val);
+ if (ret)
+ return ret;
+ iqs621_als->thresh_prox = val;
+ iqs621_als->ir_flags_mask = IQS622_IR_FLAGS_TOUCH;
+
+ indio_dev->channels = iqs622_als_channels;
+ indio_dev->num_channels = ARRAY_SIZE(iqs622_als_channels);
+ } else {
+ ret = regmap_read(iqs62x->regmap, IQS621_ALS_THRESH_LIGHT,
+ &val);
+ if (ret)
+ return ret;
+ iqs621_als->thresh_light = val;
+
+ ret = regmap_read(iqs62x->regmap, IQS621_ALS_THRESH_DARK,
+ &val);
+ if (ret)
+ return ret;
+ iqs621_als->thresh_dark = val;
+
+ indio_dev->channels = iqs621_als_channels;
+ indio_dev->num_channels = ARRAY_SIZE(iqs621_als_channels);
+ }
+
+ indio_dev->modes = INDIO_DIRECT_MODE;
+ indio_dev->name = iqs62x->dev_desc->dev_name;
+ indio_dev->info = &iqs621_als_info;
+
+ mutex_init(&iqs621_als->lock);
+
+ iqs621_als->notifier.notifier_call = iqs621_als_notifier;
+ ret = blocking_notifier_chain_register(&iqs621_als->iqs62x->nh,
+ &iqs621_als->notifier);
+ if (ret) {
+ dev_err(&pdev->dev, "Failed to register notifier: %d\n", ret);
+ return ret;
+ }
+
+ ret = devm_add_action_or_reset(&pdev->dev,
+ iqs621_als_notifier_unregister,
+ iqs621_als);
+ if (ret)
+ return ret;
+
+ return devm_iio_device_register(&pdev->dev, indio_dev);
+}
+
+static struct platform_driver iqs621_als_platform_driver = {
+ .driver = {
+ .name = "iqs621-als",
+ },
+ .probe = iqs621_als_probe,
+};
+module_platform_driver(iqs621_als_platform_driver);
+
+MODULE_AUTHOR("Jeff LaBundy <jeff@labundy.com>");
+MODULE_DESCRIPTION("Azoteq IQS621/622 Ambient Light Sensors");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:iqs621-als");
diff --git a/drivers/iio/light/isl29018.c b/drivers/iio/light/isl29018.c
index 805a74f..2689867 100644
--- a/drivers/iio/light/isl29018.c
+++ b/drivers/iio/light/isl29018.c
@@ -746,12 +746,9 @@
chip->suspended = false;
chip->vcc_reg = devm_regulator_get(&client->dev, "vcc");
- if (IS_ERR(chip->vcc_reg)) {
- err = PTR_ERR(chip->vcc_reg);
- if (err != -EPROBE_DEFER)
- dev_err(&client->dev, "failed to get VCC regulator!\n");
- return err;
- }
+ if (IS_ERR(chip->vcc_reg))
+ return dev_err_probe(&client->dev, PTR_ERR(chip->vcc_reg),
+ "failed to get VCC regulator!\n");
err = regulator_enable(chip->vcc_reg);
if (err) {
@@ -782,7 +779,6 @@
indio_dev->channels = isl29018_chip_info_tbl[dev_id].channels;
indio_dev->num_channels = isl29018_chip_info_tbl[dev_id].num_channels;
indio_dev->name = name;
- indio_dev->dev.parent = &client->dev;
indio_dev->modes = INDIO_DIRECT_MODE;
return devm_iio_device_register(&client->dev, indio_dev);
diff --git a/drivers/iio/light/isl29028.c b/drivers/iio/light/isl29028.c
index 4d220c8..2f8b494 100644
--- a/drivers/iio/light/isl29028.c
+++ b/drivers/iio/light/isl29028.c
@@ -620,7 +620,6 @@
indio_dev->channels = isl29028_channels;
indio_dev->num_channels = ARRAY_SIZE(isl29028_channels);
indio_dev->name = id->name;
- indio_dev->dev.parent = &client->dev;
indio_dev->modes = INDIO_DIRECT_MODE;
pm_runtime_enable(&client->dev);
diff --git a/drivers/iio/light/isl29125.c b/drivers/iio/light/isl29125.c
index 1590692..ba53b50 100644
--- a/drivers/iio/light/isl29125.c
+++ b/drivers/iio/light/isl29125.c
@@ -217,7 +217,7 @@
.attrs = &isl29125_attribute_group,
};
-static int isl29125_buffer_preenable(struct iio_dev *indio_dev)
+static int isl29125_buffer_postenable(struct iio_dev *indio_dev)
{
struct isl29125_data *data = iio_priv(indio_dev);
@@ -229,11 +229,6 @@
static int isl29125_buffer_predisable(struct iio_dev *indio_dev)
{
struct isl29125_data *data = iio_priv(indio_dev);
- int ret;
-
- ret = iio_triggered_buffer_predisable(indio_dev);
- if (ret < 0)
- return ret;
data->conf1 &= ~ISL29125_MODE_MASK;
data->conf1 |= ISL29125_MODE_PD;
@@ -242,8 +237,7 @@
}
static const struct iio_buffer_setup_ops isl29125_buffer_setup_ops = {
- .preenable = isl29125_buffer_preenable,
- .postenable = &iio_triggered_buffer_postenable,
+ .postenable = isl29125_buffer_postenable,
.predisable = isl29125_buffer_predisable,
};
@@ -262,7 +256,6 @@
i2c_set_clientdata(client, indio_dev);
data->client = client;
- indio_dev->dev.parent = &client->dev;
indio_dev->info = &isl29125_info;
indio_dev->name = ISL29125_DRV_NAME;
indio_dev->channels = isl29125_channels;
diff --git a/drivers/iio/light/jsa1212.c b/drivers/iio/light/jsa1212.c
index 13deeeb..724a0ec 100644
--- a/drivers/iio/light/jsa1212.c
+++ b/drivers/iio/light/jsa1212.c
@@ -338,7 +338,6 @@
if (ret < 0)
return ret;
- indio_dev->dev.parent = &client->dev;
indio_dev->channels = jsa1212_channels;
indio_dev->num_channels = ARRAY_SIZE(jsa1212_channels);
indio_dev->name = JSA1212_DRIVER_NAME;
diff --git a/drivers/iio/light/lm3533-als.c b/drivers/iio/light/lm3533-als.c
index 6733b52..8a62124 100644
--- a/drivers/iio/light/lm3533-als.c
+++ b/drivers/iio/light/lm3533-als.c
@@ -742,7 +742,7 @@
if (val < LM3533_ALS_RESISTOR_MIN || val > LM3533_ALS_RESISTOR_MAX) {
dev_err(&als->pdev->dev, "invalid resistor value\n");
return -EINVAL;
- };
+ }
ret = lm3533_write(als->lm3533, LM3533_REG_ALS_RESISTOR_SELECT, val);
if (ret) {
@@ -852,7 +852,7 @@
indio_dev->channels = lm3533_als_channels;
indio_dev->num_channels = ARRAY_SIZE(lm3533_als_channels);
indio_dev->name = dev_name(&pdev->dev);
- indio_dev->dev.parent = pdev->dev.parent;
+ iio_device_set_parent(indio_dev, pdev->dev.parent);
indio_dev->modes = INDIO_DIRECT_MODE;
als = iio_priv(indio_dev);
diff --git a/drivers/iio/light/ltr501.c b/drivers/iio/light/ltr501.c
index 8bc01e8..379236a 100644
--- a/drivers/iio/light/ltr501.c
+++ b/drivers/iio/light/ltr501.c
@@ -104,12 +104,12 @@
int uscale;
};
-static struct ltr501_gain ltr501_als_gain_tbl[] = {
+static const struct ltr501_gain ltr501_als_gain_tbl[] = {
{1, 0},
{0, 5000},
};
-static struct ltr501_gain ltr559_als_gain_tbl[] = {
+static const struct ltr501_gain ltr559_als_gain_tbl[] = {
{1, 0},
{0, 500000},
{0, 250000},
@@ -120,14 +120,14 @@
{0, 10000},
};
-static struct ltr501_gain ltr501_ps_gain_tbl[] = {
+static const struct ltr501_gain ltr501_ps_gain_tbl[] = {
{1, 0},
{0, 250000},
{0, 125000},
{0, 62500},
};
-static struct ltr501_gain ltr559_ps_gain_tbl[] = {
+static const struct ltr501_gain ltr559_ps_gain_tbl[] = {
{0, 62500}, /* x16 gain */
{0, 31250}, /* x32 gain */
{0, 15625}, /* bits X1 are for x64 gain */
@@ -136,9 +136,9 @@
struct ltr501_chip_info {
u8 partid;
- struct ltr501_gain *als_gain;
+ const struct ltr501_gain *als_gain;
int als_gain_tbl_size;
- struct ltr501_gain *ps_gain;
+ const struct ltr501_gain *ps_gain;
int ps_gain_tbl_size;
u8 als_mode_active;
u8 als_gain_mask;
@@ -195,7 +195,7 @@
return -EINVAL;
}
-static int ltr501_als_read_samp_freq(struct ltr501_data *data,
+static int ltr501_als_read_samp_freq(const struct ltr501_data *data,
int *val, int *val2)
{
int ret, i;
@@ -213,7 +213,7 @@
return IIO_VAL_INT_PLUS_MICRO;
}
-static int ltr501_ps_read_samp_freq(struct ltr501_data *data,
+static int ltr501_ps_read_samp_freq(const struct ltr501_data *data,
int *val, int *val2)
{
int ret, i;
@@ -269,7 +269,7 @@
return ret;
}
-static int ltr501_als_read_samp_period(struct ltr501_data *data, int *val)
+static int ltr501_als_read_samp_period(const struct ltr501_data *data, int *val)
{
int ret, i;
@@ -285,7 +285,7 @@
return IIO_VAL_INT;
}
-static int ltr501_ps_read_samp_period(struct ltr501_data *data, int *val)
+static int ltr501_ps_read_samp_period(const struct ltr501_data *data, int *val)
{
int ret, i;
@@ -324,7 +324,7 @@
return lux / 1000;
}
-static int ltr501_drdy(struct ltr501_data *data, u8 drdy_mask)
+static int ltr501_drdy(const struct ltr501_data *data, u8 drdy_mask)
{
int tries = 100;
int ret, status;
@@ -376,7 +376,8 @@
}
/* read int time in micro seconds */
-static int ltr501_read_it_time(struct ltr501_data *data, int *val, int *val2)
+static int ltr501_read_it_time(const struct ltr501_data *data,
+ int *val, int *val2)
{
int ret, index;
@@ -394,7 +395,7 @@
return IIO_VAL_INT_PLUS_MICRO;
}
-static int ltr501_read_als(struct ltr501_data *data, __le16 buf[2])
+static int ltr501_read_als(const struct ltr501_data *data, __le16 buf[2])
{
int ret;
@@ -406,7 +407,7 @@
buf, 2 * sizeof(__le16));
}
-static int ltr501_read_ps(struct ltr501_data *data)
+static int ltr501_read_ps(const struct ltr501_data *data)
{
__le16 status;
int ret;
@@ -423,7 +424,7 @@
return le16_to_cpu(status);
}
-static int ltr501_read_intr_prst(struct ltr501_data *data,
+static int ltr501_read_intr_prst(const struct ltr501_data *data,
enum iio_chan_type type,
int *val2)
{
@@ -720,7 +721,7 @@
return -EINVAL;
}
-static int ltr501_get_gain_index(struct ltr501_gain *gain, int size,
+static int ltr501_get_gain_index(const struct ltr501_gain *gain, int size,
int val, int val2)
{
int i;
@@ -852,14 +853,14 @@
return ret;
}
-static int ltr501_read_thresh(struct iio_dev *indio_dev,
+static int ltr501_read_thresh(const struct iio_dev *indio_dev,
const struct iio_chan_spec *chan,
enum iio_event_type type,
enum iio_event_direction dir,
enum iio_event_info info,
int *val, int *val2)
{
- struct ltr501_data *data = iio_priv(indio_dev);
+ const struct ltr501_data *data = iio_priv(indio_dev);
int ret, thresh_data;
switch (chan->type) {
@@ -1270,9 +1271,9 @@
if (mask & LTR501_STATUS_ALS_RDY) {
ret = regmap_bulk_read(data->regmap, LTR501_ALS_DATA1,
- (u8 *)als_buf, sizeof(als_buf));
+ als_buf, sizeof(als_buf));
if (ret < 0)
- return ret;
+ goto done;
if (test_bit(0, indio_dev->active_scan_mask))
scan.channels[j++] = le16_to_cpu(als_buf[1]);
if (test_bit(1, indio_dev->active_scan_mask))
@@ -1369,7 +1370,7 @@
}
}
-static struct regmap_config ltr501_regmap_config = {
+static const struct regmap_config ltr501_regmap_config = {
.name = LTR501_REGMAP_NAME,
.reg_bits = 8,
.val_bits = 8,
@@ -1489,7 +1490,6 @@
if ((partid >> 4) != data->chip_info->partid)
return -ENODEV;
- indio_dev->dev.parent = &client->dev;
indio_dev->info = data->chip_info->info;
indio_dev->channels = data->chip_info->channels;
indio_dev->num_channels = data->chip_info->no_channels;
diff --git a/drivers/iio/light/lv0104cs.c b/drivers/iio/light/lv0104cs.c
index 55b8e28..c2aef88 100644
--- a/drivers/iio/light/lv0104cs.c
+++ b/drivers/iio/light/lv0104cs.c
@@ -7,7 +7,7 @@
*
* 7-bit I2C slave address: 0x13
*
- * Link to data sheet: http://www.onsemi.com/pub/Collateral/LV0104CS-D.PDF
+ * Link to data sheet: https://www.onsemi.com/pub/Collateral/LV0104CS-D.PDF
*/
#include <linux/kernel.h>
@@ -502,7 +502,6 @@
return ret;
indio_dev->modes = INDIO_DIRECT_MODE;
- indio_dev->dev.parent = &client->dev;
indio_dev->channels = lv0104cs_channels;
indio_dev->num_channels = ARRAY_SIZE(lv0104cs_channels);
indio_dev->name = client->name;
diff --git a/drivers/iio/light/max44000.c b/drivers/iio/light/max44000.c
index 8cc619d..b8e721b 100644
--- a/drivers/iio/light/max44000.c
+++ b/drivers/iio/light/max44000.c
@@ -542,7 +542,6 @@
i2c_set_clientdata(client, indio_dev);
mutex_init(&data->lock);
- indio_dev->dev.parent = &client->dev;
indio_dev->info = &max44000_info;
indio_dev->name = MAX44000_DRV_NAME;
indio_dev->channels = max44000_channels;
diff --git a/drivers/iio/light/max44009.c b/drivers/iio/light/max44009.c
index 00ba154..801e5a0 100644
--- a/drivers/iio/light/max44009.c
+++ b/drivers/iio/light/max44009.c
@@ -501,7 +501,6 @@
data = iio_priv(indio_dev);
i2c_set_clientdata(client, indio_dev);
data->client = client;
- indio_dev->dev.parent = &client->dev;
indio_dev->info = &max44009_info;
indio_dev->modes = INDIO_DIRECT_MODE;
indio_dev->name = MAX44009_DRV_NAME;
diff --git a/drivers/iio/light/noa1305.c b/drivers/iio/light/noa1305.c
index 5ebfbc5..a308fbc 100644
--- a/drivers/iio/light/noa1305.c
+++ b/drivers/iio/light/noa1305.c
@@ -270,7 +270,6 @@
return ret;
}
- indio_dev->dev.parent = &client->dev;
indio_dev->info = &noa1305_info;
indio_dev->channels = noa1305_channels;
indio_dev->num_channels = ARRAY_SIZE(noa1305_channels);
diff --git a/drivers/iio/light/opt3001.c b/drivers/iio/light/opt3001.c
index 92004a2..ff77625 100644
--- a/drivers/iio/light/opt3001.c
+++ b/drivers/iio/light/opt3001.c
@@ -2,7 +2,7 @@
/**
* opt3001.c - Texas Instruments OPT3001 Light Sensor
*
- * Copyright (C) 2014 Texas Instruments Incorporated - http://www.ti.com
+ * Copyright (C) 2014 Texas Instruments Incorporated - https://www.ti.com
*
* Author: Andreas Dannenberg <dannenberg@ti.com>
* Based on previous work from: Felipe Balbi <balbi@ti.com>
@@ -16,6 +16,7 @@
#include <linux/irq.h>
#include <linux/kernel.h>
#include <linux/module.h>
+#include <linux/mod_devicetable.h>
#include <linux/mutex.h>
#include <linux/slab.h>
#include <linux/types.h>
@@ -275,6 +276,8 @@
ret = wait_event_timeout(opt->result_ready_queue,
opt->result_ready,
msecs_to_jiffies(OPT3001_RESULT_READY_LONG));
+ if (ret == 0)
+ return -ETIMEDOUT;
} else {
/* Sleep for result ready time */
timeout = (opt->int_time == OPT3001_INT_TIME_SHORT) ?
@@ -311,9 +314,7 @@
/* Disallow IRQ to access the device while lock is active */
opt->ok_to_ignore_lock = false;
- if (ret == 0)
- return -ETIMEDOUT;
- else if (ret < 0)
+ if (ret < 0)
return ret;
if (opt->use_irq) {
@@ -767,7 +768,6 @@
iio->name = client->name;
iio->channels = opt3001_channels;
iio->num_channels = ARRAY_SIZE(opt3001_channels);
- iio->dev.parent = dev;
iio->modes = INDIO_DIRECT_MODE;
iio->info = &opt3001_info;
@@ -844,7 +844,7 @@
.driver = {
.name = "opt3001",
- .of_match_table = of_match_ptr(opt3001_of_match),
+ .of_match_table = opt3001_of_match,
},
};
diff --git a/drivers/iio/light/pa12203001.c b/drivers/iio/light/pa12203001.c
index 0295783..bfade65 100644
--- a/drivers/iio/light/pa12203001.c
+++ b/drivers/iio/light/pa12203001.c
@@ -362,7 +362,6 @@
mutex_init(&data->lock);
- indio_dev->dev.parent = &client->dev;
indio_dev->info = &pa12203001_info;
indio_dev->name = PA12203001_DRIVER_NAME;
indio_dev->channels = pa12203001_channels;
diff --git a/drivers/iio/light/rpr0521.c b/drivers/iio/light/rpr0521.c
index 254c5b0..31224a3 100644
--- a/drivers/iio/light/rpr0521.c
+++ b/drivers/iio/light/rpr0521.c
@@ -579,8 +579,6 @@
static const struct iio_buffer_setup_ops rpr0521_buffer_setup_ops = {
.preenable = rpr0521_buffer_preenable,
- .postenable = iio_triggered_buffer_postenable,
- .predisable = iio_triggered_buffer_predisable,
.postdisable = rpr0521_buffer_postdisable,
};
@@ -957,7 +955,6 @@
mutex_init(&data->lock);
- indio_dev->dev.parent = &client->dev;
indio_dev->info = &rpr0521_info;
indio_dev->name = RPR0521_DRV_NAME;
indio_dev->channels = rpr0521_channels;
diff --git a/drivers/iio/light/si1133.c b/drivers/iio/light/si1133.c
index 9174ab9..c280b41 100644
--- a/drivers/iio/light/si1133.c
+++ b/drivers/iio/light/si1133.c
@@ -17,6 +17,8 @@
#include <linux/util_macros.h>
+#include <asm/unaligned.h>
+
#define SI1133_REG_PART_ID 0x00
#define SI1133_REG_REV_ID 0x01
#define SI1133_REG_MFR_ID 0x02
@@ -104,8 +106,6 @@
#define SI1133_LUX_BUFFER_SIZE 9
#define SI1133_MEASURE_BUFFER_SIZE 3
-#define SI1133_SIGN_BIT_INDEX 23
-
static const int si1133_scale_available[] = {
1, 2, 4, 8, 16, 32, 64, 128};
@@ -633,8 +633,7 @@
if (err)
return err;
- *val = sign_extend32((buffer[0] << 16) | (buffer[1] << 8) | buffer[2],
- SI1133_SIGN_BIT_INDEX);
+ *val = sign_extend32(get_unaligned_be24(&buffer[0]), 23);
return err;
}
@@ -723,16 +722,11 @@
if (err)
return err;
- high_vis =
- sign_extend32((buffer[0] << 16) | (buffer[1] << 8) | buffer[2],
- SI1133_SIGN_BIT_INDEX);
+ high_vis = sign_extend32(get_unaligned_be24(&buffer[0]), 23);
- low_vis =
- sign_extend32((buffer[3] << 16) | (buffer[4] << 8) | buffer[5],
- SI1133_SIGN_BIT_INDEX);
+ low_vis = sign_extend32(get_unaligned_be24(&buffer[3]), 23);
- ir = sign_extend32((buffer[6] << 16) | (buffer[7] << 8) | buffer[8],
- SI1133_SIGN_BIT_INDEX);
+ ir = sign_extend32(get_unaligned_be24(&buffer[6]), 23);
if (high_vis > SI1133_ADC_THRESHOLD || ir > SI1133_ADC_THRESHOLD)
lux = si1133_calc_polynomial(high_vis, ir,
@@ -1021,7 +1015,6 @@
i2c_set_clientdata(client, iio_dev);
data->client = client;
- iio_dev->dev.parent = &client->dev;
iio_dev->name = id->name;
iio_dev->channels = si1133_channels;
iio_dev->num_channels = ARRAY_SIZE(si1133_channels);
diff --git a/drivers/iio/light/si1145.c b/drivers/iio/light/si1145.c
index cb40345..b304801 100644
--- a/drivers/iio/light/si1145.c
+++ b/drivers/iio/light/si1145.c
@@ -17,7 +17,6 @@
#include <linux/slab.h>
#include <linux/delay.h>
#include <linux/irq.h>
-#include <linux/gpio.h>
#include <linux/iio/iio.h>
#include <linux/iio/sysfs.h>
@@ -191,7 +190,7 @@
u8 buffer[24] __aligned(8);
};
-/**
+/*
* __si1145_command_reset() - Send CMD_NOP and wait for response 0
*
* Does not modify data->rsp_seq
@@ -225,7 +224,7 @@
}
}
-/**
+/*
* si1145_command() - Execute a command and poll the response register
*
* All conversion overflows are reported as -EOVERFLOW
@@ -1046,7 +1045,7 @@
SI1145_LED_CURRENT_45mA);
if (ret < 0)
return ret;
- /* fallthrough */
+ fallthrough;
case 2:
ret = i2c_smbus_write_byte_data(client,
SI1145_REG_PS_LED21,
@@ -1175,12 +1174,10 @@
static const struct iio_buffer_setup_ops si1145_buffer_setup_ops = {
.preenable = si1145_buffer_preenable,
- .postenable = iio_triggered_buffer_postenable,
- .predisable = iio_triggered_buffer_predisable,
.validate_scan_mask = si1145_validate_scan_mask,
};
-/**
+/*
* si1145_trigger_set_state() - Set trigger state
*
* When not using triggers interrupts are disabled and measurement rate is
@@ -1311,7 +1308,6 @@
return -ENODEV;
}
- indio_dev->dev.parent = &client->dev;
indio_dev->name = id->name;
indio_dev->channels = data->part_info->channels;
indio_dev->num_channels = data->part_info->num_channels;
diff --git a/drivers/iio/light/st_uvis25_core.c b/drivers/iio/light/st_uvis25_core.c
index 9aaff35..1055594 100644
--- a/drivers/iio/light/st_uvis25_core.c
+++ b/drivers/iio/light/st_uvis25_core.c
@@ -227,8 +227,6 @@
static const struct iio_buffer_setup_ops st_uvis25_buffer_ops = {
.preenable = st_uvis25_buffer_preenable,
- .postenable = iio_triggered_buffer_postenable,
- .predisable = iio_triggered_buffer_predisable,
.postdisable = st_uvis25_buffer_postdisable,
};
@@ -305,7 +303,6 @@
return err;
iio_dev->modes = INDIO_DIRECT_MODE;
- iio_dev->dev.parent = dev;
iio_dev->channels = st_uvis25_channels;
iio_dev->num_channels = ARRAY_SIZE(st_uvis25_channels);
iio_dev->name = ST_UVIS25_DEV_NAME;
diff --git a/drivers/iio/light/st_uvis25_i2c.c b/drivers/iio/light/st_uvis25_i2c.c
index dacbac6..98cd49e 100644
--- a/drivers/iio/light/st_uvis25_i2c.c
+++ b/drivers/iio/light/st_uvis25_i2c.c
@@ -9,7 +9,7 @@
#include <linux/kernel.h>
#include <linux/module.h>
-#include <linux/acpi.h>
+#include <linux/mod_devicetable.h>
#include <linux/i2c.h>
#include <linux/slab.h>
#include <linux/regmap.h>
@@ -32,8 +32,8 @@
regmap = devm_regmap_init_i2c(client, &st_uvis25_i2c_regmap_config);
if (IS_ERR(regmap)) {
- dev_err(&client->dev, "Failed to register i2c regmap %d\n",
- (int)PTR_ERR(regmap));
+ dev_err(&client->dev, "Failed to register i2c regmap %ld\n",
+ PTR_ERR(regmap));
return PTR_ERR(regmap);
}
@@ -56,7 +56,7 @@
.driver = {
.name = "st_uvis25_i2c",
.pm = &st_uvis25_pm_ops,
- .of_match_table = of_match_ptr(st_uvis25_i2c_of_match),
+ .of_match_table = st_uvis25_i2c_of_match,
},
.probe = st_uvis25_i2c_probe,
.id_table = st_uvis25_i2c_id_table,
diff --git a/drivers/iio/light/st_uvis25_spi.c b/drivers/iio/light/st_uvis25_spi.c
index a9ceae4..af9d94d 100644
--- a/drivers/iio/light/st_uvis25_spi.c
+++ b/drivers/iio/light/st_uvis25_spi.c
@@ -9,6 +9,7 @@
#include <linux/kernel.h>
#include <linux/module.h>
+#include <linux/mod_devicetable.h>
#include <linux/spi/spi.h>
#include <linux/slab.h>
#include <linux/regmap.h>
@@ -31,8 +32,8 @@
regmap = devm_regmap_init_spi(spi, &st_uvis25_spi_regmap_config);
if (IS_ERR(regmap)) {
- dev_err(&spi->dev, "Failed to register spi regmap %d\n",
- (int)PTR_ERR(regmap));
+ dev_err(&spi->dev, "Failed to register spi regmap %ld\n",
+ PTR_ERR(regmap));
return PTR_ERR(regmap);
}
@@ -55,7 +56,7 @@
.driver = {
.name = "st_uvis25_spi",
.pm = &st_uvis25_pm_ops,
- .of_match_table = of_match_ptr(st_uvis25_spi_of_match),
+ .of_match_table = st_uvis25_spi_of_match,
},
.probe = st_uvis25_spi_probe,
.id_table = st_uvis25_spi_id_table,
diff --git a/drivers/iio/light/stk3310.c b/drivers/iio/light/stk3310.c
index 185c24a..6654573 100644
--- a/drivers/iio/light/stk3310.c
+++ b/drivers/iio/light/stk3310.c
@@ -37,6 +37,7 @@
#define STK3310_CHIP_ID_VAL 0x13
#define STK3311_CHIP_ID_VAL 0x1D
+#define STK3311X_CHIP_ID_VAL 0x12
#define STK3335_CHIP_ID_VAL 0x51
#define STK3310_PSINT_EN 0x01
#define STK3310_PS_MAX_VAL 0xFFFF
@@ -453,6 +454,7 @@
if (chipid != STK3310_CHIP_ID_VAL &&
chipid != STK3311_CHIP_ID_VAL &&
+ chipid != STK3311X_CHIP_ID_VAL &&
chipid != STK3335_CHIP_ID_VAL) {
dev_err(&client->dev, "invalid chip id: 0x%x\n", chipid);
return -ENODEV;
@@ -487,7 +489,7 @@
}
}
-static struct regmap_config stk3310_regmap_config = {
+static const struct regmap_config stk3310_regmap_config = {
.name = STK3310_REGMAP_NAME,
.reg_bits = 8,
.val_bits = 8,
@@ -544,9 +546,8 @@
mutex_lock(&data->lock);
ret = regmap_field_read(data->reg_flag_nf, &dir);
if (ret < 0) {
- dev_err(&data->client->dev, "register read failed\n");
- mutex_unlock(&data->lock);
- return ret;
+ dev_err(&data->client->dev, "register read failed: %d\n", ret);
+ goto out;
}
event = IIO_UNMOD_EVENT_CODE(IIO_PROXIMITY, 1,
IIO_EV_TYPE_THRESH,
@@ -558,6 +559,7 @@
ret = regmap_field_write(data->reg_flag_psint, 0);
if (ret < 0)
dev_err(&data->client->dev, "failed to reset interrupts\n");
+out:
mutex_unlock(&data->lock);
return IRQ_HANDLED;
@@ -585,7 +587,6 @@
if (ret < 0)
return ret;
- indio_dev->dev.parent = &client->dev;
indio_dev->info = &stk3310_info;
indio_dev->name = STK3310_DRIVER_NAME;
indio_dev->modes = INDIO_DIRECT_MODE;
diff --git a/drivers/iio/light/tcs3414.c b/drivers/iio/light/tcs3414.c
index 85bfe3b..0593abd 100644
--- a/drivers/iio/light/tcs3414.c
+++ b/drivers/iio/light/tcs3414.c
@@ -244,7 +244,7 @@
.attrs = &tcs3414_attribute_group,
};
-static int tcs3414_buffer_preenable(struct iio_dev *indio_dev)
+static int tcs3414_buffer_postenable(struct iio_dev *indio_dev)
{
struct tcs3414_data *data = iio_priv(indio_dev);
@@ -256,11 +256,6 @@
static int tcs3414_buffer_predisable(struct iio_dev *indio_dev)
{
struct tcs3414_data *data = iio_priv(indio_dev);
- int ret;
-
- ret = iio_triggered_buffer_predisable(indio_dev);
- if (ret < 0)
- return ret;
data->control &= ~TCS3414_CONTROL_ADC_EN;
return i2c_smbus_write_byte_data(data->client, TCS3414_CONTROL,
@@ -268,8 +263,7 @@
}
static const struct iio_buffer_setup_ops tcs3414_buffer_setup_ops = {
- .preenable = tcs3414_buffer_preenable,
- .postenable = &iio_triggered_buffer_postenable,
+ .postenable = tcs3414_buffer_postenable,
.predisable = tcs3414_buffer_predisable,
};
@@ -288,7 +282,6 @@
i2c_set_clientdata(client, indio_dev);
data->client = client;
- indio_dev->dev.parent = &client->dev;
indio_dev->info = &tcs3414_info;
indio_dev->name = TCS3414_DRV_NAME;
indio_dev->channels = tcs3414_channels;
diff --git a/drivers/iio/light/tcs3472.c b/drivers/iio/light/tcs3472.c
index 9ea543c..371c6a3 100644
--- a/drivers/iio/light/tcs3472.c
+++ b/drivers/iio/light/tcs3472.c
@@ -458,7 +458,6 @@
data->client = client;
mutex_init(&data->lock);
- indio_dev->dev.parent = &client->dev;
indio_dev->info = &tcs3472_info;
indio_dev->name = TCS3472_DRV_NAME;
indio_dev->channels = tcs3472_channels;
diff --git a/drivers/iio/light/tsl2563.c b/drivers/iio/light/tsl2563.c
index d8c40a8..abc8d7d 100644
--- a/drivers/iio/light/tsl2563.c
+++ b/drivers/iio/light/tsl2563.c
@@ -69,7 +69,7 @@
#define TSL2563_TIMING_GAIN16 0x10
#define TSL2563_TIMING_GAIN1 0x00
-#define TSL2563_INT_DISBLED 0x00
+#define TSL2563_INT_DISABLED 0x00
#define TSL2563_INT_LEVEL 0x10
#define TSL2563_INT_PERSIST(n) ((n) & 0x0F)
@@ -713,7 +713,7 @@
chip = iio_priv(indio_dev);
- i2c_set_clientdata(client, chip);
+ i2c_set_clientdata(client, indio_dev);
chip->client = client;
err = tsl2563_detect(chip);
@@ -750,7 +750,6 @@
indio_dev->name = client->name;
indio_dev->channels = tsl2563_channels;
indio_dev->num_channels = ARRAY_SIZE(tsl2563_channels);
- indio_dev->dev.parent = &client->dev;
indio_dev->modes = INDIO_DIRECT_MODE;
if (client->irq)
@@ -797,8 +796,8 @@
static int tsl2563_remove(struct i2c_client *client)
{
- struct tsl2563_chip *chip = i2c_get_clientdata(client);
- struct iio_dev *indio_dev = iio_priv_to_dev(chip);
+ struct iio_dev *indio_dev = i2c_get_clientdata(client);
+ struct tsl2563_chip *chip = iio_priv(indio_dev);
iio_device_unregister(indio_dev);
if (!chip->int_enabled)
@@ -816,7 +815,8 @@
#ifdef CONFIG_PM_SLEEP
static int tsl2563_suspend(struct device *dev)
{
- struct tsl2563_chip *chip = i2c_get_clientdata(to_i2c_client(dev));
+ struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev));
+ struct tsl2563_chip *chip = iio_priv(indio_dev);
int ret;
mutex_lock(&chip->lock);
@@ -834,7 +834,8 @@
static int tsl2563_resume(struct device *dev)
{
- struct tsl2563_chip *chip = i2c_get_clientdata(to_i2c_client(dev));
+ struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev));
+ struct tsl2563_chip *chip = iio_priv(indio_dev);
int ret;
mutex_lock(&chip->lock);
diff --git a/drivers/iio/light/tsl2583.c b/drivers/iio/light/tsl2583.c
index fe6001a..40b7dd2 100644
--- a/drivers/iio/light/tsl2583.c
+++ b/drivers/iio/light/tsl2583.c
@@ -848,7 +848,6 @@
indio_dev->info = &tsl2583_info;
indio_dev->channels = tsl2583_channels;
indio_dev->num_channels = ARRAY_SIZE(tsl2583_channels);
- indio_dev->dev.parent = &clientp->dev;
indio_dev->modes = INDIO_DIRECT_MODE;
indio_dev->name = chip->client->name;
diff --git a/drivers/iio/light/tsl2772.c b/drivers/iio/light/tsl2772.c
index be37fcb..d792053 100644
--- a/drivers/iio/light/tsl2772.c
+++ b/drivers/iio/light/tsl2772.c
@@ -932,7 +932,7 @@
{
struct tsl2772_chip *chip = iio_priv(dev_to_iio_dev(dev));
- return snprintf(buf, PAGE_SIZE, "%d\n", chip->settings.als_cal_target);
+ return scnprintf(buf, PAGE_SIZE, "%d\n", chip->settings.als_cal_target);
}
static ssize_t in_illuminance0_target_input_store(struct device *dev,
@@ -986,7 +986,7 @@
int offset = 0;
while (i < TSL2772_MAX_LUX_TABLE_SIZE) {
- offset += snprintf(buf + offset, PAGE_SIZE, "%u,%u,",
+ offset += scnprintf(buf + offset, PAGE_SIZE - offset, "%u,%u,",
chip->tsl2772_device_lux[i].ch0,
chip->tsl2772_device_lux[i].ch1);
if (chip->tsl2772_device_lux[i].ch0 == 0) {
@@ -1000,7 +1000,7 @@
i++;
}
- offset += snprintf(buf + offset, PAGE_SIZE, "\n");
+ offset += scnprintf(buf + offset, PAGE_SIZE - offset, "\n");
return offset;
}
@@ -1776,14 +1776,8 @@
ret = devm_regulator_bulk_get(&clientp->dev,
ARRAY_SIZE(chip->supplies),
chip->supplies);
- if (ret < 0) {
- if (ret != -EPROBE_DEFER)
- dev_err(&clientp->dev,
- "Failed to get regulators: %d\n",
- ret);
-
- return ret;
- }
+ if (ret < 0)
+ return dev_err_probe(&clientp->dev, ret, "Failed to get regulators\n");
ret = regulator_bulk_enable(ARRAY_SIZE(chip->supplies), chip->supplies);
if (ret < 0) {
@@ -1833,7 +1827,6 @@
&tsl2772_chip_info_tbl[device_channel_config[id->driver_data]];
indio_dev->info = chip->chip_info->info;
- indio_dev->dev.parent = &clientp->dev;
indio_dev->modes = INDIO_DIRECT_MODE;
indio_dev->name = chip->client->name;
indio_dev->num_channels = chip->chip_info->chan_table_elements;
diff --git a/drivers/iio/light/tsl4531.c b/drivers/iio/light/tsl4531.c
index 0dfc664..70505ba 100644
--- a/drivers/iio/light/tsl4531.c
+++ b/drivers/iio/light/tsl4531.c
@@ -192,7 +192,6 @@
if (ret < 0)
return ret;
- indio_dev->dev.parent = &client->dev;
indio_dev->info = &tsl4531_info;
indio_dev->channels = tsl4531_channels;
indio_dev->num_channels = ARRAY_SIZE(tsl4531_channels);
diff --git a/drivers/iio/light/us5182d.c b/drivers/iio/light/us5182d.c
index b995f21..393f27b 100644
--- a/drivers/iio/light/us5182d.c
+++ b/drivers/iio/light/us5182d.c
@@ -446,8 +446,8 @@
/**
* us5182d_update_dark_th - update Darh_Th registers
- * @data us5182d_data structure
- * @index index in us5182d_dark_ths array to use for the updated value
+ * @data: us5182d_data structure
+ * @index: index in us5182d_dark_ths array to use for the updated value
*
* Function needs to be called with a lock held because it needs two i2c write
* byte operations as these registers (0x27 0x28) don't work in word mode
@@ -469,8 +469,8 @@
/**
* us5182d_apply_scale - update the ALS scale
- * @data us5182d_data structure
- * @index index in us5182d_scales array to use for the updated value
+ * @data: us5182d_data structure
+ * @index: index in us5182d_scales array to use for the updated value
*
* Function needs to be called with a lock held as we're having more than one
* i2c operation.
@@ -851,7 +851,6 @@
mutex_init(&data->lock);
- indio_dev->dev.parent = &client->dev;
indio_dev->info = &us5182d_info;
indio_dev->name = US5182D_DRV_NAME;
indio_dev->channels = us5182d_channels;
diff --git a/drivers/iio/light/vcnl4000.c b/drivers/iio/light/vcnl4000.c
index 7384a3f..f4feb44 100644
--- a/drivers/iio/light/vcnl4000.c
+++ b/drivers/iio/light/vcnl4000.c
@@ -5,6 +5,7 @@
*
* Copyright 2012 Peter Meerwald <pmeerw@pmeerw.net>
* Copyright 2019 Pursim SPC
+ * Copyright 2020 Mathieu Othacehe <m.othacehe@gmail.com>
*
* IIO driver for:
* VCNL4000/10/20 (7-bit I2C slave address 0x13)
@@ -13,18 +14,23 @@
*
* TODO:
* allow to adjust IR current
- * proximity threshold and event handling
- * periodic ALS/proximity measurement (VCNL4010/20)
- * interrupts (VCNL4010/20/40, VCNL4200)
+ * interrupts (VCNL4040, VCNL4200)
*/
#include <linux/module.h>
#include <linux/i2c.h>
#include <linux/err.h>
#include <linux/delay.h>
+#include <linux/pm_runtime.h>
+#include <linux/interrupt.h>
+#include <linux/iio/buffer.h>
+#include <linux/iio/events.h>
#include <linux/iio/iio.h>
#include <linux/iio/sysfs.h>
+#include <linux/iio/trigger.h>
+#include <linux/iio/trigger_consumer.h>
+#include <linux/iio/triggered_buffer.h>
#define VCNL4000_DRV_NAME "vcnl4000"
#define VCNL4000_PROD_ID 0x01
@@ -34,14 +40,22 @@
#define VCNL4000_COMMAND 0x80 /* Command register */
#define VCNL4000_PROD_REV 0x81 /* Product ID and Revision ID */
+#define VCNL4010_PROX_RATE 0x82 /* Proximity rate */
#define VCNL4000_LED_CURRENT 0x83 /* IR LED current for proximity mode */
#define VCNL4000_AL_PARAM 0x84 /* Ambient light parameter register */
+#define VCNL4010_ALS_PARAM 0x84 /* ALS rate */
#define VCNL4000_AL_RESULT_HI 0x85 /* Ambient light result register, MSB */
#define VCNL4000_AL_RESULT_LO 0x86 /* Ambient light result register, LSB */
#define VCNL4000_PS_RESULT_HI 0x87 /* Proximity result register, MSB */
#define VCNL4000_PS_RESULT_LO 0x88 /* Proximity result register, LSB */
#define VCNL4000_PS_MEAS_FREQ 0x89 /* Proximity test signal frequency */
+#define VCNL4010_INT_CTRL 0x89 /* Interrupt control */
#define VCNL4000_PS_MOD_ADJ 0x8a /* Proximity modulator timing adjustment */
+#define VCNL4010_LOW_THR_HI 0x8a /* Low threshold, MSB */
+#define VCNL4010_LOW_THR_LO 0x8b /* Low threshold, LSB */
+#define VCNL4010_HIGH_THR_HI 0x8c /* High threshold, MSB */
+#define VCNL4010_HIGH_THR_LO 0x8d /* High threshold, LSB */
+#define VCNL4010_ISR 0x8e /* Interrupt status */
#define VCNL4200_AL_CONF 0x00 /* Ambient light configuration */
#define VCNL4200_PS_CONF1 0x03 /* Proximity configuration */
@@ -56,6 +70,38 @@
#define VCNL4000_PS_RDY BIT(5) /* proximity data ready? */
#define VCNL4000_AL_OD BIT(4) /* start on-demand ALS measurement */
#define VCNL4000_PS_OD BIT(3) /* start on-demand proximity measurement */
+#define VCNL4000_ALS_EN BIT(2) /* start ALS measurement */
+#define VCNL4000_PROX_EN BIT(1) /* start proximity measurement */
+#define VCNL4000_SELF_TIMED_EN BIT(0) /* start self-timed measurement */
+
+/* Bit masks for interrupt registers. */
+#define VCNL4010_INT_THR_SEL BIT(0) /* Select threshold interrupt source */
+#define VCNL4010_INT_THR_EN BIT(1) /* Threshold interrupt type */
+#define VCNL4010_INT_ALS_EN BIT(2) /* Enable on ALS data ready */
+#define VCNL4010_INT_PROX_EN BIT(3) /* Enable on proximity data ready */
+
+#define VCNL4010_INT_THR_HIGH 0 /* High threshold exceeded */
+#define VCNL4010_INT_THR_LOW 1 /* Low threshold exceeded */
+#define VCNL4010_INT_ALS 2 /* ALS data ready */
+#define VCNL4010_INT_PROXIMITY 3 /* Proximity data ready */
+
+#define VCNL4010_INT_THR \
+ (BIT(VCNL4010_INT_THR_LOW) | BIT(VCNL4010_INT_THR_HIGH))
+#define VCNL4010_INT_DRDY \
+ (BIT(VCNL4010_INT_PROXIMITY) | BIT(VCNL4010_INT_ALS))
+
+static const int vcnl4010_prox_sampling_frequency[][2] = {
+ {1, 950000},
+ {3, 906250},
+ {7, 812500},
+ {16, 625000},
+ {31, 250000},
+ {62, 500000},
+ {125, 0},
+ {250, 0},
+};
+
+#define VCNL4000_SLEEP_DELAY_MS 2000 /* before we enter pm_runtime_suspend */
enum vcnl4000_device_ids {
VCNL4000,
@@ -80,13 +126,19 @@
struct mutex vcnl4000_lock;
struct vcnl4200_channel vcnl4200_al;
struct vcnl4200_channel vcnl4200_ps;
+ uint32_t near_level;
};
struct vcnl4000_chip_spec {
const char *prod;
+ struct iio_chan_spec const *channels;
+ const int num_channels;
+ const struct iio_info *info;
+ bool irq_support;
int (*init)(struct vcnl4000_data *data);
int (*measure_light)(struct vcnl4000_data *data, int *val);
int (*measure_proximity)(struct vcnl4000_data *data, int *val);
+ int (*set_power_state)(struct vcnl4000_data *data, bool on);
};
static const struct i2c_device_id vcnl4000_id[] = {
@@ -99,6 +151,12 @@
};
MODULE_DEVICE_TABLE(i2c, vcnl4000_id);
+static int vcnl4000_set_power_state(struct vcnl4000_data *data, bool on)
+{
+ /* no suspend op */
+ return 0;
+}
+
static int vcnl4000_init(struct vcnl4000_data *data)
{
int ret, prod_id;
@@ -127,9 +185,31 @@
data->al_scale = 250000;
mutex_init(&data->vcnl4000_lock);
- return 0;
+ return data->chip_spec->set_power_state(data, true);
};
+static int vcnl4200_set_power_state(struct vcnl4000_data *data, bool on)
+{
+ u16 val = on ? 0 /* power on */ : 1 /* shut down */;
+ int ret;
+
+ ret = i2c_smbus_write_word_data(data->client, VCNL4200_AL_CONF, val);
+ if (ret < 0)
+ return ret;
+
+ ret = i2c_smbus_write_word_data(data->client, VCNL4200_PS_CONF1, val);
+ if (ret < 0)
+ return ret;
+
+ if (on) {
+ /* Wait at least one integration cycle before fetching data */
+ data->vcnl4200_al.last_measurement = ktime_get();
+ data->vcnl4200_ps.last_measurement = ktime_get();
+ }
+
+ return 0;
+}
+
static int vcnl4200_init(struct vcnl4000_data *data)
{
int ret, id;
@@ -155,14 +235,6 @@
data->rev = (ret >> 8) & 0xf;
- /* Set defaults and enable both channels */
- ret = i2c_smbus_write_word_data(data->client, VCNL4200_AL_CONF, 0);
- if (ret < 0)
- return ret;
- ret = i2c_smbus_write_word_data(data->client, VCNL4200_PS_CONF1, 0);
- if (ret < 0)
- return ret;
-
data->vcnl4200_al.reg = VCNL4200_AL_DATA;
data->vcnl4200_ps.reg = VCNL4200_PS_DATA;
switch (id) {
@@ -181,14 +253,37 @@
data->al_scale = 120000;
break;
}
- data->vcnl4200_al.last_measurement = ktime_set(0, 0);
- data->vcnl4200_ps.last_measurement = ktime_set(0, 0);
mutex_init(&data->vcnl4200_al.lock);
mutex_init(&data->vcnl4200_ps.lock);
+ ret = data->chip_spec->set_power_state(data, true);
+ if (ret < 0)
+ return ret;
+
return 0;
};
+static int vcnl4000_read_data(struct vcnl4000_data *data, u8 data_reg, int *val)
+{
+ s32 ret;
+
+ ret = i2c_smbus_read_word_swapped(data->client, data_reg);
+ if (ret < 0)
+ return ret;
+
+ *val = ret;
+ return 0;
+}
+
+static int vcnl4000_write_data(struct vcnl4000_data *data, u8 data_reg, int val)
+{
+ if (val > U16_MAX)
+ return -ERANGE;
+
+ return i2c_smbus_write_word_swapped(data->client, data_reg, val);
+}
+
+
static int vcnl4000_measure(struct vcnl4000_data *data, u8 req_mask,
u8 rdy_mask, u8 data_reg, int *val)
{
@@ -219,12 +314,11 @@
goto fail;
}
- ret = i2c_smbus_read_word_swapped(data->client, data_reg);
+ ret = vcnl4000_read_data(data, data_reg, val);
if (ret < 0)
goto fail;
mutex_unlock(&data->vcnl4000_lock);
- *val = ret;
return 0;
@@ -284,43 +378,51 @@
return vcnl4200_measure(data, &data->vcnl4200_ps, val);
}
-static const struct vcnl4000_chip_spec vcnl4000_chip_spec_cfg[] = {
- [VCNL4000] = {
- .prod = "VCNL4000",
- .init = vcnl4000_init,
- .measure_light = vcnl4000_measure_light,
- .measure_proximity = vcnl4000_measure_proximity,
- },
- [VCNL4010] = {
- .prod = "VCNL4010/4020",
- .init = vcnl4000_init,
- .measure_light = vcnl4000_measure_light,
- .measure_proximity = vcnl4000_measure_proximity,
- },
- [VCNL4040] = {
- .prod = "VCNL4040",
- .init = vcnl4200_init,
- .measure_light = vcnl4200_measure_light,
- .measure_proximity = vcnl4200_measure_proximity,
- },
- [VCNL4200] = {
- .prod = "VCNL4200",
- .init = vcnl4200_init,
- .measure_light = vcnl4200_measure_light,
- .measure_proximity = vcnl4200_measure_proximity,
- },
-};
+static int vcnl4010_read_proxy_samp_freq(struct vcnl4000_data *data, int *val,
+ int *val2)
+{
+ int ret;
-static const struct iio_chan_spec vcnl4000_channels[] = {
- {
- .type = IIO_LIGHT,
- .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
- BIT(IIO_CHAN_INFO_SCALE),
- }, {
- .type = IIO_PROXIMITY,
- .info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
+ ret = i2c_smbus_read_byte_data(data->client, VCNL4010_PROX_RATE);
+ if (ret < 0)
+ return ret;
+
+ if (ret >= ARRAY_SIZE(vcnl4010_prox_sampling_frequency))
+ return -EINVAL;
+
+ *val = vcnl4010_prox_sampling_frequency[ret][0];
+ *val2 = vcnl4010_prox_sampling_frequency[ret][1];
+
+ return 0;
+}
+
+static bool vcnl4010_is_in_periodic_mode(struct vcnl4000_data *data)
+{
+ int ret;
+
+ ret = i2c_smbus_read_byte_data(data->client, VCNL4000_COMMAND);
+ if (ret < 0)
+ return false;
+
+ return !!(ret & VCNL4000_SELF_TIMED_EN);
+}
+
+static int vcnl4000_set_pm_runtime_state(struct vcnl4000_data *data, bool on)
+{
+ struct device *dev = &data->client->dev;
+ int ret;
+
+ if (on) {
+ ret = pm_runtime_get_sync(dev);
+ if (ret < 0)
+ pm_runtime_put_noidle(dev);
+ } else {
+ pm_runtime_mark_last_busy(dev);
+ ret = pm_runtime_put_autosuspend(dev);
}
-};
+
+ return ret;
+}
static int vcnl4000_read_raw(struct iio_dev *indio_dev,
struct iio_chan_spec const *chan,
@@ -331,20 +433,26 @@
switch (mask) {
case IIO_CHAN_INFO_RAW:
+ ret = vcnl4000_set_pm_runtime_state(data, true);
+ if (ret < 0)
+ return ret;
+
switch (chan->type) {
case IIO_LIGHT:
ret = data->chip_spec->measure_light(data, val);
- if (ret < 0)
- return ret;
- return IIO_VAL_INT;
+ if (!ret)
+ ret = IIO_VAL_INT;
+ break;
case IIO_PROXIMITY:
ret = data->chip_spec->measure_proximity(data, val);
- if (ret < 0)
- return ret;
- return IIO_VAL_INT;
+ if (!ret)
+ ret = IIO_VAL_INT;
+ break;
default:
- return -EINVAL;
+ ret = -EINVAL;
}
+ vcnl4000_set_pm_runtime_state(data, false);
+ return ret;
case IIO_CHAN_INFO_SCALE:
if (chan->type != IIO_LIGHT)
return -EINVAL;
@@ -357,10 +465,550 @@
}
}
+static int vcnl4010_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int *val, int *val2, long mask)
+{
+ int ret;
+ struct vcnl4000_data *data = iio_priv(indio_dev);
+
+ switch (mask) {
+ case IIO_CHAN_INFO_RAW:
+ case IIO_CHAN_INFO_SCALE:
+ ret = iio_device_claim_direct_mode(indio_dev);
+ if (ret)
+ return ret;
+
+ /* Protect against event capture. */
+ if (vcnl4010_is_in_periodic_mode(data)) {
+ ret = -EBUSY;
+ } else {
+ ret = vcnl4000_read_raw(indio_dev, chan, val, val2,
+ mask);
+ }
+
+ iio_device_release_direct_mode(indio_dev);
+ return ret;
+ case IIO_CHAN_INFO_SAMP_FREQ:
+ switch (chan->type) {
+ case IIO_PROXIMITY:
+ ret = vcnl4010_read_proxy_samp_freq(data, val, val2);
+ if (ret < 0)
+ return ret;
+ return IIO_VAL_INT_PLUS_MICRO;
+ default:
+ return -EINVAL;
+ }
+ default:
+ return -EINVAL;
+ }
+}
+
+static int vcnl4010_read_avail(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ const int **vals, int *type, int *length,
+ long mask)
+{
+ switch (mask) {
+ case IIO_CHAN_INFO_SAMP_FREQ:
+ *vals = (int *)vcnl4010_prox_sampling_frequency;
+ *type = IIO_VAL_INT_PLUS_MICRO;
+ *length = 2 * ARRAY_SIZE(vcnl4010_prox_sampling_frequency);
+ return IIO_AVAIL_LIST;
+ default:
+ return -EINVAL;
+ }
+}
+
+static int vcnl4010_write_proxy_samp_freq(struct vcnl4000_data *data, int val,
+ int val2)
+{
+ unsigned int i;
+ int index = -1;
+
+ for (i = 0; i < ARRAY_SIZE(vcnl4010_prox_sampling_frequency); i++) {
+ if (val == vcnl4010_prox_sampling_frequency[i][0] &&
+ val2 == vcnl4010_prox_sampling_frequency[i][1]) {
+ index = i;
+ break;
+ }
+ }
+
+ if (index < 0)
+ return -EINVAL;
+
+ return i2c_smbus_write_byte_data(data->client, VCNL4010_PROX_RATE,
+ index);
+}
+
+static int vcnl4010_write_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int val, int val2, long mask)
+{
+ int ret;
+ struct vcnl4000_data *data = iio_priv(indio_dev);
+
+ ret = iio_device_claim_direct_mode(indio_dev);
+ if (ret)
+ return ret;
+
+ /* Protect against event capture. */
+ if (vcnl4010_is_in_periodic_mode(data)) {
+ ret = -EBUSY;
+ goto end;
+ }
+
+ switch (mask) {
+ case IIO_CHAN_INFO_SAMP_FREQ:
+ switch (chan->type) {
+ case IIO_PROXIMITY:
+ ret = vcnl4010_write_proxy_samp_freq(data, val, val2);
+ goto end;
+ default:
+ ret = -EINVAL;
+ goto end;
+ }
+ default:
+ ret = -EINVAL;
+ goto end;
+ }
+
+end:
+ iio_device_release_direct_mode(indio_dev);
+ return ret;
+}
+
+static int vcnl4010_read_event(struct iio_dev *indio_dev,
+ const struct iio_chan_spec *chan,
+ enum iio_event_type type,
+ enum iio_event_direction dir,
+ enum iio_event_info info,
+ int *val, int *val2)
+{
+ int ret;
+ struct vcnl4000_data *data = iio_priv(indio_dev);
+
+ switch (info) {
+ case IIO_EV_INFO_VALUE:
+ switch (dir) {
+ case IIO_EV_DIR_RISING:
+ ret = vcnl4000_read_data(data, VCNL4010_HIGH_THR_HI,
+ val);
+ if (ret < 0)
+ return ret;
+ return IIO_VAL_INT;
+ case IIO_EV_DIR_FALLING:
+ ret = vcnl4000_read_data(data, VCNL4010_LOW_THR_HI,
+ val);
+ if (ret < 0)
+ return ret;
+ return IIO_VAL_INT;
+ default:
+ return -EINVAL;
+ }
+ default:
+ return -EINVAL;
+ }
+}
+
+static int vcnl4010_write_event(struct iio_dev *indio_dev,
+ const struct iio_chan_spec *chan,
+ enum iio_event_type type,
+ enum iio_event_direction dir,
+ enum iio_event_info info,
+ int val, int val2)
+{
+ int ret;
+ struct vcnl4000_data *data = iio_priv(indio_dev);
+
+ switch (info) {
+ case IIO_EV_INFO_VALUE:
+ switch (dir) {
+ case IIO_EV_DIR_RISING:
+ ret = vcnl4000_write_data(data, VCNL4010_HIGH_THR_HI,
+ val);
+ if (ret < 0)
+ return ret;
+ return IIO_VAL_INT;
+ case IIO_EV_DIR_FALLING:
+ ret = vcnl4000_write_data(data, VCNL4010_LOW_THR_HI,
+ val);
+ if (ret < 0)
+ return ret;
+ return IIO_VAL_INT;
+ default:
+ return -EINVAL;
+ }
+ default:
+ return -EINVAL;
+ }
+}
+
+static bool vcnl4010_is_thr_enabled(struct vcnl4000_data *data)
+{
+ int ret;
+
+ ret = i2c_smbus_read_byte_data(data->client, VCNL4010_INT_CTRL);
+ if (ret < 0)
+ return false;
+
+ return !!(ret & VCNL4010_INT_THR_EN);
+}
+
+static int vcnl4010_read_event_config(struct iio_dev *indio_dev,
+ const struct iio_chan_spec *chan,
+ enum iio_event_type type,
+ enum iio_event_direction dir)
+{
+ struct vcnl4000_data *data = iio_priv(indio_dev);
+
+ switch (chan->type) {
+ case IIO_PROXIMITY:
+ return vcnl4010_is_thr_enabled(data);
+ default:
+ return -EINVAL;
+ }
+}
+
+static int vcnl4010_config_threshold(struct iio_dev *indio_dev, bool state)
+{
+ struct vcnl4000_data *data = iio_priv(indio_dev);
+ int ret;
+ int icr;
+ int command;
+
+ if (state) {
+ ret = iio_device_claim_direct_mode(indio_dev);
+ if (ret)
+ return ret;
+
+ /* Enable periodic measurement of proximity data. */
+ command = VCNL4000_SELF_TIMED_EN | VCNL4000_PROX_EN;
+
+ /*
+ * Enable interrupts on threshold, for proximity data by
+ * default.
+ */
+ icr = VCNL4010_INT_THR_EN;
+ } else {
+ if (!vcnl4010_is_thr_enabled(data))
+ return 0;
+
+ command = 0;
+ icr = 0;
+ }
+
+ ret = i2c_smbus_write_byte_data(data->client, VCNL4000_COMMAND,
+ command);
+ if (ret < 0)
+ goto end;
+
+ ret = i2c_smbus_write_byte_data(data->client, VCNL4010_INT_CTRL, icr);
+
+end:
+ if (state)
+ iio_device_release_direct_mode(indio_dev);
+
+ return ret;
+}
+
+static int vcnl4010_write_event_config(struct iio_dev *indio_dev,
+ const struct iio_chan_spec *chan,
+ enum iio_event_type type,
+ enum iio_event_direction dir,
+ int state)
+{
+ switch (chan->type) {
+ case IIO_PROXIMITY:
+ return vcnl4010_config_threshold(indio_dev, state);
+ default:
+ return -EINVAL;
+ }
+}
+
+static ssize_t vcnl4000_read_near_level(struct iio_dev *indio_dev,
+ uintptr_t priv,
+ const struct iio_chan_spec *chan,
+ char *buf)
+{
+ struct vcnl4000_data *data = iio_priv(indio_dev);
+
+ return sprintf(buf, "%u\n", data->near_level);
+}
+
+static const struct iio_chan_spec_ext_info vcnl4000_ext_info[] = {
+ {
+ .name = "nearlevel",
+ .shared = IIO_SEPARATE,
+ .read = vcnl4000_read_near_level,
+ },
+ { /* sentinel */ }
+};
+
+static const struct iio_event_spec vcnl4000_event_spec[] = {
+ {
+ .type = IIO_EV_TYPE_THRESH,
+ .dir = IIO_EV_DIR_RISING,
+ .mask_separate = BIT(IIO_EV_INFO_VALUE),
+ }, {
+ .type = IIO_EV_TYPE_THRESH,
+ .dir = IIO_EV_DIR_FALLING,
+ .mask_separate = BIT(IIO_EV_INFO_VALUE),
+ }, {
+ .type = IIO_EV_TYPE_THRESH,
+ .dir = IIO_EV_DIR_EITHER,
+ .mask_separate = BIT(IIO_EV_INFO_ENABLE),
+ }
+};
+
+static const struct iio_chan_spec vcnl4000_channels[] = {
+ {
+ .type = IIO_LIGHT,
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
+ BIT(IIO_CHAN_INFO_SCALE),
+ }, {
+ .type = IIO_PROXIMITY,
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
+ .ext_info = vcnl4000_ext_info,
+ }
+};
+
+static const struct iio_chan_spec vcnl4010_channels[] = {
+ {
+ .type = IIO_LIGHT,
+ .scan_index = -1,
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
+ BIT(IIO_CHAN_INFO_SCALE),
+ }, {
+ .type = IIO_PROXIMITY,
+ .scan_index = 0,
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
+ BIT(IIO_CHAN_INFO_SAMP_FREQ),
+ .info_mask_separate_available = BIT(IIO_CHAN_INFO_SAMP_FREQ),
+ .event_spec = vcnl4000_event_spec,
+ .num_event_specs = ARRAY_SIZE(vcnl4000_event_spec),
+ .ext_info = vcnl4000_ext_info,
+ .scan_type = {
+ .sign = 'u',
+ .realbits = 16,
+ .storagebits = 16,
+ .endianness = IIO_CPU,
+ },
+ },
+ IIO_CHAN_SOFT_TIMESTAMP(1),
+};
+
static const struct iio_info vcnl4000_info = {
.read_raw = vcnl4000_read_raw,
};
+static const struct iio_info vcnl4010_info = {
+ .read_raw = vcnl4010_read_raw,
+ .read_avail = vcnl4010_read_avail,
+ .write_raw = vcnl4010_write_raw,
+ .read_event_value = vcnl4010_read_event,
+ .write_event_value = vcnl4010_write_event,
+ .read_event_config = vcnl4010_read_event_config,
+ .write_event_config = vcnl4010_write_event_config,
+};
+
+static const struct vcnl4000_chip_spec vcnl4000_chip_spec_cfg[] = {
+ [VCNL4000] = {
+ .prod = "VCNL4000",
+ .init = vcnl4000_init,
+ .measure_light = vcnl4000_measure_light,
+ .measure_proximity = vcnl4000_measure_proximity,
+ .set_power_state = vcnl4000_set_power_state,
+ .channels = vcnl4000_channels,
+ .num_channels = ARRAY_SIZE(vcnl4000_channels),
+ .info = &vcnl4000_info,
+ .irq_support = false,
+ },
+ [VCNL4010] = {
+ .prod = "VCNL4010/4020",
+ .init = vcnl4000_init,
+ .measure_light = vcnl4000_measure_light,
+ .measure_proximity = vcnl4000_measure_proximity,
+ .set_power_state = vcnl4000_set_power_state,
+ .channels = vcnl4010_channels,
+ .num_channels = ARRAY_SIZE(vcnl4010_channels),
+ .info = &vcnl4010_info,
+ .irq_support = true,
+ },
+ [VCNL4040] = {
+ .prod = "VCNL4040",
+ .init = vcnl4200_init,
+ .measure_light = vcnl4200_measure_light,
+ .measure_proximity = vcnl4200_measure_proximity,
+ .set_power_state = vcnl4200_set_power_state,
+ .channels = vcnl4000_channels,
+ .num_channels = ARRAY_SIZE(vcnl4000_channels),
+ .info = &vcnl4000_info,
+ .irq_support = false,
+ },
+ [VCNL4200] = {
+ .prod = "VCNL4200",
+ .init = vcnl4200_init,
+ .measure_light = vcnl4200_measure_light,
+ .measure_proximity = vcnl4200_measure_proximity,
+ .set_power_state = vcnl4200_set_power_state,
+ .channels = vcnl4000_channels,
+ .num_channels = ARRAY_SIZE(vcnl4000_channels),
+ .info = &vcnl4000_info,
+ .irq_support = false,
+ },
+};
+
+static irqreturn_t vcnl4010_irq_thread(int irq, void *p)
+{
+ struct iio_dev *indio_dev = p;
+ struct vcnl4000_data *data = iio_priv(indio_dev);
+ unsigned long isr;
+ int ret;
+
+ ret = i2c_smbus_read_byte_data(data->client, VCNL4010_ISR);
+ if (ret < 0)
+ goto end;
+
+ isr = ret;
+
+ if (isr & VCNL4010_INT_THR) {
+ if (test_bit(VCNL4010_INT_THR_LOW, &isr)) {
+ iio_push_event(indio_dev,
+ IIO_UNMOD_EVENT_CODE(
+ IIO_PROXIMITY,
+ 1,
+ IIO_EV_TYPE_THRESH,
+ IIO_EV_DIR_FALLING),
+ iio_get_time_ns(indio_dev));
+ }
+
+ if (test_bit(VCNL4010_INT_THR_HIGH, &isr)) {
+ iio_push_event(indio_dev,
+ IIO_UNMOD_EVENT_CODE(
+ IIO_PROXIMITY,
+ 1,
+ IIO_EV_TYPE_THRESH,
+ IIO_EV_DIR_RISING),
+ iio_get_time_ns(indio_dev));
+ }
+
+ i2c_smbus_write_byte_data(data->client, VCNL4010_ISR,
+ isr & VCNL4010_INT_THR);
+ }
+
+ if (isr & VCNL4010_INT_DRDY && iio_buffer_enabled(indio_dev))
+ iio_trigger_poll_chained(indio_dev->trig);
+
+end:
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t vcnl4010_trigger_handler(int irq, void *p)
+{
+ struct iio_poll_func *pf = p;
+ struct iio_dev *indio_dev = pf->indio_dev;
+ struct vcnl4000_data *data = iio_priv(indio_dev);
+ const unsigned long *active_scan_mask = indio_dev->active_scan_mask;
+ u16 buffer[8] __aligned(8) = {0}; /* 1x16-bit + naturally aligned ts */
+ bool data_read = false;
+ unsigned long isr;
+ int val = 0;
+ int ret;
+
+ ret = i2c_smbus_read_byte_data(data->client, VCNL4010_ISR);
+ if (ret < 0)
+ goto end;
+
+ isr = ret;
+
+ if (test_bit(0, active_scan_mask)) {
+ if (test_bit(VCNL4010_INT_PROXIMITY, &isr)) {
+ ret = vcnl4000_read_data(data,
+ VCNL4000_PS_RESULT_HI,
+ &val);
+ if (ret < 0)
+ goto end;
+
+ buffer[0] = val;
+ data_read = true;
+ }
+ }
+
+ ret = i2c_smbus_write_byte_data(data->client, VCNL4010_ISR,
+ isr & VCNL4010_INT_DRDY);
+ if (ret < 0)
+ goto end;
+
+ if (!data_read)
+ goto end;
+
+ iio_push_to_buffers_with_timestamp(indio_dev, buffer,
+ iio_get_time_ns(indio_dev));
+
+end:
+ iio_trigger_notify_done(indio_dev->trig);
+ return IRQ_HANDLED;
+}
+
+static int vcnl4010_buffer_postenable(struct iio_dev *indio_dev)
+{
+ struct vcnl4000_data *data = iio_priv(indio_dev);
+ int ret;
+ int cmd;
+
+ /* Do not enable the buffer if we are already capturing events. */
+ if (vcnl4010_is_in_periodic_mode(data))
+ return -EBUSY;
+
+ ret = i2c_smbus_write_byte_data(data->client, VCNL4010_INT_CTRL,
+ VCNL4010_INT_PROX_EN);
+ if (ret < 0)
+ return ret;
+
+ cmd = VCNL4000_SELF_TIMED_EN | VCNL4000_PROX_EN;
+ return i2c_smbus_write_byte_data(data->client, VCNL4000_COMMAND, cmd);
+}
+
+static int vcnl4010_buffer_predisable(struct iio_dev *indio_dev)
+{
+ struct vcnl4000_data *data = iio_priv(indio_dev);
+ int ret;
+
+ ret = i2c_smbus_write_byte_data(data->client, VCNL4010_INT_CTRL, 0);
+ if (ret < 0)
+ return ret;
+
+ return i2c_smbus_write_byte_data(data->client, VCNL4000_COMMAND, 0);
+}
+
+static const struct iio_buffer_setup_ops vcnl4010_buffer_ops = {
+ .postenable = &vcnl4010_buffer_postenable,
+ .predisable = &vcnl4010_buffer_predisable,
+};
+
+static const struct iio_trigger_ops vcnl4010_trigger_ops = {
+ .validate_device = iio_trigger_validate_own_device,
+};
+
+static int vcnl4010_probe_trigger(struct iio_dev *indio_dev)
+{
+ struct vcnl4000_data *data = iio_priv(indio_dev);
+ struct i2c_client *client = data->client;
+ struct iio_trigger *trigger;
+
+ trigger = devm_iio_trigger_alloc(&client->dev, "%s-dev%d",
+ indio_dev->name, indio_dev->id);
+ if (!trigger)
+ return -ENOMEM;
+
+ trigger->dev.parent = &client->dev;
+ trigger->ops = &vcnl4010_trigger_ops;
+ iio_trigger_set_drvdata(trigger, indio_dev);
+
+ return devm_iio_trigger_register(&client->dev, trigger);
+}
+
static int vcnl4000_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
@@ -385,14 +1033,59 @@
dev_dbg(&client->dev, "%s Ambient light/proximity sensor, Rev: %02x\n",
data->chip_spec->prod, data->rev);
- indio_dev->dev.parent = &client->dev;
- indio_dev->info = &vcnl4000_info;
- indio_dev->channels = vcnl4000_channels;
- indio_dev->num_channels = ARRAY_SIZE(vcnl4000_channels);
+ if (device_property_read_u32(&client->dev, "proximity-near-level",
+ &data->near_level))
+ data->near_level = 0;
+
+ indio_dev->info = data->chip_spec->info;
+ indio_dev->channels = data->chip_spec->channels;
+ indio_dev->num_channels = data->chip_spec->num_channels;
indio_dev->name = VCNL4000_DRV_NAME;
indio_dev->modes = INDIO_DIRECT_MODE;
- return devm_iio_device_register(&client->dev, indio_dev);
+ if (client->irq && data->chip_spec->irq_support) {
+ ret = devm_iio_triggered_buffer_setup(&client->dev, indio_dev,
+ NULL,
+ vcnl4010_trigger_handler,
+ &vcnl4010_buffer_ops);
+ if (ret < 0) {
+ dev_err(&client->dev,
+ "unable to setup iio triggered buffer\n");
+ return ret;
+ }
+
+ ret = devm_request_threaded_irq(&client->dev, client->irq,
+ NULL, vcnl4010_irq_thread,
+ IRQF_TRIGGER_FALLING |
+ IRQF_ONESHOT,
+ "vcnl4010_irq",
+ indio_dev);
+ if (ret < 0) {
+ dev_err(&client->dev, "irq request failed\n");
+ return ret;
+ }
+
+ ret = vcnl4010_probe_trigger(indio_dev);
+ if (ret < 0)
+ return ret;
+ }
+
+ ret = pm_runtime_set_active(&client->dev);
+ if (ret < 0)
+ goto fail_poweroff;
+
+ ret = iio_device_register(indio_dev);
+ if (ret < 0)
+ goto fail_poweroff;
+
+ pm_runtime_enable(&client->dev);
+ pm_runtime_set_autosuspend_delay(&client->dev, VCNL4000_SLEEP_DELAY_MS);
+ pm_runtime_use_autosuspend(&client->dev);
+
+ return 0;
+fail_poweroff:
+ data->chip_spec->set_power_state(data, false);
+ return ret;
}
static const struct of_device_id vcnl_4000_of_match[] = {
@@ -420,17 +1113,56 @@
};
MODULE_DEVICE_TABLE(of, vcnl_4000_of_match);
+static int vcnl4000_remove(struct i2c_client *client)
+{
+ struct iio_dev *indio_dev = i2c_get_clientdata(client);
+ struct vcnl4000_data *data = iio_priv(indio_dev);
+
+ pm_runtime_dont_use_autosuspend(&client->dev);
+ pm_runtime_disable(&client->dev);
+ iio_device_unregister(indio_dev);
+ pm_runtime_set_suspended(&client->dev);
+
+ return data->chip_spec->set_power_state(data, false);
+}
+
+static int __maybe_unused vcnl4000_runtime_suspend(struct device *dev)
+{
+ struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev));
+ struct vcnl4000_data *data = iio_priv(indio_dev);
+
+ return data->chip_spec->set_power_state(data, false);
+}
+
+static int __maybe_unused vcnl4000_runtime_resume(struct device *dev)
+{
+ struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev));
+ struct vcnl4000_data *data = iio_priv(indio_dev);
+
+ return data->chip_spec->set_power_state(data, true);
+}
+
+static const struct dev_pm_ops vcnl4000_pm_ops = {
+ SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend,
+ pm_runtime_force_resume)
+ SET_RUNTIME_PM_OPS(vcnl4000_runtime_suspend,
+ vcnl4000_runtime_resume, NULL)
+};
+
static struct i2c_driver vcnl4000_driver = {
.driver = {
.name = VCNL4000_DRV_NAME,
+ .pm = &vcnl4000_pm_ops,
.of_match_table = vcnl_4000_of_match,
},
.probe = vcnl4000_probe,
.id_table = vcnl4000_id,
+ .remove = vcnl4000_remove,
};
module_i2c_driver(vcnl4000_driver);
MODULE_AUTHOR("Peter Meerwald <pmeerw@pmeerw.net>");
+MODULE_AUTHOR("Mathieu Othacehe <m.othacehe@gmail.com>");
MODULE_DESCRIPTION("Vishay VCNL4000 proximity/ambient light sensor driver");
MODULE_LICENSE("GPL");
diff --git a/drivers/iio/light/vcnl4035.c b/drivers/iio/light/vcnl4035.c
index 234623a..1bd85e2 100644
--- a/drivers/iio/light/vcnl4035.c
+++ b/drivers/iio/light/vcnl4035.c
@@ -565,7 +565,6 @@
data->client = client;
data->regmap = regmap;
- indio_dev->dev.parent = &client->dev;
indio_dev->info = &vcnl4035_info;
indio_dev->name = VCNL4035_DRV_NAME;
indio_dev->channels = vcnl4035_channels;
diff --git a/drivers/iio/light/veml6030.c b/drivers/iio/light/veml6030.c
new file mode 100644
index 0000000..de85c9b
--- /dev/null
+++ b/drivers/iio/light/veml6030.c
@@ -0,0 +1,907 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * VEML6030 Ambient Light Sensor
+ *
+ * Copyright (c) 2019, Rishi Gupta <gupt21@gmail.com>
+ *
+ * Datasheet: https://www.vishay.com/docs/84366/veml6030.pdf
+ * Appnote-84367: https://www.vishay.com/docs/84367/designingveml6030.pdf
+ */
+
+#include <linux/module.h>
+#include <linux/i2c.h>
+#include <linux/err.h>
+#include <linux/regmap.h>
+#include <linux/interrupt.h>
+#include <linux/pm_runtime.h>
+#include <linux/iio/iio.h>
+#include <linux/iio/sysfs.h>
+#include <linux/iio/events.h>
+
+/* Device registers */
+#define VEML6030_REG_ALS_CONF 0x00
+#define VEML6030_REG_ALS_WH 0x01
+#define VEML6030_REG_ALS_WL 0x02
+#define VEML6030_REG_ALS_PSM 0x03
+#define VEML6030_REG_ALS_DATA 0x04
+#define VEML6030_REG_WH_DATA 0x05
+#define VEML6030_REG_ALS_INT 0x06
+
+/* Bit masks for specific functionality */
+#define VEML6030_ALS_IT GENMASK(9, 6)
+#define VEML6030_PSM GENMASK(2, 1)
+#define VEML6030_ALS_PERS GENMASK(5, 4)
+#define VEML6030_ALS_GAIN GENMASK(12, 11)
+#define VEML6030_PSM_EN BIT(0)
+#define VEML6030_INT_TH_LOW BIT(15)
+#define VEML6030_INT_TH_HIGH BIT(14)
+#define VEML6030_ALS_INT_EN BIT(1)
+#define VEML6030_ALS_SD BIT(0)
+
+/*
+ * The resolution depends on both gain and integration time. The
+ * cur_resolution stores one of the resolution mentioned in the
+ * table during startup and gets updated whenever integration time
+ * or gain is changed.
+ *
+ * Table 'resolution and maximum detection range' in appnote 84367
+ * is visualized as a 2D array. The cur_gain stores index of gain
+ * in this table (0-3) while the cur_integration_time holds index
+ * of integration time (0-5).
+ */
+struct veml6030_data {
+ struct i2c_client *client;
+ struct regmap *regmap;
+ int cur_resolution;
+ int cur_gain;
+ int cur_integration_time;
+};
+
+/* Integration time available in seconds */
+static IIO_CONST_ATTR(in_illuminance_integration_time_available,
+ "0.025 0.05 0.1 0.2 0.4 0.8");
+
+/*
+ * Scale is 1/gain. Value 0.125 is ALS gain x (1/8), 0.25 is
+ * ALS gain x (1/4), 1.0 = ALS gain x 1 and 2.0 is ALS gain x 2.
+ */
+static IIO_CONST_ATTR(in_illuminance_scale_available,
+ "0.125 0.25 1.0 2.0");
+
+static struct attribute *veml6030_attributes[] = {
+ &iio_const_attr_in_illuminance_integration_time_available.dev_attr.attr,
+ &iio_const_attr_in_illuminance_scale_available.dev_attr.attr,
+ NULL
+};
+
+static const struct attribute_group veml6030_attr_group = {
+ .attrs = veml6030_attributes,
+};
+
+/*
+ * Persistence = 1/2/4/8 x integration time
+ * Minimum time for which light readings must stay above configured
+ * threshold to assert the interrupt.
+ */
+static const char * const period_values[] = {
+ "0.1 0.2 0.4 0.8",
+ "0.2 0.4 0.8 1.6",
+ "0.4 0.8 1.6 3.2",
+ "0.8 1.6 3.2 6.4",
+ "0.05 0.1 0.2 0.4",
+ "0.025 0.050 0.1 0.2"
+};
+
+/*
+ * Return list of valid period values in seconds corresponding to
+ * the currently active integration time.
+ */
+static ssize_t in_illuminance_period_available_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ int ret, reg, x;
+ struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev));
+ struct veml6030_data *data = iio_priv(indio_dev);
+
+ ret = regmap_read(data->regmap, VEML6030_REG_ALS_CONF, ®);
+ if (ret) {
+ dev_err(&data->client->dev,
+ "can't read als conf register %d\n", ret);
+ return ret;
+ }
+
+ ret = ((reg >> 6) & 0xF);
+ switch (ret) {
+ case 0:
+ case 1:
+ case 2:
+ case 3:
+ x = ret;
+ break;
+ case 8:
+ x = 4;
+ break;
+ case 12:
+ x = 5;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return snprintf(buf, PAGE_SIZE, "%s\n", period_values[x]);
+}
+
+static IIO_DEVICE_ATTR_RO(in_illuminance_period_available, 0);
+
+static struct attribute *veml6030_event_attributes[] = {
+ &iio_dev_attr_in_illuminance_period_available.dev_attr.attr,
+ NULL
+};
+
+static const struct attribute_group veml6030_event_attr_group = {
+ .attrs = veml6030_event_attributes,
+};
+
+static int veml6030_als_pwr_on(struct veml6030_data *data)
+{
+ return regmap_update_bits(data->regmap, VEML6030_REG_ALS_CONF,
+ VEML6030_ALS_SD, 0);
+}
+
+static int veml6030_als_shut_down(struct veml6030_data *data)
+{
+ return regmap_update_bits(data->regmap, VEML6030_REG_ALS_CONF,
+ VEML6030_ALS_SD, 1);
+}
+
+static void veml6030_als_shut_down_action(void *data)
+{
+ veml6030_als_shut_down(data);
+}
+
+static const struct iio_event_spec veml6030_event_spec[] = {
+ {
+ .type = IIO_EV_TYPE_THRESH,
+ .dir = IIO_EV_DIR_RISING,
+ .mask_separate = BIT(IIO_EV_INFO_VALUE),
+ }, {
+ .type = IIO_EV_TYPE_THRESH,
+ .dir = IIO_EV_DIR_FALLING,
+ .mask_separate = BIT(IIO_EV_INFO_VALUE),
+ }, {
+ .type = IIO_EV_TYPE_THRESH,
+ .dir = IIO_EV_DIR_EITHER,
+ .mask_separate = BIT(IIO_EV_INFO_PERIOD) |
+ BIT(IIO_EV_INFO_ENABLE),
+ },
+};
+
+/* Channel number */
+enum veml6030_chan {
+ CH_ALS,
+ CH_WHITE,
+};
+
+static const struct iio_chan_spec veml6030_channels[] = {
+ {
+ .type = IIO_LIGHT,
+ .channel = CH_ALS,
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
+ BIT(IIO_CHAN_INFO_PROCESSED) |
+ BIT(IIO_CHAN_INFO_INT_TIME) |
+ BIT(IIO_CHAN_INFO_SCALE),
+ .event_spec = veml6030_event_spec,
+ .num_event_specs = ARRAY_SIZE(veml6030_event_spec),
+ },
+ {
+ .type = IIO_INTENSITY,
+ .channel = CH_WHITE,
+ .modified = 1,
+ .channel2 = IIO_MOD_LIGHT_BOTH,
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
+ BIT(IIO_CHAN_INFO_PROCESSED),
+ },
+};
+
+static const struct regmap_config veml6030_regmap_config = {
+ .name = "veml6030_regmap",
+ .reg_bits = 8,
+ .val_bits = 16,
+ .max_register = VEML6030_REG_ALS_INT,
+ .val_format_endian = REGMAP_ENDIAN_LITTLE,
+};
+
+static int veml6030_get_intgrn_tm(struct iio_dev *indio_dev,
+ int *val, int *val2)
+{
+ int ret, reg;
+ struct veml6030_data *data = iio_priv(indio_dev);
+
+ ret = regmap_read(data->regmap, VEML6030_REG_ALS_CONF, ®);
+ if (ret) {
+ dev_err(&data->client->dev,
+ "can't read als conf register %d\n", ret);
+ return ret;
+ }
+
+ switch ((reg >> 6) & 0xF) {
+ case 0:
+ *val2 = 100000;
+ break;
+ case 1:
+ *val2 = 200000;
+ break;
+ case 2:
+ *val2 = 400000;
+ break;
+ case 3:
+ *val2 = 800000;
+ break;
+ case 8:
+ *val2 = 50000;
+ break;
+ case 12:
+ *val2 = 25000;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ *val = 0;
+ return IIO_VAL_INT_PLUS_MICRO;
+}
+
+static int veml6030_set_intgrn_tm(struct iio_dev *indio_dev,
+ int val, int val2)
+{
+ int ret, new_int_time, int_idx;
+ struct veml6030_data *data = iio_priv(indio_dev);
+
+ if (val)
+ return -EINVAL;
+
+ switch (val2) {
+ case 25000:
+ new_int_time = 0x300;
+ int_idx = 5;
+ break;
+ case 50000:
+ new_int_time = 0x200;
+ int_idx = 4;
+ break;
+ case 100000:
+ new_int_time = 0x00;
+ int_idx = 3;
+ break;
+ case 200000:
+ new_int_time = 0x40;
+ int_idx = 2;
+ break;
+ case 400000:
+ new_int_time = 0x80;
+ int_idx = 1;
+ break;
+ case 800000:
+ new_int_time = 0xC0;
+ int_idx = 0;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ ret = regmap_update_bits(data->regmap, VEML6030_REG_ALS_CONF,
+ VEML6030_ALS_IT, new_int_time);
+ if (ret) {
+ dev_err(&data->client->dev,
+ "can't update als integration time %d\n", ret);
+ return ret;
+ }
+
+ /*
+ * Cache current integration time and update resolution. For every
+ * increase in integration time to next level, resolution is halved
+ * and vice-versa.
+ */
+ if (data->cur_integration_time < int_idx)
+ data->cur_resolution <<= int_idx - data->cur_integration_time;
+ else if (data->cur_integration_time > int_idx)
+ data->cur_resolution >>= data->cur_integration_time - int_idx;
+
+ data->cur_integration_time = int_idx;
+
+ return ret;
+}
+
+static int veml6030_read_persistence(struct iio_dev *indio_dev,
+ int *val, int *val2)
+{
+ int ret, reg, period, x, y;
+ struct veml6030_data *data = iio_priv(indio_dev);
+
+ ret = veml6030_get_intgrn_tm(indio_dev, &x, &y);
+ if (ret < 0)
+ return ret;
+
+ ret = regmap_read(data->regmap, VEML6030_REG_ALS_CONF, ®);
+ if (ret) {
+ dev_err(&data->client->dev,
+ "can't read als conf register %d\n", ret);
+ }
+
+ /* integration time multiplied by 1/2/4/8 */
+ period = y * (1 << ((reg >> 4) & 0x03));
+
+ *val = period / 1000000;
+ *val2 = period % 1000000;
+
+ return IIO_VAL_INT_PLUS_MICRO;
+}
+
+static int veml6030_write_persistence(struct iio_dev *indio_dev,
+ int val, int val2)
+{
+ int ret, period, x, y;
+ struct veml6030_data *data = iio_priv(indio_dev);
+
+ ret = veml6030_get_intgrn_tm(indio_dev, &x, &y);
+ if (ret < 0)
+ return ret;
+
+ if (!val) {
+ period = val2 / y;
+ } else {
+ if ((val == 1) && (val2 == 600000))
+ period = 1600000 / y;
+ else if ((val == 3) && (val2 == 200000))
+ period = 3200000 / y;
+ else if ((val == 6) && (val2 == 400000))
+ period = 6400000 / y;
+ else
+ period = -1;
+ }
+
+ if (period <= 0 || period > 8 || hweight8(period) != 1)
+ return -EINVAL;
+
+ ret = regmap_update_bits(data->regmap, VEML6030_REG_ALS_CONF,
+ VEML6030_ALS_PERS, (ffs(period) - 1) << 4);
+ if (ret)
+ dev_err(&data->client->dev,
+ "can't set persistence value %d\n", ret);
+
+ return ret;
+}
+
+static int veml6030_set_als_gain(struct iio_dev *indio_dev,
+ int val, int val2)
+{
+ int ret, new_gain, gain_idx;
+ struct veml6030_data *data = iio_priv(indio_dev);
+
+ if (val == 0 && val2 == 125000) {
+ new_gain = 0x1000; /* 0x02 << 11 */
+ gain_idx = 3;
+ } else if (val == 0 && val2 == 250000) {
+ new_gain = 0x1800;
+ gain_idx = 2;
+ } else if (val == 1 && val2 == 0) {
+ new_gain = 0x00;
+ gain_idx = 1;
+ } else if (val == 2 && val2 == 0) {
+ new_gain = 0x800;
+ gain_idx = 0;
+ } else {
+ return -EINVAL;
+ }
+
+ ret = regmap_update_bits(data->regmap, VEML6030_REG_ALS_CONF,
+ VEML6030_ALS_GAIN, new_gain);
+ if (ret) {
+ dev_err(&data->client->dev,
+ "can't set als gain %d\n", ret);
+ return ret;
+ }
+
+ /*
+ * Cache currently set gain & update resolution. For every
+ * increase in the gain to next level, resolution is halved
+ * and vice-versa.
+ */
+ if (data->cur_gain < gain_idx)
+ data->cur_resolution <<= gain_idx - data->cur_gain;
+ else if (data->cur_gain > gain_idx)
+ data->cur_resolution >>= data->cur_gain - gain_idx;
+
+ data->cur_gain = gain_idx;
+
+ return ret;
+}
+
+static int veml6030_get_als_gain(struct iio_dev *indio_dev,
+ int *val, int *val2)
+{
+ int ret, reg;
+ struct veml6030_data *data = iio_priv(indio_dev);
+
+ ret = regmap_read(data->regmap, VEML6030_REG_ALS_CONF, ®);
+ if (ret) {
+ dev_err(&data->client->dev,
+ "can't read als conf register %d\n", ret);
+ return ret;
+ }
+
+ switch ((reg >> 11) & 0x03) {
+ case 0:
+ *val = 1;
+ *val2 = 0;
+ break;
+ case 1:
+ *val = 2;
+ *val2 = 0;
+ break;
+ case 2:
+ *val = 0;
+ *val2 = 125000;
+ break;
+ case 3:
+ *val = 0;
+ *val2 = 250000;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return IIO_VAL_INT_PLUS_MICRO;
+}
+
+static int veml6030_read_thresh(struct iio_dev *indio_dev,
+ int *val, int *val2, int dir)
+{
+ int ret, reg;
+ struct veml6030_data *data = iio_priv(indio_dev);
+
+ if (dir == IIO_EV_DIR_RISING)
+ ret = regmap_read(data->regmap, VEML6030_REG_ALS_WH, ®);
+ else
+ ret = regmap_read(data->regmap, VEML6030_REG_ALS_WL, ®);
+ if (ret) {
+ dev_err(&data->client->dev,
+ "can't read als threshold value %d\n", ret);
+ return ret;
+ }
+
+ *val = reg & 0xffff;
+ return IIO_VAL_INT;
+}
+
+static int veml6030_write_thresh(struct iio_dev *indio_dev,
+ int val, int val2, int dir)
+{
+ int ret;
+ struct veml6030_data *data = iio_priv(indio_dev);
+
+ if (val > 0xFFFF || val < 0 || val2)
+ return -EINVAL;
+
+ if (dir == IIO_EV_DIR_RISING) {
+ ret = regmap_write(data->regmap, VEML6030_REG_ALS_WH, val);
+ if (ret)
+ dev_err(&data->client->dev,
+ "can't set high threshold %d\n", ret);
+ } else {
+ ret = regmap_write(data->regmap, VEML6030_REG_ALS_WL, val);
+ if (ret)
+ dev_err(&data->client->dev,
+ "can't set low threshold %d\n", ret);
+ }
+
+ return ret;
+}
+
+/*
+ * Provide both raw as well as light reading in lux.
+ * light (in lux) = resolution * raw reading
+ */
+static int veml6030_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan, int *val,
+ int *val2, long mask)
+{
+ int ret, reg;
+ struct veml6030_data *data = iio_priv(indio_dev);
+ struct regmap *regmap = data->regmap;
+ struct device *dev = &data->client->dev;
+
+ switch (mask) {
+ case IIO_CHAN_INFO_RAW:
+ case IIO_CHAN_INFO_PROCESSED:
+ switch (chan->type) {
+ case IIO_LIGHT:
+ ret = regmap_read(regmap, VEML6030_REG_ALS_DATA, ®);
+ if (ret < 0) {
+ dev_err(dev, "can't read als data %d\n", ret);
+ return ret;
+ }
+ if (mask == IIO_CHAN_INFO_PROCESSED) {
+ *val = (reg * data->cur_resolution) / 10000;
+ *val2 = (reg * data->cur_resolution) % 10000;
+ return IIO_VAL_INT_PLUS_MICRO;
+ }
+ *val = reg;
+ return IIO_VAL_INT;
+ case IIO_INTENSITY:
+ ret = regmap_read(regmap, VEML6030_REG_WH_DATA, ®);
+ if (ret < 0) {
+ dev_err(dev, "can't read white data %d\n", ret);
+ return ret;
+ }
+ if (mask == IIO_CHAN_INFO_PROCESSED) {
+ *val = (reg * data->cur_resolution) / 10000;
+ *val2 = (reg * data->cur_resolution) % 10000;
+ return IIO_VAL_INT_PLUS_MICRO;
+ }
+ *val = reg;
+ return IIO_VAL_INT;
+ default:
+ return -EINVAL;
+ }
+ case IIO_CHAN_INFO_INT_TIME:
+ if (chan->type == IIO_LIGHT)
+ return veml6030_get_intgrn_tm(indio_dev, val, val2);
+ return -EINVAL;
+ case IIO_CHAN_INFO_SCALE:
+ if (chan->type == IIO_LIGHT)
+ return veml6030_get_als_gain(indio_dev, val, val2);
+ return -EINVAL;
+ default:
+ return -EINVAL;
+ }
+}
+
+static int veml6030_write_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int val, int val2, long mask)
+{
+ switch (mask) {
+ case IIO_CHAN_INFO_INT_TIME:
+ switch (chan->type) {
+ case IIO_LIGHT:
+ return veml6030_set_intgrn_tm(indio_dev, val, val2);
+ default:
+ return -EINVAL;
+ }
+ case IIO_CHAN_INFO_SCALE:
+ switch (chan->type) {
+ case IIO_LIGHT:
+ return veml6030_set_als_gain(indio_dev, val, val2);
+ default:
+ return -EINVAL;
+ }
+ default:
+ return -EINVAL;
+ }
+}
+
+static int veml6030_read_event_val(struct iio_dev *indio_dev,
+ const struct iio_chan_spec *chan, enum iio_event_type type,
+ enum iio_event_direction dir, enum iio_event_info info,
+ int *val, int *val2)
+{
+ switch (info) {
+ case IIO_EV_INFO_VALUE:
+ switch (dir) {
+ case IIO_EV_DIR_RISING:
+ case IIO_EV_DIR_FALLING:
+ return veml6030_read_thresh(indio_dev, val, val2, dir);
+ default:
+ return -EINVAL;
+ }
+ break;
+ case IIO_EV_INFO_PERIOD:
+ return veml6030_read_persistence(indio_dev, val, val2);
+ default:
+ return -EINVAL;
+ }
+}
+
+static int veml6030_write_event_val(struct iio_dev *indio_dev,
+ const struct iio_chan_spec *chan, enum iio_event_type type,
+ enum iio_event_direction dir, enum iio_event_info info,
+ int val, int val2)
+{
+ switch (info) {
+ case IIO_EV_INFO_VALUE:
+ return veml6030_write_thresh(indio_dev, val, val2, dir);
+ case IIO_EV_INFO_PERIOD:
+ return veml6030_write_persistence(indio_dev, val, val2);
+ default:
+ return -EINVAL;
+ }
+}
+
+static int veml6030_read_interrupt_config(struct iio_dev *indio_dev,
+ const struct iio_chan_spec *chan, enum iio_event_type type,
+ enum iio_event_direction dir)
+{
+ int ret, reg;
+ struct veml6030_data *data = iio_priv(indio_dev);
+
+ ret = regmap_read(data->regmap, VEML6030_REG_ALS_CONF, ®);
+ if (ret) {
+ dev_err(&data->client->dev,
+ "can't read als conf register %d\n", ret);
+ return ret;
+ }
+
+ if (reg & VEML6030_ALS_INT_EN)
+ return 1;
+ else
+ return 0;
+}
+
+/*
+ * Sensor should not be measuring light when interrupt is configured.
+ * Therefore correct sequence to configure interrupt functionality is:
+ * shut down -> enable/disable interrupt -> power on
+ *
+ * state = 1 enables interrupt, state = 0 disables interrupt
+ */
+static int veml6030_write_interrupt_config(struct iio_dev *indio_dev,
+ const struct iio_chan_spec *chan, enum iio_event_type type,
+ enum iio_event_direction dir, int state)
+{
+ int ret;
+ struct veml6030_data *data = iio_priv(indio_dev);
+
+ if (state < 0 || state > 1)
+ return -EINVAL;
+
+ ret = veml6030_als_shut_down(data);
+ if (ret < 0) {
+ dev_err(&data->client->dev,
+ "can't disable als to configure interrupt %d\n", ret);
+ return ret;
+ }
+
+ /* enable interrupt + power on */
+ ret = regmap_update_bits(data->regmap, VEML6030_REG_ALS_CONF,
+ VEML6030_ALS_INT_EN | VEML6030_ALS_SD, state << 1);
+ if (ret)
+ dev_err(&data->client->dev,
+ "can't enable interrupt & poweron als %d\n", ret);
+
+ return ret;
+}
+
+static const struct iio_info veml6030_info = {
+ .read_raw = veml6030_read_raw,
+ .write_raw = veml6030_write_raw,
+ .read_event_value = veml6030_read_event_val,
+ .write_event_value = veml6030_write_event_val,
+ .read_event_config = veml6030_read_interrupt_config,
+ .write_event_config = veml6030_write_interrupt_config,
+ .attrs = &veml6030_attr_group,
+ .event_attrs = &veml6030_event_attr_group,
+};
+
+static const struct iio_info veml6030_info_no_irq = {
+ .read_raw = veml6030_read_raw,
+ .write_raw = veml6030_write_raw,
+ .attrs = &veml6030_attr_group,
+};
+
+static irqreturn_t veml6030_event_handler(int irq, void *private)
+{
+ int ret, reg, evtdir;
+ struct iio_dev *indio_dev = private;
+ struct veml6030_data *data = iio_priv(indio_dev);
+
+ ret = regmap_read(data->regmap, VEML6030_REG_ALS_INT, ®);
+ if (ret) {
+ dev_err(&data->client->dev,
+ "can't read als interrupt register %d\n", ret);
+ return IRQ_HANDLED;
+ }
+
+ /* Spurious interrupt handling */
+ if (!(reg & (VEML6030_INT_TH_HIGH | VEML6030_INT_TH_LOW)))
+ return IRQ_NONE;
+
+ if (reg & VEML6030_INT_TH_HIGH)
+ evtdir = IIO_EV_DIR_RISING;
+ else
+ evtdir = IIO_EV_DIR_FALLING;
+
+ iio_push_event(indio_dev, IIO_UNMOD_EVENT_CODE(IIO_INTENSITY,
+ 0, IIO_EV_TYPE_THRESH, evtdir),
+ iio_get_time_ns(indio_dev));
+
+ return IRQ_HANDLED;
+}
+
+/*
+ * Set ALS gain to 1/8, integration time to 100 ms, PSM to mode 2,
+ * persistence to 1 x integration time and the threshold
+ * interrupt disabled by default. First shutdown the sensor,
+ * update registers and then power on the sensor.
+ */
+static int veml6030_hw_init(struct iio_dev *indio_dev)
+{
+ int ret, val;
+ struct veml6030_data *data = iio_priv(indio_dev);
+ struct i2c_client *client = data->client;
+
+ ret = veml6030_als_shut_down(data);
+ if (ret) {
+ dev_err(&client->dev, "can't shutdown als %d\n", ret);
+ return ret;
+ }
+
+ ret = regmap_write(data->regmap, VEML6030_REG_ALS_CONF, 0x1001);
+ if (ret) {
+ dev_err(&client->dev, "can't setup als configs %d\n", ret);
+ return ret;
+ }
+
+ ret = regmap_update_bits(data->regmap, VEML6030_REG_ALS_PSM,
+ VEML6030_PSM | VEML6030_PSM_EN, 0x03);
+ if (ret) {
+ dev_err(&client->dev, "can't setup default PSM %d\n", ret);
+ return ret;
+ }
+
+ ret = regmap_write(data->regmap, VEML6030_REG_ALS_WH, 0xFFFF);
+ if (ret) {
+ dev_err(&client->dev, "can't setup high threshold %d\n", ret);
+ return ret;
+ }
+
+ ret = regmap_write(data->regmap, VEML6030_REG_ALS_WL, 0x0000);
+ if (ret) {
+ dev_err(&client->dev, "can't setup low threshold %d\n", ret);
+ return ret;
+ }
+
+ ret = veml6030_als_pwr_on(data);
+ if (ret) {
+ dev_err(&client->dev, "can't poweron als %d\n", ret);
+ return ret;
+ }
+
+ /* Wait 4 ms to let processor & oscillator start correctly */
+ usleep_range(4000, 4002);
+
+ /* Clear stale interrupt status bits if any during start */
+ ret = regmap_read(data->regmap, VEML6030_REG_ALS_INT, &val);
+ if (ret < 0) {
+ dev_err(&client->dev,
+ "can't clear als interrupt status %d\n", ret);
+ return ret;
+ }
+
+ /* Cache currently active measurement parameters */
+ data->cur_gain = 3;
+ data->cur_resolution = 4608;
+ data->cur_integration_time = 3;
+
+ return ret;
+}
+
+static int veml6030_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ int ret;
+ struct veml6030_data *data;
+ struct iio_dev *indio_dev;
+ struct regmap *regmap;
+
+ if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
+ dev_err(&client->dev, "i2c adapter doesn't support plain i2c\n");
+ return -EOPNOTSUPP;
+ }
+
+ regmap = devm_regmap_init_i2c(client, &veml6030_regmap_config);
+ if (IS_ERR(regmap)) {
+ dev_err(&client->dev, "can't setup regmap\n");
+ return PTR_ERR(regmap);
+ }
+
+ 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;
+ data->regmap = regmap;
+
+ indio_dev->name = "veml6030";
+ indio_dev->channels = veml6030_channels;
+ indio_dev->num_channels = ARRAY_SIZE(veml6030_channels);
+ indio_dev->modes = INDIO_DIRECT_MODE;
+
+ if (client->irq) {
+ ret = devm_request_threaded_irq(&client->dev, client->irq,
+ NULL, veml6030_event_handler,
+ IRQF_TRIGGER_LOW | IRQF_ONESHOT,
+ "veml6030", indio_dev);
+ if (ret < 0) {
+ dev_err(&client->dev,
+ "irq %d request failed\n", client->irq);
+ return ret;
+ }
+ indio_dev->info = &veml6030_info;
+ } else {
+ indio_dev->info = &veml6030_info_no_irq;
+ }
+
+ ret = veml6030_hw_init(indio_dev);
+ if (ret < 0)
+ return ret;
+
+ ret = devm_add_action_or_reset(&client->dev,
+ veml6030_als_shut_down_action, data);
+ if (ret < 0)
+ return ret;
+
+ return devm_iio_device_register(&client->dev, indio_dev);
+}
+
+static int __maybe_unused veml6030_runtime_suspend(struct device *dev)
+{
+ int ret;
+ struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev));
+ struct veml6030_data *data = iio_priv(indio_dev);
+
+ ret = veml6030_als_shut_down(data);
+ if (ret < 0)
+ dev_err(&data->client->dev, "can't suspend als %d\n", ret);
+
+ return ret;
+}
+
+static int __maybe_unused veml6030_runtime_resume(struct device *dev)
+{
+ int ret;
+ struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev));
+ struct veml6030_data *data = iio_priv(indio_dev);
+
+ ret = veml6030_als_pwr_on(data);
+ if (ret < 0)
+ dev_err(&data->client->dev, "can't resume als %d\n", ret);
+
+ return ret;
+}
+
+static const struct dev_pm_ops veml6030_pm_ops = {
+ SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend,
+ pm_runtime_force_resume)
+ SET_RUNTIME_PM_OPS(veml6030_runtime_suspend,
+ veml6030_runtime_resume, NULL)
+};
+
+static const struct of_device_id veml6030_of_match[] = {
+ { .compatible = "vishay,veml6030" },
+ { }
+};
+MODULE_DEVICE_TABLE(of, veml6030_of_match);
+
+static const struct i2c_device_id veml6030_id[] = {
+ { "veml6030", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, veml6030_id);
+
+static struct i2c_driver veml6030_driver = {
+ .driver = {
+ .name = "veml6030",
+ .of_match_table = veml6030_of_match,
+ .pm = &veml6030_pm_ops,
+ },
+ .probe = veml6030_probe,
+ .id_table = veml6030_id,
+};
+module_i2c_driver(veml6030_driver);
+
+MODULE_AUTHOR("Rishi Gupta <gupt21@gmail.com>");
+MODULE_DESCRIPTION("VEML6030 Ambient Light Sensor");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/iio/light/veml6070.c b/drivers/iio/light/veml6070.c
index 0be553a..1e55e09 100644
--- a/drivers/iio/light/veml6070.c
+++ b/drivers/iio/light/veml6070.c
@@ -151,7 +151,6 @@
data->client1 = client;
mutex_init(&data->lock);
- indio_dev->dev.parent = &client->dev;
indio_dev->info = &veml6070_info;
indio_dev->channels = veml6070_channels;
indio_dev->num_channels = ARRAY_SIZE(veml6070_channels);
diff --git a/drivers/iio/light/vl6180.c b/drivers/iio/light/vl6180.c
index d9533a7..4775bd7 100644
--- a/drivers/iio/light/vl6180.c
+++ b/drivers/iio/light/vl6180.c
@@ -16,6 +16,7 @@
*/
#include <linux/module.h>
+#include <linux/mod_devicetable.h>
#include <linux/i2c.h>
#include <linux/mutex.h>
#include <linux/err.h>
@@ -508,7 +509,6 @@
data->client = client;
mutex_init(&data->lock);
- indio_dev->dev.parent = &client->dev;
indio_dev->info = &vl6180_info;
indio_dev->channels = vl6180_channels;
indio_dev->num_channels = ARRAY_SIZE(vl6180_channels);
@@ -537,7 +537,7 @@
static struct i2c_driver vl6180_driver = {
.driver = {
.name = VL6180_DRV_NAME,
- .of_match_table = of_match_ptr(vl6180_of_match),
+ .of_match_table = vl6180_of_match,
},
.probe = vl6180_probe,
.id_table = vl6180_id,
diff --git a/drivers/iio/light/zopt2201.c b/drivers/iio/light/zopt2201.c
index 5f54f39..e0bc9df 100644
--- a/drivers/iio/light/zopt2201.c
+++ b/drivers/iio/light/zopt2201.c
@@ -19,6 +19,8 @@
#include <linux/iio/iio.h>
#include <linux/iio/sysfs.h>
+#include <asm/unaligned.h>
+
#define ZOPT2201_DRV_NAME "zopt2201"
/* Registers */
@@ -219,7 +221,7 @@
goto fail;
mutex_unlock(&data->lock);
- return (buf[2] << 16) | (buf[1] << 8) | buf[0];
+ return get_unaligned_le24(&buf[0]);
fail:
mutex_unlock(&data->lock);
@@ -525,7 +527,6 @@
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);