diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2024-07-19 15:55:08 -0700 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2024-07-19 15:55:08 -0700 |
commit | acc5965b9ff8a1889f5b51466562896d59c6e1b9 (patch) | |
tree | 8cd651e6594a9133f216d59a4fa1f18d8c63fb26 /drivers/iio/light/veml6040.c | |
parent | 09ea8089abb5d851ce08a9b1a43706e42ef39db2 (diff) | |
parent | 5418e6dfc905b3ccc1e01bdad97d948697b20100 (diff) |
Merge tag 'char-misc-6.11-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/char-misc
Pull char / misc and other driver updates from Greg KH:
"Here is the "big" set of char/misc and other driver subsystem changes
for 6.11-rc1. Nothing major in here, just loads of new drivers and
updates. Included in here are:
- IIO api updates and new drivers added
- wait_interruptable_timeout() api cleanups for some drivers
- MODULE_DESCRIPTION() additions for loads of drivers
- parport out-of-bounds fix
- interconnect driver updates and additions
- mhi driver updates and additions
- w1 driver fixes
- binder speedups and fixes
- eeprom driver updates
- coresight driver updates
- counter driver update
- new misc driver additions
- other minor api updates
All of these, EXCEPT for the final Kconfig build fix for 32bit
systems, have been in linux-next for a while with no reported issues.
The Kconfig fixup went in 29 hours ago, so might have missed the
latest linux-next, but was acked by everyone involved"
* tag 'char-misc-6.11-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/char-misc: (330 commits)
misc: Kconfig: exclude mrvl-cn10k-dpi compilation for 32-bit systems
misc: delete Makefile.rej
binder: fix hang of unregistered readers
misc: Kconfig: add a new dependency for MARVELL_CN10K_DPI
virtio: add missing MODULE_DESCRIPTION() macro
agp: uninorth: add missing MODULE_DESCRIPTION() macro
spmi: add missing MODULE_DESCRIPTION() macros
dev/parport: fix the array out-of-bounds risk
samples: configfs: add missing MODULE_DESCRIPTION() macro
misc: mrvl-cn10k-dpi: add Octeon CN10K DPI administrative driver
misc: keba: Fix missing AUXILIARY_BUS dependency
slimbus: Fix struct and documentation alignment in stream.c
MAINTAINERS: CC dri-devel list on Qualcomm FastRPC patches
misc: fastrpc: use coherent pool for untranslated Compute Banks
misc: fastrpc: support complete DMA pool access to the DSP
misc: fastrpc: add missing MODULE_DESCRIPTION() macro
misc: fastrpc: Add missing dev_err newlines
misc: fastrpc: Use memdup_user()
nvmem: core: Implement force_ro sysfs attribute
nvmem: Use sysfs_emit() for type attribute
...
Diffstat (limited to 'drivers/iio/light/veml6040.c')
-rw-r--r-- | drivers/iio/light/veml6040.c | 281 |
1 files changed, 281 insertions, 0 deletions
diff --git a/drivers/iio/light/veml6040.c b/drivers/iio/light/veml6040.c new file mode 100644 index 000000000000..216e271001a8 --- /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, ®); + 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, ®); + 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"); |