FILE
veml6040.c
File preview with syntax highlighting.
linux-6.19.14/drivers/iio/light/veml6040.c
1// SPDX-License-Identifier: GPL-2.0+ 2/* 3 * Vishay VEML6040 RGBW light sensor driver 4 * 5 * Copyright (C) 2024 Sentec AG 6 * 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 Registers 19 * 20 * SD: Shutdown 21 * AF: Auto / Force Mode (Auto Measurements On:0, Off:1) 22 * TR: Trigger Measurement (when AF Bit is set) 23 * IT: Integration Time 24 */ 25#define VEML6040_CONF_REG 0x000 26#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 0 31#define VEML6040_CONF_IT_80_MS 1 32#define VEML6040_CONF_IT_160_MS 2 33#define VEML6040_CONF_IT_320_MS 3 34#define VEML6040_CONF_IT_640_MS 4 35#define VEML6040_CONF_IT_1280_MS 5 36 37/* VEML6040 Read Only Registers */ 38#define VEML6040_REG_R 0x08 39#define VEML6040_REG_G 0x09 40#define VEML6040_REG_B 0x0A 41#define VEML6040_REG_W 0x0B 42 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 -ENOMEM; 223 224 regmap = devm_regmap_init_i2c(client, &veml6040_regmap_config); 225 if (IS_ERR(regmap)) 226 return dev_err_probe(dev, PTR_ERR(regmap), 227 "Regmap setup failed\n"); 228 229 data = iio_priv(indio_dev); 230 i2c_set_clientdata(client, indio_dev); 231 data->client = client; 232 data->regmap = regmap; 233 234 indio_dev->name = "veml6040"; 235 indio_dev->info = &veml6040_info; 236 indio_dev->channels = veml6040_channels; 237 indio_dev->num_channels = ARRAY_SIZE(veml6040_channels); 238 indio_dev->modes = INDIO_DIRECT_MODE; 239 240 ret = devm_regulator_get_enable(dev, "vdd"); 241 if (ret) 242 return ret; 243 244 ret = regmap_write(regmap, VEML6040_CONF_REG, init_config); 245 if (ret) 246 return dev_err_probe(dev, ret, 247 "Could not set initial config\n"); 248 249 ret = devm_add_action_or_reset(dev, veml6040_shutdown_action, data); 250 if (ret) 251 return ret; 252 253 return devm_iio_device_register(dev, indio_dev); 254} 255 256static const struct i2c_device_id veml6040_id_table[] = { 257 {"veml6040"}, 258 { } 259}; 260MODULE_DEVICE_TABLE(i2c, veml6040_id_table); 261 262static const struct of_device_id veml6040_of_match[] = { 263 {.compatible = "vishay,veml6040"}, 264 { } 265}; 266MODULE_DEVICE_TABLE(of, veml6040_of_match); 267 268static struct i2c_driver veml6040_driver = { 269 .probe = veml6040_probe, 270 .id_table = veml6040_id_table, 271 .driver = { 272 .name = "veml6040", 273 .of_match_table = veml6040_of_match, 274 }, 275}; 276module_i2c_driver(veml6040_driver); 277 278MODULE_DESCRIPTION("veml6040 RGBW light sensor driver"); 279MODULE_AUTHOR("Arthur Becker <arthur.becker@sentec.com>"); 280MODULE_LICENSE("GPL");
~/
Read-only file preview.