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.

at v6.11-rc2 474 lines 12 kB view raw
1// SPDX-License-Identifier: GPL-2.0-only 2/* 3 * Vishay VEML6075 UVA and UVB light sensor 4 * 5 * Copyright 2023 Javier Carrasco <javier.carrasco.cruz@gmail.com> 6 * 7 * 7-bit I2C slave, address 0x10 8 */ 9 10#include <linux/bitfield.h> 11#include <linux/delay.h> 12#include <linux/err.h> 13#include <linux/i2c.h> 14#include <linux/module.h> 15#include <linux/mutex.h> 16#include <linux/regmap.h> 17#include <linux/units.h> 18 19#include <linux/iio/iio.h> 20 21#define VEML6075_CMD_CONF 0x00 /* configuration register */ 22#define VEML6075_CMD_UVA 0x07 /* UVA channel */ 23#define VEML6075_CMD_UVB 0x09 /* UVB channel */ 24#define VEML6075_CMD_COMP1 0x0A /* visible light compensation */ 25#define VEML6075_CMD_COMP2 0x0B /* infrarred light compensation */ 26#define VEML6075_CMD_ID 0x0C /* device ID */ 27 28#define VEML6075_CONF_IT GENMASK(6, 4) /* intregration time */ 29#define VEML6075_CONF_HD BIT(3) /* dynamic setting */ 30#define VEML6075_CONF_TRIG BIT(2) /* trigger */ 31#define VEML6075_CONF_AF BIT(1) /* active force enable */ 32#define VEML6075_CONF_SD BIT(0) /* shutdown */ 33 34#define VEML6075_IT_50_MS 0x00 35#define VEML6075_IT_100_MS 0x01 36#define VEML6075_IT_200_MS 0x02 37#define VEML6075_IT_400_MS 0x03 38#define VEML6075_IT_800_MS 0x04 39 40#define VEML6075_AF_DISABLE 0x00 41#define VEML6075_AF_ENABLE 0x01 42 43#define VEML6075_SD_DISABLE 0x00 44#define VEML6075_SD_ENABLE 0x01 45 46/* Open-air coefficients and responsivity */ 47#define VEML6075_A_COEF 2220 48#define VEML6075_B_COEF 1330 49#define VEML6075_C_COEF 2950 50#define VEML6075_D_COEF 1740 51#define VEML6075_UVA_RESP 1461 52#define VEML6075_UVB_RESP 2591 53 54static const int veml6075_it_ms[] = { 50, 100, 200, 400, 800 }; 55 56struct veml6075_data { 57 struct i2c_client *client; 58 struct regmap *regmap; 59 /* 60 * prevent integration time modification and triggering 61 * measurements while a measurement is underway. 62 */ 63 struct mutex lock; 64}; 65 66/* channel number */ 67enum veml6075_chan { 68 CH_UVA, 69 CH_UVB, 70}; 71 72static const struct iio_chan_spec veml6075_channels[] = { 73 { 74 .type = IIO_INTENSITY, 75 .channel = CH_UVA, 76 .modified = 1, 77 .channel2 = IIO_MOD_LIGHT_UVA, 78 .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | 79 BIT(IIO_CHAN_INFO_SCALE), 80 .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_INT_TIME), 81 .info_mask_shared_by_all_available = BIT(IIO_CHAN_INFO_INT_TIME), 82 }, 83 { 84 .type = IIO_INTENSITY, 85 .channel = CH_UVB, 86 .modified = 1, 87 .channel2 = IIO_MOD_LIGHT_UVB, 88 .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | 89 BIT(IIO_CHAN_INFO_SCALE), 90 .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_INT_TIME), 91 .info_mask_shared_by_all_available = BIT(IIO_CHAN_INFO_INT_TIME), 92 }, 93 { 94 .type = IIO_UVINDEX, 95 .info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED), 96 .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_INT_TIME), 97 .info_mask_shared_by_all_available = BIT(IIO_CHAN_INFO_INT_TIME), 98 }, 99}; 100 101static int veml6075_request_measurement(struct veml6075_data *data) 102{ 103 int ret, conf, int_time; 104 105 ret = regmap_read(data->regmap, VEML6075_CMD_CONF, &conf); 106 if (ret < 0) 107 return ret; 108 109 /* disable shutdown and trigger measurement */ 110 ret = regmap_write(data->regmap, VEML6075_CMD_CONF, 111 (conf | VEML6075_CONF_TRIG) & ~VEML6075_CONF_SD); 112 if (ret < 0) 113 return ret; 114 115 /* 116 * A measurement requires between 1.30 and 1.40 times the integration 117 * time for all possible configurations. Using a 1.50 factor simplifies 118 * operations and ensures reliability under all circumstances. 119 */ 120 int_time = veml6075_it_ms[FIELD_GET(VEML6075_CONF_IT, conf)]; 121 msleep(int_time + (int_time / 2)); 122 123 /* shutdown again, data registers are still accessible */ 124 return regmap_update_bits(data->regmap, VEML6075_CMD_CONF, 125 VEML6075_CONF_SD, VEML6075_CONF_SD); 126} 127 128static int veml6075_uva_comp(int raw_uva, int comp1, int comp2) 129{ 130 int comp1a_c, comp2a_c, uva_comp; 131 132 comp1a_c = (comp1 * VEML6075_A_COEF) / 1000U; 133 comp2a_c = (comp2 * VEML6075_B_COEF) / 1000U; 134 uva_comp = raw_uva - comp1a_c - comp2a_c; 135 136 return clamp_val(uva_comp, 0, U16_MAX); 137} 138 139static int veml6075_uvb_comp(int raw_uvb, int comp1, int comp2) 140{ 141 int comp1b_c, comp2b_c, uvb_comp; 142 143 comp1b_c = (comp1 * VEML6075_C_COEF) / 1000U; 144 comp2b_c = (comp2 * VEML6075_D_COEF) / 1000U; 145 uvb_comp = raw_uvb - comp1b_c - comp2b_c; 146 147 return clamp_val(uvb_comp, 0, U16_MAX); 148} 149 150static int veml6075_read_comp(struct veml6075_data *data, int *c1, int *c2) 151{ 152 int ret; 153 154 ret = regmap_read(data->regmap, VEML6075_CMD_COMP1, c1); 155 if (ret < 0) 156 return ret; 157 158 return regmap_read(data->regmap, VEML6075_CMD_COMP2, c2); 159} 160 161static int veml6075_read_uv_direct(struct veml6075_data *data, int chan, 162 int *val) 163{ 164 int c1, c2, ret; 165 166 guard(mutex)(&data->lock); 167 168 ret = veml6075_request_measurement(data); 169 if (ret < 0) 170 return ret; 171 172 ret = veml6075_read_comp(data, &c1, &c2); 173 if (ret < 0) 174 return ret; 175 176 switch (chan) { 177 case CH_UVA: 178 ret = regmap_read(data->regmap, VEML6075_CMD_UVA, val); 179 if (ret < 0) 180 return ret; 181 182 *val = veml6075_uva_comp(*val, c1, c2); 183 return IIO_VAL_INT; 184 case CH_UVB: 185 ret = regmap_read(data->regmap, VEML6075_CMD_UVB, val); 186 if (ret < 0) 187 return ret; 188 189 *val = veml6075_uvb_comp(*val, c1, c2); 190 return IIO_VAL_INT; 191 default: 192 return -EINVAL; 193 } 194} 195 196static int veml6075_read_int_time_index(struct veml6075_data *data) 197{ 198 int ret, conf; 199 200 ret = regmap_read(data->regmap, VEML6075_CMD_CONF, &conf); 201 if (ret < 0) 202 return ret; 203 204 return FIELD_GET(VEML6075_CONF_IT, conf); 205} 206 207static int veml6075_read_int_time_ms(struct veml6075_data *data, int *val) 208{ 209 int int_index; 210 211 guard(mutex)(&data->lock); 212 int_index = veml6075_read_int_time_index(data); 213 if (int_index < 0) 214 return int_index; 215 216 *val = veml6075_it_ms[int_index]; 217 218 return IIO_VAL_INT; 219} 220 221static int veml6075_get_uvi_micro(struct veml6075_data *data, int uva_comp, 222 int uvb_comp) 223{ 224 int uvia_micro = uva_comp * VEML6075_UVA_RESP; 225 int uvib_micro = uvb_comp * VEML6075_UVB_RESP; 226 int int_index; 227 228 int_index = veml6075_read_int_time_index(data); 229 if (int_index < 0) 230 return int_index; 231 232 switch (int_index) { 233 case VEML6075_IT_50_MS: 234 return uvia_micro + uvib_micro; 235 case VEML6075_IT_100_MS: 236 case VEML6075_IT_200_MS: 237 case VEML6075_IT_400_MS: 238 case VEML6075_IT_800_MS: 239 return (uvia_micro + uvib_micro) / (2 << int_index); 240 default: 241 return -EINVAL; 242 } 243} 244 245static int veml6075_read_uvi(struct veml6075_data *data, int *val, int *val2) 246{ 247 int ret, c1, c2, uva, uvb, uvi_micro; 248 249 guard(mutex)(&data->lock); 250 251 ret = veml6075_request_measurement(data); 252 if (ret < 0) 253 return ret; 254 255 ret = veml6075_read_comp(data, &c1, &c2); 256 if (ret < 0) 257 return ret; 258 259 ret = regmap_read(data->regmap, VEML6075_CMD_UVA, &uva); 260 if (ret < 0) 261 return ret; 262 263 ret = regmap_read(data->regmap, VEML6075_CMD_UVB, &uvb); 264 if (ret < 0) 265 return ret; 266 267 uvi_micro = veml6075_get_uvi_micro(data, veml6075_uva_comp(uva, c1, c2), 268 veml6075_uvb_comp(uvb, c1, c2)); 269 if (uvi_micro < 0) 270 return uvi_micro; 271 272 *val = uvi_micro / MICRO; 273 *val2 = uvi_micro % MICRO; 274 275 return IIO_VAL_INT_PLUS_MICRO; 276} 277 278static int veml6075_read_responsivity(int chan, int *val, int *val2) 279{ 280 /* scale = 1 / resp */ 281 switch (chan) { 282 case CH_UVA: 283 /* resp = 0.93 c/uW/cm2: scale = 1.75268817 */ 284 *val = 1; 285 *val2 = 75268817; 286 return IIO_VAL_INT_PLUS_NANO; 287 case CH_UVB: 288 /* resp = 2.1 c/uW/cm2: scale = 0.476190476 */ 289 *val = 0; 290 *val2 = 476190476; 291 return IIO_VAL_INT_PLUS_NANO; 292 default: 293 return -EINVAL; 294 } 295} 296 297static int veml6075_read_avail(struct iio_dev *indio_dev, 298 struct iio_chan_spec const *chan, 299 const int **vals, int *type, int *length, 300 long mask) 301{ 302 switch (mask) { 303 case IIO_CHAN_INFO_INT_TIME: 304 *length = ARRAY_SIZE(veml6075_it_ms); 305 *vals = veml6075_it_ms; 306 *type = IIO_VAL_INT; 307 return IIO_AVAIL_LIST; 308 309 default: 310 return -EINVAL; 311 } 312} 313 314static int veml6075_read_raw(struct iio_dev *indio_dev, 315 struct iio_chan_spec const *chan, 316 int *val, int *val2, long mask) 317{ 318 struct veml6075_data *data = iio_priv(indio_dev); 319 320 switch (mask) { 321 case IIO_CHAN_INFO_RAW: 322 return veml6075_read_uv_direct(data, chan->channel, val); 323 case IIO_CHAN_INFO_PROCESSED: 324 return veml6075_read_uvi(data, val, val2); 325 case IIO_CHAN_INFO_INT_TIME: 326 return veml6075_read_int_time_ms(data, val); 327 case IIO_CHAN_INFO_SCALE: 328 return veml6075_read_responsivity(chan->channel, val, val2); 329 default: 330 return -EINVAL; 331 } 332} 333 334static int veml6075_write_int_time_ms(struct veml6075_data *data, int val) 335{ 336 int i = ARRAY_SIZE(veml6075_it_ms); 337 338 guard(mutex)(&data->lock); 339 340 while (i-- > 0) { 341 if (val == veml6075_it_ms[i]) 342 break; 343 } 344 if (i < 0) 345 return -EINVAL; 346 347 return regmap_update_bits(data->regmap, VEML6075_CMD_CONF, 348 VEML6075_CONF_IT, 349 FIELD_PREP(VEML6075_CONF_IT, i)); 350} 351 352static int veml6075_write_raw(struct iio_dev *indio_dev, 353 struct iio_chan_spec const *chan, 354 int val, int val2, long mask) 355{ 356 struct veml6075_data *data = iio_priv(indio_dev); 357 358 switch (mask) { 359 case IIO_CHAN_INFO_INT_TIME: 360 return veml6075_write_int_time_ms(data, val); 361 default: 362 return -EINVAL; 363 } 364} 365 366static const struct iio_info veml6075_info = { 367 .read_avail = veml6075_read_avail, 368 .read_raw = veml6075_read_raw, 369 .write_raw = veml6075_write_raw, 370}; 371 372static bool veml6075_readable_reg(struct device *dev, unsigned int reg) 373{ 374 switch (reg) { 375 case VEML6075_CMD_CONF: 376 case VEML6075_CMD_UVA: 377 case VEML6075_CMD_UVB: 378 case VEML6075_CMD_COMP1: 379 case VEML6075_CMD_COMP2: 380 case VEML6075_CMD_ID: 381 return true; 382 default: 383 return false; 384 } 385} 386 387static bool veml6075_writable_reg(struct device *dev, unsigned int reg) 388{ 389 switch (reg) { 390 case VEML6075_CMD_CONF: 391 return true; 392 default: 393 return false; 394 } 395} 396 397static const struct regmap_config veml6075_regmap_config = { 398 .name = "veml6075", 399 .reg_bits = 8, 400 .val_bits = 16, 401 .max_register = VEML6075_CMD_ID, 402 .readable_reg = veml6075_readable_reg, 403 .writeable_reg = veml6075_writable_reg, 404 .val_format_endian = REGMAP_ENDIAN_LITTLE, 405}; 406 407static int veml6075_probe(struct i2c_client *client) 408{ 409 struct veml6075_data *data; 410 struct iio_dev *indio_dev; 411 struct regmap *regmap; 412 int config, ret; 413 414 indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data)); 415 if (!indio_dev) 416 return -ENOMEM; 417 418 regmap = devm_regmap_init_i2c(client, &veml6075_regmap_config); 419 if (IS_ERR(regmap)) 420 return PTR_ERR(regmap); 421 422 data = iio_priv(indio_dev); 423 data->client = client; 424 data->regmap = regmap; 425 426 mutex_init(&data->lock); 427 428 indio_dev->name = "veml6075"; 429 indio_dev->info = &veml6075_info; 430 indio_dev->channels = veml6075_channels; 431 indio_dev->num_channels = ARRAY_SIZE(veml6075_channels); 432 indio_dev->modes = INDIO_DIRECT_MODE; 433 434 ret = devm_regulator_get_enable(&client->dev, "vdd"); 435 if (ret < 0) 436 return ret; 437 438 /* default: 100ms integration time, active force enable, shutdown */ 439 config = FIELD_PREP(VEML6075_CONF_IT, VEML6075_IT_100_MS) | 440 FIELD_PREP(VEML6075_CONF_AF, VEML6075_AF_ENABLE) | 441 FIELD_PREP(VEML6075_CONF_SD, VEML6075_SD_ENABLE); 442 ret = regmap_write(data->regmap, VEML6075_CMD_CONF, config); 443 if (ret < 0) 444 return ret; 445 446 return devm_iio_device_register(&client->dev, indio_dev); 447} 448 449static const struct i2c_device_id veml6075_id[] = { 450 { "veml6075" }, 451 { } 452}; 453MODULE_DEVICE_TABLE(i2c, veml6075_id); 454 455static const struct of_device_id veml6075_of_match[] = { 456 { .compatible = "vishay,veml6075" }, 457 {} 458}; 459MODULE_DEVICE_TABLE(of, veml6075_of_match); 460 461static struct i2c_driver veml6075_driver = { 462 .driver = { 463 .name = "veml6075", 464 .of_match_table = veml6075_of_match, 465 }, 466 .probe = veml6075_probe, 467 .id_table = veml6075_id, 468}; 469 470module_i2c_driver(veml6075_driver); 471 472MODULE_AUTHOR("Javier Carrasco <javier.carrasco.cruz@gmail.com>"); 473MODULE_DESCRIPTION("Vishay VEML6075 UVA and UVB light sensor driver"); 474MODULE_LICENSE("GPL");