| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281 |
- // 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");
|