Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

iio: light: add support for veml3235

The Vishay veml3235 is a low-power ambient light sensor with I2C
interface. It provides a minimum detectable intensity of
0.0021 lx/cnt, configurable integration time and gain, and an additional
white channel to distinguish between different light sources.

Signed-off-by: Javier Carrasco <javier.carrasco.cruz@gmail.com>
Link: https://patch.msgid.link/20241023-veml3235-v3-2-8490f2622f9a@gmail.com
Signed-off-by: Jonathan Cameron <Jonathan.Cameron@huawei.com>

authored by

Javier Carrasco and committed by
Jonathan Cameron
c5a23f80 6df21ae0

+513
+6
MAINTAINERS
··· 24590 24590 F: drivers/input/serio/userio.c 24591 24591 F: include/uapi/linux/userio.h 24592 24592 24593 + VISHAY VEML3235 AMBIENT LIGHT SENSOR DRIVER 24594 + M: Javier Carrasco <javier.carrasco.cruz@gmail.com> 24595 + S: Maintained 24596 + F: Documentation/devicetree/bindings/iio/light/vishay,veml6030.yaml 24597 + F: drivers/iio/light/veml3235.c 24598 + 24593 24599 VISHAY VEML6030 AMBIENT LIGHT SENSOR DRIVER 24594 24600 M: Javier Carrasco <javier.carrasco.cruz@gmail.com> 24595 24601 S: Maintained
+11
drivers/iio/light/Kconfig
··· 669 669 To compile this driver as a module, choose M here: the 670 670 module will be called vcnl4035. 671 671 672 + config VEML3235 673 + tristate "VEML3235 ambient light sensor" 674 + select REGMAP_I2C 675 + depends on I2C 676 + help 677 + Say Y here if you want to build a driver for the Vishay VEML3235 678 + ambient light sensor. 679 + 680 + To compile this driver as a module, choose M here: the 681 + module will be called veml3235. 682 + 672 683 config VEML6030 673 684 tristate "VEML6030 and VEML6035 ambient light sensors" 674 685 select REGMAP_I2C
+1
drivers/iio/light/Makefile
··· 62 62 obj-$(CONFIG_US5182D) += us5182d.o 63 63 obj-$(CONFIG_VCNL4000) += vcnl4000.o 64 64 obj-$(CONFIG_VCNL4035) += vcnl4035.o 65 + obj-$(CONFIG_VEML3235) += veml3235.o 65 66 obj-$(CONFIG_VEML6030) += veml6030.o 66 67 obj-$(CONFIG_VEML6040) += veml6040.o 67 68 obj-$(CONFIG_VEML6070) += veml6070.o
+495
drivers/iio/light/veml3235.c
··· 1 + // SPDX-License-Identifier: GPL-2.0+ 2 + /* 3 + * VEML3235 Ambient Light Sensor 4 + * 5 + * Copyright (c) 2024, Javier Carrasco <javier.carrasco.cruz@gmail.com> 6 + * 7 + * Datasheet: https://www.vishay.com/docs/80131/veml3235.pdf 8 + * Appnote-80222: https://www.vishay.com/docs/80222/designingveml3235.pdf 9 + */ 10 + 11 + #include <linux/err.h> 12 + #include <linux/i2c.h> 13 + #include <linux/iio/iio.h> 14 + #include <linux/module.h> 15 + #include <linux/pm_runtime.h> 16 + #include <linux/regmap.h> 17 + #include <linux/regulator/consumer.h> 18 + 19 + #define VEML3235_REG_CONF 0x00 20 + #define VEML3235_REG_WH_DATA 0x04 21 + #define VEML3235_REG_ALS_DATA 0x05 22 + #define VEML3235_REG_ID 0x09 23 + 24 + #define VEML3235_CONF_SD BIT(0) 25 + #define VEML3235_CONF_SD0 BIT(15) 26 + 27 + struct veml3235_rf { 28 + struct regmap_field *it; 29 + struct regmap_field *gain; 30 + struct regmap_field *id; 31 + }; 32 + 33 + struct veml3235_data { 34 + struct i2c_client *client; 35 + struct device *dev; 36 + struct regmap *regmap; 37 + struct veml3235_rf rf; 38 + }; 39 + 40 + static const int veml3235_it_times[][2] = { 41 + { 0, 50000 }, 42 + { 0, 100000 }, 43 + { 0, 200000 }, 44 + { 0, 400000 }, 45 + { 0, 800000 }, 46 + }; 47 + 48 + static const int veml3235_scale_vals[] = { 1, 2, 4, 8 }; 49 + 50 + static int veml3235_power_on(struct veml3235_data *data) 51 + { 52 + int ret; 53 + 54 + ret = regmap_clear_bits(data->regmap, VEML3235_REG_CONF, 55 + VEML3235_CONF_SD | VEML3235_CONF_SD0); 56 + if (ret) 57 + return ret; 58 + 59 + /* Wait 4 ms to let processor & oscillator start correctly */ 60 + fsleep(4000); 61 + 62 + return 0; 63 + } 64 + 65 + static int veml3235_shut_down(struct veml3235_data *data) 66 + { 67 + return regmap_set_bits(data->regmap, VEML3235_REG_CONF, 68 + VEML3235_CONF_SD | VEML3235_CONF_SD0); 69 + } 70 + 71 + static void veml3235_shut_down_action(void *data) 72 + { 73 + veml3235_shut_down(data); 74 + } 75 + 76 + enum veml3235_chan { 77 + CH_ALS, 78 + CH_WHITE, 79 + }; 80 + 81 + static const struct iio_chan_spec veml3235_channels[] = { 82 + { 83 + .type = IIO_LIGHT, 84 + .channel = CH_ALS, 85 + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), 86 + .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_INT_TIME) | 87 + BIT(IIO_CHAN_INFO_SCALE), 88 + .info_mask_shared_by_all_available = BIT(IIO_CHAN_INFO_INT_TIME) | 89 + BIT(IIO_CHAN_INFO_SCALE), 90 + }, 91 + { 92 + .type = IIO_INTENSITY, 93 + .channel = CH_WHITE, 94 + .modified = 1, 95 + .channel2 = IIO_MOD_LIGHT_BOTH, 96 + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), 97 + .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_INT_TIME) | 98 + BIT(IIO_CHAN_INFO_SCALE), 99 + .info_mask_shared_by_all_available = BIT(IIO_CHAN_INFO_INT_TIME) | 100 + BIT(IIO_CHAN_INFO_SCALE), 101 + }, 102 + }; 103 + 104 + static const struct regmap_config veml3235_regmap_config = { 105 + .name = "veml3235_regmap", 106 + .reg_bits = 8, 107 + .val_bits = 16, 108 + .max_register = VEML3235_REG_ID, 109 + .val_format_endian = REGMAP_ENDIAN_LITTLE, 110 + }; 111 + 112 + static int veml3235_get_it(struct veml3235_data *data, int *val, int *val2) 113 + { 114 + int ret, reg; 115 + 116 + ret = regmap_field_read(data->rf.it, &reg); 117 + if (ret) 118 + return ret; 119 + 120 + switch (reg) { 121 + case 0: 122 + *val2 = 50000; 123 + break; 124 + case 1: 125 + *val2 = 100000; 126 + break; 127 + case 2: 128 + *val2 = 200000; 129 + break; 130 + case 3: 131 + *val2 = 400000; 132 + break; 133 + case 4: 134 + *val2 = 800000; 135 + break; 136 + default: 137 + return -EINVAL; 138 + } 139 + 140 + *val = 0; 141 + 142 + return IIO_VAL_INT_PLUS_MICRO; 143 + } 144 + 145 + static int veml3235_set_it(struct iio_dev *indio_dev, int val, int val2) 146 + { 147 + struct veml3235_data *data = iio_priv(indio_dev); 148 + int ret, new_it; 149 + 150 + if (val) 151 + return -EINVAL; 152 + 153 + switch (val2) { 154 + case 50000: 155 + new_it = 0x00; 156 + break; 157 + case 100000: 158 + new_it = 0x01; 159 + break; 160 + case 200000: 161 + new_it = 0x02; 162 + break; 163 + case 400000: 164 + new_it = 0x03; 165 + break; 166 + case 800000: 167 + new_it = 0x04; 168 + break; 169 + default: 170 + return -EINVAL; 171 + } 172 + 173 + ret = regmap_field_write(data->rf.it, new_it); 174 + if (ret) { 175 + dev_err(data->dev, 176 + "failed to update integration time: %d\n", ret); 177 + return ret; 178 + } 179 + 180 + return 0; 181 + } 182 + 183 + static int veml3235_set_gain(struct iio_dev *indio_dev, int val, int val2) 184 + { 185 + struct veml3235_data *data = iio_priv(indio_dev); 186 + int ret, new_gain; 187 + 188 + if (val2 != 0) 189 + return -EINVAL; 190 + 191 + switch (val) { 192 + case 1: 193 + new_gain = 0x00; 194 + break; 195 + case 2: 196 + new_gain = 0x01; 197 + break; 198 + case 4: 199 + new_gain = 0x03; 200 + break; 201 + case 8: 202 + new_gain = 0x07; 203 + break; 204 + default: 205 + return -EINVAL; 206 + } 207 + 208 + ret = regmap_field_write(data->rf.gain, new_gain); 209 + if (ret) { 210 + dev_err(data->dev, "failed to set gain: %d\n", ret); 211 + return ret; 212 + } 213 + 214 + return 0; 215 + } 216 + 217 + static int veml3235_get_gain(struct veml3235_data *data, int *val) 218 + { 219 + int ret, reg; 220 + 221 + ret = regmap_field_read(data->rf.gain, &reg); 222 + if (ret) { 223 + dev_err(data->dev, "failed to read gain %d\n", ret); 224 + return ret; 225 + } 226 + 227 + switch (reg & 0x03) { 228 + case 0: 229 + *val = 1; 230 + break; 231 + case 1: 232 + *val = 2; 233 + break; 234 + case 3: 235 + *val = 4; 236 + break; 237 + default: 238 + return -EINVAL; 239 + } 240 + 241 + /* Double gain */ 242 + if (reg & 0x04) 243 + *val *= 2; 244 + 245 + return IIO_VAL_INT; 246 + } 247 + 248 + static int veml3235_read_raw(struct iio_dev *indio_dev, 249 + struct iio_chan_spec const *chan, int *val, 250 + int *val2, long mask) 251 + { 252 + struct veml3235_data *data = iio_priv(indio_dev); 253 + struct regmap *regmap = data->regmap; 254 + int ret, reg; 255 + 256 + switch (mask) { 257 + case IIO_CHAN_INFO_RAW: 258 + switch (chan->type) { 259 + case IIO_LIGHT: 260 + ret = regmap_read(regmap, VEML3235_REG_ALS_DATA, &reg); 261 + if (ret < 0) 262 + return ret; 263 + 264 + *val = reg; 265 + return IIO_VAL_INT; 266 + case IIO_INTENSITY: 267 + ret = regmap_read(regmap, VEML3235_REG_WH_DATA, &reg); 268 + if (ret < 0) 269 + return ret; 270 + 271 + *val = reg; 272 + return IIO_VAL_INT; 273 + default: 274 + return -EINVAL; 275 + } 276 + case IIO_CHAN_INFO_INT_TIME: 277 + return veml3235_get_it(data, val, val2); 278 + case IIO_CHAN_INFO_SCALE: 279 + return veml3235_get_gain(data, val); 280 + default: 281 + return -EINVAL; 282 + } 283 + } 284 + 285 + static int veml3235_read_avail(struct iio_dev *indio_dev, 286 + struct iio_chan_spec const *chan, 287 + const int **vals, int *type, int *length, 288 + long mask) 289 + { 290 + switch (mask) { 291 + case IIO_CHAN_INFO_INT_TIME: 292 + *vals = (int *)&veml3235_it_times; 293 + *length = 2 * ARRAY_SIZE(veml3235_it_times); 294 + *type = IIO_VAL_INT_PLUS_MICRO; 295 + return IIO_AVAIL_LIST; 296 + case IIO_CHAN_INFO_SCALE: 297 + *vals = (int *)&veml3235_scale_vals; 298 + *length = ARRAY_SIZE(veml3235_scale_vals); 299 + *type = IIO_VAL_INT; 300 + return IIO_AVAIL_LIST; 301 + default: 302 + return -EINVAL; 303 + } 304 + } 305 + 306 + static int veml3235_write_raw(struct iio_dev *indio_dev, 307 + struct iio_chan_spec const *chan, 308 + int val, int val2, long mask) 309 + { 310 + switch (mask) { 311 + case IIO_CHAN_INFO_INT_TIME: 312 + return veml3235_set_it(indio_dev, val, val2); 313 + case IIO_CHAN_INFO_SCALE: 314 + return veml3235_set_gain(indio_dev, val, val2); 315 + } 316 + 317 + return -EINVAL; 318 + } 319 + 320 + static void veml3235_read_id(struct veml3235_data *data) 321 + { 322 + int ret, reg; 323 + 324 + ret = regmap_field_read(data->rf.id, &reg); 325 + if (ret) { 326 + dev_info(data->dev, "failed to read ID\n"); 327 + return; 328 + } 329 + 330 + if (reg != 0x35) 331 + dev_info(data->dev, "Unknown ID %d\n", reg); 332 + } 333 + 334 + static const struct reg_field veml3235_rf_it = 335 + REG_FIELD(VEML3235_REG_CONF, 4, 6); 336 + 337 + static const struct reg_field veml3235_rf_gain = 338 + REG_FIELD(VEML3235_REG_CONF, 11, 13); 339 + 340 + static const struct reg_field veml3235_rf_id = 341 + REG_FIELD(VEML3235_REG_ID, 0, 7); 342 + 343 + static int veml3235_regfield_init(struct veml3235_data *data) 344 + { 345 + struct regmap *regmap = data->regmap; 346 + struct device *dev = data->dev; 347 + struct regmap_field *rm_field; 348 + struct veml3235_rf *rf = &data->rf; 349 + 350 + rm_field = devm_regmap_field_alloc(dev, regmap, veml3235_rf_it); 351 + if (IS_ERR(rm_field)) 352 + return PTR_ERR(rm_field); 353 + rf->it = rm_field; 354 + 355 + rm_field = devm_regmap_field_alloc(dev, regmap, veml3235_rf_gain); 356 + if (IS_ERR(rm_field)) 357 + return PTR_ERR(rm_field); 358 + rf->gain = rm_field; 359 + 360 + rm_field = devm_regmap_field_alloc(dev, regmap, veml3235_rf_id); 361 + if (IS_ERR(rm_field)) 362 + return PTR_ERR(rm_field); 363 + rf->id = rm_field; 364 + 365 + return 0; 366 + } 367 + 368 + static int veml3235_hw_init(struct iio_dev *indio_dev) 369 + { 370 + struct veml3235_data *data = iio_priv(indio_dev); 371 + struct device *dev = data->dev; 372 + int ret; 373 + 374 + /* Set gain to 1 and integration time to 100 ms */ 375 + ret = regmap_field_write(data->rf.gain, 0x00); 376 + if (ret) 377 + return dev_err_probe(data->dev, ret, "failed to set gain\n"); 378 + 379 + ret = regmap_field_write(data->rf.it, 0x01); 380 + if (ret) 381 + return dev_err_probe(data->dev, ret, 382 + "failed to set integration time\n"); 383 + 384 + ret = veml3235_power_on(data); 385 + if (ret) 386 + return dev_err_probe(dev, ret, "failed to power on\n"); 387 + 388 + return devm_add_action_or_reset(dev, veml3235_shut_down_action, data); 389 + } 390 + 391 + static const struct iio_info veml3235_info = { 392 + .read_raw = veml3235_read_raw, 393 + .read_avail = veml3235_read_avail, 394 + .write_raw = veml3235_write_raw, 395 + }; 396 + 397 + static int veml3235_probe(struct i2c_client *client) 398 + { 399 + struct device *dev = &client->dev; 400 + struct veml3235_data *data; 401 + struct iio_dev *indio_dev; 402 + struct regmap *regmap; 403 + int ret; 404 + 405 + regmap = devm_regmap_init_i2c(client, &veml3235_regmap_config); 406 + if (IS_ERR(regmap)) 407 + return dev_err_probe(dev, PTR_ERR(regmap), 408 + "failed to setup regmap\n"); 409 + 410 + indio_dev = devm_iio_device_alloc(dev, sizeof(*data)); 411 + if (!indio_dev) 412 + return -ENOMEM; 413 + 414 + data = iio_priv(indio_dev); 415 + i2c_set_clientdata(client, indio_dev); 416 + data->client = client; 417 + data->dev = dev; 418 + data->regmap = regmap; 419 + 420 + ret = veml3235_regfield_init(data); 421 + if (ret) 422 + return dev_err_probe(dev, ret, "failed to init regfield\n"); 423 + 424 + ret = devm_regulator_get_enable(dev, "vdd"); 425 + if (ret) 426 + return dev_err_probe(dev, ret, "failed to enable regulator\n"); 427 + 428 + indio_dev->name = "veml3235"; 429 + indio_dev->channels = veml3235_channels; 430 + indio_dev->num_channels = ARRAY_SIZE(veml3235_channels); 431 + indio_dev->modes = INDIO_DIRECT_MODE; 432 + indio_dev->info = &veml3235_info; 433 + 434 + veml3235_read_id(data); 435 + 436 + ret = veml3235_hw_init(indio_dev); 437 + if (ret < 0) 438 + return ret; 439 + 440 + return devm_iio_device_register(dev, indio_dev); 441 + } 442 + 443 + static int veml3235_runtime_suspend(struct device *dev) 444 + { 445 + struct veml3235_data *data = iio_priv(dev_get_drvdata(dev)); 446 + int ret; 447 + 448 + ret = veml3235_shut_down(data); 449 + if (ret < 0) 450 + dev_err(data->dev, "failed to suspend: %d\n", ret); 451 + 452 + return ret; 453 + } 454 + 455 + static int veml3235_runtime_resume(struct device *dev) 456 + { 457 + struct veml3235_data *data = iio_priv(dev_get_drvdata(dev)); 458 + int ret; 459 + 460 + ret = veml3235_power_on(data); 461 + if (ret < 0) 462 + dev_err(data->dev, "failed to resume: %d\n", ret); 463 + 464 + return ret; 465 + } 466 + 467 + static DEFINE_RUNTIME_DEV_PM_OPS(veml3235_pm_ops, veml3235_runtime_suspend, 468 + veml3235_runtime_resume, NULL); 469 + 470 + static const struct of_device_id veml3235_of_match[] = { 471 + { .compatible = "vishay,veml3235" }, 472 + { } 473 + }; 474 + MODULE_DEVICE_TABLE(of, veml3235_of_match); 475 + 476 + static const struct i2c_device_id veml3235_id[] = { 477 + { "veml3235" }, 478 + { } 479 + }; 480 + MODULE_DEVICE_TABLE(i2c, veml3235_id); 481 + 482 + static struct i2c_driver veml3235_driver = { 483 + .driver = { 484 + .name = "veml3235", 485 + .of_match_table = veml3235_of_match, 486 + .pm = pm_ptr(&veml3235_pm_ops), 487 + }, 488 + .probe = veml3235_probe, 489 + .id_table = veml3235_id, 490 + }; 491 + module_i2c_driver(veml3235_driver); 492 + 493 + MODULE_AUTHOR("Javier Carrasco <javier.carrasco.cruz@gmail.com>"); 494 + MODULE_DESCRIPTION("VEML3235 Ambient Light Sensor"); 495 + MODULE_LICENSE("GPL");