summaryrefslogtreecommitdiff
path: root/drivers/iio/light
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/iio/light')
-rw-r--r--drivers/iio/light/Kconfig106
-rw-r--r--drivers/iio/light/Makefile8
-rw-r--r--drivers/iio/light/acpi-als.c2
-rw-r--r--drivers/iio/light/adjd_s311.c7
-rw-r--r--drivers/iio/light/adux1020.c30
-rw-r--r--drivers/iio/light/al3000a.c210
-rw-r--r--drivers/iio/light/al3010.c106
-rw-r--r--drivers/iio/light/al3320a.c118
-rw-r--r--drivers/iio/light/apds9160.c1594
-rw-r--r--drivers/iio/light/apds9300.c22
-rw-r--r--drivers/iio/light/apds9306.c1357
-rw-r--r--drivers/iio/light/apds9960.c69
-rw-r--r--drivers/iio/light/as73211.c68
-rw-r--r--drivers/iio/light/bh1745.c904
-rw-r--r--drivers/iio/light/bh1750.c24
-rw-r--r--drivers/iio/light/bh1780.c6
-rw-r--r--drivers/iio/light/cm32181.c5
-rw-r--r--drivers/iio/light/cm3232.c24
-rw-r--r--drivers/iio/light/cm3323.c6
-rw-r--r--drivers/iio/light/cm3605.c4
-rw-r--r--drivers/iio/light/cm36651.c6
-rw-r--r--drivers/iio/light/cros_ec_light_prox.c2
-rw-r--r--drivers/iio/light/gp2ap002.c10
-rw-r--r--drivers/iio/light/gp2ap020a00f.c23
-rw-r--r--drivers/iio/light/hid-sensor-als.c12
-rw-r--r--drivers/iio/light/hid-sensor-prox.c230
-rw-r--r--drivers/iio/light/iqs621-als.c6
-rw-r--r--drivers/iio/light/isl29018.c46
-rw-r--r--drivers/iio/light/isl29028.c8
-rw-r--r--drivers/iio/light/isl29125.c14
-rw-r--r--drivers/iio/light/jsa1212.c4
-rw-r--r--drivers/iio/light/lm3533-als.c8
-rw-r--r--drivers/iio/light/ltr390.c595
-rw-r--r--drivers/iio/light/ltr501.c207
-rw-r--r--drivers/iio/light/ltrf216a.c60
-rw-r--r--drivers/iio/light/lv0104cs.c2
-rw-r--r--drivers/iio/light/max44000.c4
-rw-r--r--drivers/iio/light/max44009.c4
-rw-r--r--drivers/iio/light/noa1305.c171
-rw-r--r--drivers/iio/light/opt3001.c200
-rw-r--r--drivers/iio/light/opt4001.c2
-rw-r--r--drivers/iio/light/opt4060.c1344
-rw-r--r--drivers/iio/light/pa12203001.c6
-rw-r--r--drivers/iio/light/rohm-bu27008.c1635
-rw-r--r--drivers/iio/light/rohm-bu27034.c434
-rw-r--r--drivers/iio/light/rpr0521.c81
-rw-r--r--drivers/iio/light/si1133.c4
-rw-r--r--drivers/iio/light/si1145.c32
-rw-r--r--drivers/iio/light/st_uvis25.h2
-rw-r--r--drivers/iio/light/st_uvis25_core.c18
-rw-r--r--drivers/iio/light/st_uvis25_i2c.c6
-rw-r--r--drivers/iio/light/st_uvis25_spi.c6
-rw-r--r--drivers/iio/light/stk3310.c56
-rw-r--r--drivers/iio/light/tcs3414.c16
-rw-r--r--drivers/iio/light/tcs3472.c18
-rw-r--r--drivers/iio/light/tsl2563.c6
-rw-r--r--drivers/iio/light/tsl2583.c4
-rw-r--r--drivers/iio/light/tsl2591.c6
-rw-r--r--drivers/iio/light/tsl2772.c10
-rw-r--r--drivers/iio/light/tsl4531.c2
-rw-r--r--drivers/iio/light/us5182d.c10
-rw-r--r--drivers/iio/light/vcnl4000.c87
-rw-r--r--drivers/iio/light/vcnl4035.c46
-rw-r--r--drivers/iio/light/veml3235.c547
-rw-r--r--drivers/iio/light/veml6030.c910
-rw-r--r--drivers/iio/light/veml6040.c281
-rw-r--r--drivers/iio/light/veml6070.c203
-rw-r--r--drivers/iio/light/veml6075.c10
-rw-r--r--drivers/iio/light/vl6180.c259
-rw-r--r--drivers/iio/light/zopt2201.c46
70 files changed, 9195 insertions, 3174 deletions
diff --git a/drivers/iio/light/Kconfig b/drivers/iio/light/Kconfig
index fd5a9879a582..4a7d983c9cd4 100644
--- a/drivers/iio/light/Kconfig
+++ b/drivers/iio/light/Kconfig
@@ -43,6 +43,16 @@ config ADUX1020
To compile this driver as a module, choose M here: the
module will be called adux1020.
+config AL3000A
+ tristate "AL3000a ambient light sensor"
+ depends on I2C
+ help
+ Say Y here if you want to build a driver for the Dyna Image AL3000a
+ ambient light sensor.
+
+ To compile this driver as a module, choose M here: the
+ module will be called al3000a.
+
config AL3010
tristate "AL3010 ambient light sensor"
depends on I2C
@@ -63,6 +73,17 @@ config AL3320A
To compile this driver as a module, choose M here: the
module will be called al3320a.
+config APDS9160
+ tristate "APDS9160 combined als and proximity sensor"
+ depends on I2C
+ select REGMAP_I2C
+ help
+ Say Y here if you want to build support for a Broadcom APDS9160
+ combined ambient light and proximity sensor.
+
+ To compile this driver as a module, choose M here: the
+ module will be called apds9160.
+
config APDS9300
tristate "APDS9300 ambient light sensor"
depends on I2C
@@ -73,6 +94,18 @@ config APDS9300
To compile this driver as a module, choose M here: the
module will be called apds9300.
+config APDS9306
+ tristate "Avago APDS9306 Ambient Light Sensor"
+ depends on I2C
+ select REGMAP_I2C
+ select IIO_GTS_HELPER
+ help
+ If you say Y or M here, you get support for Avago APDS9306
+ Ambient Light Sensor.
+
+ If built as a dynamically linked module, it will be called
+ apds9306.
+
config APDS9960
tristate "Avago APDS9960 gesture/RGB/ALS/proximity sensor"
select REGMAP_I2C
@@ -102,6 +135,19 @@ config AS73211
This driver can also be built as a module. If so, the module
will be called as73211.
+config BH1745
+ tristate "ROHM BH1745 colour sensor"
+ depends on I2C
+ select REGMAP_I2C
+ select IIO_BUFFER
+ select IIO_TRIGGERED_BUFFER
+ select IIO_GTS_HELPER
+ help
+ Say Y here to build support for the ROHM bh1745 colour sensor.
+
+ To compile this driver as a module, choose M here: the module will
+ be called bh1745.
+
config BH1750
tristate "ROHM BH1750 ambient light sensor"
depends on I2C
@@ -223,7 +269,6 @@ config SENSORS_ISL29018
tristate "Intersil 29018 light and proximity sensor"
depends on I2C
select REGMAP_I2C
- default n
help
If you say yes here you get support for ambient light sensing and
proximity infrared sensing from Intersil ISL29018.
@@ -305,20 +350,6 @@ config JSA1212
To compile this driver as a module, choose M here:
the module will be called jsa1212.
-config ROHM_BU27008
- tristate "ROHM BU27008 color (RGB+C/IR) sensor"
- depends on I2C
- select REGMAP_I2C
- select IIO_GTS_HELPER
- help
- Enable support for the ROHM BU27008 color sensor.
- The ROHM BU27008 is a sensor with 5 photodiodes (red, green,
- blue, clear and IR) with four configurable channels. Red and
- green being always available and two out of the rest three
- (blue, clear, IR) can be selected to be simultaneously measured.
- Typical application is adjusting LCD backlight of TVs,
- mobile phones and tablet PCs.
-
config ROHM_BU27034
tristate "ROHM BU27034 ambient light sensor"
depends on I2C
@@ -449,7 +480,7 @@ config OPT3001
depends on I2C
help
If you say Y or M here, you get support for Texas Instruments
- OPT3001 Ambient Light Sensor.
+ OPT3001 Ambient Light Sensor, OPT3002 Light-to-Digital Sensor.
If built as a dynamically linked module, it will be called
opt3001.
@@ -465,6 +496,19 @@ config OPT4001
If built as a dynamically linked module, it will be called
opt4001.
+config OPT4060
+ tristate "Texas Instruments OPT4060 RGBW Color Sensor"
+ depends on I2C
+ select REGMAP_I2C
+ select IIO_BUFFER
+ select IIO_TRIGGERED_BUFFER
+ help
+ If you say Y or M here, you get support for Texas Instruments
+ OPT4060 RGBW Color Sensor.
+
+ If built as a dynamically linked module, it will be called
+ opt4060.
+
config PA12203001
tristate "TXC PA12203001 light and proximity sensor"
depends on I2C
@@ -643,17 +687,43 @@ config VCNL4035
To compile this driver as a module, choose M here: the
module will be called vcnl4035.
+config VEML3235
+ tristate "VEML3235 ambient light sensor"
+ select REGMAP_I2C
+ select IIO_GTS_HELPER
+ depends on I2C
+ help
+ Say Y here if you want to build a driver for the Vishay VEML3235
+ ambient light sensor.
+
+ To compile this driver as a module, choose M here: the
+ module will be called veml3235.
+
config VEML6030
- tristate "VEML6030 ambient light sensor"
+ tristate "VEML6030 and VEML6035 ambient light sensors"
select REGMAP_I2C
+ select IIO_BUFFER
+ select IIO_TRIGGERED_BUFFER
+ select IIO_GTS_HELPER
depends on I2C
help
Say Y here if you want to build a driver for the Vishay VEML6030
- ambient light sensor (ALS).
+ and VEML6035 ambient light sensors (ALS).
To compile this driver as a module, choose M here: the
module will be called veml6030.
+config VEML6040
+ tristate "VEML6040 RGBW light sensor"
+ select REGMAP_I2C
+ depends on I2C
+ help
+ Say Y here if you want to build a driver for the Vishay VEML6040
+ RGBW light sensor.
+
+ To compile this driver as a module, choose M here: the
+ module will be called veml6040.
+
config VEML6070
tristate "VEML6070 UV A light sensor"
depends on I2C
diff --git a/drivers/iio/light/Makefile b/drivers/iio/light/Makefile
index 2e5fdb33e0e9..8229ebe6edc4 100644
--- a/drivers/iio/light/Makefile
+++ b/drivers/iio/light/Makefile
@@ -7,11 +7,15 @@
obj-$(CONFIG_ACPI_ALS) += acpi-als.o
obj-$(CONFIG_ADJD_S311) += adjd_s311.o
obj-$(CONFIG_ADUX1020) += adux1020.o
+obj-$(CONFIG_AL3000A) += al3000a.o
obj-$(CONFIG_AL3010) += al3010.o
obj-$(CONFIG_AL3320A) += al3320a.o
+obj-$(CONFIG_APDS9160) += apds9160.o
obj-$(CONFIG_APDS9300) += apds9300.o
+obj-$(CONFIG_APDS9306) += apds9306.o
obj-$(CONFIG_APDS9960) += apds9960.o
obj-$(CONFIG_AS73211) += as73211.o
+obj-$(CONFIG_BH1745) += bh1745.o
obj-$(CONFIG_BH1750) += bh1750.o
obj-$(CONFIG_BH1780) += bh1780.o
obj-$(CONFIG_CM32181) += cm32181.o
@@ -40,8 +44,8 @@ obj-$(CONFIG_MAX44009) += max44009.o
obj-$(CONFIG_NOA1305) += noa1305.o
obj-$(CONFIG_OPT3001) += opt3001.o
obj-$(CONFIG_OPT4001) += opt4001.o
+obj-$(CONFIG_OPT4060) += opt4060.o
obj-$(CONFIG_PA12203001) += pa12203001.o
-obj-$(CONFIG_ROHM_BU27008) += rohm-bu27008.o
obj-$(CONFIG_ROHM_BU27034) += rohm-bu27034.o
obj-$(CONFIG_RPR0521) += rpr0521.o
obj-$(CONFIG_SI1133) += si1133.o
@@ -60,7 +64,9 @@ obj-$(CONFIG_TSL4531) += tsl4531.o
obj-$(CONFIG_US5182D) += us5182d.o
obj-$(CONFIG_VCNL4000) += vcnl4000.o
obj-$(CONFIG_VCNL4035) += vcnl4035.o
+obj-$(CONFIG_VEML3235) += veml3235.o
obj-$(CONFIG_VEML6030) += veml6030.o
+obj-$(CONFIG_VEML6040) += veml6040.o
obj-$(CONFIG_VEML6070) += veml6070.o
obj-$(CONFIG_VEML6075) += veml6075.o
obj-$(CONFIG_VL6180) += vl6180.o
diff --git a/drivers/iio/light/acpi-als.c b/drivers/iio/light/acpi-als.c
index 2d91caf24dd0..032e6cae8b80 100644
--- a/drivers/iio/light/acpi-als.c
+++ b/drivers/iio/light/acpi-als.c
@@ -230,7 +230,7 @@ static int acpi_als_add(struct acpi_device *device)
static const struct acpi_device_id acpi_als_device_ids[] = {
{"ACPI0008", 0},
- {},
+ { }
};
MODULE_DEVICE_TABLE(acpi, acpi_als_device_ids);
diff --git a/drivers/iio/light/adjd_s311.c b/drivers/iio/light/adjd_s311.c
index 5fd775a20176..cf96e3dd8bc6 100644
--- a/drivers/iio/light/adjd_s311.c
+++ b/drivers/iio/light/adjd_s311.c
@@ -56,7 +56,7 @@ struct adjd_s311_data {
struct i2c_client *client;
struct {
s16 chans[4];
- s64 ts __aligned(8);
+ aligned_s64 ts;
} scan;
};
@@ -125,8 +125,7 @@ static irqreturn_t adjd_s311_trigger_handler(int irq, void *p)
if (ret < 0)
goto done;
- for_each_set_bit(i, indio_dev->active_scan_mask,
- indio_dev->masklength) {
+ iio_for_each_active_channel(indio_dev, i) {
ret = i2c_smbus_read_word_data(data->client,
ADJD_S311_DATA_REG(i));
if (ret < 0)
@@ -261,7 +260,7 @@ static int adjd_s311_probe(struct i2c_client *client)
}
static const struct i2c_device_id adjd_s311_id[] = {
- { "adjd_s311", 0 },
+ { "adjd_s311" },
{ }
};
MODULE_DEVICE_TABLE(i2c, adjd_s311_id);
diff --git a/drivers/iio/light/adux1020.c b/drivers/iio/light/adux1020.c
index aa4a6c78f0aa..e321f89c5340 100644
--- a/drivers/iio/light/adux1020.c
+++ b/drivers/iio/light/adux1020.c
@@ -118,7 +118,6 @@ static const struct regmap_config adux1020_regmap_config = {
.reg_bits = 8,
.val_bits = 16,
.max_register = 0x6F,
- .cache_type = REGCACHE_NONE,
};
static const struct reg_sequence adux1020_def_conf[] = {
@@ -502,7 +501,8 @@ fail:
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)
+ enum iio_event_direction dir,
+ bool state)
{
struct adux1020_data *data = iio_priv(indio_dev);
int ret, mask;
@@ -526,12 +526,11 @@ static int adux1020_write_event_config(struct iio_dev *indio_dev,
mask = ADUX1020_PROX_OFF1_INT;
if (state)
- state = 0;
+ ret = regmap_clear_bits(data->regmap,
+ ADUX1020_REG_INT_MASK, mask);
else
- state = mask;
-
- ret = regmap_update_bits(data->regmap, ADUX1020_REG_INT_MASK,
- mask, state);
+ ret = regmap_set_bits(data->regmap,
+ ADUX1020_REG_INT_MASK, mask);
if (ret < 0)
goto fail;
@@ -539,9 +538,8 @@ static int adux1020_write_event_config(struct iio_dev *indio_dev,
* 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);
+ ret = regmap_set_bits(data->regmap, ADUX1020_REG_PROX_TYPE,
+ ADUX1020_PROX_TYPE);
if (ret < 0)
goto fail;
@@ -748,8 +746,8 @@ static int adux1020_chip_init(struct adux1020_data *data)
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);
+ ret = regmap_set_bits(data->regmap, ADUX1020_REG_SW_RESET,
+ ADUX1020_SW_RESET);
if (ret < 0)
return ret;
@@ -764,8 +762,8 @@ static int adux1020_chip_init(struct adux1020_data *data)
return ret;
/* Use LED_IREF for proximity mode */
- ret = regmap_update_bits(data->regmap, ADUX1020_REG_LED_CURRENT,
- ADUX1020_LED_PIREF_EN, 0);
+ ret = regmap_clear_bits(data->regmap, ADUX1020_REG_LED_CURRENT,
+ ADUX1020_LED_PIREF_EN);
if (ret < 0)
return ret;
@@ -821,8 +819,8 @@ static int adux1020_probe(struct i2c_client *client)
}
static const struct i2c_device_id adux1020_id[] = {
- { "adux1020", 0 },
- {}
+ { "adux1020" },
+ { }
};
MODULE_DEVICE_TABLE(i2c, adux1020_id);
diff --git a/drivers/iio/light/al3000a.c b/drivers/iio/light/al3000a.c
new file mode 100644
index 000000000000..6f301c067045
--- /dev/null
+++ b/drivers/iio/light/al3000a.c
@@ -0,0 +1,210 @@
+// SPDX-License-Identifier: GPL-2.0-only
+#include <linux/array_size.h>
+#include <linux/bitfield.h>
+#include <linux/device.h>
+#include <linux/err.h>
+#include <linux/i2c.h>
+#include <linux/mod_devicetable.h>
+#include <linux/module.h>
+#include <linux/pm.h>
+#include <linux/regmap.h>
+#include <linux/regulator/consumer.h>
+#include <linux/types.h>
+
+#include <linux/iio/iio.h>
+
+#define AL3000A_REG_SYSTEM 0x00
+#define AL3000A_REG_DATA 0x05
+
+#define AL3000A_CONFIG_ENABLE 0x00
+#define AL3000A_CONFIG_DISABLE 0x0b
+#define AL3000A_CONFIG_RESET 0x0f
+#define AL3000A_GAIN_MASK GENMASK(5, 0)
+
+/*
+ * These are pre-calculated lux values based on possible output of sensor
+ * (range 0x00 - 0x3F)
+ */
+static const u32 lux_table[] = {
+ 1, 1, 1, 2, 2, 2, 3, 4, /* 0 - 7 */
+ 4, 5, 6, 7, 9, 11, 13, 16, /* 8 - 15 */
+ 19, 22, 27, 32, 39, 46, 56, 67, /* 16 - 23 */
+ 80, 96, 116, 139, 167, 200, 240, 289, /* 24 - 31 */
+ 347, 416, 499, 600, 720, 864, 1037, 1245, /* 32 - 39 */
+ 1495, 1795, 2155, 2587, 3105, 3728, 4475, 5373, /* 40 - 47 */
+ 6450, 7743, 9296, 11160, 13397, 16084, 19309, 23180, /* 48 - 55 */
+ 27828, 33408, 40107, 48148, 57803, 69393, 83306, 100000 /* 56 - 63 */
+};
+
+static const struct regmap_config al3000a_regmap_config = {
+ .reg_bits = 8,
+ .val_bits = 8,
+ .max_register = AL3000A_REG_DATA,
+};
+
+struct al3000a_data {
+ struct regmap *regmap;
+ struct regulator *vdd_supply;
+};
+
+static const struct iio_chan_spec al3000a_channels[] = {
+ {
+ .type = IIO_LIGHT,
+ .info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED),
+ },
+};
+
+static int al3000a_set_pwr_on(struct al3000a_data *data)
+{
+ struct device *dev = regmap_get_device(data->regmap);
+ int ret;
+
+ ret = regulator_enable(data->vdd_supply);
+ if (ret) {
+ dev_err(dev, "failed to enable vdd power supply\n");
+ return ret;
+ }
+
+ return regmap_write(data->regmap, AL3000A_REG_SYSTEM, AL3000A_CONFIG_ENABLE);
+}
+
+static void al3000a_set_pwr_off(void *_data)
+{
+ struct al3000a_data *data = _data;
+ struct device *dev = regmap_get_device(data->regmap);
+ int ret;
+
+ ret = regmap_write(data->regmap, AL3000A_REG_SYSTEM, AL3000A_CONFIG_DISABLE);
+ if (ret)
+ dev_err(dev, "failed to write system register\n");
+
+ ret = regulator_disable(data->vdd_supply);
+ if (ret)
+ dev_err(dev, "failed to disable vdd power supply\n");
+}
+
+static int al3000a_init(struct al3000a_data *data)
+{
+ struct device *dev = regmap_get_device(data->regmap);
+ int ret;
+
+ ret = al3000a_set_pwr_on(data);
+ if (ret)
+ return ret;
+
+ ret = devm_add_action_or_reset(dev, al3000a_set_pwr_off, data);
+ if (ret)
+ return dev_err_probe(dev, ret, "failed to add action\n");
+
+ ret = regmap_write(data->regmap, AL3000A_REG_SYSTEM, AL3000A_CONFIG_RESET);
+ if (ret)
+ return ret;
+
+ return regmap_write(data->regmap, AL3000A_REG_SYSTEM, AL3000A_CONFIG_ENABLE);
+}
+
+static int al3000a_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan, int *val,
+ int *val2, long mask)
+{
+ struct al3000a_data *data = iio_priv(indio_dev);
+ int ret, gain;
+
+ switch (mask) {
+ case IIO_CHAN_INFO_PROCESSED:
+ ret = regmap_read(data->regmap, AL3000A_REG_DATA, &gain);
+ if (ret)
+ return ret;
+
+ *val = lux_table[gain & AL3000A_GAIN_MASK];
+
+ return IIO_VAL_INT;
+ default:
+ return -EINVAL;
+ }
+}
+
+static const struct iio_info al3000a_info = {
+ .read_raw = al3000a_read_raw,
+};
+
+static int al3000a_probe(struct i2c_client *client)
+{
+ struct al3000a_data *data;
+ struct device *dev = &client->dev;
+ 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->regmap = devm_regmap_init_i2c(client, &al3000a_regmap_config);
+ if (IS_ERR(data->regmap))
+ return dev_err_probe(dev, PTR_ERR(data->regmap),
+ "cannot allocate regmap\n");
+
+ data->vdd_supply = devm_regulator_get(dev, "vdd");
+ if (IS_ERR(data->vdd_supply))
+ return dev_err_probe(dev, PTR_ERR(data->vdd_supply),
+ "failed to get vdd regulator\n");
+
+ indio_dev->info = &al3000a_info;
+ indio_dev->name = "al3000a";
+ indio_dev->channels = al3000a_channels;
+ indio_dev->num_channels = ARRAY_SIZE(al3000a_channels);
+ indio_dev->modes = INDIO_DIRECT_MODE;
+
+ ret = al3000a_init(data);
+ if (ret)
+ return dev_err_probe(dev, ret, "failed to init ALS\n");
+
+ return devm_iio_device_register(dev, indio_dev);
+}
+
+static int al3000a_suspend(struct device *dev)
+{
+ struct al3000a_data *data = iio_priv(dev_get_drvdata(dev));
+
+ al3000a_set_pwr_off(data);
+ return 0;
+}
+
+static int al3000a_resume(struct device *dev)
+{
+ struct al3000a_data *data = iio_priv(dev_get_drvdata(dev));
+
+ return al3000a_set_pwr_on(data);
+}
+
+static DEFINE_SIMPLE_DEV_PM_OPS(al3000a_pm_ops, al3000a_suspend, al3000a_resume);
+
+static const struct i2c_device_id al3000a_id[] = {
+ { "al3000a" },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, al3000a_id);
+
+static const struct of_device_id al3000a_of_match[] = {
+ { .compatible = "dynaimage,al3000a" },
+ { }
+};
+MODULE_DEVICE_TABLE(of, al3000a_of_match);
+
+static struct i2c_driver al3000a_driver = {
+ .driver = {
+ .name = "al3000a",
+ .of_match_table = al3000a_of_match,
+ .pm = pm_sleep_ptr(&al3000a_pm_ops),
+ },
+ .probe = al3000a_probe,
+ .id_table = al3000a_id,
+};
+module_i2c_driver(al3000a_driver);
+
+MODULE_AUTHOR("Svyatolsav Ryhel <clamor95@gmail.com>");
+MODULE_DESCRIPTION("al3000a Ambient Light Sensor driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/iio/light/al3010.c b/drivers/iio/light/al3010.c
index 53569587ccb7..0932fa2b49fa 100644
--- a/drivers/iio/light/al3010.c
+++ b/drivers/iio/light/al3010.c
@@ -17,13 +17,12 @@
#include <linux/bitfield.h>
#include <linux/i2c.h>
#include <linux/module.h>
+#include <linux/regmap.h>
#include <linux/mod_devicetable.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
@@ -46,8 +45,14 @@ static const int al3010_scales[][2] = {
{0, 1187200}, {0, 296800}, {0, 74200}, {0, 18600}
};
+static const struct regmap_config al3010_regmap_config = {
+ .reg_bits = 8,
+ .val_bits = 8,
+ .max_register = AL3010_REG_CONFIG,
+};
+
struct al3010_data {
- struct i2c_client *client;
+ struct regmap *regmap;
};
static const struct iio_chan_spec al3010_channels[] = {
@@ -69,35 +74,36 @@ static const struct attribute_group al3010_attribute_group = {
.attrs = al3010_attributes,
};
-static int al3010_set_pwr(struct i2c_client *client, bool pwr)
+static int al3010_set_pwr_on(struct al3010_data *data)
{
- u8 val = pwr ? AL3010_CONFIG_ENABLE : AL3010_CONFIG_DISABLE;
- return i2c_smbus_write_byte_data(client, AL3010_REG_SYSTEM, val);
+ return regmap_write(data->regmap, AL3010_REG_SYSTEM, AL3010_CONFIG_ENABLE);
}
static void al3010_set_pwr_off(void *_data)
{
struct al3010_data *data = _data;
+ struct device *dev = regmap_get_device(data->regmap);
+ int ret;
- al3010_set_pwr(data->client, false);
+ ret = regmap_write(data->regmap, AL3010_REG_SYSTEM, AL3010_CONFIG_DISABLE);
+ if (ret)
+ dev_err(dev, "failed to write system register\n");
}
static int al3010_init(struct al3010_data *data)
{
+ struct device *dev = regmap_get_device(data->regmap);
int ret;
- ret = al3010_set_pwr(data->client, true);
-
- if (ret < 0)
+ ret = al3010_set_pwr_on(data);
+ if (ret)
return ret;
- ret = i2c_smbus_write_byte_data(data->client, AL3010_REG_CONFIG,
- FIELD_PREP(AL3010_GAIN_MASK,
- AL3XXX_RANGE_3));
- if (ret < 0)
+ ret = devm_add_action_or_reset(dev, al3010_set_pwr_off, data);
+ if (ret)
return ret;
-
- return 0;
+ return regmap_write(data->regmap, AL3010_REG_CONFIG,
+ FIELD_PREP(AL3010_GAIN_MASK, AL3XXX_RANGE_3));
}
static int al3010_read_raw(struct iio_dev *indio_dev,
@@ -105,7 +111,7 @@ static int al3010_read_raw(struct iio_dev *indio_dev,
int *val2, long mask)
{
struct al3010_data *data = iio_priv(indio_dev);
- int ret;
+ int ret, gain, raw;
switch (mask) {
case IIO_CHAN_INFO_RAW:
@@ -114,21 +120,21 @@ static int al3010_read_raw(struct iio_dev *indio_dev,
* - 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)
+ ret = regmap_read(data->regmap, AL3010_REG_DATA_LOW, &raw);
+ if (ret)
return ret;
- *val = ret;
+
+ *val = raw;
+
return IIO_VAL_INT;
case IIO_CHAN_INFO_SCALE:
- ret = i2c_smbus_read_byte_data(data->client,
- AL3010_REG_CONFIG);
- if (ret < 0)
+ ret = regmap_read(data->regmap, AL3010_REG_CONFIG, &gain);
+ if (ret)
return ret;
- ret = FIELD_GET(AL3010_GAIN_MASK, ret);
- *val = al3010_scales[ret][0];
- *val2 = al3010_scales[ret][1];
+ gain = FIELD_GET(AL3010_GAIN_MASK, gain);
+ *val = al3010_scales[gain][0];
+ *val2 = al3010_scales[gain][1];
return IIO_VAL_INT_PLUS_MICRO;
}
@@ -140,7 +146,7 @@ static int al3010_write_raw(struct iio_dev *indio_dev,
int val2, long mask)
{
struct al3010_data *data = iio_priv(indio_dev);
- int i;
+ unsigned int i;
switch (mask) {
case IIO_CHAN_INFO_SCALE:
@@ -149,9 +155,8 @@ static int al3010_write_raw(struct iio_dev *indio_dev,
val2 != al3010_scales[i][1])
continue;
- return i2c_smbus_write_byte_data(data->client,
- AL3010_REG_CONFIG,
- FIELD_PREP(AL3010_GAIN_MASK, i));
+ return regmap_write(data->regmap, AL3010_REG_CONFIG,
+ FIELD_PREP(AL3010_GAIN_MASK, i));
}
break;
}
@@ -167,65 +172,66 @@ static const struct iio_info al3010_info = {
static int al3010_probe(struct i2c_client *client)
{
struct al3010_data *data;
+ struct device *dev = &client->dev;
struct iio_dev *indio_dev;
int ret;
- indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data));
+ 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;
+ data->regmap = devm_regmap_init_i2c(client, &al3010_regmap_config);
+ if (IS_ERR(data->regmap))
+ return dev_err_probe(dev, PTR_ERR(data->regmap),
+ "cannot allocate regmap\n");
indio_dev->info = &al3010_info;
- indio_dev->name = AL3010_DRV_NAME;
+ indio_dev->name = "al3010";
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;
+ if (ret)
+ return dev_err_probe(dev, ret, "failed to init ALS\n");
- return devm_iio_device_register(&client->dev, indio_dev);
+ return devm_iio_device_register(dev, indio_dev);
}
static int al3010_suspend(struct device *dev)
{
- return al3010_set_pwr(to_i2c_client(dev), false);
+ struct al3010_data *data = iio_priv(dev_get_drvdata(dev));
+
+ al3010_set_pwr_off(data);
+ return 0;
}
static int al3010_resume(struct device *dev)
{
- return al3010_set_pwr(to_i2c_client(dev), true);
+ struct al3010_data *data = iio_priv(dev_get_drvdata(dev));
+
+ return al3010_set_pwr_on(data);
}
static DEFINE_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,
+ .name = "al3010",
.of_match_table = al3010_of_match,
.pm = pm_sleep_ptr(&al3010_pm_ops),
},
diff --git a/drivers/iio/light/al3320a.c b/drivers/iio/light/al3320a.c
index 105f379b9b41..63f5a85912fc 100644
--- a/drivers/iio/light/al3320a.c
+++ b/drivers/iio/light/al3320a.c
@@ -15,13 +15,12 @@
#include <linux/bitfield.h>
#include <linux/i2c.h>
#include <linux/module.h>
+#include <linux/regmap.h>
#include <linux/mod_devicetable.h>
#include <linux/iio/iio.h>
#include <linux/iio/sysfs.h>
-#define AL3320A_DRV_NAME "al3320a"
-
#define AL3320A_REG_CONFIG 0x00
#define AL3320A_REG_STATUS 0x01
#define AL3320A_REG_INT 0x02
@@ -59,8 +58,14 @@ static const int al3320a_scales[][2] = {
{0, 512000}, {0, 128000}, {0, 32000}, {0, 10000}
};
+static const struct regmap_config al3320a_regmap_config = {
+ .reg_bits = 8,
+ .val_bits = 8,
+ .max_register = AL3320A_REG_HIGH_THRESH_HIGH,
+};
+
struct al3320a_data {
- struct i2c_client *client;
+ struct regmap *regmap;
};
static const struct iio_chan_spec al3320a_channels[] = {
@@ -82,45 +87,47 @@ static const struct attribute_group al3320a_attribute_group = {
.attrs = al3320a_attributes,
};
-static int al3320a_set_pwr(struct i2c_client *client, bool pwr)
+static int al3320a_set_pwr_on(struct al3320a_data *data)
{
- u8 val = pwr ? AL3320A_CONFIG_ENABLE : AL3320A_CONFIG_DISABLE;
- return i2c_smbus_write_byte_data(client, AL3320A_REG_CONFIG, val);
+ return regmap_write(data->regmap, AL3320A_REG_CONFIG, AL3320A_CONFIG_ENABLE);
}
static void al3320a_set_pwr_off(void *_data)
{
struct al3320a_data *data = _data;
+ struct device *dev = regmap_get_device(data->regmap);
+ int ret;
- al3320a_set_pwr(data->client, false);
+ ret = regmap_write(data->regmap, AL3320A_REG_CONFIG, AL3320A_CONFIG_DISABLE);
+ if (ret)
+ dev_err(dev, "failed to write system register\n");
}
static int al3320a_init(struct al3320a_data *data)
{
+ struct device *dev = regmap_get_device(data->regmap);
int ret;
- ret = al3320a_set_pwr(data->client, true);
-
- if (ret < 0)
+ ret = al3320a_set_pwr_on(data);
+ if (ret)
return ret;
- ret = i2c_smbus_write_byte_data(data->client, AL3320A_REG_CONFIG_RANGE,
- FIELD_PREP(AL3320A_GAIN_MASK,
- AL3320A_RANGE_3));
- if (ret < 0)
+ ret = devm_add_action_or_reset(dev, al3320a_set_pwr_off, data);
+ if (ret)
return ret;
- ret = i2c_smbus_write_byte_data(data->client, AL3320A_REG_MEAN_TIME,
- AL3320A_DEFAULT_MEAN_TIME);
- if (ret < 0)
+ ret = regmap_write(data->regmap, AL3320A_REG_CONFIG_RANGE,
+ FIELD_PREP(AL3320A_GAIN_MASK, AL3320A_RANGE_3));
+ if (ret)
return ret;
- ret = i2c_smbus_write_byte_data(data->client, AL3320A_REG_WAIT,
- AL3320A_DEFAULT_WAIT_TIME);
- if (ret < 0)
+ ret = regmap_write(data->regmap, AL3320A_REG_MEAN_TIME,
+ AL3320A_DEFAULT_MEAN_TIME);
+ if (ret)
return ret;
- return 0;
+ return regmap_write(data->regmap, AL3320A_REG_WAIT,
+ AL3320A_DEFAULT_WAIT_TIME);
}
static int al3320a_read_raw(struct iio_dev *indio_dev,
@@ -128,7 +135,7 @@ static int al3320a_read_raw(struct iio_dev *indio_dev,
int *val2, long mask)
{
struct al3320a_data *data = iio_priv(indio_dev);
- int ret;
+ int ret, gain, raw;
switch (mask) {
case IIO_CHAN_INFO_RAW:
@@ -137,21 +144,21 @@ static int al3320a_read_raw(struct iio_dev *indio_dev,
* - low byte of output is stored at AL3320A_REG_DATA_LOW
* - high byte of output is stored at AL3320A_REG_DATA_LOW + 1
*/
- ret = i2c_smbus_read_word_data(data->client,
- AL3320A_REG_DATA_LOW);
- if (ret < 0)
+ ret = regmap_read(data->regmap, AL3320A_REG_DATA_LOW, &raw);
+ if (ret)
return ret;
- *val = ret;
+
+ *val = raw;
+
return IIO_VAL_INT;
case IIO_CHAN_INFO_SCALE:
- ret = i2c_smbus_read_byte_data(data->client,
- AL3320A_REG_CONFIG_RANGE);
- if (ret < 0)
+ ret = regmap_read(data->regmap, AL3320A_REG_CONFIG_RANGE, &gain);
+ if (ret)
return ret;
- ret = FIELD_GET(AL3320A_GAIN_MASK, ret);
- *val = al3320a_scales[ret][0];
- *val2 = al3320a_scales[ret][1];
+ gain = FIELD_GET(AL3320A_GAIN_MASK, gain);
+ *val = al3320a_scales[gain][0];
+ *val2 = al3320a_scales[gain][1];
return IIO_VAL_INT_PLUS_MICRO;
}
@@ -163,7 +170,7 @@ static int al3320a_write_raw(struct iio_dev *indio_dev,
int val2, long mask)
{
struct al3320a_data *data = iio_priv(indio_dev);
- int i;
+ unsigned int i;
switch (mask) {
case IIO_CHAN_INFO_SCALE:
@@ -172,9 +179,8 @@ static int al3320a_write_raw(struct iio_dev *indio_dev,
val2 != al3320a_scales[i][1])
continue;
- return i2c_smbus_write_byte_data(data->client,
- AL3320A_REG_CONFIG_RANGE,
- FIELD_PREP(AL3320A_GAIN_MASK, i));
+ return regmap_write(data->regmap, AL3320A_REG_CONFIG_RANGE,
+ FIELD_PREP(AL3320A_GAIN_MASK, i));
}
break;
}
@@ -190,72 +196,76 @@ static const struct iio_info al3320a_info = {
static int al3320a_probe(struct i2c_client *client)
{
struct al3320a_data *data;
+ struct device *dev = &client->dev;
struct iio_dev *indio_dev;
int ret;
- indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data));
+ 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;
+
+ data->regmap = devm_regmap_init_i2c(client, &al3320a_regmap_config);
+ if (IS_ERR(data->regmap))
+ return dev_err_probe(dev, PTR_ERR(data->regmap),
+ "cannot allocate regmap\n");
indio_dev->info = &al3320a_info;
- indio_dev->name = AL3320A_DRV_NAME;
+ indio_dev->name = "al3320a";
indio_dev->channels = al3320a_channels;
indio_dev->num_channels = ARRAY_SIZE(al3320a_channels);
indio_dev->modes = INDIO_DIRECT_MODE;
ret = al3320a_init(data);
if (ret < 0) {
- dev_err(&client->dev, "al3320a chip init failed\n");
+ dev_err(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);
+ return devm_iio_device_register(dev, indio_dev);
}
static int al3320a_suspend(struct device *dev)
{
- return al3320a_set_pwr(to_i2c_client(dev), false);
+ struct al3320a_data *data = iio_priv(dev_get_drvdata(dev));
+
+ al3320a_set_pwr_off(data);
+ return 0;
}
static int al3320a_resume(struct device *dev)
{
- return al3320a_set_pwr(to_i2c_client(dev), true);
+ struct al3320a_data *data = iio_priv(dev_get_drvdata(dev));
+
+ return al3320a_set_pwr_on(data);
}
static DEFINE_SIMPLE_DEV_PM_OPS(al3320a_pm_ops, al3320a_suspend,
al3320a_resume);
static const struct i2c_device_id al3320a_id[] = {
- {"al3320a", 0},
- {}
+ { "al3320a" },
+ { }
};
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 const struct acpi_device_id al3320a_acpi_match[] = {
{"CALS0001"},
- { },
+ { }
};
MODULE_DEVICE_TABLE(acpi, al3320a_acpi_match);
static struct i2c_driver al3320a_driver = {
.driver = {
- .name = AL3320A_DRV_NAME,
+ .name = "al3320a",
.of_match_table = al3320a_of_match,
.pm = pm_sleep_ptr(&al3320a_pm_ops),
.acpi_match_table = al3320a_acpi_match,
diff --git a/drivers/iio/light/apds9160.c b/drivers/iio/light/apds9160.c
new file mode 100644
index 000000000000..d3f415930ec9
--- /dev/null
+++ b/drivers/iio/light/apds9160.c
@@ -0,0 +1,1594 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * APDS9160 sensor driver.
+ * Chip is combined proximity and ambient light sensor.
+ * Author: 2024 Mikael Gonella-Bolduc <m.gonella.bolduc@gmail.com>
+ */
+
+#include <linux/bits.h>
+#include <linux/bitfield.h>
+#include <linux/cleanup.h>
+#include <linux/delay.h>
+#include <linux/err.h>
+#include <linux/i2c.h>
+#include <linux/interrupt.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/regmap.h>
+#include <linux/regulator/consumer.h>
+#include <linux/types.h>
+#include <linux/units.h>
+
+#include <linux/iio/iio.h>
+#include <linux/iio/events.h>
+#include <linux/iio/sysfs.h>
+
+#include <linux/unaligned.h>
+
+#define APDS9160_REGMAP_NAME "apds9160_regmap"
+
+/* Main control register */
+#define APDS9160_REG_CTRL 0x00
+#define APDS9160_CTRL_SWRESET BIT(4) /* 1: Activate reset */
+#define APDS9160_CTRL_MODE_RGB BIT(2) /* 0: ALS & IR, 1: RGB & IR */
+#define APDS9160_CTRL_EN_ALS BIT(1) /* 1: ALS active */
+#define APDS9160_CTLR_EN_PS BIT(0) /* 1: PS active */
+
+/* Status register */
+#define APDS9160_SR_LS_INT BIT(4)
+#define APDS9160_SR_LS_NEW_DATA BIT(3)
+#define APDS9160_SR_PS_INT BIT(1)
+#define APDS9160_SR_PS_NEW_DATA BIT(0)
+
+/* Interrupt configuration registers */
+#define APDS9160_REG_INT_CFG 0x19
+#define APDS9160_REG_INT_PST 0x1A
+#define APDS9160_INT_CFG_EN_LS BIT(2) /* LS int enable */
+#define APDS9160_INT_CFG_EN_PS BIT(0) /* PS int enable */
+
+/* Proximity registers */
+#define APDS9160_REG_PS_LED 0x01
+#define APDS9160_REG_PS_PULSES 0x02
+#define APDS9160_REG_PS_MEAS_RATE 0x03
+#define APDS9160_REG_PS_THRES_HI_LSB 0x1B
+#define APDS9160_REG_PS_THRES_HI_MSB 0x1C
+#define APDS9160_REG_PS_THRES_LO_LSB 0x1D
+#define APDS9160_REG_PS_THRES_LO_MSB 0x1E
+#define APDS9160_REG_PS_DATA_LSB 0x08
+#define APDS9160_REG_PS_DATA_MSB 0x09
+#define APDS9160_REG_PS_CAN_LEVEL_DIG_LSB 0x1F
+#define APDS9160_REG_PS_CAN_LEVEL_DIG_MSB 0x20
+#define APDS9160_REG_PS_CAN_LEVEL_ANA_DUR 0x21
+#define APDS9160_REG_PS_CAN_LEVEL_ANA_CURRENT 0x22
+
+/* Light sensor registers */
+#define APDS9160_REG_LS_MEAS_RATE 0x04
+#define APDS9160_REG_LS_GAIN 0x05
+#define APDS9160_REG_LS_DATA_CLEAR_LSB 0x0A
+#define APDS9160_REG_LS_DATA_CLEAR 0x0B
+#define APDS9160_REG_LS_DATA_CLEAR_MSB 0x0C
+#define APDS9160_REG_LS_DATA_ALS_LSB 0x0D
+#define APDS9160_REG_LS_DATA_ALS 0x0E
+#define APDS9160_REG_LS_DATA_ALS_MSB 0x0F
+#define APDS9160_REG_LS_THRES_UP_LSB 0x24
+#define APDS9160_REG_LS_THRES_UP 0x25
+#define APDS9160_REG_LS_THRES_UP_MSB 0x26
+#define APDS9160_REG_LS_THRES_LO_LSB 0x27
+#define APDS9160_REG_LS_THRES_LO 0x28
+#define APDS9160_REG_LS_THRES_LO_MSB 0x29
+#define APDS9160_REG_LS_THRES_VAR 0x2A
+
+/* Part identification number register */
+#define APDS9160_REG_ID 0x06
+
+/* Status register */
+#define APDS9160_REG_SR 0x07
+#define APDS9160_SR_DATA_ALS BIT(3)
+#define APDS9160_SR_DATA_PS BIT(0)
+
+/* Supported ID:s */
+#define APDS9160_PART_ID_0 0x03
+
+#define APDS9160_PS_THRES_MAX 0x7FF
+#define APDS9160_LS_THRES_MAX 0xFFFFF
+#define APDS9160_CMD_LS_RESOLUTION_25MS 0x04
+#define APDS9160_CMD_LS_RESOLUTION_50MS 0x03
+#define APDS9160_CMD_LS_RESOLUTION_100MS 0x02
+#define APDS9160_CMD_LS_RESOLUTION_200MS 0x01
+#define APDS9160_PS_DATA_MASK 0x7FF
+
+#define APDS9160_DEFAULT_LS_GAIN 3
+#define APDS9160_DEFAULT_LS_RATE 100
+#define APDS9160_DEFAULT_PS_RATE 100
+#define APDS9160_DEFAULT_PS_CANCELLATION_LEVEL 0
+#define APDS9160_DEFAULT_PS_ANALOG_CANCELLATION 0
+#define APDS9160_DEFAULT_PS_GAIN 1
+#define APDS9160_DEFAULT_PS_CURRENT 100
+#define APDS9160_DEFAULT_PS_RESOLUTION_11BITS 0x03
+
+static const struct reg_default apds9160_reg_defaults[] = {
+ { APDS9160_REG_CTRL, 0x00 }, /* Sensors disabled by default */
+ { APDS9160_REG_PS_LED, 0x33 }, /* 60 kHz frequency, 100 mA */
+ { APDS9160_REG_PS_PULSES, 0x08 }, /* 8 pulses */
+ { APDS9160_REG_PS_MEAS_RATE, 0x05 }, /* 100ms */
+ { APDS9160_REG_LS_MEAS_RATE, 0x22 }, /* 100ms */
+ { APDS9160_REG_LS_GAIN, 0x01 }, /* 3x */
+ { APDS9160_REG_INT_CFG, 0x10 }, /* Interrupts disabled */
+ { APDS9160_REG_INT_PST, 0x00 },
+ { APDS9160_REG_PS_THRES_HI_LSB, 0xFF },
+ { APDS9160_REG_PS_THRES_HI_MSB, 0x07 },
+ { APDS9160_REG_PS_THRES_LO_LSB, 0x00 },
+ { APDS9160_REG_PS_THRES_LO_MSB, 0x00 },
+ { APDS9160_REG_PS_CAN_LEVEL_DIG_LSB, 0x00 },
+ { APDS9160_REG_PS_CAN_LEVEL_DIG_MSB, 0x00 },
+ { APDS9160_REG_PS_CAN_LEVEL_ANA_DUR, 0x00 },
+ { APDS9160_REG_PS_CAN_LEVEL_ANA_CURRENT, 0x00 },
+ { APDS9160_REG_LS_THRES_UP_LSB, 0xFF },
+ { APDS9160_REG_LS_THRES_UP, 0xFF },
+ { APDS9160_REG_LS_THRES_UP_MSB, 0x0F },
+ { APDS9160_REG_LS_THRES_LO_LSB, 0x00 },
+ { APDS9160_REG_LS_THRES_LO, 0x00 },
+ { APDS9160_REG_LS_THRES_LO_MSB, 0x00 },
+ { APDS9160_REG_LS_THRES_VAR, 0x00 },
+};
+
+static const struct regmap_range apds9160_readable_ranges[] = {
+ regmap_reg_range(APDS9160_REG_CTRL, APDS9160_REG_LS_THRES_VAR),
+};
+
+static const struct regmap_access_table apds9160_readable_table = {
+ .yes_ranges = apds9160_readable_ranges,
+ .n_yes_ranges = ARRAY_SIZE(apds9160_readable_ranges),
+};
+
+static const struct regmap_range apds9160_writeable_ranges[] = {
+ regmap_reg_range(APDS9160_REG_CTRL, APDS9160_REG_LS_GAIN),
+ regmap_reg_range(APDS9160_REG_INT_CFG, APDS9160_REG_LS_THRES_VAR),
+};
+
+static const struct regmap_access_table apds9160_writeable_table = {
+ .yes_ranges = apds9160_writeable_ranges,
+ .n_yes_ranges = ARRAY_SIZE(apds9160_writeable_ranges),
+};
+
+static const struct regmap_range apds9160_volatile_ranges[] = {
+ regmap_reg_range(APDS9160_REG_SR, APDS9160_REG_LS_DATA_ALS_MSB),
+};
+
+static const struct regmap_access_table apds9160_volatile_table = {
+ .yes_ranges = apds9160_volatile_ranges,
+ .n_yes_ranges = ARRAY_SIZE(apds9160_volatile_ranges),
+};
+
+static const struct regmap_config apds9160_regmap_config = {
+ .name = APDS9160_REGMAP_NAME,
+ .reg_bits = 8,
+ .val_bits = 8,
+ .use_single_read = true,
+ .use_single_write = true,
+
+ .rd_table = &apds9160_readable_table,
+ .wr_table = &apds9160_writeable_table,
+ .volatile_table = &apds9160_volatile_table,
+
+ .reg_defaults = apds9160_reg_defaults,
+ .num_reg_defaults = ARRAY_SIZE(apds9160_reg_defaults),
+ .max_register = 37,
+ .cache_type = REGCACHE_RBTREE,
+};
+
+static const struct iio_event_spec apds9160_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 apds9160_channels[] = {
+ {
+ /* Proximity sensor channel */
+ .type = IIO_PROXIMITY,
+ .address = APDS9160_REG_PS_DATA_LSB,
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
+ .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_INT_TIME) |
+ BIT(IIO_CHAN_INFO_SCALE) |
+ BIT(IIO_CHAN_INFO_CALIBBIAS),
+ .info_mask_separate_available = BIT(IIO_CHAN_INFO_INT_TIME) |
+ BIT(IIO_CHAN_INFO_SCALE),
+ .event_spec = apds9160_event_spec,
+ .num_event_specs = ARRAY_SIZE(apds9160_event_spec),
+ },
+ {
+ /* Proximity sensor led current */
+ .type = IIO_CURRENT,
+ .output = 1,
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
+ .info_mask_separate_available = BIT(IIO_CHAN_INFO_RAW),
+ },
+ {
+ /* Illuminance */
+ .type = IIO_LIGHT,
+ .address = APDS9160_REG_LS_DATA_ALS_LSB,
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
+ .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_INT_TIME) |
+ BIT(IIO_CHAN_INFO_HARDWAREGAIN) |
+ BIT(IIO_CHAN_INFO_SCALE),
+ .info_mask_separate_available = BIT(IIO_CHAN_INFO_INT_TIME) |
+ BIT(IIO_CHAN_INFO_SCALE),
+ .event_spec = apds9160_event_spec,
+ .num_event_specs = ARRAY_SIZE(apds9160_event_spec),
+ },
+ {
+ /* Clear channel */
+ .type = IIO_INTENSITY,
+ .address = APDS9160_REG_LS_DATA_CLEAR_LSB,
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
+ .channel2 = IIO_MOD_LIGHT_CLEAR,
+ .modified = 1,
+ },
+};
+
+static const struct iio_chan_spec apds9160_channels_without_events[] = {
+ {
+ /* Proximity sensor channel */
+ .type = IIO_PROXIMITY,
+ .address = APDS9160_REG_PS_DATA_LSB,
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
+ .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_INT_TIME) |
+ BIT(IIO_CHAN_INFO_SCALE) |
+ BIT(IIO_CHAN_INFO_CALIBBIAS),
+ .info_mask_separate_available = BIT(IIO_CHAN_INFO_INT_TIME) |
+ BIT(IIO_CHAN_INFO_SCALE),
+ },
+ {
+ /* Proximity sensor led current */
+ .type = IIO_CURRENT,
+ .output = 1,
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
+ .info_mask_separate_available = BIT(IIO_CHAN_INFO_RAW),
+ },
+ {
+ /* Illuminance */
+ .type = IIO_LIGHT,
+ .address = APDS9160_REG_LS_DATA_ALS_LSB,
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
+ .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_INT_TIME) |
+ BIT(IIO_CHAN_INFO_HARDWAREGAIN) |
+ BIT(IIO_CHAN_INFO_SCALE),
+ .info_mask_separate_available = BIT(IIO_CHAN_INFO_INT_TIME) |
+ BIT(IIO_CHAN_INFO_SCALE),
+ },
+ {
+ /* Clear channel */
+ .type = IIO_INTENSITY,
+ .address = APDS9160_REG_LS_DATA_CLEAR_LSB,
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
+ .channel2 = IIO_MOD_LIGHT_CLEAR,
+ .modified = 1,
+ },
+};
+
+static const int apds9160_als_rate_avail[] = {
+ 25, 50, 100, 200
+};
+
+static const int apds9160_als_rate_map[][2] = {
+ { 25, 0x00 },
+ { 50, 0x01 },
+ { 100, 0x02 },
+ { 200, 0x03 },
+};
+
+static const int apds9160_als_gain_map[][2] = {
+ { 1, 0x00 },
+ { 3, 0x01 },
+ { 6, 0x02 },
+ { 18, 0x03 },
+ { 54, 0x04 },
+};
+
+static const int apds9160_ps_gain_avail[] = {
+ 1, 2, 4, 8
+};
+
+static const int apds9160_ps_gain_map[][2] = {
+ { 1, 0x00 },
+ { 2, 0x01 },
+ { 4, 0x02 },
+ { 8, 0x03 },
+};
+
+static const int apds9160_ps_rate_avail[] = {
+ 25, 50, 100, 200, 400
+};
+
+static const int apds9160_ps_rate_map[][2] = {
+ { 25, 0x03 },
+ { 50, 0x04 },
+ { 100, 0x05 },
+ { 200, 0x06 },
+ { 400, 0x07 },
+};
+
+static const int apds9160_ps_led_current_avail[] = {
+ 10, 25, 50, 100, 150, 175, 200
+};
+
+static const int apds9160_ps_led_current_map[][2] = {
+ { 10, 0x00 },
+ { 25, 0x01 },
+ { 50, 0x02 },
+ { 100, 0x03 },
+ { 150, 0x04 },
+ { 175, 0x05 },
+ { 200, 0x06 },
+};
+
+/**
+ * struct apds9160_scale - apds9160 scale mapping definition
+ *
+ * @itime: Integration time in ms
+ * @gain: Gain multiplier
+ * @scale1: lux/count resolution
+ * @scale2: micro lux/count
+ */
+struct apds9160_scale {
+ int itime;
+ int gain;
+ int scale1;
+ int scale2;
+};
+
+/* Scale mapping extracted from datasheet */
+static const struct apds9160_scale apds9160_als_scale_map[] = {
+ {
+ .itime = 25,
+ .gain = 1,
+ .scale1 = 3,
+ .scale2 = 272000,
+ },
+ {
+ .itime = 25,
+ .gain = 3,
+ .scale1 = 1,
+ .scale2 = 77000,
+ },
+ {
+ .itime = 25,
+ .gain = 6,
+ .scale1 = 0,
+ .scale2 = 525000,
+ },
+ {
+ .itime = 25,
+ .gain = 18,
+ .scale1 = 0,
+ .scale2 = 169000,
+ },
+ {
+ .itime = 25,
+ .gain = 54,
+ .scale1 = 0,
+ .scale2 = 49000,
+ },
+ {
+ .itime = 50,
+ .gain = 1,
+ .scale1 = 1,
+ .scale2 = 639000,
+ },
+ {
+ .itime = 50,
+ .gain = 3,
+ .scale1 = 0,
+ .scale2 = 538000,
+ },
+ {
+ .itime = 50,
+ .gain = 6,
+ .scale1 = 0,
+ .scale2 = 263000,
+ },
+ {
+ .itime = 50,
+ .gain = 18,
+ .scale1 = 0,
+ .scale2 = 84000,
+ },
+ {
+ .itime = 50,
+ .gain = 54,
+ .scale1 = 0,
+ .scale2 = 25000,
+ },
+ {
+ .itime = 100,
+ .gain = 1,
+ .scale1 = 0,
+ .scale2 = 819000,
+ },
+ {
+ .itime = 100,
+ .gain = 3,
+ .scale1 = 0,
+ .scale2 = 269000,
+ },
+ {
+ .itime = 100,
+ .gain = 6,
+ .scale1 = 0,
+ .scale2 = 131000,
+ },
+ {
+ .itime = 100,
+ .gain = 18,
+ .scale1 = 0,
+ .scale2 = 42000,
+ },
+ {
+ .itime = 100,
+ .gain = 54,
+ .scale1 = 0,
+ .scale2 = 12000,
+ },
+ {
+ .itime = 200,
+ .gain = 1,
+ .scale1 = 0,
+ .scale2 = 409000,
+ },
+ {
+ .itime = 200,
+ .gain = 3,
+ .scale1 = 0,
+ .scale2 = 135000,
+ },
+ {
+ .itime = 200,
+ .gain = 6,
+ .scale1 = 0,
+ .scale2 = 66000,
+ },
+ {
+ .itime = 200,
+ .gain = 18,
+ .scale1 = 0,
+ .scale2 = 21000,
+ },
+ {
+ .itime = 200,
+ .gain = 54,
+ .scale1 = 0,
+ .scale2 = 6000,
+ },
+};
+
+static const int apds9160_25ms_avail[][2] = {
+ { 3, 272000 },
+ { 1, 77000 },
+ { 0, 525000 },
+ { 0, 169000 },
+ { 0, 49000 },
+};
+
+static const int apds9160_50ms_avail[][2] = {
+ { 1, 639000 },
+ { 0, 538000 },
+ { 0, 263000 },
+ { 0, 84000 },
+ { 0, 25000 },
+};
+
+static const int apds9160_100ms_avail[][2] = {
+ { 0, 819000 },
+ { 0, 269000 },
+ { 0, 131000 },
+ { 0, 42000 },
+ { 0, 12000 },
+};
+
+static const int apds9160_200ms_avail[][2] = {
+ { 0, 409000 },
+ { 0, 135000 },
+ { 0, 66000 },
+ { 0, 21000 },
+ { 0, 6000 },
+};
+
+static const struct reg_field apds9160_reg_field_ls_en =
+ REG_FIELD(APDS9160_REG_CTRL, 1, 1);
+
+static const struct reg_field apds9160_reg_field_ps_en =
+ REG_FIELD(APDS9160_REG_CTRL, 0, 0);
+
+static const struct reg_field apds9160_reg_field_int_ps =
+ REG_FIELD(APDS9160_REG_INT_CFG, 0, 0);
+
+static const struct reg_field apds9160_reg_field_int_als =
+ REG_FIELD(APDS9160_REG_INT_CFG, 2, 2);
+
+static const struct reg_field apds9160_reg_field_ps_overflow =
+ REG_FIELD(APDS9160_REG_PS_DATA_MSB, 3, 3);
+
+static const struct reg_field apds9160_reg_field_als_rate =
+ REG_FIELD(APDS9160_REG_LS_MEAS_RATE, 0, 2);
+
+static const struct reg_field apds9160_reg_field_als_gain =
+ REG_FIELD(APDS9160_REG_LS_GAIN, 0, 2);
+
+static const struct reg_field apds9160_reg_field_ps_rate =
+ REG_FIELD(APDS9160_REG_PS_MEAS_RATE, 0, 2);
+
+static const struct reg_field apds9160_reg_field_als_res =
+ REG_FIELD(APDS9160_REG_LS_MEAS_RATE, 4, 6);
+
+static const struct reg_field apds9160_reg_field_ps_current =
+ REG_FIELD(APDS9160_REG_PS_LED, 0, 2);
+
+static const struct reg_field apds9160_reg_field_ps_gain =
+ REG_FIELD(APDS9160_REG_PS_MEAS_RATE, 6, 7);
+
+static const struct reg_field apds9160_reg_field_ps_resolution =
+ REG_FIELD(APDS9160_REG_PS_MEAS_RATE, 3, 4);
+
+struct apds9160_chip {
+ struct i2c_client *client;
+ struct regmap *regmap;
+
+ struct regmap_field *reg_enable_ps;
+ struct regmap_field *reg_enable_als;
+ struct regmap_field *reg_int_ps;
+ struct regmap_field *reg_int_als;
+ struct regmap_field *reg_ps_overflow;
+ struct regmap_field *reg_als_rate;
+ struct regmap_field *reg_als_resolution;
+ struct regmap_field *reg_ps_rate;
+ struct regmap_field *reg_als_gain;
+ struct regmap_field *reg_ps_current;
+ struct regmap_field *reg_ps_gain;
+ struct regmap_field *reg_ps_resolution;
+
+ struct mutex lock; /* protects state and config data */
+
+ /* State data */
+ int als_int;
+ int ps_int;
+
+ /* Configuration values */
+ int als_itime;
+ int als_hwgain;
+ int als_scale1;
+ int als_scale2;
+ int ps_rate;
+ int ps_cancellation_level;
+ int ps_current;
+ int ps_gain;
+};
+
+static int apds9160_set_ps_rate(struct apds9160_chip *data, int val)
+{
+ int idx;
+
+ for (idx = 0; idx < ARRAY_SIZE(apds9160_ps_rate_map); idx++) {
+ int ret;
+
+ if (apds9160_ps_rate_map[idx][0] != val)
+ continue;
+
+ ret = regmap_field_write(data->reg_ps_rate,
+ apds9160_ps_rate_map[idx][1]);
+ if (ret)
+ return ret;
+ data->ps_rate = val;
+
+ return ret;
+ }
+
+ return -EINVAL;
+}
+
+static int apds9160_set_ps_gain(struct apds9160_chip *data, int val)
+{
+ int idx;
+
+ for (idx = 0; idx < ARRAY_SIZE(apds9160_ps_gain_map); idx++) {
+ int ret;
+
+ if (apds9160_ps_gain_map[idx][0] != val)
+ continue;
+
+ ret = regmap_field_write(data->reg_ps_gain,
+ apds9160_ps_gain_map[idx][1]);
+ if (ret)
+ return ret;
+ data->ps_gain = val;
+
+ return ret;
+ }
+
+ return -EINVAL;
+}
+
+/*
+ * The PS intelligent cancellation level register allows
+ * for an on-chip substraction of the ADC count caused by
+ * unwanted reflected light from PS ADC output.
+ */
+static int apds9160_set_ps_cancellation_level(struct apds9160_chip *data,
+ int val)
+{
+ int ret;
+ __le16 buf;
+
+ if (val < 0 || val > 0xFFFF)
+ return -EINVAL;
+
+ buf = cpu_to_le16(val);
+ ret = regmap_bulk_write(data->regmap, APDS9160_REG_PS_CAN_LEVEL_DIG_LSB,
+ &buf, 2);
+ if (ret)
+ return ret;
+
+ data->ps_cancellation_level = val;
+
+ return ret;
+}
+
+/*
+ * This parameter determines the cancellation pulse duration
+ * in each of the PWM pulse. The cancellation is applied during the
+ * integration phase of the PS measurement.
+ * Duration is programmed in half clock cycles
+ * A duration value of 0 or 1 will not generate any cancellation pulse
+ */
+static int apds9160_set_ps_analog_cancellation(struct apds9160_chip *data,
+ int val)
+{
+ if (val < 0 || val > 63)
+ return -EINVAL;
+
+ return regmap_write(data->regmap, APDS9160_REG_PS_CAN_LEVEL_ANA_DUR,
+ val);
+}
+
+/*
+ * This parameter works in conjunction with the cancellation pulse duration
+ * The value determines the current used for crosstalk cancellation
+ * Coarse value is in steps of 60 nA
+ * Fine value is in steps of 2.4 nA
+ */
+static int apds9160_set_ps_cancellation_current(struct apds9160_chip *data,
+ int coarse_val,
+ int fine_val)
+{
+ int val;
+
+ if (coarse_val < 0 || coarse_val > 4)
+ return -EINVAL;
+
+ if (fine_val < 0 || fine_val > 15)
+ return -EINVAL;
+
+ /* Coarse value at B4:B5 and fine value at B0:B3 */
+ val = (coarse_val << 4) | fine_val;
+
+ return regmap_write(data->regmap, APDS9160_REG_PS_CAN_LEVEL_ANA_CURRENT,
+ val);
+}
+
+static int apds9160_ps_init_analog_cancellation(struct device *dev,
+ struct apds9160_chip *data)
+{
+ int ret, duration, picoamp, idx, coarse, fine;
+
+ ret = device_property_read_u32(dev,
+ "ps-cancellation-duration", &duration);
+ if (ret || duration == 0) {
+ /* Don't fail since this is not required */
+ return 0;
+ }
+
+ ret = device_property_read_u32(dev,
+ "ps-cancellation-current-picoamp", &picoamp);
+ if (ret)
+ return ret;
+
+ if (picoamp < 60000 || picoamp > 276000 || picoamp % 2400 != 0)
+ return dev_err_probe(dev, -EINVAL,
+ "Invalid cancellation current\n");
+
+ /* Compute required coarse and fine value from requested current */
+ fine = 0;
+ coarse = 0;
+ for (idx = 60000; idx < picoamp; idx += 2400) {
+ if (fine == 15) {
+ fine = 0;
+ coarse++;
+ idx += 21600;
+ } else {
+ fine++;
+ }
+ }
+
+ if (picoamp != idx)
+ dev_warn(dev,
+ "Invalid cancellation current %i, rounding to %i\n",
+ picoamp, idx);
+
+ ret = apds9160_set_ps_analog_cancellation(data, duration);
+ if (ret)
+ return ret;
+
+ return apds9160_set_ps_cancellation_current(data, coarse, fine);
+}
+
+static int apds9160_set_ps_current(struct apds9160_chip *data, int val)
+{
+ int idx;
+
+ for (idx = 0; idx < ARRAY_SIZE(apds9160_ps_led_current_map); idx++) {
+ int ret;
+
+ if (apds9160_ps_led_current_map[idx][0] != val)
+ continue;
+
+ ret = regmap_field_write(
+ data->reg_ps_current,
+ apds9160_ps_led_current_map[idx][1]);
+ if (ret)
+ return ret;
+ data->ps_current = val;
+
+ return ret;
+ }
+
+ return -EINVAL;
+}
+
+static int apds9160_set_als_gain(struct apds9160_chip *data, int gain)
+{
+ int idx;
+
+ for (idx = 0; idx < ARRAY_SIZE(apds9160_als_gain_map); idx++) {
+ int ret;
+
+ if (gain != apds9160_als_gain_map[idx][0])
+ continue;
+
+ ret = regmap_field_write(data->reg_als_gain,
+ apds9160_als_gain_map[idx][1]);
+ if (ret)
+ return ret;
+ data->als_hwgain = gain;
+
+ return ret;
+ }
+
+ return -EINVAL;
+}
+
+static int apds9160_set_als_scale(struct apds9160_chip *data, int val, int val2)
+{
+ int idx;
+
+ for (idx = 0; idx < ARRAY_SIZE(apds9160_als_scale_map); idx++) {
+ if (apds9160_als_scale_map[idx].itime == data->als_itime &&
+ apds9160_als_scale_map[idx].scale1 == val &&
+ apds9160_als_scale_map[idx].scale2 == val2) {
+ int ret = apds9160_set_als_gain(data,
+ apds9160_als_scale_map[idx].gain);
+ if (ret)
+ return ret;
+ data->als_scale1 = val;
+ data->als_scale2 = val2;
+
+ return ret;
+ }
+ }
+
+ return -EINVAL;
+}
+
+static int apds9160_set_als_resolution(struct apds9160_chip *data, int val)
+{
+ switch (val) {
+ case 25:
+ return regmap_field_write(data->reg_als_resolution,
+ APDS9160_CMD_LS_RESOLUTION_25MS);
+ case 50:
+ return regmap_field_write(data->reg_als_resolution,
+ APDS9160_CMD_LS_RESOLUTION_50MS);
+ case 200:
+ return regmap_field_write(data->reg_als_resolution,
+ APDS9160_CMD_LS_RESOLUTION_200MS);
+ default:
+ return regmap_field_write(data->reg_als_resolution,
+ APDS9160_CMD_LS_RESOLUTION_100MS);
+ }
+}
+
+static int apds9160_set_als_rate(struct apds9160_chip *data, int val)
+{
+ int idx;
+
+ for (idx = 0; idx < ARRAY_SIZE(apds9160_als_rate_map); idx++) {
+ if (apds9160_als_rate_map[idx][0] != val)
+ continue;
+
+ return regmap_field_write(data->reg_als_rate,
+ apds9160_als_rate_map[idx][1]);
+ }
+
+ return -EINVAL;
+}
+
+/*
+ * Setting the integration time ajusts resolution, rate, scale and gain
+ */
+static int apds9160_set_als_int_time(struct apds9160_chip *data, int val)
+{
+ int ret;
+ int idx;
+
+ ret = apds9160_set_als_rate(data, val);
+ if (ret)
+ return ret;
+
+ /* Match resolution register with rate */
+ ret = apds9160_set_als_resolution(data, val);
+ if (ret)
+ return ret;
+
+ data->als_itime = val;
+
+ /* Set the scale minimum gain */
+ for (idx = 0; idx < ARRAY_SIZE(apds9160_als_scale_map); idx++) {
+ if (data->als_itime != apds9160_als_scale_map[idx].itime)
+ continue;
+
+ return apds9160_set_als_scale(data,
+ apds9160_als_scale_map[idx].scale1,
+ apds9160_als_scale_map[idx].scale2);
+ }
+
+ return -EINVAL;
+}
+
+static int apds9160_read_avail(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ const int **vals, int *type, int *length,
+ long mask)
+{
+ struct apds9160_chip *data = iio_priv(indio_dev);
+
+ switch (mask) {
+ case IIO_CHAN_INFO_INT_TIME:
+ switch (chan->type) {
+ case IIO_LIGHT:
+ *length = ARRAY_SIZE(apds9160_als_rate_avail);
+ *vals = (const int *)apds9160_als_rate_avail;
+ *type = IIO_VAL_INT;
+
+ return IIO_AVAIL_LIST;
+ case IIO_PROXIMITY:
+ *length = ARRAY_SIZE(apds9160_ps_rate_avail);
+ *vals = (const int *)apds9160_ps_rate_avail;
+ *type = IIO_VAL_INT;
+
+ return IIO_AVAIL_LIST;
+ default:
+ return -EINVAL;
+ }
+ case IIO_CHAN_INFO_SCALE:
+ switch (chan->type) {
+ case IIO_PROXIMITY:
+ *length = ARRAY_SIZE(apds9160_ps_gain_avail);
+ *vals = (const int *)apds9160_ps_gain_avail;
+ *type = IIO_VAL_INT;
+
+ return IIO_AVAIL_LIST;
+ case IIO_LIGHT:
+ /* The available scales changes depending on itime */
+ switch (data->als_itime) {
+ case 25:
+ *length = ARRAY_SIZE(apds9160_25ms_avail) * 2;
+ *vals = (const int *)apds9160_25ms_avail;
+ *type = IIO_VAL_INT_PLUS_MICRO;
+
+ return IIO_AVAIL_LIST;
+ case 50:
+ *length = ARRAY_SIZE(apds9160_50ms_avail) * 2;
+ *vals = (const int *)apds9160_50ms_avail;
+ *type = IIO_VAL_INT_PLUS_MICRO;
+
+ return IIO_AVAIL_LIST;
+ case 100:
+ *length = ARRAY_SIZE(apds9160_100ms_avail) * 2;
+ *vals = (const int *)apds9160_100ms_avail;
+ *type = IIO_VAL_INT_PLUS_MICRO;
+
+ return IIO_AVAIL_LIST;
+ case 200:
+ *length = ARRAY_SIZE(apds9160_200ms_avail) * 2;
+ *vals = (const int *)apds9160_200ms_avail;
+ *type = IIO_VAL_INT_PLUS_MICRO;
+
+ return IIO_AVAIL_LIST;
+ default:
+ return -EINVAL;
+ }
+ default:
+ return -EINVAL;
+ }
+ case IIO_CHAN_INFO_RAW:
+ switch (chan->type) {
+ case IIO_CURRENT:
+ *length = ARRAY_SIZE(apds9160_ps_led_current_avail);
+ *vals = (const int *)apds9160_ps_led_current_avail;
+ *type = IIO_VAL_INT;
+
+ return IIO_AVAIL_LIST;
+ default:
+ return -EINVAL;
+ }
+
+ default:
+ return -EINVAL;
+ }
+}
+
+static int apds9160_write_raw_get_fmt(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ long mask)
+{
+ switch (mask) {
+ case IIO_CHAN_INFO_INT_TIME:
+ return IIO_VAL_INT;
+ case IIO_CHAN_INFO_CALIBBIAS:
+ return IIO_VAL_INT;
+ case IIO_CHAN_INFO_HARDWAREGAIN:
+ return IIO_VAL_INT;
+ case IIO_CHAN_INFO_RAW:
+ return IIO_VAL_INT;
+ case IIO_CHAN_INFO_SCALE:
+ return IIO_VAL_INT_PLUS_MICRO;
+ default:
+ return -EINVAL;
+ }
+}
+
+static int apds9160_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan, int *val,
+ int *val2, long mask)
+{
+ struct apds9160_chip *data = iio_priv(indio_dev);
+ int ret;
+
+ switch (mask) {
+ case IIO_CHAN_INFO_RAW:
+ switch (chan->type) {
+ case IIO_PROXIMITY: {
+ __le16 buf;
+
+ ret = regmap_bulk_read(data->regmap, chan->address,
+ &buf, 2);
+ if (ret)
+ return ret;
+ *val = le16_to_cpu(buf);
+ /* Remove overflow bits from result */
+ *val = FIELD_GET(APDS9160_PS_DATA_MASK, *val);
+
+ return IIO_VAL_INT;
+ }
+ case IIO_LIGHT:
+ case IIO_INTENSITY: {
+ u8 buf[3];
+
+ ret = regmap_bulk_read(data->regmap, chan->address,
+ &buf, 3);
+ if (ret)
+ return ret;
+ *val = get_unaligned_le24(buf);
+
+ return IIO_VAL_INT;
+ }
+ case IIO_CURRENT:
+ *val = data->ps_current;
+
+ return IIO_VAL_INT;
+ default:
+ return -EINVAL;
+ }
+ case IIO_CHAN_INFO_HARDWAREGAIN:
+ switch (chan->type) {
+ case IIO_LIGHT:
+ *val = data->als_hwgain;
+
+ return IIO_VAL_INT;
+ default:
+ return -EINVAL;
+ }
+ case IIO_CHAN_INFO_INT_TIME:
+ switch (chan->type) {
+ case IIO_PROXIMITY:
+ *val = data->ps_rate;
+
+ return IIO_VAL_INT;
+ case IIO_LIGHT:
+ *val = data->als_itime;
+
+ return IIO_VAL_INT;
+ default:
+ return -EINVAL;
+ }
+ case IIO_CHAN_INFO_CALIBBIAS:
+ switch (chan->type) {
+ case IIO_PROXIMITY:
+ *val = data->ps_cancellation_level;
+
+ return IIO_VAL_INT;
+ default:
+ return -EINVAL;
+ }
+ case IIO_CHAN_INFO_SCALE:
+ switch (chan->type) {
+ case IIO_PROXIMITY:
+ *val = data->ps_gain;
+
+ return IIO_VAL_INT;
+ case IIO_LIGHT:
+ *val = data->als_scale1;
+ *val2 = data->als_scale2;
+
+ return IIO_VAL_INT_PLUS_MICRO;
+ default:
+ return -EINVAL;
+ }
+ default:
+ return -EINVAL;
+ }
+};
+
+static int apds9160_write_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan, int val,
+ int val2, long mask)
+{
+ struct apds9160_chip *data = iio_priv(indio_dev);
+
+ guard(mutex)(&data->lock);
+
+ switch (mask) {
+ case IIO_CHAN_INFO_INT_TIME:
+ if (val2 != 0)
+ return -EINVAL;
+ switch (chan->type) {
+ case IIO_PROXIMITY:
+ return apds9160_set_ps_rate(data, val);
+ case IIO_LIGHT:
+ return apds9160_set_als_int_time(data, val);
+ default:
+ return -EINVAL;
+ }
+ case IIO_CHAN_INFO_SCALE:
+ switch (chan->type) {
+ case IIO_PROXIMITY:
+ return apds9160_set_ps_gain(data, val);
+ case IIO_LIGHT:
+ return apds9160_set_als_scale(data, val, val2);
+ default:
+ return -EINVAL;
+ }
+ case IIO_CHAN_INFO_CALIBBIAS:
+ if (val2 != 0)
+ return -EINVAL;
+ switch (chan->type) {
+ case IIO_PROXIMITY:
+ return apds9160_set_ps_cancellation_level(data, val);
+ default:
+ return -EINVAL;
+ }
+ case IIO_CHAN_INFO_RAW:
+ if (val2 != 0)
+ return -EINVAL;
+ switch (chan->type) {
+ case IIO_CURRENT:
+ return apds9160_set_ps_current(data, val);
+ default:
+ return -EINVAL;
+ }
+ default:
+ return -EINVAL;
+ }
+}
+
+static inline int apds9160_get_thres_reg(const struct iio_chan_spec *chan,
+ enum iio_event_direction dir, u8 *reg)
+{
+ switch (dir) {
+ case IIO_EV_DIR_RISING:
+ switch (chan->type) {
+ case IIO_PROXIMITY:
+ *reg = APDS9160_REG_PS_THRES_HI_LSB;
+ break;
+ case IIO_LIGHT:
+ *reg = APDS9160_REG_LS_THRES_UP_LSB;
+ break;
+ default:
+ return -EINVAL;
+ } break;
+ case IIO_EV_DIR_FALLING:
+ switch (chan->type) {
+ case IIO_PROXIMITY:
+ *reg = APDS9160_REG_PS_THRES_LO_LSB;
+ break;
+ case IIO_LIGHT:
+ *reg = APDS9160_REG_LS_THRES_LO_LSB;
+ break;
+ default:
+ return -EINVAL;
+ }
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int apds9160_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)
+{
+ u8 reg;
+ int ret;
+ struct apds9160_chip *data = iio_priv(indio_dev);
+
+ if (info != IIO_EV_INFO_VALUE)
+ return -EINVAL;
+
+ ret = apds9160_get_thres_reg(chan, dir, &reg);
+ if (ret < 0)
+ return ret;
+
+ switch (chan->type) {
+ case IIO_PROXIMITY: {
+ __le16 buf;
+
+ ret = regmap_bulk_read(data->regmap, reg, &buf, 2);
+ if (ret < 0)
+ return ret;
+ *val = le16_to_cpu(buf);
+ return IIO_VAL_INT;
+ }
+ case IIO_LIGHT: {
+ u8 buf[3];
+
+ ret = regmap_bulk_read(data->regmap, reg, &buf, 3);
+ if (ret < 0)
+ return ret;
+ *val = get_unaligned_le24(buf);
+ return IIO_VAL_INT;
+ }
+ default:
+ return -EINVAL;
+ }
+}
+
+static int apds9160_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)
+{
+ u8 reg;
+ int ret = 0;
+ struct apds9160_chip *data = iio_priv(indio_dev);
+
+ if (info != IIO_EV_INFO_VALUE)
+ return -EINVAL;
+
+ ret = apds9160_get_thres_reg(chan, dir, &reg);
+ if (ret < 0)
+ return ret;
+
+ switch (chan->type) {
+ case IIO_PROXIMITY: {
+ __le16 buf;
+
+ if (val < 0 || val > APDS9160_PS_THRES_MAX)
+ return -EINVAL;
+
+ buf = cpu_to_le16(val);
+ return regmap_bulk_write(data->regmap, reg, &buf, 2);
+ }
+ case IIO_LIGHT: {
+ u8 buf[3];
+
+ if (val < 0 || val > APDS9160_LS_THRES_MAX)
+ return -EINVAL;
+
+ put_unaligned_le24(val, buf);
+ return regmap_bulk_write(data->regmap, reg, &buf, 3);
+ }
+ default:
+ return -EINVAL;
+ }
+}
+
+static int apds9160_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 apds9160_chip *data = iio_priv(indio_dev);
+
+ switch (chan->type) {
+ case IIO_PROXIMITY:
+ return data->ps_int;
+ case IIO_LIGHT:
+ return data->als_int;
+ default:
+ return -EINVAL;
+ }
+}
+
+static int apds9160_write_event_config(struct iio_dev *indio_dev,
+ const struct iio_chan_spec *chan,
+ enum iio_event_type type,
+ enum iio_event_direction dir, bool state)
+{
+ struct apds9160_chip *data = iio_priv(indio_dev);
+ int ret;
+
+ switch (chan->type) {
+ case IIO_PROXIMITY:
+ ret = regmap_field_write(data->reg_int_ps, state);
+ if (ret)
+ return ret;
+ data->ps_int = state;
+
+ return 0;
+ case IIO_LIGHT:
+ ret = regmap_field_write(data->reg_int_als, state);
+ if (ret)
+ return ret;
+ data->als_int = state;
+
+ return 0;
+ default:
+ return -EINVAL;
+ }
+}
+
+static irqreturn_t apds9160_irq_handler(int irq, void *private)
+{
+ struct iio_dev *indio_dev = private;
+ struct apds9160_chip *data = iio_priv(indio_dev);
+ int ret, status;
+
+ /* Reading status register clears the interrupt flag */
+ ret = regmap_read(data->regmap, APDS9160_REG_SR, &status);
+ if (ret < 0) {
+ dev_err_ratelimited(&data->client->dev,
+ "irq status reg read failed\n");
+ return IRQ_HANDLED;
+ }
+
+ if ((status & APDS9160_SR_LS_INT) &&
+ (status & APDS9160_SR_LS_NEW_DATA) && data->als_int) {
+ iio_push_event(indio_dev,
+ IIO_UNMOD_EVENT_CODE(IIO_LIGHT, 0,
+ IIO_EV_TYPE_THRESH,
+ IIO_EV_DIR_EITHER),
+ iio_get_time_ns(indio_dev));
+ }
+
+ if ((status & APDS9160_SR_PS_INT) &&
+ (status & APDS9160_SR_PS_NEW_DATA) && data->ps_int) {
+ iio_push_event(indio_dev,
+ IIO_UNMOD_EVENT_CODE(IIO_PROXIMITY, 0,
+ IIO_EV_TYPE_THRESH,
+ IIO_EV_DIR_EITHER),
+ iio_get_time_ns(indio_dev));
+ }
+
+ return IRQ_HANDLED;
+}
+
+static int apds9160_detect(struct apds9160_chip *chip)
+{
+ struct i2c_client *client = chip->client;
+ int ret;
+ u32 val;
+
+ ret = regmap_read(chip->regmap, APDS9160_REG_ID, &val);
+ if (ret < 0) {
+ dev_err(&client->dev, "ID read failed\n");
+ return ret;
+ }
+
+ if (val != APDS9160_PART_ID_0)
+ dev_info(&client->dev, "Unknown part id %u\n", val);
+
+ return 0;
+}
+
+static void apds9160_disable(void *chip)
+{
+ struct apds9160_chip *data = chip;
+ int ret;
+
+ ret = regmap_field_write(data->reg_enable_als, 0);
+ if (ret)
+ return;
+
+ regmap_field_write(data->reg_enable_ps, 0);
+}
+
+static int apds9160_chip_init(struct apds9160_chip *chip)
+{
+ int ret;
+
+ /* Write default values to interrupt register */
+ ret = regmap_field_write(chip->reg_int_ps, 0);
+ chip->ps_int = 0;
+ if (ret)
+ return ret;
+
+ ret = regmap_field_write(chip->reg_int_als, 0);
+ chip->als_int = 0;
+ if (ret)
+ return ret;
+
+ /* Write default values to control register */
+ ret = regmap_field_write(chip->reg_enable_als, 1);
+ if (ret)
+ return ret;
+
+ ret = regmap_field_write(chip->reg_enable_ps, 1);
+ if (ret)
+ return ret;
+
+ /* Write other default values */
+ ret = regmap_field_write(chip->reg_ps_resolution,
+ APDS9160_DEFAULT_PS_RESOLUTION_11BITS);
+ if (ret)
+ return ret;
+
+ /* Write default values to configuration registers */
+ ret = apds9160_set_ps_current(chip, APDS9160_DEFAULT_PS_CURRENT);
+ if (ret)
+ return ret;
+
+ ret = apds9160_set_ps_rate(chip, APDS9160_DEFAULT_PS_RATE);
+ if (ret)
+ return ret;
+
+ ret = apds9160_set_als_int_time(chip, APDS9160_DEFAULT_LS_RATE);
+ if (ret)
+ return ret;
+
+ ret = apds9160_set_als_scale(chip,
+ apds9160_100ms_avail[0][0],
+ apds9160_100ms_avail[0][1]);
+ if (ret)
+ return ret;
+
+ ret = apds9160_set_ps_gain(chip, APDS9160_DEFAULT_PS_GAIN);
+ if (ret)
+ return ret;
+
+ ret = apds9160_set_ps_analog_cancellation(
+ chip, APDS9160_DEFAULT_PS_ANALOG_CANCELLATION);
+ if (ret)
+ return ret;
+
+ ret = apds9160_set_ps_cancellation_level(
+ chip, APDS9160_DEFAULT_PS_CANCELLATION_LEVEL);
+ if (ret)
+ return ret;
+
+ return devm_add_action_or_reset(&chip->client->dev, apds9160_disable,
+ chip);
+}
+
+static int apds9160_regfield_init(struct apds9160_chip *data)
+{
+ struct device *dev = &data->client->dev;
+ struct regmap *regmap = data->regmap;
+ struct regmap_field *tmp;
+
+ tmp = devm_regmap_field_alloc(dev, regmap, apds9160_reg_field_int_als);
+ if (IS_ERR(tmp))
+ return PTR_ERR(tmp);
+ data->reg_int_als = tmp;
+
+ tmp = devm_regmap_field_alloc(dev, regmap, apds9160_reg_field_int_ps);
+ if (IS_ERR(tmp))
+ return PTR_ERR(tmp);
+ data->reg_int_ps = tmp;
+
+ tmp = devm_regmap_field_alloc(dev, regmap, apds9160_reg_field_ls_en);
+ if (IS_ERR(tmp))
+ return PTR_ERR(tmp);
+ data->reg_enable_als = tmp;
+
+ tmp = devm_regmap_field_alloc(dev, regmap, apds9160_reg_field_ps_en);
+ if (IS_ERR(tmp))
+ return PTR_ERR(tmp);
+ data->reg_enable_ps = tmp;
+
+ tmp = devm_regmap_field_alloc(dev, regmap,
+ apds9160_reg_field_ps_overflow);
+ if (IS_ERR(tmp))
+ return PTR_ERR(tmp);
+ data->reg_ps_overflow = tmp;
+
+ tmp = devm_regmap_field_alloc(dev, regmap, apds9160_reg_field_als_rate);
+ if (IS_ERR(tmp))
+ return PTR_ERR(tmp);
+ data->reg_als_rate = tmp;
+
+ tmp = devm_regmap_field_alloc(dev, regmap, apds9160_reg_field_als_res);
+ if (IS_ERR(tmp))
+ return PTR_ERR(tmp);
+ data->reg_als_resolution = tmp;
+
+ tmp = devm_regmap_field_alloc(dev, regmap, apds9160_reg_field_ps_rate);
+ if (IS_ERR(tmp))
+ return PTR_ERR(tmp);
+ data->reg_ps_rate = tmp;
+
+ tmp = devm_regmap_field_alloc(dev, regmap, apds9160_reg_field_als_gain);
+ if (IS_ERR(tmp))
+ return PTR_ERR(tmp);
+ data->reg_als_gain = tmp;
+
+ tmp = devm_regmap_field_alloc(dev, regmap,
+ apds9160_reg_field_ps_current);
+ if (IS_ERR(tmp))
+ return PTR_ERR(tmp);
+ data->reg_ps_current = tmp;
+
+ tmp = devm_regmap_field_alloc(dev, regmap, apds9160_reg_field_ps_gain);
+ if (IS_ERR(tmp))
+ return PTR_ERR(tmp);
+ data->reg_ps_gain = tmp;
+
+ tmp = devm_regmap_field_alloc(dev, regmap,
+ apds9160_reg_field_ps_resolution);
+ if (IS_ERR(tmp))
+ return PTR_ERR(tmp);
+ data->reg_ps_resolution = tmp;
+
+ return 0;
+}
+
+static const struct iio_info apds9160_info = {
+ .read_avail = apds9160_read_avail,
+ .read_raw = apds9160_read_raw,
+ .write_raw = apds9160_write_raw,
+ .write_raw_get_fmt = apds9160_write_raw_get_fmt,
+ .read_event_value = apds9160_read_event,
+ .write_event_value = apds9160_write_event,
+ .read_event_config = apds9160_read_event_config,
+ .write_event_config = apds9160_write_event_config,
+};
+
+static const struct iio_info apds9160_info_no_events = {
+ .read_avail = apds9160_read_avail,
+ .read_raw = apds9160_read_raw,
+ .write_raw = apds9160_write_raw,
+ .write_raw_get_fmt = apds9160_write_raw_get_fmt,
+};
+
+static int apds9160_probe(struct i2c_client *client)
+{
+ struct device *dev = &client->dev;
+ struct apds9160_chip *chip;
+ struct iio_dev *indio_dev;
+ int ret;
+
+ indio_dev = devm_iio_device_alloc(dev, sizeof(*chip));
+ if (!indio_dev)
+ return -ENOMEM;
+
+ ret = devm_regulator_get_enable(dev, "vdd");
+ if (ret)
+ return dev_err_probe(dev, ret, "Failed to enable vdd supply\n");
+
+ indio_dev->name = "apds9160";
+ indio_dev->modes = INDIO_DIRECT_MODE;
+
+ chip = iio_priv(indio_dev);
+ chip->client = client;
+ chip->regmap = devm_regmap_init_i2c(client, &apds9160_regmap_config);
+ if (IS_ERR(chip->regmap))
+ return dev_err_probe(dev, PTR_ERR(chip->regmap),
+ "regmap initialization failed.\n");
+
+ chip->client = client;
+ mutex_init(&chip->lock);
+
+ ret = apds9160_detect(chip);
+ if (ret < 0)
+ return dev_err_probe(dev, ret, "apds9160 not found\n");
+
+ ret = apds9160_regfield_init(chip);
+ if (ret)
+ return ret;
+
+ ret = apds9160_chip_init(chip);
+ if (ret)
+ return ret;
+
+ ret = apds9160_ps_init_analog_cancellation(dev, chip);
+ if (ret)
+ return ret;
+
+ if (client->irq > 0) {
+ indio_dev->info = &apds9160_info;
+ indio_dev->channels = apds9160_channels;
+ indio_dev->num_channels = ARRAY_SIZE(apds9160_channels);
+ ret = devm_request_threaded_irq(dev, client->irq, NULL,
+ apds9160_irq_handler,
+ IRQF_ONESHOT, "apds9160_event",
+ indio_dev);
+ if (ret) {
+ return dev_err_probe(dev, ret,
+ "request irq (%d) failed\n",
+ client->irq);
+ }
+ } else {
+ indio_dev->info = &apds9160_info_no_events;
+ indio_dev->channels = apds9160_channels_without_events;
+ indio_dev->num_channels =
+ ARRAY_SIZE(apds9160_channels_without_events);
+ }
+
+ ret = devm_iio_device_register(dev, indio_dev);
+ if (ret)
+ return dev_err_probe(dev, ret,
+ "failed iio device registration\n");
+
+ return ret;
+}
+
+static const struct of_device_id apds9160_of_match[] = {
+ { .compatible = "brcm,apds9160" },
+ { }
+};
+MODULE_DEVICE_TABLE(of, apds9160_of_match);
+
+static const struct i2c_device_id apds9160_id[] = {
+ { "apds9160", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, apds9160_id);
+
+static struct i2c_driver apds9160_driver = {
+ .driver = {
+ .name = "apds9160",
+ .of_match_table = apds9160_of_match,
+ },
+ .probe = apds9160_probe,
+ .id_table = apds9160_id,
+};
+module_i2c_driver(apds9160_driver);
+
+MODULE_DESCRIPTION("APDS9160 combined ALS and proximity sensor");
+MODULE_AUTHOR("Mikael Gonella-Bolduc <m.gonella.bolduc@gmail.com>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/iio/light/apds9300.c b/drivers/iio/light/apds9300.c
index 0f978b30a232..938d76f7e312 100644
--- a/drivers/iio/light/apds9300.c
+++ b/drivers/iio/light/apds9300.c
@@ -46,10 +46,10 @@
struct apds9300_data {
struct i2c_client *client;
struct mutex mutex;
- int power_state;
+ bool power_state;
int thresh_low;
int thresh_hi;
- int intr_en;
+ bool intr_en;
};
/* Lux calculation */
@@ -148,7 +148,7 @@ static int apds9300_set_thresh_hi(struct apds9300_data *data, int value)
return 0;
}
-static int apds9300_set_intr_state(struct apds9300_data *data, int state)
+static int apds9300_set_intr_state(struct apds9300_data *data, bool state)
{
int ret;
u8 cmd;
@@ -169,7 +169,7 @@ static int apds9300_set_intr_state(struct apds9300_data *data, int state)
return 0;
}
-static int apds9300_set_power_state(struct apds9300_data *data, int state)
+static int apds9300_set_power_state(struct apds9300_data *data, bool state)
{
int ret;
u8 cmd;
@@ -221,7 +221,7 @@ static int apds9300_chip_init(struct apds9300_data *data)
* Disable interrupt to ensure thai it is doesn't enable
* i.e. after device soft reset
*/
- ret = apds9300_set_intr_state(data, 0);
+ ret = apds9300_set_intr_state(data, false);
if (ret < 0)
goto err;
@@ -321,7 +321,7 @@ static int apds9300_read_interrupt_config(struct iio_dev *indio_dev,
static int apds9300_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)
+ enum iio_event_direction dir, bool state)
{
struct apds9300_data *data = iio_priv(indio_dev);
int ret;
@@ -459,8 +459,8 @@ static void apds9300_remove(struct i2c_client *client)
iio_device_unregister(indio_dev);
/* Ensure that power off and interrupts are disabled */
- apds9300_set_intr_state(data, 0);
- apds9300_set_power_state(data, 0);
+ apds9300_set_intr_state(data, false);
+ apds9300_set_power_state(data, false);
}
static int apds9300_suspend(struct device *dev)
@@ -470,7 +470,7 @@ static int apds9300_suspend(struct device *dev)
int ret;
mutex_lock(&data->mutex);
- ret = apds9300_set_power_state(data, 0);
+ ret = apds9300_set_power_state(data, false);
mutex_unlock(&data->mutex);
return ret;
@@ -483,7 +483,7 @@ static int apds9300_resume(struct device *dev)
int ret;
mutex_lock(&data->mutex);
- ret = apds9300_set_power_state(data, 1);
+ ret = apds9300_set_power_state(data, true);
mutex_unlock(&data->mutex);
return ret;
@@ -493,7 +493,7 @@ static DEFINE_SIMPLE_DEV_PM_OPS(apds9300_pm_ops, apds9300_suspend,
apds9300_resume);
static const struct i2c_device_id apds9300_id[] = {
- { APDS9300_DRV_NAME, 0 },
+ { APDS9300_DRV_NAME },
{ }
};
diff --git a/drivers/iio/light/apds9306.c b/drivers/iio/light/apds9306.c
new file mode 100644
index 000000000000..e9b237de180a
--- /dev/null
+++ b/drivers/iio/light/apds9306.c
@@ -0,0 +1,1357 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * APDS-9306/APDS-9306-065 Ambient Light Sensor
+ * I2C Address: 0x52
+ * Datasheet: https://docs.broadcom.com/doc/AV02-4755EN
+ *
+ * Copyright (C) 2024 Subhajit Ghosh <subhajit.ghosh@tweaklogic.com>
+ */
+
+#include <linux/bits.h>
+#include <linux/cleanup.h>
+#include <linux/delay.h>
+#include <linux/err.h>
+#include <linux/i2c.h>
+#include <linux/interrupt.h>
+#include <linux/minmax.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/pm.h>
+#include <linux/pm_runtime.h>
+#include <linux/regmap.h>
+#include <linux/regulator/consumer.h>
+#include <linux/types.h>
+#include <linux/units.h>
+
+#include <linux/iio/iio.h>
+#include <linux/iio/iio-gts-helper.h>
+#include <linux/iio/events.h>
+#include <linux/iio/sysfs.h>
+
+#include <linux/unaligned.h>
+
+#define APDS9306_MAIN_CTRL_REG 0x00
+#define APDS9306_ALS_MEAS_RATE_REG 0x04
+#define APDS9306_ALS_GAIN_REG 0x05
+#define APDS9306_PART_ID_REG 0x06
+#define APDS9306_MAIN_STATUS_REG 0x07
+#define APDS9306_CLEAR_DATA_0_REG 0x0A
+#define APDS9306_CLEAR_DATA_1_REG 0x0B
+#define APDS9306_CLEAR_DATA_2_REG 0x0C
+#define APDS9306_ALS_DATA_0_REG 0x0D
+#define APDS9306_ALS_DATA_1_REG 0x0E
+#define APDS9306_ALS_DATA_2_REG 0x0F
+#define APDS9306_INT_CFG_REG 0x19
+#define APDS9306_INT_PERSISTENCE_REG 0x1A
+#define APDS9306_ALS_THRES_UP_0_REG 0x21
+#define APDS9306_ALS_THRES_UP_1_REG 0x22
+#define APDS9306_ALS_THRES_UP_2_REG 0x23
+#define APDS9306_ALS_THRES_LOW_0_REG 0x24
+#define APDS9306_ALS_THRES_LOW_1_REG 0x25
+#define APDS9306_ALS_THRES_LOW_2_REG 0x26
+#define APDS9306_ALS_THRES_VAR_REG 0x27
+
+#define APDS9306_ALS_INT_STAT_MASK BIT(4)
+#define APDS9306_ALS_DATA_STAT_MASK BIT(3)
+
+#define APDS9306_ALS_THRES_VAL_MAX (BIT(20) - 1)
+#define APDS9306_ALS_THRES_VAR_NUM_VALS 8
+#define APDS9306_ALS_PERSIST_NUM_VALS 16
+#define APDS9306_ALS_READ_DATA_DELAY_US (20 * USEC_PER_MSEC)
+#define APDS9306_NUM_REPEAT_RATES 7
+#define APDS9306_INT_SRC_CLEAR 0
+#define APDS9306_INT_SRC_ALS 1
+#define APDS9306_SAMP_FREQ_10HZ 0
+
+/**
+ * struct part_id_gts_multiplier - Part no. and corresponding gts multiplier
+ *
+ * GTS (Gain Time Scale) are helper functions for Light sensors which along
+ * with hardware gains also has gains associated with Integration times.
+ *
+ * There are two variants of the device with slightly different characteristics,
+ * they have same ADC count for different Lux levels as mentioned in the
+ * datasheet. This multiplier array is used to store the derived Lux per count
+ * value for the two variants to be used by the GTS helper functions.
+ *
+ * @part_id: Part ID of the device
+ * @max_scale_int: Multiplier for iio_init_iio_gts()
+ * @max_scale_nano: Multiplier for iio_init_iio_gts()
+ */
+struct part_id_gts_multiplier {
+ int part_id;
+ int max_scale_int;
+ int max_scale_nano;
+};
+
+/*
+ * As per the datasheet, at HW Gain = 3x, Integration time 100mS (32x),
+ * typical 2000 ADC counts are observed for 49.8 uW per sq cm (340.134 lux)
+ * for apds9306 and 43 uW per sq cm (293.69 lux) for apds9306-065.
+ * Assuming lux per count is linear across all integration time ranges.
+ *
+ * Lux = (raw + offset) * scale; offset can be any value by userspace.
+ * HG = Hardware Gain; ITG = Gain by changing integration time.
+ * Scale table by IIO GTS Helpers = (1 / HG) * (1 / ITG) * Multiplier.
+ *
+ * The Lux values provided in the datasheet are at ITG=32x and HG=3x,
+ * at typical 2000 count for both variants of the device.
+ *
+ * Lux per ADC count at 3x and 32x for apds9306 = 340.134 / 2000
+ * Lux per ADC count at 3x and 32x for apds9306-065 = 293.69 / 2000
+ *
+ * The Multiplier for the scale table provided to userspace:
+ * IIO GTS scale Multiplier for apds9306 = (340.134 / 2000) * 32 * 3 = 16.326432
+ * and for apds9306-065 = (293.69 / 2000) * 32 * 3 = 14.09712
+ */
+static const struct part_id_gts_multiplier apds9306_gts_mul[] = {
+ {
+ .part_id = 0xB1,
+ .max_scale_int = 16,
+ .max_scale_nano = 326432000,
+ }, {
+ .part_id = 0xB3,
+ .max_scale_int = 14,
+ .max_scale_nano = 97120000,
+ },
+};
+
+static const int apds9306_repeat_rate_freq[APDS9306_NUM_REPEAT_RATES][2] = {
+ { 40, 0 },
+ { 20, 0 },
+ { 10, 0 },
+ { 5, 0 },
+ { 2, 0 },
+ { 1, 0 },
+ { 0, 500000 },
+};
+
+static const int apds9306_repeat_rate_period[APDS9306_NUM_REPEAT_RATES] = {
+ 25000, 50000, 100000, 200000, 500000, 1000000, 2000000,
+};
+
+/**
+ * struct apds9306_regfields - apds9306 regmap fields definitions
+ *
+ * @sw_reset: Software reset regfield
+ * @en: Enable regfield
+ * @intg_time: Resolution regfield
+ * @repeat_rate: Measurement Rate regfield
+ * @gain: Hardware gain regfield
+ * @int_src: Interrupt channel regfield
+ * @int_thresh_var_en: Interrupt variance threshold regfield
+ * @int_en: Interrupt enable regfield
+ * @int_persist_val: Interrupt persistence regfield
+ * @int_thresh_var_val: Interrupt threshold variance value regfield
+ */
+struct apds9306_regfields {
+ struct regmap_field *sw_reset;
+ struct regmap_field *en;
+ struct regmap_field *intg_time;
+ struct regmap_field *repeat_rate;
+ struct regmap_field *gain;
+ struct regmap_field *int_src;
+ struct regmap_field *int_thresh_var_en;
+ struct regmap_field *int_en;
+ struct regmap_field *int_persist_val;
+ struct regmap_field *int_thresh_var_val;
+};
+
+/**
+ * struct apds9306_data - apds9306 private data and registers definitions
+ *
+ * @dev: Pointer to the device structure
+ * @gts: IIO Gain Time Scale structure
+ * @mutex: Lock for protecting adc reads, device settings changes where
+ * some calculations are required before or after setting or
+ * getting the raw settings values from regmap writes or reads
+ * respectively.
+ * @regmap: Regmap structure pointer
+ * @rf: Regmap register fields structure
+ * @nlux_per_count: Nano lux per ADC count for a particular model
+ * @read_data_available: Flag set by IRQ handler for ADC data available
+ */
+struct apds9306_data {
+ struct device *dev;
+ struct iio_gts gts;
+
+ struct mutex mutex;
+
+ struct regmap *regmap;
+ struct apds9306_regfields rf;
+
+ int nlux_per_count;
+ int read_data_available;
+};
+
+/*
+ * Available scales with gain 1x - 18x, timings 3.125, 25, 50, 100, 200, 400 ms
+ * Time impacts to gain: 1x, 8x, 16x, 32x, 64x, 128x
+ */
+#define APDS9306_GSEL_1X 0x00
+#define APDS9306_GSEL_3X 0x01
+#define APDS9306_GSEL_6X 0x02
+#define APDS9306_GSEL_9X 0x03
+#define APDS9306_GSEL_18X 0x04
+
+static const struct iio_gain_sel_pair apds9306_gains[] = {
+ GAIN_SCALE_GAIN(1, APDS9306_GSEL_1X),
+ GAIN_SCALE_GAIN(3, APDS9306_GSEL_3X),
+ GAIN_SCALE_GAIN(6, APDS9306_GSEL_6X),
+ GAIN_SCALE_GAIN(9, APDS9306_GSEL_9X),
+ GAIN_SCALE_GAIN(18, APDS9306_GSEL_18X),
+};
+
+#define APDS9306_MEAS_MODE_400MS 0x00
+#define APDS9306_MEAS_MODE_200MS 0x01
+#define APDS9306_MEAS_MODE_100MS 0x02
+#define APDS9306_MEAS_MODE_50MS 0x03
+#define APDS9306_MEAS_MODE_25MS 0x04
+#define APDS9306_MEAS_MODE_3125US 0x05
+
+static const struct iio_itime_sel_mul apds9306_itimes[] = {
+ GAIN_SCALE_ITIME_US(400000, APDS9306_MEAS_MODE_400MS, BIT(7)),
+ GAIN_SCALE_ITIME_US(200000, APDS9306_MEAS_MODE_200MS, BIT(6)),
+ GAIN_SCALE_ITIME_US(100000, APDS9306_MEAS_MODE_100MS, BIT(5)),
+ GAIN_SCALE_ITIME_US(50000, APDS9306_MEAS_MODE_50MS, BIT(4)),
+ GAIN_SCALE_ITIME_US(25000, APDS9306_MEAS_MODE_25MS, BIT(3)),
+ GAIN_SCALE_ITIME_US(3125, APDS9306_MEAS_MODE_3125US, BIT(0)),
+};
+
+static const struct iio_event_spec apds9306_event_spec[] = {
+ {
+ .type = IIO_EV_TYPE_THRESH,
+ .dir = IIO_EV_DIR_RISING,
+ .mask_shared_by_all = BIT(IIO_EV_INFO_VALUE),
+ }, {
+ .type = IIO_EV_TYPE_THRESH,
+ .dir = IIO_EV_DIR_FALLING,
+ .mask_shared_by_all = BIT(IIO_EV_INFO_VALUE),
+ }, {
+ .type = IIO_EV_TYPE_THRESH,
+ .dir = IIO_EV_DIR_EITHER,
+ .mask_shared_by_all = BIT(IIO_EV_INFO_PERIOD),
+ .mask_separate = BIT(IIO_EV_INFO_ENABLE),
+ }, {
+ .type = IIO_EV_TYPE_THRESH_ADAPTIVE,
+ .mask_shared_by_all = BIT(IIO_EV_INFO_VALUE) |
+ BIT(IIO_EV_INFO_ENABLE),
+ },
+};
+
+static const struct iio_chan_spec apds9306_channels_with_events[] = {
+ {
+ .type = IIO_LIGHT,
+ .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_INT_TIME) |
+ BIT(IIO_CHAN_INFO_SAMP_FREQ),
+ .info_mask_shared_by_all_available = BIT(IIO_CHAN_INFO_INT_TIME) |
+ BIT(IIO_CHAN_INFO_SAMP_FREQ),
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
+ BIT(IIO_CHAN_INFO_SCALE),
+ .info_mask_separate_available = BIT(IIO_CHAN_INFO_SCALE),
+ .event_spec = apds9306_event_spec,
+ .num_event_specs = ARRAY_SIZE(apds9306_event_spec),
+ }, {
+ .type = IIO_INTENSITY,
+ .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_INT_TIME) |
+ BIT(IIO_CHAN_INFO_SAMP_FREQ),
+ .info_mask_shared_by_all_available = BIT(IIO_CHAN_INFO_INT_TIME) |
+ BIT(IIO_CHAN_INFO_SAMP_FREQ),
+ .channel2 = IIO_MOD_LIGHT_CLEAR,
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
+ .modified = 1,
+ .event_spec = apds9306_event_spec,
+ .num_event_specs = ARRAY_SIZE(apds9306_event_spec),
+ },
+};
+
+static const struct iio_chan_spec apds9306_channels_without_events[] = {
+ {
+ .type = IIO_LIGHT,
+ .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_INT_TIME) |
+ BIT(IIO_CHAN_INFO_SAMP_FREQ),
+ .info_mask_shared_by_all_available = BIT(IIO_CHAN_INFO_INT_TIME) |
+ BIT(IIO_CHAN_INFO_SAMP_FREQ),
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
+ BIT(IIO_CHAN_INFO_SCALE),
+ .info_mask_separate_available = BIT(IIO_CHAN_INFO_SCALE),
+ }, {
+ .type = IIO_INTENSITY,
+ .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_INT_TIME) |
+ BIT(IIO_CHAN_INFO_SAMP_FREQ),
+ .info_mask_shared_by_all_available = BIT(IIO_CHAN_INFO_INT_TIME) |
+ BIT(IIO_CHAN_INFO_SAMP_FREQ),
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
+ .modified = 1,
+ .channel2 = IIO_MOD_LIGHT_CLEAR,
+ },
+};
+
+/* INT_PERSISTENCE available */
+static IIO_CONST_ATTR(thresh_either_period_available, "[0 1 15]");
+
+/* ALS_THRESH_VAR available */
+static IIO_CONST_ATTR(thresh_adaptive_either_values_available, "[0 1 7]");
+
+static struct attribute *apds9306_event_attributes[] = {
+ &iio_const_attr_thresh_either_period_available.dev_attr.attr,
+ &iio_const_attr_thresh_adaptive_either_values_available.dev_attr.attr,
+ NULL
+};
+
+static const struct attribute_group apds9306_event_attr_group = {
+ .attrs = apds9306_event_attributes,
+};
+
+static const struct regmap_range apds9306_readable_ranges[] = {
+ regmap_reg_range(APDS9306_MAIN_CTRL_REG, APDS9306_ALS_THRES_VAR_REG)
+};
+
+static const struct regmap_range apds9306_writable_ranges[] = {
+ regmap_reg_range(APDS9306_MAIN_CTRL_REG, APDS9306_ALS_GAIN_REG),
+ regmap_reg_range(APDS9306_INT_CFG_REG, APDS9306_ALS_THRES_VAR_REG)
+};
+
+static const struct regmap_range apds9306_volatile_ranges[] = {
+ regmap_reg_range(APDS9306_MAIN_STATUS_REG, APDS9306_MAIN_STATUS_REG),
+ regmap_reg_range(APDS9306_CLEAR_DATA_0_REG, APDS9306_ALS_DATA_2_REG)
+};
+
+static const struct regmap_range apds9306_precious_ranges[] = {
+ regmap_reg_range(APDS9306_MAIN_STATUS_REG, APDS9306_MAIN_STATUS_REG)
+};
+
+static const struct regmap_access_table apds9306_readable_table = {
+ .yes_ranges = apds9306_readable_ranges,
+ .n_yes_ranges = ARRAY_SIZE(apds9306_readable_ranges)
+};
+
+static const struct regmap_access_table apds9306_writable_table = {
+ .yes_ranges = apds9306_writable_ranges,
+ .n_yes_ranges = ARRAY_SIZE(apds9306_writable_ranges)
+};
+
+static const struct regmap_access_table apds9306_volatile_table = {
+ .yes_ranges = apds9306_volatile_ranges,
+ .n_yes_ranges = ARRAY_SIZE(apds9306_volatile_ranges)
+};
+
+static const struct regmap_access_table apds9306_precious_table = {
+ .yes_ranges = apds9306_precious_ranges,
+ .n_yes_ranges = ARRAY_SIZE(apds9306_precious_ranges)
+};
+
+static const struct regmap_config apds9306_regmap = {
+ .name = "apds9306_regmap",
+ .reg_bits = 8,
+ .val_bits = 8,
+ .rd_table = &apds9306_readable_table,
+ .wr_table = &apds9306_writable_table,
+ .volatile_table = &apds9306_volatile_table,
+ .precious_table = &apds9306_precious_table,
+ .max_register = APDS9306_ALS_THRES_VAR_REG,
+ .cache_type = REGCACHE_RBTREE,
+};
+
+static const struct reg_field apds9306_rf_sw_reset =
+ REG_FIELD(APDS9306_MAIN_CTRL_REG, 4, 4);
+
+static const struct reg_field apds9306_rf_en =
+ REG_FIELD(APDS9306_MAIN_CTRL_REG, 1, 1);
+
+static const struct reg_field apds9306_rf_intg_time =
+ REG_FIELD(APDS9306_ALS_MEAS_RATE_REG, 4, 6);
+
+static const struct reg_field apds9306_rf_repeat_rate =
+ REG_FIELD(APDS9306_ALS_MEAS_RATE_REG, 0, 2);
+
+static const struct reg_field apds9306_rf_gain =
+ REG_FIELD(APDS9306_ALS_GAIN_REG, 0, 2);
+
+static const struct reg_field apds9306_rf_int_src =
+ REG_FIELD(APDS9306_INT_CFG_REG, 4, 5);
+
+static const struct reg_field apds9306_rf_int_thresh_var_en =
+ REG_FIELD(APDS9306_INT_CFG_REG, 3, 3);
+
+static const struct reg_field apds9306_rf_int_en =
+ REG_FIELD(APDS9306_INT_CFG_REG, 2, 2);
+
+static const struct reg_field apds9306_rf_int_persist_val =
+ REG_FIELD(APDS9306_INT_PERSISTENCE_REG, 4, 7);
+
+static const struct reg_field apds9306_rf_int_thresh_var_val =
+ REG_FIELD(APDS9306_ALS_THRES_VAR_REG, 0, 2);
+
+static int apds9306_regfield_init(struct apds9306_data *data)
+{
+ struct device *dev = data->dev;
+ struct regmap *regmap = data->regmap;
+ struct regmap_field *tmp;
+ struct apds9306_regfields *rf = &data->rf;
+
+ tmp = devm_regmap_field_alloc(dev, regmap, apds9306_rf_sw_reset);
+ if (IS_ERR(tmp))
+ return PTR_ERR(tmp);
+ rf->sw_reset = tmp;
+
+ tmp = devm_regmap_field_alloc(dev, regmap, apds9306_rf_en);
+ if (IS_ERR(tmp))
+ return PTR_ERR(tmp);
+ rf->en = tmp;
+
+ tmp = devm_regmap_field_alloc(dev, regmap, apds9306_rf_intg_time);
+ if (IS_ERR(tmp))
+ return PTR_ERR(tmp);
+ rf->intg_time = tmp;
+
+ tmp = devm_regmap_field_alloc(dev, regmap, apds9306_rf_repeat_rate);
+ if (IS_ERR(tmp))
+ return PTR_ERR(tmp);
+ rf->repeat_rate = tmp;
+
+ tmp = devm_regmap_field_alloc(dev, regmap, apds9306_rf_gain);
+ if (IS_ERR(tmp))
+ return PTR_ERR(tmp);
+ rf->gain = tmp;
+
+ tmp = devm_regmap_field_alloc(dev, regmap, apds9306_rf_int_src);
+ if (IS_ERR(tmp))
+ return PTR_ERR(tmp);
+ rf->int_src = tmp;
+
+ tmp = devm_regmap_field_alloc(dev, regmap, apds9306_rf_int_thresh_var_en);
+ if (IS_ERR(tmp))
+ return PTR_ERR(tmp);
+ rf->int_thresh_var_en = tmp;
+
+ tmp = devm_regmap_field_alloc(dev, regmap, apds9306_rf_int_en);
+ if (IS_ERR(tmp))
+ return PTR_ERR(tmp);
+ rf->int_en = tmp;
+
+ tmp = devm_regmap_field_alloc(dev, regmap, apds9306_rf_int_persist_val);
+ if (IS_ERR(tmp))
+ return PTR_ERR(tmp);
+ rf->int_persist_val = tmp;
+
+ tmp = devm_regmap_field_alloc(dev, regmap, apds9306_rf_int_thresh_var_val);
+ if (IS_ERR(tmp))
+ return PTR_ERR(tmp);
+ rf->int_thresh_var_val = tmp;
+
+ return 0;
+}
+
+static int apds9306_power_state(struct apds9306_data *data, bool state)
+{
+ struct apds9306_regfields *rf = &data->rf;
+ int ret;
+
+ /* Reset not included as it causes ugly I2C bus error */
+ if (state) {
+ ret = regmap_field_write(rf->en, 1);
+ if (ret)
+ return ret;
+ /* 5ms wake up time */
+ fsleep(5000);
+ return 0;
+ }
+
+ return regmap_field_write(rf->en, 0);
+}
+
+static int apds9306_read_data(struct apds9306_data *data, int *val, int reg)
+{
+ struct device *dev = data->dev;
+ struct iio_dev *indio_dev = dev_to_iio_dev(dev);
+ struct apds9306_regfields *rf = &data->rf;
+ u64 ev_code;
+ int ret, delay, intg_time, intg_time_idx, repeat_rate_idx, int_src;
+ int status = 0;
+ u8 buff[3];
+
+ ret = pm_runtime_resume_and_get(data->dev);
+ if (ret)
+ return ret;
+
+ ret = regmap_field_read(rf->intg_time, &intg_time_idx);
+ if (ret)
+ return ret;
+
+ ret = regmap_field_read(rf->repeat_rate, &repeat_rate_idx);
+ if (ret)
+ return ret;
+
+ ret = regmap_field_read(rf->int_src, &int_src);
+ if (ret)
+ return ret;
+
+ intg_time = iio_gts_find_int_time_by_sel(&data->gts, intg_time_idx);
+ if (intg_time < 0)
+ return intg_time;
+
+ /* Whichever is greater - integration time period or sampling period. */
+ delay = max(intg_time, apds9306_repeat_rate_period[repeat_rate_idx]);
+
+ /*
+ * Clear stale data flag that might have been set by the interrupt
+ * handler if it got data available flag set in the status reg.
+ */
+ data->read_data_available = 0;
+
+ /*
+ * If this function runs parallel with the interrupt handler, either
+ * this reads and clears the status registers or the interrupt handler
+ * does. The interrupt handler sets a flag for read data available
+ * in our private structure which we read here.
+ */
+ ret = regmap_read_poll_timeout(data->regmap, APDS9306_MAIN_STATUS_REG,
+ status, data->read_data_available ||
+ (status & (APDS9306_ALS_DATA_STAT_MASK |
+ APDS9306_ALS_INT_STAT_MASK)),
+ APDS9306_ALS_READ_DATA_DELAY_US, delay * 2);
+ if (ret)
+ return ret;
+
+ /* If we reach here before the interrupt handler we push an event */
+ if ((status & APDS9306_ALS_INT_STAT_MASK)) {
+ if (int_src == APDS9306_INT_SRC_ALS)
+ ev_code = IIO_UNMOD_EVENT_CODE(IIO_LIGHT, 0,
+ IIO_EV_TYPE_THRESH,
+ IIO_EV_DIR_EITHER);
+ else
+ ev_code = IIO_MOD_EVENT_CODE(IIO_INTENSITY, 0,
+ IIO_MOD_LIGHT_CLEAR,
+ IIO_EV_TYPE_THRESH,
+ IIO_EV_DIR_EITHER);
+
+ iio_push_event(indio_dev, ev_code, iio_get_time_ns(indio_dev));
+ }
+
+ ret = regmap_bulk_read(data->regmap, reg, buff, sizeof(buff));
+ if (ret) {
+ dev_err_ratelimited(dev, "read data failed\n");
+ return ret;
+ }
+
+ *val = get_unaligned_le24(&buff);
+
+ pm_runtime_mark_last_busy(data->dev);
+ pm_runtime_put_autosuspend(data->dev);
+
+ return 0;
+}
+
+static int apds9306_intg_time_get(struct apds9306_data *data, int *val2)
+{
+ struct apds9306_regfields *rf = &data->rf;
+ int ret, intg_time_idx;
+
+ ret = regmap_field_read(rf->intg_time, &intg_time_idx);
+ if (ret)
+ return ret;
+
+ ret = iio_gts_find_int_time_by_sel(&data->gts, intg_time_idx);
+ if (ret < 0)
+ return ret;
+
+ *val2 = ret;
+
+ return 0;
+}
+
+static int apds9306_intg_time_set(struct apds9306_data *data, int val2)
+{
+ struct device *dev = data->dev;
+ struct apds9306_regfields *rf = &data->rf;
+ int ret, intg_old, gain_old, gain_new, gain_new_closest, intg_time_idx;
+ int gain_idx;
+ bool ok;
+
+ if (!iio_gts_valid_time(&data->gts, val2)) {
+ dev_err_ratelimited(dev, "Unsupported integration time %u\n", val2);
+ return -EINVAL;
+ }
+
+ ret = regmap_field_read(rf->intg_time, &intg_time_idx);
+ if (ret)
+ return ret;
+
+ ret = regmap_field_read(rf->gain, &gain_idx);
+ if (ret)
+ return ret;
+
+ intg_old = iio_gts_find_int_time_by_sel(&data->gts, intg_time_idx);
+ if (intg_old < 0)
+ return intg_old;
+
+ if (intg_old == val2)
+ return 0;
+
+ gain_old = iio_gts_find_gain_by_sel(&data->gts, gain_idx);
+ if (gain_old < 0)
+ return gain_old;
+
+ iio_gts_find_new_gain_by_old_gain_time(&data->gts, gain_old, intg_old,
+ val2, &gain_new);
+
+ if (gain_new < 0) {
+ dev_err_ratelimited(dev, "Unsupported gain with time\n");
+ return gain_new;
+ }
+
+ gain_new_closest = iio_find_closest_gain_low(&data->gts, gain_new, &ok);
+ if (gain_new_closest < 0) {
+ gain_new_closest = iio_gts_get_min_gain(&data->gts);
+ if (gain_new_closest < 0)
+ return gain_new_closest;
+ }
+ if (!ok)
+ dev_dbg(dev, "Unable to find optimum gain, setting minimum");
+
+ ret = iio_gts_find_sel_by_int_time(&data->gts, val2);
+ if (ret < 0)
+ return ret;
+
+ ret = regmap_field_write(rf->intg_time, ret);
+ if (ret)
+ return ret;
+
+ ret = iio_gts_find_sel_by_gain(&data->gts, gain_new_closest);
+ if (ret < 0)
+ return ret;
+
+ return regmap_field_write(rf->gain, ret);
+}
+
+static int apds9306_sampling_freq_get(struct apds9306_data *data, int *val,
+ int *val2)
+{
+ struct apds9306_regfields *rf = &data->rf;
+ int ret, repeat_rate_idx;
+
+ ret = regmap_field_read(rf->repeat_rate, &repeat_rate_idx);
+ if (ret)
+ return ret;
+
+ if (repeat_rate_idx >= ARRAY_SIZE(apds9306_repeat_rate_freq))
+ return -EINVAL;
+
+ *val = apds9306_repeat_rate_freq[repeat_rate_idx][0];
+ *val2 = apds9306_repeat_rate_freq[repeat_rate_idx][1];
+
+ return 0;
+}
+
+static int apds9306_sampling_freq_set(struct apds9306_data *data, int val,
+ int val2)
+{
+ struct apds9306_regfields *rf = &data->rf;
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(apds9306_repeat_rate_freq); i++) {
+ if (apds9306_repeat_rate_freq[i][0] == val &&
+ apds9306_repeat_rate_freq[i][1] == val2)
+ return regmap_field_write(rf->repeat_rate, i);
+ }
+
+ return -EINVAL;
+}
+
+static int apds9306_scale_get(struct apds9306_data *data, int *val, int *val2)
+{
+ struct apds9306_regfields *rf = &data->rf;
+ int gain, intg, ret, intg_time_idx, gain_idx;
+
+ ret = regmap_field_read(rf->gain, &gain_idx);
+ if (ret)
+ return ret;
+
+ ret = regmap_field_read(rf->intg_time, &intg_time_idx);
+ if (ret)
+ return ret;
+
+ gain = iio_gts_find_gain_by_sel(&data->gts, gain_idx);
+ if (gain < 0)
+ return gain;
+
+ intg = iio_gts_find_int_time_by_sel(&data->gts, intg_time_idx);
+ if (intg < 0)
+ return intg;
+
+ return iio_gts_get_scale(&data->gts, gain, intg, val, val2);
+}
+
+static int apds9306_scale_set(struct apds9306_data *data, int val, int val2)
+{
+ struct apds9306_regfields *rf = &data->rf;
+ int i, ret, time_sel, gain_sel, intg_time_idx;
+
+ ret = regmap_field_read(rf->intg_time, &intg_time_idx);
+ if (ret)
+ return ret;
+
+ ret = iio_gts_find_gain_sel_for_scale_using_time(&data->gts,
+ intg_time_idx, val, val2, &gain_sel);
+ if (ret) {
+ for (i = 0; i < data->gts.num_itime; i++) {
+ time_sel = data->gts.itime_table[i].sel;
+
+ if (time_sel == intg_time_idx)
+ continue;
+
+ ret = iio_gts_find_gain_sel_for_scale_using_time(&data->gts,
+ time_sel, val, val2, &gain_sel);
+ if (!ret)
+ break;
+ }
+ if (ret)
+ return -EINVAL;
+
+ ret = regmap_field_write(rf->intg_time, time_sel);
+ if (ret)
+ return ret;
+ }
+
+ return regmap_field_write(rf->gain, gain_sel);
+}
+
+static int apds9306_event_period_get(struct apds9306_data *data, int *val)
+{
+ struct apds9306_regfields *rf = &data->rf;
+ int period, ret;
+
+ ret = regmap_field_read(rf->int_persist_val, &period);
+ if (ret)
+ return ret;
+
+ if (!in_range(period, 0, APDS9306_ALS_PERSIST_NUM_VALS))
+ return -EINVAL;
+
+ *val = period;
+
+ return ret;
+}
+
+static int apds9306_event_period_set(struct apds9306_data *data, int val)
+{
+ struct apds9306_regfields *rf = &data->rf;
+
+ if (!in_range(val, 0, APDS9306_ALS_PERSIST_NUM_VALS))
+ return -EINVAL;
+
+ return regmap_field_write(rf->int_persist_val, val);
+}
+
+static int apds9306_event_thresh_get(struct apds9306_data *data, int dir,
+ int *val)
+{
+ int var, ret;
+ u8 buff[3];
+
+ if (dir == IIO_EV_DIR_RISING)
+ var = APDS9306_ALS_THRES_UP_0_REG;
+ else if (dir == IIO_EV_DIR_FALLING)
+ var = APDS9306_ALS_THRES_LOW_0_REG;
+ else
+ return -EINVAL;
+
+ ret = regmap_bulk_read(data->regmap, var, buff, sizeof(buff));
+ if (ret)
+ return ret;
+
+ *val = get_unaligned_le24(&buff);
+
+ return 0;
+}
+
+static int apds9306_event_thresh_set(struct apds9306_data *data, int dir,
+ int val)
+{
+ int var;
+ u8 buff[3];
+
+ if (dir == IIO_EV_DIR_RISING)
+ var = APDS9306_ALS_THRES_UP_0_REG;
+ else if (dir == IIO_EV_DIR_FALLING)
+ var = APDS9306_ALS_THRES_LOW_0_REG;
+ else
+ return -EINVAL;
+
+ if (!in_range(val, 0, APDS9306_ALS_THRES_VAL_MAX))
+ return -EINVAL;
+
+ put_unaligned_le24(val, buff);
+
+ return regmap_bulk_write(data->regmap, var, buff, sizeof(buff));
+}
+
+static int apds9306_event_thresh_adaptive_get(struct apds9306_data *data, int *val)
+{
+ struct apds9306_regfields *rf = &data->rf;
+ int thr_adpt, ret;
+
+ ret = regmap_field_read(rf->int_thresh_var_val, &thr_adpt);
+ if (ret)
+ return ret;
+
+ if (!in_range(thr_adpt, 0, APDS9306_ALS_THRES_VAR_NUM_VALS))
+ return -EINVAL;
+
+ *val = thr_adpt;
+
+ return ret;
+}
+
+static int apds9306_event_thresh_adaptive_set(struct apds9306_data *data, int val)
+{
+ struct apds9306_regfields *rf = &data->rf;
+
+ if (!in_range(val, 0, APDS9306_ALS_THRES_VAR_NUM_VALS))
+ return -EINVAL;
+
+ return regmap_field_write(rf->int_thresh_var_val, val);
+}
+
+static int apds9306_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan, int *val,
+ int *val2, long mask)
+{
+ struct apds9306_data *data = iio_priv(indio_dev);
+ int ret, reg;
+
+ switch (mask) {
+ case IIO_CHAN_INFO_RAW:
+ if (chan->channel2 == IIO_MOD_LIGHT_CLEAR)
+ reg = APDS9306_CLEAR_DATA_0_REG;
+ else
+ reg = APDS9306_ALS_DATA_0_REG;
+ /*
+ * Changing device parameters during adc operation, resets
+ * the ADC which has to avoided.
+ */
+ if (!iio_device_claim_direct(indio_dev))
+ return -EBUSY;
+ ret = apds9306_read_data(data, val, reg);
+ iio_device_release_direct(indio_dev);
+ if (ret)
+ return ret;
+
+ return IIO_VAL_INT;
+ case IIO_CHAN_INFO_INT_TIME:
+ ret = apds9306_intg_time_get(data, val2);
+ if (ret)
+ return ret;
+ *val = 0;
+
+ return IIO_VAL_INT_PLUS_MICRO;
+ case IIO_CHAN_INFO_SAMP_FREQ:
+ ret = apds9306_sampling_freq_get(data, val, val2);
+ if (ret)
+ return ret;
+
+ return IIO_VAL_INT_PLUS_MICRO;
+ case IIO_CHAN_INFO_SCALE:
+ ret = apds9306_scale_get(data, val, val2);
+ if (ret)
+ return ret;
+
+ return IIO_VAL_INT_PLUS_NANO;
+ default:
+ return -EINVAL;
+ }
+};
+
+static int apds9306_read_avail(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ const int **vals, int *type, int *length,
+ long mask)
+{
+ struct apds9306_data *data = iio_priv(indio_dev);
+
+ switch (mask) {
+ case IIO_CHAN_INFO_INT_TIME:
+ return iio_gts_avail_times(&data->gts, vals, type, length);
+ case IIO_CHAN_INFO_SCALE:
+ return iio_gts_all_avail_scales(&data->gts, vals, type, length);
+ case IIO_CHAN_INFO_SAMP_FREQ:
+ *length = ARRAY_SIZE(apds9306_repeat_rate_freq) * 2;
+ *vals = (const int *)apds9306_repeat_rate_freq;
+ *type = IIO_VAL_INT_PLUS_MICRO;
+
+ return IIO_AVAIL_LIST;
+ default:
+ return -EINVAL;
+ }
+}
+
+static int apds9306_write_raw_get_fmt(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ long mask)
+{
+ switch (mask) {
+ case IIO_CHAN_INFO_SCALE:
+ return IIO_VAL_INT_PLUS_NANO;
+ case IIO_CHAN_INFO_INT_TIME:
+ return IIO_VAL_INT_PLUS_MICRO;
+ case IIO_CHAN_INFO_SAMP_FREQ:
+ return IIO_VAL_INT_PLUS_MICRO;
+ default:
+ return -EINVAL;
+ }
+}
+
+static int apds9306_write_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan, int val,
+ int val2, long mask)
+{
+ struct apds9306_data *data = iio_priv(indio_dev);
+
+ guard(mutex)(&data->mutex);
+
+ switch (mask) {
+ case IIO_CHAN_INFO_INT_TIME:
+ if (val)
+ return -EINVAL;
+ return apds9306_intg_time_set(data, val2);
+ case IIO_CHAN_INFO_SCALE:
+ return apds9306_scale_set(data, val, val2);
+ case IIO_CHAN_INFO_SAMP_FREQ:
+ return apds9306_sampling_freq_set(data, val, val2);
+ default:
+ return -EINVAL;
+ }
+}
+
+static irqreturn_t apds9306_irq_handler(int irq, void *priv)
+{
+ struct iio_dev *indio_dev = priv;
+ struct apds9306_data *data = iio_priv(indio_dev);
+ struct apds9306_regfields *rf = &data->rf;
+ u64 ev_code;
+ int ret, status, int_src;
+
+ /*
+ * The interrupt line is released and the interrupt flag is
+ * cleared as a result of reading the status register. All the
+ * status flags are cleared as a result of this read.
+ */
+ ret = regmap_read(data->regmap, APDS9306_MAIN_STATUS_REG, &status);
+ if (ret < 0) {
+ dev_err_ratelimited(data->dev, "status reg read failed\n");
+ return IRQ_HANDLED;
+ }
+
+ ret = regmap_field_read(rf->int_src, &int_src);
+ if (ret)
+ return ret;
+
+ if ((status & APDS9306_ALS_INT_STAT_MASK)) {
+ if (int_src == APDS9306_INT_SRC_ALS)
+ ev_code = IIO_UNMOD_EVENT_CODE(IIO_LIGHT, 0,
+ IIO_EV_TYPE_THRESH,
+ IIO_EV_DIR_EITHER);
+ else
+ ev_code = IIO_MOD_EVENT_CODE(IIO_INTENSITY, 0,
+ IIO_MOD_LIGHT_CLEAR,
+ IIO_EV_TYPE_THRESH,
+ IIO_EV_DIR_EITHER);
+
+ iio_push_event(indio_dev, ev_code, iio_get_time_ns(indio_dev));
+ }
+
+ /*
+ * If a one-shot read through sysfs is underway at the same time
+ * as this interrupt handler is executing and a read data available
+ * flag was set, this flag is set to inform read_poll_timeout()
+ * to exit.
+ */
+ if ((status & APDS9306_ALS_DATA_STAT_MASK))
+ data->read_data_available = 1;
+
+ return IRQ_HANDLED;
+}
+
+static int apds9306_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)
+{
+ struct apds9306_data *data = iio_priv(indio_dev);
+ int ret;
+
+ switch (type) {
+ case IIO_EV_TYPE_THRESH:
+ if (dir == IIO_EV_DIR_EITHER && info == IIO_EV_INFO_PERIOD)
+ ret = apds9306_event_period_get(data, val);
+ else
+ ret = apds9306_event_thresh_get(data, dir, val);
+ if (ret)
+ return ret;
+
+ return IIO_VAL_INT;
+ case IIO_EV_TYPE_THRESH_ADAPTIVE:
+ ret = apds9306_event_thresh_adaptive_get(data, val);
+ if (ret)
+ return ret;
+
+ return IIO_VAL_INT;
+ default:
+ return -EINVAL;
+ }
+}
+
+static int apds9306_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)
+{
+ struct apds9306_data *data = iio_priv(indio_dev);
+
+ switch (type) {
+ case IIO_EV_TYPE_THRESH:
+ if (dir == IIO_EV_DIR_EITHER && info == IIO_EV_INFO_PERIOD)
+ return apds9306_event_period_set(data, val);
+
+ return apds9306_event_thresh_set(data, dir, val);
+ case IIO_EV_TYPE_THRESH_ADAPTIVE:
+ return apds9306_event_thresh_adaptive_set(data, val);
+ default:
+ return -EINVAL;
+ }
+}
+
+static int apds9306_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 apds9306_data *data = iio_priv(indio_dev);
+ struct apds9306_regfields *rf = &data->rf;
+ int int_en, int_src, ret;
+
+ switch (type) {
+ case IIO_EV_TYPE_THRESH: {
+ guard(mutex)(&data->mutex);
+
+ ret = regmap_field_read(rf->int_src, &int_src);
+ if (ret)
+ return ret;
+
+ ret = regmap_field_read(rf->int_en, &int_en);
+ if (ret)
+ return ret;
+
+ if (chan->type == IIO_LIGHT)
+ return int_en && (int_src == APDS9306_INT_SRC_ALS);
+
+ if (chan->type == IIO_INTENSITY)
+ return int_en && (int_src == APDS9306_INT_SRC_CLEAR);
+
+ return -EINVAL;
+ }
+ case IIO_EV_TYPE_THRESH_ADAPTIVE:
+ ret = regmap_field_read(rf->int_thresh_var_en, &int_en);
+ if (ret)
+ return ret;
+
+ return int_en;
+ default:
+ return -EINVAL;
+ }
+}
+
+static int apds9306_write_event_config(struct iio_dev *indio_dev,
+ const struct iio_chan_spec *chan,
+ enum iio_event_type type,
+ enum iio_event_direction dir,
+ bool state)
+{
+ struct apds9306_data *data = iio_priv(indio_dev);
+ struct apds9306_regfields *rf = &data->rf;
+ int ret, enabled;
+
+ switch (type) {
+ case IIO_EV_TYPE_THRESH: {
+ guard(mutex)(&data->mutex);
+
+ ret = regmap_field_read(rf->int_en, &enabled);
+ if (ret)
+ return ret;
+
+ /*
+ * If interrupt is enabled, the channel is set before enabling
+ * the interrupt. In case of disable, no need to switch
+ * channels. In case of different channel is selected while
+ * interrupt in on, just change the channel.
+ */
+ if (state) {
+ if (chan->type == IIO_LIGHT)
+ ret = regmap_field_write(rf->int_src, 1);
+ else if (chan->type == IIO_INTENSITY)
+ ret = regmap_field_write(rf->int_src, 0);
+ else
+ return -EINVAL;
+
+ if (ret)
+ return ret;
+
+ if (enabled)
+ return 0;
+
+ ret = regmap_field_write(rf->int_en, 1);
+ if (ret)
+ return ret;
+
+ return pm_runtime_resume_and_get(data->dev);
+ } else {
+ if (!enabled)
+ return 0;
+
+ ret = regmap_field_write(rf->int_en, 0);
+ if (ret)
+ return ret;
+
+ pm_runtime_mark_last_busy(data->dev);
+ pm_runtime_put_autosuspend(data->dev);
+
+ return 0;
+ }
+ }
+ case IIO_EV_TYPE_THRESH_ADAPTIVE:
+ return regmap_field_write(rf->int_thresh_var_en, state);
+ default:
+ return -EINVAL;
+ }
+}
+
+static const struct iio_info apds9306_info_no_events = {
+ .read_avail = apds9306_read_avail,
+ .read_raw = apds9306_read_raw,
+ .write_raw = apds9306_write_raw,
+ .write_raw_get_fmt = apds9306_write_raw_get_fmt,
+};
+
+static const struct iio_info apds9306_info = {
+ .read_avail = apds9306_read_avail,
+ .read_raw = apds9306_read_raw,
+ .write_raw = apds9306_write_raw,
+ .write_raw_get_fmt = apds9306_write_raw_get_fmt,
+ .read_event_value = apds9306_read_event,
+ .write_event_value = apds9306_write_event,
+ .read_event_config = apds9306_read_event_config,
+ .write_event_config = apds9306_write_event_config,
+ .event_attrs = &apds9306_event_attr_group,
+};
+
+static int apds9306_init_iio_gts(struct apds9306_data *data)
+{
+ int i, ret, part_id;
+
+ ret = regmap_read(data->regmap, APDS9306_PART_ID_REG, &part_id);
+ if (ret)
+ return ret;
+
+ for (i = 0; i < ARRAY_SIZE(apds9306_gts_mul); i++)
+ if (part_id == apds9306_gts_mul[i].part_id)
+ break;
+
+ if (i == ARRAY_SIZE(apds9306_gts_mul))
+ return -ENOENT;
+
+ return devm_iio_init_iio_gts(data->dev,
+ apds9306_gts_mul[i].max_scale_int,
+ apds9306_gts_mul[i].max_scale_nano,
+ apds9306_gains, ARRAY_SIZE(apds9306_gains),
+ apds9306_itimes, ARRAY_SIZE(apds9306_itimes),
+ &data->gts);
+}
+
+static void apds9306_powerdown(void *ptr)
+{
+ struct apds9306_data *data = (struct apds9306_data *)ptr;
+ struct apds9306_regfields *rf = &data->rf;
+ int ret;
+
+ ret = regmap_field_write(rf->int_thresh_var_en, 0);
+ if (ret)
+ return;
+
+ ret = regmap_field_write(rf->int_en, 0);
+ if (ret)
+ return;
+
+ apds9306_power_state(data, false);
+}
+
+static int apds9306_device_init(struct apds9306_data *data)
+{
+ struct apds9306_regfields *rf = &data->rf;
+ int ret;
+
+ ret = apds9306_init_iio_gts(data);
+ if (ret)
+ return ret;
+
+ ret = regmap_field_write(rf->intg_time, APDS9306_MEAS_MODE_100MS);
+ if (ret)
+ return ret;
+
+ ret = regmap_field_write(rf->repeat_rate, APDS9306_SAMP_FREQ_10HZ);
+ if (ret)
+ return ret;
+
+ ret = regmap_field_write(rf->gain, APDS9306_GSEL_3X);
+ if (ret)
+ return ret;
+
+ ret = regmap_field_write(rf->int_src, APDS9306_INT_SRC_ALS);
+ if (ret)
+ return ret;
+
+ ret = regmap_field_write(rf->int_en, 0);
+ if (ret)
+ return ret;
+
+ return regmap_field_write(rf->int_thresh_var_en, 0);
+}
+
+static int apds9306_pm_init(struct apds9306_data *data)
+{
+ struct device *dev = data->dev;
+ int ret;
+
+ ret = apds9306_power_state(data, true);
+ if (ret)
+ return ret;
+
+ ret = pm_runtime_set_active(dev);
+ if (ret)
+ return ret;
+
+ ret = devm_pm_runtime_enable(dev);
+ if (ret)
+ return ret;
+
+ pm_runtime_set_autosuspend_delay(dev, 5000);
+ pm_runtime_use_autosuspend(dev);
+ pm_runtime_get(dev);
+
+ return 0;
+}
+
+static int apds9306_probe(struct i2c_client *client)
+{
+ struct device *dev = &client->dev;
+ struct apds9306_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);
+
+ mutex_init(&data->mutex);
+
+ data->regmap = devm_regmap_init_i2c(client, &apds9306_regmap);
+ if (IS_ERR(data->regmap))
+ return dev_err_probe(dev, PTR_ERR(data->regmap),
+ "regmap initialization failed\n");
+
+ data->dev = dev;
+ i2c_set_clientdata(client, indio_dev);
+
+ ret = apds9306_regfield_init(data);
+ if (ret)
+ return dev_err_probe(dev, ret, "regfield initialization failed\n");
+
+ ret = devm_regulator_get_enable(dev, "vdd");
+ if (ret)
+ return dev_err_probe(dev, ret, "Failed to enable regulator\n");
+
+ indio_dev->name = "apds9306";
+ indio_dev->modes = INDIO_DIRECT_MODE;
+ if (client->irq) {
+ indio_dev->info = &apds9306_info;
+ indio_dev->channels = apds9306_channels_with_events;
+ indio_dev->num_channels = ARRAY_SIZE(apds9306_channels_with_events);
+ ret = devm_request_threaded_irq(dev, client->irq, NULL,
+ apds9306_irq_handler, IRQF_ONESHOT,
+ "apds9306_event", indio_dev);
+ if (ret)
+ return dev_err_probe(dev, ret,
+ "failed to assign interrupt.\n");
+ } else {
+ indio_dev->info = &apds9306_info_no_events;
+ indio_dev->channels = apds9306_channels_without_events;
+ indio_dev->num_channels =
+ ARRAY_SIZE(apds9306_channels_without_events);
+ }
+
+ ret = apds9306_pm_init(data);
+ if (ret)
+ return dev_err_probe(dev, ret, "failed pm init\n");
+
+ ret = apds9306_device_init(data);
+ if (ret)
+ return dev_err_probe(dev, ret, "failed to init device\n");
+
+ ret = devm_add_action_or_reset(dev, apds9306_powerdown, data);
+ if (ret)
+ return dev_err_probe(dev, ret, "failed to add action or reset\n");
+
+ ret = devm_iio_device_register(dev, indio_dev);
+ if (ret)
+ return dev_err_probe(dev, ret, "failed iio device registration\n");
+
+ pm_runtime_put_autosuspend(dev);
+
+ return 0;
+}
+
+static int apds9306_runtime_suspend(struct device *dev)
+{
+ struct apds9306_data *data = iio_priv(dev_get_drvdata(dev));
+
+ return apds9306_power_state(data, false);
+}
+
+static int apds9306_runtime_resume(struct device *dev)
+{
+ struct apds9306_data *data = iio_priv(dev_get_drvdata(dev));
+
+ return apds9306_power_state(data, true);
+}
+
+static DEFINE_RUNTIME_DEV_PM_OPS(apds9306_pm_ops,
+ apds9306_runtime_suspend,
+ apds9306_runtime_resume,
+ NULL);
+
+static const struct of_device_id apds9306_of_match[] = {
+ { .compatible = "avago,apds9306" },
+ { }
+};
+MODULE_DEVICE_TABLE(of, apds9306_of_match);
+
+static struct i2c_driver apds9306_driver = {
+ .driver = {
+ .name = "apds9306",
+ .pm = pm_ptr(&apds9306_pm_ops),
+ .of_match_table = apds9306_of_match,
+ },
+ .probe = apds9306_probe,
+};
+module_i2c_driver(apds9306_driver);
+
+MODULE_AUTHOR("Subhajit Ghosh <subhajit.ghosh@tweaklogic.com>");
+MODULE_DESCRIPTION("APDS9306 Ambient Light Sensor driver");
+MODULE_LICENSE("GPL");
+MODULE_IMPORT_NS("IIO_GTS_HELPER");
diff --git a/drivers/iio/light/apds9960.c b/drivers/iio/light/apds9960.c
index 1065a340b12b..0003a29bf264 100644
--- a/drivers/iio/light/apds9960.c
+++ b/drivers/iio/light/apds9960.c
@@ -133,8 +133,8 @@ struct apds9960_data {
struct regmap_field *reg_enable_pxs;
/* state */
- int als_int;
- int pxs_int;
+ bool als_int;
+ bool pxs_int;
int gesture_mode_running;
/* gain values */
@@ -146,6 +146,25 @@ struct apds9960_data {
/* gesture buffer */
u8 buffer[4]; /* 4 8-bit channels */
+
+ /* calibration value buffer */
+ int calibbias[5];
+};
+
+enum {
+ APDS9960_CHAN_PROXIMITY,
+ APDS9960_CHAN_GESTURE_UP,
+ APDS9960_CHAN_GESTURE_DOWN,
+ APDS9960_CHAN_GESTURE_LEFT,
+ APDS9960_CHAN_GESTURE_RIGHT,
+};
+
+static const unsigned int apds9960_offset_regs[][2] = {
+ [APDS9960_CHAN_PROXIMITY] = {APDS9960_REG_POFFSET_UR, APDS9960_REG_POFFSET_DL},
+ [APDS9960_CHAN_GESTURE_UP] = {APDS9960_REG_GOFFSET_U, 0},
+ [APDS9960_CHAN_GESTURE_DOWN] = {APDS9960_REG_GOFFSET_D, 0},
+ [APDS9960_CHAN_GESTURE_LEFT] = {APDS9960_REG_GOFFSET_L, 0},
+ [APDS9960_CHAN_GESTURE_RIGHT] = {APDS9960_REG_GOFFSET_R, 0},
};
static const struct reg_default apds9960_reg_defaults[] = {
@@ -255,6 +274,7 @@ static const struct iio_event_spec apds9960_als_event_spec[] = {
#define APDS9960_GESTURE_CHANNEL(_dir, _si) { \
.type = IIO_PROXIMITY, \
+ .info_mask_separate = BIT(IIO_CHAN_INFO_CALIBBIAS), \
.channel = _si + 1, \
.scan_index = _si, \
.indexed = 1, \
@@ -282,7 +302,8 @@ static const struct iio_chan_spec apds9960_channels[] = {
{
.type = IIO_PROXIMITY,
.address = APDS9960_REG_PDATA,
- .info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
+ BIT(IIO_CHAN_INFO_CALIBBIAS),
.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE),
.channel = 0,
.indexed = 0,
@@ -316,6 +337,28 @@ static const struct iio_chan_spec apds9960_channels[] = {
APDS9960_INTENSITY_CHANNEL(BLUE),
};
+static int apds9960_set_calibbias(struct apds9960_data *data,
+ struct iio_chan_spec const *chan, int calibbias)
+{
+ int ret, i;
+
+ if (calibbias < S8_MIN || calibbias > S8_MAX)
+ return -EINVAL;
+
+ guard(mutex)(&data->lock);
+ for (i = 0; i < 2; i++) {
+ if (apds9960_offset_regs[chan->channel][i] == 0)
+ break;
+
+ ret = regmap_write(data->regmap, apds9960_offset_regs[chan->channel][i], calibbias);
+ if (ret < 0)
+ return ret;
+ }
+ data->calibbias[chan->channel] = calibbias;
+
+ return 0;
+}
+
/* integration time in us */
static const int apds9960_int_time[][2] = {
{ 28000, 246},
@@ -531,6 +574,12 @@ static int apds9960_read_raw(struct iio_dev *indio_dev,
}
mutex_unlock(&data->lock);
break;
+ case IIO_CHAN_INFO_CALIBBIAS:
+ mutex_lock(&data->lock);
+ *val = data->calibbias[chan->channel];
+ ret = IIO_VAL_INT;
+ mutex_unlock(&data->lock);
+ break;
}
return ret;
@@ -564,6 +613,10 @@ static int apds9960_write_raw(struct iio_dev *indio_dev,
default:
return -EINVAL;
}
+ case IIO_CHAN_INFO_CALIBBIAS:
+ if (val2 != 0)
+ return -EINVAL;
+ return apds9960_set_calibbias(data, chan, val);
default:
return -EINVAL;
}
@@ -696,21 +749,17 @@ static int apds9960_read_event_config(struct iio_dev *indio_dev,
default:
return -EINVAL;
}
-
- return 0;
}
static int apds9960_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)
+ bool state)
{
struct apds9960_data *data = iio_priv(indio_dev);
int ret;
- state = !!state;
-
switch (chan->type) {
case IIO_PROXIMITY:
if (data->pxs_int == state)
@@ -1107,8 +1156,8 @@ static const struct dev_pm_ops apds9960_pm_ops = {
};
static const struct i2c_device_id apds9960_id[] = {
- { "apds9960", 0 },
- {}
+ { "apds9960" },
+ { }
};
MODULE_DEVICE_TABLE(i2c, apds9960_id);
diff --git a/drivers/iio/light/as73211.c b/drivers/iio/light/as73211.c
index be0068081ebb..68f60dc3c79d 100644
--- a/drivers/iio/light/as73211.c
+++ b/drivers/iio/light/as73211.c
@@ -16,6 +16,7 @@
*/
#include <linux/bitfield.h>
+#include <linux/cleanup.h>
#include <linux/completion.h>
#include <linux/delay.h>
#include <linux/i2c.h>
@@ -177,6 +178,12 @@ struct as73211_data {
BIT(AS73211_SCAN_INDEX_TEMP) | \
AS73211_SCAN_MASK_COLOR)
+static const unsigned long as73211_scan_masks[] = {
+ AS73211_SCAN_MASK_COLOR,
+ AS73211_SCAN_MASK_ALL,
+ 0
+};
+
static const struct iio_chan_spec as73211_channels[] = {
{
.type = IIO_TEMP,
@@ -412,18 +419,17 @@ static int as73211_read_raw(struct iio_dev *indio_dev, struct iio_chan_spec cons
case IIO_CHAN_INFO_RAW: {
int ret;
- ret = iio_device_claim_direct_mode(indio_dev);
- if (ret < 0)
- return ret;
+ if (!iio_device_claim_direct(indio_dev))
+ return -EBUSY;
ret = as73211_req_data(data);
if (ret < 0) {
- iio_device_release_direct_mode(indio_dev);
+ iio_device_release_direct(indio_dev);
return ret;
}
ret = i2c_smbus_read_word_data(data->client, chan->address);
- iio_device_release_direct_mode(indio_dev);
+ iio_device_release_direct(indio_dev);
if (ret < 0)
return ret;
@@ -511,6 +517,16 @@ static int _as73211_write_raw(struct iio_dev *indio_dev,
struct as73211_data *data = iio_priv(indio_dev);
int ret;
+ /* 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)
+ return ret;
+ }
+
switch (mask) {
case IIO_CHAN_INFO_SAMP_FREQ: {
int reg_bits, freq_kHz = val / HZ_PER_KHZ; /* 1024, 2048, ... */
@@ -595,28 +611,14 @@ static int as73211_write_raw(struct iio_dev *indio_dev, struct iio_chan_spec con
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;
+ guard(mutex)(&data->mutex);
- ret = i2c_smbus_write_byte_data(data->client, AS73211_REG_OSR, data->osr);
- if (ret < 0)
- goto error_release;
- }
+ if (!iio_device_claim_direct(indio_dev))
+ return -EBUSY;
ret = _as73211_write_raw(indio_dev, chan, val, val2, mask);
+ iio_device_release_direct(indio_dev);
-error_release:
- iio_device_release_direct_mode(indio_dev);
-error_unlock:
- mutex_unlock(&data->mutex);
return ret;
}
@@ -636,7 +638,7 @@ static irqreturn_t as73211_trigger_handler(int irq __always_unused, void *p)
struct as73211_data *data = iio_priv(indio_dev);
struct {
__le16 chan[4];
- s64 ts __aligned(8);
+ aligned_s64 ts;
} scan;
int data_result, ret;
@@ -672,9 +674,12 @@ static irqreturn_t as73211_trigger_handler(int irq __always_unused, void *p)
/* AS73211 starts reading at address 2 */
ret = i2c_master_recv(data->client,
- (char *)&scan.chan[1], 3 * sizeof(scan.chan[1]));
+ (char *)&scan.chan[0], 3 * sizeof(scan.chan[0]));
if (ret < 0)
goto done;
+
+ /* Avoid pushing uninitialized data */
+ scan.chan[3] = 0;
}
if (data_result) {
@@ -682,9 +687,15 @@ static irqreturn_t as73211_trigger_handler(int irq __always_unused, void *p)
* 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);
+ if (*indio_dev->active_scan_mask == AS73211_SCAN_MASK_ALL) {
+ scan.chan[1] = cpu_to_le16(U16_MAX);
+ scan.chan[2] = cpu_to_le16(U16_MAX);
+ scan.chan[3] = cpu_to_le16(U16_MAX);
+ } else {
+ scan.chan[0] = cpu_to_le16(U16_MAX);
+ scan.chan[1] = cpu_to_le16(U16_MAX);
+ scan.chan[2] = cpu_to_le16(U16_MAX);
+ }
}
iio_push_to_buffers_with_timestamp(indio_dev, &scan, iio_get_time_ns(indio_dev));
@@ -758,6 +769,7 @@ static int as73211_probe(struct i2c_client *client)
indio_dev->channels = data->spec_dev->channels;
indio_dev->num_channels = data->spec_dev->num_channels;
indio_dev->modes = INDIO_DIRECT_MODE;
+ indio_dev->available_scan_masks = as73211_scan_masks;
ret = i2c_smbus_read_byte_data(data->client, AS73211_REG_OSR);
if (ret < 0)
diff --git a/drivers/iio/light/bh1745.c b/drivers/iio/light/bh1745.c
new file mode 100644
index 000000000000..56ab5fe90ff9
--- /dev/null
+++ b/drivers/iio/light/bh1745.c
@@ -0,0 +1,904 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * ROHM BH1745 digital colour sensor driver
+ *
+ * Copyright (C) Mudit Sharma <muditsharma.info@gmail.com>
+ *
+ * 7-bit I2C slave addresses:
+ * 0x38 (ADDR pin low)
+ * 0x39 (ADDR pin high)
+ */
+
+#include <linux/i2c.h>
+#include <linux/mutex.h>
+#include <linux/util_macros.h>
+#include <linux/iio/events.h>
+#include <linux/regmap.h>
+#include <linux/bits.h>
+#include <linux/bitfield.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>
+#include <linux/iio/iio-gts-helper.h>
+
+/* BH1745 configuration registers */
+
+/* System control */
+#define BH1745_SYS_CTRL 0x40
+#define BH1745_SYS_CTRL_SW_RESET BIT(7)
+#define BH1745_SYS_CTRL_INTR_RESET BIT(6)
+#define BH1745_SYS_CTRL_PART_ID_MASK GENMASK(5, 0)
+#define BH1745_PART_ID 0x0B
+
+/* Mode control 1 */
+#define BH1745_MODE_CTRL1 0x41
+#define BH1745_CTRL1_MEASUREMENT_TIME_MASK GENMASK(2, 0)
+
+/* Mode control 2 */
+#define BH1745_MODE_CTRL2 0x42
+#define BH1745_CTRL2_RGBC_EN BIT(4)
+#define BH1745_CTRL2_ADC_GAIN_MASK GENMASK(1, 0)
+
+/* Interrupt */
+#define BH1745_INTR 0x60
+#define BH1745_INTR_STATUS BIT(7)
+#define BH1745_INTR_SOURCE_MASK GENMASK(3, 2)
+#define BH1745_INTR_ENABLE BIT(0)
+
+#define BH1745_PERSISTENCE 0x61
+
+/* Threshold high */
+#define BH1745_TH_LSB 0x62
+#define BH1745_TH_MSB 0x63
+
+/* Threshold low */
+#define BH1745_TL_LSB 0x64
+#define BH1745_TL_MSB 0x65
+
+/* BH1745 data output regs */
+#define BH1745_RED_LSB 0x50
+#define BH1745_RED_MSB 0x51
+#define BH1745_GREEN_LSB 0x52
+#define BH1745_GREEN_MSB 0x53
+#define BH1745_BLUE_LSB 0x54
+#define BH1745_BLUE_MSB 0x55
+#define BH1745_CLEAR_LSB 0x56
+#define BH1745_CLEAR_MSB 0x57
+
+#define BH1745_MANU_ID_REG 0x92
+
+/* From 16x max HW gain and 32x max integration time */
+#define BH1745_MAX_GAIN 512
+
+enum bh1745_int_source {
+ BH1745_INTR_SOURCE_RED,
+ BH1745_INTR_SOURCE_GREEN,
+ BH1745_INTR_SOURCE_BLUE,
+ BH1745_INTR_SOURCE_CLEAR,
+};
+
+enum bh1745_gain {
+ BH1745_ADC_GAIN_1X,
+ BH1745_ADC_GAIN_2X,
+ BH1745_ADC_GAIN_16X,
+};
+
+enum bh1745_measurement_time {
+ BH1745_MEASUREMENT_TIME_160MS,
+ BH1745_MEASUREMENT_TIME_320MS,
+ BH1745_MEASUREMENT_TIME_640MS,
+ BH1745_MEASUREMENT_TIME_1280MS,
+ BH1745_MEASUREMENT_TIME_2560MS,
+ BH1745_MEASUREMENT_TIME_5120MS,
+};
+
+enum bh1745_presistence_value {
+ BH1745_PRESISTENCE_UPDATE_TOGGLE,
+ BH1745_PRESISTENCE_UPDATE_EACH_MEASUREMENT,
+ BH1745_PRESISTENCE_UPDATE_FOUR_MEASUREMENT,
+ BH1745_PRESISTENCE_UPDATE_EIGHT_MEASUREMENT,
+};
+
+static const struct iio_gain_sel_pair bh1745_gain[] = {
+ GAIN_SCALE_GAIN(1, BH1745_ADC_GAIN_1X),
+ GAIN_SCALE_GAIN(2, BH1745_ADC_GAIN_2X),
+ GAIN_SCALE_GAIN(16, BH1745_ADC_GAIN_16X),
+};
+
+static const struct iio_itime_sel_mul bh1745_itimes[] = {
+ GAIN_SCALE_ITIME_US(5120000, BH1745_MEASUREMENT_TIME_5120MS, 32),
+ GAIN_SCALE_ITIME_US(2560000, BH1745_MEASUREMENT_TIME_2560MS, 16),
+ GAIN_SCALE_ITIME_US(1280000, BH1745_MEASUREMENT_TIME_1280MS, 8),
+ GAIN_SCALE_ITIME_US(640000, BH1745_MEASUREMENT_TIME_640MS, 4),
+ GAIN_SCALE_ITIME_US(320000, BH1745_MEASUREMENT_TIME_320MS, 2),
+ GAIN_SCALE_ITIME_US(160000, BH1745_MEASUREMENT_TIME_160MS, 1),
+};
+
+struct bh1745_data {
+ /*
+ * Lock to prevent device setting update or read before
+ * related calculations are completed
+ */
+ struct mutex lock;
+ struct regmap *regmap;
+ struct device *dev;
+ struct iio_trigger *trig;
+ struct iio_gts gts;
+};
+
+static const struct regmap_range bh1745_volatile_ranges[] = {
+ regmap_reg_range(BH1745_MODE_CTRL2, BH1745_MODE_CTRL2), /* VALID */
+ regmap_reg_range(BH1745_RED_LSB, BH1745_CLEAR_MSB), /* Data */
+ regmap_reg_range(BH1745_INTR, BH1745_INTR), /* Interrupt */
+};
+
+static const struct regmap_access_table bh1745_volatile_regs = {
+ .yes_ranges = bh1745_volatile_ranges,
+ .n_yes_ranges = ARRAY_SIZE(bh1745_volatile_ranges),
+};
+
+static const struct regmap_range bh1745_readable_ranges[] = {
+ regmap_reg_range(BH1745_SYS_CTRL, BH1745_MODE_CTRL2),
+ regmap_reg_range(BH1745_RED_LSB, BH1745_CLEAR_MSB),
+ regmap_reg_range(BH1745_INTR, BH1745_INTR),
+ regmap_reg_range(BH1745_PERSISTENCE, BH1745_TL_MSB),
+ regmap_reg_range(BH1745_MANU_ID_REG, BH1745_MANU_ID_REG),
+};
+
+static const struct regmap_access_table bh1745_readable_regs = {
+ .yes_ranges = bh1745_readable_ranges,
+ .n_yes_ranges = ARRAY_SIZE(bh1745_readable_ranges),
+};
+
+static const struct regmap_range bh1745_writable_ranges[] = {
+ regmap_reg_range(BH1745_SYS_CTRL, BH1745_MODE_CTRL2),
+ regmap_reg_range(BH1745_INTR, BH1745_INTR),
+ regmap_reg_range(BH1745_PERSISTENCE, BH1745_TL_MSB),
+};
+
+static const struct regmap_access_table bh1745_writable_regs = {
+ .yes_ranges = bh1745_writable_ranges,
+ .n_yes_ranges = ARRAY_SIZE(bh1745_writable_ranges),
+};
+
+static const struct regmap_config bh1745_regmap = {
+ .reg_bits = 8,
+ .val_bits = 8,
+ .max_register = BH1745_MANU_ID_REG,
+ .cache_type = REGCACHE_RBTREE,
+ .volatile_table = &bh1745_volatile_regs,
+ .wr_table = &bh1745_writable_regs,
+ .rd_table = &bh1745_readable_regs,
+};
+
+static const struct iio_event_spec bh1745_event_spec[] = {
+ {
+ .type = IIO_EV_TYPE_THRESH,
+ .dir = IIO_EV_DIR_RISING,
+ .mask_shared_by_type = BIT(IIO_EV_INFO_VALUE),
+ },
+ {
+ .type = IIO_EV_TYPE_THRESH,
+ .dir = IIO_EV_DIR_FALLING,
+ .mask_shared_by_type = BIT(IIO_EV_INFO_VALUE),
+ },
+ {
+ .type = IIO_EV_TYPE_THRESH,
+ .dir = IIO_EV_DIR_EITHER,
+ .mask_shared_by_type = BIT(IIO_EV_INFO_PERIOD),
+ .mask_separate = BIT(IIO_EV_INFO_ENABLE),
+ },
+};
+
+#define BH1745_CHANNEL(_colour, _si, _addr) \
+ { \
+ .type = IIO_INTENSITY, .modified = 1, \
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
+ .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SCALE) | \
+ BIT(IIO_CHAN_INFO_INT_TIME), \
+ .info_mask_shared_by_all_available = \
+ BIT(IIO_CHAN_INFO_SCALE) | \
+ BIT(IIO_CHAN_INFO_INT_TIME), \
+ .event_spec = bh1745_event_spec, \
+ .num_event_specs = ARRAY_SIZE(bh1745_event_spec), \
+ .channel2 = IIO_MOD_LIGHT_##_colour, .address = _addr, \
+ .scan_index = _si, \
+ .scan_type = { \
+ .sign = 'u', \
+ .realbits = 16, \
+ .storagebits = 16, \
+ .endianness = IIO_CPU, \
+ }, \
+ }
+
+static const struct iio_chan_spec bh1745_channels[] = {
+ BH1745_CHANNEL(RED, 0, BH1745_RED_LSB),
+ BH1745_CHANNEL(GREEN, 1, BH1745_GREEN_LSB),
+ BH1745_CHANNEL(BLUE, 2, BH1745_BLUE_LSB),
+ BH1745_CHANNEL(CLEAR, 3, BH1745_CLEAR_LSB),
+ IIO_CHAN_SOFT_TIMESTAMP(4),
+};
+
+static int bh1745_reset(struct bh1745_data *data)
+{
+ return regmap_set_bits(data->regmap, BH1745_SYS_CTRL,
+ BH1745_SYS_CTRL_SW_RESET |
+ BH1745_SYS_CTRL_INTR_RESET);
+}
+
+static int bh1745_power_on(struct bh1745_data *data)
+{
+ return regmap_set_bits(data->regmap, BH1745_MODE_CTRL2,
+ BH1745_CTRL2_RGBC_EN);
+}
+
+static void bh1745_power_off(void *data_ptr)
+{
+ struct bh1745_data *data = data_ptr;
+ struct device *dev = data->dev;
+ int ret;
+
+ ret = regmap_clear_bits(data->regmap, BH1745_MODE_CTRL2,
+ BH1745_CTRL2_RGBC_EN);
+ if (ret)
+ dev_err(dev, "Failed to turn off device\n");
+}
+
+static int bh1745_get_scale(struct bh1745_data *data, int *val, int *val2)
+{
+ int ret;
+ int value;
+ int gain_sel, int_time_sel;
+ int gain;
+ const struct iio_itime_sel_mul *int_time;
+
+ ret = regmap_read(data->regmap, BH1745_MODE_CTRL2, &value);
+ if (ret)
+ return ret;
+
+ gain_sel = FIELD_GET(BH1745_CTRL2_ADC_GAIN_MASK, value);
+ gain = iio_gts_find_gain_by_sel(&data->gts, gain_sel);
+
+ ret = regmap_read(data->regmap, BH1745_MODE_CTRL1, &value);
+ if (ret)
+ return ret;
+
+ int_time_sel = FIELD_GET(BH1745_CTRL1_MEASUREMENT_TIME_MASK, value);
+ int_time = iio_gts_find_itime_by_sel(&data->gts, int_time_sel);
+
+ return iio_gts_get_scale(&data->gts, gain, int_time->time_us, val,
+ val2);
+}
+
+static int bh1745_set_scale(struct bh1745_data *data, int val)
+{
+ struct device *dev = data->dev;
+ int ret;
+ int value;
+ int hw_gain_sel, current_int_time_sel, new_int_time_sel;
+
+ ret = regmap_read(data->regmap, BH1745_MODE_CTRL1, &value);
+ if (ret)
+ return ret;
+
+ current_int_time_sel = FIELD_GET(BH1745_CTRL1_MEASUREMENT_TIME_MASK,
+ value);
+ ret = iio_gts_find_gain_sel_for_scale_using_time(&data->gts,
+ current_int_time_sel,
+ val, 0, &hw_gain_sel);
+ if (ret) {
+ for (int i = 0; i < data->gts.num_itime; i++) {
+ new_int_time_sel = data->gts.itime_table[i].sel;
+
+ if (new_int_time_sel == current_int_time_sel)
+ continue;
+
+ ret = iio_gts_find_gain_sel_for_scale_using_time(&data->gts,
+ new_int_time_sel,
+ val, 0,
+ &hw_gain_sel);
+ if (!ret)
+ break;
+ }
+
+ if (ret) {
+ dev_dbg(dev, "Unsupported scale value requested: %d\n",
+ val);
+ return -EINVAL;
+ }
+
+ ret = regmap_write_bits(data->regmap, BH1745_MODE_CTRL1,
+ BH1745_CTRL1_MEASUREMENT_TIME_MASK,
+ new_int_time_sel);
+ if (ret)
+ return ret;
+ }
+
+ return regmap_write_bits(data->regmap, BH1745_MODE_CTRL2,
+ BH1745_CTRL2_ADC_GAIN_MASK, hw_gain_sel);
+}
+
+static int bh1745_get_int_time(struct bh1745_data *data, int *val)
+{
+ int ret;
+ int value;
+ int int_time, int_time_sel;
+
+ ret = regmap_read(data->regmap, BH1745_MODE_CTRL1, &value);
+ if (ret)
+ return ret;
+
+ int_time_sel = FIELD_GET(BH1745_CTRL1_MEASUREMENT_TIME_MASK, value);
+ int_time = iio_gts_find_int_time_by_sel(&data->gts, int_time_sel);
+ if (int_time < 0)
+ return int_time;
+
+ *val = int_time;
+
+ return 0;
+}
+
+static int bh1745_set_int_time(struct bh1745_data *data, int val, int val2)
+{
+ struct device *dev = data->dev;
+ int ret;
+ int value;
+ int current_int_time, current_hwgain_sel, current_hwgain;
+ int new_hwgain, new_hwgain_sel, new_int_time_sel;
+ int req_int_time = (1000000 * val) + val2;
+
+ if (!iio_gts_valid_time(&data->gts, req_int_time)) {
+ dev_dbg(dev, "Unsupported integration time requested: %d\n",
+ req_int_time);
+ return -EINVAL;
+ }
+
+ ret = bh1745_get_int_time(data, &current_int_time);
+ if (ret)
+ return ret;
+
+ if (current_int_time == req_int_time)
+ return 0;
+
+ ret = regmap_read(data->regmap, BH1745_MODE_CTRL2, &value);
+ if (ret)
+ return ret;
+
+ current_hwgain_sel = FIELD_GET(BH1745_CTRL2_ADC_GAIN_MASK, value);
+ current_hwgain = iio_gts_find_gain_by_sel(&data->gts,
+ current_hwgain_sel);
+ ret = iio_gts_find_new_gain_by_old_gain_time(&data->gts, current_hwgain,
+ current_int_time,
+ req_int_time,
+ &new_hwgain);
+ if (new_hwgain < 0) {
+ dev_dbg(dev, "No corresponding gain for requested integration time\n");
+ return ret;
+ }
+
+ if (ret) {
+ bool in_range;
+
+ new_hwgain = iio_find_closest_gain_low(&data->gts, new_hwgain,
+ &in_range);
+ if (new_hwgain < 0) {
+ new_hwgain = iio_gts_get_min_gain(&data->gts);
+ if (new_hwgain < 0)
+ return ret;
+ }
+
+ if (!in_range)
+ dev_dbg(dev, "Optimal gain out of range\n");
+
+ dev_dbg(dev, "Scale changed, new hw_gain %d\n", new_hwgain);
+ }
+
+ new_hwgain_sel = iio_gts_find_sel_by_gain(&data->gts, new_hwgain);
+ if (new_hwgain_sel < 0)
+ return new_hwgain_sel;
+
+ ret = regmap_write_bits(data->regmap, BH1745_MODE_CTRL2,
+ BH1745_CTRL2_ADC_GAIN_MASK,
+ new_hwgain_sel);
+ if (ret)
+ return ret;
+
+ new_int_time_sel = iio_gts_find_sel_by_int_time(&data->gts,
+ req_int_time);
+ if (new_int_time_sel < 0)
+ return new_int_time_sel;
+
+ return regmap_write_bits(data->regmap, BH1745_MODE_CTRL1,
+ BH1745_CTRL1_MEASUREMENT_TIME_MASK,
+ new_int_time_sel);
+}
+
+static int bh1745_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int *val, int *val2, long mask)
+{
+ struct bh1745_data *data = iio_priv(indio_dev);
+ int ret;
+ int value;
+
+ switch (mask) {
+ case IIO_CHAN_INFO_RAW:
+ if (!iio_device_claim_direct(indio_dev))
+ return -EBUSY;
+
+ ret = regmap_bulk_read(data->regmap, chan->address, &value, 2);
+ iio_device_release_direct(indio_dev);
+ if (ret)
+ return ret;
+ *val = value;
+
+ return IIO_VAL_INT;
+
+ case IIO_CHAN_INFO_SCALE: {
+ guard(mutex)(&data->lock);
+ ret = bh1745_get_scale(data, val, val2);
+ if (ret)
+ return ret;
+
+ return IIO_VAL_INT;
+ }
+
+ case IIO_CHAN_INFO_INT_TIME: {
+ guard(mutex)(&data->lock);
+ *val = 0;
+ ret = bh1745_get_int_time(data, val2);
+ if (ret)
+ return 0;
+
+ return IIO_VAL_INT_PLUS_MICRO;
+ }
+
+ default:
+ return -EINVAL;
+ }
+}
+
+static int bh1745_write_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int val, int val2, long mask)
+{
+ struct bh1745_data *data = iio_priv(indio_dev);
+
+ guard(mutex)(&data->lock);
+ switch (mask) {
+ case IIO_CHAN_INFO_SCALE:
+ return bh1745_set_scale(data, val);
+
+ case IIO_CHAN_INFO_INT_TIME:
+ return bh1745_set_int_time(data, val, val2);
+
+ default:
+ return -EINVAL;
+ }
+}
+
+static int bh1745_write_raw_get_fmt(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ long mask)
+{
+ switch (mask) {
+ case IIO_CHAN_INFO_SCALE:
+ return IIO_VAL_INT;
+
+ case IIO_CHAN_INFO_INT_TIME:
+ return IIO_VAL_INT_PLUS_MICRO;
+
+ default:
+ return -EINVAL;
+ }
+}
+
+static int bh1745_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 bh1745_data *data = iio_priv(indio_dev);
+ int ret;
+
+ switch (info) {
+ case IIO_EV_INFO_VALUE:
+ switch (dir) {
+ case IIO_EV_DIR_RISING:
+ ret = regmap_bulk_read(data->regmap, BH1745_TH_LSB,
+ val, 2);
+ if (ret)
+ return ret;
+
+ return IIO_VAL_INT;
+
+ case IIO_EV_DIR_FALLING:
+ ret = regmap_bulk_read(data->regmap, BH1745_TL_LSB,
+ val, 2);
+ if (ret)
+ return ret;
+
+ return IIO_VAL_INT;
+
+ default:
+ return -EINVAL;
+ }
+
+ case IIO_EV_INFO_PERIOD:
+ ret = regmap_read(data->regmap, BH1745_PERSISTENCE, val);
+ if (ret)
+ return ret;
+
+ return IIO_VAL_INT;
+
+ default:
+ return -EINVAL;
+ }
+}
+
+static int bh1745_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 bh1745_data *data = iio_priv(indio_dev);
+ int ret;
+
+ switch (info) {
+ case IIO_EV_INFO_VALUE:
+ if (val < 0x0 || val > 0xFFFF)
+ return -EINVAL;
+
+ switch (dir) {
+ case IIO_EV_DIR_RISING:
+ ret = regmap_bulk_write(data->regmap, BH1745_TH_LSB,
+ &val, 2);
+ if (ret)
+ return ret;
+
+ return IIO_VAL_INT;
+
+ case IIO_EV_DIR_FALLING:
+ ret = regmap_bulk_write(data->regmap, BH1745_TL_LSB,
+ &val, 2);
+ if (ret)
+ return ret;
+
+ return IIO_VAL_INT;
+
+ default:
+ return -EINVAL;
+ }
+
+ case IIO_EV_INFO_PERIOD:
+ if (val < BH1745_PRESISTENCE_UPDATE_TOGGLE ||
+ val > BH1745_PRESISTENCE_UPDATE_EIGHT_MEASUREMENT)
+ return -EINVAL;
+ ret = regmap_write(data->regmap, BH1745_PERSISTENCE, val);
+ if (ret)
+ return ret;
+
+ return IIO_VAL_INT;
+
+ default:
+ return -EINVAL;
+ }
+}
+
+static int bh1745_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 bh1745_data *data = iio_priv(indio_dev);
+ int ret;
+ int value;
+ int int_src;
+
+ ret = regmap_read(data->regmap, BH1745_INTR, &value);
+ if (ret)
+ return ret;
+
+ if (!FIELD_GET(BH1745_INTR_ENABLE, value))
+ return 0;
+
+ int_src = FIELD_GET(BH1745_INTR_SOURCE_MASK, value);
+
+ switch (chan->channel2) {
+ case IIO_MOD_LIGHT_RED:
+ if (int_src == BH1745_INTR_SOURCE_RED)
+ return 1;
+ return 0;
+
+ case IIO_MOD_LIGHT_GREEN:
+ if (int_src == BH1745_INTR_SOURCE_GREEN)
+ return 1;
+ return 0;
+
+ case IIO_MOD_LIGHT_BLUE:
+ if (int_src == BH1745_INTR_SOURCE_BLUE)
+ return 1;
+ return 0;
+
+ case IIO_MOD_LIGHT_CLEAR:
+ if (int_src == BH1745_INTR_SOURCE_CLEAR)
+ return 1;
+ return 0;
+
+ default:
+ return -EINVAL;
+ }
+}
+
+static int bh1745_write_event_config(struct iio_dev *indio_dev,
+ const struct iio_chan_spec *chan,
+ enum iio_event_type type,
+ enum iio_event_direction dir, bool state)
+{
+ struct bh1745_data *data = iio_priv(indio_dev);
+ int value;
+
+ if (!state)
+ return regmap_clear_bits(data->regmap,
+ BH1745_INTR, BH1745_INTR_ENABLE);
+
+ /* Latch is always enabled when enabling interrupt */
+ value = BH1745_INTR_ENABLE;
+
+ switch (chan->channel2) {
+ case IIO_MOD_LIGHT_RED:
+ return regmap_write(data->regmap, BH1745_INTR,
+ value | FIELD_PREP(BH1745_INTR_SOURCE_MASK,
+ BH1745_INTR_SOURCE_RED));
+
+ case IIO_MOD_LIGHT_GREEN:
+ return regmap_write(data->regmap, BH1745_INTR,
+ value | FIELD_PREP(BH1745_INTR_SOURCE_MASK,
+ BH1745_INTR_SOURCE_GREEN));
+
+ case IIO_MOD_LIGHT_BLUE:
+ return regmap_write(data->regmap, BH1745_INTR,
+ value | FIELD_PREP(BH1745_INTR_SOURCE_MASK,
+ BH1745_INTR_SOURCE_BLUE));
+
+ case IIO_MOD_LIGHT_CLEAR:
+ return regmap_write(data->regmap, BH1745_INTR,
+ value | FIELD_PREP(BH1745_INTR_SOURCE_MASK,
+ BH1745_INTR_SOURCE_CLEAR));
+
+ default:
+ return -EINVAL;
+ }
+}
+
+static int bh1745_read_avail(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan, const int **vals,
+ int *type, int *length, long mask)
+{
+ struct bh1745_data *data = iio_priv(indio_dev);
+
+ switch (mask) {
+ case IIO_CHAN_INFO_INT_TIME:
+ return iio_gts_avail_times(&data->gts, vals, type, length);
+
+ case IIO_CHAN_INFO_SCALE:
+ return iio_gts_all_avail_scales(&data->gts, vals, type, length);
+
+ default:
+ return -EINVAL;
+ }
+}
+
+static const struct iio_info bh1745_info = {
+ .read_raw = bh1745_read_raw,
+ .write_raw = bh1745_write_raw,
+ .write_raw_get_fmt = bh1745_write_raw_get_fmt,
+ .read_event_value = bh1745_read_thresh,
+ .write_event_value = bh1745_write_thresh,
+ .read_event_config = bh1745_read_event_config,
+ .write_event_config = bh1745_write_event_config,
+ .read_avail = bh1745_read_avail,
+};
+
+static irqreturn_t bh1745_interrupt_handler(int interrupt, void *p)
+{
+ struct iio_dev *indio_dev = p;
+ struct bh1745_data *data = iio_priv(indio_dev);
+ int ret;
+ int value;
+ int int_src;
+
+ ret = regmap_read(data->regmap, BH1745_INTR, &value);
+ if (ret)
+ return IRQ_NONE;
+
+ int_src = FIELD_GET(BH1745_INTR_SOURCE_MASK, value);
+
+ if (value & BH1745_INTR_STATUS) {
+ iio_push_event(indio_dev,
+ IIO_UNMOD_EVENT_CODE(IIO_INTENSITY, int_src,
+ IIO_EV_TYPE_THRESH,
+ IIO_EV_DIR_EITHER),
+ iio_get_time_ns(indio_dev));
+
+ return IRQ_HANDLED;
+ }
+
+ return IRQ_NONE;
+}
+
+static irqreturn_t bh1745_trigger_handler(int interrupt, void *p)
+{
+ struct iio_poll_func *pf = p;
+ struct iio_dev *indio_dev = pf->indio_dev;
+ struct bh1745_data *data = iio_priv(indio_dev);
+ struct {
+ u16 chans[4];
+ aligned_s64 timestamp;
+ } scan;
+ u16 value;
+ int ret;
+ int i;
+ int j = 0;
+
+ memset(&scan, 0, sizeof(scan));
+
+ iio_for_each_active_channel(indio_dev, i) {
+ ret = regmap_bulk_read(data->regmap, BH1745_RED_LSB + 2 * i,
+ &value, 2);
+ if (ret)
+ goto err;
+
+ scan.chans[j++] = value;
+ }
+
+ iio_push_to_buffers_with_timestamp(indio_dev, &scan,
+ iio_get_time_ns(indio_dev));
+
+err:
+ iio_trigger_notify_done(indio_dev->trig);
+
+ return IRQ_HANDLED;
+}
+
+static int bh1745_setup_triggered_buffer(struct iio_dev *indio_dev,
+ struct device *parent,
+ int irq)
+{
+ struct bh1745_data *data = iio_priv(indio_dev);
+ struct device *dev = data->dev;
+ int ret;
+
+ ret = devm_iio_triggered_buffer_setup(parent, indio_dev, NULL,
+ bh1745_trigger_handler, NULL);
+ if (ret)
+ return dev_err_probe(dev, ret,
+ "Triggered buffer setup failed\n");
+
+ if (irq) {
+ ret = devm_request_threaded_irq(dev, irq, NULL,
+ bh1745_interrupt_handler,
+ IRQF_ONESHOT,
+ "bh1745_interrupt", indio_dev);
+ if (ret)
+ return dev_err_probe(dev, ret,
+ "Request for IRQ failed\n");
+ }
+
+ return 0;
+}
+
+static int bh1745_init(struct bh1745_data *data)
+{
+ int ret;
+ struct device *dev = data->dev;
+
+ mutex_init(&data->lock);
+
+ ret = devm_iio_init_iio_gts(dev, BH1745_MAX_GAIN, 0, bh1745_gain,
+ ARRAY_SIZE(bh1745_gain), bh1745_itimes,
+ ARRAY_SIZE(bh1745_itimes), &data->gts);
+ if (ret)
+ return ret;
+
+ ret = bh1745_reset(data);
+ if (ret)
+ return dev_err_probe(dev, ret, "Failed to reset sensor\n");
+
+ ret = bh1745_power_on(data);
+ if (ret)
+ return dev_err_probe(dev, ret, "Failed to turn on sensor\n");
+
+ ret = devm_add_action_or_reset(dev, bh1745_power_off, data);
+ if (ret)
+ return dev_err_probe(dev, ret,
+ "Failed to add action or reset\n");
+
+ return 0;
+}
+
+static int bh1745_probe(struct i2c_client *client)
+{
+ int ret;
+ int value;
+ int part_id;
+ struct bh1745_data *data;
+ struct iio_dev *indio_dev;
+ struct device *dev = &client->dev;
+
+ indio_dev = devm_iio_device_alloc(dev, sizeof(*data));
+ if (!indio_dev)
+ return -ENOMEM;
+
+ indio_dev->info = &bh1745_info;
+ indio_dev->name = "bh1745";
+ indio_dev->channels = bh1745_channels;
+ indio_dev->modes = INDIO_DIRECT_MODE;
+ indio_dev->num_channels = ARRAY_SIZE(bh1745_channels);
+ data = iio_priv(indio_dev);
+ data->dev = &client->dev;
+ data->regmap = devm_regmap_init_i2c(client, &bh1745_regmap);
+ if (IS_ERR(data->regmap))
+ return dev_err_probe(dev, PTR_ERR(data->regmap),
+ "Failed to initialize Regmap\n");
+
+ ret = regmap_read(data->regmap, BH1745_SYS_CTRL, &value);
+ if (ret)
+ return ret;
+
+ part_id = FIELD_GET(BH1745_SYS_CTRL_PART_ID_MASK, value);
+ if (part_id != BH1745_PART_ID)
+ dev_warn(dev, "Unknown part ID 0x%x\n", part_id);
+
+ ret = devm_regulator_get_enable(dev, "vdd");
+ if (ret)
+ return dev_err_probe(dev, ret,
+ "Failed to get and enable regulator\n");
+
+ ret = bh1745_init(data);
+ if (ret)
+ return ret;
+
+ ret = bh1745_setup_triggered_buffer(indio_dev, indio_dev->dev.parent,
+ client->irq);
+ if (ret)
+ return ret;
+
+ ret = devm_iio_device_register(dev, indio_dev);
+ if (ret)
+ return dev_err_probe(dev, ret, "Failed to register device\n");
+
+ return 0;
+}
+
+static const struct i2c_device_id bh1745_idtable[] = {
+ { "bh1745" },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, bh1745_idtable);
+
+static const struct of_device_id bh1745_of_match[] = {
+ { .compatible = "rohm,bh1745" },
+ { }
+};
+MODULE_DEVICE_TABLE(of, bh1745_of_match);
+
+static struct i2c_driver bh1745_driver = {
+ .driver = {
+ .name = "bh1745",
+ .of_match_table = bh1745_of_match,
+ },
+ .probe = bh1745_probe,
+ .id_table = bh1745_idtable,
+};
+module_i2c_driver(bh1745_driver);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Mudit Sharma <muditsharma.info@gmail.com>");
+MODULE_DESCRIPTION("BH1745 colour sensor driver");
+MODULE_IMPORT_NS("IIO_GTS_HELPER");
diff --git a/drivers/iio/light/bh1750.c b/drivers/iio/light/bh1750.c
index 4b869fa9e5b1..764f88826fcb 100644
--- a/drivers/iio/light/bh1750.c
+++ b/drivers/iio/light/bh1750.c
@@ -22,12 +22,16 @@
#include <linux/iio/iio.h>
#include <linux/iio/sysfs.h>
#include <linux/module.h>
+#include <linux/gpio/consumer.h>
#define BH1750_POWER_DOWN 0x00
#define BH1750_ONE_TIME_H_RES_MODE 0x20 /* auto-mode for BH1721 */
#define BH1750_CHANGE_INT_TIME_H_BIT 0x40
#define BH1750_CHANGE_INT_TIME_L_BIT 0x60
+/* Define the reset delay time in microseconds */
+#define BH1750_RESET_DELAY_US 10000 /* 10ms */
+
enum {
BH1710,
BH1721,
@@ -40,6 +44,7 @@ struct bh1750_data {
struct mutex lock;
const struct bh1750_chip_info *chip_info;
u16 mtreg;
+ struct gpio_desc *reset_gpio;
};
struct bh1750_chip_info {
@@ -248,6 +253,25 @@ static int bh1750_probe(struct i2c_client *client)
data->client = client;
data->chip_info = &bh1750_chip_info_tbl[id->driver_data];
+ /* Get reset GPIO from device tree */
+ data->reset_gpio = devm_gpiod_get_optional(&client->dev,
+ "reset", GPIOD_OUT_HIGH);
+
+ if (IS_ERR(data->reset_gpio))
+ return dev_err_probe(&client->dev, PTR_ERR(data->reset_gpio),
+ "Failed to get reset GPIO\n");
+
+ /* Perform hardware reset if GPIO is provided */
+ if (data->reset_gpio) {
+ /* Perform reset sequence: low-high */
+ gpiod_set_value_cansleep(data->reset_gpio, 1);
+ fsleep(BH1750_RESET_DELAY_US);
+ gpiod_set_value_cansleep(data->reset_gpio, 0);
+ fsleep(BH1750_RESET_DELAY_US);
+
+ dev_dbg(&client->dev, "BH1750 reset completed via GPIO\n");
+ }
+
usec = data->chip_info->mtreg_to_usec * data->chip_info->mtreg_default;
ret = bh1750_change_int_time(data, usec);
if (ret < 0)
diff --git a/drivers/iio/light/bh1780.c b/drivers/iio/light/bh1780.c
index b84166c5fa06..c7c877d2fe67 100644
--- a/drivers/iio/light/bh1780.c
+++ b/drivers/iio/light/bh1780.c
@@ -256,15 +256,15 @@ static DEFINE_RUNTIME_DEV_PM_OPS(bh1780_dev_pm_ops, bh1780_runtime_suspend,
bh1780_runtime_resume, NULL);
static const struct i2c_device_id bh1780_id[] = {
- { "bh1780", 0 },
- { },
+ { "bh1780" },
+ { }
};
MODULE_DEVICE_TABLE(i2c, bh1780_id);
static const struct of_device_id of_bh1780_match[] = {
{ .compatible = "rohm,bh1780gli", },
- {},
+ { }
};
MODULE_DEVICE_TABLE(of, of_bh1780_match);
diff --git a/drivers/iio/light/cm32181.c b/drivers/iio/light/cm32181.c
index 9df85b3999fa..bb90f738312a 100644
--- a/drivers/iio/light/cm32181.c
+++ b/drivers/iio/light/cm32181.c
@@ -217,8 +217,7 @@ static int cm32181_reg_init(struct cm32181_chip *cm32181)
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);
+ cm32181_acpi_parse_cpm_tables(cm32181);
/* Initialize registers*/
for_each_set_bit(i, &cm32181->init_regs_bitmap, CM32181_CONF_REG_NUM) {
@@ -493,7 +492,7 @@ static int cm32181_probe(struct i2c_client *client)
ret = devm_iio_device_register(dev, indio_dev);
if (ret) {
- dev_err(dev, "%s: regist device failed\n", __func__);
+ dev_err(dev, "%s: register device failed\n", __func__);
return ret;
}
diff --git a/drivers/iio/light/cm3232.c b/drivers/iio/light/cm3232.c
index d48a70efca69..e864d2ef036e 100644
--- a/drivers/iio/light/cm3232.c
+++ b/drivers/iio/light/cm3232.c
@@ -89,6 +89,15 @@ static int cm3232_reg_init(struct cm3232_chip *chip)
chip->als_info = &cm3232_als_info_default;
+ /* Disable and reset device */
+ chip->regs_cmd = CM3232_CMD_ALS_DISABLE | CM3232_CMD_ALS_RESET;
+ ret = i2c_smbus_write_byte_data(client, CM3232_REG_ADDR_CMD,
+ chip->regs_cmd);
+ if (ret < 0) {
+ dev_err(&chip->client->dev, "Error writing reg_cmd\n");
+ return ret;
+ }
+
/* Identify device */
ret = i2c_smbus_read_word_data(client, CM3232_REG_ADDR_ID);
if (ret < 0) {
@@ -99,15 +108,6 @@ static int cm3232_reg_init(struct cm3232_chip *chip)
if ((ret & 0xFF) != chip->als_info->hw_id)
return -ENODEV;
- /* Disable and reset device */
- chip->regs_cmd = CM3232_CMD_ALS_DISABLE | CM3232_CMD_ALS_RESET;
- ret = i2c_smbus_write_byte_data(client, CM3232_REG_ADDR_CMD,
- chip->regs_cmd);
- if (ret < 0) {
- dev_err(&chip->client->dev, "Error writing reg_cmd\n");
- return ret;
- }
-
/* Register default value */
chip->regs_cmd = chip->als_info->regs_cmd_default;
@@ -368,8 +368,8 @@ static void cm3232_remove(struct i2c_client *client)
}
static const struct i2c_device_id cm3232_id[] = {
- {"cm3232", 0},
- {}
+ { "cm3232" },
+ { }
};
static int cm3232_suspend(struct device *dev)
@@ -406,7 +406,7 @@ MODULE_DEVICE_TABLE(i2c, cm3232_id);
static const struct of_device_id cm3232_of_match[] = {
{.compatible = "capella,cm3232"},
- {}
+ { }
};
MODULE_DEVICE_TABLE(of, cm3232_of_match);
diff --git a/drivers/iio/light/cm3323.c b/drivers/iio/light/cm3323.c
index 35d20207a648..79ad6e2209ca 100644
--- a/drivers/iio/light/cm3323.c
+++ b/drivers/iio/light/cm3323.c
@@ -250,14 +250,14 @@ static int cm3323_probe(struct i2c_client *client)
}
static const struct i2c_device_id cm3323_id[] = {
- {"cm3323", 0},
- {}
+ { "cm3323" },
+ { }
};
MODULE_DEVICE_TABLE(i2c, cm3323_id);
static const struct of_device_id cm3323_of_match[] = {
{ .compatible = "capella,cm3323", },
- { /* sentinel */ }
+ { }
};
MODULE_DEVICE_TABLE(of, cm3323_of_match);
diff --git a/drivers/iio/light/cm3605.c b/drivers/iio/light/cm3605.c
index 22a63a89f289..0c17378e27d1 100644
--- a/drivers/iio/light/cm3605.c
+++ b/drivers/iio/light/cm3605.c
@@ -307,7 +307,7 @@ static DEFINE_SIMPLE_DEV_PM_OPS(cm3605_dev_pm_ops, cm3605_pm_suspend,
static const struct of_device_id cm3605_of_match[] = {
{.compatible = "capella,cm3605"},
- { },
+ { }
};
MODULE_DEVICE_TABLE(of, cm3605_of_match);
@@ -318,7 +318,7 @@ static struct platform_driver cm3605_driver = {
.pm = pm_sleep_ptr(&cm3605_dev_pm_ops),
},
.probe = cm3605_probe,
- .remove_new = cm3605_remove,
+ .remove = cm3605_remove,
};
module_platform_driver(cm3605_driver);
diff --git a/drivers/iio/light/cm36651.c b/drivers/iio/light/cm36651.c
index 97e559acba2b..446dd54d5037 100644
--- a/drivers/iio/light/cm36651.c
+++ b/drivers/iio/light/cm36651.c
@@ -529,7 +529,7 @@ static int cm36651_write_prox_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)
+ bool state)
{
struct cm36651_data *cm36651 = iio_priv(indio_dev);
int cmd, ret;
@@ -683,7 +683,7 @@ static int cm36651_probe(struct i2c_client *client)
ret = iio_device_register(indio_dev);
if (ret) {
- dev_err(&client->dev, "%s: regist device failed\n", __func__);
+ dev_err(&client->dev, "%s: register device failed\n", __func__);
goto error_free_irq;
}
@@ -713,7 +713,7 @@ static void cm36651_remove(struct i2c_client *client)
}
static const struct i2c_device_id cm36651_id[] = {
- { "cm36651", 0 },
+ { "cm36651" },
{ }
};
diff --git a/drivers/iio/light/cros_ec_light_prox.c b/drivers/iio/light/cros_ec_light_prox.c
index 19e529c84e95..815806ceb5c8 100644
--- a/drivers/iio/light/cros_ec_light_prox.c
+++ b/drivers/iio/light/cros_ec_light_prox.c
@@ -249,7 +249,7 @@ static const struct platform_device_id cros_ec_light_prox_ids[] = {
{
.name = "cros-ec-light",
},
- { /* sentinel */ }
+ { }
};
MODULE_DEVICE_TABLE(platform, cros_ec_light_prox_ids);
diff --git a/drivers/iio/light/gp2ap002.c b/drivers/iio/light/gp2ap002.c
index fec10d5e037e..42859e5b1089 100644
--- a/drivers/iio/light/gp2ap002.c
+++ b/drivers/iio/light/gp2ap002.c
@@ -340,7 +340,7 @@ 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)
+ bool state)
{
struct gp2ap002 *gp2ap002 = iio_priv(indio_dev);
@@ -420,7 +420,7 @@ static int gp2ap002_regmap_i2c_write(void *context, unsigned int reg,
return i2c_smbus_write_byte_data(i2c, reg, val);
}
-static struct regmap_bus gp2ap002_regmap_bus = {
+static const struct regmap_bus gp2ap002_regmap_bus = {
.reg_read = gp2ap002_regmap_i2c_read,
.reg_write = gp2ap002_regmap_i2c_write,
};
@@ -692,15 +692,15 @@ static DEFINE_RUNTIME_DEV_PM_OPS(gp2ap002_dev_pm_ops, gp2ap002_runtime_suspend,
gp2ap002_runtime_resume, NULL);
static const struct i2c_device_id gp2ap002_id_table[] = {
- { "gp2ap002", 0 },
- { },
+ { "gp2ap002" },
+ { }
};
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);
diff --git a/drivers/iio/light/gp2ap020a00f.c b/drivers/iio/light/gp2ap020a00f.c
index 9f41724819b6..c7df4b258e2c 100644
--- a/drivers/iio/light/gp2ap020a00f.c
+++ b/drivers/iio/light/gp2ap020a00f.c
@@ -43,7 +43,7 @@
#include <linux/regmap.h>
#include <linux/regulator/consumer.h>
#include <linux/slab.h>
-#include <asm/unaligned.h>
+#include <linux/unaligned.h>
#include <linux/iio/buffer.h>
#include <linux/iio/events.h>
#include <linux/iio/iio.h>
@@ -237,7 +237,6 @@ enum gp2ap020a00f_thresh_val_id {
};
struct gp2ap020a00f_data {
- const struct gp2ap020a00f_platform_data *pdata;
struct i2c_client *client;
struct mutex lock;
char *buffer;
@@ -966,8 +965,7 @@ static irqreturn_t gp2ap020a00f_trigger_handler(int irq, void *data)
size_t d_size = 0;
int i, out_val, ret;
- for_each_set_bit(i, indio_dev->active_scan_mask,
- indio_dev->masklength) {
+ iio_for_each_active_channel(indio_dev, i) {
ret = regmap_bulk_read(priv->regmap,
GP2AP020A00F_DATA_REG(i),
&priv->buffer[d_size], 2);
@@ -1161,7 +1159,7 @@ static int gp2ap020a00f_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)
+ bool state)
{
struct gp2ap020a00f_data *data = iio_priv(indio_dev);
enum gp2ap020a00f_cmd cmd;
@@ -1285,12 +1283,11 @@ static int gp2ap020a00f_read_raw(struct iio_dev *indio_dev,
int err = -EINVAL;
if (mask == IIO_CHAN_INFO_RAW) {
- err = iio_device_claim_direct_mode(indio_dev);
- if (err)
- return err;
+ if (!iio_device_claim_direct(indio_dev))
+ return -EBUSY;
err = gp2ap020a00f_read_channel(data, chan, val);
- iio_device_release_direct_mode(indio_dev);
+ iio_device_release_direct(indio_dev);
}
return err < 0 ? err : IIO_VAL_INT;
}
@@ -1398,8 +1395,7 @@ static int gp2ap020a00f_buffer_postenable(struct iio_dev *indio_dev)
* two separate IIO channels they are treated in the driver logic
* as if they were controlled independently.
*/
- for_each_set_bit(i, indio_dev->active_scan_mask,
- indio_dev->masklength) {
+ iio_for_each_active_channel(indio_dev, i) {
switch (i) {
case GP2AP020A00F_SCAN_MODE_LIGHT_CLEAR:
err = gp2ap020a00f_exec_cmd(data,
@@ -1436,8 +1432,7 @@ static int gp2ap020a00f_buffer_predisable(struct iio_dev *indio_dev)
mutex_lock(&data->lock);
- for_each_set_bit(i, indio_dev->active_scan_mask,
- indio_dev->masklength) {
+ iio_for_each_active_channel(indio_dev, i) {
switch (i) {
case GP2AP020A00F_SCAN_MODE_LIGHT_CLEAR:
err = gp2ap020a00f_exec_cmd(data,
@@ -1592,7 +1587,7 @@ static void gp2ap020a00f_remove(struct i2c_client *client)
}
static const struct i2c_device_id gp2ap020a00f_id[] = {
- { GP2A_I2C_NAME, 0 },
+ { GP2A_I2C_NAME },
{ }
};
diff --git a/drivers/iio/light/hid-sensor-als.c b/drivers/iio/light/hid-sensor-als.c
index 260281194f61..830e5ae7f34a 100644
--- a/drivers/iio/light/hid-sensor-als.c
+++ b/drivers/iio/light/hid-sensor-als.c
@@ -31,7 +31,7 @@ struct als_state {
struct iio_chan_spec channels[CHANNEL_SCAN_INDEX_MAX + 1];
struct {
u32 illum[CHANNEL_SCAN_INDEX_MAX];
- u64 timestamp __aligned(8);
+ aligned_s64 timestamp;
} scan;
int scale_pre_decml;
int scale_post_decml;
@@ -356,11 +356,11 @@ static int als_parse_report(struct platform_device *pdev,
/* Function to initialize the processing for usage id */
static int hid_als_probe(struct platform_device *pdev)
{
+ struct hid_sensor_hub_device *hsdev = dev_get_platdata(&pdev->dev);
int ret = 0;
static const char *name = "als";
struct iio_dev *indio_dev;
struct als_state *als_state;
- struct hid_sensor_hub_device *hsdev = pdev->dev.platform_data;
indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(struct als_state));
if (!indio_dev)
@@ -438,7 +438,7 @@ error_remove_trigger:
/* Function to deinitialize the processing for usage id */
static void hid_als_remove(struct platform_device *pdev)
{
- struct hid_sensor_hub_device *hsdev = pdev->dev.platform_data;
+ struct hid_sensor_hub_device *hsdev = dev_get_platdata(&pdev->dev);
struct iio_dev *indio_dev = platform_get_drvdata(pdev);
struct als_state *als_state = iio_priv(indio_dev);
@@ -456,7 +456,7 @@ static const struct platform_device_id hid_als_ids[] = {
/* Format: HID-SENSOR-custom_sensor_tag-usage_id_in_hex_lowercase */
.name = "HID-SENSOR-LISS-0041",
},
- { /* sentinel */ }
+ { }
};
MODULE_DEVICE_TABLE(platform, hid_als_ids);
@@ -467,11 +467,11 @@ static struct platform_driver hid_als_platform_driver = {
.pm = &hid_sensor_pm_ops,
},
.probe = hid_als_probe,
- .remove_new = hid_als_remove,
+ .remove = hid_als_remove,
};
module_platform_driver(hid_als_platform_driver);
MODULE_DESCRIPTION("HID Sensor ALS");
MODULE_AUTHOR("Srinivas Pandruvada <srinivas.pandruvada@intel.com>");
MODULE_LICENSE("GPL");
-MODULE_IMPORT_NS(IIO_HID);
+MODULE_IMPORT_NS("IIO_HID");
diff --git a/drivers/iio/light/hid-sensor-prox.c b/drivers/iio/light/hid-sensor-prox.c
index 26c481d2998c..efa904a70d0e 100644
--- a/drivers/iio/light/hid-sensor-prox.c
+++ b/drivers/iio/light/hid-sensor-prox.c
@@ -13,16 +13,32 @@
#include <linux/iio/buffer.h>
#include "../common/hid-sensors/hid-sensor-trigger.h"
-#define CHANNEL_SCAN_INDEX_PRESENCE 0
+static const u32 prox_usage_ids[] = {
+ HID_USAGE_SENSOR_HUMAN_PRESENCE,
+ HID_USAGE_SENSOR_HUMAN_PROXIMITY,
+ HID_USAGE_SENSOR_HUMAN_ATTENTION,
+};
+
+#define MAX_CHANNELS ARRAY_SIZE(prox_usage_ids)
+
+enum {
+ HID_HUMAN_PRESENCE,
+ HID_HUMAN_PROXIMITY,
+ HID_HUMAN_ATTENTION,
+};
struct prox_state {
struct hid_sensor_hub_callbacks callbacks;
struct hid_sensor_common common_attributes;
- struct hid_sensor_hub_attribute_info prox_attr;
- u32 human_presence;
- int scale_pre_decml;
- int scale_post_decml;
- int scale_precision;
+ struct hid_sensor_hub_attribute_info prox_attr[MAX_CHANNELS];
+ struct iio_chan_spec channels[MAX_CHANNELS];
+ u32 channel2usage[MAX_CHANNELS];
+ u32 human_presence[MAX_CHANNELS];
+ int scale_pre_decml[MAX_CHANNELS];
+ int scale_post_decml[MAX_CHANNELS];
+ int scale_precision[MAX_CHANNELS];
+ unsigned long scan_mask[2]; /* One entry plus one terminator. */
+ int num_channels;
};
static const u32 prox_sensitivity_addresses[] = {
@@ -30,17 +46,25 @@ static const u32 prox_sensitivity_addresses[] = {
HID_USAGE_SENSOR_DATA_PRESENCE,
};
-/* Channel definitions */
-static const struct iio_chan_spec prox_channels[] = {
- {
- .type = IIO_PROXIMITY,
- .info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
- .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_OFFSET) |
- BIT(IIO_CHAN_INFO_SCALE) |
- BIT(IIO_CHAN_INFO_SAMP_FREQ) |
- BIT(IIO_CHAN_INFO_HYSTERESIS),
- .scan_index = CHANNEL_SCAN_INDEX_PRESENCE,
+#define PROX_CHANNEL(_is_proximity, _channel) \
+ {\
+ .type = _is_proximity ? IIO_PROXIMITY : IIO_ATTENTION,\
+ .info_mask_separate = \
+ (_is_proximity ? BIT(IIO_CHAN_INFO_RAW) :\
+ BIT(IIO_CHAN_INFO_PROCESSED)) |\
+ BIT(IIO_CHAN_INFO_OFFSET) |\
+ BIT(IIO_CHAN_INFO_SCALE) |\
+ BIT(IIO_CHAN_INFO_SAMP_FREQ) |\
+ BIT(IIO_CHAN_INFO_HYSTERESIS),\
+ .indexed = _is_proximity,\
+ .channel = _channel,\
}
+
+/* Channel definitions (same order as prox_usage_ids) */
+static const struct iio_chan_spec prox_channels[] = {
+ PROX_CHANNEL(true, HID_HUMAN_PRESENCE),
+ PROX_CHANNEL(true, HID_HUMAN_PROXIMITY),
+ PROX_CHANNEL(false, 0),
};
/* Adjust channel real bits based on report descriptor */
@@ -62,7 +86,7 @@ static int prox_read_raw(struct iio_dev *indio_dev,
{
struct prox_state *prox_state = iio_priv(indio_dev);
struct hid_sensor_hub_device *hsdev;
- int report_id = -1;
+ int report_id;
u32 address;
int ret_type;
s32 min;
@@ -71,39 +95,36 @@ static int prox_read_raw(struct iio_dev *indio_dev,
*val2 = 0;
switch (mask) {
case IIO_CHAN_INFO_RAW:
- switch (chan->scan_index) {
- case CHANNEL_SCAN_INDEX_PRESENCE:
- report_id = prox_state->prox_attr.report_id;
- min = prox_state->prox_attr.logical_minimum;
- address = HID_USAGE_SENSOR_HUMAN_PRESENCE;
- hsdev = prox_state->common_attributes.hsdev;
- break;
- default:
- report_id = -1;
- break;
- }
- if (report_id >= 0) {
- hid_sensor_power_state(&prox_state->common_attributes,
- true);
- *val = sensor_hub_input_attr_get_raw_value(
- hsdev, hsdev->usage, address, report_id,
- SENSOR_HUB_SYNC, min < 0);
- hid_sensor_power_state(&prox_state->common_attributes,
- false);
- } else {
- *val = 0;
+ case IIO_CHAN_INFO_PROCESSED:
+ if (chan->scan_index >= prox_state->num_channels)
return -EINVAL;
- }
+ address = prox_state->channel2usage[chan->scan_index];
+ report_id = prox_state->prox_attr[chan->scan_index].report_id;
+ hsdev = prox_state->common_attributes.hsdev;
+ min = prox_state->prox_attr[chan->scan_index].logical_minimum;
+ hid_sensor_power_state(&prox_state->common_attributes, true);
+ *val = sensor_hub_input_attr_get_raw_value(hsdev,
+ hsdev->usage,
+ address,
+ report_id,
+ SENSOR_HUB_SYNC,
+ min < 0);
+ if (prox_state->channel2usage[chan->scan_index] ==
+ HID_USAGE_SENSOR_HUMAN_ATTENTION)
+ *val *= 100;
+ hid_sensor_power_state(&prox_state->common_attributes, false);
ret_type = IIO_VAL_INT;
break;
case IIO_CHAN_INFO_SCALE:
- *val = prox_state->scale_pre_decml;
- *val2 = prox_state->scale_post_decml;
- ret_type = prox_state->scale_precision;
+ if (chan->scan_index >= prox_state->num_channels)
+ return -EINVAL;
+
+ *val = prox_state->scale_pre_decml[chan->scan_index];
+ *val2 = prox_state->scale_post_decml[chan->scan_index];
+ ret_type = prox_state->scale_precision[chan->scan_index];
break;
case IIO_CHAN_INFO_OFFSET:
- *val = hid_sensor_convert_exponent(
- prox_state->prox_attr.unit_expo);
+ *val = 0;
ret_type = IIO_VAL_INT;
break;
case IIO_CHAN_INFO_SAMP_FREQ:
@@ -153,14 +174,6 @@ static const struct iio_info prox_info = {
.write_raw = &prox_write_raw,
};
-/* Function to push data to buffer */
-static void hid_sensor_push_data(struct iio_dev *indio_dev, const void *data,
- int len)
-{
- dev_dbg(&indio_dev->dev, "hid_sensor_push_data\n");
- iio_push_to_buffers(indio_dev, data);
-}
-
/* Callback handler to send event after all samples are received and captured */
static int prox_proc_event(struct hid_sensor_hub_device *hsdev,
unsigned usage_id,
@@ -170,10 +183,10 @@ static int prox_proc_event(struct hid_sensor_hub_device *hsdev,
struct prox_state *prox_state = iio_priv(indio_dev);
dev_dbg(&indio_dev->dev, "prox_proc_event\n");
- if (atomic_read(&prox_state->common_attributes.data_ready))
- hid_sensor_push_data(indio_dev,
- &prox_state->human_presence,
- sizeof(prox_state->human_presence));
+ if (atomic_read(&prox_state->common_attributes.data_ready)) {
+ dev_dbg(&indio_dev->dev, "hid_sensor_push_data\n");
+ iio_push_to_buffers(indio_dev, &prox_state->human_presence);
+ }
return 0;
}
@@ -186,58 +199,84 @@ static int prox_capture_sample(struct hid_sensor_hub_device *hsdev,
{
struct iio_dev *indio_dev = platform_get_drvdata(priv);
struct prox_state *prox_state = iio_priv(indio_dev);
- int ret = -EINVAL;
-
- switch (usage_id) {
- case HID_USAGE_SENSOR_HUMAN_PRESENCE:
- switch (raw_len) {
- case 1:
- prox_state->human_presence = *(u8 *)raw_data;
- return 0;
- case 4:
- prox_state->human_presence = *(u32 *)raw_data;
- return 0;
- default:
+ int multiplier = 1;
+ int chan;
+
+ for (chan = 0; chan < prox_state->num_channels; chan++)
+ if (prox_state->channel2usage[chan] == usage_id)
break;
- }
- break;
+ if (chan == prox_state->num_channels)
+ return -EINVAL;
+
+ if (usage_id == HID_USAGE_SENSOR_HUMAN_ATTENTION)
+ multiplier = 100;
+
+ switch (raw_len) {
+ case 1:
+ prox_state->human_presence[chan] = *(u8 *)raw_data * multiplier;
+ return 0;
+ case 2:
+ prox_state->human_presence[chan] = *(u16 *)raw_data * multiplier;
+ return 0;
+ case 4:
+ prox_state->human_presence[chan] = *(u32 *)raw_data * multiplier;
+ return 0;
}
- return ret;
+ return -EINVAL;
}
/* Parse report which is specific to an usage id*/
static int prox_parse_report(struct platform_device *pdev,
struct hid_sensor_hub_device *hsdev,
- struct iio_chan_spec *channels,
- unsigned usage_id,
struct prox_state *st)
{
+ struct iio_chan_spec *channels = st->channels;
+ int index = 0;
int ret;
+ int i;
+
+ for (i = 0; i < MAX_CHANNELS; i++) {
+ u32 usage_id = prox_usage_ids[i];
+
+ ret = sensor_hub_input_get_attribute_info(hsdev,
+ HID_INPUT_REPORT,
+ hsdev->usage,
+ usage_id,
+ &st->prox_attr[index]);
+ if (ret < 0)
+ continue;
+ st->channel2usage[index] = usage_id;
+ st->scan_mask[0] |= BIT(index);
+ channels[index] = prox_channels[i];
+ channels[index].scan_index = index;
+ prox_adjust_channel_bit_mask(channels, index,
+ st->prox_attr[index].size);
+ dev_dbg(&pdev->dev, "prox %x:%x\n", st->prox_attr[index].index,
+ st->prox_attr[index].report_id);
+ st->scale_precision[index] =
+ hid_sensor_format_scale(usage_id, &st->prox_attr[index],
+ &st->scale_pre_decml[index],
+ &st->scale_post_decml[index]);
+ index++;
+ }
- ret = sensor_hub_input_get_attribute_info(hsdev, HID_INPUT_REPORT,
- usage_id,
- HID_USAGE_SENSOR_HUMAN_PRESENCE,
- &st->prox_attr);
- if (ret < 0)
+ if (!index)
return ret;
- prox_adjust_channel_bit_mask(channels, CHANNEL_SCAN_INDEX_PRESENCE,
- st->prox_attr.size);
- dev_dbg(&pdev->dev, "prox %x:%x\n", st->prox_attr.index,
- st->prox_attr.report_id);
+ st->num_channels = index;
- return ret;
+ return 0;
}
/* Function to initialize the processing for usage id */
static int hid_prox_probe(struct platform_device *pdev)
{
+ struct hid_sensor_hub_device *hsdev = dev_get_platdata(&pdev->dev);
int ret = 0;
static const char *name = "prox";
struct iio_dev *indio_dev;
struct prox_state *prox_state;
- struct hid_sensor_hub_device *hsdev = pdev->dev.platform_data;
indio_dev = devm_iio_device_alloc(&pdev->dev,
sizeof(struct prox_state));
@@ -258,22 +297,15 @@ static int hid_prox_probe(struct platform_device *pdev)
return ret;
}
- indio_dev->channels = devm_kmemdup(&pdev->dev, prox_channels,
- sizeof(prox_channels), GFP_KERNEL);
- if (!indio_dev->channels) {
- dev_err(&pdev->dev, "failed to duplicate channels\n");
- return -ENOMEM;
- }
-
- ret = prox_parse_report(pdev, hsdev,
- (struct iio_chan_spec *)indio_dev->channels,
- hsdev->usage, prox_state);
+ ret = prox_parse_report(pdev, hsdev, prox_state);
if (ret) {
dev_err(&pdev->dev, "failed to setup attributes\n");
return ret;
}
- indio_dev->num_channels = ARRAY_SIZE(prox_channels);
+ indio_dev->num_channels = prox_state->num_channels;
+ indio_dev->channels = prox_state->channels;
+ indio_dev->available_scan_masks = prox_state->scan_mask;
indio_dev->info = &prox_info;
indio_dev->name = name;
indio_dev->modes = INDIO_DIRECT_MODE;
@@ -315,7 +347,7 @@ error_remove_trigger:
/* Function to deinitialize the processing for usage id */
static void hid_prox_remove(struct platform_device *pdev)
{
- struct hid_sensor_hub_device *hsdev = pdev->dev.platform_data;
+ struct hid_sensor_hub_device *hsdev = dev_get_platdata(&pdev->dev);
struct iio_dev *indio_dev = platform_get_drvdata(pdev);
struct prox_state *prox_state = iio_priv(indio_dev);
@@ -333,7 +365,7 @@ static const struct platform_device_id hid_prox_ids[] = {
/* Format: HID-SENSOR-tag-usage_id_in_hex_lowercase */
.name = "HID-SENSOR-LISS-0226",
},
- { /* sentinel */ }
+ { }
};
MODULE_DEVICE_TABLE(platform, hid_prox_ids);
@@ -344,11 +376,11 @@ static struct platform_driver hid_prox_platform_driver = {
.pm = &hid_sensor_pm_ops,
},
.probe = hid_prox_probe,
- .remove_new = hid_prox_remove,
+ .remove = hid_prox_remove,
};
module_platform_driver(hid_prox_platform_driver);
MODULE_DESCRIPTION("HID Sensor Proximity");
MODULE_AUTHOR("Archana Patni <archana.patni@intel.com>");
MODULE_LICENSE("GPL");
-MODULE_IMPORT_NS(IIO_HID);
+MODULE_IMPORT_NS("IIO_HID");
diff --git a/drivers/iio/light/iqs621-als.c b/drivers/iio/light/iqs621-als.c
index 004ea890a4b2..b9f230210f07 100644
--- a/drivers/iio/light/iqs621-als.c
+++ b/drivers/iio/light/iqs621-als.c
@@ -86,8 +86,8 @@ static int iqs621_als_init(struct iqs621_als_private *iqs621_als)
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);
+ return regmap_clear_bits(iqs62x->regmap, IQS620_GLBL_EVENT_MASK,
+ event_mask);
}
static int iqs621_als_notifier(struct notifier_block *notifier,
@@ -271,7 +271,7 @@ 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)
+ bool state)
{
struct iqs621_als_private *iqs621_als = iio_priv(indio_dev);
struct iqs62x_core *iqs62x = iqs621_als->iqs62x;
diff --git a/drivers/iio/light/isl29018.c b/drivers/iio/light/isl29018.c
index 43484c18b101..1b4c18423048 100644
--- a/drivers/iio/light/isl29018.c
+++ b/drivers/iio/light/isl29018.c
@@ -8,17 +8,18 @@
* Copyright (c) 2010, NVIDIA Corporation.
*/
-#include <linux/module.h>
#include <linux/i2c.h>
#include <linux/err.h>
+#include <linux/mod_devicetable.h>
+#include <linux/module.h>
#include <linux/mutex.h>
#include <linux/delay.h>
#include <linux/regmap.h>
#include <linux/regulator/consumer.h>
#include <linux/slab.h>
+
#include <linux/iio/iio.h>
#include <linux/iio/sysfs.h>
-#include <linux/acpi.h>
#define ISL29018_CONV_TIME_MS 100
@@ -550,9 +551,9 @@ static int isl29018_chip_init(struct isl29018_chip *chip)
return -ENODEV;
/* Clear brownout bit */
- status = regmap_update_bits(chip->regmap,
- ISL29035_REG_DEVICE_ID,
- ISL29035_BOUT_MASK, 0);
+ status = regmap_clear_bits(chip->regmap,
+ ISL29035_REG_DEVICE_ID,
+ ISL29035_BOUT_MASK);
if (status < 0)
return status;
}
@@ -687,20 +688,6 @@ static const struct isl29018_chip_info isl29018_chip_info_tbl[] = {
},
};
-static const char *isl29018_match_acpi_device(struct device *dev, int *data)
-{
- const struct acpi_device_id *id;
-
- id = acpi_match_device(dev->driver->acpi_match_table, dev);
-
- if (!id)
- return NULL;
-
- *data = (int)id->driver_data;
-
- return dev_name(dev);
-}
-
static void isl29018_disable_regulator_action(void *_data)
{
struct isl29018_chip *chip = _data;
@@ -716,9 +703,10 @@ static int isl29018_probe(struct i2c_client *client)
const struct i2c_device_id *id = i2c_client_get_device_id(client);
struct isl29018_chip *chip;
struct iio_dev *indio_dev;
+ const void *ddata = NULL;
+ const char *name;
+ int dev_id;
int err;
- const char *name = NULL;
- int dev_id = 0;
indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*chip));
if (!indio_dev)
@@ -731,11 +719,11 @@ static int isl29018_probe(struct i2c_client *client)
if (id) {
name = id->name;
dev_id = id->driver_data;
+ } else {
+ name = iio_get_acpi_device_name_and_data(&client->dev, &ddata);
+ dev_id = (intptr_t)ddata;
}
- if (ACPI_HANDLE(&client->dev))
- name = isl29018_match_acpi_device(&client->dev, &dev_id);
-
mutex_init(&chip->lock);
chip->type = dev_id;
@@ -832,21 +820,19 @@ static int isl29018_resume(struct device *dev)
static DEFINE_SIMPLE_DEV_PM_OPS(isl29018_pm_ops, isl29018_suspend,
isl29018_resume);
-#ifdef CONFIG_ACPI
static const struct acpi_device_id isl29018_acpi_match[] = {
{"ISL29018", isl29018},
{"ISL29023", isl29023},
{"ISL29035", isl29035},
- {},
+ { }
};
MODULE_DEVICE_TABLE(acpi, isl29018_acpi_match);
-#endif
static const struct i2c_device_id isl29018_id[] = {
{"isl29018", isl29018},
{"isl29023", isl29023},
{"isl29035", isl29035},
- {}
+ { }
};
MODULE_DEVICE_TABLE(i2c, isl29018_id);
@@ -854,14 +840,14 @@ static const struct of_device_id isl29018_of_match[] = {
{ .compatible = "isil,isl29018", },
{ .compatible = "isil,isl29023", },
{ .compatible = "isil,isl29035", },
- { },
+ { }
};
MODULE_DEVICE_TABLE(of, isl29018_of_match);
static struct i2c_driver isl29018_driver = {
.driver = {
.name = "isl29018",
- .acpi_match_table = ACPI_PTR(isl29018_acpi_match),
+ .acpi_match_table = isl29018_acpi_match,
.pm = pm_sleep_ptr(&isl29018_pm_ops),
.of_match_table = isl29018_of_match,
},
diff --git a/drivers/iio/light/isl29028.c b/drivers/iio/light/isl29028.c
index 5694683389be..609ebf0f7313 100644
--- a/drivers/iio/light/isl29028.c
+++ b/drivers/iio/light/isl29028.c
@@ -678,9 +678,9 @@ static DEFINE_RUNTIME_DEV_PM_OPS(isl29028_pm_ops, isl29028_suspend,
isl29028_resume, NULL);
static const struct i2c_device_id isl29028_id[] = {
- {"isl29028", 0},
- {"isl29030", 0},
- {}
+ { "isl29028" },
+ { "isl29030" },
+ { }
};
MODULE_DEVICE_TABLE(i2c, isl29028_id);
@@ -688,7 +688,7 @@ static const struct of_device_id isl29028_of_match[] = {
{ .compatible = "isl,isl29028", }, /* for backward compat., don't use */
{ .compatible = "isil,isl29028", },
{ .compatible = "isil,isl29030", },
- { },
+ { }
};
MODULE_DEVICE_TABLE(of, isl29028_of_match);
diff --git a/drivers/iio/light/isl29125.c b/drivers/iio/light/isl29125.c
index f1d3356d3369..6bc23b164cc5 100644
--- a/drivers/iio/light/isl29125.c
+++ b/drivers/iio/light/isl29125.c
@@ -54,7 +54,7 @@ struct isl29125_data {
/* Ensure timestamp is naturally aligned */
struct {
u16 chans[3];
- s64 timestamp __aligned(8);
+ aligned_s64 timestamp;
} scan;
};
@@ -131,11 +131,10 @@ static int isl29125_read_raw(struct iio_dev *indio_dev,
switch (mask) {
case IIO_CHAN_INFO_RAW:
- ret = iio_device_claim_direct_mode(indio_dev);
- if (ret)
- return ret;
+ if (!iio_device_claim_direct(indio_dev))
+ return -EBUSY;
ret = isl29125_read_data(data, chan->scan_index);
- iio_device_release_direct_mode(indio_dev);
+ iio_device_release_direct(indio_dev);
if (ret < 0)
return ret;
*val = ret;
@@ -181,8 +180,7 @@ static irqreturn_t isl29125_trigger_handler(int irq, void *p)
struct isl29125_data *data = iio_priv(indio_dev);
int i, j = 0;
- for_each_set_bit(i, indio_dev->active_scan_mask,
- indio_dev->masklength) {
+ iio_for_each_active_channel(indio_dev, i) {
int ret = i2c_smbus_read_word_data(data->client,
isl29125_regs[i].data);
if (ret < 0)
@@ -327,7 +325,7 @@ static DEFINE_SIMPLE_DEV_PM_OPS(isl29125_pm_ops, isl29125_suspend,
isl29125_resume);
static const struct i2c_device_id isl29125_id[] = {
- { "isl29125", 0 },
+ { "isl29125" },
{ }
};
MODULE_DEVICE_TABLE(i2c, isl29125_id);
diff --git a/drivers/iio/light/jsa1212.c b/drivers/iio/light/jsa1212.c
index 869196746045..fa4677c28931 100644
--- a/drivers/iio/light/jsa1212.c
+++ b/drivers/iio/light/jsa1212.c
@@ -424,12 +424,12 @@ static DEFINE_SIMPLE_DEV_PM_OPS(jsa1212_pm_ops, jsa1212_suspend,
static const struct acpi_device_id jsa1212_acpi_match[] = {
{"JSA1212", 0},
- { },
+ { }
};
MODULE_DEVICE_TABLE(acpi, jsa1212_acpi_match);
static const struct i2c_device_id jsa1212_id[] = {
- { JSA1212_DRIVER_NAME, 0 },
+ { JSA1212_DRIVER_NAME },
{ }
};
MODULE_DEVICE_TABLE(i2c, jsa1212_id);
diff --git a/drivers/iio/light/lm3533-als.c b/drivers/iio/light/lm3533-als.c
index 7800f7fa51b7..99f0b903018c 100644
--- a/drivers/iio/light/lm3533-als.c
+++ b/drivers/iio/light/lm3533-als.c
@@ -754,7 +754,7 @@ static int lm3533_als_set_resistor(struct lm3533_als *als, u8 val)
}
static int lm3533_als_setup(struct lm3533_als *als,
- struct lm3533_als_platform_data *pdata)
+ const struct lm3533_als_platform_data *pdata)
{
int ret;
@@ -828,8 +828,8 @@ static const struct iio_info lm3533_als_info = {
static int lm3533_als_probe(struct platform_device *pdev)
{
+ const struct lm3533_als_platform_data *pdata;
struct lm3533 *lm3533;
- struct lm3533_als_platform_data *pdata;
struct lm3533_als *als;
struct iio_dev *indio_dev;
int ret;
@@ -838,7 +838,7 @@ static int lm3533_als_probe(struct platform_device *pdev)
if (!lm3533)
return -EINVAL;
- pdata = pdev->dev.platform_data;
+ pdata = dev_get_platdata(&pdev->dev);
if (!pdata) {
dev_err(&pdev->dev, "no platform data\n");
return -EINVAL;
@@ -912,7 +912,7 @@ static struct platform_driver lm3533_als_driver = {
.name = "lm3533-als",
},
.probe = lm3533_als_probe,
- .remove_new = lm3533_als_remove,
+ .remove = lm3533_als_remove,
};
module_platform_driver(lm3533_als_driver);
diff --git a/drivers/iio/light/ltr390.c b/drivers/iio/light/ltr390.c
index fff1e899097d..ee59bbb8aa09 100644
--- a/drivers/iio/light/ltr390.c
+++ b/drivers/iio/light/ltr390.c
@@ -18,25 +18,48 @@
* - Interrupt support
*/
+#include <linux/bitfield.h>
+#include <linux/device.h>
#include <linux/i2c.h>
+#include <linux/irq.h>
+#include <linux/interrupt.h>
#include <linux/math.h>
#include <linux/module.h>
#include <linux/mutex.h>
#include <linux/regmap.h>
#include <linux/iio/iio.h>
-
-#include <asm/unaligned.h>
-
-#define LTR390_MAIN_CTRL 0x00
-#define LTR390_PART_ID 0x06
-#define LTR390_UVS_DATA 0x10
+#include <linux/iio/events.h>
+
+#include <linux/unaligned.h>
+
+#define LTR390_MAIN_CTRL 0x00
+#define LTR390_ALS_UVS_MEAS_RATE 0x04
+#define LTR390_ALS_UVS_GAIN 0x05
+#define LTR390_PART_ID 0x06
+#define LTR390_MAIN_STATUS 0x07
+#define LTR390_ALS_DATA 0x0D
+#define LTR390_UVS_DATA 0x10
+#define LTR390_INT_CFG 0x19
+#define LTR390_INT_PST 0x1A
+#define LTR390_THRESH_UP 0x21
+#define LTR390_THRESH_LOW 0x24
+
+#define LTR390_PART_NUMBER_ID 0xb
+#define LTR390_ALS_UVS_GAIN_MASK GENMASK(2, 0)
+#define LTR390_ALS_UVS_MEAS_RATE_MASK GENMASK(2, 0)
+#define LTR390_ALS_UVS_INT_TIME_MASK GENMASK(6, 4)
+#define LTR390_ALS_UVS_INT_TIME(x) FIELD_PREP(LTR390_ALS_UVS_INT_TIME_MASK, (x))
+#define LTR390_INT_PST_MASK GENMASK(7, 4)
+#define LTR390_INT_PST_VAL(x) FIELD_PREP(LTR390_INT_PST_MASK, (x))
#define LTR390_SW_RESET BIT(4)
#define LTR390_UVS_MODE BIT(3)
#define LTR390_SENSOR_ENABLE BIT(1)
+#define LTR390_LS_INT_EN BIT(2)
+#define LTR390_LS_INT_SEL_UVS BIT(5)
-#define LTR390_PART_NUMBER_ID 0xb
+#define LTR390_FRACTIONAL_PRECISION 100
/*
* At 20-bit resolution (integration time: 400ms) and 18x gain, 2300 counts of
@@ -55,11 +78,24 @@
*/
#define LTR390_WINDOW_FACTOR 1
+enum ltr390_mode {
+ LTR390_SET_ALS_MODE,
+ LTR390_SET_UVS_MODE,
+};
+
+enum ltr390_meas_rate {
+ LTR390_GET_FREQ,
+ LTR390_GET_PERIOD,
+};
+
struct ltr390_data {
struct regmap *regmap;
struct i2c_client *client;
/* Protects device from simulataneous reads */
struct mutex lock;
+ enum ltr390_mode mode;
+ int gain;
+ int int_time_us;
};
static const struct regmap_config ltr390_regmap_config = {
@@ -69,14 +105,24 @@ static const struct regmap_config ltr390_regmap_config = {
.val_bits = 8,
};
+/* Sampling frequency is in mili Hz and mili Seconds */
+static const int ltr390_samp_freq_table[][2] = {
+ [0] = { 40000, 25 },
+ [1] = { 20000, 50 },
+ [2] = { 10000, 100 },
+ [3] = { 5000, 200 },
+ [4] = { 2000, 500 },
+ [5] = { 1000, 1000 },
+ [6] = { 500, 2000 },
+ [7] = { 500, 2000 },
+};
+
static int ltr390_register_read(struct ltr390_data *data, u8 register_address)
{
struct device *dev = &data->client->dev;
int ret;
u8 recieve_buffer[3];
- guard(mutex)(&data->lock);
-
ret = regmap_bulk_read(data->regmap, register_address, recieve_buffer,
sizeof(recieve_buffer));
if (ret) {
@@ -87,6 +133,51 @@ static int ltr390_register_read(struct ltr390_data *data, u8 register_address)
return get_unaligned_le24(recieve_buffer);
}
+static int ltr390_set_mode(struct ltr390_data *data, enum ltr390_mode mode)
+{
+ int ret;
+
+ if (data->mode == mode)
+ return 0;
+
+ switch (mode) {
+ case LTR390_SET_ALS_MODE:
+ ret = regmap_clear_bits(data->regmap, LTR390_MAIN_CTRL, LTR390_UVS_MODE);
+ break;
+
+ case LTR390_SET_UVS_MODE:
+ ret = regmap_set_bits(data->regmap, LTR390_MAIN_CTRL, LTR390_UVS_MODE);
+ break;
+ }
+
+ if (ret)
+ return ret;
+
+ data->mode = mode;
+ return 0;
+}
+
+static int ltr390_counts_per_uvi(struct ltr390_data *data)
+{
+ const int orig_gain = 18;
+ const int orig_int_time = 400;
+
+ return DIV_ROUND_CLOSEST(23 * data->gain * data->int_time_us, 10 * orig_gain * orig_int_time);
+}
+
+static int ltr390_get_samp_freq_or_period(struct ltr390_data *data,
+ enum ltr390_meas_rate option)
+{
+ int ret, value;
+
+ ret = regmap_read(data->regmap, LTR390_ALS_UVS_MEAS_RATE, &value);
+ if (ret < 0)
+ return ret;
+ value = FIELD_GET(LTR390_ALS_UVS_MEAS_RATE_MASK, value);
+
+ return ltr390_samp_freq_table[value][option];
+}
+
static int ltr390_read_raw(struct iio_dev *iio_device,
struct iio_chan_spec const *chan, int *val,
int *val2, long mask)
@@ -94,17 +185,402 @@ static int ltr390_read_raw(struct iio_dev *iio_device,
int ret;
struct ltr390_data *data = iio_priv(iio_device);
+ guard(mutex)(&data->lock);
switch (mask) {
case IIO_CHAN_INFO_RAW:
- ret = ltr390_register_read(data, LTR390_UVS_DATA);
+ switch (chan->type) {
+ case IIO_UVINDEX:
+ ret = ltr390_set_mode(data, LTR390_SET_UVS_MODE);
+ if (ret < 0)
+ return ret;
+
+ ret = ltr390_register_read(data, LTR390_UVS_DATA);
+ if (ret < 0)
+ return ret;
+ break;
+
+ case IIO_LIGHT:
+ ret = ltr390_set_mode(data, LTR390_SET_ALS_MODE);
+ if (ret < 0)
+ return ret;
+
+ ret = ltr390_register_read(data, LTR390_ALS_DATA);
+ if (ret < 0)
+ return ret;
+ break;
+
+ default:
+ return -EINVAL;
+ }
+ *val = ret;
+ return IIO_VAL_INT;
+ case IIO_CHAN_INFO_SCALE:
+ switch (chan->type) {
+ case IIO_UVINDEX:
+ *val = LTR390_WINDOW_FACTOR * LTR390_FRACTIONAL_PRECISION;
+ *val2 = ltr390_counts_per_uvi(data);
+ return IIO_VAL_FRACTIONAL;
+
+ case IIO_LIGHT:
+ *val = LTR390_WINDOW_FACTOR * 6 * 100;
+ *val2 = data->gain * data->int_time_us;
+ return IIO_VAL_FRACTIONAL;
+
+ default:
+ return -EINVAL;
+ }
+
+ case IIO_CHAN_INFO_INT_TIME:
+ *val = data->int_time_us;
+ return IIO_VAL_INT;
+
+ case IIO_CHAN_INFO_SAMP_FREQ:
+ *val = ltr390_get_samp_freq_or_period(data, LTR390_GET_FREQ);
+ return IIO_VAL_INT;
+
+ default:
+ return -EINVAL;
+ }
+}
+
+/* integration time in us */
+static const int ltr390_int_time_map_us[] = { 400000, 200000, 100000, 50000, 25000, 12500 };
+static const int ltr390_gain_map[] = { 1, 3, 6, 9, 18 };
+static const int ltr390_freq_map[] = { 40000, 20000, 10000, 5000, 2000, 1000, 500, 500 };
+
+static const struct iio_event_spec ltr390_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) |
+ BIT(IIO_EV_INFO_PERIOD),
+ }
+};
+
+static const struct iio_chan_spec ltr390_channels[] = {
+ /* UV sensor */
+ {
+ .type = IIO_UVINDEX,
+ .scan_index = 0,
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE),
+ .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_INT_TIME) | BIT(IIO_CHAN_INFO_SAMP_FREQ),
+ .info_mask_shared_by_all_available = BIT(IIO_CHAN_INFO_INT_TIME) |
+ BIT(IIO_CHAN_INFO_SCALE) |
+ BIT(IIO_CHAN_INFO_SAMP_FREQ),
+ .event_spec = ltr390_event_spec,
+ .num_event_specs = ARRAY_SIZE(ltr390_event_spec),
+ },
+ /* ALS sensor */
+ {
+ .type = IIO_LIGHT,
+ .scan_index = 1,
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE),
+ .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_INT_TIME) | BIT(IIO_CHAN_INFO_SAMP_FREQ),
+ .info_mask_shared_by_all_available = BIT(IIO_CHAN_INFO_INT_TIME) |
+ BIT(IIO_CHAN_INFO_SCALE) |
+ BIT(IIO_CHAN_INFO_SAMP_FREQ),
+ .event_spec = ltr390_event_spec,
+ .num_event_specs = ARRAY_SIZE(ltr390_event_spec),
+ },
+};
+
+static int ltr390_set_gain(struct ltr390_data *data, int val)
+{
+ int ret, idx;
+
+ for (idx = 0; idx < ARRAY_SIZE(ltr390_gain_map); idx++) {
+ if (ltr390_gain_map[idx] != val)
+ continue;
+
+ guard(mutex)(&data->lock);
+ ret = regmap_update_bits(data->regmap,
+ LTR390_ALS_UVS_GAIN,
+ LTR390_ALS_UVS_GAIN_MASK, idx);
+ if (ret)
+ return ret;
+
+ data->gain = ltr390_gain_map[idx];
+ return 0;
+ }
+
+ return -EINVAL;
+}
+
+static int ltr390_set_int_time(struct ltr390_data *data, int val)
+{
+ int ret, idx;
+
+ for (idx = 0; idx < ARRAY_SIZE(ltr390_int_time_map_us); idx++) {
+ if (ltr390_int_time_map_us[idx] != val)
+ continue;
+
+ guard(mutex)(&data->lock);
+ ret = regmap_update_bits(data->regmap,
+ LTR390_ALS_UVS_MEAS_RATE,
+ LTR390_ALS_UVS_INT_TIME_MASK,
+ LTR390_ALS_UVS_INT_TIME(idx));
+ if (ret)
+ return ret;
+
+ data->int_time_us = ltr390_int_time_map_us[idx];
+ return 0;
+ }
+
+ return -EINVAL;
+}
+
+static int ltr390_set_samp_freq(struct ltr390_data *data, int val)
+{
+ int idx;
+
+ for (idx = 0; idx < ARRAY_SIZE(ltr390_samp_freq_table); idx++) {
+ if (ltr390_samp_freq_table[idx][0] != val)
+ continue;
+
+ guard(mutex)(&data->lock);
+ return regmap_update_bits(data->regmap,
+ LTR390_ALS_UVS_MEAS_RATE,
+ LTR390_ALS_UVS_MEAS_RATE_MASK, idx);
+ }
+
+ return -EINVAL;
+}
+
+static int ltr390_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_SCALE:
+ *length = ARRAY_SIZE(ltr390_gain_map);
+ *type = IIO_VAL_INT;
+ *vals = ltr390_gain_map;
+ return IIO_AVAIL_LIST;
+ case IIO_CHAN_INFO_INT_TIME:
+ *length = ARRAY_SIZE(ltr390_int_time_map_us);
+ *type = IIO_VAL_INT;
+ *vals = ltr390_int_time_map_us;
+ return IIO_AVAIL_LIST;
+ case IIO_CHAN_INFO_SAMP_FREQ:
+ *length = ARRAY_SIZE(ltr390_freq_map);
+ *type = IIO_VAL_INT;
+ *vals = ltr390_freq_map;
+ return IIO_AVAIL_LIST;
+ default:
+ return -EINVAL;
+ }
+}
+
+static int ltr390_write_raw(struct iio_dev *indio_dev, struct iio_chan_spec const *chan,
+ int val, int val2, long mask)
+{
+ struct ltr390_data *data = iio_priv(indio_dev);
+
+ switch (mask) {
+ case IIO_CHAN_INFO_SCALE:
+ if (val2 != 0)
+ return -EINVAL;
+
+ return ltr390_set_gain(data, val);
+
+ case IIO_CHAN_INFO_INT_TIME:
+ if (val2 != 0)
+ return -EINVAL;
+
+ return ltr390_set_int_time(data, val);
+
+ case IIO_CHAN_INFO_SAMP_FREQ:
+ if (val2 != 0)
+ return -EINVAL;
+
+ return ltr390_set_samp_freq(data, val);
+
+ default:
+ return -EINVAL;
+ }
+}
+
+static int ltr390_read_intr_prst(struct ltr390_data *data, int *val)
+{
+ int ret, prst, samp_period;
+
+ samp_period = ltr390_get_samp_freq_or_period(data, LTR390_GET_PERIOD);
+ ret = regmap_read(data->regmap, LTR390_INT_PST, &prst);
+ if (ret < 0)
+ return ret;
+ *val = prst * samp_period;
+
+ return IIO_VAL_INT;
+}
+
+static int ltr390_write_intr_prst(struct ltr390_data *data, int val)
+{
+ int ret, samp_period, new_val;
+
+ samp_period = ltr390_get_samp_freq_or_period(data, LTR390_GET_PERIOD);
+
+ /* persist period should be greater than or equal to samp period */
+ if (val < samp_period)
+ return -EINVAL;
+
+ new_val = DIV_ROUND_UP(val, samp_period);
+ if (new_val < 0 || new_val > 0x0f)
+ return -EINVAL;
+
+ guard(mutex)(&data->lock);
+ ret = regmap_update_bits(data->regmap,
+ LTR390_INT_PST,
+ LTR390_INT_PST_MASK,
+ LTR390_INT_PST_VAL(new_val));
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
+static int ltr390_read_threshold(struct iio_dev *indio_dev,
+ enum iio_event_direction dir,
+ int *val, int *val2)
+{
+ struct ltr390_data *data = iio_priv(indio_dev);
+ int ret;
+
+ switch (dir) {
+ case IIO_EV_DIR_RISING:
+ ret = ltr390_register_read(data, LTR390_THRESH_UP);
if (ret < 0)
return ret;
*val = ret;
return IIO_VAL_INT;
- case IIO_CHAN_INFO_SCALE:
- *val = LTR390_WINDOW_FACTOR;
- *val2 = LTR390_COUNTS_PER_UVI;
- return IIO_VAL_FRACTIONAL;
+
+ case IIO_EV_DIR_FALLING:
+ ret = ltr390_register_read(data, LTR390_THRESH_LOW);
+ if (ret < 0)
+ return ret;
+ *val = ret;
+ return IIO_VAL_INT;
+ default:
+ return -EINVAL;
+ }
+}
+
+static int ltr390_write_threshold(struct iio_dev *indio_dev,
+ enum iio_event_direction dir,
+ int val, int val2)
+{
+ struct ltr390_data *data = iio_priv(indio_dev);
+
+ guard(mutex)(&data->lock);
+ switch (dir) {
+ case IIO_EV_DIR_RISING:
+ return regmap_bulk_write(data->regmap, LTR390_THRESH_UP, &val, 3);
+
+ case IIO_EV_DIR_FALLING:
+ return regmap_bulk_write(data->regmap, LTR390_THRESH_LOW, &val, 3);
+
+ default:
+ return -EINVAL;
+ }
+}
+
+static int ltr390_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)
+{
+ switch (info) {
+ case IIO_EV_INFO_VALUE:
+ return ltr390_read_threshold(indio_dev, dir, val, val2);
+
+ case IIO_EV_INFO_PERIOD:
+ return ltr390_read_intr_prst(iio_priv(indio_dev), val);
+
+ default:
+ return -EINVAL;
+ }
+}
+
+static int ltr390_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)
+{
+ switch (info) {
+ case IIO_EV_INFO_VALUE:
+ if (val2 != 0)
+ return -EINVAL;
+
+ return ltr390_write_threshold(indio_dev, dir, val, val2);
+
+ case IIO_EV_INFO_PERIOD:
+ if (val2 != 0)
+ return -EINVAL;
+
+ return ltr390_write_intr_prst(iio_priv(indio_dev), val);
+
+ default:
+ return -EINVAL;
+ }
+}
+
+static int ltr390_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 ltr390_data *data = iio_priv(indio_dev);
+ int ret, status;
+
+ ret = regmap_read(data->regmap, LTR390_INT_CFG, &status);
+ if (ret < 0)
+ return ret;
+
+ return FIELD_GET(LTR390_LS_INT_EN, status);
+}
+
+static int ltr390_write_event_config(struct iio_dev *indio_dev,
+ const struct iio_chan_spec *chan,
+ enum iio_event_type type,
+ enum iio_event_direction dir,
+ bool state)
+{
+ struct ltr390_data *data = iio_priv(indio_dev);
+ int ret;
+
+ if (!state)
+ return regmap_clear_bits(data->regmap, LTR390_INT_CFG, LTR390_LS_INT_EN);
+
+ guard(mutex)(&data->lock);
+ ret = regmap_set_bits(data->regmap, LTR390_INT_CFG, LTR390_LS_INT_EN);
+ if (ret < 0)
+ return ret;
+
+ switch (chan->type) {
+ case IIO_LIGHT:
+ ret = ltr390_set_mode(data, LTR390_SET_ALS_MODE);
+ if (ret < 0)
+ return ret;
+
+ return regmap_clear_bits(data->regmap, LTR390_INT_CFG, LTR390_LS_INT_SEL_UVS);
+
+ case IIO_UVINDEX:
+ ret = ltr390_set_mode(data, LTR390_SET_UVS_MODE);
+ if (ret < 0)
+ return ret;
+
+ return regmap_set_bits(data->regmap, LTR390_INT_CFG, LTR390_LS_INT_SEL_UVS);
+
default:
return -EINVAL;
}
@@ -112,12 +588,45 @@ static int ltr390_read_raw(struct iio_dev *iio_device,
static const struct iio_info ltr390_info = {
.read_raw = ltr390_read_raw,
+ .write_raw = ltr390_write_raw,
+ .read_avail = ltr390_read_avail,
+ .read_event_value = ltr390_read_event_value,
+ .read_event_config = ltr390_read_event_config,
+ .write_event_value = ltr390_write_event_value,
+ .write_event_config = ltr390_write_event_config,
};
-static const struct iio_chan_spec ltr390_channel = {
- .type = IIO_UVINDEX,
- .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE)
-};
+static irqreturn_t ltr390_interrupt_handler(int irq, void *private)
+{
+ struct iio_dev *indio_dev = private;
+ struct ltr390_data *data = iio_priv(indio_dev);
+ int ret, status;
+
+ /* Reading the status register to clear the interrupt flag, Datasheet pg: 17*/
+ ret = regmap_read(data->regmap, LTR390_MAIN_STATUS, &status);
+ if (ret < 0)
+ return ret;
+
+ switch (data->mode) {
+ case LTR390_SET_ALS_MODE:
+ iio_push_event(indio_dev,
+ IIO_UNMOD_EVENT_CODE(IIO_LIGHT, 0,
+ IIO_EV_TYPE_THRESH,
+ IIO_EV_DIR_EITHER),
+ iio_get_time_ns(indio_dev));
+ break;
+
+ case LTR390_SET_UVS_MODE:
+ iio_push_event(indio_dev,
+ IIO_UNMOD_EVENT_CODE(IIO_UVINDEX, 0,
+ IIO_EV_TYPE_THRESH,
+ IIO_EV_DIR_EITHER),
+ iio_get_time_ns(indio_dev));
+ break;
+ }
+
+ return IRQ_HANDLED;
+}
static int ltr390_probe(struct i2c_client *client)
{
@@ -139,11 +648,18 @@ static int ltr390_probe(struct i2c_client *client)
"regmap initialization failed\n");
data->client = client;
+ /* default value of integration time from pg: 15 of the datasheet */
+ data->int_time_us = 100000;
+ /* default value of gain from pg: 16 of the datasheet */
+ data->gain = 3;
+ /* default mode for ltr390 is ALS mode */
+ data->mode = LTR390_SET_ALS_MODE;
+
mutex_init(&data->lock);
indio_dev->info = &ltr390_info;
- indio_dev->channels = &ltr390_channel;
- indio_dev->num_channels = 1;
+ indio_dev->channels = ltr390_channels;
+ indio_dev->num_channels = ARRAY_SIZE(ltr390_channels);
indio_dev->name = "ltr390";
ret = regmap_read(data->regmap, LTR390_PART_ID, &part_number);
@@ -161,23 +677,53 @@ static int ltr390_probe(struct i2c_client *client)
/* Wait for the registers to reset before proceeding */
usleep_range(1000, 2000);
- ret = regmap_set_bits(data->regmap, LTR390_MAIN_CTRL,
- LTR390_SENSOR_ENABLE | LTR390_UVS_MODE);
+ ret = regmap_set_bits(data->regmap, LTR390_MAIN_CTRL, LTR390_SENSOR_ENABLE);
if (ret)
return dev_err_probe(dev, ret, "failed to enable the sensor\n");
+ if (client->irq) {
+ ret = devm_request_threaded_irq(dev, client->irq,
+ NULL, ltr390_interrupt_handler,
+ IRQF_ONESHOT,
+ "ltr390_thresh_event",
+ indio_dev);
+ if (ret)
+ return dev_err_probe(dev, ret,
+ "request irq (%d) failed\n", client->irq);
+ }
+
return devm_iio_device_register(dev, indio_dev);
}
+static int ltr390_suspend(struct device *dev)
+{
+ struct iio_dev *indio_dev = dev_get_drvdata(dev);
+ struct ltr390_data *data = iio_priv(indio_dev);
+
+ return regmap_clear_bits(data->regmap, LTR390_MAIN_CTRL,
+ LTR390_SENSOR_ENABLE);
+}
+
+static int ltr390_resume(struct device *dev)
+{
+ struct iio_dev *indio_dev = dev_get_drvdata(dev);
+ struct ltr390_data *data = iio_priv(indio_dev);
+
+ return regmap_set_bits(data->regmap, LTR390_MAIN_CTRL,
+ LTR390_SENSOR_ENABLE);
+}
+
+static DEFINE_SIMPLE_DEV_PM_OPS(ltr390_pm_ops, ltr390_suspend, ltr390_resume);
+
static const struct i2c_device_id ltr390_id[] = {
{ "ltr390" },
- { /* Sentinel */ }
+ { }
};
MODULE_DEVICE_TABLE(i2c, ltr390_id);
static const struct of_device_id ltr390_of_table[] = {
{ .compatible = "liteon,ltr390" },
- { /* Sentinel */ }
+ { }
};
MODULE_DEVICE_TABLE(of, ltr390_of_table);
@@ -185,6 +731,7 @@ static struct i2c_driver ltr390_driver = {
.driver = {
.name = "ltr390",
.of_match_table = ltr390_of_table,
+ .pm = pm_sleep_ptr(&ltr390_pm_ops),
},
.probe = ltr390_probe,
.id_table = ltr390_id,
diff --git a/drivers/iio/light/ltr501.c b/drivers/iio/light/ltr501.c
index 8c516ede9116..8d8051cf6927 100644
--- a/drivers/iio/light/ltr501.c
+++ b/drivers/iio/light/ltr501.c
@@ -15,7 +15,6 @@
#include <linux/err.h>
#include <linux/delay.h>
#include <linux/regmap.h>
-#include <linux/acpi.h>
#include <linux/regulator/consumer.h>
#include <linux/iio/iio.h>
@@ -542,7 +541,7 @@ static const struct iio_chan_spec_ext_info ltr501_ext_info[] = {
.shared = IIO_SEPARATE,
.read = ltr501_read_near_level,
},
- { /* sentinel */ }
+ { }
};
static const struct iio_event_spec ltr501_als_event_spec[] = {
@@ -647,6 +646,36 @@ static const struct iio_chan_spec ltr301_channels[] = {
IIO_CHAN_SOFT_TIMESTAMP(2),
};
+static int ltr501_read_info_raw(struct ltr501_data *data,
+ struct iio_chan_spec const *chan,
+ int *val)
+{
+ __le16 buf[2];
+ int ret;
+
+ switch (chan->type) {
+ case IIO_INTENSITY:
+ mutex_lock(&data->lock_als);
+ ret = ltr501_read_als(data, buf);
+ mutex_unlock(&data->lock_als);
+ if (ret < 0)
+ return ret;
+ *val = le16_to_cpu(chan->address == LTR501_ALS_DATA1 ?
+ buf[0] : buf[1]);
+ return IIO_VAL_INT;
+ case IIO_PROXIMITY:
+ mutex_lock(&data->lock_ps);
+ ret = ltr501_read_ps(data);
+ mutex_unlock(&data->lock_ps);
+ if (ret < 0)
+ return ret;
+ *val = ret & LTR501_PS_DATA_MASK;
+ return IIO_VAL_INT;
+ default:
+ return -EINVAL;
+ }
+}
+
static int ltr501_read_raw(struct iio_dev *indio_dev,
struct iio_chan_spec const *chan,
int *val, int *val2, long mask)
@@ -659,14 +688,13 @@ static int ltr501_read_raw(struct iio_dev *indio_dev,
case IIO_CHAN_INFO_PROCESSED:
switch (chan->type) {
case IIO_LIGHT:
- ret = iio_device_claim_direct_mode(indio_dev);
- if (ret)
- return ret;
+ if (!iio_device_claim_direct(indio_dev))
+ return -EBUSY;
mutex_lock(&data->lock_als);
ret = ltr501_read_als(data, buf);
mutex_unlock(&data->lock_als);
- iio_device_release_direct_mode(indio_dev);
+ iio_device_release_direct(indio_dev);
if (ret < 0)
return ret;
*val = ltr501_calculate_lux(le16_to_cpu(buf[1]),
@@ -676,36 +704,12 @@ static int ltr501_read_raw(struct iio_dev *indio_dev,
return -EINVAL;
}
case IIO_CHAN_INFO_RAW:
- ret = iio_device_claim_direct_mode(indio_dev);
- if (ret)
- return ret;
+ if (!iio_device_claim_direct(indio_dev))
+ return -EBUSY;
- switch (chan->type) {
- case IIO_INTENSITY:
- mutex_lock(&data->lock_als);
- ret = ltr501_read_als(data, buf);
- mutex_unlock(&data->lock_als);
- if (ret < 0)
- break;
- *val = le16_to_cpu(chan->address == LTR501_ALS_DATA1 ?
- buf[0] : buf[1]);
- ret = IIO_VAL_INT;
- break;
- case IIO_PROXIMITY:
- mutex_lock(&data->lock_ps);
- ret = ltr501_read_ps(data);
- mutex_unlock(&data->lock_ps);
- if (ret < 0)
- break;
- *val = ret & LTR501_PS_DATA_MASK;
- ret = IIO_VAL_INT;
- break;
- default:
- ret = -EINVAL;
- break;
- }
+ ret = ltr501_read_info_raw(data, chan, val);
- iio_device_release_direct_mode(indio_dev);
+ iio_device_release_direct(indio_dev);
return ret;
case IIO_CHAN_INFO_SCALE:
@@ -757,18 +761,14 @@ static int ltr501_get_gain_index(const struct ltr501_gain *gain, int size,
return -1;
}
-static int ltr501_write_raw(struct iio_dev *indio_dev,
- struct iio_chan_spec const *chan,
- int val, int val2, long mask)
+static int __ltr501_write_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int val, int val2, long mask)
{
struct ltr501_data *data = iio_priv(indio_dev);
int i, ret, freq_val, freq_val2;
const struct ltr501_chip_info *info = data->chip_info;
- ret = iio_device_claim_direct_mode(indio_dev);
- if (ret)
- return ret;
-
switch (mask) {
case IIO_CHAN_INFO_SCALE:
switch (chan->type) {
@@ -776,53 +776,43 @@ static int ltr501_write_raw(struct iio_dev *indio_dev,
i = ltr501_get_gain_index(info->als_gain,
info->als_gain_tbl_size,
val, val2);
- if (i < 0) {
- ret = -EINVAL;
- break;
- }
+ if (i < 0)
+ return -EINVAL;
data->als_contr &= ~info->als_gain_mask;
data->als_contr |= i << info->als_gain_shift;
- ret = regmap_write(data->regmap, LTR501_ALS_CONTR,
- data->als_contr);
- break;
+ return regmap_write(data->regmap, LTR501_ALS_CONTR,
+ data->als_contr);
case IIO_PROXIMITY:
i = ltr501_get_gain_index(info->ps_gain,
info->ps_gain_tbl_size,
val, val2);
- if (i < 0) {
- ret = -EINVAL;
- break;
- }
+ if (i < 0)
+ return -EINVAL;
+
data->ps_contr &= ~LTR501_CONTR_PS_GAIN_MASK;
data->ps_contr |= i << LTR501_CONTR_PS_GAIN_SHIFT;
- ret = regmap_write(data->regmap, LTR501_PS_CONTR,
- data->ps_contr);
- break;
+ return regmap_write(data->regmap, LTR501_PS_CONTR,
+ data->ps_contr);
default:
- ret = -EINVAL;
- break;
+ return -EINVAL;
}
- break;
case IIO_CHAN_INFO_INT_TIME:
switch (chan->type) {
case IIO_INTENSITY:
- if (val != 0) {
- ret = -EINVAL;
- break;
- }
+ if (val != 0)
+ return -EINVAL;
+
mutex_lock(&data->lock_als);
ret = ltr501_set_it_time(data, val2);
mutex_unlock(&data->lock_als);
- break;
+ return ret;
default:
- ret = -EINVAL;
- break;
+ return -EINVAL;
}
- break;
case IIO_CHAN_INFO_SAMP_FREQ:
switch (chan->type) {
@@ -830,50 +820,61 @@ static int ltr501_write_raw(struct iio_dev *indio_dev,
ret = ltr501_als_read_samp_freq(data, &freq_val,
&freq_val2);
if (ret < 0)
- break;
+ return ret;
ret = ltr501_als_write_samp_freq(data, val, val2);
if (ret < 0)
- break;
+ return ret;
/* update persistence count when changing frequency */
ret = ltr501_write_intr_prst(data, chan->type,
0, data->als_period);
if (ret < 0)
- ret = ltr501_als_write_samp_freq(data, freq_val,
- freq_val2);
- break;
+ /* Do not ovewrite error */
+ ltr501_als_write_samp_freq(data, freq_val,
+ freq_val2);
+ return ret;
case IIO_PROXIMITY:
ret = ltr501_ps_read_samp_freq(data, &freq_val,
&freq_val2);
if (ret < 0)
- break;
+ return ret;
ret = ltr501_ps_write_samp_freq(data, val, val2);
if (ret < 0)
- break;
+ return ret;
/* update persistence count when changing frequency */
ret = ltr501_write_intr_prst(data, chan->type,
0, data->ps_period);
if (ret < 0)
- ret = ltr501_ps_write_samp_freq(data, freq_val,
- freq_val2);
- break;
+ /* Do not overwrite error */
+ ltr501_ps_write_samp_freq(data, freq_val,
+ freq_val2);
+ return ret;
default:
- ret = -EINVAL;
- break;
+ return -EINVAL;
}
- break;
-
default:
- ret = -EINVAL;
- break;
+ return -EINVAL;
}
+}
+
+static int ltr501_write_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int val, int val2, long mask)
+{
+ int ret;
+
+ if (!iio_device_claim_direct(indio_dev))
+ return -EBUSY;
+
+ ret = __ltr501_write_raw(indio_dev, chan, val, val2, mask);
+
+ iio_device_release_direct(indio_dev);
- iio_device_release_direct_mode(indio_dev);
return ret;
}
@@ -1078,15 +1079,11 @@ static int ltr501_read_event_config(struct iio_dev *indio_dev,
static int ltr501_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)
+ enum iio_event_direction dir, bool state)
{
struct ltr501_data *data = iio_priv(indio_dev);
int ret;
- /* only 1 and 0 are valid inputs */
- if (state != 1 && state != 0)
- return -EINVAL;
-
switch (chan->type) {
case IIO_INTENSITY:
mutex_lock(&data->lock_als);
@@ -1285,7 +1282,7 @@ static irqreturn_t ltr501_trigger_handler(int irq, void *p)
struct ltr501_data *data = iio_priv(indio_dev);
struct {
u16 channels[3];
- s64 ts __aligned(8);
+ aligned_s64 ts;
} scan;
__le16 als_buf[2];
u8 mask = 0;
@@ -1422,17 +1419,6 @@ static int ltr501_powerdown(struct ltr501_data *data)
data->ps_contr & ~LTR501_CONTR_ACTIVE);
}
-static const char *ltr501_match_acpi_device(struct device *dev, int *chip_idx)
-{
- const struct acpi_device_id *id;
-
- id = acpi_match_device(dev->driver->acpi_match_table, dev);
- if (!id)
- return NULL;
- *chip_idx = id->driver_data;
- return dev_name(dev);
-}
-
static int ltr501_probe(struct i2c_client *client)
{
const struct i2c_device_id *id = i2c_client_get_device_id(client);
@@ -1440,8 +1426,10 @@ static int ltr501_probe(struct i2c_client *client)
struct ltr501_data *data;
struct iio_dev *indio_dev;
struct regmap *regmap;
- int ret, partid, chip_idx = 0;
- const char *name = NULL;
+ const void *ddata = NULL;
+ int partid, chip_idx;
+ const char *name;
+ int ret;
indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data));
if (!indio_dev)
@@ -1523,11 +1511,12 @@ static int ltr501_probe(struct i2c_client *client)
if (id) {
name = id->name;
chip_idx = id->driver_data;
- } else if (ACPI_HANDLE(&client->dev)) {
- name = ltr501_match_acpi_device(&client->dev, &chip_idx);
} else {
- return -ENODEV;
+ name = iio_get_acpi_device_name_and_data(&client->dev, &ddata);
+ chip_idx = (intptr_t)ddata;
}
+ if (!name)
+ return -ENODEV;
data->chip_info = &ltr501_chip_info_tbl[chip_idx];
@@ -1610,10 +1599,10 @@ static int ltr501_resume(struct device *dev)
static DEFINE_SIMPLE_DEV_PM_OPS(ltr501_pm_ops, ltr501_suspend, ltr501_resume);
static const struct acpi_device_id ltr_acpi_match[] = {
- { "LTER0501", ltr501 },
- { "LTER0559", ltr559 },
{ "LTER0301", ltr301 },
- { },
+ /* https://www.catalog.update.microsoft.com/Search.aspx?q=lter0303 */
+ { "LTER0303", ltr303 },
+ { }
};
MODULE_DEVICE_TABLE(acpi, ltr_acpi_match);
@@ -1631,7 +1620,7 @@ static const struct of_device_id ltr501_of_match[] = {
{ .compatible = "liteon,ltr559", },
{ .compatible = "liteon,ltr301", },
{ .compatible = "liteon,ltr303", },
- {}
+ { }
};
MODULE_DEVICE_TABLE(of, ltr501_of_match);
diff --git a/drivers/iio/light/ltrf216a.c b/drivers/iio/light/ltrf216a.c
index 68dc48420a88..61f57a82b872 100644
--- a/drivers/iio/light/ltrf216a.c
+++ b/drivers/iio/light/ltrf216a.c
@@ -26,7 +26,7 @@
#include <linux/iio/iio.h>
-#include <asm/unaligned.h>
+#include <linux/unaligned.h>
#define LTRF216A_ALS_RESET_MASK BIT(4)
#define LTRF216A_ALS_DATA_STATUS BIT(3)
@@ -68,6 +68,13 @@ static const int ltrf216a_int_time_reg[][2] = {
{ 25, 0x40 },
};
+struct ltr_chip_info {
+ /* Chip contains CLEAR_DATA_0/1/2 registers at offset 0xa..0xc */
+ bool has_clear_data;
+ /* Lux calculation multiplier for ALS data */
+ int lux_multiplier;
+};
+
/*
* Window Factor is needed when the device is under Window glass
* with coated tinted ink. This is to compensate for the light loss
@@ -79,6 +86,7 @@ static const int ltrf216a_int_time_reg[][2] = {
struct ltrf216a_data {
struct regmap *regmap;
struct i2c_client *client;
+ const struct ltr_chip_info *info;
u32 int_time;
u16 int_time_fac;
u8 als_gain_fac;
@@ -246,7 +254,7 @@ static int ltrf216a_get_lux(struct ltrf216a_data *data)
ltrf216a_set_power_state(data, false);
- lux = greendata * 45 * LTRF216A_WIN_FAC;
+ lux = greendata * data->info->lux_multiplier * LTRF216A_WIN_FAC;
return lux;
}
@@ -334,15 +342,15 @@ static const struct iio_info ltrf216a_info = {
static bool ltrf216a_readable_reg(struct device *dev, unsigned int reg)
{
+ struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev));
+ struct ltrf216a_data *data = iio_priv(indio_dev);
+
switch (reg) {
case LTRF216A_MAIN_CTRL:
case LTRF216A_ALS_MEAS_RES:
case LTRF216A_ALS_GAIN:
case LTRF216A_PART_ID:
case LTRF216A_MAIN_STATUS:
- case LTRF216A_ALS_CLEAR_DATA_0:
- case LTRF216A_ALS_CLEAR_DATA_1:
- case LTRF216A_ALS_CLEAR_DATA_2:
case LTRF216A_ALS_DATA_0:
case LTRF216A_ALS_DATA_1:
case LTRF216A_ALS_DATA_2:
@@ -355,6 +363,10 @@ static bool ltrf216a_readable_reg(struct device *dev, unsigned int reg)
case LTRF216A_ALS_THRES_LOW_1:
case LTRF216A_ALS_THRES_LOW_2:
return true;
+ case LTRF216A_ALS_CLEAR_DATA_0:
+ case LTRF216A_ALS_CLEAR_DATA_1:
+ case LTRF216A_ALS_CLEAR_DATA_2:
+ return data->info->has_clear_data;
default:
return false;
}
@@ -382,15 +394,23 @@ static bool ltrf216a_writable_reg(struct device *dev, unsigned int reg)
static bool ltrf216a_volatile_reg(struct device *dev, unsigned int reg)
{
+ struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev));
+ struct ltrf216a_data *data = iio_priv(indio_dev);
+
switch (reg) {
case LTRF216A_MAIN_STATUS:
- case LTRF216A_ALS_CLEAR_DATA_0:
- case LTRF216A_ALS_CLEAR_DATA_1:
- case LTRF216A_ALS_CLEAR_DATA_2:
case LTRF216A_ALS_DATA_0:
case LTRF216A_ALS_DATA_1:
case LTRF216A_ALS_DATA_2:
return true;
+ /*
+ * If these registers are not present on a chip (like LTR-308),
+ * the missing registers are not considered volatile.
+ */
+ case LTRF216A_ALS_CLEAR_DATA_0:
+ case LTRF216A_ALS_CLEAR_DATA_1:
+ case LTRF216A_ALS_CLEAR_DATA_2:
+ return data->info->has_clear_data;
default:
return false;
}
@@ -433,6 +453,7 @@ static int ltrf216a_probe(struct i2c_client *client)
i2c_set_clientdata(client, indio_dev);
data->client = client;
+ data->info = i2c_get_match_data(client);
mutex_init(&data->lock);
@@ -520,16 +541,29 @@ cache_only:
static DEFINE_RUNTIME_DEV_PM_OPS(ltrf216a_pm_ops, ltrf216a_runtime_suspend,
ltrf216a_runtime_resume, NULL);
+static const struct ltr_chip_info ltr308_chip_info = {
+ .has_clear_data = false,
+ .lux_multiplier = 60,
+};
+
+static const struct ltr_chip_info ltrf216a_chip_info = {
+ .has_clear_data = true,
+ .lux_multiplier = 45,
+};
+
static const struct i2c_device_id ltrf216a_id[] = {
- { "ltrf216a" },
- {}
+ { "ltr308", .driver_data = (kernel_ulong_t)&ltr308_chip_info },
+ { "ltrf216a", .driver_data = (kernel_ulong_t)&ltrf216a_chip_info },
+ { }
};
MODULE_DEVICE_TABLE(i2c, ltrf216a_id);
static const struct of_device_id ltrf216a_of_match[] = {
- { .compatible = "liteon,ltrf216a" },
- { .compatible = "ltr,ltrf216a" },
- {}
+ { .compatible = "liteon,ltr308", .data = &ltr308_chip_info },
+ { .compatible = "liteon,ltrf216a", .data = &ltrf216a_chip_info },
+ /* For Valve's Steamdeck device, an ACPI platform using PRP0001 */
+ { .compatible = "ltr,ltrf216a", .data = &ltrf216a_chip_info },
+ { }
};
MODULE_DEVICE_TABLE(of, ltrf216a_of_match);
diff --git a/drivers/iio/light/lv0104cs.c b/drivers/iio/light/lv0104cs.c
index a5445d58fddf..916109ec3217 100644
--- a/drivers/iio/light/lv0104cs.c
+++ b/drivers/iio/light/lv0104cs.c
@@ -510,7 +510,7 @@ static int lv0104cs_probe(struct i2c_client *client)
}
static const struct i2c_device_id lv0104cs_id[] = {
- { "lv0104cs", 0 },
+ { "lv0104cs" },
{ }
};
MODULE_DEVICE_TABLE(i2c, lv0104cs_id);
diff --git a/drivers/iio/light/max44000.c b/drivers/iio/light/max44000.c
index 26b464b1b650..e8b767680133 100644
--- a/drivers/iio/light/max44000.c
+++ b/drivers/iio/light/max44000.c
@@ -78,7 +78,7 @@ struct max44000_data {
/* Ensure naturally aligned timestamp */
struct {
u16 channels[2];
- s64 ts __aligned(8);
+ aligned_s64 ts;
} scan;
};
@@ -598,7 +598,7 @@ static int max44000_probe(struct i2c_client *client)
}
static const struct i2c_device_id max44000_id[] = {
- {"max44000", 0},
+ { "max44000" },
{ }
};
MODULE_DEVICE_TABLE(i2c, max44000_id);
diff --git a/drivers/iio/light/max44009.c b/drivers/iio/light/max44009.c
index 61ce276e86f7..8cd7f5664e5b 100644
--- a/drivers/iio/light/max44009.c
+++ b/drivers/iio/light/max44009.c
@@ -422,7 +422,7 @@ static int max44009_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)
+ bool state)
{
struct max44009_data *data = iio_priv(indio_dev);
int ret;
@@ -534,7 +534,7 @@ static const struct of_device_id max44009_of_match[] = {
MODULE_DEVICE_TABLE(of, max44009_of_match);
static const struct i2c_device_id max44009_id[] = {
- { "max44009", 0 },
+ { "max44009" },
{ }
};
MODULE_DEVICE_TABLE(i2c, max44009_id);
diff --git a/drivers/iio/light/noa1305.c b/drivers/iio/light/noa1305.c
index 1574310020e3..25f63da70297 100644
--- a/drivers/iio/light/noa1305.c
+++ b/drivers/iio/light/noa1305.c
@@ -29,6 +29,7 @@
#define NOA1305_INTEGR_TIME_25MS 0x05
#define NOA1305_INTEGR_TIME_12_5MS 0x06
#define NOA1305_INTEGR_TIME_6_25MS 0x07
+#define NOA1305_INTEGR_TIME_MASK 0x07
#define NOA1305_REG_INT_SELECT 0x3
#define NOA1305_INT_SEL_ACTIVE_HIGH 0x01
#define NOA1305_INT_SEL_ACTIVE_LOW 0x02
@@ -43,12 +44,34 @@
#define NOA1305_DEVICE_ID 0x0519
#define NOA1305_DRIVER_NAME "noa1305"
+static int noa1305_scale_available[] = {
+ 100, 8 * 77, /* 800 ms */
+ 100, 4 * 77, /* 400 ms */
+ 100, 2 * 77, /* 200 ms */
+ 100, 1 * 77, /* 100 ms */
+ 1000, 5 * 77, /* 50 ms */
+ 10000, 25 * 77, /* 25 ms */
+ 100000, 125 * 77, /* 12.5 ms */
+ 1000000, 625 * 77, /* 6.25 ms */
+};
+
+static int noa1305_int_time_available[] = {
+ 0, 800000, /* 800 ms */
+ 0, 400000, /* 400 ms */
+ 0, 200000, /* 200 ms */
+ 0, 100000, /* 100 ms */
+ 0, 50000, /* 50 ms */
+ 0, 25000, /* 25 ms */
+ 0, 12500, /* 12.5 ms */
+ 0, 6250, /* 6.25 ms */
+};
+
struct noa1305_priv {
struct i2c_client *client;
struct regmap *regmap;
};
-static int noa1305_measure(struct noa1305_priv *priv)
+static int noa1305_measure(struct noa1305_priv *priv, int *val)
{
__le16 data;
int ret;
@@ -58,7 +81,9 @@ static int noa1305_measure(struct noa1305_priv *priv)
if (ret < 0)
return ret;
- return le16_to_cpu(data);
+ *val = le16_to_cpu(data);
+
+ return IIO_VAL_INT;
}
static int noa1305_scale(struct noa1305_priv *priv, int *val, int *val2)
@@ -76,91 +101,113 @@ static int noa1305_scale(struct noa1305_priv *priv, int *val, int *val2)
* Integration Constant = 7.7
* Integration Time in Seconds
*/
- switch (data) {
- case NOA1305_INTEGR_TIME_800MS:
- *val = 100;
- *val2 = 77 * 8;
- break;
- case NOA1305_INTEGR_TIME_400MS:
- *val = 100;
- *val2 = 77 * 4;
- break;
- case NOA1305_INTEGR_TIME_200MS:
- *val = 100;
- *val2 = 77 * 2;
- break;
- case NOA1305_INTEGR_TIME_100MS:
- *val = 100;
- *val2 = 77;
- break;
- case NOA1305_INTEGR_TIME_50MS:
- *val = 1000;
- *val2 = 77 * 5;
- break;
- case NOA1305_INTEGR_TIME_25MS:
- *val = 10000;
- *val2 = 77 * 25;
- break;
- case NOA1305_INTEGR_TIME_12_5MS:
- *val = 100000;
- *val2 = 77 * 125;
- break;
- case NOA1305_INTEGR_TIME_6_25MS:
- *val = 1000000;
- *val2 = 77 * 625;
- break;
- default:
- return -EINVAL;
- }
+ data &= NOA1305_INTEGR_TIME_MASK;
+ *val = noa1305_scale_available[2 * data + 0];
+ *val2 = noa1305_scale_available[2 * data + 1];
return IIO_VAL_FRACTIONAL;
}
+static int noa1305_int_time(struct noa1305_priv *priv, int *val, int *val2)
+{
+ int data;
+ int ret;
+
+ ret = regmap_read(priv->regmap, NOA1305_REG_INTEGRATION_TIME, &data);
+ if (ret < 0)
+ return ret;
+
+ data &= NOA1305_INTEGR_TIME_MASK;
+ *val = noa1305_int_time_available[2 * data + 0];
+ *val2 = noa1305_int_time_available[2 * data + 1];
+
+ return IIO_VAL_INT_PLUS_MICRO;
+}
+
static const struct iio_chan_spec noa1305_channels[] = {
{
.type = IIO_LIGHT,
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE),
+ .info_mask_shared_by_type_available = BIT(IIO_CHAN_INFO_SCALE),
+ .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_INT_TIME),
+ .info_mask_shared_by_all_available = BIT(IIO_CHAN_INFO_INT_TIME),
}
};
+static int noa1305_read_avail(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ const int **vals, int *type,
+ int *length, long mask)
+{
+ if (chan->type != IIO_LIGHT)
+ return -EINVAL;
+
+ switch (mask) {
+ case IIO_CHAN_INFO_SCALE:
+ *vals = noa1305_scale_available;
+ *length = ARRAY_SIZE(noa1305_scale_available);
+ *type = IIO_VAL_FRACTIONAL;
+ return IIO_AVAIL_LIST;
+ case IIO_CHAN_INFO_INT_TIME:
+ *vals = noa1305_int_time_available;
+ *length = ARRAY_SIZE(noa1305_int_time_available);
+ *type = IIO_VAL_INT_PLUS_MICRO;
+ return IIO_AVAIL_LIST;
+ default:
+ return -EINVAL;
+ }
+}
+
static int noa1305_read_raw(struct iio_dev *indio_dev,
- struct iio_chan_spec const *chan,
- int *val, int *val2, long mask)
+ struct iio_chan_spec const *chan,
+ int *val, int *val2, long mask)
{
- int ret = -EINVAL;
struct noa1305_priv *priv = iio_priv(indio_dev);
+ if (chan->type != IIO_LIGHT)
+ return -EINVAL;
+
switch (mask) {
case IIO_CHAN_INFO_RAW:
- switch (chan->type) {
- case IIO_LIGHT:
- ret = noa1305_measure(priv);
- if (ret < 0)
- return ret;
- *val = ret;
- return IIO_VAL_INT;
- default:
- break;
- }
- break;
+ return noa1305_measure(priv, val);
case IIO_CHAN_INFO_SCALE:
- switch (chan->type) {
- case IIO_LIGHT:
- return noa1305_scale(priv, val, val2);
- default:
- break;
- }
- break;
+ return noa1305_scale(priv, val, val2);
+ case IIO_CHAN_INFO_INT_TIME:
+ return noa1305_int_time(priv, val, val2);
default:
- break;
+ return -EINVAL;
}
+}
- return ret;
+static int noa1305_write_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int val, int val2, long mask)
+{
+ struct noa1305_priv *priv = iio_priv(indio_dev);
+ int i;
+
+ if (chan->type != IIO_LIGHT)
+ return -EINVAL;
+
+ if (mask != IIO_CHAN_INFO_INT_TIME)
+ return -EINVAL;
+
+ if (val) /* >= 1s integration time not supported */
+ return -EINVAL;
+
+ /* Look up integration time register settings and write it if found. */
+ for (i = 0; i < ARRAY_SIZE(noa1305_int_time_available) / 2; i++)
+ if (noa1305_int_time_available[2 * i + 1] == val2)
+ return regmap_write(priv->regmap, NOA1305_REG_INTEGRATION_TIME, i);
+
+ return -EINVAL;
}
static const struct iio_info noa1305_info = {
+ .read_avail = noa1305_read_avail,
.read_raw = noa1305_read_raw,
+ .write_raw = noa1305_write_raw,
};
static bool noa1305_writable_reg(struct device *dev, unsigned int reg)
@@ -268,7 +315,7 @@ static const struct of_device_id noa1305_of_match[] = {
MODULE_DEVICE_TABLE(of, noa1305_of_match);
static const struct i2c_device_id noa1305_ids[] = {
- { "noa1305", 0 },
+ { "noa1305" },
{ }
};
MODULE_DEVICE_TABLE(i2c, noa1305_ids);
diff --git a/drivers/iio/light/opt3001.c b/drivers/iio/light/opt3001.c
index cb41e5ee8ec1..393a3d2fbe1d 100644
--- a/drivers/iio/light/opt3001.c
+++ b/drivers/iio/light/opt3001.c
@@ -70,6 +70,35 @@
#define OPT3001_RESULT_READY_SHORT 150
#define OPT3001_RESULT_READY_LONG 1000
+struct opt3001_scale {
+ int val;
+ int val2;
+};
+
+struct opt3001_chip_info {
+ const struct iio_chan_spec (*channels)[2];
+ enum iio_chan_type chan_type;
+ int num_channels;
+
+ const struct opt3001_scale (*scales)[12];
+ /*
+ * Factor as specified by conversion equation in datasheet.
+ * eg. 0.01 (scaled to integer 10) for opt3001.
+ */
+ int factor_whole;
+ /*
+ * Factor to compensate for potentially scaled factor_whole.
+ */
+ int factor_integer;
+ /*
+ * Factor used to align decimal part of proccessed value to six decimal
+ * places.
+ */
+ int factor_decimal;
+
+ bool has_id;
+};
+
struct opt3001 {
struct i2c_client *client;
struct device *dev;
@@ -79,6 +108,7 @@ struct opt3001 {
bool result_ready;
wait_queue_head_t result_ready_queue;
u16 result;
+ const struct opt3001_chip_info *chip_info;
u32 int_time;
u32 mode;
@@ -92,11 +122,6 @@ struct opt3001 {
bool use_irq;
};
-struct opt3001_scale {
- int val;
- int val2;
-};
-
static const struct opt3001_scale opt3001_scales[] = {
{
.val = 40,
@@ -139,26 +164,77 @@ static const struct opt3001_scale opt3001_scales[] = {
.val2 = 400000,
},
{
+ .val = 41932,
+ .val2 = 800000,
+ },
+ {
.val = 83865,
.val2 = 600000,
},
};
+static const struct opt3001_scale opt3002_scales[] = {
+ {
+ .val = 4914,
+ .val2 = 0,
+ },
+ {
+ .val = 9828,
+ .val2 = 0,
+ },
+ {
+ .val = 19656,
+ .val2 = 0,
+ },
+ {
+ .val = 39312,
+ .val2 = 0,
+ },
+ {
+ .val = 78624,
+ .val2 = 0,
+ },
+ {
+ .val = 157248,
+ .val2 = 0,
+ },
+ {
+ .val = 314496,
+ .val2 = 0,
+ },
+ {
+ .val = 628992,
+ .val2 = 0,
+ },
+ {
+ .val = 1257984,
+ .val2 = 0,
+ },
+ {
+ .val = 2515968,
+ .val2 = 0,
+ },
+ {
+ .val = 5031936,
+ .val2 = 0,
+ },
+ {
+ .val = 10063872,
+ .val2 = 0,
+ },
+};
+
static int opt3001_find_scale(const struct opt3001 *opt, int val,
int val2, u8 *exponent)
{
int i;
-
- for (i = 0; i < ARRAY_SIZE(opt3001_scales); i++) {
- const struct opt3001_scale *scale = &opt3001_scales[i];
-
+ for (i = 0; i < ARRAY_SIZE(*opt->chip_info->scales); i++) {
+ const struct opt3001_scale *scale = &(*opt->chip_info->scales)[i];
/*
- * Combine the integer and micro parts for comparison
- * purposes. Use milli lux precision to avoid 32-bit integer
- * overflows.
+ * Compare the integer and micro parts to determine value scale.
*/
- if ((val * 1000 + val2 / 1000) <=
- (scale->val * 1000 + scale->val2 / 1000)) {
+ if (val < scale->val ||
+ (val == scale->val && val2 <= scale->val2)) {
*exponent = i;
return 0;
}
@@ -170,11 +246,14 @@ static int opt3001_find_scale(const struct opt3001 *opt, int val,
static void opt3001_to_iio_ret(struct opt3001 *opt, u8 exponent,
u16 mantissa, int *val, int *val2)
{
- int lux;
+ int ret;
+ int whole = opt->chip_info->factor_whole;
+ int integer = opt->chip_info->factor_integer;
+ int decimal = opt->chip_info->factor_decimal;
- lux = 10 * (mantissa << exponent);
- *val = lux / 1000;
- *val2 = (lux - (*val * 1000)) * 1000;
+ ret = whole * (mantissa << exponent);
+ *val = ret / integer;
+ *val2 = (ret - (*val * integer)) * decimal;
}
static void opt3001_set_mode(struct opt3001 *opt, u16 *reg, u16 mode)
@@ -221,7 +300,18 @@ static const struct iio_chan_spec opt3001_channels[] = {
IIO_CHAN_SOFT_TIMESTAMP(1),
};
-static int opt3001_get_lux(struct opt3001 *opt, int *val, int *val2)
+static const struct iio_chan_spec opt3002_channels[] = {
+ {
+ .type = IIO_INTENSITY,
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
+ BIT(IIO_CHAN_INFO_INT_TIME),
+ .event_spec = opt3001_event_spec,
+ .num_event_specs = ARRAY_SIZE(opt3001_event_spec),
+ },
+ IIO_CHAN_SOFT_TIMESTAMP(1),
+};
+
+static int opt3001_get_processed(struct opt3001 *opt, int *val, int *val2)
{
int ret;
u16 mantissa;
@@ -393,14 +483,15 @@ static int opt3001_read_raw(struct iio_dev *iio,
if (opt->mode == OPT3001_CONFIGURATION_M_CONTINUOUS)
return -EBUSY;
- if (chan->type != IIO_LIGHT)
+ if (chan->type != opt->chip_info->chan_type)
return -EINVAL;
mutex_lock(&opt->lock);
switch (mask) {
+ case IIO_CHAN_INFO_RAW:
case IIO_CHAN_INFO_PROCESSED:
- ret = opt3001_get_lux(opt, val, val2);
+ ret = opt3001_get_processed(opt, val, val2);
break;
case IIO_CHAN_INFO_INT_TIME:
ret = opt3001_get_int_time(opt, val, val2);
@@ -424,7 +515,7 @@ static int opt3001_write_raw(struct iio_dev *iio,
if (opt->mode == OPT3001_CONFIGURATION_M_CONTINUOUS)
return -EBUSY;
- if (chan->type != IIO_LIGHT)
+ if (chan->type != opt->chip_info->chan_type)
return -EINVAL;
if (mask != IIO_CHAN_INFO_INT_TIME)
@@ -475,6 +566,9 @@ static int opt3001_write_event_value(struct iio_dev *iio,
{
struct opt3001 *opt = iio_priv(iio);
int ret;
+ int whole;
+ int integer;
+ int decimal;
u16 mantissa;
u16 value;
@@ -493,7 +587,12 @@ static int opt3001_write_event_value(struct iio_dev *iio,
goto err;
}
- mantissa = (((val * 1000) + (val2 / 1000)) / 10) >> exponent;
+ whole = opt->chip_info->factor_whole;
+ integer = opt->chip_info->factor_integer;
+ decimal = opt->chip_info->factor_decimal;
+
+ mantissa = (((val * integer) + (val2 / decimal)) / whole) >> exponent;
+
value = (exponent << 12) | mantissa;
switch (dir) {
@@ -535,7 +634,7 @@ static int opt3001_read_event_config(struct iio_dev *iio,
static int opt3001_write_event_config(struct iio_dev *iio,
const struct iio_chan_spec *chan, enum iio_event_type type,
- enum iio_event_direction dir, int state)
+ enum iio_event_direction dir, bool state)
{
struct opt3001 *opt = iio_priv(iio);
int ret;
@@ -606,7 +705,7 @@ static int opt3001_read_id(struct opt3001 *opt)
ret = i2c_smbus_read_word_swapped(opt->client, OPT3001_DEVICE_ID);
if (ret < 0) {
dev_err(opt->dev, "failed to read register %02x\n",
- OPT3001_DEVICE_ID);
+ OPT3001_DEVICE_ID);
return ret;
}
@@ -688,8 +787,10 @@ static irqreturn_t opt3001_irq(int irq, void *_iio)
struct opt3001 *opt = iio_priv(iio);
int ret;
bool wake_result_ready_queue = false;
+ enum iio_chan_type chan_type = opt->chip_info->chan_type;
+ bool ok_to_ignore_lock = opt->ok_to_ignore_lock;
- if (!opt->ok_to_ignore_lock)
+ if (!ok_to_ignore_lock)
mutex_lock(&opt->lock);
ret = i2c_smbus_read_word_swapped(opt->client, OPT3001_CONFIGURATION);
@@ -703,13 +804,13 @@ static irqreturn_t opt3001_irq(int irq, void *_iio)
OPT3001_CONFIGURATION_M_CONTINUOUS) {
if (ret & OPT3001_CONFIGURATION_FH)
iio_push_event(iio,
- IIO_UNMOD_EVENT_CODE(IIO_LIGHT, 0,
+ IIO_UNMOD_EVENT_CODE(chan_type, 0,
IIO_EV_TYPE_THRESH,
IIO_EV_DIR_RISING),
iio_get_time_ns(iio));
if (ret & OPT3001_CONFIGURATION_FL)
iio_push_event(iio,
- IIO_UNMOD_EVENT_CODE(IIO_LIGHT, 0,
+ IIO_UNMOD_EVENT_CODE(chan_type, 0,
IIO_EV_TYPE_THRESH,
IIO_EV_DIR_FALLING),
iio_get_time_ns(iio));
@@ -726,7 +827,7 @@ static irqreturn_t opt3001_irq(int irq, void *_iio)
}
out:
- if (!opt->ok_to_ignore_lock)
+ if (!ok_to_ignore_lock)
mutex_unlock(&opt->lock);
if (wake_result_ready_queue)
@@ -751,22 +852,25 @@ static int opt3001_probe(struct i2c_client *client)
opt = iio_priv(iio);
opt->client = client;
opt->dev = dev;
+ opt->chip_info = i2c_get_match_data(client);
mutex_init(&opt->lock);
init_waitqueue_head(&opt->result_ready_queue);
i2c_set_clientdata(client, iio);
- ret = opt3001_read_id(opt);
- if (ret)
- return ret;
+ if (opt->chip_info->has_id) {
+ ret = opt3001_read_id(opt);
+ if (ret)
+ return ret;
+ }
ret = opt3001_configure(opt);
if (ret)
return ret;
iio->name = client->name;
- iio->channels = opt3001_channels;
- iio->num_channels = ARRAY_SIZE(opt3001_channels);
+ iio->channels = *opt->chip_info->channels;
+ iio->num_channels = opt->chip_info->num_channels;
iio->modes = INDIO_DIRECT_MODE;
iio->info = &opt3001_info;
@@ -821,14 +925,38 @@ static void opt3001_remove(struct i2c_client *client)
}
}
+static const struct opt3001_chip_info opt3001_chip_information = {
+ .channels = &opt3001_channels,
+ .chan_type = IIO_LIGHT,
+ .num_channels = ARRAY_SIZE(opt3001_channels),
+ .scales = &opt3001_scales,
+ .factor_whole = 10,
+ .factor_integer = 1000,
+ .factor_decimal = 1000,
+ .has_id = true,
+};
+
+static const struct opt3001_chip_info opt3002_chip_information = {
+ .channels = &opt3002_channels,
+ .chan_type = IIO_INTENSITY,
+ .num_channels = ARRAY_SIZE(opt3002_channels),
+ .scales = &opt3002_scales,
+ .factor_whole = 12,
+ .factor_integer = 10,
+ .factor_decimal = 100000,
+ .has_id = false,
+};
+
static const struct i2c_device_id opt3001_id[] = {
- { "opt3001", 0 },
+ { "opt3001", (kernel_ulong_t)&opt3001_chip_information },
+ { "opt3002", (kernel_ulong_t)&opt3002_chip_information },
{ } /* Terminating Entry */
};
MODULE_DEVICE_TABLE(i2c, opt3001_id);
static const struct of_device_id opt3001_of_match[] = {
- { .compatible = "ti,opt3001" },
+ { .compatible = "ti,opt3001", .data = &opt3001_chip_information },
+ { .compatible = "ti,opt3002", .data = &opt3002_chip_information },
{ }
};
MODULE_DEVICE_TABLE(of, opt3001_of_match);
diff --git a/drivers/iio/light/opt4001.c b/drivers/iio/light/opt4001.c
index 6cf60151b3d8..ba4eb82d9bc2 100644
--- a/drivers/iio/light/opt4001.c
+++ b/drivers/iio/light/opt4001.c
@@ -448,7 +448,7 @@ MODULE_DEVICE_TABLE(i2c, opt4001_id);
static const struct of_device_id opt4001_of_match[] = {
{ .compatible = "ti,opt4001-sot-5x3", .data = &opt4001_sot_5x3_info},
{ .compatible = "ti,opt4001-picostar", .data = &opt4001_picostar_info},
- {}
+ { }
};
MODULE_DEVICE_TABLE(of, opt4001_of_match);
diff --git a/drivers/iio/light/opt4060.c b/drivers/iio/light/opt4060.c
new file mode 100644
index 000000000000..f4085020e03e
--- /dev/null
+++ b/drivers/iio/light/opt4060.c
@@ -0,0 +1,1344 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (C) 2024 Axis Communications AB
+ *
+ * Datasheet: https://www.ti.com/lit/gpn/opt4060
+ *
+ * Device driver for the Texas Instruments OPT4060 RGBW Color Sensor.
+ */
+
+#include <linux/bitfield.h>
+#include <linux/i2c.h>
+#include <linux/iio/iio.h>
+#include <linux/math64.h>
+#include <linux/units.h>
+#include <linux/limits.h>
+#include <linux/module.h>
+#include <linux/property.h>
+#include <linux/regmap.h>
+#include <linux/mutex.h>
+#include <linux/regulator/consumer.h>
+#include <linux/iio/events.h>
+#include <linux/iio/trigger.h>
+#include <linux/iio/trigger_consumer.h>
+#include <linux/iio/triggered_buffer.h>
+
+/* OPT4060 register set */
+#define OPT4060_RED_MSB 0x00
+#define OPT4060_RED_LSB 0x01
+#define OPT4060_GREEN_MSB 0x02
+#define OPT4060_GREEN_LSB 0x03
+#define OPT4060_BLUE_MSB 0x04
+#define OPT4060_BLUE_LSB 0x05
+#define OPT4060_CLEAR_MSB 0x06
+#define OPT4060_CLEAR_LSB 0x07
+#define OPT4060_THRESHOLD_LOW 0x08
+#define OPT4060_THRESHOLD_HIGH 0x09
+#define OPT4060_CTRL 0x0a
+#define OPT4060_INT_CTRL 0x0b
+#define OPT4060_RES_CTRL 0x0c
+#define OPT4060_DEVICE_ID 0x11
+
+/* OPT4060 register mask */
+#define OPT4060_EXPONENT_MASK GENMASK(15, 12)
+#define OPT4060_MSB_MASK GENMASK(11, 0)
+#define OPT4060_LSB_MASK GENMASK(15, 8)
+#define OPT4060_COUNTER_MASK GENMASK(7, 4)
+#define OPT4060_CRC_MASK GENMASK(3, 0)
+
+/* OPT4060 device id mask */
+#define OPT4060_DEVICE_ID_MASK GENMASK(11, 0)
+
+/* OPT4060 control register masks */
+#define OPT4060_CTRL_QWAKE_MASK BIT(15)
+#define OPT4060_CTRL_RANGE_MASK GENMASK(13, 10)
+#define OPT4060_CTRL_CONV_TIME_MASK GENMASK(9, 6)
+#define OPT4060_CTRL_OPER_MODE_MASK GENMASK(5, 4)
+#define OPT4060_CTRL_LATCH_MASK BIT(3)
+#define OPT4060_CTRL_INT_POL_MASK BIT(2)
+#define OPT4060_CTRL_FAULT_COUNT_MASK GENMASK(1, 0)
+
+/* OPT4060 interrupt control register masks */
+#define OPT4060_INT_CTRL_THRESH_SEL GENMASK(6, 5)
+#define OPT4060_INT_CTRL_OUTPUT BIT(4)
+#define OPT4060_INT_CTRL_INT_CFG GENMASK(3, 2)
+#define OPT4060_INT_CTRL_THRESHOLD 0x0
+#define OPT4060_INT_CTRL_NEXT_CH 0x1
+#define OPT4060_INT_CTRL_ALL_CH 0x3
+
+/* OPT4060 result control register masks */
+#define OPT4060_RES_CTRL_OVERLOAD BIT(3)
+#define OPT4060_RES_CTRL_CONV_READY BIT(2)
+#define OPT4060_RES_CTRL_FLAG_H BIT(1)
+#define OPT4060_RES_CTRL_FLAG_L BIT(0)
+
+/* OPT4060 constants */
+#define OPT4060_DEVICE_ID_VAL 0x821
+
+/* OPT4060 operating modes */
+#define OPT4060_CTRL_OPER_MODE_OFF 0x0
+#define OPT4060_CTRL_OPER_MODE_FORCED 0x1
+#define OPT4060_CTRL_OPER_MODE_ONE_SHOT 0x2
+#define OPT4060_CTRL_OPER_MODE_CONTINUOUS 0x3
+
+/* OPT4060 conversion control register definitions */
+#define OPT4060_CTRL_CONVERSION_0_6MS 0x0
+#define OPT4060_CTRL_CONVERSION_1MS 0x1
+#define OPT4060_CTRL_CONVERSION_1_8MS 0x2
+#define OPT4060_CTRL_CONVERSION_3_4MS 0x3
+#define OPT4060_CTRL_CONVERSION_6_5MS 0x4
+#define OPT4060_CTRL_CONVERSION_12_7MS 0x5
+#define OPT4060_CTRL_CONVERSION_25MS 0x6
+#define OPT4060_CTRL_CONVERSION_50MS 0x7
+#define OPT4060_CTRL_CONVERSION_100MS 0x8
+#define OPT4060_CTRL_CONVERSION_200MS 0x9
+#define OPT4060_CTRL_CONVERSION_400MS 0xa
+#define OPT4060_CTRL_CONVERSION_800MS 0xb
+
+/* OPT4060 fault count control register definitions */
+#define OPT4060_CTRL_FAULT_COUNT_1 0x0
+#define OPT4060_CTRL_FAULT_COUNT_2 0x1
+#define OPT4060_CTRL_FAULT_COUNT_4 0x2
+#define OPT4060_CTRL_FAULT_COUNT_8 0x3
+
+/* OPT4060 scale light level range definitions */
+#define OPT4060_CTRL_LIGHT_SCALE_AUTO 12
+
+/* OPT4060 default values */
+#define OPT4060_DEFAULT_CONVERSION_TIME OPT4060_CTRL_CONVERSION_50MS
+
+/*
+ * enum opt4060_chan_type - OPT4060 channel types
+ * @OPT4060_RED: Red channel.
+ * @OPT4060_GREEN: Green channel.
+ * @OPT4060_BLUE: Blue channel.
+ * @OPT4060_CLEAR: Clear (white) channel.
+ * @OPT4060_ILLUM: Calculated illuminance channel.
+ * @OPT4060_NUM_CHANS: Number of channel types.
+ */
+enum opt4060_chan_type {
+ OPT4060_RED,
+ OPT4060_GREEN,
+ OPT4060_BLUE,
+ OPT4060_CLEAR,
+ OPT4060_ILLUM,
+ OPT4060_NUM_CHANS
+};
+
+struct opt4060_chip {
+ struct regmap *regmap;
+ struct device *dev;
+ struct iio_trigger *trig;
+ u8 int_time;
+ int irq;
+ /*
+ * Mutex for protecting sensor irq settings. Switching between interrupt
+ * on each sample and on thresholds needs to be synchronized.
+ */
+ struct mutex irq_setting_lock;
+ /*
+ * Mutex for protecting event enabling.
+ */
+ struct mutex event_enabling_lock;
+ struct completion completion;
+ bool thresh_event_lo_active;
+ bool thresh_event_hi_active;
+};
+
+struct opt4060_channel_factor {
+ u32 mul;
+ u32 div;
+};
+
+static const int opt4060_int_time_available[][2] = {
+ { 0, 600 },
+ { 0, 1000 },
+ { 0, 1800 },
+ { 0, 3400 },
+ { 0, 6500 },
+ { 0, 12700 },
+ { 0, 25000 },
+ { 0, 50000 },
+ { 0, 100000 },
+ { 0, 200000 },
+ { 0, 400000 },
+ { 0, 800000 },
+};
+
+/*
+ * Conversion time is integration time + time to set register
+ * this is used as integration time.
+ */
+static const int opt4060_int_time_reg[][2] = {
+ { 600, OPT4060_CTRL_CONVERSION_0_6MS },
+ { 1000, OPT4060_CTRL_CONVERSION_1MS },
+ { 1800, OPT4060_CTRL_CONVERSION_1_8MS },
+ { 3400, OPT4060_CTRL_CONVERSION_3_4MS },
+ { 6500, OPT4060_CTRL_CONVERSION_6_5MS },
+ { 12700, OPT4060_CTRL_CONVERSION_12_7MS },
+ { 25000, OPT4060_CTRL_CONVERSION_25MS },
+ { 50000, OPT4060_CTRL_CONVERSION_50MS },
+ { 100000, OPT4060_CTRL_CONVERSION_100MS },
+ { 200000, OPT4060_CTRL_CONVERSION_200MS },
+ { 400000, OPT4060_CTRL_CONVERSION_400MS },
+ { 800000, OPT4060_CTRL_CONVERSION_800MS },
+};
+
+static int opt4060_als_time_to_index(const u32 als_integration_time)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(opt4060_int_time_available); i++) {
+ if (als_integration_time == opt4060_int_time_available[i][1])
+ return i;
+ }
+
+ return -EINVAL;
+}
+
+static u8 opt4060_calculate_crc(u8 exp, u32 mantissa, u8 count)
+{
+ u8 crc;
+
+ /*
+ * Calculates a 4-bit CRC from a 20-bit mantissa, 4-bit exponent and a 4-bit counter.
+ * crc[0] = XOR(mantissa[19:0], exp[3:0], count[3:0])
+ * crc[1] = XOR(mantissa[1,3,5,7,9,11,13,15,17,19], exp[1,3], count[1,3])
+ * crc[2] = XOR(mantissa[3,7,11,15,19], exp[3], count[3])
+ * crc[3] = XOR(mantissa[3,11,19])
+ */
+ crc = (hweight32(mantissa) + hweight32(exp) + hweight32(count)) % 2;
+ crc |= ((hweight32(mantissa & 0xAAAAA) + hweight32(exp & 0xA)
+ + hweight32(count & 0xA)) % 2) << 1;
+ crc |= ((hweight32(mantissa & 0x88888) + hweight32(exp & 0x8)
+ + hweight32(count & 0x8)) % 2) << 2;
+ crc |= (hweight32(mantissa & 0x80808) % 2) << 3;
+
+ return crc;
+}
+
+static int opt4060_set_int_state(struct opt4060_chip *chip, u32 state)
+{
+ int ret;
+ unsigned int regval;
+
+ guard(mutex)(&chip->irq_setting_lock);
+
+ regval = FIELD_PREP(OPT4060_INT_CTRL_INT_CFG, state);
+ ret = regmap_update_bits(chip->regmap, OPT4060_INT_CTRL,
+ OPT4060_INT_CTRL_INT_CFG, regval);
+ if (ret)
+ dev_err(chip->dev, "Failed to set interrupt config\n");
+ return ret;
+}
+
+static int opt4060_set_sampling_mode(struct opt4060_chip *chip,
+ bool continuous)
+{
+ unsigned int reg;
+ int ret;
+
+ ret = regmap_read(chip->regmap, OPT4060_CTRL, &reg);
+ if (ret < 0) {
+ dev_err(chip->dev, "Failed to read ctrl register\n");
+ return ret;
+ }
+ reg &= ~OPT4060_CTRL_OPER_MODE_MASK;
+ if (continuous)
+ reg |= FIELD_PREP(OPT4060_CTRL_OPER_MODE_MASK,
+ OPT4060_CTRL_OPER_MODE_CONTINUOUS);
+ else
+ reg |= FIELD_PREP(OPT4060_CTRL_OPER_MODE_MASK,
+ OPT4060_CTRL_OPER_MODE_ONE_SHOT);
+
+ /*
+ * Trigger a new conversions by writing to CRTL register. It is not
+ * possible to use regmap_update_bits() since that will only write when
+ * data is modified.
+ */
+ ret = regmap_write(chip->regmap, OPT4060_CTRL, reg);
+ if (ret)
+ dev_err(chip->dev, "Failed to set ctrl register\n");
+ return ret;
+}
+
+static bool opt4060_event_active(struct opt4060_chip *chip)
+{
+ return chip->thresh_event_lo_active || chip->thresh_event_hi_active;
+}
+
+static int opt4060_set_state_common(struct opt4060_chip *chip,
+ bool continuous_sampling,
+ bool continuous_irq)
+{
+ int ret = 0;
+
+ /* It is important to setup irq before sampling to avoid missing samples. */
+ if (continuous_irq)
+ ret = opt4060_set_int_state(chip, OPT4060_INT_CTRL_ALL_CH);
+ else
+ ret = opt4060_set_int_state(chip, OPT4060_INT_CTRL_THRESHOLD);
+ if (ret) {
+ dev_err(chip->dev, "Failed to set irq state.\n");
+ return ret;
+ }
+
+ if (continuous_sampling || opt4060_event_active(chip))
+ ret = opt4060_set_sampling_mode(chip, true);
+ else
+ ret = opt4060_set_sampling_mode(chip, false);
+ if (ret)
+ dev_err(chip->dev, "Failed to set sampling state.\n");
+ return ret;
+}
+
+/*
+ * Function for setting the driver state for sampling and irq. Either direct
+ * mode of buffer mode will be claimed during the transition to prevent races
+ * between sysfs read, buffer or events.
+ */
+static int opt4060_set_driver_state(struct iio_dev *indio_dev,
+ bool continuous_sampling,
+ bool continuous_irq)
+{
+ struct opt4060_chip *chip = iio_priv(indio_dev);
+ int ret = 0;
+any_mode_retry:
+ if (iio_device_claim_buffer_mode(indio_dev)) {
+ /*
+ * This one is a *bit* hacky. If we cannot claim buffer mode,
+ * then try direct mode so that we make sure things cannot
+ * concurrently change. And we just keep trying until we get one
+ * of the modes...
+ */
+ if (!iio_device_claim_direct(indio_dev))
+ goto any_mode_retry;
+ /*
+ * This path means that we managed to claim direct mode. In
+ * this case the buffer isn't enabled and it's okay to leave
+ * continuous mode for sampling and/or irq.
+ */
+ ret = opt4060_set_state_common(chip, continuous_sampling,
+ continuous_irq);
+ iio_device_release_direct(indio_dev);
+ return ret;
+ } else {
+ /*
+ * This path means that we managed to claim buffer mode. In
+ * this case the buffer is enabled and irq and sampling must go
+ * to or remain continuous, but only if the trigger is from this
+ * device.
+ */
+ if (!iio_trigger_validate_own_device(indio_dev->trig, indio_dev))
+ ret = opt4060_set_state_common(chip, true, true);
+ else
+ ret = opt4060_set_state_common(chip, continuous_sampling,
+ continuous_irq);
+ iio_device_release_buffer_mode(indio_dev);
+ }
+ return ret;
+}
+
+/*
+ * This function is called with framework mutex locked.
+ */
+static int opt4060_trigger_set_state(struct iio_trigger *trig, bool state)
+{
+ struct iio_dev *indio_dev = iio_trigger_get_drvdata(trig);
+ struct opt4060_chip *chip = iio_priv(indio_dev);
+
+ return opt4060_set_state_common(chip, state, state);
+}
+
+static int opt4060_read_raw_value(struct opt4060_chip *chip,
+ unsigned long address, u32 *raw)
+{
+ int ret;
+ u16 result[2];
+ u32 mantissa_raw;
+ u16 msb, lsb;
+ u8 exp, count, crc, calc_crc;
+
+ ret = regmap_bulk_read(chip->regmap, address, result, 2);
+ if (ret) {
+ dev_err(chip->dev, "Reading channel data failed\n");
+ return ret;
+ }
+ exp = FIELD_GET(OPT4060_EXPONENT_MASK, result[0]);
+ msb = FIELD_GET(OPT4060_MSB_MASK, result[0]);
+ count = FIELD_GET(OPT4060_COUNTER_MASK, result[1]);
+ crc = FIELD_GET(OPT4060_CRC_MASK, result[1]);
+ lsb = FIELD_GET(OPT4060_LSB_MASK, result[1]);
+ mantissa_raw = (msb << 8) + lsb;
+ calc_crc = opt4060_calculate_crc(exp, mantissa_raw, count);
+ if (calc_crc != crc)
+ return -EIO;
+ *raw = mantissa_raw << exp;
+ return 0;
+}
+
+static int opt4060_trigger_new_samples(struct iio_dev *indio_dev)
+{
+ struct opt4060_chip *chip = iio_priv(indio_dev);
+ int ret;
+
+ /*
+ * The conversion time should be 500us startup time plus the integration time
+ * times the number of channels. An exact timeout isn't critical, it's better
+ * not to get incorrect errors in the log. Setting the timeout to double the
+ * theoretical time plus and extra 100ms margin.
+ */
+ unsigned int timeout_us = (500 + OPT4060_NUM_CHANS *
+ opt4060_int_time_reg[chip->int_time][0]) * 2 + 100000;
+
+ /* Setting the state in one shot mode with irq on each sample. */
+ ret = opt4060_set_driver_state(indio_dev, false, true);
+ if (ret)
+ return ret;
+
+ if (chip->irq) {
+ guard(mutex)(&chip->irq_setting_lock);
+ reinit_completion(&chip->completion);
+ if (wait_for_completion_timeout(&chip->completion,
+ usecs_to_jiffies(timeout_us)) == 0) {
+ dev_err(chip->dev, "Completion timed out.\n");
+ return -ETIME;
+ }
+ } else {
+ unsigned int ready;
+
+ ret = regmap_read_poll_timeout(chip->regmap, OPT4060_RES_CTRL,
+ ready, (ready & OPT4060_RES_CTRL_CONV_READY),
+ 1000, timeout_us);
+ if (ret)
+ dev_err(chip->dev, "Conversion ready did not finish within timeout.\n");
+ }
+ /* Setting the state in one shot mode with irq on thresholds. */
+ return opt4060_set_driver_state(indio_dev, false, false);
+}
+
+static int opt4060_read_chan_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan, int *val)
+{
+ struct opt4060_chip *chip = iio_priv(indio_dev);
+ u32 adc_raw;
+ int ret;
+
+ ret = opt4060_trigger_new_samples(indio_dev);
+ if (ret) {
+ dev_err(chip->dev, "Failed to trigger new samples.\n");
+ return ret;
+ }
+
+ ret = opt4060_read_raw_value(chip, chan->address, &adc_raw);
+ if (ret) {
+ dev_err(chip->dev, "Reading raw channel data failed.\n");
+ return ret;
+ }
+ *val = adc_raw;
+ return IIO_VAL_INT;
+}
+
+/*
+ * Returns the scale values used for red, green and blue. Scales the raw value
+ * so that for a particular test light source, typically white, the measurement
+ * intensity is the same across different color channels.
+ */
+static int opt4060_get_chan_scale(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int *val, int *val2)
+{
+ struct opt4060_chip *chip = iio_priv(indio_dev);
+
+ switch (chan->scan_index) {
+ case OPT4060_RED:
+ /* 2.4 */
+ *val = 2;
+ *val2 = 400000;
+ break;
+ case OPT4060_GREEN:
+ /* 1.0 */
+ *val = 1;
+ *val2 = 0;
+ break;
+ case OPT4060_BLUE:
+ /* 1.3 */
+ *val = 1;
+ *val2 = 300000;
+ break;
+ default:
+ dev_err(chip->dev, "Unexpected channel index.\n");
+ return -EINVAL;
+ }
+ return IIO_VAL_INT_PLUS_MICRO;
+}
+
+static int opt4060_calc_illuminance(struct opt4060_chip *chip, int *val)
+{
+ u32 lux_raw;
+ int ret;
+
+ /* The green wide spectral channel is used for illuminance. */
+ ret = opt4060_read_raw_value(chip, OPT4060_GREEN_MSB, &lux_raw);
+ if (ret) {
+ dev_err(chip->dev, "Reading raw channel data failed\n");
+ return ret;
+ }
+
+ /* Illuminance is calculated by ADC_RAW * 2.15e-3. */
+ *val = DIV_U64_ROUND_CLOSEST((u64)(lux_raw * 215), 1000);
+ return ret;
+}
+
+static int opt4060_read_illuminance(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int *val)
+{
+ struct opt4060_chip *chip = iio_priv(indio_dev);
+ int ret;
+
+ ret = opt4060_trigger_new_samples(indio_dev);
+ if (ret) {
+ dev_err(chip->dev, "Failed to trigger new samples.\n");
+ return ret;
+ }
+ ret = opt4060_calc_illuminance(chip, val);
+ if (ret) {
+ dev_err(chip->dev, "Failed to calculate illuminance.\n");
+ return ret;
+ }
+
+ return IIO_VAL_INT;
+}
+
+static int opt4060_set_int_time(struct opt4060_chip *chip)
+{
+ unsigned int regval;
+ int ret;
+
+ regval = FIELD_PREP(OPT4060_CTRL_CONV_TIME_MASK, chip->int_time);
+ ret = regmap_update_bits(chip->regmap, OPT4060_CTRL,
+ OPT4060_CTRL_CONV_TIME_MASK, regval);
+ if (ret)
+ dev_err(chip->dev, "Failed to set integration time.\n");
+
+ return ret;
+}
+
+static int opt4060_power_down(struct opt4060_chip *chip)
+{
+ int ret;
+
+ ret = regmap_clear_bits(chip->regmap, OPT4060_CTRL, OPT4060_CTRL_OPER_MODE_MASK);
+ if (ret)
+ dev_err(chip->dev, "Failed to power down\n");
+
+ return ret;
+}
+
+static void opt4060_chip_off_action(void *chip)
+{
+ opt4060_power_down(chip);
+}
+
+#define _OPT4060_COLOR_CHANNEL(_color, _mask, _ev_spec, _num_ev_spec) \
+{ \
+ .type = IIO_INTENSITY, \
+ .modified = 1, \
+ .channel2 = IIO_MOD_LIGHT_##_color, \
+ .info_mask_separate = _mask, \
+ .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_INT_TIME), \
+ .info_mask_shared_by_all_available = BIT(IIO_CHAN_INFO_INT_TIME), \
+ .address = OPT4060_##_color##_MSB, \
+ .scan_index = OPT4060_##_color, \
+ .scan_type = { \
+ .sign = 'u', \
+ .realbits = 32, \
+ .storagebits = 32, \
+ .endianness = IIO_CPU, \
+ }, \
+ .event_spec = _ev_spec, \
+ .num_event_specs = _num_ev_spec, \
+}
+
+#define OPT4060_COLOR_CHANNEL(_color, _mask) \
+ _OPT4060_COLOR_CHANNEL(_color, _mask, opt4060_event_spec, \
+ ARRAY_SIZE(opt4060_event_spec)) \
+
+#define OPT4060_COLOR_CHANNEL_NO_EVENTS(_color, _mask) \
+ _OPT4060_COLOR_CHANNEL(_color, _mask, NULL, 0) \
+
+#define OPT4060_LIGHT_CHANNEL(_channel) \
+{ \
+ .type = IIO_LIGHT, \
+ .info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED), \
+ .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_INT_TIME), \
+ .info_mask_shared_by_all_available = BIT(IIO_CHAN_INFO_INT_TIME), \
+ .scan_index = OPT4060_##_channel, \
+ .scan_type = { \
+ .sign = 'u', \
+ .realbits = 32, \
+ .storagebits = 32, \
+ .endianness = IIO_CPU, \
+ }, \
+}
+
+static const struct iio_event_spec opt4060_event_spec[] = {
+ {
+ .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),
+ }, {
+ .type = IIO_EV_TYPE_THRESH,
+ .dir = IIO_EV_DIR_EITHER,
+ .mask_separate = BIT(IIO_EV_INFO_PERIOD),
+ },
+};
+
+static const struct iio_chan_spec opt4060_channels[] = {
+ OPT4060_COLOR_CHANNEL(RED, BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE)),
+ OPT4060_COLOR_CHANNEL(GREEN, BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE)),
+ OPT4060_COLOR_CHANNEL(BLUE, BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE)),
+ OPT4060_COLOR_CHANNEL(CLEAR, BIT(IIO_CHAN_INFO_RAW)),
+ OPT4060_LIGHT_CHANNEL(ILLUM),
+ IIO_CHAN_SOFT_TIMESTAMP(OPT4060_NUM_CHANS),
+};
+
+static const struct iio_chan_spec opt4060_channels_no_events[] = {
+ OPT4060_COLOR_CHANNEL_NO_EVENTS(RED, BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE)),
+ OPT4060_COLOR_CHANNEL_NO_EVENTS(GREEN, BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE)),
+ OPT4060_COLOR_CHANNEL_NO_EVENTS(BLUE, BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE)),
+ OPT4060_COLOR_CHANNEL_NO_EVENTS(CLEAR, BIT(IIO_CHAN_INFO_RAW)),
+ OPT4060_LIGHT_CHANNEL(ILLUM),
+ IIO_CHAN_SOFT_TIMESTAMP(OPT4060_NUM_CHANS),
+};
+
+static int opt4060_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int *val, int *val2, long mask)
+{
+ struct opt4060_chip *chip = iio_priv(indio_dev);
+
+ switch (mask) {
+ case IIO_CHAN_INFO_RAW:
+ return opt4060_read_chan_raw(indio_dev, chan, val);
+ case IIO_CHAN_INFO_SCALE:
+ return opt4060_get_chan_scale(indio_dev, chan, val, val2);
+ case IIO_CHAN_INFO_PROCESSED:
+ return opt4060_read_illuminance(indio_dev, chan, val);
+ case IIO_CHAN_INFO_INT_TIME:
+ *val = 0;
+ *val2 = opt4060_int_time_reg[chip->int_time][0];
+ return IIO_VAL_INT_PLUS_MICRO;
+ default:
+ return -EINVAL;
+ }
+}
+
+static int opt4060_write_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int val, int val2, long mask)
+{
+ struct opt4060_chip *chip = iio_priv(indio_dev);
+ int int_time;
+
+ switch (mask) {
+ case IIO_CHAN_INFO_INT_TIME:
+ int_time = opt4060_als_time_to_index(val2);
+ if (int_time < 0)
+ return int_time;
+ chip->int_time = int_time;
+ return opt4060_set_int_time(chip);
+ default:
+ return -EINVAL;
+ }
+}
+
+static int opt4060_write_raw_get_fmt(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ long mask)
+{
+ switch (mask) {
+ case IIO_CHAN_INFO_INT_TIME:
+ return IIO_VAL_INT_PLUS_MICRO;
+ default:
+ return -EINVAL;
+ }
+}
+
+static u32 opt4060_calc_th_reg(u32 adc_val)
+{
+ u32 th_val, th_exp, bits;
+ /*
+ * The threshold registers take 4 bits of exponent and 12 bits of data
+ * ADC = TH_VAL << (8 + TH_EXP)
+ */
+ bits = fls(adc_val);
+
+ if (bits > 31)
+ th_exp = 11; /* Maximum exponent */
+ else if (bits > 20)
+ th_exp = bits - 20;
+ else
+ th_exp = 0;
+ th_val = (adc_val >> (8 + th_exp)) & 0xfff;
+
+ return (th_exp << 12) + th_val;
+}
+
+static u32 opt4060_calc_val_from_th_reg(u32 th_reg)
+{
+ /*
+ * The threshold registers take 4 bits of exponent and 12 bits of data
+ * ADC = TH_VAL << (8 + TH_EXP)
+ */
+ u32 th_val, th_exp;
+
+ th_exp = (th_reg >> 12) & 0xf;
+ th_val = th_reg & 0xfff;
+
+ return th_val << (8 + th_exp);
+}
+
+static int opt4060_read_available(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_INT_TIME:
+ *length = ARRAY_SIZE(opt4060_int_time_available) * 2;
+ *vals = (const int *)opt4060_int_time_available;
+ *type = IIO_VAL_INT_PLUS_MICRO;
+ return IIO_AVAIL_LIST;
+
+ default:
+ return -EINVAL;
+ }
+}
+
+static ssize_t opt4060_read_ev_period(struct opt4060_chip *chip, int *val,
+ int *val2)
+{
+ int ret, pers, fault_count, int_time;
+ u64 uval;
+
+ int_time = opt4060_int_time_reg[chip->int_time][0];
+
+ ret = regmap_read(chip->regmap, OPT4060_CTRL, &fault_count);
+ if (ret < 0)
+ return ret;
+
+ fault_count = fault_count & OPT4060_CTRL_FAULT_COUNT_MASK;
+ switch (fault_count) {
+ case OPT4060_CTRL_FAULT_COUNT_2:
+ pers = 2;
+ break;
+ case OPT4060_CTRL_FAULT_COUNT_4:
+ pers = 4;
+ break;
+ case OPT4060_CTRL_FAULT_COUNT_8:
+ pers = 8;
+ break;
+
+ default:
+ pers = 1;
+ break;
+ }
+
+ uval = mul_u32_u32(int_time, pers);
+ *val = div_u64_rem(uval, MICRO, val2);
+
+ return IIO_VAL_INT_PLUS_MICRO;
+}
+
+static ssize_t opt4060_write_ev_period(struct opt4060_chip *chip, int val,
+ int val2)
+{
+ u64 uval, int_time;
+ unsigned int regval, fault_count_val;
+
+ uval = mul_u32_u32(val, MICRO) + val2;
+ int_time = opt4060_int_time_reg[chip->int_time][0];
+
+ /* Check if the period is closest to 1, 2, 4 or 8 times integration time.*/
+ if (uval <= int_time)
+ fault_count_val = OPT4060_CTRL_FAULT_COUNT_1;
+ else if (uval <= int_time * 2)
+ fault_count_val = OPT4060_CTRL_FAULT_COUNT_2;
+ else if (uval <= int_time * 4)
+ fault_count_val = OPT4060_CTRL_FAULT_COUNT_4;
+ else
+ fault_count_val = OPT4060_CTRL_FAULT_COUNT_8;
+
+ regval = FIELD_PREP(OPT4060_CTRL_FAULT_COUNT_MASK, fault_count_val);
+ return regmap_update_bits(chip->regmap, OPT4060_CTRL,
+ OPT4060_CTRL_FAULT_COUNT_MASK, regval);
+}
+
+static int opt4060_get_channel_sel(struct opt4060_chip *chip, int *ch_sel)
+{
+ int ret;
+ u32 regval;
+
+ ret = regmap_read(chip->regmap, OPT4060_INT_CTRL, &regval);
+ if (ret) {
+ dev_err(chip->dev, "Failed to get channel selection.\n");
+ return ret;
+ }
+ *ch_sel = FIELD_GET(OPT4060_INT_CTRL_THRESH_SEL, regval);
+ return ret;
+}
+
+static int opt4060_set_channel_sel(struct opt4060_chip *chip, int ch_sel)
+{
+ int ret;
+ u32 regval;
+
+ regval = FIELD_PREP(OPT4060_INT_CTRL_THRESH_SEL, ch_sel);
+ ret = regmap_update_bits(chip->regmap, OPT4060_INT_CTRL,
+ OPT4060_INT_CTRL_THRESH_SEL, regval);
+ if (ret)
+ dev_err(chip->dev, "Failed to set channel selection.\n");
+ return ret;
+}
+
+static int opt4060_get_thresholds(struct opt4060_chip *chip, u32 *th_lo, u32 *th_hi)
+{
+ int ret;
+ u32 regval;
+
+ ret = regmap_read(chip->regmap, OPT4060_THRESHOLD_LOW, &regval);
+ if (ret) {
+ dev_err(chip->dev, "Failed to read THRESHOLD_LOW.\n");
+ return ret;
+ }
+ *th_lo = opt4060_calc_val_from_th_reg(regval);
+
+ ret = regmap_read(chip->regmap, OPT4060_THRESHOLD_HIGH, &regval);
+ if (ret) {
+ dev_err(chip->dev, "Failed to read THRESHOLD_LOW.\n");
+ return ret;
+ }
+ *th_hi = opt4060_calc_val_from_th_reg(regval);
+
+ return ret;
+}
+
+static int opt4060_set_thresholds(struct opt4060_chip *chip, u32 th_lo, u32 th_hi)
+{
+ int ret;
+
+ ret = regmap_write(chip->regmap, OPT4060_THRESHOLD_LOW, opt4060_calc_th_reg(th_lo));
+ if (ret) {
+ dev_err(chip->dev, "Failed to write THRESHOLD_LOW.\n");
+ return ret;
+ }
+
+ ret = regmap_write(chip->regmap, OPT4060_THRESHOLD_HIGH, opt4060_calc_th_reg(th_hi));
+ if (ret)
+ dev_err(chip->dev, "Failed to write THRESHOLD_HIGH.\n");
+
+ return ret;
+}
+
+static int opt4060_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)
+{
+ struct opt4060_chip *chip = iio_priv(indio_dev);
+ u32 th_lo, th_hi;
+ int ret;
+
+ if (chan->type != IIO_INTENSITY)
+ return -EINVAL;
+ if (type != IIO_EV_TYPE_THRESH)
+ return -EINVAL;
+
+ switch (info) {
+ case IIO_EV_INFO_VALUE:
+ ret = opt4060_get_thresholds(chip, &th_lo, &th_hi);
+ if (ret)
+ return ret;
+ if (dir == IIO_EV_DIR_FALLING) {
+ *val = th_lo;
+ ret = IIO_VAL_INT;
+ } else if (dir == IIO_EV_DIR_RISING) {
+ *val = th_hi;
+ ret = IIO_VAL_INT;
+ }
+ return ret;
+ case IIO_EV_INFO_PERIOD:
+ return opt4060_read_ev_period(chip, val, val2);
+ default:
+ return -EINVAL;
+ }
+}
+
+static int opt4060_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)
+{
+ struct opt4060_chip *chip = iio_priv(indio_dev);
+ u32 th_lo, th_hi;
+ int ret;
+
+ if (chan->type != IIO_INTENSITY)
+ return -EINVAL;
+ if (type != IIO_EV_TYPE_THRESH)
+ return -EINVAL;
+
+ switch (info) {
+ case IIO_EV_INFO_VALUE:
+ ret = opt4060_get_thresholds(chip, &th_lo, &th_hi);
+ if (ret)
+ return ret;
+ if (dir == IIO_EV_DIR_FALLING)
+ th_lo = val;
+ else if (dir == IIO_EV_DIR_RISING)
+ th_hi = val;
+ return opt4060_set_thresholds(chip, th_lo, th_hi);
+ case IIO_EV_INFO_PERIOD:
+ return opt4060_write_ev_period(chip, val, val2);
+ default:
+ return -EINVAL;
+ }
+}
+
+static int opt4060_read_event_config(struct iio_dev *indio_dev,
+ const struct iio_chan_spec *chan,
+ enum iio_event_type type,
+ enum iio_event_direction dir)
+{
+ int ch_sel, ch_idx = chan->scan_index;
+ struct opt4060_chip *chip = iio_priv(indio_dev);
+ int ret;
+
+ if (chan->type != IIO_INTENSITY)
+ return -EINVAL;
+ if (type != IIO_EV_TYPE_THRESH)
+ return -EINVAL;
+
+ ret = opt4060_get_channel_sel(chip, &ch_sel);
+ if (ret)
+ return ret;
+
+ if (((dir == IIO_EV_DIR_FALLING) && chip->thresh_event_lo_active) ||
+ ((dir == IIO_EV_DIR_RISING) && chip->thresh_event_hi_active))
+ return ch_sel == ch_idx;
+
+ return ret;
+}
+
+static int opt4060_write_event_config(struct iio_dev *indio_dev,
+ const struct iio_chan_spec *chan,
+ enum iio_event_type type,
+ enum iio_event_direction dir, bool state)
+{
+ int ch_sel, ch_idx = chan->scan_index;
+ struct opt4060_chip *chip = iio_priv(indio_dev);
+ int ret;
+
+ guard(mutex)(&chip->event_enabling_lock);
+
+ if (chan->type != IIO_INTENSITY)
+ return -EINVAL;
+ if (type != IIO_EV_TYPE_THRESH)
+ return -EINVAL;
+
+ ret = opt4060_get_channel_sel(chip, &ch_sel);
+ if (ret)
+ return ret;
+
+ if (state) {
+ /* Only one channel can be active at the same time */
+ if ((chip->thresh_event_lo_active || chip->thresh_event_hi_active) &&
+ (ch_idx != ch_sel))
+ return -EBUSY;
+ if (dir == IIO_EV_DIR_FALLING)
+ chip->thresh_event_lo_active = true;
+ else if (dir == IIO_EV_DIR_RISING)
+ chip->thresh_event_hi_active = true;
+ ret = opt4060_set_channel_sel(chip, ch_idx);
+ if (ret)
+ return ret;
+ } else {
+ if (ch_idx == ch_sel) {
+ if (dir == IIO_EV_DIR_FALLING)
+ chip->thresh_event_lo_active = false;
+ else if (dir == IIO_EV_DIR_RISING)
+ chip->thresh_event_hi_active = false;
+ }
+ }
+
+ return opt4060_set_driver_state(indio_dev,
+ chip->thresh_event_hi_active |
+ chip->thresh_event_lo_active,
+ false);
+}
+
+static const struct iio_info opt4060_info = {
+ .read_raw = opt4060_read_raw,
+ .write_raw = opt4060_write_raw,
+ .write_raw_get_fmt = opt4060_write_raw_get_fmt,
+ .read_avail = opt4060_read_available,
+ .read_event_value = opt4060_read_event,
+ .write_event_value = opt4060_write_event,
+ .read_event_config = opt4060_read_event_config,
+ .write_event_config = opt4060_write_event_config,
+};
+
+static const struct iio_info opt4060_info_no_irq = {
+ .read_raw = opt4060_read_raw,
+ .write_raw = opt4060_write_raw,
+ .write_raw_get_fmt = opt4060_write_raw_get_fmt,
+ .read_avail = opt4060_read_available,
+};
+
+static int opt4060_load_defaults(struct opt4060_chip *chip)
+{
+ u16 reg;
+ int ret;
+
+ chip->int_time = OPT4060_DEFAULT_CONVERSION_TIME;
+
+ /* Set initial MIN/MAX thresholds */
+ ret = opt4060_set_thresholds(chip, 0, UINT_MAX);
+ if (ret)
+ return ret;
+
+ /*
+ * Setting auto-range, latched window for thresholds, one-shot conversion
+ * and quick wake-up mode as default.
+ */
+ reg = FIELD_PREP(OPT4060_CTRL_RANGE_MASK,
+ OPT4060_CTRL_LIGHT_SCALE_AUTO);
+ reg |= FIELD_PREP(OPT4060_CTRL_CONV_TIME_MASK, chip->int_time);
+ reg |= FIELD_PREP(OPT4060_CTRL_OPER_MODE_MASK,
+ OPT4060_CTRL_OPER_MODE_ONE_SHOT);
+ reg |= OPT4060_CTRL_QWAKE_MASK | OPT4060_CTRL_LATCH_MASK;
+
+ ret = regmap_write(chip->regmap, OPT4060_CTRL, reg);
+ if (ret)
+ dev_err(chip->dev, "Failed to set configuration\n");
+
+ return ret;
+}
+
+static bool opt4060_volatile_reg(struct device *dev, unsigned int reg)
+{
+ return reg <= OPT4060_CLEAR_LSB || reg == OPT4060_RES_CTRL;
+}
+
+static bool opt4060_writable_reg(struct device *dev, unsigned int reg)
+{
+ return reg >= OPT4060_THRESHOLD_LOW || reg >= OPT4060_INT_CTRL;
+}
+
+static bool opt4060_readonly_reg(struct device *dev, unsigned int reg)
+{
+ return reg == OPT4060_DEVICE_ID;
+}
+
+static bool opt4060_readable_reg(struct device *dev, unsigned int reg)
+{
+ /* Volatile, writable and read-only registers are readable. */
+ return opt4060_volatile_reg(dev, reg) || opt4060_writable_reg(dev, reg) ||
+ opt4060_readonly_reg(dev, reg);
+}
+
+static const struct regmap_config opt4060_regmap_config = {
+ .name = "opt4060",
+ .reg_bits = 8,
+ .val_bits = 16,
+ .cache_type = REGCACHE_RBTREE,
+ .max_register = OPT4060_DEVICE_ID,
+ .readable_reg = opt4060_readable_reg,
+ .writeable_reg = opt4060_writable_reg,
+ .volatile_reg = opt4060_volatile_reg,
+ .val_format_endian = REGMAP_ENDIAN_BIG,
+};
+
+static const struct iio_trigger_ops opt4060_trigger_ops = {
+ .set_trigger_state = opt4060_trigger_set_state,
+};
+
+static irqreturn_t opt4060_trigger_handler(int irq, void *p)
+{
+ struct iio_poll_func *pf = p;
+ struct iio_dev *idev = pf->indio_dev;
+ struct opt4060_chip *chip = iio_priv(idev);
+ struct {
+ u32 chan[OPT4060_NUM_CHANS];
+ aligned_s64 ts;
+ } raw;
+ int i = 0;
+ int chan, ret;
+
+ /* If the trigger is not from this driver, a new sample is needed.*/
+ if (iio_trigger_validate_own_device(idev->trig, idev))
+ opt4060_trigger_new_samples(idev);
+
+ memset(&raw, 0, sizeof(raw));
+
+ iio_for_each_active_channel(idev, chan) {
+ if (chan == OPT4060_ILLUM)
+ ret = opt4060_calc_illuminance(chip, &raw.chan[i++]);
+ else
+ ret = opt4060_read_raw_value(chip,
+ idev->channels[chan].address,
+ &raw.chan[i++]);
+ if (ret) {
+ dev_err(chip->dev, "Reading channel data failed\n");
+ goto err_read;
+ }
+ }
+
+ iio_push_to_buffers_with_timestamp(idev, &raw, pf->timestamp);
+err_read:
+ iio_trigger_notify_done(idev->trig);
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t opt4060_irq_thread(int irq, void *private)
+{
+ struct iio_dev *idev = private;
+ struct opt4060_chip *chip = iio_priv(idev);
+ int ret, dummy;
+ unsigned int int_res;
+
+ ret = regmap_read(chip->regmap, OPT4060_RES_CTRL, &int_res);
+ if (ret < 0) {
+ dev_err(chip->dev, "Failed to read interrupt reasons.\n");
+ return IRQ_NONE;
+ }
+
+ /* Read OPT4060_CTRL to clear interrupt */
+ ret = regmap_read(chip->regmap, OPT4060_CTRL, &dummy);
+ if (ret < 0) {
+ dev_err(chip->dev, "Failed to clear interrupt\n");
+ return IRQ_NONE;
+ }
+
+ /* Handle events */
+ if (int_res & (OPT4060_RES_CTRL_FLAG_H | OPT4060_RES_CTRL_FLAG_L)) {
+ u64 code;
+ int chan = 0;
+
+ ret = opt4060_get_channel_sel(chip, &chan);
+ if (ret) {
+ dev_err(chip->dev, "Failed to read threshold channel.\n");
+ return IRQ_NONE;
+ }
+
+ /* Check if the interrupt is from the lower threshold */
+ if (int_res & OPT4060_RES_CTRL_FLAG_L) {
+ code = IIO_MOD_EVENT_CODE(IIO_INTENSITY,
+ chan,
+ idev->channels[chan].channel2,
+ IIO_EV_TYPE_THRESH,
+ IIO_EV_DIR_FALLING);
+ iio_push_event(idev, code, iio_get_time_ns(idev));
+ }
+ /* Check if the interrupt is from the upper threshold */
+ if (int_res & OPT4060_RES_CTRL_FLAG_H) {
+ code = IIO_MOD_EVENT_CODE(IIO_INTENSITY,
+ chan,
+ idev->channels[chan].channel2,
+ IIO_EV_TYPE_THRESH,
+ IIO_EV_DIR_RISING);
+ iio_push_event(idev, code, iio_get_time_ns(idev));
+ }
+ }
+
+ /* Handle conversion ready */
+ if (int_res & OPT4060_RES_CTRL_CONV_READY) {
+ /* Signal completion for potentially waiting reads */
+ complete(&chip->completion);
+
+ /* Handle data ready triggers */
+ if (iio_buffer_enabled(idev))
+ iio_trigger_poll_nested(chip->trig);
+ }
+ return IRQ_HANDLED;
+}
+
+static int opt4060_setup_buffer(struct opt4060_chip *chip, struct iio_dev *idev)
+{
+ int ret;
+
+ ret = devm_iio_triggered_buffer_setup(chip->dev, idev,
+ &iio_pollfunc_store_time,
+ opt4060_trigger_handler, NULL);
+ if (ret)
+ return dev_err_probe(chip->dev, ret,
+ "Buffer setup failed.\n");
+ return ret;
+}
+
+static int opt4060_setup_trigger(struct opt4060_chip *chip, struct iio_dev *idev)
+{
+ struct iio_trigger *data_trigger;
+ char *name;
+ int ret;
+
+ data_trigger = devm_iio_trigger_alloc(chip->dev, "%s-data-ready-dev%d",
+ idev->name, iio_device_id(idev));
+ if (!data_trigger)
+ return -ENOMEM;
+
+ /*
+ * The data trigger allows for sample capture on each new conversion
+ * ready interrupt.
+ */
+ chip->trig = data_trigger;
+ data_trigger->ops = &opt4060_trigger_ops;
+ iio_trigger_set_drvdata(data_trigger, idev);
+ ret = devm_iio_trigger_register(chip->dev, data_trigger);
+ if (ret)
+ return dev_err_probe(chip->dev, ret,
+ "Data ready trigger registration failed\n");
+
+ name = devm_kasprintf(chip->dev, GFP_KERNEL, "%s-opt4060",
+ dev_name(chip->dev));
+ if (!name)
+ return dev_err_probe(chip->dev, -ENOMEM, "Failed to alloc chip name\n");
+
+ ret = devm_request_threaded_irq(chip->dev, chip->irq, NULL, opt4060_irq_thread,
+ IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
+ name, idev);
+ if (ret)
+ return dev_err_probe(chip->dev, ret, "Could not request IRQ\n");
+
+ init_completion(&chip->completion);
+
+ ret = devm_mutex_init(chip->dev, &chip->irq_setting_lock);
+ if (ret)
+ return ret;
+
+ ret = devm_mutex_init(chip->dev, &chip->event_enabling_lock);
+ if (ret)
+ return ret;
+
+ ret = regmap_write_bits(chip->regmap, OPT4060_INT_CTRL,
+ OPT4060_INT_CTRL_OUTPUT,
+ OPT4060_INT_CTRL_OUTPUT);
+ if (ret)
+ return dev_err_probe(chip->dev, ret,
+ "Failed to set interrupt as output\n");
+
+ return 0;
+}
+
+static int opt4060_probe(struct i2c_client *client)
+{
+ struct device *dev = &client->dev;
+ struct opt4060_chip *chip;
+ struct iio_dev *indio_dev;
+ int ret;
+ unsigned int regval, dev_id;
+
+ indio_dev = devm_iio_device_alloc(dev, sizeof(*chip));
+ if (!indio_dev)
+ return -ENOMEM;
+
+ chip = iio_priv(indio_dev);
+
+ ret = devm_regulator_get_enable(dev, "vdd");
+ if (ret)
+ return dev_err_probe(dev, ret, "Failed to enable vdd supply\n");
+
+ chip->regmap = devm_regmap_init_i2c(client, &opt4060_regmap_config);
+ if (IS_ERR(chip->regmap))
+ return dev_err_probe(dev, PTR_ERR(chip->regmap),
+ "regmap initialization failed\n");
+
+ chip->dev = dev;
+ chip->irq = client->irq;
+
+ ret = regmap_reinit_cache(chip->regmap, &opt4060_regmap_config);
+ if (ret)
+ return dev_err_probe(dev, ret,
+ "failed to reinit regmap cache\n");
+
+ ret = regmap_read(chip->regmap, OPT4060_DEVICE_ID, &regval);
+ if (ret < 0)
+ return dev_err_probe(dev, ret,
+ "Failed to read the device ID register\n");
+
+ dev_id = FIELD_GET(OPT4060_DEVICE_ID_MASK, regval);
+ if (dev_id != OPT4060_DEVICE_ID_VAL)
+ dev_info(dev, "Device ID: %#04x unknown\n", dev_id);
+
+ if (chip->irq) {
+ indio_dev->info = &opt4060_info;
+ indio_dev->channels = opt4060_channels;
+ indio_dev->num_channels = ARRAY_SIZE(opt4060_channels);
+ } else {
+ indio_dev->info = &opt4060_info_no_irq;
+ indio_dev->channels = opt4060_channels_no_events;
+ indio_dev->num_channels = ARRAY_SIZE(opt4060_channels_no_events);
+ }
+ indio_dev->modes = INDIO_DIRECT_MODE;
+ indio_dev->name = "opt4060";
+
+ ret = opt4060_load_defaults(chip);
+ if (ret < 0)
+ return dev_err_probe(dev, ret,
+ "Failed to set sensor defaults\n");
+
+ ret = devm_add_action_or_reset(dev, opt4060_chip_off_action, chip);
+ if (ret < 0)
+ return dev_err_probe(dev, ret,
+ "Failed to setup power off action\n");
+
+ ret = opt4060_setup_buffer(chip, indio_dev);
+ if (ret)
+ return ret;
+
+ if (chip->irq) {
+ ret = opt4060_setup_trigger(chip, indio_dev);
+ if (ret)
+ return ret;
+ }
+
+ return devm_iio_device_register(dev, indio_dev);
+}
+
+static const struct i2c_device_id opt4060_id[] = {
+ { "opt4060", },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, opt4060_id);
+
+static const struct of_device_id opt4060_of_match[] = {
+ { .compatible = "ti,opt4060" },
+ { }
+};
+MODULE_DEVICE_TABLE(of, opt4060_of_match);
+
+static struct i2c_driver opt4060_driver = {
+ .driver = {
+ .name = "opt4060",
+ .of_match_table = opt4060_of_match,
+ },
+ .probe = opt4060_probe,
+ .id_table = opt4060_id,
+};
+module_i2c_driver(opt4060_driver);
+
+MODULE_AUTHOR("Per-Daniel Olsson <perdaniel.olsson@axis.com>");
+MODULE_DESCRIPTION("Texas Instruments OPT4060 RGBW color sensor driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/iio/light/pa12203001.c b/drivers/iio/light/pa12203001.c
index 636432c45651..8885852bef22 100644
--- a/drivers/iio/light/pa12203001.c
+++ b/drivers/iio/light/pa12203001.c
@@ -456,14 +456,14 @@ static const struct dev_pm_ops pa12203001_pm_ops = {
static const struct acpi_device_id pa12203001_acpi_match[] = {
{ "TXCPA122", 0 },
- {}
+ { }
};
MODULE_DEVICE_TABLE(acpi, pa12203001_acpi_match);
static const struct i2c_device_id pa12203001_id[] = {
- { "txcpa122", 0 },
- {}
+ { "txcpa122" },
+ { }
};
MODULE_DEVICE_TABLE(i2c, pa12203001_id);
diff --git a/drivers/iio/light/rohm-bu27008.c b/drivers/iio/light/rohm-bu27008.c
deleted file mode 100644
index 0f010eff1981..000000000000
--- a/drivers/iio/light/rohm-bu27008.c
+++ /dev/null
@@ -1,1635 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0-only
-/*
- * ROHM Colour Sensor driver for
- * - BU27008 RGBC sensor
- * - BU27010 RGBC + Flickering sensor
- *
- * Copyright (c) 2023, ROHM Semiconductor.
- */
-
-#include <linux/bitfield.h>
-#include <linux/bitops.h>
-#include <linux/device.h>
-#include <linux/i2c.h>
-#include <linux/interrupt.h>
-#include <linux/module.h>
-#include <linux/property.h>
-#include <linux/regmap.h>
-#include <linux/regulator/consumer.h>
-#include <linux/units.h>
-
-#include <linux/iio/iio.h>
-#include <linux/iio/iio-gts-helper.h>
-#include <linux/iio/trigger.h>
-#include <linux/iio/trigger_consumer.h>
-#include <linux/iio/triggered_buffer.h>
-
-/*
- * A word about register address and mask definitions.
- *
- * At a quick glance to the data-sheet register tables, the BU27010 has all the
- * registers that the BU27008 has. On top of that the BU27010 adds couple of new
- * ones.
- *
- * So, all definitions BU27008_REG_* are there also for BU27010 but none of the
- * BU27010_REG_* are present on BU27008. This makes sense as BU27010 just adds
- * some features (Flicker FIFO, more power control) on top of the BU27008.
- *
- * Unfortunately, some of the wheel has been re-invented. Even though the names
- * of the registers have stayed the same, pretty much all of the functionality
- * provided by the registers has changed place. Contents of all MODE_CONTROL
- * registers on BU27008 and BU27010 are different.
- *
- * Chip-specific mapping from register addresses/bits to functionality is done
- * in bu27_chip_data structures.
- */
-#define BU27008_REG_SYSTEM_CONTROL 0x40
-#define BU27008_MASK_SW_RESET BIT(7)
-#define BU27008_MASK_PART_ID GENMASK(5, 0)
-#define BU27008_ID 0x1a
-#define BU27008_REG_MODE_CONTROL1 0x41
-#define BU27008_MASK_MEAS_MODE GENMASK(2, 0)
-#define BU27008_MASK_CHAN_SEL GENMASK(3, 2)
-
-#define BU27008_REG_MODE_CONTROL2 0x42
-#define BU27008_MASK_RGBC_GAIN GENMASK(7, 3)
-#define BU27008_MASK_IR_GAIN_LO GENMASK(2, 0)
-#define BU27008_SHIFT_IR_GAIN 3
-
-#define BU27008_REG_MODE_CONTROL3 0x43
-#define BU27008_MASK_VALID BIT(7)
-#define BU27008_MASK_INT_EN BIT(1)
-#define BU27008_INT_EN BU27008_MASK_INT_EN
-#define BU27008_INT_DIS 0
-#define BU27008_MASK_MEAS_EN BIT(0)
-#define BU27008_MEAS_EN BIT(0)
-#define BU27008_MEAS_DIS 0
-
-#define BU27008_REG_DATA0_LO 0x50
-#define BU27008_REG_DATA1_LO 0x52
-#define BU27008_REG_DATA2_LO 0x54
-#define BU27008_REG_DATA3_LO 0x56
-#define BU27008_REG_DATA3_HI 0x57
-#define BU27008_REG_MANUFACTURER_ID 0x92
-#define BU27008_REG_MAX BU27008_REG_MANUFACTURER_ID
-
-/* BU27010 specific definitions */
-
-#define BU27010_MASK_SW_RESET BIT(7)
-#define BU27010_ID 0x1b
-#define BU27010_REG_POWER 0x3e
-#define BU27010_MASK_POWER BIT(0)
-
-#define BU27010_REG_RESET 0x3f
-#define BU27010_MASK_RESET BIT(0)
-#define BU27010_RESET_RELEASE BU27010_MASK_RESET
-
-#define BU27010_MASK_MEAS_EN BIT(1)
-
-#define BU27010_MASK_CHAN_SEL GENMASK(7, 6)
-#define BU27010_MASK_MEAS_MODE GENMASK(5, 4)
-#define BU27010_MASK_RGBC_GAIN GENMASK(3, 0)
-
-#define BU27010_MASK_DATA3_GAIN GENMASK(7, 6)
-#define BU27010_MASK_DATA2_GAIN GENMASK(5, 4)
-#define BU27010_MASK_DATA1_GAIN GENMASK(3, 2)
-#define BU27010_MASK_DATA0_GAIN GENMASK(1, 0)
-
-#define BU27010_MASK_FLC_MODE BIT(7)
-#define BU27010_MASK_FLC_GAIN GENMASK(4, 0)
-
-#define BU27010_REG_MODE_CONTROL4 0x44
-/* If flicker is ever to be supported the IRQ must be handled as a field */
-#define BU27010_IRQ_DIS_ALL GENMASK(1, 0)
-#define BU27010_DRDY_EN BIT(0)
-#define BU27010_MASK_INT_SEL GENMASK(1, 0)
-
-#define BU27010_REG_MODE_CONTROL5 0x45
-#define BU27010_MASK_RGB_VALID BIT(7)
-#define BU27010_MASK_FLC_VALID BIT(6)
-#define BU27010_MASK_WAIT_EN BIT(3)
-#define BU27010_MASK_FIFO_EN BIT(2)
-#define BU27010_MASK_RGB_EN BIT(1)
-#define BU27010_MASK_FLC_EN BIT(0)
-
-#define BU27010_REG_DATA_FLICKER_LO 0x56
-#define BU27010_MASK_DATA_FLICKER_HI GENMASK(2, 0)
-#define BU27010_REG_FLICKER_COUNT 0x5a
-#define BU27010_REG_FIFO_LEVEL_LO 0x5b
-#define BU27010_MASK_FIFO_LEVEL_HI BIT(0)
-#define BU27010_REG_FIFO_DATA_LO 0x5d
-#define BU27010_REG_FIFO_DATA_HI 0x5e
-#define BU27010_MASK_FIFO_DATA_HI GENMASK(2, 0)
-#define BU27010_REG_MANUFACTURER_ID 0x92
-#define BU27010_REG_MAX BU27010_REG_MANUFACTURER_ID
-
-/**
- * enum bu27008_chan_type - BU27008 channel types
- * @BU27008_RED: Red channel. Always via data0.
- * @BU27008_GREEN: Green channel. Always via data1.
- * @BU27008_BLUE: Blue channel. Via data2 (when used).
- * @BU27008_CLEAR: Clear channel. Via data2 or data3 (when used).
- * @BU27008_IR: IR channel. Via data3 (when used).
- * @BU27008_LUX: Illuminance channel, computed using RGB and IR.
- * @BU27008_NUM_CHANS: Number of channel types.
- */
-enum bu27008_chan_type {
- BU27008_RED,
- BU27008_GREEN,
- BU27008_BLUE,
- BU27008_CLEAR,
- BU27008_IR,
- BU27008_LUX,
- BU27008_NUM_CHANS
-};
-
-/**
- * enum bu27008_chan - BU27008 physical data channel
- * @BU27008_DATA0: Always red.
- * @BU27008_DATA1: Always green.
- * @BU27008_DATA2: Blue or clear.
- * @BU27008_DATA3: IR or clear.
- * @BU27008_NUM_HW_CHANS: Number of physical channels
- */
-enum bu27008_chan {
- BU27008_DATA0,
- BU27008_DATA1,
- BU27008_DATA2,
- BU27008_DATA3,
- BU27008_NUM_HW_CHANS
-};
-
-/* We can always measure red and green at same time */
-#define ALWAYS_SCANNABLE (BIT(BU27008_RED) | BIT(BU27008_GREEN))
-
-/* We use these data channel configs. Ensure scan_masks below follow them too */
-#define BU27008_BLUE2_CLEAR3 0x0 /* buffer is R, G, B, C */
-#define BU27008_CLEAR2_IR3 0x1 /* buffer is R, G, C, IR */
-#define BU27008_BLUE2_IR3 0x2 /* buffer is R, G, B, IR */
-
-static const unsigned long bu27008_scan_masks[] = {
- /* buffer is R, G, B, C */
- ALWAYS_SCANNABLE | BIT(BU27008_BLUE) | BIT(BU27008_CLEAR),
- /* buffer is R, G, C, IR */
- ALWAYS_SCANNABLE | BIT(BU27008_CLEAR) | BIT(BU27008_IR),
- /* buffer is R, G, B, IR */
- ALWAYS_SCANNABLE | BIT(BU27008_BLUE) | BIT(BU27008_IR),
- /* buffer is R, G, B, IR, LUX */
- ALWAYS_SCANNABLE | BIT(BU27008_BLUE) | BIT(BU27008_IR) | BIT(BU27008_LUX),
- 0
-};
-
-/*
- * Available scales with gain 1x - 1024x, timings 55, 100, 200, 400 mS
- * Time impacts to gain: 1x, 2x, 4x, 8x.
- *
- * => Max total gain is HWGAIN * gain by integration time (8 * 1024) = 8192
- *
- * Max amplification is (HWGAIN * MAX integration-time multiplier) 1024 * 8
- * = 8192. With NANO scale we get rid of accuracy loss when we start with the
- * scale 16.0 for HWGAIN1, INT-TIME 55 mS. This way the nano scale for MAX
- * total gain 8192 will be 1953125
- */
-#define BU27008_SCALE_1X 16
-
-/*
- * On BU27010 available scales with gain 1x - 4096x,
- * timings 55, 100, 200, 400 mS. Time impacts to gain: 1x, 2x, 4x, 8x.
- *
- * => Max total gain is HWGAIN * gain by integration time (8 * 4096)
- *
- * Using NANO precision for scale we must use scale 64x corresponding gain 1x
- * to avoid precision loss.
- */
-#define BU27010_SCALE_1X 64
-
-/* See the data sheet for the "Gain Setting" table */
-#define BU27008_GSEL_1X 0x00
-#define BU27008_GSEL_4X 0x08
-#define BU27008_GSEL_8X 0x09
-#define BU27008_GSEL_16X 0x0a
-#define BU27008_GSEL_32X 0x0b
-#define BU27008_GSEL_64X 0x0c
-#define BU27008_GSEL_256X 0x18
-#define BU27008_GSEL_512X 0x19
-#define BU27008_GSEL_1024X 0x1a
-
-static const struct iio_gain_sel_pair bu27008_gains[] = {
- GAIN_SCALE_GAIN(1, BU27008_GSEL_1X),
- GAIN_SCALE_GAIN(4, BU27008_GSEL_4X),
- GAIN_SCALE_GAIN(8, BU27008_GSEL_8X),
- GAIN_SCALE_GAIN(16, BU27008_GSEL_16X),
- GAIN_SCALE_GAIN(32, BU27008_GSEL_32X),
- GAIN_SCALE_GAIN(64, BU27008_GSEL_64X),
- GAIN_SCALE_GAIN(256, BU27008_GSEL_256X),
- GAIN_SCALE_GAIN(512, BU27008_GSEL_512X),
- GAIN_SCALE_GAIN(1024, BU27008_GSEL_1024X),
-};
-
-static const struct iio_gain_sel_pair bu27008_gains_ir[] = {
- GAIN_SCALE_GAIN(2, BU27008_GSEL_1X),
- GAIN_SCALE_GAIN(4, BU27008_GSEL_4X),
- GAIN_SCALE_GAIN(8, BU27008_GSEL_8X),
- GAIN_SCALE_GAIN(16, BU27008_GSEL_16X),
- GAIN_SCALE_GAIN(32, BU27008_GSEL_32X),
- GAIN_SCALE_GAIN(64, BU27008_GSEL_64X),
- GAIN_SCALE_GAIN(256, BU27008_GSEL_256X),
- GAIN_SCALE_GAIN(512, BU27008_GSEL_512X),
- GAIN_SCALE_GAIN(1024, BU27008_GSEL_1024X),
-};
-
-#define BU27010_GSEL_1X 0x00 /* 000000 */
-#define BU27010_GSEL_4X 0x08 /* 001000 */
-#define BU27010_GSEL_16X 0x09 /* 001001 */
-#define BU27010_GSEL_64X 0x0e /* 001110 */
-#define BU27010_GSEL_256X 0x1e /* 011110 */
-#define BU27010_GSEL_1024X 0x2e /* 101110 */
-#define BU27010_GSEL_4096X 0x3f /* 111111 */
-
-static const struct iio_gain_sel_pair bu27010_gains[] = {
- GAIN_SCALE_GAIN(1, BU27010_GSEL_1X),
- GAIN_SCALE_GAIN(4, BU27010_GSEL_4X),
- GAIN_SCALE_GAIN(16, BU27010_GSEL_16X),
- GAIN_SCALE_GAIN(64, BU27010_GSEL_64X),
- GAIN_SCALE_GAIN(256, BU27010_GSEL_256X),
- GAIN_SCALE_GAIN(1024, BU27010_GSEL_1024X),
- GAIN_SCALE_GAIN(4096, BU27010_GSEL_4096X),
-};
-
-static const struct iio_gain_sel_pair bu27010_gains_ir[] = {
- GAIN_SCALE_GAIN(2, BU27010_GSEL_1X),
- GAIN_SCALE_GAIN(4, BU27010_GSEL_4X),
- GAIN_SCALE_GAIN(16, BU27010_GSEL_16X),
- GAIN_SCALE_GAIN(64, BU27010_GSEL_64X),
- GAIN_SCALE_GAIN(256, BU27010_GSEL_256X),
- GAIN_SCALE_GAIN(1024, BU27010_GSEL_1024X),
- GAIN_SCALE_GAIN(4096, BU27010_GSEL_4096X),
-};
-
-#define BU27008_MEAS_MODE_100MS 0x00
-#define BU27008_MEAS_MODE_55MS 0x01
-#define BU27008_MEAS_MODE_200MS 0x02
-#define BU27008_MEAS_MODE_400MS 0x04
-
-#define BU27010_MEAS_MODE_100MS 0x00
-#define BU27010_MEAS_MODE_55MS 0x03
-#define BU27010_MEAS_MODE_200MS 0x01
-#define BU27010_MEAS_MODE_400MS 0x02
-
-#define BU27008_MEAS_TIME_MAX_MS 400
-
-static const struct iio_itime_sel_mul bu27008_itimes[] = {
- GAIN_SCALE_ITIME_US(400000, BU27008_MEAS_MODE_400MS, 8),
- GAIN_SCALE_ITIME_US(200000, BU27008_MEAS_MODE_200MS, 4),
- GAIN_SCALE_ITIME_US(100000, BU27008_MEAS_MODE_100MS, 2),
- GAIN_SCALE_ITIME_US(55000, BU27008_MEAS_MODE_55MS, 1),
-};
-
-static const struct iio_itime_sel_mul bu27010_itimes[] = {
- GAIN_SCALE_ITIME_US(400000, BU27010_MEAS_MODE_400MS, 8),
- GAIN_SCALE_ITIME_US(200000, BU27010_MEAS_MODE_200MS, 4),
- GAIN_SCALE_ITIME_US(100000, BU27010_MEAS_MODE_100MS, 2),
- GAIN_SCALE_ITIME_US(55000, BU27010_MEAS_MODE_55MS, 1),
-};
-
-/*
- * All the RGBC channels share the same gain.
- * IR gain can be fine-tuned from the gain set for the RGBC by 2 bit, but this
- * would yield quite complex gain setting. Especially since not all bit
- * compinations are supported. And in any case setting GAIN for RGBC will
- * always also change the IR-gain.
- *
- * On top of this, the selector '0' which corresponds to hw-gain 1X on RGBC,
- * corresponds to gain 2X on IR. Rest of the selctors correspond to same gains
- * though. This, however, makes it not possible to use shared gain for all
- * RGBC and IR settings even though they are all changed at the one go.
- */
-#define BU27008_CHAN(color, data, separate_avail) \
-{ \
- .type = IIO_INTENSITY, \
- .modified = 1, \
- .channel2 = IIO_MOD_LIGHT_##color, \
- .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \
- BIT(IIO_CHAN_INFO_SCALE), \
- .info_mask_separate_available = (separate_avail), \
- .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_INT_TIME), \
- .info_mask_shared_by_all_available = BIT(IIO_CHAN_INFO_INT_TIME), \
- .address = BU27008_REG_##data##_LO, \
- .scan_index = BU27008_##color, \
- .scan_type = { \
- .sign = 'u', \
- .realbits = 16, \
- .storagebits = 16, \
- .endianness = IIO_LE, \
- }, \
-}
-
-/* For raw reads we always configure DATA3 for CLEAR */
-static const struct iio_chan_spec bu27008_channels[] = {
- BU27008_CHAN(RED, DATA0, BIT(IIO_CHAN_INFO_SCALE)),
- BU27008_CHAN(GREEN, DATA1, BIT(IIO_CHAN_INFO_SCALE)),
- BU27008_CHAN(BLUE, DATA2, BIT(IIO_CHAN_INFO_SCALE)),
- BU27008_CHAN(CLEAR, DATA2, BIT(IIO_CHAN_INFO_SCALE)),
- /*
- * We don't allow setting scale for IR (because of shared gain bits).
- * Hence we don't advertise available ones either.
- */
- BU27008_CHAN(IR, DATA3, 0),
- {
- .type = IIO_LIGHT,
- .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
- BIT(IIO_CHAN_INFO_SCALE),
- .channel = BU27008_LUX,
- .scan_index = BU27008_LUX,
- .scan_type = {
- .sign = 'u',
- .realbits = 64,
- .storagebits = 64,
- .endianness = IIO_CPU,
- },
- },
- IIO_CHAN_SOFT_TIMESTAMP(BU27008_NUM_CHANS),
-};
-
-struct bu27008_data;
-
-struct bu27_chip_data {
- const char *name;
- int (*chip_init)(struct bu27008_data *data);
- int (*get_gain_sel)(struct bu27008_data *data, int *sel);
- int (*write_gain_sel)(struct bu27008_data *data, int sel);
- const struct regmap_config *regmap_cfg;
- const struct iio_gain_sel_pair *gains;
- const struct iio_gain_sel_pair *gains_ir;
- const struct iio_itime_sel_mul *itimes;
- int num_gains;
- int num_gains_ir;
- int num_itimes;
- int scale1x;
-
- int drdy_en_reg;
- int drdy_en_mask;
- int meas_en_reg;
- int meas_en_mask;
- int valid_reg;
- int chan_sel_reg;
- int chan_sel_mask;
- int int_time_mask;
- u8 part_id;
-};
-
-struct bu27008_data {
- const struct bu27_chip_data *cd;
- struct regmap *regmap;
- struct iio_trigger *trig;
- struct device *dev;
- struct iio_gts gts;
- struct iio_gts gts_ir;
- int irq;
-
- /*
- * Prevent changing gain/time config when scale is read/written.
- * Similarly, protect the integration_time read/change sequence.
- * Prevent changing gain/time when data is read.
- */
- struct mutex mutex;
-};
-
-static const struct regmap_range bu27008_volatile_ranges[] = {
- {
- .range_min = BU27008_REG_SYSTEM_CONTROL, /* SWRESET */
- .range_max = BU27008_REG_SYSTEM_CONTROL,
- }, {
- .range_min = BU27008_REG_MODE_CONTROL3, /* VALID */
- .range_max = BU27008_REG_MODE_CONTROL3,
- }, {
- .range_min = BU27008_REG_DATA0_LO, /* DATA */
- .range_max = BU27008_REG_DATA3_HI,
- },
-};
-
-static const struct regmap_range bu27010_volatile_ranges[] = {
- {
- .range_min = BU27010_REG_RESET, /* RSTB */
- .range_max = BU27008_REG_SYSTEM_CONTROL, /* RESET */
- }, {
- .range_min = BU27010_REG_MODE_CONTROL5, /* VALID bits */
- .range_max = BU27010_REG_MODE_CONTROL5,
- }, {
- .range_min = BU27008_REG_DATA0_LO,
- .range_max = BU27010_REG_FIFO_DATA_HI,
- },
-};
-
-static const struct regmap_access_table bu27008_volatile_regs = {
- .yes_ranges = &bu27008_volatile_ranges[0],
- .n_yes_ranges = ARRAY_SIZE(bu27008_volatile_ranges),
-};
-
-static const struct regmap_access_table bu27010_volatile_regs = {
- .yes_ranges = &bu27010_volatile_ranges[0],
- .n_yes_ranges = ARRAY_SIZE(bu27010_volatile_ranges),
-};
-
-static const struct regmap_range bu27008_read_only_ranges[] = {
- {
- .range_min = BU27008_REG_DATA0_LO,
- .range_max = BU27008_REG_DATA3_HI,
- }, {
- .range_min = BU27008_REG_MANUFACTURER_ID,
- .range_max = BU27008_REG_MANUFACTURER_ID,
- },
-};
-
-static const struct regmap_range bu27010_read_only_ranges[] = {
- {
- .range_min = BU27008_REG_DATA0_LO,
- .range_max = BU27010_REG_FIFO_DATA_HI,
- }, {
- .range_min = BU27010_REG_MANUFACTURER_ID,
- .range_max = BU27010_REG_MANUFACTURER_ID,
- }
-};
-
-static const struct regmap_access_table bu27008_ro_regs = {
- .no_ranges = &bu27008_read_only_ranges[0],
- .n_no_ranges = ARRAY_SIZE(bu27008_read_only_ranges),
-};
-
-static const struct regmap_access_table bu27010_ro_regs = {
- .no_ranges = &bu27010_read_only_ranges[0],
- .n_no_ranges = ARRAY_SIZE(bu27010_read_only_ranges),
-};
-
-static const struct regmap_config bu27008_regmap = {
- .reg_bits = 8,
- .val_bits = 8,
- .max_register = BU27008_REG_MAX,
- .cache_type = REGCACHE_RBTREE,
- .volatile_table = &bu27008_volatile_regs,
- .wr_table = &bu27008_ro_regs,
- /*
- * All register writes are serialized by the mutex which protects the
- * scale setting/getting. This is needed because scale is combined by
- * gain and integration time settings and we need to ensure those are
- * not read / written when scale is being computed.
- *
- * As a result of this serializing, we don't need regmap locking. Note,
- * this is not true if we add any configurations which are not
- * serialized by the mutex and which may need for example a protected
- * read-modify-write cycle (eg. regmap_update_bits()). Please, revise
- * this when adding features to the driver.
- */
- .disable_locking = true,
-};
-
-static const struct regmap_config bu27010_regmap = {
- .reg_bits = 8,
- .val_bits = 8,
-
- .max_register = BU27010_REG_MAX,
- .cache_type = REGCACHE_RBTREE,
- .volatile_table = &bu27010_volatile_regs,
- .wr_table = &bu27010_ro_regs,
- .disable_locking = true,
-};
-
-static int bu27008_write_gain_sel(struct bu27008_data *data, int sel)
-{
- int regval;
-
- regval = FIELD_PREP(BU27008_MASK_RGBC_GAIN, sel);
-
- /*
- * We do always set also the LOW bits of IR-gain because othervice we
- * would risk resulting an invalid GAIN register value.
- *
- * We could allow setting separate gains for RGBC and IR when the
- * values were such that HW could support both gain settings.
- * Eg, when the shared bits were same for both gain values.
- *
- * This, however, has a negligible benefit compared to the increased
- * software complexity when we would need to go through the gains
- * for both channels separately when the integration time changes.
- * This would end up with nasty logic for computing gain values for
- * both channels - and rejecting them if shared bits changed.
- *
- * We should then build the logic by guessing what a user prefers.
- * RGBC or IR gains correctly set while other jumps to odd value?
- * Maybe look-up a value where both gains are somehow optimized
- * <what this somehow is, is ATM unknown to us>. Or maybe user would
- * expect us to reject changes when optimal gains can't be set to both
- * channels w/given integration time. At best that would result
- * solution that works well for a very specific subset of
- * configurations but causes unexpected corner-cases.
- *
- * So, we keep it simple. Always set same selector to IR and RGBC.
- * We disallow setting IR (as I expect that most of the users are
- * interested in RGBC). This way we can show the user that the scales
- * for RGBC and IR channels are different (1X Vs 2X with sel 0) while
- * still keeping the operation deterministic.
- */
- regval |= FIELD_PREP(BU27008_MASK_IR_GAIN_LO, sel);
-
- return regmap_update_bits(data->regmap, BU27008_REG_MODE_CONTROL2,
- BU27008_MASK_RGBC_GAIN, regval);
-}
-
-static int bu27010_write_gain_sel(struct bu27008_data *data, int sel)
-{
- unsigned int regval;
- int ret, chan_selector;
-
- /*
- * Gain 'selector' is composed of two registers. Selector is 6bit value,
- * 4 high bits being the RGBC gain fieild in MODE_CONTROL1 register and
- * two low bits being the channel specific gain in MODE_CONTROL2.
- *
- * Let's take the 4 high bits of whole 6 bit selector, and prepare
- * the MODE_CONTROL1 value (RGBC gain part).
- */
- regval = FIELD_PREP(BU27010_MASK_RGBC_GAIN, (sel >> 2));
-
- ret = regmap_update_bits(data->regmap, BU27008_REG_MODE_CONTROL1,
- BU27010_MASK_RGBC_GAIN, regval);
- if (ret)
- return ret;
-
- /*
- * Two low two bits of the selector must be written for all 4
- * channels in the MODE_CONTROL2 register. Copy these two bits for
- * all channels.
- */
- chan_selector = sel & GENMASK(1, 0);
-
- regval = FIELD_PREP(BU27010_MASK_DATA0_GAIN, chan_selector);
- regval |= FIELD_PREP(BU27010_MASK_DATA1_GAIN, chan_selector);
- regval |= FIELD_PREP(BU27010_MASK_DATA2_GAIN, chan_selector);
- regval |= FIELD_PREP(BU27010_MASK_DATA3_GAIN, chan_selector);
-
- return regmap_write(data->regmap, BU27008_REG_MODE_CONTROL2, regval);
-}
-
-static int bu27008_get_gain_sel(struct bu27008_data *data, int *sel)
-{
- int ret;
-
- /*
- * If we always "lock" the gain selectors for all channels to prevent
- * unsupported configs, then it does not matter which channel is used
- * we can just return selector from any of them.
- *
- * This, however is not true if we decide to support only 4X and 16X
- * and then individual gains for channels. Currently this is not the
- * case.
- *
- * If we some day decide to support individual gains, then we need to
- * have channel information here.
- */
-
- ret = regmap_read(data->regmap, BU27008_REG_MODE_CONTROL2, sel);
- if (ret)
- return ret;
-
- *sel = FIELD_GET(BU27008_MASK_RGBC_GAIN, *sel);
-
- return 0;
-}
-
-static int bu27010_get_gain_sel(struct bu27008_data *data, int *sel)
-{
- int ret, tmp;
-
- /*
- * We always "lock" the gain selectors for all channels to prevent
- * unsupported configs. It does not matter which channel is used
- * we can just return selector from any of them.
- *
- * Read the channel0 gain.
- */
- ret = regmap_read(data->regmap, BU27008_REG_MODE_CONTROL2, sel);
- if (ret)
- return ret;
-
- *sel = FIELD_GET(BU27010_MASK_DATA0_GAIN, *sel);
-
- /* Read the shared gain */
- ret = regmap_read(data->regmap, BU27008_REG_MODE_CONTROL1, &tmp);
- if (ret)
- return ret;
-
- /*
- * The gain selector is made as a combination of common RGBC gain and
- * the channel specific gain. The channel specific gain forms the low
- * bits of selector and RGBC gain is appended right after it.
- *
- * Compose the selector from channel0 gain and shared RGBC gain.
- */
- *sel |= FIELD_GET(BU27010_MASK_RGBC_GAIN, tmp) << fls(BU27010_MASK_DATA0_GAIN);
-
- return ret;
-}
-
-static int bu27008_chip_init(struct bu27008_data *data)
-{
- int ret;
-
- ret = regmap_write_bits(data->regmap, BU27008_REG_SYSTEM_CONTROL,
- BU27008_MASK_SW_RESET, BU27008_MASK_SW_RESET);
- if (ret)
- return dev_err_probe(data->dev, ret, "Sensor reset failed\n");
-
- /*
- * The data-sheet does not tell how long performing the IC reset takes.
- * However, the data-sheet says the minimum time it takes the IC to be
- * able to take inputs after power is applied, is 100 uS. I'd assume
- * > 1 mS is enough.
- */
- msleep(1);
-
- ret = regmap_reinit_cache(data->regmap, data->cd->regmap_cfg);
- if (ret)
- dev_err(data->dev, "Failed to reinit reg cache\n");
-
- return ret;
-}
-
-static int bu27010_chip_init(struct bu27008_data *data)
-{
- int ret;
-
- ret = regmap_write_bits(data->regmap, BU27008_REG_SYSTEM_CONTROL,
- BU27010_MASK_SW_RESET, BU27010_MASK_SW_RESET);
- if (ret)
- return dev_err_probe(data->dev, ret, "Sensor reset failed\n");
-
- msleep(1);
-
- /* Power ON*/
- ret = regmap_write_bits(data->regmap, BU27010_REG_POWER,
- BU27010_MASK_POWER, BU27010_MASK_POWER);
- if (ret)
- return dev_err_probe(data->dev, ret, "Sensor power-on failed\n");
-
- msleep(1);
-
- /* Release blocks from reset */
- ret = regmap_write_bits(data->regmap, BU27010_REG_RESET,
- BU27010_MASK_RESET, BU27010_RESET_RELEASE);
- if (ret)
- return dev_err_probe(data->dev, ret, "Sensor powering failed\n");
-
- msleep(1);
-
- /*
- * The IRQ enabling on BU27010 is done in a peculiar way. The IRQ
- * enabling is not a bit mask where individual IRQs could be enabled but
- * a field which values are:
- * 00 => IRQs disabled
- * 01 => Data-ready (RGBC/IR)
- * 10 => Data-ready (flicker)
- * 11 => Flicker FIFO
- *
- * So, only one IRQ can be enabled at a time and enabling for example
- * flicker FIFO would automagically disable data-ready IRQ.
- *
- * Currently the driver does not support the flicker. Hence, we can
- * just treat the RGBC data-ready as single bit which can be enabled /
- * disabled. This works for as long as the second bit in the field
- * stays zero. Here we ensure it gets zeroed.
- */
- return regmap_clear_bits(data->regmap, BU27010_REG_MODE_CONTROL4,
- BU27010_IRQ_DIS_ALL);
-}
-
-static const struct bu27_chip_data bu27010_chip = {
- .name = "bu27010",
- .chip_init = bu27010_chip_init,
- .get_gain_sel = bu27010_get_gain_sel,
- .write_gain_sel = bu27010_write_gain_sel,
- .regmap_cfg = &bu27010_regmap,
- .gains = &bu27010_gains[0],
- .gains_ir = &bu27010_gains_ir[0],
- .itimes = &bu27010_itimes[0],
- .num_gains = ARRAY_SIZE(bu27010_gains),
- .num_gains_ir = ARRAY_SIZE(bu27010_gains_ir),
- .num_itimes = ARRAY_SIZE(bu27010_itimes),
- .scale1x = BU27010_SCALE_1X,
- .drdy_en_reg = BU27010_REG_MODE_CONTROL4,
- .drdy_en_mask = BU27010_DRDY_EN,
- .meas_en_reg = BU27010_REG_MODE_CONTROL5,
- .meas_en_mask = BU27010_MASK_MEAS_EN,
- .valid_reg = BU27010_REG_MODE_CONTROL5,
- .chan_sel_reg = BU27008_REG_MODE_CONTROL1,
- .chan_sel_mask = BU27010_MASK_CHAN_SEL,
- .int_time_mask = BU27010_MASK_MEAS_MODE,
- .part_id = BU27010_ID,
-};
-
-static const struct bu27_chip_data bu27008_chip = {
- .name = "bu27008",
- .chip_init = bu27008_chip_init,
- .get_gain_sel = bu27008_get_gain_sel,
- .write_gain_sel = bu27008_write_gain_sel,
- .regmap_cfg = &bu27008_regmap,
- .gains = &bu27008_gains[0],
- .gains_ir = &bu27008_gains_ir[0],
- .itimes = &bu27008_itimes[0],
- .num_gains = ARRAY_SIZE(bu27008_gains),
- .num_gains_ir = ARRAY_SIZE(bu27008_gains_ir),
- .num_itimes = ARRAY_SIZE(bu27008_itimes),
- .scale1x = BU27008_SCALE_1X,
- .drdy_en_reg = BU27008_REG_MODE_CONTROL3,
- .drdy_en_mask = BU27008_MASK_INT_EN,
- .valid_reg = BU27008_REG_MODE_CONTROL3,
- .meas_en_reg = BU27008_REG_MODE_CONTROL3,
- .meas_en_mask = BU27008_MASK_MEAS_EN,
- .chan_sel_reg = BU27008_REG_MODE_CONTROL3,
- .chan_sel_mask = BU27008_MASK_CHAN_SEL,
- .int_time_mask = BU27008_MASK_MEAS_MODE,
- .part_id = BU27008_ID,
-};
-
-#define BU27008_MAX_VALID_RESULT_WAIT_US 50000
-#define BU27008_VALID_RESULT_WAIT_QUANTA_US 1000
-
-static int bu27008_chan_read_data(struct bu27008_data *data, int reg, int *val)
-{
- int ret, valid;
- __le16 tmp;
-
- ret = regmap_read_poll_timeout(data->regmap, data->cd->valid_reg,
- valid, (valid & BU27008_MASK_VALID),
- BU27008_VALID_RESULT_WAIT_QUANTA_US,
- BU27008_MAX_VALID_RESULT_WAIT_US);
- if (ret)
- return ret;
-
- ret = regmap_bulk_read(data->regmap, reg, &tmp, sizeof(tmp));
- if (ret)
- dev_err(data->dev, "Reading channel data failed\n");
-
- *val = le16_to_cpu(tmp);
-
- return ret;
-}
-
-static int bu27008_get_gain(struct bu27008_data *data, struct iio_gts *gts, int *gain)
-{
- int ret, sel;
-
- ret = data->cd->get_gain_sel(data, &sel);
- if (ret)
- return ret;
-
- ret = iio_gts_find_gain_by_sel(gts, sel);
- if (ret < 0) {
- dev_err(data->dev, "unknown gain value 0x%x\n", sel);
- return ret;
- }
-
- *gain = ret;
-
- return 0;
-}
-
-static int bu27008_set_gain(struct bu27008_data *data, int gain)
-{
- int ret;
-
- ret = iio_gts_find_sel_by_gain(&data->gts, gain);
- if (ret < 0)
- return ret;
-
- return data->cd->write_gain_sel(data, ret);
-}
-
-static int bu27008_get_int_time_sel(struct bu27008_data *data, int *sel)
-{
- int ret, val;
-
- ret = regmap_read(data->regmap, BU27008_REG_MODE_CONTROL1, &val);
- if (ret)
- return ret;
-
- val &= data->cd->int_time_mask;
- val >>= ffs(data->cd->int_time_mask) - 1;
-
- *sel = val;
-
- return 0;
-}
-
-static int bu27008_set_int_time_sel(struct bu27008_data *data, int sel)
-{
- sel <<= ffs(data->cd->int_time_mask) - 1;
-
- return regmap_update_bits(data->regmap, BU27008_REG_MODE_CONTROL1,
- data->cd->int_time_mask, sel);
-}
-
-static int bu27008_get_int_time_us(struct bu27008_data *data)
-{
- int ret, sel;
-
- ret = bu27008_get_int_time_sel(data, &sel);
- if (ret)
- return ret;
-
- return iio_gts_find_int_time_by_sel(&data->gts, sel);
-}
-
-static int _bu27008_get_scale(struct bu27008_data *data, bool ir, int *val,
- int *val2)
-{
- struct iio_gts *gts;
- int gain, ret;
-
- if (ir)
- gts = &data->gts_ir;
- else
- gts = &data->gts;
-
- ret = bu27008_get_gain(data, gts, &gain);
- if (ret)
- return ret;
-
- ret = bu27008_get_int_time_us(data);
- if (ret < 0)
- return ret;
-
- return iio_gts_get_scale(gts, gain, ret, val, val2);
-}
-
-static int bu27008_get_scale(struct bu27008_data *data, bool ir, int *val,
- int *val2)
-{
- int ret;
-
- mutex_lock(&data->mutex);
- ret = _bu27008_get_scale(data, ir, val, val2);
- mutex_unlock(&data->mutex);
-
- return ret;
-}
-
-static int bu27008_set_int_time(struct bu27008_data *data, int time)
-{
- int ret;
-
- ret = iio_gts_find_sel_by_int_time(&data->gts, time);
- if (ret < 0)
- return ret;
-
- return bu27008_set_int_time_sel(data, ret);
-}
-
-/* Try to change the time so that the scale is maintained */
-static int bu27008_try_set_int_time(struct bu27008_data *data, int int_time_new)
-{
- int ret, old_time_sel, new_time_sel, old_gain, new_gain;
-
- mutex_lock(&data->mutex);
-
- ret = bu27008_get_int_time_sel(data, &old_time_sel);
- if (ret < 0)
- goto unlock_out;
-
- if (!iio_gts_valid_time(&data->gts, int_time_new)) {
- dev_dbg(data->dev, "Unsupported integration time %u\n",
- int_time_new);
-
- ret = -EINVAL;
- goto unlock_out;
- }
-
- /* If we already use requested time, then we're done */
- new_time_sel = iio_gts_find_sel_by_int_time(&data->gts, int_time_new);
- if (new_time_sel == old_time_sel)
- goto unlock_out;
-
- ret = bu27008_get_gain(data, &data->gts, &old_gain);
- if (ret)
- goto unlock_out;
-
- ret = iio_gts_find_new_gain_sel_by_old_gain_time(&data->gts, old_gain,
- old_time_sel, new_time_sel, &new_gain);
- if (ret) {
- int scale1, scale2;
- bool ok;
-
- _bu27008_get_scale(data, false, &scale1, &scale2);
- dev_dbg(data->dev,
- "Can't support time %u with current scale %u %u\n",
- int_time_new, scale1, scale2);
-
- if (new_gain < 0)
- goto unlock_out;
-
- /*
- * If caller requests for integration time change and we
- * can't support the scale - then the caller should be
- * prepared to 'pick up the pieces and deal with the
- * fact that the scale changed'.
- */
- ret = iio_find_closest_gain_low(&data->gts, new_gain, &ok);
- if (!ok)
- dev_dbg(data->dev, "optimal gain out of range\n");
-
- if (ret < 0) {
- dev_dbg(data->dev,
- "Total gain increase. Risk of saturation");
- ret = iio_gts_get_min_gain(&data->gts);
- if (ret < 0)
- goto unlock_out;
- }
- new_gain = ret;
- dev_dbg(data->dev, "scale changed, new gain %u\n", new_gain);
- }
-
- ret = bu27008_set_gain(data, new_gain);
- if (ret)
- goto unlock_out;
-
- ret = bu27008_set_int_time(data, int_time_new);
-
-unlock_out:
- mutex_unlock(&data->mutex);
-
- return ret;
-}
-
-static int bu27008_meas_set(struct bu27008_data *data, bool enable)
-{
- if (enable)
- return regmap_set_bits(data->regmap, data->cd->meas_en_reg,
- data->cd->meas_en_mask);
- return regmap_clear_bits(data->regmap, data->cd->meas_en_reg,
- data->cd->meas_en_mask);
-}
-
-static int bu27008_chan_cfg(struct bu27008_data *data,
- struct iio_chan_spec const *chan)
-{
- int chan_sel;
-
- if (chan->scan_index == BU27008_BLUE)
- chan_sel = BU27008_BLUE2_CLEAR3;
- else
- chan_sel = BU27008_CLEAR2_IR3;
-
- /*
- * prepare bitfield for channel sel. The FIELD_PREP works only when
- * mask is constant. In our case the mask is assigned based on the
- * chip type. Hence the open-coded FIELD_PREP here. We don't bother
- * zeroing the irrelevant bits though - update_bits takes care of that.
- */
- chan_sel <<= ffs(data->cd->chan_sel_mask) - 1;
-
- return regmap_update_bits(data->regmap, data->cd->chan_sel_reg,
- BU27008_MASK_CHAN_SEL, chan_sel);
-}
-
-static int bu27008_read_one(struct bu27008_data *data, struct iio_dev *idev,
- struct iio_chan_spec const *chan, int *val, int *val2)
-{
- int ret, int_time;
-
- ret = bu27008_chan_cfg(data, chan);
- if (ret)
- return ret;
-
- ret = bu27008_meas_set(data, true);
- if (ret)
- return ret;
-
- ret = bu27008_get_int_time_us(data);
- if (ret < 0)
- int_time = BU27008_MEAS_TIME_MAX_MS;
- else
- int_time = ret / USEC_PER_MSEC;
-
- msleep(int_time);
-
- ret = bu27008_chan_read_data(data, chan->address, val);
- if (!ret)
- ret = IIO_VAL_INT;
-
- if (bu27008_meas_set(data, false))
- dev_warn(data->dev, "measurement disabling failed\n");
-
- return ret;
-}
-
-#define BU27008_LUX_DATA_RED 0
-#define BU27008_LUX_DATA_GREEN 1
-#define BU27008_LUX_DATA_BLUE 2
-#define BU27008_LUX_DATA_IR 3
-#define LUX_DATA_SIZE (BU27008_NUM_HW_CHANS * sizeof(__le16))
-
-static int bu27008_read_lux_chans(struct bu27008_data *data, unsigned int time,
- __le16 *chan_data)
-{
- int ret, chan_sel, tmpret, valid;
-
- chan_sel = BU27008_BLUE2_IR3 << (ffs(data->cd->chan_sel_mask) - 1);
-
- ret = regmap_update_bits(data->regmap, data->cd->chan_sel_reg,
- data->cd->chan_sel_mask, chan_sel);
- if (ret)
- return ret;
-
- ret = bu27008_meas_set(data, true);
- if (ret)
- return ret;
-
- msleep(time / USEC_PER_MSEC);
-
- ret = regmap_read_poll_timeout(data->regmap, data->cd->valid_reg,
- valid, (valid & BU27008_MASK_VALID),
- BU27008_VALID_RESULT_WAIT_QUANTA_US,
- BU27008_MAX_VALID_RESULT_WAIT_US);
- if (ret)
- goto out;
-
- ret = regmap_bulk_read(data->regmap, BU27008_REG_DATA0_LO, chan_data,
- LUX_DATA_SIZE);
- if (ret)
- goto out;
-out:
- tmpret = bu27008_meas_set(data, false);
- if (tmpret)
- dev_warn(data->dev, "Stopping measurement failed\n");
-
- return ret;
-}
-
-/*
- * Following equation for computing lux out of register values was given by
- * ROHM HW colleagues;
- *
- * Red = RedData*1024 / Gain * 20 / meas_mode
- * Green = GreenData* 1024 / Gain * 20 / meas_mode
- * Blue = BlueData* 1024 / Gain * 20 / meas_mode
- * IR = IrData* 1024 / Gain * 20 / meas_mode
- *
- * where meas_mode is the integration time in mS / 10
- *
- * IRratio = (IR > 0.18 * Green) ? 0 : 1
- *
- * Lx = max(c1*Red + c2*Green + c3*Blue,0)
- *
- * for
- * IRratio 0: c1 = -0.00002237, c2 = 0.0003219, c3 = -0.000120371
- * IRratio 1: c1 = -0.00001074, c2 = 0.000305415, c3 = -0.000129367
- */
-
-/*
- * The max chan data is 0xffff. When we multiply it by 1024 * 20, we'll get
- * 0x4FFFB000 which still fits in 32-bit integer. This won't overflow.
- */
-#define NORM_CHAN_DATA_FOR_LX_CALC(chan, gain, time) (le16_to_cpu(chan) * \
- 1024 * 20 / (gain) / (time))
-static u64 bu27008_calc_nlux(struct bu27008_data *data, __le16 *lux_data,
- unsigned int gain, unsigned int gain_ir, unsigned int time)
-{
- unsigned int red, green, blue, ir;
- s64 c1, c2, c3, nlux;
-
- time /= 10000;
- ir = NORM_CHAN_DATA_FOR_LX_CALC(lux_data[BU27008_LUX_DATA_IR], gain_ir, time);
- red = NORM_CHAN_DATA_FOR_LX_CALC(lux_data[BU27008_LUX_DATA_RED], gain, time);
- green = NORM_CHAN_DATA_FOR_LX_CALC(lux_data[BU27008_LUX_DATA_GREEN], gain, time);
- blue = NORM_CHAN_DATA_FOR_LX_CALC(lux_data[BU27008_LUX_DATA_BLUE], gain, time);
-
- if ((u64)ir * 100LLU > (u64)green * 18LLU) {
- c1 = -22370;
- c2 = 321900;
- c3 = -120371;
- } else {
- c1 = -10740;
- c2 = 305415;
- c3 = -129367;
- }
- nlux = c1 * red + c2 * green + c3 * blue;
-
- return max_t(s64, 0, nlux);
-}
-
-static int bu27008_get_time_n_gains(struct bu27008_data *data,
- unsigned int *gain, unsigned int *gain_ir, unsigned int *time)
-{
- int ret;
-
- ret = bu27008_get_gain(data, &data->gts, gain);
- if (ret < 0)
- return ret;
-
- ret = bu27008_get_gain(data, &data->gts_ir, gain_ir);
- if (ret < 0)
- return ret;
-
- ret = bu27008_get_int_time_us(data);
- if (ret < 0)
- return ret;
-
- /* Max integration time is 400000. Fits in signed int. */
- *time = ret;
-
- return 0;
-}
-
-struct bu27008_buf {
- __le16 chan[BU27008_NUM_HW_CHANS];
- u64 lux __aligned(8);
- s64 ts __aligned(8);
-};
-
-static int bu27008_buffer_fill_lux(struct bu27008_data *data,
- struct bu27008_buf *raw)
-{
- unsigned int gain, gain_ir, time;
- int ret;
-
- ret = bu27008_get_time_n_gains(data, &gain, &gain_ir, &time);
- if (ret)
- return ret;
-
- raw->lux = bu27008_calc_nlux(data, raw->chan, gain, gain_ir, time);
-
- return 0;
-}
-
-static int bu27008_read_lux(struct bu27008_data *data, struct iio_dev *idev,
- struct iio_chan_spec const *chan,
- int *val, int *val2)
-{
- __le16 lux_data[BU27008_NUM_HW_CHANS];
- unsigned int gain, gain_ir, time;
- u64 nlux;
- int ret;
-
- ret = bu27008_get_time_n_gains(data, &gain, &gain_ir, &time);
- if (ret)
- return ret;
-
- ret = bu27008_read_lux_chans(data, time, lux_data);
- if (ret)
- return ret;
-
- nlux = bu27008_calc_nlux(data, lux_data, gain, gain_ir, time);
- *val = (int)nlux;
- *val2 = nlux >> 32LLU;
-
- return IIO_VAL_INT_64;
-}
-
-static int bu27008_read_raw(struct iio_dev *idev,
- struct iio_chan_spec const *chan,
- int *val, int *val2, long mask)
-{
- struct bu27008_data *data = iio_priv(idev);
- int busy, ret;
-
- switch (mask) {
- case IIO_CHAN_INFO_RAW:
- busy = iio_device_claim_direct_mode(idev);
- if (busy)
- return -EBUSY;
-
- mutex_lock(&data->mutex);
- if (chan->type == IIO_LIGHT)
- ret = bu27008_read_lux(data, idev, chan, val, val2);
- else
- ret = bu27008_read_one(data, idev, chan, val, val2);
- mutex_unlock(&data->mutex);
-
- iio_device_release_direct_mode(idev);
-
- return ret;
-
- case IIO_CHAN_INFO_SCALE:
- if (chan->type == IIO_LIGHT) {
- *val = 0;
- *val2 = 1;
- return IIO_VAL_INT_PLUS_NANO;
- }
- ret = bu27008_get_scale(data, chan->scan_index == BU27008_IR,
- val, val2);
- if (ret)
- return ret;
-
- return IIO_VAL_INT_PLUS_NANO;
-
- case IIO_CHAN_INFO_INT_TIME:
- ret = bu27008_get_int_time_us(data);
- if (ret < 0)
- return ret;
-
- *val = 0;
- *val2 = ret;
-
- return IIO_VAL_INT_PLUS_MICRO;
-
- default:
- return -EINVAL;
- }
-}
-
-/* Called if the new scale could not be supported with existing int-time */
-static int bu27008_try_find_new_time_gain(struct bu27008_data *data, int val,
- int val2, int *gain_sel)
-{
- int i, ret, new_time_sel;
-
- for (i = 0; i < data->gts.num_itime; i++) {
- new_time_sel = data->gts.itime_table[i].sel;
- ret = iio_gts_find_gain_sel_for_scale_using_time(&data->gts,
- new_time_sel, val, val2, gain_sel);
- if (!ret)
- break;
- }
- if (i == data->gts.num_itime) {
- dev_err(data->dev, "Can't support scale %u %u\n", val, val2);
-
- return -EINVAL;
- }
-
- return bu27008_set_int_time_sel(data, new_time_sel);
-}
-
-static int bu27008_set_scale(struct bu27008_data *data,
- struct iio_chan_spec const *chan,
- int val, int val2)
-{
- int ret, gain_sel, time_sel;
-
- if (chan->scan_index == BU27008_IR)
- return -EINVAL;
-
- mutex_lock(&data->mutex);
-
- ret = bu27008_get_int_time_sel(data, &time_sel);
- if (ret < 0)
- goto unlock_out;
-
- ret = iio_gts_find_gain_sel_for_scale_using_time(&data->gts, time_sel,
- val, val2, &gain_sel);
- if (ret) {
- ret = bu27008_try_find_new_time_gain(data, val, val2, &gain_sel);
- if (ret)
- goto unlock_out;
-
- }
- ret = data->cd->write_gain_sel(data, gain_sel);
-
-unlock_out:
- mutex_unlock(&data->mutex);
-
- return ret;
-}
-
-static int bu27008_write_raw_get_fmt(struct iio_dev *indio_dev,
- struct iio_chan_spec const *chan,
- long mask)
-{
-
- switch (mask) {
- case IIO_CHAN_INFO_SCALE:
- return IIO_VAL_INT_PLUS_NANO;
- case IIO_CHAN_INFO_INT_TIME:
- return IIO_VAL_INT_PLUS_MICRO;
- default:
- return -EINVAL;
- }
-}
-
-static int bu27008_write_raw(struct iio_dev *idev,
- struct iio_chan_spec const *chan,
- int val, int val2, long mask)
-{
- struct bu27008_data *data = iio_priv(idev);
- int ret;
-
- /*
- * Do not allow changing scale when measurement is ongoing as doing so
- * could make values in the buffer inconsistent.
- */
- ret = iio_device_claim_direct_mode(idev);
- if (ret)
- return ret;
-
- switch (mask) {
- case IIO_CHAN_INFO_SCALE:
- ret = bu27008_set_scale(data, chan, val, val2);
- break;
- case IIO_CHAN_INFO_INT_TIME:
- if (val) {
- ret = -EINVAL;
- break;
- }
- ret = bu27008_try_set_int_time(data, val2);
- break;
- default:
- ret = -EINVAL;
- break;
- }
- iio_device_release_direct_mode(idev);
-
- return ret;
-}
-
-static int bu27008_read_avail(struct iio_dev *idev,
- struct iio_chan_spec const *chan, const int **vals,
- int *type, int *length, long mask)
-{
- struct bu27008_data *data = iio_priv(idev);
-
- switch (mask) {
- case IIO_CHAN_INFO_INT_TIME:
- return iio_gts_avail_times(&data->gts, vals, type, length);
- case IIO_CHAN_INFO_SCALE:
- if (chan->channel2 == IIO_MOD_LIGHT_IR)
- return iio_gts_all_avail_scales(&data->gts_ir, vals,
- type, length);
- return iio_gts_all_avail_scales(&data->gts, vals, type, length);
- default:
- return -EINVAL;
- }
-}
-
-static int bu27008_update_scan_mode(struct iio_dev *idev,
- const unsigned long *scan_mask)
-{
- struct bu27008_data *data = iio_priv(idev);
- int chan_sel;
-
- /* Configure channel selection */
- if (test_bit(BU27008_BLUE, idev->active_scan_mask)) {
- if (test_bit(BU27008_CLEAR, idev->active_scan_mask))
- chan_sel = BU27008_BLUE2_CLEAR3;
- else
- chan_sel = BU27008_BLUE2_IR3;
- } else {
- chan_sel = BU27008_CLEAR2_IR3;
- }
-
- chan_sel <<= ffs(data->cd->chan_sel_mask) - 1;
-
- return regmap_update_bits(data->regmap, data->cd->chan_sel_reg,
- data->cd->chan_sel_mask, chan_sel);
-}
-
-static const struct iio_info bu27008_info = {
- .read_raw = &bu27008_read_raw,
- .write_raw = &bu27008_write_raw,
- .write_raw_get_fmt = &bu27008_write_raw_get_fmt,
- .read_avail = &bu27008_read_avail,
- .update_scan_mode = bu27008_update_scan_mode,
- .validate_trigger = iio_validate_own_trigger,
-};
-
-static int bu27008_trigger_set_state(struct iio_trigger *trig, bool state)
-{
- struct bu27008_data *data = iio_trigger_get_drvdata(trig);
- int ret;
-
-
- if (state)
- ret = regmap_set_bits(data->regmap, data->cd->drdy_en_reg,
- data->cd->drdy_en_mask);
- else
- ret = regmap_clear_bits(data->regmap, data->cd->drdy_en_reg,
- data->cd->drdy_en_mask);
- if (ret)
- dev_err(data->dev, "Failed to set trigger state\n");
-
- return ret;
-}
-
-static void bu27008_trigger_reenable(struct iio_trigger *trig)
-{
- struct bu27008_data *data = iio_trigger_get_drvdata(trig);
-
- enable_irq(data->irq);
-}
-
-static const struct iio_trigger_ops bu27008_trigger_ops = {
- .set_trigger_state = bu27008_trigger_set_state,
- .reenable = bu27008_trigger_reenable,
-};
-
-static irqreturn_t bu27008_trigger_handler(int irq, void *p)
-{
- struct iio_poll_func *pf = p;
- struct iio_dev *idev = pf->indio_dev;
- struct bu27008_data *data = iio_priv(idev);
- struct bu27008_buf raw;
- int ret, dummy;
-
- memset(&raw, 0, sizeof(raw));
-
- /*
- * After some measurements, it seems reading the
- * BU27008_REG_MODE_CONTROL3 debounces the IRQ line
- */
- ret = regmap_read(data->regmap, data->cd->valid_reg, &dummy);
- if (ret < 0)
- goto err_read;
-
- ret = regmap_bulk_read(data->regmap, BU27008_REG_DATA0_LO, &raw.chan,
- sizeof(raw.chan));
- if (ret < 0)
- goto err_read;
-
- if (test_bit(BU27008_LUX, idev->active_scan_mask)) {
- ret = bu27008_buffer_fill_lux(data, &raw);
- if (ret)
- goto err_read;
- }
-
- iio_push_to_buffers_with_timestamp(idev, &raw, pf->timestamp);
-err_read:
- iio_trigger_notify_done(idev->trig);
-
- return IRQ_HANDLED;
-}
-
-static int bu27008_buffer_preenable(struct iio_dev *idev)
-{
- struct bu27008_data *data = iio_priv(idev);
-
- return bu27008_meas_set(data, true);
-}
-
-static int bu27008_buffer_postdisable(struct iio_dev *idev)
-{
- struct bu27008_data *data = iio_priv(idev);
-
- return bu27008_meas_set(data, false);
-}
-
-static const struct iio_buffer_setup_ops bu27008_buffer_ops = {
- .preenable = bu27008_buffer_preenable,
- .postdisable = bu27008_buffer_postdisable,
-};
-
-static irqreturn_t bu27008_data_rdy_poll(int irq, void *private)
-{
- /*
- * The BU27008 keeps IRQ asserted until we read the VALID bit from
- * a register. We need to keep the IRQ disabled until then.
- */
- disable_irq_nosync(irq);
- iio_trigger_poll(private);
-
- return IRQ_HANDLED;
-}
-
-static int bu27008_setup_trigger(struct bu27008_data *data, struct iio_dev *idev)
-{
- struct iio_trigger *itrig;
- char *name;
- int ret;
-
- ret = devm_iio_triggered_buffer_setup(data->dev, idev,
- &iio_pollfunc_store_time,
- bu27008_trigger_handler,
- &bu27008_buffer_ops);
- if (ret)
- return dev_err_probe(data->dev, ret,
- "iio_triggered_buffer_setup_ext FAIL\n");
-
- itrig = devm_iio_trigger_alloc(data->dev, "%sdata-rdy-dev%d",
- idev->name, iio_device_id(idev));
- if (!itrig)
- return -ENOMEM;
-
- data->trig = itrig;
-
- itrig->ops = &bu27008_trigger_ops;
- iio_trigger_set_drvdata(itrig, data);
-
- name = devm_kasprintf(data->dev, GFP_KERNEL, "%s-bu27008",
- dev_name(data->dev));
-
- ret = devm_request_irq(data->dev, data->irq,
- &bu27008_data_rdy_poll,
- 0, name, itrig);
- if (ret)
- return dev_err_probe(data->dev, ret, "Could not request IRQ\n");
-
- ret = devm_iio_trigger_register(data->dev, itrig);
- if (ret)
- return dev_err_probe(data->dev, ret,
- "Trigger registration failed\n");
-
- /* set default trigger */
- idev->trig = iio_trigger_get(itrig);
-
- return 0;
-}
-
-static int bu27008_probe(struct i2c_client *i2c)
-{
- struct device *dev = &i2c->dev;
- struct bu27008_data *data;
- struct regmap *regmap;
- unsigned int part_id, reg;
- struct iio_dev *idev;
- int ret;
-
- idev = devm_iio_device_alloc(dev, sizeof(*data));
- if (!idev)
- return -ENOMEM;
-
- ret = devm_regulator_get_enable(dev, "vdd");
- if (ret)
- return dev_err_probe(dev, ret, "Failed to get regulator\n");
-
- data = iio_priv(idev);
-
- data->cd = device_get_match_data(&i2c->dev);
- if (!data->cd)
- return -ENODEV;
-
- regmap = devm_regmap_init_i2c(i2c, data->cd->regmap_cfg);
- if (IS_ERR(regmap))
- return dev_err_probe(dev, PTR_ERR(regmap),
- "Failed to initialize Regmap\n");
-
-
- ret = regmap_read(regmap, BU27008_REG_SYSTEM_CONTROL, &reg);
- if (ret)
- return dev_err_probe(dev, ret, "Failed to access sensor\n");
-
- part_id = FIELD_GET(BU27008_MASK_PART_ID, reg);
-
- if (part_id != data->cd->part_id)
- dev_warn(dev, "unknown device 0x%x\n", part_id);
-
- ret = devm_iio_init_iio_gts(dev, data->cd->scale1x, 0, data->cd->gains,
- data->cd->num_gains, data->cd->itimes,
- data->cd->num_itimes, &data->gts);
- if (ret)
- return ret;
-
- ret = devm_iio_init_iio_gts(dev, data->cd->scale1x, 0, data->cd->gains_ir,
- data->cd->num_gains_ir, data->cd->itimes,
- data->cd->num_itimes, &data->gts_ir);
- if (ret)
- return ret;
-
- mutex_init(&data->mutex);
- data->regmap = regmap;
- data->dev = dev;
- data->irq = i2c->irq;
-
- idev->channels = bu27008_channels;
- idev->num_channels = ARRAY_SIZE(bu27008_channels);
- idev->name = data->cd->name;
- idev->info = &bu27008_info;
- idev->modes = INDIO_DIRECT_MODE;
- idev->available_scan_masks = bu27008_scan_masks;
-
- ret = data->cd->chip_init(data);
- if (ret)
- return ret;
-
- if (i2c->irq) {
- ret = bu27008_setup_trigger(data, idev);
- if (ret)
- return ret;
- } else {
- dev_info(dev, "No IRQ, buffered mode disabled\n");
- }
-
- ret = devm_iio_device_register(dev, idev);
- if (ret)
- return dev_err_probe(dev, ret,
- "Unable to register iio device\n");
-
- return 0;
-}
-
-static const struct of_device_id bu27008_of_match[] = {
- { .compatible = "rohm,bu27008", .data = &bu27008_chip },
- { .compatible = "rohm,bu27010", .data = &bu27010_chip },
- { }
-};
-MODULE_DEVICE_TABLE(of, bu27008_of_match);
-
-static struct i2c_driver bu27008_i2c_driver = {
- .driver = {
- .name = "bu27008",
- .of_match_table = bu27008_of_match,
- .probe_type = PROBE_PREFER_ASYNCHRONOUS,
- },
- .probe = bu27008_probe,
-};
-module_i2c_driver(bu27008_i2c_driver);
-
-MODULE_DESCRIPTION("ROHM BU27008 and BU27010 colour sensor driver");
-MODULE_AUTHOR("Matti Vaittinen <matti.vaittinen@fi.rohmeurope.com>");
-MODULE_LICENSE("GPL");
-MODULE_IMPORT_NS(IIO_GTS_HELPER);
diff --git a/drivers/iio/light/rohm-bu27034.c b/drivers/iio/light/rohm-bu27034.c
index bf3de853a811..7cec5e943373 100644
--- a/drivers/iio/light/rohm-bu27034.c
+++ b/drivers/iio/light/rohm-bu27034.c
@@ -1,13 +1,13 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
- * BU27034 ROHM Ambient Light Sensor
+ * BU27034ANUC ROHM Ambient Light Sensor
*
* Copyright (c) 2023, ROHM Semiconductor.
- * https://fscdn.rohm.com/en/products/databook/datasheet/ic/sensor/light/bu27034nuc-e.pdf
*/
#include <linux/bitfield.h>
#include <linux/bits.h>
+#include <linux/cleanup.h>
#include <linux/device.h>
#include <linux/i2c.h>
#include <linux/module.h>
@@ -30,17 +30,15 @@
#define BU27034_REG_MODE_CONTROL2 0x42
#define BU27034_MASK_D01_GAIN GENMASK(7, 3)
-#define BU27034_MASK_D2_GAIN_HI GENMASK(7, 6)
-#define BU27034_MASK_D2_GAIN_LO GENMASK(2, 0)
#define BU27034_REG_MODE_CONTROL3 0x43
#define BU27034_REG_MODE_CONTROL4 0x44
#define BU27034_MASK_MEAS_EN BIT(0)
#define BU27034_MASK_VALID BIT(7)
+#define BU27034_NUM_HW_DATA_CHANS 2
#define BU27034_REG_DATA0_LO 0x50
#define BU27034_REG_DATA1_LO 0x52
-#define BU27034_REG_DATA2_LO 0x54
-#define BU27034_REG_DATA2_HI 0x55
+#define BU27034_REG_DATA1_HI 0x53
#define BU27034_REG_MANUFACTURER_ID 0x92
#define BU27034_REG_MAX BU27034_REG_MANUFACTURER_ID
@@ -88,58 +86,48 @@ enum {
BU27034_CHAN_ALS,
BU27034_CHAN_DATA0,
BU27034_CHAN_DATA1,
- BU27034_CHAN_DATA2,
BU27034_NUM_CHANS
};
static const unsigned long bu27034_scan_masks[] = {
- GENMASK(BU27034_CHAN_DATA2, BU27034_CHAN_ALS), 0
+ GENMASK(BU27034_CHAN_DATA1, BU27034_CHAN_DATA0),
+ GENMASK(BU27034_CHAN_DATA1, BU27034_CHAN_ALS), 0
};
/*
- * Available scales with gain 1x - 4096x, timings 55, 100, 200, 400 mS
+ * Available scales with gain 1x - 1024x, timings 55, 100, 200, 400 mS
* Time impacts to gain: 1x, 2x, 4x, 8x.
*
- * => Max total gain is HWGAIN * gain by integration time (8 * 4096) = 32768
+ * => Max total gain is HWGAIN * gain by integration time (8 * 1024) = 8192
+ * if 1x gain is scale 1, scale for 2x gain is 0.5, 4x => 0.25,
+ * ... 8192x => 0.0001220703125 => 122070.3125 nanos
*
- * Using NANO precision for scale we must use scale 64x corresponding gain 1x
- * to avoid precision loss. (32x would result scale 976 562.5(nanos).
+ * Using NANO precision for scale, we must use scale 16x corresponding gain 1x
+ * to avoid precision loss. (8x would result scale 976 562.5(nanos).
*/
-#define BU27034_SCALE_1X 64
+#define BU27034_SCALE_1X 16
/* See the data sheet for the "Gain Setting" table */
#define BU27034_GSEL_1X 0x00 /* 00000 */
#define BU27034_GSEL_4X 0x08 /* 01000 */
-#define BU27034_GSEL_16X 0x0a /* 01010 */
#define BU27034_GSEL_32X 0x0b /* 01011 */
-#define BU27034_GSEL_64X 0x0c /* 01100 */
#define BU27034_GSEL_256X 0x18 /* 11000 */
#define BU27034_GSEL_512X 0x19 /* 11001 */
#define BU27034_GSEL_1024X 0x1a /* 11010 */
-#define BU27034_GSEL_2048X 0x1b /* 11011 */
-#define BU27034_GSEL_4096X 0x1c /* 11100 */
/* Available gain settings */
static const struct iio_gain_sel_pair bu27034_gains[] = {
GAIN_SCALE_GAIN(1, BU27034_GSEL_1X),
GAIN_SCALE_GAIN(4, BU27034_GSEL_4X),
- GAIN_SCALE_GAIN(16, BU27034_GSEL_16X),
GAIN_SCALE_GAIN(32, BU27034_GSEL_32X),
- GAIN_SCALE_GAIN(64, BU27034_GSEL_64X),
GAIN_SCALE_GAIN(256, BU27034_GSEL_256X),
GAIN_SCALE_GAIN(512, BU27034_GSEL_512X),
GAIN_SCALE_GAIN(1024, BU27034_GSEL_1024X),
- GAIN_SCALE_GAIN(2048, BU27034_GSEL_2048X),
- GAIN_SCALE_GAIN(4096, BU27034_GSEL_4096X),
};
/*
- * The IC has 5 modes for sampling time. 5 mS mode is exceptional as it limits
- * the data collection to data0-channel only and cuts the supported range to
- * 10 bit. It is not supported by the driver.
- *
- * "normal" modes are 55, 100, 200 and 400 mS modes - which do have direct
- * multiplying impact to the register values (similar to gain).
+ * Measurement modes are 55, 100, 200 and 400 mS modes - which do have direct
+ * multiplying impact to the data register values (similar to gain).
*
* This means that if meas-mode is changed for example from 400 => 200,
* the scale is doubled. Eg, time impact to total gain is x1, x2, x4, x8.
@@ -156,13 +144,13 @@ static const struct iio_itime_sel_mul bu27034_itimes[] = {
GAIN_SCALE_ITIME_US(55000, BU27034_MEAS_MODE_55MS, 1),
};
-#define BU27034_CHAN_DATA(_name, _ch2) \
+#define BU27034_CHAN_DATA(_name) \
{ \
.type = IIO_INTENSITY, \
.channel = BU27034_CHAN_##_name, \
- .channel2 = (_ch2), \
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \
- BIT(IIO_CHAN_INFO_SCALE), \
+ BIT(IIO_CHAN_INFO_SCALE) | \
+ BIT(IIO_CHAN_INFO_HARDWAREGAIN), \
.info_mask_separate_available = BIT(IIO_CHAN_INFO_SCALE), \
.info_mask_shared_by_all = BIT(IIO_CHAN_INFO_INT_TIME), \
.info_mask_shared_by_all_available = \
@@ -195,13 +183,12 @@ static const struct iio_chan_spec bu27034_channels[] = {
/*
* The BU27034 DATA0 and DATA1 channels are both on the visible light
* area (mostly). The data0 sensitivity peaks at 500nm, DATA1 at 600nm.
- * These wave lengths are pretty much on the border of colours making
- * these a poor candidates for R/G/B standardization. Hence they're both
- * marked as clear channels
+ * These wave lengths are cyan(ish) and orange(ish), making these
+ * sub-optiomal candidates for R/G/B standardization. Hence the
+ * colour modifier is omitted.
*/
- BU27034_CHAN_DATA(DATA0, IIO_MOD_LIGHT_CLEAR),
- BU27034_CHAN_DATA(DATA1, IIO_MOD_LIGHT_CLEAR),
- BU27034_CHAN_DATA(DATA2, IIO_MOD_LIGHT_IR),
+ BU27034_CHAN_DATA(DATA0),
+ BU27034_CHAN_DATA(DATA1),
IIO_CHAN_SOFT_TIMESTAMP(4),
};
@@ -215,20 +202,14 @@ struct bu27034_data {
struct mutex mutex;
struct iio_gts gts;
struct task_struct *task;
- __le16 raw[3];
+ __le16 raw[BU27034_NUM_HW_DATA_CHANS];
struct {
u32 mlux;
- __le16 channels[3];
- s64 ts __aligned(8);
+ __le16 channels[BU27034_NUM_HW_DATA_CHANS];
+ aligned_s64 ts;
} scan;
};
-struct bu27034_result {
- u16 ch0;
- u16 ch1;
- u16 ch2;
-};
-
static const struct regmap_range bu27034_volatile_ranges[] = {
{
.range_min = BU27034_REG_SYSTEM_CONTROL,
@@ -238,7 +219,7 @@ static const struct regmap_range bu27034_volatile_ranges[] = {
.range_max = BU27034_REG_MODE_CONTROL4,
}, {
.range_min = BU27034_REG_DATA0_LO,
- .range_max = BU27034_REG_DATA2_HI,
+ .range_max = BU27034_REG_DATA1_HI,
},
};
@@ -250,7 +231,7 @@ static const struct regmap_access_table bu27034_volatile_regs = {
static const struct regmap_range bu27034_read_only_ranges[] = {
{
.range_min = BU27034_REG_DATA0_LO,
- .range_max = BU27034_REG_DATA2_HI,
+ .range_max = BU27034_REG_DATA1_HI,
}, {
.range_min = BU27034_REG_MANUFACTURER_ID,
.range_max = BU27034_REG_MANUFACTURER_ID,
@@ -279,41 +260,17 @@ struct bu27034_gain_check {
static int bu27034_get_gain_sel(struct bu27034_data *data, int chan)
{
+ int reg[] = {
+ [BU27034_CHAN_DATA0] = BU27034_REG_MODE_CONTROL2,
+ [BU27034_CHAN_DATA1] = BU27034_REG_MODE_CONTROL3,
+ };
int ret, val;
- switch (chan) {
- case BU27034_CHAN_DATA0:
- case BU27034_CHAN_DATA1:
- {
- int reg[] = {
- [BU27034_CHAN_DATA0] = BU27034_REG_MODE_CONTROL2,
- [BU27034_CHAN_DATA1] = BU27034_REG_MODE_CONTROL3,
- };
- ret = regmap_read(data->regmap, reg[chan], &val);
- if (ret)
- return ret;
-
- return FIELD_GET(BU27034_MASK_D01_GAIN, val);
- }
- case BU27034_CHAN_DATA2:
- {
- int d2_lo_bits = fls(BU27034_MASK_D2_GAIN_LO);
-
- ret = regmap_read(data->regmap, BU27034_REG_MODE_CONTROL2, &val);
- if (ret)
- return ret;
+ ret = regmap_read(data->regmap, reg[chan], &val);
+ if (ret)
+ return ret;
- /*
- * The data2 channel gain is composed by 5 non continuous bits
- * [7:6], [2:0]. Thus when we combine the 5-bit 'selector'
- * from register value we must right shift the high bits by 3.
- */
- return FIELD_GET(BU27034_MASK_D2_GAIN_HI, val) << d2_lo_bits |
- FIELD_GET(BU27034_MASK_D2_GAIN_LO, val);
- }
- default:
- return -EINVAL;
- }
+ return FIELD_GET(BU27034_MASK_D01_GAIN, val);
}
static int bu27034_get_gain(struct bu27034_data *data, int chan, int *gain)
@@ -396,44 +353,9 @@ static int bu27034_write_gain_sel(struct bu27034_data *data, int chan, int sel)
};
int mask, val;
- if (chan != BU27034_CHAN_DATA0 && chan != BU27034_CHAN_DATA1)
- return -EINVAL;
-
val = FIELD_PREP(BU27034_MASK_D01_GAIN, sel);
-
mask = BU27034_MASK_D01_GAIN;
- if (chan == BU27034_CHAN_DATA0) {
- /*
- * We keep the same gain for channel 2 as we set for channel 0
- * We can't allow them to be individually controlled because
- * setting one will impact also the other. Also, if we don't
- * always update both gains we may result unsupported bit
- * combinations.
- *
- * This is not nice but this is yet another place where the
- * user space must be prepared to surprizes. Namely, see chan 2
- * gain changed when chan 0 gain is changed.
- *
- * This is not fatal for most users though. I don't expect the
- * channel 2 to be used in any generic cases - the intensity
- * values provided by the sensor for IR area are not openly
- * documented. Also, channel 2 is not used for visible light.
- *
- * So, if there is application which is written to utilize the
- * channel 2 - then it is probably specifically targeted to this
- * sensor and knows how to utilize those values. It is safe to
- * hope such user can also cope with the gain changes.
- */
- mask |= BU27034_MASK_D2_GAIN_LO;
-
- /*
- * The D2 gain bits are directly the lowest bits of selector.
- * Just do add those bits to the value
- */
- val |= sel & BU27034_MASK_D2_GAIN_LO;
- }
-
return regmap_update_bits(data->regmap, reg[chan], mask, val);
}
@@ -441,13 +363,6 @@ static int bu27034_set_gain(struct bu27034_data *data, int chan, int gain)
{
int ret;
- /*
- * We don't allow setting channel 2 gain as it messes up the
- * gain for channel 0 - which shares the high bits
- */
- if (chan != BU27034_CHAN_DATA0 && chan != BU27034_CHAN_DATA1)
- return -EINVAL;
-
ret = iio_gts_find_sel_by_gain(&data->gts, gain);
if (ret < 0)
return ret;
@@ -481,30 +396,26 @@ static int bu27034_try_set_int_time(struct bu27034_data *data, int time_us)
int numg = ARRAY_SIZE(gains);
int ret, int_time_old, i;
- mutex_lock(&data->mutex);
+ guard(mutex)(&data->mutex);
ret = bu27034_get_int_time(data);
if (ret < 0)
- goto unlock_out;
+ return ret;
int_time_old = ret;
if (!iio_gts_valid_time(&data->gts, time_us)) {
dev_err(data->dev, "Unsupported integration time %u\n",
time_us);
- ret = -EINVAL;
-
- goto unlock_out;
+ return -EINVAL;
}
- if (time_us == int_time_old) {
- ret = 0;
- goto unlock_out;
- }
+ if (time_us == int_time_old)
+ return 0;
for (i = 0; i < numg; i++) {
ret = bu27034_get_gain(data, gains[i].chan, &gains[i].old_gain);
if (ret)
- goto unlock_out;
+ return 0;
ret = iio_gts_find_new_gain_by_old_gain_time(&data->gts,
gains[i].old_gain,
@@ -520,7 +431,7 @@ static int bu27034_try_set_int_time(struct bu27034_data *data, int time_us)
gains[i].chan, time_us, scale1, scale2);
if (gains[i].new_gain < 0)
- goto unlock_out;
+ return ret;
/*
* If caller requests for integration time change and we
@@ -541,7 +452,7 @@ static int bu27034_try_set_int_time(struct bu27034_data *data, int time_us)
"Total gain increase. Risk of saturation");
ret = iio_gts_get_min_gain(&data->gts);
if (ret < 0)
- goto unlock_out;
+ return ret;
}
dev_dbg(data->dev, "chan %u scale changed\n",
gains[i].chan);
@@ -554,15 +465,10 @@ static int bu27034_try_set_int_time(struct bu27034_data *data, int time_us)
for (i = 0; i < numg; i++) {
ret = bu27034_set_gain(data, gains[i].chan, gains[i].new_gain);
if (ret)
- goto unlock_out;
+ return ret;
}
- ret = bu27034_set_int_time(data, time_us);
-
-unlock_out:
- mutex_unlock(&data->mutex);
-
- return ret;
+ return bu27034_set_int_time(data, time_us);
}
static int bu27034_set_scale(struct bu27034_data *data, int chan,
@@ -571,9 +477,6 @@ static int bu27034_set_scale(struct bu27034_data *data, int chan,
int ret, time_sel, gain_sel, i;
bool found = false;
- if (chan == BU27034_CHAN_DATA2)
- return -EINVAL;
-
if (chan == BU27034_CHAN_ALS) {
if (val == 0 && val2 == 1000000)
return 0;
@@ -581,10 +484,10 @@ static int bu27034_set_scale(struct bu27034_data *data, int chan,
return -EINVAL;
}
- mutex_lock(&data->mutex);
+ guard(mutex)(&data->mutex);
ret = regmap_read(data->regmap, BU27034_REG_MODE_CONTROL1, &time_sel);
if (ret)
- goto unlock_out;
+ return ret;
ret = iio_gts_find_gain_sel_for_scale_using_time(&data->gts, time_sel,
val, val2, &gain_sel);
@@ -598,9 +501,7 @@ static int bu27034_set_scale(struct bu27034_data *data, int chan,
/*
* Populate information for the other channel which should also
- * maintain the scale. (Due to the HW limitations the chan2
- * gets the same gain as chan0, so we only need to explicitly
- * set the chan 0 and 1).
+ * maintain the scale.
*/
if (chan == BU27034_CHAN_DATA0)
gain.chan = BU27034_CHAN_DATA1;
@@ -609,12 +510,12 @@ static int bu27034_set_scale(struct bu27034_data *data, int chan,
ret = bu27034_get_gain(data, gain.chan, &gain.old_gain);
if (ret)
- goto unlock_out;
+ return ret;
/*
* Iterate through all the times to see if we find one which
* can support requested scale for requested channel, while
- * maintaining the scale for other channels
+ * maintaining the scale for the other channel
*/
for (i = 0; i < data->gts.num_itime; i++) {
new_time_sel = data->gts.itime_table[i].sel;
@@ -629,7 +530,7 @@ static int bu27034_set_scale(struct bu27034_data *data, int chan,
if (ret)
continue;
- /* Can the other channel(s) maintain scale? */
+ /* Can the other channel maintain scale? */
ret = iio_gts_find_new_gain_sel_by_old_gain_time(
&data->gts, gain.old_gain, time_sel,
new_time_sel, &gain.new_gain);
@@ -641,126 +542,39 @@ static int bu27034_set_scale(struct bu27034_data *data, int chan,
}
if (!found) {
dev_dbg(data->dev,
- "Can't set scale maintaining other channels\n");
- ret = -EINVAL;
-
- goto unlock_out;
+ "Can't set scale maintaining other channel\n");
+ return -EINVAL;
}
ret = bu27034_set_gain(data, gain.chan, gain.new_gain);
if (ret)
- goto unlock_out;
+ return ret;
ret = regmap_update_bits(data->regmap, BU27034_REG_MODE_CONTROL1,
BU27034_MASK_MEAS_MODE, new_time_sel);
if (ret)
- goto unlock_out;
+ return ret;
}
- ret = bu27034_write_gain_sel(data, chan, gain_sel);
-unlock_out:
- mutex_unlock(&data->mutex);
-
- return ret;
+ return bu27034_write_gain_sel(data, chan, gain_sel);
}
/*
- * for (D1/D0 < 0.87):
- * lx = 0.004521097 * D1 - 0.002663996 * D0 +
- * 0.00012213 * D1 * D1 / D0
- *
- * => 115.7400832 * ch1 / gain1 / mt -
- * 68.1982976 * ch0 / gain0 / mt +
- * 0.00012213 * 25600 * (ch1 / gain1 / mt) * 25600 *
- * (ch1 /gain1 / mt) / (25600 * ch0 / gain0 / mt)
+ * for (D1/D0 < 1.5):
+ * lx = (0.001193 * D0 + (-0.0000747) * D1) * ((D1/D0 – 1.5) * (0.25) + 1)
*
- * A = 0.00012213 * 25600 * (ch1 /gain1 / mt) * 25600 *
- * (ch1 /gain1 / mt) / (25600 * ch0 / gain0 / mt)
- * => 0.00012213 * 25600 * (ch1 /gain1 / mt) *
- * (ch1 /gain1 / mt) / (ch0 / gain0 / mt)
- * => 0.00012213 * 25600 * (ch1 / gain1) * (ch1 /gain1 / mt) /
- * (ch0 / gain0)
- * => 0.00012213 * 25600 * (ch1 / gain1) * (ch1 /gain1 / mt) *
- * gain0 / ch0
- * => 3.126528 * ch1 * ch1 * gain0 / gain1 / gain1 / mt /ch0
+ * => -0.000745625 * D0 + 0.0002515625 * D1 + -0.000018675 * D1 * D1 / D0
*
- * lx = (115.7400832 * ch1 / gain1 - 68.1982976 * ch0 / gain0) /
- * mt + A
- * => (115.7400832 * ch1 / gain1 - 68.1982976 * ch0 / gain0) /
- * mt + 3.126528 * ch1 * ch1 * gain0 / gain1 / gain1 / mt /
- * ch0
+ * => (6.44 * ch1 / gain1 + 19.088 * ch0 / gain0 -
+ * 0.47808 * ch1 * ch1 * gain0 / gain1 / gain1 / ch0) /
+ * mt
*
- * => (115.7400832 * ch1 / gain1 - 68.1982976 * ch0 / gain0 +
- * 3.126528 * ch1 * ch1 * gain0 / gain1 / gain1 / ch0) /
- * mt
+ * Else
+ * lx = 0.001193 * D0 - 0.0000747 * D1
*
- * For (0.87 <= D1/D0 < 1.00)
- * lx = (0.001331* D0 + 0.0000354 * D1) * ((D1/D0 – 0.87) * (0.385) + 1)
- * => (0.001331 * 256 * 100 * ch0 / gain0 / mt + 0.0000354 * 256 *
- * 100 * ch1 / gain1 / mt) * ((D1/D0 - 0.87) * (0.385) + 1)
- * => (34.0736 * ch0 / gain0 / mt + 0.90624 * ch1 / gain1 / mt) *
- * ((D1/D0 - 0.87) * (0.385) + 1)
- * => (34.0736 * ch0 / gain0 / mt + 0.90624 * ch1 / gain1 / mt) *
- * (0.385 * D1/D0 - 0.66505)
- * => (34.0736 * ch0 / gain0 / mt + 0.90624 * ch1 / gain1 / mt) *
- * (0.385 * 256 * 100 * ch1 / gain1 / mt / (256 * 100 * ch0 / gain0 / mt) - 0.66505)
- * => (34.0736 * ch0 / gain0 / mt + 0.90624 * ch1 / gain1 / mt) *
- * (9856 * ch1 / gain1 / mt / (25600 * ch0 / gain0 / mt) + 0.66505)
- * => 13.118336 * ch1 / (gain1 * mt)
- * + 22.66064768 * ch0 / (gain0 * mt)
- * + 8931.90144 * ch1 * ch1 * gain0 /
- * (25600 * ch0 * gain1 * gain1 * mt)
- * + 0.602694912 * ch1 / (gain1 * mt)
- *
- * => [0.3489024 * ch1 * ch1 * gain0 / (ch0 * gain1 * gain1)
- * + 22.66064768 * ch0 / gain0
- * + 13.721030912 * ch1 / gain1
- * ] / mt
- *
- * For (D1/D0 >= 1.00)
- *
- * lx = (0.001331* D0 + 0.0000354 * D1) * ((D1/D0 – 2.0) * (-0.05) + 1)
- * => (0.001331* D0 + 0.0000354 * D1) * (-0.05D1/D0 + 1.1)
- * => (0.001331 * 256 * 100 * ch0 / gain0 / mt + 0.0000354 * 256 *
- * 100 * ch1 / gain1 / mt) * (-0.05D1/D0 + 1.1)
- * => (34.0736 * ch0 / gain0 / mt + 0.90624 * ch1 / gain1 / mt) *
- * (-0.05 * 256 * 100 * ch1 / gain1 / mt / (256 * 100 * ch0 / gain0 / mt) + 1.1)
- * => (34.0736 * ch0 / gain0 / mt + 0.90624 * ch1 / gain1 / mt) *
- * (-1280 * ch1 / (gain1 * mt * 25600 * ch0 / gain0 / mt) + 1.1)
- * => (34.0736 * ch0 * -1280 * ch1 * gain0 * mt /( gain0 * mt * gain1 * mt * 25600 * ch0)
- * + 34.0736 * 1.1 * ch0 / (gain0 * mt)
- * + 0.90624 * ch1 * -1280 * ch1 *gain0 * mt / (gain1 * mt *gain1 * mt * 25600 * ch0)
- * + 1.1 * 0.90624 * ch1 / (gain1 * mt)
- * => -43614.208 * ch1 / (gain1 * mt * 25600)
- * + 37.48096 ch0 / (gain0 * mt)
- * - 1159.9872 * ch1 * ch1 * gain0 / (gain1 * gain1 * mt * 25600 * ch0)
- * + 0.996864 ch1 / (gain1 * mt)
- * => [
- * - 0.045312 * ch1 * ch1 * gain0 / (gain1 * gain1 * ch0)
- * - 0.706816 * ch1 / gain1
- * + 37.48096 ch0 /gain0
- * ] * mt
- *
- *
- * So, the first case (D1/D0 < 0.87) can be computed to a form:
- *
- * lx = (3.126528 * ch1 * ch1 * gain0 / (ch0 * gain1 * gain1) +
- * 115.7400832 * ch1 / gain1 +
- * -68.1982976 * ch0 / gain0
- * / mt
- *
- * Second case (0.87 <= D1/D0 < 1.00) goes to form:
- *
- * => [0.3489024 * ch1 * ch1 * gain0 / (ch0 * gain1 * gain1) +
- * 13.721030912 * ch1 / gain1 +
- * 22.66064768 * ch0 / gain0
- * ] / mt
- *
- * Third case (D1/D0 >= 1.00) goes to form:
- * => [-0.045312 * ch1 * ch1 * gain0 / (ch0 * gain1 * gain1) +
- * -0.706816 * ch1 / gain1 +
- * 37.48096 ch0 /(gain0
- * ] / mt
+ * => (1.91232 * ch1 / gain1 + 30.5408 * ch0 / gain0 +
+ * [0 * ch1 * ch1 * gain0 / gain1 / gain1 / ch0] ) /
+ * mt
*
* This can be unified to format:
* lx = [
@@ -770,19 +584,14 @@ unlock_out:
* ] / mt
*
* For case 1:
- * A = 3.126528,
- * B = 115.7400832
- * C = -68.1982976
+ * A = -0.47808,
+ * B = 6.44,
+ * C = 19.088
*
* For case 2:
- * A = 0.3489024
- * B = 13.721030912
- * C = 22.66064768
- *
- * For case 3:
- * A = -0.045312
- * B = -0.706816
- * C = 37.48096
+ * A = 0
+ * B = 1.91232
+ * C = 30.5408
*/
struct bu27034_lx_coeff {
@@ -887,21 +696,16 @@ static int bu27034_fixp_calc_lx(unsigned int ch0, unsigned int ch1,
{
static const struct bu27034_lx_coeff coeff[] = {
{
- .A = 31265280, /* 3.126528 */
- .B = 1157400832, /*115.7400832 */
- .C = 681982976, /* -68.1982976 */
- .is_neg = {false, false, true},
+ .A = 4780800, /* -0.47808 */
+ .B = 64400000, /* 6.44 */
+ .C = 190880000, /* 19.088 */
+ .is_neg = { true, false, false },
}, {
- .A = 3489024, /* 0.3489024 */
- .B = 137210309, /* 13.721030912 */
- .C = 226606476, /* 22.66064768 */
+ .A = 0, /* 0 */
+ .B = 19123200, /* 1.91232 */
+ .C = 305408000, /* 30.5408 */
/* All terms positive */
- }, {
- .A = 453120, /* -0.045312 */
- .B = 7068160, /* -0.706816 */
- .C = 374809600, /* 37.48096 */
- .is_neg = {true, true, false},
- }
+ },
};
const struct bu27034_lx_coeff *c = &coeff[coeff_idx];
u64 res = 0, terms[3];
@@ -973,7 +777,6 @@ static int bu27034_read_result(struct bu27034_data *data, int chan, int *res)
int reg[] = {
[BU27034_CHAN_DATA0] = BU27034_REG_DATA0_LO,
[BU27034_CHAN_DATA1] = BU27034_REG_DATA1_LO,
- [BU27034_CHAN_DATA2] = BU27034_REG_DATA2_LO,
};
int valid, ret;
__le16 val;
@@ -1040,7 +843,7 @@ static int bu27034_get_single_result(struct bu27034_data *data, int chan,
{
int ret;
- if (chan < BU27034_CHAN_DATA0 || chan > BU27034_CHAN_DATA2)
+ if (chan < BU27034_CHAN_DATA0 || chan > BU27034_CHAN_DATA1)
return -EINVAL;
ret = bu27034_meas_set(data, true);
@@ -1065,12 +868,10 @@ static int bu27034_get_single_result(struct bu27034_data *data, int chan,
* D1 = data1/ch1_gain/meas_time_ms * 25600
*
* Then:
- * if (D1/D0 < 0.87)
- * lx = (0.001331 * D0 + 0.0000354 * D1) * ((D1 / D0 - 0.87) * 3.45 + 1)
- * else if (D1/D0 < 1)
- * lx = (0.001331 * D0 + 0.0000354 * D1) * ((D1 / D0 - 0.87) * 0.385 + 1)
- * else
- * lx = (0.001331 * D0 + 0.0000354 * D1) * ((D1 / D0 - 2) * -0.05 + 1)
+ * If (D1/D0 < 1.5)
+ * lx = (0.001193 * D0 + (-0.0000747) * D1) * ((D1 / D0 – 1.5) * 0.25 + 1)
+ * Else
+ * lx = (0.001193 * D0 + (-0.0000747) * D1)
*
* We use it here. Users who have for example some colored lens
* need to modify the calculation but I hope this gives a starting point for
@@ -1121,12 +922,10 @@ static int bu27034_calc_mlux(struct bu27034_data *data, __le16 *res, int *val)
d1_d0_ratio_scaled /= ch0 * gain1;
}
- if (d1_d0_ratio_scaled < 87)
+ if (d1_d0_ratio_scaled < 150)
ret = bu27034_fixp_calc_lx(ch0, ch1, gain0, gain1, meastime, 0);
- else if (d1_d0_ratio_scaled < 100)
- ret = bu27034_fixp_calc_lx(ch0, ch1, gain0, gain1, meastime, 1);
else
- ret = bu27034_fixp_calc_lx(ch0, ch1, gain0, gain1, meastime, 2);
+ ret = bu27034_fixp_calc_lx(ch0, ch1, gain0, gain1, meastime, 1);
if (ret < 0)
return ret;
@@ -1139,7 +938,7 @@ static int bu27034_calc_mlux(struct bu27034_data *data, __le16 *res, int *val)
static int bu27034_get_mlux(struct bu27034_data *data, int chan, int *val)
{
- __le16 res[3];
+ __le16 res[BU27034_NUM_HW_DATA_CHANS];
int ret;
ret = bu27034_meas_set(data, true);
@@ -1177,6 +976,13 @@ static int bu27034_read_raw(struct iio_dev *idev,
return IIO_VAL_INT_PLUS_MICRO;
+ case IIO_CHAN_INFO_HARDWAREGAIN:
+ ret = bu27034_get_gain(data, chan->channel, val);
+ if (ret)
+ return ret;
+
+ return IIO_VAL_INT;
+
case IIO_CHAN_INFO_SCALE:
return bu27034_get_scale(data, chan->channel, val, val2);
@@ -1192,9 +998,8 @@ static int bu27034_read_raw(struct iio_dev *idev,
return -EINVAL;
/* Don't mess with measurement enabling while buffering */
- ret = iio_device_claim_direct_mode(idev);
- if (ret)
- return ret;
+ if (!iio_device_claim_direct(idev))
+ return -EBUSY;
mutex_lock(&data->mutex);
/*
@@ -1205,7 +1010,7 @@ static int bu27034_read_raw(struct iio_dev *idev,
ret = result_get(data, chan->channel, val);
mutex_unlock(&data->mutex);
- iio_device_release_direct_mode(idev);
+ iio_device_release_direct(idev);
if (ret)
return ret;
@@ -1221,12 +1026,17 @@ static int bu27034_write_raw_get_fmt(struct iio_dev *indio_dev,
struct iio_chan_spec const *chan,
long mask)
{
+ struct bu27034_data *data = iio_priv(indio_dev);
switch (mask) {
case IIO_CHAN_INFO_SCALE:
return IIO_VAL_INT_PLUS_NANO;
case IIO_CHAN_INFO_INT_TIME:
return IIO_VAL_INT_PLUS_MICRO;
+ case IIO_CHAN_INFO_HARDWAREGAIN:
+ dev_dbg(data->dev,
+ "HARDWAREGAIN is read-only, use scale to set\n");
+ return -EINVAL;
default:
return -EINVAL;
}
@@ -1239,9 +1049,8 @@ static int bu27034_write_raw(struct iio_dev *idev,
struct bu27034_data *data = iio_priv(idev);
int ret;
- ret = iio_device_claim_direct_mode(idev);
- if (ret)
- return ret;
+ if (!iio_device_claim_direct(idev))
+ return -EBUSY;
switch (mask) {
case IIO_CHAN_INFO_SCALE:
@@ -1258,7 +1067,7 @@ static int bu27034_write_raw(struct iio_dev *idev,
break;
}
- iio_device_release_direct_mode(idev);
+ iio_device_release_direct(idev);
return ret;
}
@@ -1396,42 +1205,33 @@ static int bu27034_buffer_enable(struct iio_dev *idev)
struct task_struct *task;
int ret;
- mutex_lock(&data->mutex);
+ guard(mutex)(&data->mutex);
ret = bu27034_meas_set(data, true);
if (ret)
- goto unlock_out;
+ return ret;
task = kthread_run(bu27034_buffer_thread, idev,
"bu27034-buffering-%u",
iio_device_id(idev));
- if (IS_ERR(task)) {
- ret = PTR_ERR(task);
- goto unlock_out;
- }
+ if (IS_ERR(task))
+ return PTR_ERR(task);
data->task = task;
-unlock_out:
- mutex_unlock(&data->mutex);
-
- return ret;
+ return 0;
}
static int bu27034_buffer_disable(struct iio_dev *idev)
{
struct bu27034_data *data = iio_priv(idev);
- int ret;
- mutex_lock(&data->mutex);
+ guard(mutex)(&data->mutex);
if (data->task) {
kthread_stop(data->task);
data->task = NULL;
}
- ret = bu27034_meas_set(data, false);
- mutex_unlock(&data->mutex);
-
- return ret;
+ return bu27034_meas_set(data, false);
}
static const struct iio_buffer_setup_ops bu27034_buffer_ops = {
@@ -1507,7 +1307,7 @@ static int bu27034_probe(struct i2c_client *i2c)
}
static const struct of_device_id bu27034_of_match[] = {
- { .compatible = "rohm,bu27034" },
+ { .compatible = "rohm,bu27034anuc" },
{ }
};
MODULE_DEVICE_TABLE(of, bu27034_of_match);
@@ -1525,4 +1325,4 @@ module_i2c_driver(bu27034_i2c_driver);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Matti Vaittinen <matti.vaittinen@fi.rohmeurope.com>");
MODULE_DESCRIPTION("ROHM BU27034 ambient light sensor driver");
-MODULE_IMPORT_NS(IIO_GTS_HELPER);
+MODULE_IMPORT_NS("IIO_GTS_HELPER");
diff --git a/drivers/iio/light/rpr0521.c b/drivers/iio/light/rpr0521.c
index 40d5732b5e32..92e7552f3e39 100644
--- a/drivers/iio/light/rpr0521.c
+++ b/drivers/iio/light/rpr0521.c
@@ -11,6 +11,7 @@
#include <linux/module.h>
#include <linux/mod_devicetable.h>
+#include <linux/cleanup.h>
#include <linux/init.h>
#include <linux/i2c.h>
#include <linux/regmap.h>
@@ -203,7 +204,7 @@ struct rpr0521_data {
struct {
__le16 channels[3];
u8 garbage;
- s64 ts __aligned(8);
+ aligned_s64 ts;
} scan;
};
@@ -438,18 +439,6 @@ static irqreturn_t rpr0521_drdy_irq_thread(int irq, void *private)
return IRQ_NONE;
}
-static irqreturn_t rpr0521_trigger_consumer_store_time(int irq, void *p)
-{
- struct iio_poll_func *pf = p;
- struct iio_dev *indio_dev = pf->indio_dev;
-
- /* Other trigger polls store time here. */
- if (!iio_trigger_using_own(indio_dev))
- pf->timestamp = iio_get_time_ns(indio_dev);
-
- return IRQ_WAKE_THREAD;
-}
-
static irqreturn_t rpr0521_trigger_consumer_handler(int irq, void *p)
{
struct iio_poll_func *pf = p;
@@ -716,50 +705,58 @@ static int rpr0521_write_ps_offset(struct rpr0521_data *data, int offset)
return ret;
}
+static int rpr0521_read_info_raw(struct rpr0521_data *data,
+ struct iio_chan_spec const *chan,
+ int *val)
+{
+ u8 device_mask;
+ __le16 raw_data;
+ int ret;
+
+ device_mask = rpr0521_data_reg[chan->address].device_mask;
+
+ guard(mutex)(&data->lock);
+ ret = rpr0521_set_power_state(data, true, device_mask);
+ if (ret < 0)
+ return ret;
+
+ ret = regmap_bulk_read(data->regmap,
+ rpr0521_data_reg[chan->address].address,
+ &raw_data, sizeof(raw_data));
+ if (ret < 0) {
+ rpr0521_set_power_state(data, false, device_mask);
+ return ret;
+ }
+
+ ret = rpr0521_set_power_state(data, false, device_mask);
+ if (ret < 0)
+ return ret;
+
+ *val = le16_to_cpu(raw_data);
+
+ return 0;
+}
+
static int rpr0521_read_raw(struct iio_dev *indio_dev,
struct iio_chan_spec const *chan, int *val,
int *val2, long mask)
{
struct rpr0521_data *data = iio_priv(indio_dev);
int ret;
- int busy;
- u8 device_mask;
- __le16 raw_data;
switch (mask) {
case IIO_CHAN_INFO_RAW:
if (chan->type != IIO_INTENSITY && chan->type != IIO_PROXIMITY)
return -EINVAL;
- busy = iio_device_claim_direct_mode(indio_dev);
- if (busy)
+ if (!iio_device_claim_direct(indio_dev))
return -EBUSY;
- device_mask = rpr0521_data_reg[chan->address].device_mask;
-
- mutex_lock(&data->lock);
- ret = rpr0521_set_power_state(data, true, device_mask);
- if (ret < 0)
- goto rpr0521_read_raw_out;
-
- ret = regmap_bulk_read(data->regmap,
- rpr0521_data_reg[chan->address].address,
- &raw_data, sizeof(raw_data));
- if (ret < 0) {
- rpr0521_set_power_state(data, false, device_mask);
- goto rpr0521_read_raw_out;
- }
-
- ret = rpr0521_set_power_state(data, false, device_mask);
-
-rpr0521_read_raw_out:
- mutex_unlock(&data->lock);
- iio_device_release_direct_mode(indio_dev);
+ ret = rpr0521_read_info_raw(data, chan, val);
+ iio_device_release_direct(indio_dev);
if (ret < 0)
return ret;
- *val = le16_to_cpu(raw_data);
-
return IIO_VAL_INT;
case IIO_CHAN_INFO_SCALE:
@@ -1016,7 +1013,7 @@ static int rpr0521_probe(struct i2c_client *client)
/* Trigger consumer setup */
ret = devm_iio_triggered_buffer_setup(indio_dev->dev.parent,
indio_dev,
- rpr0521_trigger_consumer_store_time,
+ iio_pollfunc_store_time,
rpr0521_trigger_consumer_handler,
&rpr0521_buffer_setup_ops);
if (ret < 0) {
@@ -1109,7 +1106,7 @@ static const struct acpi_device_id rpr0521_acpi_match[] = {
MODULE_DEVICE_TABLE(acpi, rpr0521_acpi_match);
static const struct i2c_device_id rpr0521_id[] = {
- {"rpr0521", 0},
+ { "rpr0521" },
{ }
};
diff --git a/drivers/iio/light/si1133.c b/drivers/iio/light/si1133.c
index ea2c437199c0..44fa152dbd24 100644
--- a/drivers/iio/light/si1133.c
+++ b/drivers/iio/light/si1133.c
@@ -17,7 +17,7 @@
#include <linux/util_macros.h>
-#include <asm/unaligned.h>
+#include <linux/unaligned.h>
#define SI1133_REG_PART_ID 0x00
#define SI1133_REG_REV_ID 0x01
@@ -1055,7 +1055,7 @@ static int si1133_probe(struct i2c_client *client)
}
static const struct i2c_device_id si1133_ids[] = {
- { "si1133", 0 },
+ { "si1133" },
{ }
};
MODULE_DEVICE_TABLE(i2c, si1133_ids);
diff --git a/drivers/iio/light/si1145.c b/drivers/iio/light/si1145.c
index 77666b780a5c..4aa02afd853e 100644
--- a/drivers/iio/light/si1145.c
+++ b/drivers/iio/light/si1145.c
@@ -465,11 +465,10 @@ static irqreturn_t si1145_trigger_handler(int irq, void *private)
goto done;
}
- for_each_set_bit(i, indio_dev->active_scan_mask,
- indio_dev->masklength) {
+ iio_for_each_active_channel(indio_dev, i) {
int run = 1;
- while (i + run < indio_dev->masklength) {
+ while (i + run < iio_get_masklength(indio_dev)) {
if (!test_bit(i + run, indio_dev->active_scan_mask))
break;
if (indio_dev->channels[i + run].address !=
@@ -514,7 +513,7 @@ static int si1145_set_chlist(struct iio_dev *indio_dev, unsigned long scan_mask)
if (data->scan_mask == scan_mask)
return 0;
- for_each_set_bit(i, &scan_mask, indio_dev->masklength) {
+ for_each_set_bit(i, &scan_mask, iio_get_masklength(indio_dev)) {
switch (indio_dev->channels[i].address) {
case SI1145_REG_ALSVIS_DATA:
reg |= SI1145_CHLIST_EN_ALSVIS;
@@ -634,11 +633,10 @@ static int si1145_read_raw(struct iio_dev *indio_dev,
case IIO_VOLTAGE:
case IIO_TEMP:
case IIO_UVINDEX:
- ret = iio_device_claim_direct_mode(indio_dev);
- if (ret)
- return ret;
+ if (!iio_device_claim_direct(indio_dev))
+ return -EBUSY;
ret = si1145_measure(indio_dev, chan);
- iio_device_release_direct_mode(indio_dev);
+ iio_device_release_direct(indio_dev);
if (ret < 0)
return ret;
@@ -751,18 +749,17 @@ static int si1145_write_raw(struct iio_dev *indio_dev,
return -EINVAL;
}
- ret = iio_device_claim_direct_mode(indio_dev);
- if (ret)
- return ret;
+ if (!iio_device_claim_direct(indio_dev))
+ return -EBUSY;
ret = si1145_param_set(data, reg1, val);
if (ret < 0) {
- iio_device_release_direct_mode(indio_dev);
+ iio_device_release_direct(indio_dev);
return ret;
}
/* Set recovery period to one's complement of gain */
ret = si1145_param_set(data, reg2, (~val & 0x07) << 4);
- iio_device_release_direct_mode(indio_dev);
+ iio_device_release_direct(indio_dev);
return ret;
case IIO_CHAN_INFO_RAW:
if (chan->type != IIO_CURRENT)
@@ -774,19 +771,18 @@ static int si1145_write_raw(struct iio_dev *indio_dev,
reg1 = SI1145_PS_LED_REG(chan->channel);
shift = SI1145_PS_LED_SHIFT(chan->channel);
- ret = iio_device_claim_direct_mode(indio_dev);
- if (ret)
- return ret;
+ if (!iio_device_claim_direct(indio_dev))
+ return -EBUSY;
ret = i2c_smbus_read_byte_data(data->client, reg1);
if (ret < 0) {
- iio_device_release_direct_mode(indio_dev);
+ iio_device_release_direct(indio_dev);
return ret;
}
ret = i2c_smbus_write_byte_data(data->client, reg1,
(ret & ~(0x0f << shift)) |
((val & 0x0f) << shift));
- iio_device_release_direct_mode(indio_dev);
+ iio_device_release_direct(indio_dev);
return ret;
case IIO_CHAN_INFO_SAMP_FREQ:
return si1145_store_samp_freq(data, val);
diff --git a/drivers/iio/light/st_uvis25.h b/drivers/iio/light/st_uvis25.h
index 283086887caf..1f93e3dc45c2 100644
--- a/drivers/iio/light/st_uvis25.h
+++ b/drivers/iio/light/st_uvis25.h
@@ -30,7 +30,7 @@ struct st_uvis25_hw {
/* Ensure timestamp is naturally aligned */
struct {
u8 chan;
- s64 ts __aligned(8);
+ aligned_s64 ts;
} scan;
};
diff --git a/drivers/iio/light/st_uvis25_core.c b/drivers/iio/light/st_uvis25_core.c
index 50f95c5d2060..124a8f9204a9 100644
--- a/drivers/iio/light/st_uvis25_core.c
+++ b/drivers/iio/light/st_uvis25_core.c
@@ -117,9 +117,8 @@ static int st_uvis25_read_raw(struct iio_dev *iio_dev,
{
int ret;
- ret = iio_device_claim_direct_mode(iio_dev);
- if (ret)
- return ret;
+ if (!iio_device_claim_direct(iio_dev))
+ return -EBUSY;
switch (mask) {
case IIO_CHAN_INFO_PROCESSED: {
@@ -144,7 +143,7 @@ static int st_uvis25_read_raw(struct iio_dev *iio_dev,
break;
}
- iio_device_release_direct_mode(iio_dev);
+ iio_device_release_direct(iio_dev);
return ret;
}
@@ -174,8 +173,7 @@ static int st_uvis25_allocate_trigger(struct iio_dev *iio_dev)
unsigned long irq_type;
int err;
- irq_type = irqd_get_trigger_type(irq_get_irq_data(hw->irq));
-
+ irq_type = irq_get_trigger_type(hw->irq);
switch (irq_type) {
case IRQF_TRIGGER_HIGH:
case IRQF_TRIGGER_RISING:
@@ -291,7 +289,7 @@ int st_uvis25_probe(struct device *dev, int irq, struct regmap *regmap)
if (!iio_dev)
return -ENOMEM;
- dev_set_drvdata(dev, (void *)iio_dev);
+ dev_set_drvdata(dev, iio_dev);
hw = iio_priv(iio_dev);
hw->irq = irq;
@@ -323,15 +321,15 @@ int st_uvis25_probe(struct device *dev, int irq, struct regmap *regmap)
return devm_iio_device_register(dev, iio_dev);
}
-EXPORT_SYMBOL_NS(st_uvis25_probe, IIO_UVIS25);
+EXPORT_SYMBOL_NS(st_uvis25_probe, "IIO_UVIS25");
static int st_uvis25_suspend(struct device *dev)
{
struct iio_dev *iio_dev = dev_get_drvdata(dev);
struct st_uvis25_hw *hw = iio_priv(iio_dev);
- return regmap_update_bits(hw->regmap, ST_UVIS25_REG_CTRL1_ADDR,
- ST_UVIS25_REG_ODR_MASK, 0);
+ return regmap_clear_bits(hw->regmap, ST_UVIS25_REG_CTRL1_ADDR,
+ ST_UVIS25_REG_ODR_MASK);
}
static int st_uvis25_resume(struct device *dev)
diff --git a/drivers/iio/light/st_uvis25_i2c.c b/drivers/iio/light/st_uvis25_i2c.c
index 6bc2ddfb77ca..5d9bb4d9be63 100644
--- a/drivers/iio/light/st_uvis25_i2c.c
+++ b/drivers/iio/light/st_uvis25_i2c.c
@@ -41,13 +41,13 @@ static int st_uvis25_i2c_probe(struct i2c_client *client)
static const struct of_device_id st_uvis25_i2c_of_match[] = {
{ .compatible = "st,uvis25", },
- {},
+ { }
};
MODULE_DEVICE_TABLE(of, st_uvis25_i2c_of_match);
static const struct i2c_device_id st_uvis25_i2c_id_table[] = {
{ ST_UVIS25_DEV_NAME },
- {},
+ { }
};
MODULE_DEVICE_TABLE(i2c, st_uvis25_i2c_id_table);
@@ -65,4 +65,4 @@ module_i2c_driver(st_uvis25_driver);
MODULE_AUTHOR("Lorenzo Bianconi <lorenzo.bianconi83@gmail.com>");
MODULE_DESCRIPTION("STMicroelectronics uvis25 i2c driver");
MODULE_LICENSE("GPL v2");
-MODULE_IMPORT_NS(IIO_UVIS25);
+MODULE_IMPORT_NS("IIO_UVIS25");
diff --git a/drivers/iio/light/st_uvis25_spi.c b/drivers/iio/light/st_uvis25_spi.c
index 86a232320d7d..a5aad74ce73e 100644
--- a/drivers/iio/light/st_uvis25_spi.c
+++ b/drivers/iio/light/st_uvis25_spi.c
@@ -42,13 +42,13 @@ static int st_uvis25_spi_probe(struct spi_device *spi)
static const struct of_device_id st_uvis25_spi_of_match[] = {
{ .compatible = "st,uvis25", },
- {},
+ { }
};
MODULE_DEVICE_TABLE(of, st_uvis25_spi_of_match);
static const struct spi_device_id st_uvis25_spi_id_table[] = {
{ ST_UVIS25_DEV_NAME },
- {},
+ { }
};
MODULE_DEVICE_TABLE(spi, st_uvis25_spi_id_table);
@@ -66,4 +66,4 @@ module_spi_driver(st_uvis25_driver);
MODULE_AUTHOR("Lorenzo Bianconi <lorenzo.bianconi83@gmail.com>");
MODULE_DESCRIPTION("STMicroelectronics uvis25 spi driver");
MODULE_LICENSE("GPL v2");
-MODULE_IMPORT_NS(IIO_UVIS25);
+MODULE_IMPORT_NS("IIO_UVIS25");
diff --git a/drivers/iio/light/stk3310.c b/drivers/iio/light/stk3310.c
index 7b71ad71d78d..deada9ba4748 100644
--- a/drivers/iio/light/stk3310.c
+++ b/drivers/iio/light/stk3310.c
@@ -35,8 +35,11 @@
#define STK3310_STATE_EN_ALS BIT(1)
#define STK3310_STATE_STANDBY 0x00
+#define STK3013_CHIP_ID_VAL 0x31
#define STK3310_CHIP_ID_VAL 0x13
#define STK3311_CHIP_ID_VAL 0x1D
+#define STK3311A_CHIP_ID_VAL 0x15
+#define STK3311S34_CHIP_ID_VAL 0x1E
#define STK3311X_CHIP_ID_VAL 0x12
#define STK3335_CHIP_ID_VAL 0x51
#define STK3310_PSINT_EN 0x01
@@ -81,6 +84,16 @@ static const struct reg_field stk3310_reg_field_flag_psint =
static const struct reg_field stk3310_reg_field_flag_nf =
REG_FIELD(STK3310_REG_FLAG, 0, 0);
+static const u8 stk3310_chip_ids[] = {
+ STK3013_CHIP_ID_VAL,
+ STK3310_CHIP_ID_VAL,
+ STK3311A_CHIP_ID_VAL,
+ STK3311S34_CHIP_ID_VAL,
+ STK3311X_CHIP_ID_VAL,
+ STK3311_CHIP_ID_VAL,
+ STK3335_CHIP_ID_VAL,
+};
+
/* Estimate maximum proximity values with regard to measurement scale. */
static const int stk3310_ps_max[4] = {
STK3310_PS_MAX_VAL / 640,
@@ -152,7 +165,7 @@ static const struct iio_chan_spec_ext_info stk3310_ext_info[] = {
.shared = IIO_SEPARATE,
.read = stk3310_read_near_level,
},
- { /* sentinel */ }
+ { }
};
static const struct iio_chan_spec stk3310_channels[] = {
@@ -197,6 +210,16 @@ static const struct attribute_group stk3310_attribute_group = {
.attrs = stk3310_attributes
};
+static int stk3310_check_chip_id(const u8 chip_id)
+{
+ for (int i = 0; i < ARRAY_SIZE(stk3310_chip_ids); i++) {
+ if (chip_id == stk3310_chip_ids[i])
+ return 0;
+ }
+
+ return -ENODEV;
+}
+
static int stk3310_get_index(const int table[][2], int table_size,
int val, int val2)
{
@@ -301,15 +324,12 @@ static int stk3310_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)
+ bool state)
{
int ret;
struct stk3310_data *data = iio_priv(indio_dev);
struct i2c_client *client = data->client;
- if (state < 0 || state > 7)
- return -EINVAL;
-
/* Set INT_PS value */
mutex_lock(&data->lock);
ret = regmap_field_write(data->reg_int_ps, state);
@@ -473,13 +493,9 @@ static int stk3310_init(struct iio_dev *indio_dev)
if (ret < 0)
return ret;
- 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;
- }
+ ret = stk3310_check_chip_id(chipid);
+ if (ret < 0)
+ dev_info(&client->dev, "new unknown chip id: 0x%x\n", chipid);
state = STK3310_STATE_EN_ALS | STK3310_STATE_EN_PS;
ret = stk3310_set_state(data, state);
@@ -683,27 +699,29 @@ static DEFINE_SIMPLE_DEV_PM_OPS(stk3310_pm_ops, stk3310_suspend,
stk3310_resume);
static const struct i2c_device_id stk3310_i2c_id[] = {
- {"STK3310", 0},
- {"STK3311", 0},
- {"STK3335", 0},
- {}
+ { "STK3013" },
+ { "STK3310" },
+ { "STK3311" },
+ { "STK3335" },
+ { }
};
MODULE_DEVICE_TABLE(i2c, stk3310_i2c_id);
static const struct acpi_device_id stk3310_acpi_id[] = {
+ {"STK3013", 0},
{"STK3310", 0},
{"STK3311", 0},
- {"STK3335", 0},
- {}
+ { }
};
MODULE_DEVICE_TABLE(acpi, stk3310_acpi_id);
static const struct of_device_id stk3310_of_match[] = {
+ { .compatible = "sensortek,stk3013", },
{ .compatible = "sensortek,stk3310", },
{ .compatible = "sensortek,stk3311", },
{ .compatible = "sensortek,stk3335", },
- {}
+ { }
};
MODULE_DEVICE_TABLE(of, stk3310_of_match);
diff --git a/drivers/iio/light/tcs3414.c b/drivers/iio/light/tcs3414.c
index dcdd85b006be..39268f855c77 100644
--- a/drivers/iio/light/tcs3414.c
+++ b/drivers/iio/light/tcs3414.c
@@ -56,7 +56,7 @@ struct tcs3414_data {
/* Ensure timestamp is naturally aligned */
struct {
u16 chans[4];
- s64 timestamp __aligned(8);
+ aligned_s64 timestamp;
} scan;
};
@@ -134,16 +134,15 @@ static int tcs3414_read_raw(struct iio_dev *indio_dev,
switch (mask) {
case IIO_CHAN_INFO_RAW:
- ret = iio_device_claim_direct_mode(indio_dev);
- if (ret)
- return ret;
+ if (!iio_device_claim_direct(indio_dev))
+ return -EBUSY;
ret = tcs3414_req_data(data);
if (ret < 0) {
- iio_device_release_direct_mode(indio_dev);
+ iio_device_release_direct(indio_dev);
return ret;
}
ret = i2c_smbus_read_word_data(data->client, chan->address);
- iio_device_release_direct_mode(indio_dev);
+ iio_device_release_direct(indio_dev);
if (ret < 0)
return ret;
*val = ret;
@@ -206,8 +205,7 @@ static irqreturn_t tcs3414_trigger_handler(int irq, void *p)
struct tcs3414_data *data = iio_priv(indio_dev);
int i, j = 0;
- for_each_set_bit(i, indio_dev->active_scan_mask,
- indio_dev->masklength) {
+ iio_for_each_active_channel(indio_dev, i) {
int ret = i2c_smbus_read_word_data(data->client,
TCS3414_DATA_GREEN + 2*i);
if (ret < 0)
@@ -363,7 +361,7 @@ static DEFINE_SIMPLE_DEV_PM_OPS(tcs3414_pm_ops, tcs3414_suspend,
tcs3414_resume);
static const struct i2c_device_id tcs3414_id[] = {
- { "tcs3414", 0 },
+ { "tcs3414" },
{ }
};
MODULE_DEVICE_TABLE(i2c, tcs3414_id);
diff --git a/drivers/iio/light/tcs3472.c b/drivers/iio/light/tcs3472.c
index 75fcf2c93717..0f8bf8503edd 100644
--- a/drivers/iio/light/tcs3472.c
+++ b/drivers/iio/light/tcs3472.c
@@ -67,7 +67,7 @@ struct tcs3472_data {
/* Ensure timestamp is naturally aligned */
struct {
u16 chans[4];
- s64 timestamp __aligned(8);
+ aligned_s64 timestamp;
} scan;
};
@@ -148,16 +148,15 @@ static int tcs3472_read_raw(struct iio_dev *indio_dev,
switch (mask) {
case IIO_CHAN_INFO_RAW:
- ret = iio_device_claim_direct_mode(indio_dev);
- if (ret)
- return ret;
+ if (!iio_device_claim_direct(indio_dev))
+ return -EBUSY;
ret = tcs3472_req_data(data);
if (ret < 0) {
- iio_device_release_direct_mode(indio_dev);
+ iio_device_release_direct(indio_dev);
return ret;
}
ret = i2c_smbus_read_word_data(data->client, chan->address);
- iio_device_release_direct_mode(indio_dev);
+ iio_device_release_direct(indio_dev);
if (ret < 0)
return ret;
*val = ret;
@@ -327,7 +326,7 @@ static int tcs3472_read_event_config(struct iio_dev *indio_dev,
static int tcs3472_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)
+ enum iio_event_direction dir, bool state)
{
struct tcs3472_data *data = iio_priv(indio_dev);
int ret = 0;
@@ -383,8 +382,7 @@ static irqreturn_t tcs3472_trigger_handler(int irq, void *p)
if (ret < 0)
goto done;
- for_each_set_bit(i, indio_dev->active_scan_mask,
- indio_dev->masklength) {
+ iio_for_each_active_channel(indio_dev, i) {
ret = i2c_smbus_read_word_data(data->client,
TCS3472_CDATA + 2*i);
if (ret < 0)
@@ -599,7 +597,7 @@ static DEFINE_SIMPLE_DEV_PM_OPS(tcs3472_pm_ops, tcs3472_suspend,
tcs3472_resume);
static const struct i2c_device_id tcs3472_id[] = {
- { "tcs3472", 0 },
+ { "tcs3472" },
{ }
};
MODULE_DEVICE_TABLE(i2c, tcs3472_id);
diff --git a/drivers/iio/light/tsl2563.c b/drivers/iio/light/tsl2563.c
index 1a6f514bced6..f2af1cd7c2d1 100644
--- a/drivers/iio/light/tsl2563.c
+++ b/drivers/iio/light/tsl2563.c
@@ -630,7 +630,7 @@ static irqreturn_t tsl2563_event_handler(int irq, void *private)
static int tsl2563_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)
+ enum iio_event_direction dir, bool state)
{
struct tsl2563_chip *chip = iio_priv(indio_dev);
int ret = 0;
@@ -843,7 +843,7 @@ static const struct i2c_device_id tsl2563_id[] = {
{ "tsl2561", 1 },
{ "tsl2562", 2 },
{ "tsl2563", 3 },
- {}
+ { }
};
MODULE_DEVICE_TABLE(i2c, tsl2563_id);
@@ -852,7 +852,7 @@ static const struct of_device_id tsl2563_of_match[] = {
{ .compatible = "amstaos,tsl2561" },
{ .compatible = "amstaos,tsl2562" },
{ .compatible = "amstaos,tsl2563" },
- {}
+ { }
};
MODULE_DEVICE_TABLE(of, tsl2563_of_match);
diff --git a/drivers/iio/light/tsl2583.c b/drivers/iio/light/tsl2583.c
index 02ad11611b9c..fc3b0c4226be 100644
--- a/drivers/iio/light/tsl2583.c
+++ b/drivers/iio/light/tsl2583.c
@@ -922,7 +922,7 @@ static const struct i2c_device_id tsl2583_idtable[] = {
{ "tsl2580", 0 },
{ "tsl2581", 1 },
{ "tsl2583", 2 },
- {}
+ { }
};
MODULE_DEVICE_TABLE(i2c, tsl2583_idtable);
@@ -930,7 +930,7 @@ static const struct of_device_id tsl2583_of_match[] = {
{ .compatible = "amstaos,tsl2580", },
{ .compatible = "amstaos,tsl2581", },
{ .compatible = "amstaos,tsl2583", },
- { },
+ { }
};
MODULE_DEVICE_TABLE(of, tsl2583_of_match);
diff --git a/drivers/iio/light/tsl2591.c b/drivers/iio/light/tsl2591.c
index 7bdbfe72f0f0..08476f193a44 100644
--- a/drivers/iio/light/tsl2591.c
+++ b/drivers/iio/light/tsl2591.c
@@ -21,7 +21,7 @@
#include <linux/pm_runtime.h>
#include <linux/sysfs.h>
-#include <asm/unaligned.h>
+#include <linux/unaligned.h>
#include <linux/iio/events.h>
#include <linux/iio/iio.h>
@@ -985,7 +985,7 @@ static int tsl2591_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)
+ bool state)
{
struct tsl2591_chip *chip = iio_priv(indio_dev);
struct i2c_client *client = chip->client;
@@ -1204,7 +1204,7 @@ static int tsl2591_probe(struct i2c_client *client)
static const struct of_device_id tsl2591_of_match[] = {
{ .compatible = "amstaos,tsl2591"},
- {}
+ { }
};
MODULE_DEVICE_TABLE(of, tsl2591_of_match);
diff --git a/drivers/iio/light/tsl2772.c b/drivers/iio/light/tsl2772.c
index cab468a82b61..0b171106441a 100644
--- a/drivers/iio/light/tsl2772.c
+++ b/drivers/iio/light/tsl2772.c
@@ -1081,14 +1081,14 @@ static int tsl2772_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 val)
+ bool val)
{
struct tsl2772_chip *chip = iio_priv(indio_dev);
if (chan->type == IIO_INTENSITY)
- chip->settings.als_interrupt_en = val ? true : false;
+ chip->settings.als_interrupt_en = val;
else
- chip->settings.prox_interrupt_en = val ? true : false;
+ chip->settings.prox_interrupt_en = val;
return tsl2772_invoke_change(indio_dev);
}
@@ -1899,7 +1899,7 @@ static const struct i2c_device_id tsl2772_idtable[] = {
{ "tsl2772", tsl2772 },
{ "tmd2772", tmd2772 },
{ "apds9930", apds9930 },
- {}
+ { }
};
MODULE_DEVICE_TABLE(i2c, tsl2772_idtable);
@@ -1916,7 +1916,7 @@ static const struct of_device_id tsl2772_of_match[] = {
{ .compatible = "amstaos,tsl2772" },
{ .compatible = "amstaos,tmd2772" },
{ .compatible = "avago,apds9930" },
- {}
+ { }
};
MODULE_DEVICE_TABLE(of, tsl2772_of_match);
diff --git a/drivers/iio/light/tsl4531.c b/drivers/iio/light/tsl4531.c
index 4da7d78906d4..a5788c09ad02 100644
--- a/drivers/iio/light/tsl4531.c
+++ b/drivers/iio/light/tsl4531.c
@@ -227,7 +227,7 @@ static DEFINE_SIMPLE_DEV_PM_OPS(tsl4531_pm_ops, tsl4531_suspend,
tsl4531_resume);
static const struct i2c_device_id tsl4531_id[] = {
- { "tsl4531", 0 },
+ { "tsl4531" },
{ }
};
MODULE_DEVICE_TABLE(i2c, tsl4531_id);
diff --git a/drivers/iio/light/us5182d.c b/drivers/iio/light/us5182d.c
index 9189a1d4d7e1..61a0957317a1 100644
--- a/drivers/iio/light/us5182d.c
+++ b/drivers/iio/light/us5182d.c
@@ -627,7 +627,7 @@ static int us5182d_read_event_config(struct iio_dev *indio_dev,
static int us5182d_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)
+ enum iio_event_direction dir, bool state)
{
struct us5182d_data *data = iio_priv(indio_dev);
int ret;
@@ -949,21 +949,21 @@ static const struct dev_pm_ops us5182d_pm_ops = {
static const struct acpi_device_id us5182d_acpi_match[] = {
{ "USD5182", 0 },
- {}
+ { }
};
MODULE_DEVICE_TABLE(acpi, us5182d_acpi_match);
static const struct i2c_device_id us5182d_id[] = {
- { "usd5182", 0 },
- {}
+ { "usd5182" },
+ { }
};
MODULE_DEVICE_TABLE(i2c, us5182d_id);
static const struct of_device_id us5182d_of_match[] = {
{ .compatible = "upisemi,usd5182" },
- {}
+ { }
};
MODULE_DEVICE_TABLE(of, us5182d_of_match);
diff --git a/drivers/iio/light/vcnl4000.c b/drivers/iio/light/vcnl4000.c
index 4e3641ff2ed4..90e7d4421abf 100644
--- a/drivers/iio/light/vcnl4000.c
+++ b/drivers/iio/light/vcnl4000.c
@@ -1084,9 +1084,8 @@ static int vcnl4010_read_raw(struct iio_dev *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;
+ if (!iio_device_claim_direct(indio_dev))
+ return -EBUSY;
/* Protect against event capture. */
if (vcnl4010_is_in_periodic_mode(data)) {
@@ -1096,7 +1095,7 @@ static int vcnl4010_read_raw(struct iio_dev *indio_dev,
mask);
}
- iio_device_release_direct_mode(indio_dev);
+ iio_device_release_direct(indio_dev);
return ret;
case IIO_CHAN_INFO_SAMP_FREQ:
switch (chan->type) {
@@ -1157,9 +1156,8 @@ static int vcnl4010_write_raw(struct iio_dev *indio_dev,
int ret;
struct vcnl4000_data *data = iio_priv(indio_dev);
- ret = iio_device_claim_direct_mode(indio_dev);
- if (ret)
- return ret;
+ if (!iio_device_claim_direct(indio_dev))
+ return -EBUSY;
/* Protect against event capture. */
if (vcnl4010_is_in_periodic_mode(data)) {
@@ -1183,7 +1181,7 @@ static int vcnl4010_write_raw(struct iio_dev *indio_dev,
}
end:
- iio_device_release_direct_mode(indio_dev);
+ iio_device_release_direct(indio_dev);
return ret;
}
@@ -1410,53 +1408,59 @@ static int vcnl4010_read_event_config(struct iio_dev *indio_dev,
}
}
-static int vcnl4010_config_threshold(struct iio_dev *indio_dev, bool state)
+static int vcnl4010_config_threshold_enable(struct vcnl4000_data *data)
{
- 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. */
+ ret = i2c_smbus_write_byte_data(data->client, VCNL4000_COMMAND,
+ VCNL4000_SELF_TIMED_EN | VCNL4000_PROX_EN);
+ if (ret < 0)
+ 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.
+ */
+ return i2c_smbus_write_byte_data(data->client, VCNL4010_INT_CTRL,
+ VCNL4010_INT_THR_EN);
+}
- /*
- * Enable interrupts on threshold, for proximity data by
- * default.
- */
- icr = VCNL4010_INT_THR_EN;
- } else {
- if (!vcnl4010_is_thr_enabled(data))
- return 0;
+static int vcnl4010_config_threshold_disable(struct vcnl4000_data *data)
+{
+ int ret;
- command = 0;
- icr = 0;
- }
+ if (!vcnl4010_is_thr_enabled(data))
+ return 0;
- ret = i2c_smbus_write_byte_data(data->client, VCNL4000_COMMAND,
- command);
+ ret = i2c_smbus_write_byte_data(data->client, VCNL4000_COMMAND, 0);
if (ret < 0)
- goto end;
+ return ret;
- ret = i2c_smbus_write_byte_data(data->client, VCNL4010_INT_CTRL, icr);
+ return i2c_smbus_write_byte_data(data->client, VCNL4010_INT_CTRL, 0);
+}
-end:
- if (state)
- iio_device_release_direct_mode(indio_dev);
+static int vcnl4010_config_threshold(struct iio_dev *indio_dev, bool state)
+{
+ struct vcnl4000_data *data = iio_priv(indio_dev);
+ int ret;
- return ret;
+ if (state) {
+ if (!iio_device_claim_direct(indio_dev))
+ return -EBUSY;
+ ret = vcnl4010_config_threshold_enable(data);
+ iio_device_release_direct(indio_dev);
+ return ret;
+ } else {
+ return vcnl4010_config_threshold_disable(data);
+ }
}
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)
+ bool state)
{
switch (chan->type) {
case IIO_PROXIMITY:
@@ -1501,7 +1505,8 @@ static int vcnl4040_read_event_config(struct iio_dev *indio_dev,
static int vcnl4040_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)
+ enum iio_event_direction dir,
+ bool state)
{
int ret = -EINVAL;
u16 val, mask;
@@ -1740,7 +1745,7 @@ static const struct iio_chan_spec_ext_info vcnl4000_ext_info[] = {
.shared = IIO_SEPARATE,
.read = vcnl4000_read_near_level,
},
- { /* sentinel */ }
+ { }
};
static const struct iio_event_spec vcnl4000_event_spec[] = {
@@ -2063,7 +2068,7 @@ static const struct of_device_id vcnl_4000_of_match[] = {
.compatible = "vishay,vcnl4200",
.data = (void *)VCNL4200,
},
- {},
+ { }
};
MODULE_DEVICE_TABLE(of, vcnl_4000_of_match);
diff --git a/drivers/iio/light/vcnl4035.c b/drivers/iio/light/vcnl4035.c
index 56bbefbc0ae6..b2bede9d3daa 100644
--- a/drivers/iio/light/vcnl4035.c
+++ b/drivers/iio/light/vcnl4035.c
@@ -105,7 +105,7 @@ static irqreturn_t vcnl4035_trigger_consumer_handler(int irq, void *p)
struct iio_dev *indio_dev = pf->indio_dev;
struct vcnl4035_data *data = iio_priv(indio_dev);
/* Ensure naturally aligned timestamp */
- u8 buffer[ALIGN(sizeof(u16), sizeof(s64)) + sizeof(s64)] __aligned(8);
+ u8 buffer[ALIGN(sizeof(u16), sizeof(s64)) + sizeof(s64)] __aligned(8) = { };
int ret;
ret = regmap_read(data->regmap, VCNL4035_ALS_DATA, (int *)buffer);
@@ -156,6 +156,31 @@ static int vcnl4035_set_pm_runtime_state(struct vcnl4035_data *data, bool on)
return ret;
}
+static int vcnl4035_read_info_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan, int *val)
+{
+ struct vcnl4035_data *data = iio_priv(indio_dev);
+ int ret;
+ int raw_data;
+ unsigned int reg;
+
+ if (!iio_device_claim_direct(indio_dev))
+ return -EBUSY;
+
+ if (chan->channel)
+ reg = VCNL4035_ALS_DATA;
+ else
+ reg = VCNL4035_WHITE_DATA;
+ ret = regmap_read(data->regmap, reg, &raw_data);
+ iio_device_release_direct(indio_dev);
+ if (ret)
+ return ret;
+
+ *val = raw_data;
+
+ return IIO_VAL_INT;
+}
+
/*
* Device IT INT Time (ms) Scale (lux/step)
* 000 50 0.064
@@ -175,28 +200,13 @@ static int vcnl4035_read_raw(struct iio_dev *indio_dev,
{
struct vcnl4035_data *data = iio_priv(indio_dev);
int ret;
- int raw_data;
- unsigned int reg;
switch (mask) {
case IIO_CHAN_INFO_RAW:
ret = vcnl4035_set_pm_runtime_state(data, true);
if (ret < 0)
return ret;
-
- ret = iio_device_claim_direct_mode(indio_dev);
- if (!ret) {
- if (chan->channel)
- reg = VCNL4035_ALS_DATA;
- else
- reg = VCNL4035_WHITE_DATA;
- ret = regmap_read(data->regmap, reg, &raw_data);
- iio_device_release_direct_mode(indio_dev);
- if (!ret) {
- *val = raw_data;
- ret = IIO_VAL_INT;
- }
- }
+ ret = vcnl4035_read_info_raw(indio_dev, chan, val);
vcnl4035_set_pm_runtime_state(data, false);
return ret;
case IIO_CHAN_INFO_INT_TIME:
@@ -653,7 +663,7 @@ static DEFINE_RUNTIME_DEV_PM_OPS(vcnl4035_pm_ops, vcnl4035_runtime_suspend,
vcnl4035_runtime_resume, NULL);
static const struct i2c_device_id vcnl4035_id[] = {
- { "vcnl4035", 0 },
+ { "vcnl4035" },
{ }
};
MODULE_DEVICE_TABLE(i2c, vcnl4035_id);
diff --git a/drivers/iio/light/veml3235.c b/drivers/iio/light/veml3235.c
new file mode 100644
index 000000000000..77c9ae17ed47
--- /dev/null
+++ b/drivers/iio/light/veml3235.c
@@ -0,0 +1,547 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * VEML3235 Ambient Light Sensor
+ *
+ * Copyright (c) 2024, Javier Carrasco <javier.carrasco.cruz@gmail.com>
+ *
+ * Datasheet: https://www.vishay.com/docs/80131/veml3235.pdf
+ * Appnote-80222: https://www.vishay.com/docs/80222/designingveml3235.pdf
+ */
+
+#include <linux/err.h>
+#include <linux/i2c.h>
+#include <linux/iio/iio.h>
+#include <linux/iio/iio-gts-helper.h>
+#include <linux/module.h>
+#include <linux/pm_runtime.h>
+#include <linux/regmap.h>
+#include <linux/regulator/consumer.h>
+
+#define VEML3235_REG_CONF 0x00
+#define VEML3235_REG_WH_DATA 0x04
+#define VEML3235_REG_ALS_DATA 0x05
+#define VEML3235_REG_ID 0x09
+
+#define VEML3235_CONF_SD BIT(0)
+#define VEML3235_CONF_SD0 BIT(15)
+
+struct veml3235_rf {
+ struct regmap_field *it;
+ struct regmap_field *gain;
+ struct regmap_field *id;
+};
+
+struct veml3235_data {
+ struct i2c_client *client;
+ struct device *dev;
+ struct regmap *regmap;
+ struct veml3235_rf rf;
+ struct iio_gts gts;
+};
+
+static const struct iio_itime_sel_mul veml3235_it_sel[] = {
+ GAIN_SCALE_ITIME_US(50000, 0, 1),
+ GAIN_SCALE_ITIME_US(100000, 1, 2),
+ GAIN_SCALE_ITIME_US(200000, 2, 4),
+ GAIN_SCALE_ITIME_US(400000, 3, 8),
+ GAIN_SCALE_ITIME_US(800000, 4, 16),
+};
+
+/*
+ * The MSB (DG) doubles the value of the rest of the field, which leads to
+ * two possible combinations to obtain gain = 2 and gain = 4. The gain
+ * handling can be simplified by restricting DG = 1 to the only gain that
+ * really requires it, gain = 8. Note that "X10" is a reserved value.
+ */
+#define VEML3235_SEL_GAIN_X1 0
+#define VEML3235_SEL_GAIN_X2 1
+#define VEML3235_SEL_GAIN_X4 3
+#define VEML3235_SEL_GAIN_X8 7
+static const struct iio_gain_sel_pair veml3235_gain_sel[] = {
+ GAIN_SCALE_GAIN(1, VEML3235_SEL_GAIN_X1),
+ GAIN_SCALE_GAIN(2, VEML3235_SEL_GAIN_X2),
+ GAIN_SCALE_GAIN(4, VEML3235_SEL_GAIN_X4),
+ GAIN_SCALE_GAIN(8, VEML3235_SEL_GAIN_X8),
+};
+
+static int veml3235_power_on(struct veml3235_data *data)
+{
+ int ret;
+
+ ret = regmap_clear_bits(data->regmap, VEML3235_REG_CONF,
+ VEML3235_CONF_SD | VEML3235_CONF_SD0);
+ if (ret)
+ return ret;
+
+ /* Wait 4 ms to let processor & oscillator start correctly */
+ fsleep(4000);
+
+ return 0;
+}
+
+static int veml3235_shut_down(struct veml3235_data *data)
+{
+ return regmap_set_bits(data->regmap, VEML3235_REG_CONF,
+ VEML3235_CONF_SD | VEML3235_CONF_SD0);
+}
+
+static void veml3235_shut_down_action(void *data)
+{
+ veml3235_shut_down(data);
+}
+
+enum veml3235_chan {
+ CH_ALS,
+ CH_WHITE,
+};
+
+static const struct iio_chan_spec veml3235_channels[] = {
+ {
+ .type = IIO_LIGHT,
+ .channel = CH_ALS,
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
+ .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_INT_TIME) |
+ BIT(IIO_CHAN_INFO_SCALE),
+ .info_mask_shared_by_all_available = BIT(IIO_CHAN_INFO_INT_TIME) |
+ BIT(IIO_CHAN_INFO_SCALE),
+ },
+ {
+ .type = IIO_INTENSITY,
+ .channel = CH_WHITE,
+ .modified = 1,
+ .channel2 = IIO_MOD_LIGHT_BOTH,
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
+ .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_INT_TIME) |
+ BIT(IIO_CHAN_INFO_SCALE),
+ .info_mask_shared_by_all_available = BIT(IIO_CHAN_INFO_INT_TIME) |
+ BIT(IIO_CHAN_INFO_SCALE),
+ },
+};
+
+static const struct regmap_range veml3235_readable_ranges[] = {
+ regmap_reg_range(VEML3235_REG_CONF, VEML3235_REG_ID),
+};
+
+static const struct regmap_access_table veml3235_readable_table = {
+ .yes_ranges = veml3235_readable_ranges,
+ .n_yes_ranges = ARRAY_SIZE(veml3235_readable_ranges),
+};
+
+static const struct regmap_range veml3235_writable_ranges[] = {
+ regmap_reg_range(VEML3235_REG_CONF, VEML3235_REG_CONF),
+};
+
+static const struct regmap_access_table veml3235_writable_table = {
+ .yes_ranges = veml3235_writable_ranges,
+ .n_yes_ranges = ARRAY_SIZE(veml3235_writable_ranges),
+};
+
+static const struct regmap_range veml3235_volatile_ranges[] = {
+ regmap_reg_range(VEML3235_REG_WH_DATA, VEML3235_REG_ALS_DATA),
+};
+
+static const struct regmap_access_table veml3235_volatile_table = {
+ .yes_ranges = veml3235_volatile_ranges,
+ .n_yes_ranges = ARRAY_SIZE(veml3235_volatile_ranges),
+};
+
+static const struct regmap_config veml3235_regmap_config = {
+ .name = "veml3235_regmap",
+ .reg_bits = 8,
+ .val_bits = 16,
+ .max_register = VEML3235_REG_ID,
+ .val_format_endian = REGMAP_ENDIAN_LITTLE,
+ .rd_table = &veml3235_readable_table,
+ .wr_table = &veml3235_writable_table,
+ .volatile_table = &veml3235_volatile_table,
+ .cache_type = REGCACHE_RBTREE,
+};
+
+static int veml3235_get_it(struct veml3235_data *data, int *val, int *val2)
+{
+ int ret, it_idx;
+
+ ret = regmap_field_read(data->rf.it, &it_idx);
+ if (ret)
+ return ret;
+
+ ret = iio_gts_find_int_time_by_sel(&data->gts, it_idx);
+ if (ret < 0)
+ return ret;
+
+ *val2 = ret;
+ *val = 0;
+
+ return IIO_VAL_INT_PLUS_MICRO;
+}
+
+static int veml3235_set_it(struct iio_dev *indio_dev, int val, int val2)
+{
+ struct veml3235_data *data = iio_priv(indio_dev);
+ int ret, gain_idx, it_idx, new_gain, prev_gain, prev_it;
+ bool in_range;
+
+ if (val || !iio_gts_valid_time(&data->gts, val2))
+ return -EINVAL;
+
+ ret = regmap_field_read(data->rf.it, &it_idx);
+ if (ret)
+ return ret;
+
+ ret = regmap_field_read(data->rf.gain, &gain_idx);
+ if (ret)
+ return ret;
+
+ prev_it = iio_gts_find_int_time_by_sel(&data->gts, it_idx);
+ if (prev_it < 0)
+ return prev_it;
+
+ if (prev_it == val2)
+ return 0;
+
+ prev_gain = iio_gts_find_gain_by_sel(&data->gts, gain_idx);
+ if (prev_gain < 0)
+ return prev_gain;
+
+ ret = iio_gts_find_new_gain_by_gain_time_min(&data->gts, prev_gain, prev_it,
+ val2, &new_gain, &in_range);
+ if (ret)
+ return ret;
+
+ if (!in_range)
+ dev_dbg(data->dev, "Optimal gain out of range\n");
+
+ ret = iio_gts_find_sel_by_int_time(&data->gts, val2);
+ if (ret < 0)
+ return ret;
+
+ ret = regmap_field_write(data->rf.it, ret);
+ if (ret)
+ return ret;
+
+ ret = iio_gts_find_sel_by_gain(&data->gts, new_gain);
+ if (ret < 0)
+ return ret;
+
+ return regmap_field_write(data->rf.gain, ret);
+}
+
+static int veml3235_set_scale(struct iio_dev *indio_dev, int val, int val2)
+{
+ struct veml3235_data *data = iio_priv(indio_dev);
+ int ret, it_idx, gain_sel, time_sel;
+
+ ret = regmap_field_read(data->rf.it, &it_idx);
+ if (ret)
+ return ret;
+
+ ret = iio_gts_find_gain_time_sel_for_scale(&data->gts, val, val2,
+ &gain_sel, &time_sel);
+ if (ret)
+ return ret;
+
+ ret = regmap_field_write(data->rf.it, time_sel);
+ if (ret)
+ return ret;
+
+ return regmap_field_write(data->rf.gain, gain_sel);
+}
+
+static int veml3235_get_scale(struct veml3235_data *data, int *val, int *val2)
+{
+ int gain, it, reg, ret;
+
+ ret = regmap_field_read(data->rf.gain, &reg);
+ if (ret) {
+ dev_err(data->dev, "failed to read gain %d\n", ret);
+ return ret;
+ }
+
+ gain = iio_gts_find_gain_by_sel(&data->gts, reg);
+ if (gain < 0)
+ return gain;
+
+ ret = regmap_field_read(data->rf.it, &reg);
+ if (ret) {
+ dev_err(data->dev, "failed to read integration time %d\n", ret);
+ return ret;
+ }
+
+ it = iio_gts_find_int_time_by_sel(&data->gts, reg);
+ if (it < 0)
+ return it;
+
+ ret = iio_gts_get_scale(&data->gts, gain, it, val, val2);
+ if (ret)
+ return ret;
+
+ return IIO_VAL_INT_PLUS_NANO;
+}
+
+static int veml3235_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan, int *val,
+ int *val2, long mask)
+{
+ struct veml3235_data *data = iio_priv(indio_dev);
+ struct regmap *regmap = data->regmap;
+ int ret, reg;
+
+ switch (mask) {
+ case IIO_CHAN_INFO_RAW:
+ switch (chan->type) {
+ case IIO_LIGHT:
+ ret = regmap_read(regmap, VEML3235_REG_ALS_DATA, &reg);
+ if (ret < 0)
+ return ret;
+
+ *val = reg;
+ return IIO_VAL_INT;
+ case IIO_INTENSITY:
+ ret = regmap_read(regmap, VEML3235_REG_WH_DATA, &reg);
+ if (ret < 0)
+ return ret;
+
+ *val = reg;
+ return IIO_VAL_INT;
+ default:
+ return -EINVAL;
+ }
+ case IIO_CHAN_INFO_INT_TIME:
+ return veml3235_get_it(data, val, val2);
+ case IIO_CHAN_INFO_SCALE:
+ return veml3235_get_scale(data, val, val2);
+ default:
+ return -EINVAL;
+ }
+}
+
+static int veml3235_read_avail(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ const int **vals, int *type, int *length,
+ long mask)
+{
+ struct veml3235_data *data = iio_priv(indio_dev);
+
+ switch (mask) {
+ case IIO_CHAN_INFO_INT_TIME:
+ return iio_gts_avail_times(&data->gts, vals, type, length);
+ case IIO_CHAN_INFO_SCALE:
+ return iio_gts_all_avail_scales(&data->gts, vals, type, length);
+ default:
+ return -EINVAL;
+ }
+}
+
+static int veml3235_write_raw_get_fmt(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ long mask)
+{
+ switch (mask) {
+ case IIO_CHAN_INFO_SCALE:
+ return IIO_VAL_INT_PLUS_NANO;
+ case IIO_CHAN_INFO_INT_TIME:
+ return IIO_VAL_INT_PLUS_MICRO;
+ default:
+ return -EINVAL;
+ }
+}
+
+static int veml3235_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:
+ return veml3235_set_it(indio_dev, val, val2);
+ case IIO_CHAN_INFO_SCALE:
+ return veml3235_set_scale(indio_dev, val, val2);
+ }
+
+ return -EINVAL;
+}
+
+static void veml3235_read_id(struct veml3235_data *data)
+{
+ int ret, reg;
+
+ ret = regmap_field_read(data->rf.id, &reg);
+ if (ret) {
+ dev_info(data->dev, "failed to read ID\n");
+ return;
+ }
+
+ if (reg != 0x35)
+ dev_info(data->dev, "Unknown ID %d\n", reg);
+}
+
+static const struct reg_field veml3235_rf_it =
+ REG_FIELD(VEML3235_REG_CONF, 4, 6);
+
+static const struct reg_field veml3235_rf_gain =
+ REG_FIELD(VEML3235_REG_CONF, 11, 13);
+
+static const struct reg_field veml3235_rf_id =
+ REG_FIELD(VEML3235_REG_ID, 0, 7);
+
+static int veml3235_regfield_init(struct veml3235_data *data)
+{
+ struct regmap *regmap = data->regmap;
+ struct device *dev = data->dev;
+ struct regmap_field *rm_field;
+ struct veml3235_rf *rf = &data->rf;
+
+ rm_field = devm_regmap_field_alloc(dev, regmap, veml3235_rf_it);
+ if (IS_ERR(rm_field))
+ return PTR_ERR(rm_field);
+ rf->it = rm_field;
+
+ rm_field = devm_regmap_field_alloc(dev, regmap, veml3235_rf_gain);
+ if (IS_ERR(rm_field))
+ return PTR_ERR(rm_field);
+ rf->gain = rm_field;
+
+ rm_field = devm_regmap_field_alloc(dev, regmap, veml3235_rf_id);
+ if (IS_ERR(rm_field))
+ return PTR_ERR(rm_field);
+ rf->id = rm_field;
+
+ return 0;
+}
+
+static int veml3235_hw_init(struct iio_dev *indio_dev)
+{
+ struct veml3235_data *data = iio_priv(indio_dev);
+ struct device *dev = data->dev;
+ int ret;
+
+ ret = devm_iio_init_iio_gts(data->dev, 0, 272640000,
+ veml3235_gain_sel, ARRAY_SIZE(veml3235_gain_sel),
+ veml3235_it_sel, ARRAY_SIZE(veml3235_it_sel),
+ &data->gts);
+ if (ret)
+ return dev_err_probe(data->dev, ret, "failed to init iio gts\n");
+
+ /* Set gain to 1 and integration time to 100 ms */
+ ret = regmap_field_write(data->rf.gain, 0x00);
+ if (ret)
+ return dev_err_probe(data->dev, ret, "failed to set gain\n");
+
+ ret = regmap_field_write(data->rf.it, 0x01);
+ if (ret)
+ return dev_err_probe(data->dev, ret,
+ "failed to set integration time\n");
+
+ ret = veml3235_power_on(data);
+ if (ret)
+ return dev_err_probe(dev, ret, "failed to power on\n");
+
+ return devm_add_action_or_reset(dev, veml3235_shut_down_action, data);
+}
+
+static const struct iio_info veml3235_info = {
+ .read_raw = veml3235_read_raw,
+ .read_avail = veml3235_read_avail,
+ .write_raw = veml3235_write_raw,
+ .write_raw_get_fmt = veml3235_write_raw_get_fmt,
+};
+
+static int veml3235_probe(struct i2c_client *client)
+{
+ struct device *dev = &client->dev;
+ struct veml3235_data *data;
+ struct iio_dev *indio_dev;
+ struct regmap *regmap;
+ int ret;
+
+ regmap = devm_regmap_init_i2c(client, &veml3235_regmap_config);
+ if (IS_ERR(regmap))
+ return dev_err_probe(dev, PTR_ERR(regmap),
+ "failed to setup regmap\n");
+
+ 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;
+ data->dev = dev;
+ data->regmap = regmap;
+
+ ret = veml3235_regfield_init(data);
+ if (ret)
+ return dev_err_probe(dev, ret, "failed to init regfield\n");
+
+ ret = devm_regulator_get_enable(dev, "vdd");
+ if (ret)
+ return dev_err_probe(dev, ret, "failed to enable regulator\n");
+
+ indio_dev->name = "veml3235";
+ indio_dev->channels = veml3235_channels;
+ indio_dev->num_channels = ARRAY_SIZE(veml3235_channels);
+ indio_dev->modes = INDIO_DIRECT_MODE;
+ indio_dev->info = &veml3235_info;
+
+ veml3235_read_id(data);
+
+ ret = veml3235_hw_init(indio_dev);
+ if (ret < 0)
+ return ret;
+
+ return devm_iio_device_register(dev, indio_dev);
+}
+
+static int veml3235_runtime_suspend(struct device *dev)
+{
+ struct veml3235_data *data = iio_priv(dev_get_drvdata(dev));
+ int ret;
+
+ ret = veml3235_shut_down(data);
+ if (ret < 0)
+ dev_err(data->dev, "failed to suspend: %d\n", ret);
+
+ return ret;
+}
+
+static int veml3235_runtime_resume(struct device *dev)
+{
+ struct veml3235_data *data = iio_priv(dev_get_drvdata(dev));
+ int ret;
+
+ ret = veml3235_power_on(data);
+ if (ret < 0)
+ dev_err(data->dev, "failed to resume: %d\n", ret);
+
+ return ret;
+}
+
+static DEFINE_RUNTIME_DEV_PM_OPS(veml3235_pm_ops, veml3235_runtime_suspend,
+ veml3235_runtime_resume, NULL);
+
+static const struct of_device_id veml3235_of_match[] = {
+ { .compatible = "vishay,veml3235" },
+ { }
+};
+MODULE_DEVICE_TABLE(of, veml3235_of_match);
+
+static const struct i2c_device_id veml3235_id[] = {
+ { "veml3235" },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, veml3235_id);
+
+static struct i2c_driver veml3235_driver = {
+ .driver = {
+ .name = "veml3235",
+ .of_match_table = veml3235_of_match,
+ .pm = pm_ptr(&veml3235_pm_ops),
+ },
+ .probe = veml3235_probe,
+ .id_table = veml3235_id,
+};
+module_i2c_driver(veml3235_driver);
+
+MODULE_AUTHOR("Javier Carrasco <javier.carrasco.cruz@gmail.com>");
+MODULE_DESCRIPTION("VEML3235 Ambient Light Sensor");
+MODULE_LICENSE("GPL");
+MODULE_IMPORT_NS("IIO_GTS_HELPER");
diff --git a/drivers/iio/light/veml6030.c b/drivers/iio/light/veml6030.c
index 043f233d9bdb..473a9c3e32a3 100644
--- a/drivers/iio/light/veml6030.c
+++ b/drivers/iio/light/veml6030.c
@@ -1,22 +1,37 @@
// SPDX-License-Identifier: GPL-2.0+
/*
- * VEML6030 Ambient Light Sensor
+ * VEML6030, VMEL6035 and VEML7700 Ambient Light Sensors
*
* Copyright (c) 2019, Rishi Gupta <gupt21@gmail.com>
*
+ * VEML6030:
* Datasheet: https://www.vishay.com/docs/84366/veml6030.pdf
* Appnote-84367: https://www.vishay.com/docs/84367/designingveml6030.pdf
+ *
+ * VEML6035:
+ * Datasheet: https://www.vishay.com/docs/84889/veml6035.pdf
+ * Appnote-84944: https://www.vishay.com/docs/84944/designingveml6035.pdf
+ *
+ * VEML7700:
+ * Datasheet: https://www.vishay.com/docs/84286/veml7700.pdf
+ * Appnote-84323: https://www.vishay.com/docs/84323/designingveml7700.pdf
*/
+#include <linux/bitfield.h>
#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/units.h>
+#include <linux/regulator/consumer.h>
#include <linux/iio/iio.h>
#include <linux/iio/sysfs.h>
#include <linux/iio/events.h>
+#include <linux/iio/iio-gts-helper.h>
+#include <linux/iio/trigger_consumer.h>
+#include <linux/iio/triggered_buffer.h>
/* Device registers */
#define VEML6030_REG_ALS_CONF 0x00
@@ -26,6 +41,7 @@
#define VEML6030_REG_ALS_DATA 0x04
#define VEML6030_REG_WH_DATA 0x05
#define VEML6030_REG_ALS_INT 0x06
+#define VEML6030_REG_DATA(ch) (VEML6030_REG_ALS_DATA + (ch))
/* Bit masks for specific functionality */
#define VEML6030_ALS_IT GENMASK(9, 6)
@@ -38,44 +54,109 @@
#define VEML6030_ALS_INT_EN BIT(1)
#define VEML6030_ALS_SD BIT(0)
+#define VEML6035_GAIN_M GENMASK(12, 10)
+#define VEML6035_GAIN BIT(10)
+#define VEML6035_DG BIT(11)
+#define VEML6035_SENS BIT(12)
+#define VEML6035_INT_CHAN BIT(3)
+#define VEML6035_CHAN_EN BIT(2)
+
+/* Regfields */
+#define VEML6030_GAIN_RF REG_FIELD(VEML6030_REG_ALS_CONF, 11, 12)
+#define VEML6030_IT_RF REG_FIELD(VEML6030_REG_ALS_CONF, 6, 9)
+
+#define VEML6035_GAIN_RF REG_FIELD(VEML6030_REG_ALS_CONF, 10, 12)
+
+/* Maximum scales x 10000 to work with integers */
+#define VEML6030_MAX_SCALE 21504
+#define VEML6035_MAX_SCALE 4096
+
+enum veml6030_scan {
+ VEML6030_SCAN_ALS,
+ VEML6030_SCAN_WH,
+ VEML6030_SCAN_TIMESTAMP,
+};
+
+struct veml6030_rf {
+ struct regmap_field *it;
+ struct regmap_field *gain;
+};
+
+struct veml603x_chip {
+ const char *name;
+ const struct iio_chan_spec *channels;
+ const int num_channels;
+ const struct reg_field gain_rf;
+ const struct reg_field it_rf;
+ const int max_scale;
+ int (*hw_init)(struct iio_dev *indio_dev, struct device *dev);
+ int (*set_info)(struct iio_dev *indio_dev);
+};
+
/*
* 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
+ * Table 'resolution and maximum detection range' in the appnotes
* 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).
+ * in this table (0-3 for VEML6030, 0-5 for VEML6035) 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;
+ struct veml6030_rf rf;
+ const struct veml603x_chip *chip;
+ struct iio_gts gts;
+
};
-/* 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");
+#define VEML6030_SEL_IT_25MS 0x0C
+#define VEML6030_SEL_IT_50MS 0x08
+#define VEML6030_SEL_IT_100MS 0x00
+#define VEML6030_SEL_IT_200MS 0x01
+#define VEML6030_SEL_IT_400MS 0x02
+#define VEML6030_SEL_IT_800MS 0x03
+static const struct iio_itime_sel_mul veml6030_it_sel[] = {
+ GAIN_SCALE_ITIME_US(25000, VEML6030_SEL_IT_25MS, 1),
+ GAIN_SCALE_ITIME_US(50000, VEML6030_SEL_IT_50MS, 2),
+ GAIN_SCALE_ITIME_US(100000, VEML6030_SEL_IT_100MS, 4),
+ GAIN_SCALE_ITIME_US(200000, VEML6030_SEL_IT_200MS, 8),
+ GAIN_SCALE_ITIME_US(400000, VEML6030_SEL_IT_400MS, 16),
+ GAIN_SCALE_ITIME_US(800000, VEML6030_SEL_IT_800MS, 32),
+};
-/*
- * 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.
+/* Gains are multiplied by 8 to work with integers. The values in the
+ * iio-gts tables don't need corrections because the maximum value of
+ * the scale refers to GAIN = x1, and the rest of the values are
+ * obtained from the resulting linear function.
*/
-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
+#define VEML6030_SEL_MILLI_GAIN_X125 2
+#define VEML6030_SEL_MILLI_GAIN_X250 3
+#define VEML6030_SEL_MILLI_GAIN_X1000 0
+#define VEML6030_SEL_MILLI_GAIN_X2000 1
+static const struct iio_gain_sel_pair veml6030_gain_sel[] = {
+ GAIN_SCALE_GAIN(1, VEML6030_SEL_MILLI_GAIN_X125),
+ GAIN_SCALE_GAIN(2, VEML6030_SEL_MILLI_GAIN_X250),
+ GAIN_SCALE_GAIN(8, VEML6030_SEL_MILLI_GAIN_X1000),
+ GAIN_SCALE_GAIN(16, VEML6030_SEL_MILLI_GAIN_X2000),
};
-static const struct attribute_group veml6030_attr_group = {
- .attrs = veml6030_attributes,
+#define VEML6035_SEL_MILLI_GAIN_X125 4
+#define VEML6035_SEL_MILLI_GAIN_X250 5
+#define VEML6035_SEL_MILLI_GAIN_X500 7
+#define VEML6035_SEL_MILLI_GAIN_X1000 0
+#define VEML6035_SEL_MILLI_GAIN_X2000 1
+#define VEML6035_SEL_MILLI_GAIN_X4000 3
+static const struct iio_gain_sel_pair veml6035_gain_sel[] = {
+ GAIN_SCALE_GAIN(1, VEML6035_SEL_MILLI_GAIN_X125),
+ GAIN_SCALE_GAIN(2, VEML6035_SEL_MILLI_GAIN_X250),
+ GAIN_SCALE_GAIN(4, VEML6035_SEL_MILLI_GAIN_X500),
+ GAIN_SCALE_GAIN(8, VEML6035_SEL_MILLI_GAIN_X1000),
+ GAIN_SCALE_GAIN(16, VEML6035_SEL_MILLI_GAIN_X2000),
+ GAIN_SCALE_GAIN(32, VEML6035_SEL_MILLI_GAIN_X4000),
};
/*
@@ -99,9 +180,8 @@ static const char * const period_values[] = {
static ssize_t in_illuminance_period_available_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
+ struct veml6030_data *data = iio_priv(dev_to_iio_dev(dev));
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, &reg);
if (ret) {
@@ -144,14 +224,23 @@ static const struct attribute_group veml6030_event_attr_group = {
static int veml6030_als_pwr_on(struct veml6030_data *data)
{
- return regmap_update_bits(data->regmap, VEML6030_REG_ALS_CONF,
- VEML6030_ALS_SD, 0);
+ int ret;
+
+ ret = regmap_clear_bits(data->regmap, VEML6030_REG_ALS_CONF,
+ VEML6030_ALS_SD);
+ if (ret)
+ return ret;
+
+ /* Wait 4 ms to let processor & oscillator start correctly */
+ fsleep(4000);
+
+ return 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);
+ return regmap_set_bits(data->regmap, VEML6030_REG_ALS_CONF,
+ VEML6030_ALS_SD);
}
static void veml6030_als_shut_down_action(void *data)
@@ -190,8 +279,17 @@ static const struct iio_chan_spec veml6030_channels[] = {
BIT(IIO_CHAN_INFO_PROCESSED) |
BIT(IIO_CHAN_INFO_INT_TIME) |
BIT(IIO_CHAN_INFO_SCALE),
+ .info_mask_shared_by_all_available = BIT(IIO_CHAN_INFO_INT_TIME) |
+ BIT(IIO_CHAN_INFO_SCALE),
.event_spec = veml6030_event_spec,
.num_event_specs = ARRAY_SIZE(veml6030_event_spec),
+ .scan_index = VEML6030_SCAN_ALS,
+ .scan_type = {
+ .sign = 'u',
+ .realbits = 16,
+ .storagebits = 16,
+ .endianness = IIO_CPU,
+ },
},
{
.type = IIO_INTENSITY,
@@ -199,8 +297,85 @@ static const struct iio_chan_spec veml6030_channels[] = {
.modified = 1,
.channel2 = IIO_MOD_LIGHT_BOTH,
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
- BIT(IIO_CHAN_INFO_PROCESSED),
+ BIT(IIO_CHAN_INFO_INT_TIME) |
+ BIT(IIO_CHAN_INFO_SCALE),
+ .info_mask_shared_by_all_available = BIT(IIO_CHAN_INFO_INT_TIME) |
+ BIT(IIO_CHAN_INFO_SCALE),
+ .scan_index = VEML6030_SCAN_WH,
+ .scan_type = {
+ .sign = 'u',
+ .realbits = 16,
+ .storagebits = 16,
+ .endianness = IIO_CPU,
+ },
+ },
+ IIO_CHAN_SOFT_TIMESTAMP(VEML6030_SCAN_TIMESTAMP),
+};
+
+static const struct iio_chan_spec veml7700_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),
+ .info_mask_shared_by_all_available = BIT(IIO_CHAN_INFO_INT_TIME) |
+ BIT(IIO_CHAN_INFO_SCALE),
+ .scan_index = VEML6030_SCAN_ALS,
+ .scan_type = {
+ .sign = 'u',
+ .realbits = 16,
+ .storagebits = 16,
+ .endianness = IIO_CPU,
+ },
+ },
+ {
+ .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_INT_TIME) |
+ BIT(IIO_CHAN_INFO_SCALE),
+ .info_mask_shared_by_all_available = BIT(IIO_CHAN_INFO_INT_TIME) |
+ BIT(IIO_CHAN_INFO_SCALE),
+ .scan_index = VEML6030_SCAN_WH,
+ .scan_type = {
+ .sign = 'u',
+ .realbits = 16,
+ .storagebits = 16,
+ .endianness = IIO_CPU,
+ },
},
+ IIO_CHAN_SOFT_TIMESTAMP(VEML6030_SCAN_TIMESTAMP),
+};
+
+static const struct regmap_range veml6030_readable_ranges[] = {
+ regmap_reg_range(VEML6030_REG_ALS_CONF, VEML6030_REG_ALS_INT),
+};
+
+static const struct regmap_access_table veml6030_readable_table = {
+ .yes_ranges = veml6030_readable_ranges,
+ .n_yes_ranges = ARRAY_SIZE(veml6030_readable_ranges),
+};
+
+static const struct regmap_range veml6030_writable_ranges[] = {
+ regmap_reg_range(VEML6030_REG_ALS_CONF, VEML6030_REG_ALS_PSM),
+};
+
+static const struct regmap_access_table veml6030_writable_table = {
+ .yes_ranges = veml6030_writable_ranges,
+ .n_yes_ranges = ARRAY_SIZE(veml6030_writable_ranges),
+};
+
+static const struct regmap_range veml6030_volatile_ranges[] = {
+ regmap_reg_range(VEML6030_REG_ALS_DATA, VEML6030_REG_WH_DATA),
+};
+
+static const struct regmap_access_table veml6030_volatile_table = {
+ .yes_ranges = veml6030_volatile_ranges,
+ .n_yes_ranges = ARRAY_SIZE(veml6030_volatile_ranges),
};
static const struct regmap_config veml6030_regmap_config = {
@@ -209,107 +384,79 @@ static const struct regmap_config veml6030_regmap_config = {
.val_bits = 16,
.max_register = VEML6030_REG_ALS_INT,
.val_format_endian = REGMAP_ENDIAN_LITTLE,
+ .rd_table = &veml6030_readable_table,
+ .wr_table = &veml6030_writable_table,
+ .volatile_table = &veml6030_volatile_table,
+ .cache_type = REGCACHE_RBTREE,
};
-static int veml6030_get_intgrn_tm(struct iio_dev *indio_dev,
- int *val, int *val2)
+static int veml6030_get_it(struct veml6030_data *data, int *val, int *val2)
{
- int ret, reg;
- struct veml6030_data *data = iio_priv(indio_dev);
+ int ret, it_idx;
- ret = regmap_read(data->regmap, VEML6030_REG_ALS_CONF, &reg);
- if (ret) {
- dev_err(&data->client->dev,
- "can't read als conf register %d\n", ret);
+ ret = regmap_field_read(data->rf.it, &it_idx);
+ if (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;
- }
+ ret = iio_gts_find_int_time_by_sel(&data->gts, it_idx);
+ if (ret < 0)
+ return ret;
+ *val2 = ret;
*val = 0;
+
return IIO_VAL_INT_PLUS_MICRO;
}
-static int veml6030_set_intgrn_tm(struct iio_dev *indio_dev,
- int val, int val2)
+static int veml6030_set_it(struct iio_dev *indio_dev, int val, int val2)
{
- int ret, new_int_time, int_idx;
struct veml6030_data *data = iio_priv(indio_dev);
+ int ret, gain_idx, it_idx, new_gain, prev_gain, prev_it;
+ bool in_range;
- if (val)
+ if (val || !iio_gts_valid_time(&data->gts, val2))
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_field_read(data->rf.it, &it_idx);
+ if (ret)
+ return ret;
- 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);
+ ret = regmap_field_read(data->rf.gain, &gain_idx);
+ if (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;
+ prev_it = iio_gts_find_int_time_by_sel(&data->gts, it_idx);
+ if (prev_it < 0)
+ return prev_it;
- data->cur_integration_time = int_idx;
+ if (prev_it == val2)
+ return 0;
- return ret;
+ prev_gain = iio_gts_find_gain_by_sel(&data->gts, gain_idx);
+ if (prev_gain < 0)
+ return prev_gain;
+
+ ret = iio_gts_find_new_gain_by_gain_time_min(&data->gts, prev_gain, prev_it,
+ val2, &new_gain, &in_range);
+ if (ret)
+ return ret;
+
+ if (!in_range)
+ dev_dbg(&data->client->dev, "Optimal gain out of range\n");
+
+ ret = iio_gts_find_sel_by_int_time(&data->gts, val2);
+ if (ret < 0)
+ return ret;
+
+ ret = regmap_field_write(data->rf.it, ret);
+ if (ret)
+ return ret;
+
+ ret = iio_gts_find_sel_by_gain(&data->gts, new_gain);
+ if (ret < 0)
+ return ret;
+
+ return regmap_field_write(data->rf.gain, ret);
}
static int veml6030_read_persistence(struct iio_dev *indio_dev,
@@ -318,7 +465,7 @@ static int veml6030_read_persistence(struct iio_dev *indio_dev,
int ret, reg, period, x, y;
struct veml6030_data *data = iio_priv(indio_dev);
- ret = veml6030_get_intgrn_tm(indio_dev, &x, &y);
+ ret = veml6030_get_it(data, &x, &y);
if (ret < 0)
return ret;
@@ -343,7 +490,7 @@ static int veml6030_write_persistence(struct iio_dev *indio_dev,
int ret, period, x, y;
struct veml6030_data *data = iio_priv(indio_dev);
- ret = veml6030_get_intgrn_tm(indio_dev, &x, &y);
+ ret = veml6030_get_it(data, &x, &y);
if (ret < 0)
return ret;
@@ -372,86 +519,29 @@ static int veml6030_write_persistence(struct iio_dev *indio_dev,
return ret;
}
-static int veml6030_set_als_gain(struct iio_dev *indio_dev,
- int val, int val2)
+static int veml6030_set_scale(struct iio_dev *indio_dev, int val, int val2)
{
- int ret, new_gain, gain_idx;
+ int ret, gain_sel, it_idx, it_sel;
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);
+ ret = regmap_field_read(data->rf.it, &it_idx);
+ if (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 = iio_gts_find_gain_time_sel_for_scale(&data->gts, val, val2,
+ &gain_sel, &it_sel);
+ if (ret)
+ return ret;
- ret = regmap_read(data->regmap, VEML6030_REG_ALS_CONF, &reg);
- if (ret) {
- dev_err(&data->client->dev,
- "can't read als conf register %d\n", ret);
+ ret = regmap_field_write(data->rf.it, it_sel);
+ if (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;
- }
+ ret = regmap_field_write(data->rf.gain, gain_sel);
+ if (ret)
+ return ret;
- return IIO_VAL_INT_PLUS_MICRO;
+ return 0;
}
static int veml6030_read_thresh(struct iio_dev *indio_dev,
@@ -498,6 +588,71 @@ static int veml6030_write_thresh(struct iio_dev *indio_dev,
return ret;
}
+static int veml6030_get_total_gain(struct veml6030_data *data)
+{
+ int gain, it, reg, ret;
+
+ ret = regmap_field_read(data->rf.gain, &reg);
+ if (ret)
+ return ret;
+
+ gain = iio_gts_find_gain_by_sel(&data->gts, reg);
+ if (gain < 0)
+ return gain;
+
+ ret = regmap_field_read(data->rf.it, &reg);
+ if (ret)
+ return ret;
+
+ it = iio_gts_find_int_time_by_sel(&data->gts, reg);
+ if (it < 0)
+ return it;
+
+ return iio_gts_get_total_gain(&data->gts, gain, it);
+}
+
+static int veml6030_get_scale(struct veml6030_data *data, int *val, int *val2)
+{
+ int gain, it, reg, ret;
+
+ ret = regmap_field_read(data->rf.gain, &reg);
+ if (ret)
+ return ret;
+
+ gain = iio_gts_find_gain_by_sel(&data->gts, reg);
+ if (gain < 0)
+ return gain;
+
+ ret = regmap_field_read(data->rf.it, &reg);
+ if (ret)
+ return ret;
+
+ it = iio_gts_find_int_time_by_sel(&data->gts, reg);
+ if (it < 0)
+ return it;
+
+ ret = iio_gts_get_scale(&data->gts, gain, it, val, val2);
+ if (ret)
+ return ret;
+
+ return IIO_VAL_INT_PLUS_NANO;
+}
+
+static int veml6030_process_als(struct veml6030_data *data, int raw,
+ int *val, int *val2)
+{
+ int total_gain;
+
+ total_gain = veml6030_get_total_gain(data);
+ if (total_gain < 0)
+ return total_gain;
+
+ *val = raw * data->chip->max_scale / total_gain / 10000;
+ *val2 = raw * data->chip->max_scale / total_gain % 10000 * 100;
+
+ return IIO_VAL_INT_PLUS_MICRO;
+}
+
/*
* Provide both raw as well as light reading in lux.
* light (in lux) = resolution * raw reading
@@ -521,11 +676,9 @@ static int veml6030_read_raw(struct iio_dev *indio_dev,
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;
- }
+ if (mask == IIO_CHAN_INFO_PROCESSED)
+ return veml6030_process_als(data, reg, val, val2);
+
*val = reg;
return IIO_VAL_INT;
case IIO_INTENSITY:
@@ -534,48 +687,60 @@ static int veml6030_read_raw(struct iio_dev *indio_dev,
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;
+ return veml6030_get_it(data, val, val2);
case IIO_CHAN_INFO_SCALE:
- if (chan->type == IIO_LIGHT)
- return veml6030_get_als_gain(indio_dev, val, val2);
- return -EINVAL;
+ return veml6030_get_scale(data, val, val2);
default:
return -EINVAL;
}
}
+static int veml6030_read_avail(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ const int **vals, int *type, int *length,
+ long mask)
+{
+ struct veml6030_data *data = iio_priv(indio_dev);
+
+ switch (mask) {
+ case IIO_CHAN_INFO_INT_TIME:
+ return iio_gts_avail_times(&data->gts, vals, type, length);
+ case IIO_CHAN_INFO_SCALE:
+ return iio_gts_all_avail_scales(&data->gts, vals, type, length);
+ }
+
+ 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;
- }
+ return veml6030_set_it(indio_dev, val, val2);
case IIO_CHAN_INFO_SCALE:
- switch (chan->type) {
- case IIO_LIGHT:
- return veml6030_set_als_gain(indio_dev, val, val2);
- default:
- return -EINVAL;
- }
+ return veml6030_set_scale(indio_dev, val, val2);
+ default:
+ return -EINVAL;
+ }
+}
+
+static int veml6030_write_raw_get_fmt(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ long mask)
+{
+ switch (mask) {
+ case IIO_CHAN_INFO_SCALE:
+ return IIO_VAL_INT_PLUS_NANO;
+ case IIO_CHAN_INFO_INT_TIME:
+ return IIO_VAL_INT_PLUS_MICRO;
default:
return -EINVAL;
}
@@ -647,14 +812,11 @@ static int veml6030_read_interrupt_config(struct iio_dev *indio_dev,
*/
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)
+ enum iio_event_direction dir, bool 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,
@@ -674,19 +836,21 @@ static int veml6030_write_interrupt_config(struct iio_dev *indio_dev,
static const struct iio_info veml6030_info = {
.read_raw = veml6030_read_raw,
+ .read_avail = veml6030_read_avail,
.write_raw = veml6030_write_raw,
+ .write_raw_get_fmt = veml6030_write_raw_get_fmt,
.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,
+ .read_avail = veml6030_read_avail,
.write_raw = veml6030_write_raw,
- .attrs = &veml6030_attr_group,
+ .write_raw_get_fmt = veml6030_write_raw_get_fmt,
};
static irqreturn_t veml6030_event_handler(int irq, void *private)
@@ -718,72 +882,201 @@ static irqreturn_t veml6030_event_handler(int irq, void *private)
return IRQ_HANDLED;
}
+static irqreturn_t veml6030_trigger_handler(int irq, void *p)
+{
+ struct iio_poll_func *pf = p;
+ struct iio_dev *iio = pf->indio_dev;
+ struct veml6030_data *data = iio_priv(iio);
+ unsigned int reg;
+ int ch, ret, i = 0;
+ struct {
+ u16 chans[2];
+ aligned_s64 timestamp;
+ } scan;
+
+ memset(&scan, 0, sizeof(scan));
+
+ iio_for_each_active_channel(iio, ch) {
+ ret = regmap_read(data->regmap, VEML6030_REG_DATA(ch),
+ &reg);
+ if (ret)
+ goto done;
+
+ scan.chans[i++] = reg;
+ }
+
+ iio_push_to_buffers_with_timestamp(iio, &scan, pf->timestamp);
+
+done:
+ iio_trigger_notify_done(iio->trig);
+
+ return IRQ_HANDLED;
+}
+
+static int veml6030_set_info(struct iio_dev *indio_dev)
+{
+ struct veml6030_data *data = iio_priv(indio_dev);
+ struct i2c_client *client = data->client;
+ int ret;
+
+ if (client->irq) {
+ ret = devm_request_threaded_irq(&client->dev, client->irq,
+ NULL, veml6030_event_handler,
+ IRQF_TRIGGER_LOW | IRQF_ONESHOT,
+ indio_dev->name, indio_dev);
+ if (ret < 0)
+ return dev_err_probe(&client->dev, ret,
+ "irq %d request failed\n",
+ client->irq);
+
+ indio_dev->info = &veml6030_info;
+ } else {
+ indio_dev->info = &veml6030_info_no_irq;
+ }
+
+ return 0;
+}
+
+static int veml7700_set_info(struct iio_dev *indio_dev)
+{
+ indio_dev->info = &veml6030_info_no_irq;
+
+ return 0;
+}
+
+static int veml6030_regfield_init(struct iio_dev *indio_dev)
+{
+ struct veml6030_data *data = iio_priv(indio_dev);
+ struct regmap *regmap = data->regmap;
+ struct device *dev = &data->client->dev;
+ struct regmap_field *rm_field;
+ struct veml6030_rf *rf = &data->rf;
+
+ rm_field = devm_regmap_field_alloc(dev, regmap, data->chip->it_rf);
+ if (IS_ERR(rm_field))
+ return PTR_ERR(rm_field);
+ rf->it = rm_field;
+
+ rm_field = devm_regmap_field_alloc(dev, regmap, data->chip->gain_rf);
+ if (IS_ERR(rm_field))
+ return PTR_ERR(rm_field);
+ rf->gain = rm_field;
+
+ return 0;
+}
+
/*
* 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)
+static int veml6030_hw_init(struct iio_dev *indio_dev, struct device *dev)
{
int ret, val;
struct veml6030_data *data = iio_priv(indio_dev);
- struct i2c_client *client = data->client;
+
+ ret = devm_iio_init_iio_gts(dev, 2, 150400000,
+ veml6030_gain_sel, ARRAY_SIZE(veml6030_gain_sel),
+ veml6030_it_sel, ARRAY_SIZE(veml6030_it_sel),
+ &data->gts);
+ if (ret)
+ return dev_err_probe(dev, ret, "failed to init iio gts\n");
ret = veml6030_als_shut_down(data);
- if (ret) {
- dev_err(&client->dev, "can't shutdown als %d\n", ret);
- return ret;
- }
+ if (ret)
+ return dev_err_probe(dev, ret, "can't shutdown als\n");
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;
- }
+ if (ret)
+ return dev_err_probe(dev, ret, "can't setup als configs\n");
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;
- }
+ if (ret)
+ return dev_err_probe(dev, ret, "can't setup default PSM\n");
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;
- }
+ if (ret)
+ return dev_err_probe(dev, ret, "can't setup high threshold\n");
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;
- }
+ if (ret)
+ return dev_err_probe(dev, ret, "can't setup low threshold\n");
ret = veml6030_als_pwr_on(data);
- if (ret) {
- dev_err(&client->dev, "can't poweron als %d\n", ret);
- return ret;
- }
+ if (ret)
+ return dev_err_probe(dev, ret, "can't poweron als\n");
- /* Wait 4 ms to let processor & oscillator start correctly */
- usleep_range(4000, 4002);
+ ret = devm_add_action_or_reset(dev, veml6030_als_shut_down_action, data);
+ if (ret < 0)
+ return ret;
/* 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);
+ if (ret < 0)
+ return dev_err_probe(dev, ret,
+ "can't clear als interrupt status\n");
+
+ return ret;
+}
+
+/*
+ * Set ALS gain to 1/8, integration time to 100 ms, ALS and WHITE
+ * channel enabled, ALS channel interrupt, PSM enabled,
+ * PSM_WAIT = 0.8 s, 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 veml6035_hw_init(struct iio_dev *indio_dev, struct device *dev)
+{
+ int ret, val;
+ struct veml6030_data *data = iio_priv(indio_dev);
+
+ ret = devm_iio_init_iio_gts(dev, 0, 409600000,
+ veml6035_gain_sel, ARRAY_SIZE(veml6035_gain_sel),
+ veml6030_it_sel, ARRAY_SIZE(veml6030_it_sel),
+ &data->gts);
+ if (ret)
+ return dev_err_probe(dev, ret, "failed to init iio gts\n");
+
+ ret = veml6030_als_shut_down(data);
+ if (ret)
+ return dev_err_probe(dev, ret, "can't shutdown als\n");
+
+ ret = regmap_write(data->regmap, VEML6030_REG_ALS_CONF,
+ VEML6035_SENS | VEML6035_CHAN_EN | VEML6030_ALS_SD);
+ if (ret)
+ return dev_err_probe(dev, ret, "can't setup als configs\n");
+
+ ret = regmap_update_bits(data->regmap, VEML6030_REG_ALS_PSM,
+ VEML6030_PSM | VEML6030_PSM_EN, 0x03);
+ if (ret)
+ return dev_err_probe(dev, ret, "can't setup default PSM\n");
+
+ ret = regmap_write(data->regmap, VEML6030_REG_ALS_WH, 0xFFFF);
+ if (ret)
+ return dev_err_probe(dev, ret, "can't setup high threshold\n");
+
+ ret = regmap_write(data->regmap, VEML6030_REG_ALS_WL, 0x0000);
+ if (ret)
+ return dev_err_probe(dev, ret, "can't setup low threshold\n");
+
+ ret = veml6030_als_pwr_on(data);
+ if (ret)
+ return dev_err_probe(dev, ret, "can't poweron als\n");
+
+ ret = devm_add_action_or_reset(dev, veml6030_als_shut_down_action, data);
+ if (ret < 0)
return ret;
- }
- /* Cache currently active measurement parameters */
- data->cur_gain = 3;
- data->cur_resolution = 4608;
- data->cur_integration_time = 3;
+ /* Clear stale interrupt status bits if any during start */
+ ret = regmap_read(data->regmap, VEML6030_REG_ALS_INT, &val);
+ if (ret < 0)
+ return dev_err_probe(dev, ret,
+ "can't clear als interrupt status\n");
- return ret;
+ return 0;
}
static int veml6030_probe(struct i2c_client *client)
@@ -793,16 +1086,14 @@ static int veml6030_probe(struct i2c_client *client)
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;
- }
+ if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C))
+ return dev_err_probe(&client->dev, -EOPNOTSUPP,
+ "i2c adapter doesn't support plain i2c\n");
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);
- }
+ if (IS_ERR(regmap))
+ return dev_err_probe(&client->dev, PTR_ERR(regmap),
+ "can't setup regmap\n");
indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data));
if (!indio_dev)
@@ -813,35 +1104,39 @@ static int veml6030_probe(struct i2c_client *client)
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;
+ ret = devm_regulator_get_enable(&client->dev, "vdd");
+ if (ret)
+ return dev_err_probe(&client->dev, ret,
+ "failed to enable regulator\n");
- 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;
- }
+ data->chip = i2c_get_match_data(client);
+ if (!data->chip)
+ return -EINVAL;
+
+ indio_dev->name = data->chip->name;
+ indio_dev->channels = data->chip->channels;
+ indio_dev->num_channels = data->chip->num_channels;
+ indio_dev->modes = INDIO_DIRECT_MODE;
- ret = veml6030_hw_init(indio_dev);
+ ret = data->chip->set_info(indio_dev);
if (ret < 0)
return ret;
- ret = devm_add_action_or_reset(&client->dev,
- veml6030_als_shut_down_action, data);
+ ret = veml6030_regfield_init(indio_dev);
+ if (ret)
+ return dev_err_probe(&client->dev, ret,
+ "failed to init regfields\n");
+
+ ret = data->chip->hw_init(indio_dev, &client->dev);
if (ret < 0)
return ret;
+ ret = devm_iio_triggered_buffer_setup(&client->dev, indio_dev, NULL,
+ veml6030_trigger_handler, NULL);
+ if (ret)
+ return dev_err_probe(&client->dev, ret,
+ "Failed to register triggered buffer");
+
return devm_iio_device_register(&client->dev, indio_dev);
}
@@ -874,14 +1169,60 @@ static int veml6030_runtime_resume(struct device *dev)
static DEFINE_RUNTIME_DEV_PM_OPS(veml6030_pm_ops, veml6030_runtime_suspend,
veml6030_runtime_resume, NULL);
+static const struct veml603x_chip veml6030_chip = {
+ .name = "veml6030",
+ .channels = veml6030_channels,
+ .num_channels = ARRAY_SIZE(veml6030_channels),
+ .gain_rf = VEML6030_GAIN_RF,
+ .it_rf = VEML6030_IT_RF,
+ .max_scale = VEML6030_MAX_SCALE,
+ .hw_init = veml6030_hw_init,
+ .set_info = veml6030_set_info,
+};
+
+static const struct veml603x_chip veml6035_chip = {
+ .name = "veml6035",
+ .channels = veml6030_channels,
+ .num_channels = ARRAY_SIZE(veml6030_channels),
+ .gain_rf = VEML6035_GAIN_RF,
+ .it_rf = VEML6030_IT_RF,
+ .max_scale = VEML6035_MAX_SCALE,
+ .hw_init = veml6035_hw_init,
+ .set_info = veml6030_set_info,
+};
+
+static const struct veml603x_chip veml7700_chip = {
+ .name = "veml7700",
+ .channels = veml7700_channels,
+ .num_channels = ARRAY_SIZE(veml7700_channels),
+ .gain_rf = VEML6030_GAIN_RF,
+ .it_rf = VEML6030_IT_RF,
+ .max_scale = VEML6030_MAX_SCALE,
+ .hw_init = veml6030_hw_init,
+ .set_info = veml7700_set_info,
+};
+
static const struct of_device_id veml6030_of_match[] = {
- { .compatible = "vishay,veml6030" },
+ {
+ .compatible = "vishay,veml6030",
+ .data = &veml6030_chip,
+ },
+ {
+ .compatible = "vishay,veml6035",
+ .data = &veml6035_chip,
+ },
+ {
+ .compatible = "vishay,veml7700",
+ .data = &veml7700_chip,
+ },
{ }
};
MODULE_DEVICE_TABLE(of, veml6030_of_match);
static const struct i2c_device_id veml6030_id[] = {
- { "veml6030", 0 },
+ { "veml6030", (kernel_ulong_t)&veml6030_chip},
+ { "veml6035", (kernel_ulong_t)&veml6035_chip},
+ { "veml7700", (kernel_ulong_t)&veml7700_chip},
{ }
};
MODULE_DEVICE_TABLE(i2c, veml6030_id);
@@ -900,3 +1241,4 @@ module_i2c_driver(veml6030_driver);
MODULE_AUTHOR("Rishi Gupta <gupt21@gmail.com>");
MODULE_DESCRIPTION("VEML6030 Ambient Light Sensor");
MODULE_LICENSE("GPL v2");
+MODULE_IMPORT_NS("IIO_GTS_HELPER");
diff --git a/drivers/iio/light/veml6040.c b/drivers/iio/light/veml6040.c
new file mode 100644
index 000000000000..71a594b2ec85
--- /dev/null
+++ b/drivers/iio/light/veml6040.c
@@ -0,0 +1,281 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Vishay VEML6040 RGBW light sensor driver
+ *
+ * Copyright (C) 2024 Sentec AG
+ * Author: Arthur Becker <arthur.becker@sentec.com>
+ *
+ */
+
+#include <linux/bitfield.h>
+#include <linux/err.h>
+#include <linux/i2c.h>
+#include <linux/iio/iio.h>
+#include <linux/iio/sysfs.h>
+#include <linux/module.h>
+#include <linux/regmap.h>
+
+/* VEML6040 Configuration Registers
+ *
+ * SD: Shutdown
+ * AF: Auto / Force Mode (Auto Measurements On:0, Off:1)
+ * TR: Trigger Measurement (when AF Bit is set)
+ * IT: Integration Time
+ */
+#define VEML6040_CONF_REG 0x000
+#define VEML6040_CONF_SD_MSK BIT(0)
+#define VEML6040_CONF_AF_MSK BIT(1)
+#define VEML6040_CONF_TR_MSK BIT(2)
+#define VEML6040_CONF_IT_MSK GENMASK(6, 4)
+#define VEML6040_CONF_IT_40_MS 0
+#define VEML6040_CONF_IT_80_MS 1
+#define VEML6040_CONF_IT_160_MS 2
+#define VEML6040_CONF_IT_320_MS 3
+#define VEML6040_CONF_IT_640_MS 4
+#define VEML6040_CONF_IT_1280_MS 5
+
+/* VEML6040 Read Only Registers */
+#define VEML6040_REG_R 0x08
+#define VEML6040_REG_G 0x09
+#define VEML6040_REG_B 0x0A
+#define VEML6040_REG_W 0x0B
+
+static const int veml6040_it_ms[] = { 40, 80, 160, 320, 640, 1280 };
+
+enum veml6040_chan {
+ CH_RED,
+ CH_GREEN,
+ CH_BLUE,
+ CH_WHITE,
+};
+
+struct veml6040_data {
+ struct i2c_client *client;
+ struct regmap *regmap;
+};
+
+static const struct regmap_config veml6040_regmap_config = {
+ .name = "veml6040_regmap",
+ .reg_bits = 8,
+ .val_bits = 16,
+ .max_register = VEML6040_REG_W,
+ .val_format_endian = REGMAP_ENDIAN_LITTLE,
+};
+
+static int veml6040_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan, int *val,
+ int *val2, long mask)
+{
+ int ret, reg, it_index;
+ struct veml6040_data *data = iio_priv(indio_dev);
+ struct regmap *regmap = data->regmap;
+ struct device *dev = &data->client->dev;
+
+ switch (mask) {
+ case IIO_CHAN_INFO_RAW:
+ ret = regmap_read(regmap, chan->address, &reg);
+ if (ret) {
+ dev_err(dev, "Data read failed: %d\n", ret);
+ return ret;
+ }
+ *val = reg;
+ return IIO_VAL_INT;
+
+ case IIO_CHAN_INFO_INT_TIME:
+ ret = regmap_read(regmap, VEML6040_CONF_REG, &reg);
+ if (ret) {
+ dev_err(dev, "Data read failed: %d\n", ret);
+ return ret;
+ }
+ it_index = FIELD_GET(VEML6040_CONF_IT_MSK, reg);
+ if (it_index >= ARRAY_SIZE(veml6040_it_ms)) {
+ dev_err(dev, "Invalid Integration Time Set");
+ return -EINVAL;
+ }
+ *val = veml6040_it_ms[it_index];
+ return IIO_VAL_INT;
+
+ default:
+ return -EINVAL;
+ }
+}
+
+static int veml6040_write_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan, int val,
+ int val2, long mask)
+{
+ struct veml6040_data *data = iio_priv(indio_dev);
+
+ switch (mask) {
+ case IIO_CHAN_INFO_INT_TIME:
+ for (int i = 0; i < ARRAY_SIZE(veml6040_it_ms); i++) {
+ if (veml6040_it_ms[i] != val)
+ continue;
+
+ return regmap_update_bits(data->regmap,
+ VEML6040_CONF_REG,
+ VEML6040_CONF_IT_MSK,
+ FIELD_PREP(VEML6040_CONF_IT_MSK, i));
+ }
+ return -EINVAL;
+ default:
+ return -EINVAL;
+ }
+}
+
+static int veml6040_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_INT_TIME:
+ *length = ARRAY_SIZE(veml6040_it_ms);
+ *vals = veml6040_it_ms;
+ *type = IIO_VAL_INT;
+ return IIO_AVAIL_LIST;
+
+ default:
+ return -EINVAL;
+ }
+}
+
+static const struct iio_info veml6040_info = {
+ .read_raw = veml6040_read_raw,
+ .write_raw = veml6040_write_raw,
+ .read_avail = veml6040_read_avail,
+};
+
+static const struct iio_chan_spec veml6040_channels[] = {
+ {
+ .type = IIO_INTENSITY,
+ .address = VEML6040_REG_R,
+ .channel = CH_RED,
+ .channel2 = IIO_MOD_LIGHT_RED,
+ .modified = 1,
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
+ .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_INT_TIME),
+ .info_mask_shared_by_type_available =
+ BIT(IIO_CHAN_INFO_INT_TIME),
+ },
+ {
+ .type = IIO_INTENSITY,
+ .address = VEML6040_REG_G,
+ .channel = CH_GREEN,
+ .channel2 = IIO_MOD_LIGHT_GREEN,
+ .modified = 1,
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
+ .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_INT_TIME),
+ .info_mask_shared_by_type_available =
+ BIT(IIO_CHAN_INFO_INT_TIME),
+ },
+ {
+ .type = IIO_INTENSITY,
+ .address = VEML6040_REG_B,
+ .channel = CH_BLUE,
+ .channel2 = IIO_MOD_LIGHT_BLUE,
+ .modified = 1,
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
+ .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_INT_TIME),
+ .info_mask_shared_by_type_available =
+ BIT(IIO_CHAN_INFO_INT_TIME),
+ },
+ {
+ .type = IIO_INTENSITY,
+ .address = VEML6040_REG_W,
+ .channel = CH_WHITE,
+ .channel2 = IIO_MOD_LIGHT_CLEAR,
+ .modified = 1,
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
+ .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_INT_TIME),
+ .info_mask_shared_by_type_available =
+ BIT(IIO_CHAN_INFO_INT_TIME),
+ }
+};
+
+static void veml6040_shutdown_action(void *data)
+{
+ struct veml6040_data *veml6040_data = data;
+
+ regmap_update_bits(veml6040_data->regmap, VEML6040_CONF_REG,
+ VEML6040_CONF_SD_MSK, VEML6040_CONF_SD_MSK);
+}
+
+static int veml6040_probe(struct i2c_client *client)
+{
+ struct device *dev = &client->dev;
+ struct veml6040_data *data;
+ struct iio_dev *indio_dev;
+ struct regmap *regmap;
+ const int init_config =
+ FIELD_PREP(VEML6040_CONF_IT_MSK, VEML6040_CONF_IT_40_MS) |
+ FIELD_PREP(VEML6040_CONF_AF_MSK, 0) |
+ FIELD_PREP(VEML6040_CONF_SD_MSK, 0);
+ int ret;
+
+ if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C))
+ return dev_err_probe(dev, -EOPNOTSUPP,
+ "I2C adapter doesn't support plain I2C\n");
+
+ indio_dev = devm_iio_device_alloc(dev, sizeof(*data));
+ if (!indio_dev)
+ return dev_err_probe(dev, -ENOMEM,
+ "IIO device allocation failed\n");
+
+ regmap = devm_regmap_init_i2c(client, &veml6040_regmap_config);
+ if (IS_ERR(regmap))
+ return dev_err_probe(dev, PTR_ERR(regmap),
+ "Regmap setup failed\n");
+
+ data = iio_priv(indio_dev);
+ i2c_set_clientdata(client, indio_dev);
+ data->client = client;
+ data->regmap = regmap;
+
+ indio_dev->name = "veml6040";
+ indio_dev->info = &veml6040_info;
+ indio_dev->channels = veml6040_channels;
+ indio_dev->num_channels = ARRAY_SIZE(veml6040_channels);
+ indio_dev->modes = INDIO_DIRECT_MODE;
+
+ ret = devm_regulator_get_enable(dev, "vdd");
+ if (ret)
+ return ret;
+
+ ret = regmap_write(regmap, VEML6040_CONF_REG, init_config);
+ if (ret)
+ return dev_err_probe(dev, ret,
+ "Could not set initial config\n");
+
+ ret = devm_add_action_or_reset(dev, veml6040_shutdown_action, data);
+ if (ret)
+ return ret;
+
+ return devm_iio_device_register(dev, indio_dev);
+}
+
+static const struct i2c_device_id veml6040_id_table[] = {
+ {"veml6040"},
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, veml6040_id_table);
+
+static const struct of_device_id veml6040_of_match[] = {
+ {.compatible = "vishay,veml6040"},
+ { }
+};
+MODULE_DEVICE_TABLE(of, veml6040_of_match);
+
+static struct i2c_driver veml6040_driver = {
+ .probe = veml6040_probe,
+ .id_table = veml6040_id_table,
+ .driver = {
+ .name = "veml6040",
+ .of_match_table = veml6040_of_match,
+ },
+};
+module_i2c_driver(veml6040_driver);
+
+MODULE_DESCRIPTION("veml6040 RGBW light sensor driver");
+MODULE_AUTHOR("Arthur Becker <arthur.becker@sentec.com>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/iio/light/veml6070.c b/drivers/iio/light/veml6070.c
index d99bf3ae0fe8..6d4483c85f30 100644
--- a/drivers/iio/light/veml6070.c
+++ b/drivers/iio/light/veml6070.c
@@ -6,14 +6,16 @@
*
* IIO driver for VEML6070 (7-bit I2C slave addresses 0x38 and 0x39)
*
- * TODO: integration time, ACK signal
+ * TODO: ACK signal
*/
+#include <linux/bitfield.h>
#include <linux/module.h>
#include <linux/i2c.h>
#include <linux/mutex.h>
#include <linux/err.h>
#include <linux/delay.h>
+#include <linux/units.h>
#include <linux/iio/iio.h>
#include <linux/iio/sysfs.h>
@@ -28,50 +30,113 @@
#define VEML6070_COMMAND_RSRVD BIT(1) /* reserved, set to 1 */
#define VEML6070_COMMAND_SD BIT(0) /* shutdown mode when set */
-#define VEML6070_IT_10 0x04 /* integration time 1x */
+#define VEML6070_IT_05 0x00
+#define VEML6070_IT_10 0x01
+#define VEML6070_IT_20 0x02
+#define VEML6070_IT_40 0x03
+
+#define VEML6070_MIN_RSET_KOHM 75
+#define VEML6070_MIN_IT_US 15625 /* Rset = 75 kohm, IT = 1/2 */
struct veml6070_data {
struct i2c_client *client1;
struct i2c_client *client2;
u8 config;
struct mutex lock;
+ u32 rset;
+ int it[4][2];
};
+static int veml6070_calc_it(struct device *dev, struct veml6070_data *data)
+{
+ int i, tmp_it;
+
+ data->rset = 270000;
+ device_property_read_u32(dev, "vishay,rset-ohms", &data->rset);
+
+ if (data->rset < 75000 || data->rset > 1200000)
+ return dev_err_probe(dev, -EINVAL, "Rset out of range\n");
+
+ /*
+ * convert to kohm to avoid overflows and work with the same units as
+ * in the datasheet and simplify UVI operations.
+ */
+ data->rset /= KILO;
+
+ tmp_it = VEML6070_MIN_IT_US * data->rset / VEML6070_MIN_RSET_KOHM;
+ for (i = 0; i < ARRAY_SIZE(data->it); i++) {
+ data->it[i][0] = (tmp_it << i) / MICRO;
+ data->it[i][1] = (tmp_it << i) % MICRO;
+ }
+
+ return 0;
+}
+
+static int veml6070_get_it(struct veml6070_data *data, int *val, int *val2)
+{
+ int it_idx = FIELD_GET(VEML6070_COMMAND_IT, data->config);
+
+ *val = data->it[it_idx][0];
+ *val2 = data->it[it_idx][1];
+
+ return IIO_VAL_INT_PLUS_MICRO;
+}
+
+static int veml6070_set_it(struct veml6070_data *data, int val, int val2)
+{
+ int it_idx;
+
+ for (it_idx = 0; it_idx < ARRAY_SIZE(data->it); it_idx++) {
+ if (data->it[it_idx][0] == val && data->it[it_idx][1] == val2)
+ break;
+ }
+
+ if (it_idx >= ARRAY_SIZE(data->it))
+ return -EINVAL;
+
+ data->config = (data->config & ~VEML6070_COMMAND_IT) |
+ FIELD_PREP(VEML6070_COMMAND_IT, it_idx);
+
+ return i2c_smbus_write_byte(data->client1, data->config);
+}
+
static int veml6070_read(struct veml6070_data *data)
{
- int ret;
+ int ret, it_ms, val, val2;
u8 msb, lsb;
- mutex_lock(&data->lock);
+ guard(mutex)(&data->lock);
/* disable shutdown */
ret = i2c_smbus_write_byte(data->client1,
data->config & ~VEML6070_COMMAND_SD);
if (ret < 0)
- goto out;
+ return ret;
- msleep(125 + 10); /* measurement takes up to 125 ms for IT 1x */
+ veml6070_get_it(data, &val, &val2);
+ it_ms = val * MILLI + val2 / (MICRO / MILLI);
+ msleep(it_ms + 10);
ret = i2c_smbus_read_byte(data->client2); /* read MSB, address 0x39 */
if (ret < 0)
- goto out;
+ return ret;
+
msb = ret;
ret = i2c_smbus_read_byte(data->client1); /* read LSB, address 0x38 */
if (ret < 0)
- goto out;
+ return ret;
+
lsb = ret;
/* shutdown again */
ret = i2c_smbus_write_byte(data->client1, data->config);
if (ret < 0)
- goto out;
+ return ret;
ret = (msb << 8) | lsb;
-out:
- mutex_unlock(&data->lock);
- return ret;
+ return 0;
}
static const struct iio_chan_spec veml6070_channels[] = {
@@ -80,26 +145,37 @@ static const struct iio_chan_spec veml6070_channels[] = {
.modified = 1,
.channel2 = IIO_MOD_LIGHT_UV,
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
+ .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_INT_TIME),
+ .info_mask_shared_by_all_available = BIT(IIO_CHAN_INFO_INT_TIME),
},
{
.type = IIO_UVINDEX,
.info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED),
+ .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_INT_TIME),
+ .info_mask_shared_by_all_available = BIT(IIO_CHAN_INFO_INT_TIME),
}
};
-static int veml6070_to_uv_index(unsigned val)
+static int veml6070_to_uv_index(struct veml6070_data *data, unsigned int val)
{
/*
* conversion of raw UV intensity values to UV index depends on
* integration time (IT) and value of the resistor connected to
- * the RSET pin (default: 270 KOhm)
+ * the RSET pin.
*/
- unsigned uvi[11] = {
+ unsigned int uvi[11] = {
187, 373, 560, /* low */
746, 933, 1120, /* moderate */
1308, 1494, /* high */
1681, 1868, 2054}; /* very high */
- int i;
+ int i, it_idx;
+
+ it_idx = FIELD_GET(VEML6070_COMMAND_IT, data->config);
+
+ if (!it_idx)
+ val = (val * 270 / data->rset) << 1;
+ else
+ val = (val * 270 / data->rset) >> (it_idx - 1);
for (i = 0; i < ARRAY_SIZE(uvi); i++)
if (val <= uvi[i])
@@ -122,10 +198,44 @@ static int veml6070_read_raw(struct iio_dev *indio_dev,
if (ret < 0)
return ret;
if (mask == IIO_CHAN_INFO_PROCESSED)
- *val = veml6070_to_uv_index(ret);
+ *val = veml6070_to_uv_index(data, ret);
else
*val = ret;
return IIO_VAL_INT;
+ case IIO_CHAN_INFO_INT_TIME:
+ return veml6070_get_it(data, val, val2);
+ default:
+ return -EINVAL;
+ }
+}
+
+static int veml6070_read_avail(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ const int **vals, int *type, int *length,
+ long mask)
+{
+ struct veml6070_data *data = iio_priv(indio_dev);
+
+ switch (mask) {
+ case IIO_CHAN_INFO_INT_TIME:
+ *vals = (int *)data->it;
+ *length = 2 * ARRAY_SIZE(data->it);
+ *type = IIO_VAL_INT_PLUS_MICRO;
+ return IIO_AVAIL_LIST;
+ default:
+ return -EINVAL;
+ }
+}
+
+static int veml6070_write_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int val, int val2, long mask)
+{
+ struct veml6070_data *data = iio_priv(indio_dev);
+
+ switch (mask) {
+ case IIO_CHAN_INFO_INT_TIME:
+ return veml6070_set_it(data, val, val2);
default:
return -EINVAL;
}
@@ -133,8 +243,17 @@ static int veml6070_read_raw(struct iio_dev *indio_dev,
static const struct iio_info veml6070_info = {
.read_raw = veml6070_read_raw,
+ .read_avail = veml6070_read_avail,
+ .write_raw = veml6070_write_raw,
};
+static void veml6070_i2c_unreg(void *p)
+{
+ struct veml6070_data *data = p;
+
+ i2c_unregister_device(data->client2);
+}
+
static int veml6070_probe(struct i2c_client *client)
{
struct veml6070_data *data;
@@ -156,50 +275,50 @@ static int veml6070_probe(struct i2c_client *client)
indio_dev->name = VEML6070_DRV_NAME;
indio_dev->modes = INDIO_DIRECT_MODE;
- data->client2 = i2c_new_dummy_device(client->adapter, VEML6070_ADDR_DATA_LSB);
- if (IS_ERR(data->client2)) {
- dev_err(&client->dev, "i2c device for second chip address failed\n");
- return PTR_ERR(data->client2);
- }
-
- data->config = VEML6070_IT_10 | VEML6070_COMMAND_RSRVD |
- VEML6070_COMMAND_SD;
- ret = i2c_smbus_write_byte(data->client1, data->config);
+ ret = veml6070_calc_it(&client->dev, data);
if (ret < 0)
- goto fail;
+ return ret;
- ret = iio_device_register(indio_dev);
+ ret = devm_regulator_get_enable(&client->dev, "vdd");
if (ret < 0)
- goto fail;
+ return ret;
- return ret;
+ data->client2 = i2c_new_dummy_device(client->adapter, VEML6070_ADDR_DATA_LSB);
+ if (IS_ERR(data->client2))
+ return dev_err_probe(&client->dev, PTR_ERR(data->client2),
+ "i2c device for second chip address failed\n");
-fail:
- i2c_unregister_device(data->client2);
- return ret;
-}
+ data->config = FIELD_PREP(VEML6070_COMMAND_IT, VEML6070_IT_10) |
+ VEML6070_COMMAND_RSRVD | VEML6070_COMMAND_SD;
+ ret = i2c_smbus_write_byte(data->client1, data->config);
+ if (ret < 0)
+ return ret;
-static void veml6070_remove(struct i2c_client *client)
-{
- struct iio_dev *indio_dev = i2c_get_clientdata(client);
- struct veml6070_data *data = iio_priv(indio_dev);
+ ret = devm_add_action_or_reset(&client->dev, veml6070_i2c_unreg, data);
+ if (ret < 0)
+ return ret;
- iio_device_unregister(indio_dev);
- i2c_unregister_device(data->client2);
+ return devm_iio_device_register(&client->dev, indio_dev);
}
static const struct i2c_device_id veml6070_id[] = {
- { "veml6070", 0 },
+ { "veml6070" },
{ }
};
MODULE_DEVICE_TABLE(i2c, veml6070_id);
+static const struct of_device_id veml6070_of_match[] = {
+ { .compatible = "vishay,veml6070" },
+ { }
+};
+MODULE_DEVICE_TABLE(of, veml6070_of_match);
+
static struct i2c_driver veml6070_driver = {
.driver = {
.name = VEML6070_DRV_NAME,
+ .of_match_table = veml6070_of_match,
},
.probe = veml6070_probe,
- .remove = veml6070_remove,
.id_table = veml6070_id,
};
diff --git a/drivers/iio/light/veml6075.c b/drivers/iio/light/veml6075.c
index 05d4c0e9015d..edbb43407054 100644
--- a/drivers/iio/light/veml6075.c
+++ b/drivers/iio/light/veml6075.c
@@ -195,13 +195,17 @@ static int veml6075_read_uv_direct(struct veml6075_data *data, int chan,
static int veml6075_read_int_time_index(struct veml6075_data *data)
{
- int ret, conf;
+ int ret, conf, int_index;
ret = regmap_read(data->regmap, VEML6075_CMD_CONF, &conf);
if (ret < 0)
return ret;
- return FIELD_GET(VEML6075_CONF_IT, conf);
+ int_index = FIELD_GET(VEML6075_CONF_IT, conf);
+ if (int_index >= ARRAY_SIZE(veml6075_it_ms))
+ return -EINVAL;
+
+ return int_index;
}
static int veml6075_read_int_time_ms(struct veml6075_data *data, int *val)
@@ -454,7 +458,7 @@ MODULE_DEVICE_TABLE(i2c, veml6075_id);
static const struct of_device_id veml6075_of_match[] = {
{ .compatible = "vishay,veml6075" },
- {}
+ { }
};
MODULE_DEVICE_TABLE(of, veml6075_of_match);
diff --git a/drivers/iio/light/vl6180.c b/drivers/iio/light/vl6180.c
index dcadf6428a87..cc4f2e5404aa 100644
--- a/drivers/iio/light/vl6180.c
+++ b/drivers/iio/light/vl6180.c
@@ -25,6 +25,10 @@
#include <linux/iio/iio.h>
#include <linux/iio/sysfs.h>
+#include <linux/iio/buffer.h>
+#include <linux/iio/trigger.h>
+#include <linux/iio/trigger_consumer.h>
+#include <linux/iio/triggered_buffer.h>
#define VL6180_DRV_NAME "vl6180"
@@ -38,7 +42,9 @@
#define VL6180_OUT_OF_RESET 0x016
#define VL6180_HOLD 0x017
#define VL6180_RANGE_START 0x018
+#define VL6180_RANGE_INTER_MEAS_TIME 0x01b
#define VL6180_ALS_START 0x038
+#define VL6180_ALS_INTER_MEAS_TIME 0x03e
#define VL6180_ALS_GAIN 0x03f
#define VL6180_ALS_IT 0x040
@@ -84,8 +90,17 @@
struct vl6180_data {
struct i2c_client *client;
struct mutex lock;
+ struct completion completion;
+ struct iio_trigger *trig;
unsigned int als_gain_milli;
unsigned int als_it_ms;
+ unsigned int als_meas_rate;
+ unsigned int range_meas_rate;
+
+ struct {
+ u16 chan[2];
+ aligned_s64 timestamp;
+ } scan;
};
enum { VL6180_ALS, VL6180_RANGE, VL6180_PROX };
@@ -207,29 +222,40 @@ static int vl6180_write_word(struct i2c_client *client, u16 cmd, u16 val)
static int vl6180_measure(struct vl6180_data *data, int addr)
{
struct i2c_client *client = data->client;
+ unsigned long time_left;
int tries = 20, ret;
u16 value;
mutex_lock(&data->lock);
+ reinit_completion(&data->completion);
+
/* Start single shot measurement */
ret = vl6180_write_byte(client,
vl6180_chan_regs_table[addr].start_reg, VL6180_STARTSTOP);
if (ret < 0)
goto fail;
- while (tries--) {
- ret = vl6180_read_byte(client, VL6180_INTR_STATUS);
- if (ret < 0)
+ if (client->irq) {
+ time_left = wait_for_completion_timeout(&data->completion, HZ / 10);
+ if (time_left == 0) {
+ ret = -ETIMEDOUT;
goto fail;
+ }
+ } else {
+ while (tries--) {
+ ret = vl6180_read_byte(client, VL6180_INTR_STATUS);
+ if (ret < 0)
+ goto fail;
+
+ if (ret & vl6180_chan_regs_table[addr].drdy_mask)
+ break;
+ msleep(20);
+ }
- if (ret & vl6180_chan_regs_table[addr].drdy_mask)
- break;
- msleep(20);
- }
-
- if (tries < 0) {
- ret = -EIO;
- goto fail;
+ if (tries < 0) {
+ ret = -EIO;
+ goto fail;
+ }
}
/* Read result value from appropriate registers */
@@ -258,20 +284,41 @@ static const struct iio_chan_spec vl6180_channels[] = {
{
.type = IIO_LIGHT,
.address = VL6180_ALS,
+ .scan_index = VL6180_ALS,
+ .scan_type = {
+ .sign = 'u',
+ .realbits = 16,
+ .storagebits = 16,
+ },
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
BIT(IIO_CHAN_INFO_INT_TIME) |
BIT(IIO_CHAN_INFO_SCALE) |
- BIT(IIO_CHAN_INFO_HARDWAREGAIN),
+ BIT(IIO_CHAN_INFO_HARDWAREGAIN) |
+ BIT(IIO_CHAN_INFO_SAMP_FREQ),
}, {
.type = IIO_DISTANCE,
.address = VL6180_RANGE,
+ .scan_index = VL6180_RANGE,
+ .scan_type = {
+ .sign = 'u',
+ .realbits = 8,
+ .storagebits = 8,
+ },
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
- BIT(IIO_CHAN_INFO_SCALE),
+ BIT(IIO_CHAN_INFO_SCALE) |
+ BIT(IIO_CHAN_INFO_SAMP_FREQ),
}, {
.type = IIO_PROXIMITY,
.address = VL6180_PROX,
+ .scan_index = VL6180_PROX,
+ .scan_type = {
+ .sign = 'u',
+ .realbits = 16,
+ .storagebits = 16,
+ },
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
- }
+ },
+ IIO_CHAN_SOFT_TIMESTAMP(3),
};
/*
@@ -333,6 +380,18 @@ static int vl6180_read_raw(struct iio_dev *indio_dev,
return IIO_VAL_FRACTIONAL;
+ case IIO_CHAN_INFO_SAMP_FREQ:
+ switch (chan->type) {
+ case IIO_DISTANCE:
+ *val = data->range_meas_rate;
+ return IIO_VAL_INT;
+ case IIO_LIGHT:
+ *val = data->als_meas_rate;
+ return IIO_VAL_INT;
+ default:
+ return -EINVAL;
+ }
+
default:
return -EINVAL;
}
@@ -412,11 +471,23 @@ fail:
return ret;
}
+static int vl6180_meas_reg_val_from_mhz(unsigned int mhz)
+{
+ unsigned int period = DIV_ROUND_CLOSEST(1000 * 1000, mhz);
+ unsigned int reg_val = 0;
+
+ if (period > 10)
+ reg_val = period < 2550 ? (DIV_ROUND_CLOSEST(period, 10) - 1) : 254;
+
+ return reg_val;
+}
+
static int vl6180_write_raw(struct iio_dev *indio_dev,
struct iio_chan_spec const *chan,
int val, int val2, long mask)
{
struct vl6180_data *data = iio_priv(indio_dev);
+ unsigned int reg_val;
switch (mask) {
case IIO_CHAN_INFO_INT_TIME:
@@ -427,18 +498,126 @@ static int vl6180_write_raw(struct iio_dev *indio_dev,
return -EINVAL;
return vl6180_set_als_gain(data, val, val2);
+
+ case IIO_CHAN_INFO_SAMP_FREQ:
+ {
+ guard(mutex)(&data->lock);
+ switch (chan->type) {
+ case IIO_DISTANCE:
+ data->range_meas_rate = val;
+ reg_val = vl6180_meas_reg_val_from_mhz(val);
+ return vl6180_write_byte(data->client,
+ VL6180_RANGE_INTER_MEAS_TIME, reg_val);
+
+ case IIO_LIGHT:
+ data->als_meas_rate = val;
+ reg_val = vl6180_meas_reg_val_from_mhz(val);
+ return vl6180_write_byte(data->client,
+ VL6180_ALS_INTER_MEAS_TIME, reg_val);
+
+ default:
+ return -EINVAL;
+ }
+ }
+
default:
return -EINVAL;
}
}
+static irqreturn_t vl6180_threaded_irq(int irq, void *priv)
+{
+ struct iio_dev *indio_dev = priv;
+ struct vl6180_data *data = iio_priv(indio_dev);
+
+ if (iio_buffer_enabled(indio_dev))
+ iio_trigger_poll_nested(indio_dev->trig);
+ else
+ complete(&data->completion);
+
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t vl6180_trigger_handler(int irq, void *priv)
+{
+ struct iio_poll_func *pf = priv;
+ struct iio_dev *indio_dev = pf->indio_dev;
+ struct vl6180_data *data = iio_priv(indio_dev);
+ s64 time_ns = iio_get_time_ns(indio_dev);
+ int ret, bit, i = 0;
+
+ iio_for_each_active_channel(indio_dev, bit) {
+ if (vl6180_chan_regs_table[bit].word)
+ ret = vl6180_read_word(data->client,
+ vl6180_chan_regs_table[bit].value_reg);
+ else
+ ret = vl6180_read_byte(data->client,
+ vl6180_chan_regs_table[bit].value_reg);
+
+ if (ret < 0) {
+ dev_err(&data->client->dev,
+ "failed to read from value regs: %d\n", ret);
+ return IRQ_HANDLED;
+ }
+
+ data->scan.chan[i++] = ret;
+ }
+
+ iio_push_to_buffers_with_timestamp(indio_dev, &data->scan, time_ns);
+ iio_trigger_notify_done(indio_dev->trig);
+
+ /* Clear the interrupt flag after data read */
+ ret = vl6180_write_byte(data->client, VL6180_INTR_CLEAR,
+ VL6180_CLEAR_ERROR | VL6180_CLEAR_ALS | VL6180_CLEAR_RANGE);
+ if (ret < 0)
+ dev_err(&data->client->dev, "failed to clear irq: %d\n", ret);
+
+ return IRQ_HANDLED;
+}
+
static const struct iio_info vl6180_info = {
.read_raw = vl6180_read_raw,
.write_raw = vl6180_write_raw,
.attrs = &vl6180_attribute_group,
+ .validate_trigger = iio_validate_own_trigger,
};
-static int vl6180_init(struct vl6180_data *data)
+static int vl6180_buffer_postenable(struct iio_dev *indio_dev)
+{
+ struct vl6180_data *data = iio_priv(indio_dev);
+ int bit;
+
+ iio_for_each_active_channel(indio_dev, bit)
+ return vl6180_write_byte(data->client,
+ vl6180_chan_regs_table[bit].start_reg,
+ VL6180_MODE_CONT | VL6180_STARTSTOP);
+
+ return -EINVAL;
+}
+
+static int vl6180_buffer_postdisable(struct iio_dev *indio_dev)
+{
+ struct vl6180_data *data = iio_priv(indio_dev);
+ int bit;
+
+ iio_for_each_active_channel(indio_dev, bit)
+ return vl6180_write_byte(data->client,
+ vl6180_chan_regs_table[bit].start_reg,
+ VL6180_STARTSTOP);
+
+ return -EINVAL;
+}
+
+static const struct iio_buffer_setup_ops iio_triggered_buffer_setup_ops = {
+ .postenable = &vl6180_buffer_postenable,
+ .postdisable = &vl6180_buffer_postdisable,
+};
+
+static const struct iio_trigger_ops vl6180_trigger_ops = {
+ .validate_device = iio_trigger_validate_own_device,
+};
+
+static int vl6180_init(struct vl6180_data *data, struct iio_dev *indio_dev)
{
struct i2c_client *client = data->client;
int ret;
@@ -473,6 +652,26 @@ static int vl6180_init(struct vl6180_data *data)
if (ret < 0)
return ret;
+ ret = devm_iio_triggered_buffer_setup(&client->dev, indio_dev, NULL,
+ &vl6180_trigger_handler,
+ &iio_triggered_buffer_setup_ops);
+ if (ret)
+ return ret;
+
+ /* Default Range inter-measurement time: 50ms or 20000 mHz */
+ ret = vl6180_write_byte(client, VL6180_RANGE_INTER_MEAS_TIME,
+ vl6180_meas_reg_val_from_mhz(20000));
+ if (ret < 0)
+ return ret;
+ data->range_meas_rate = 20000;
+
+ /* Default ALS inter-measurement time: 10ms or 100000 mHz */
+ ret = vl6180_write_byte(client, VL6180_ALS_INTER_MEAS_TIME,
+ vl6180_meas_reg_val_from_mhz(100000));
+ if (ret < 0)
+ return ret;
+ data->als_meas_rate = 100000;
+
/* ALS integration time: 100ms */
data->als_it_ms = 100;
ret = vl6180_write_word(client, VL6180_ALS_IT, VL6180_ALS_IT_100);
@@ -513,21 +712,45 @@ static int vl6180_probe(struct i2c_client *client)
indio_dev->name = VL6180_DRV_NAME;
indio_dev->modes = INDIO_DIRECT_MODE;
- ret = vl6180_init(data);
+ ret = vl6180_init(data, indio_dev);
if (ret < 0)
return ret;
+ if (client->irq) {
+ ret = devm_request_threaded_irq(&client->dev, client->irq,
+ NULL, vl6180_threaded_irq,
+ IRQF_ONESHOT,
+ indio_dev->name, indio_dev);
+ if (ret)
+ return dev_err_probe(&client->dev, ret, "devm_request_irq error \n");
+
+ init_completion(&data->completion);
+
+ data->trig = devm_iio_trigger_alloc(&client->dev, "%s-dev%d",
+ indio_dev->name, iio_device_id(indio_dev));
+ if (!data->trig)
+ return -ENOMEM;
+
+ data->trig->ops = &vl6180_trigger_ops;
+ iio_trigger_set_drvdata(data->trig, indio_dev);
+ ret = devm_iio_trigger_register(&client->dev, data->trig);
+ if (ret)
+ return ret;
+
+ indio_dev->trig = iio_trigger_get(data->trig);
+ }
+
return devm_iio_device_register(&client->dev, indio_dev);
}
static const struct of_device_id vl6180_of_match[] = {
{ .compatible = "st,vl6180", },
- { },
+ { }
};
MODULE_DEVICE_TABLE(of, vl6180_of_match);
static const struct i2c_device_id vl6180_id[] = {
- { "vl6180", 0 },
+ { "vl6180" },
{ }
};
MODULE_DEVICE_TABLE(i2c, vl6180_id);
diff --git a/drivers/iio/light/zopt2201.c b/drivers/iio/light/zopt2201.c
index d370193a4742..1e5e9bf2935f 100644
--- a/drivers/iio/light/zopt2201.c
+++ b/drivers/iio/light/zopt2201.c
@@ -19,7 +19,7 @@
#include <linux/iio/iio.h>
#include <linux/iio/sysfs.h>
-#include <asm/unaligned.h>
+#include <linux/unaligned.h>
#define ZOPT2201_DRV_NAME "zopt2201"
@@ -113,11 +113,13 @@ static const struct {
{ 13, 3125 },
};
-static const struct {
+struct zopt2201_scale {
unsigned int scale, uscale; /* scale factor as integer + micro */
u8 gain; /* gain register value */
u8 res; /* resolution register value */
-} zopt2201_scale_als[] = {
+};
+
+static struct zopt2201_scale zopt2201_scale_als[] = {
{ 19, 200000, 0, 5 },
{ 6, 400000, 1, 5 },
{ 3, 200000, 2, 5 },
@@ -142,11 +144,7 @@ static const struct {
{ 0, 8333, 4, 0 },
};
-static const struct {
- unsigned int scale, uscale; /* scale factor as integer + micro */
- u8 gain; /* gain register value */
- u8 res; /* resolution register value */
-} zopt2201_scale_uvb[] = {
+static struct zopt2201_scale zopt2201_scale_uvb[] = {
{ 0, 460800, 0, 5 },
{ 0, 153600, 1, 5 },
{ 0, 76800, 2, 5 },
@@ -348,16 +346,17 @@ static int zopt2201_set_gain(struct zopt2201_data *data, u8 gain)
return 0;
}
-static int zopt2201_write_scale_als_by_idx(struct zopt2201_data *data, int idx)
+static int zopt2201_write_scale_by_idx(struct zopt2201_data *data, int idx,
+ struct zopt2201_scale *zopt2201_scale_array)
{
int ret;
mutex_lock(&data->lock);
- ret = zopt2201_set_resolution(data, zopt2201_scale_als[idx].res);
+ ret = zopt2201_set_resolution(data, zopt2201_scale_array[idx].res);
if (ret < 0)
goto unlock;
- ret = zopt2201_set_gain(data, zopt2201_scale_als[idx].gain);
+ ret = zopt2201_set_gain(data, zopt2201_scale_array[idx].gain);
unlock:
mutex_unlock(&data->lock);
@@ -371,29 +370,12 @@ static int zopt2201_write_scale_als(struct zopt2201_data *data,
for (i = 0; i < ARRAY_SIZE(zopt2201_scale_als); i++)
if (val == zopt2201_scale_als[i].scale &&
- val2 == zopt2201_scale_als[i].uscale) {
- return zopt2201_write_scale_als_by_idx(data, i);
- }
+ val2 == zopt2201_scale_als[i].uscale)
+ return zopt2201_write_scale_by_idx(data, i, zopt2201_scale_als);
return -EINVAL;
}
-static int zopt2201_write_scale_uvb_by_idx(struct zopt2201_data *data, int idx)
-{
- int ret;
-
- mutex_lock(&data->lock);
- ret = zopt2201_set_resolution(data, zopt2201_scale_als[idx].res);
- if (ret < 0)
- goto unlock;
-
- ret = zopt2201_set_gain(data, zopt2201_scale_als[idx].gain);
-
-unlock:
- mutex_unlock(&data->lock);
- return ret;
-}
-
static int zopt2201_write_scale_uvb(struct zopt2201_data *data,
int val, int val2)
{
@@ -402,7 +384,7 @@ static int zopt2201_write_scale_uvb(struct zopt2201_data *data,
for (i = 0; i < ARRAY_SIZE(zopt2201_scale_uvb); i++)
if (val == zopt2201_scale_uvb[i].scale &&
val2 == zopt2201_scale_uvb[i].uscale)
- return zopt2201_write_scale_uvb_by_idx(data, i);
+ return zopt2201_write_scale_by_idx(data, i, zopt2201_scale_uvb);
return -EINVAL;
}
@@ -545,7 +527,7 @@ static int zopt2201_probe(struct i2c_client *client)
}
static const struct i2c_device_id zopt2201_id[] = {
- { "zopt2201", 0 },
+ { "zopt2201" },
{ }
};
MODULE_DEVICE_TABLE(i2c, zopt2201_id);