282 lines · c
1// SPDX-License-Identifier: GPL-2.0+2/*3 * Vishay VEML6040 RGBW light sensor driver4 *5 * Copyright (C) 2024 Sentec AG6 * Author: Arthur Becker <arthur.becker@sentec.com>7 *8 */9 10#include <linux/bitfield.h>11#include <linux/err.h>12#include <linux/i2c.h>13#include <linux/iio/iio.h>14#include <linux/iio/sysfs.h>15#include <linux/module.h>16#include <linux/regmap.h>17 18/* VEML6040 Configuration Registers19 *20 * SD: Shutdown21 * AF: Auto / Force Mode (Auto Measurements On:0, Off:1)22 * TR: Trigger Measurement (when AF Bit is set)23 * IT: Integration Time24 */25#define VEML6040_CONF_REG 0x00026#define VEML6040_CONF_SD_MSK BIT(0)27#define VEML6040_CONF_AF_MSK BIT(1)28#define VEML6040_CONF_TR_MSK BIT(2)29#define VEML6040_CONF_IT_MSK GENMASK(6, 4)30#define VEML6040_CONF_IT_40_MS 031#define VEML6040_CONF_IT_80_MS 132#define VEML6040_CONF_IT_160_MS 233#define VEML6040_CONF_IT_320_MS 334#define VEML6040_CONF_IT_640_MS 435#define VEML6040_CONF_IT_1280_MS 536 37/* VEML6040 Read Only Registers */38#define VEML6040_REG_R 0x0839#define VEML6040_REG_G 0x0940#define VEML6040_REG_B 0x0A41#define VEML6040_REG_W 0x0B42 43static const int veml6040_it_ms[] = { 40, 80, 160, 320, 640, 1280 };44 45enum veml6040_chan {46 CH_RED,47 CH_GREEN,48 CH_BLUE,49 CH_WHITE,50};51 52struct veml6040_data {53 struct i2c_client *client;54 struct regmap *regmap;55};56 57static const struct regmap_config veml6040_regmap_config = {58 .name = "veml6040_regmap",59 .reg_bits = 8,60 .val_bits = 16,61 .max_register = VEML6040_REG_W,62 .val_format_endian = REGMAP_ENDIAN_LITTLE,63};64 65static int veml6040_read_raw(struct iio_dev *indio_dev,66 struct iio_chan_spec const *chan, int *val,67 int *val2, long mask)68{69 int ret, reg, it_index;70 struct veml6040_data *data = iio_priv(indio_dev);71 struct regmap *regmap = data->regmap;72 struct device *dev = &data->client->dev;73 74 switch (mask) {75 case IIO_CHAN_INFO_RAW:76 ret = regmap_read(regmap, chan->address, ®);77 if (ret) {78 dev_err(dev, "Data read failed: %d\n", ret);79 return ret;80 }81 *val = reg;82 return IIO_VAL_INT;83 84 case IIO_CHAN_INFO_INT_TIME:85 ret = regmap_read(regmap, VEML6040_CONF_REG, ®);86 if (ret) {87 dev_err(dev, "Data read failed: %d\n", ret);88 return ret;89 }90 it_index = FIELD_GET(VEML6040_CONF_IT_MSK, reg);91 if (it_index >= ARRAY_SIZE(veml6040_it_ms)) {92 dev_err(dev, "Invalid Integration Time Set");93 return -EINVAL;94 }95 *val = veml6040_it_ms[it_index];96 return IIO_VAL_INT;97 98 default:99 return -EINVAL;100 }101}102 103static int veml6040_write_raw(struct iio_dev *indio_dev,104 struct iio_chan_spec const *chan, int val,105 int val2, long mask)106{107 struct veml6040_data *data = iio_priv(indio_dev);108 109 switch (mask) {110 case IIO_CHAN_INFO_INT_TIME:111 for (int i = 0; i < ARRAY_SIZE(veml6040_it_ms); i++) {112 if (veml6040_it_ms[i] != val)113 continue;114 115 return regmap_update_bits(data->regmap,116 VEML6040_CONF_REG,117 VEML6040_CONF_IT_MSK,118 FIELD_PREP(VEML6040_CONF_IT_MSK, i));119 }120 return -EINVAL;121 default:122 return -EINVAL;123 }124}125 126static int veml6040_read_avail(struct iio_dev *indio_dev,127 struct iio_chan_spec const *chan,128 const int **vals, int *type, int *length,129 long mask)130{131 switch (mask) {132 case IIO_CHAN_INFO_INT_TIME:133 *length = ARRAY_SIZE(veml6040_it_ms);134 *vals = veml6040_it_ms;135 *type = IIO_VAL_INT;136 return IIO_AVAIL_LIST;137 138 default:139 return -EINVAL;140 }141}142 143static const struct iio_info veml6040_info = {144 .read_raw = veml6040_read_raw,145 .write_raw = veml6040_write_raw,146 .read_avail = veml6040_read_avail,147};148 149static const struct iio_chan_spec veml6040_channels[] = {150 {151 .type = IIO_INTENSITY,152 .address = VEML6040_REG_R,153 .channel = CH_RED,154 .channel2 = IIO_MOD_LIGHT_RED,155 .modified = 1,156 .info_mask_separate = BIT(IIO_CHAN_INFO_RAW),157 .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_INT_TIME),158 .info_mask_shared_by_type_available =159 BIT(IIO_CHAN_INFO_INT_TIME),160 },161 {162 .type = IIO_INTENSITY,163 .address = VEML6040_REG_G,164 .channel = CH_GREEN,165 .channel2 = IIO_MOD_LIGHT_GREEN,166 .modified = 1,167 .info_mask_separate = BIT(IIO_CHAN_INFO_RAW),168 .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_INT_TIME),169 .info_mask_shared_by_type_available =170 BIT(IIO_CHAN_INFO_INT_TIME),171 },172 {173 .type = IIO_INTENSITY,174 .address = VEML6040_REG_B,175 .channel = CH_BLUE,176 .channel2 = IIO_MOD_LIGHT_BLUE,177 .modified = 1,178 .info_mask_separate = BIT(IIO_CHAN_INFO_RAW),179 .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_INT_TIME),180 .info_mask_shared_by_type_available =181 BIT(IIO_CHAN_INFO_INT_TIME),182 },183 {184 .type = IIO_INTENSITY,185 .address = VEML6040_REG_W,186 .channel = CH_WHITE,187 .channel2 = IIO_MOD_LIGHT_CLEAR,188 .modified = 1,189 .info_mask_separate = BIT(IIO_CHAN_INFO_RAW),190 .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_INT_TIME),191 .info_mask_shared_by_type_available =192 BIT(IIO_CHAN_INFO_INT_TIME),193 }194};195 196static void veml6040_shutdown_action(void *data)197{198 struct veml6040_data *veml6040_data = data;199 200 regmap_update_bits(veml6040_data->regmap, VEML6040_CONF_REG,201 VEML6040_CONF_SD_MSK, VEML6040_CONF_SD_MSK);202}203 204static int veml6040_probe(struct i2c_client *client)205{206 struct device *dev = &client->dev;207 struct veml6040_data *data;208 struct iio_dev *indio_dev;209 struct regmap *regmap;210 const int init_config =211 FIELD_PREP(VEML6040_CONF_IT_MSK, VEML6040_CONF_IT_40_MS) |212 FIELD_PREP(VEML6040_CONF_AF_MSK, 0) |213 FIELD_PREP(VEML6040_CONF_SD_MSK, 0);214 int ret;215 216 if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C))217 return dev_err_probe(dev, -EOPNOTSUPP,218 "I2C adapter doesn't support plain I2C\n");219 220 indio_dev = devm_iio_device_alloc(dev, sizeof(*data));221 if (!indio_dev)222 return dev_err_probe(dev, -ENOMEM,223 "IIO device allocation failed\n");224 225 regmap = devm_regmap_init_i2c(client, &veml6040_regmap_config);226 if (IS_ERR(regmap))227 return dev_err_probe(dev, PTR_ERR(regmap),228 "Regmap setup failed\n");229 230 data = iio_priv(indio_dev);231 i2c_set_clientdata(client, indio_dev);232 data->client = client;233 data->regmap = regmap;234 235 indio_dev->name = "veml6040";236 indio_dev->info = &veml6040_info;237 indio_dev->channels = veml6040_channels;238 indio_dev->num_channels = ARRAY_SIZE(veml6040_channels);239 indio_dev->modes = INDIO_DIRECT_MODE;240 241 ret = devm_regulator_get_enable(dev, "vdd");242 if (ret)243 return ret;244 245 ret = regmap_write(regmap, VEML6040_CONF_REG, init_config);246 if (ret)247 return dev_err_probe(dev, ret,248 "Could not set initial config\n");249 250 ret = devm_add_action_or_reset(dev, veml6040_shutdown_action, data);251 if (ret)252 return ret;253 254 return devm_iio_device_register(dev, indio_dev);255}256 257static const struct i2c_device_id veml6040_id_table[] = {258 {"veml6040"},259 {}260};261MODULE_DEVICE_TABLE(i2c, veml6040_id_table);262 263static const struct of_device_id veml6040_of_match[] = {264 {.compatible = "vishay,veml6040"},265 {}266};267MODULE_DEVICE_TABLE(of, veml6040_of_match);268 269static struct i2c_driver veml6040_driver = {270 .probe = veml6040_probe,271 .id_table = veml6040_id_table,272 .driver = {273 .name = "veml6040",274 .of_match_table = veml6040_of_match,275 },276};277module_i2c_driver(veml6040_driver);278 279MODULE_DESCRIPTION("veml6040 RGBW light sensor driver");280MODULE_AUTHOR("Arthur Becker <arthur.becker@sentec.com>");281MODULE_LICENSE("GPL");282