vcnl3020.c (15819B)
1// SPDX-License-Identifier: GPL-2.0-only 2/* 3 * Support for Vishay VCNL3020 proximity sensor on i2c bus. 4 * Based on Vishay VCNL4000 driver code. 5 */ 6 7#include <linux/module.h> 8#include <linux/i2c.h> 9#include <linux/err.h> 10#include <linux/delay.h> 11#include <linux/regmap.h> 12#include <linux/interrupt.h> 13 14#include <linux/iio/iio.h> 15#include <linux/iio/events.h> 16 17#define VCNL3020_PROD_ID 0x21 18 19#define VCNL_COMMAND 0x80 /* Command register */ 20#define VCNL_PROD_REV 0x81 /* Product ID and Revision ID */ 21#define VCNL_PROXIMITY_RATE 0x82 /* Rate of Proximity Measurement */ 22#define VCNL_LED_CURRENT 0x83 /* IR LED current for proximity mode */ 23#define VCNL_PS_RESULT_HI 0x87 /* Proximity result register, MSB */ 24#define VCNL_PS_RESULT_LO 0x88 /* Proximity result register, LSB */ 25#define VCNL_PS_ICR 0x89 /* Interrupt Control Register */ 26#define VCNL_PS_LO_THR_HI 0x8a /* High byte of low threshold value */ 27#define VCNL_PS_LO_THR_LO 0x8b /* Low byte of low threshold value */ 28#define VCNL_PS_HI_THR_HI 0x8c /* High byte of high threshold value */ 29#define VCNL_PS_HI_THR_LO 0x8d /* Low byte of high threshold value */ 30#define VCNL_ISR 0x8e /* Interrupt Status Register */ 31#define VCNL_PS_MOD_ADJ 0x8f /* Proximity Modulator Timing Adjustment */ 32 33/* Bit masks for COMMAND register */ 34#define VCNL_PS_RDY BIT(5) /* proximity data ready? */ 35#define VCNL_PS_OD BIT(3) /* start on-demand proximity 36 * measurement 37 */ 38 39/* Enables periodic proximity measurement */ 40#define VCNL_PS_EN BIT(1) 41 42/* Enables state machine and LP oscillator for self timed measurements */ 43#define VCNL_PS_SELFTIMED_EN BIT(0) 44 45/* Bit masks for ICR */ 46 47/* Enable interrupts on low or high thresholds */ 48#define VCNL_ICR_THRES_EN BIT(1) 49 50/* Bit masks for ISR */ 51#define VCNL_INT_TH_HI BIT(0) /* High threshold hit */ 52#define VCNL_INT_TH_LOW BIT(1) /* Low threshold hit */ 53 54#define VCNL_ON_DEMAND_TIMEOUT_US 100000 55#define VCNL_POLL_US 20000 56 57static const int vcnl3020_prox_sampling_frequency[][2] = { 58 {1, 950000}, 59 {3, 906250}, 60 {7, 812500}, 61 {16, 625000}, 62 {31, 250000}, 63 {62, 500000}, 64 {125, 0}, 65 {250, 0}, 66}; 67 68/** 69 * struct vcnl3020_data - vcnl3020 specific data. 70 * @regmap: device register map. 71 * @dev: vcnl3020 device. 72 * @rev: revision id. 73 * @lock: lock for protecting access to device hardware registers. 74 * @buf: DMA safe __be16 buffer. 75 */ 76struct vcnl3020_data { 77 struct regmap *regmap; 78 struct device *dev; 79 u8 rev; 80 struct mutex lock; 81 __be16 buf ____cacheline_aligned; 82}; 83 84/** 85 * struct vcnl3020_property - vcnl3020 property. 86 * @name: property name. 87 * @reg: i2c register offset. 88 * @conversion_func: conversion function. 89 */ 90struct vcnl3020_property { 91 const char *name; 92 u32 reg; 93 u32 (*conversion_func)(u32 *val); 94}; 95 96static u32 microamp_to_reg(u32 *val) 97{ 98 /* 99 * An example of conversion from uA to reg val: 100 * 200000 uA == 200 mA == 20 101 */ 102 return *val /= 10000; 103}; 104 105static struct vcnl3020_property vcnl3020_led_current_property = { 106 .name = "vishay,led-current-microamp", 107 .reg = VCNL_LED_CURRENT, 108 .conversion_func = microamp_to_reg, 109}; 110 111static int vcnl3020_get_and_apply_property(struct vcnl3020_data *data, 112 struct vcnl3020_property prop) 113{ 114 int rc; 115 u32 val; 116 117 rc = device_property_read_u32(data->dev, prop.name, &val); 118 if (rc) 119 return 0; 120 121 if (prop.conversion_func) 122 prop.conversion_func(&val); 123 124 rc = regmap_write(data->regmap, prop.reg, val); 125 if (rc) { 126 dev_err(data->dev, "Error (%d) setting property (%s)\n", 127 rc, prop.name); 128 } 129 130 return rc; 131} 132 133static int vcnl3020_init(struct vcnl3020_data *data) 134{ 135 int rc; 136 unsigned int reg; 137 138 rc = regmap_read(data->regmap, VCNL_PROD_REV, ®); 139 if (rc) { 140 dev_err(data->dev, 141 "Error (%d) reading product revision\n", rc); 142 return rc; 143 } 144 145 if (reg != VCNL3020_PROD_ID) { 146 dev_err(data->dev, 147 "Product id (%x) did not match vcnl3020 (%x)\n", reg, 148 VCNL3020_PROD_ID); 149 return -ENODEV; 150 } 151 152 data->rev = reg; 153 mutex_init(&data->lock); 154 155 return vcnl3020_get_and_apply_property(data, 156 vcnl3020_led_current_property); 157}; 158 159static bool vcnl3020_is_in_periodic_mode(struct vcnl3020_data *data) 160{ 161 int rc; 162 unsigned int cmd; 163 164 rc = regmap_read(data->regmap, VCNL_COMMAND, &cmd); 165 if (rc) { 166 dev_err(data->dev, 167 "Error (%d) reading command register\n", rc); 168 return false; 169 } 170 171 return !!(cmd & VCNL_PS_SELFTIMED_EN); 172} 173 174static int vcnl3020_measure_proximity(struct vcnl3020_data *data, int *val) 175{ 176 int rc; 177 unsigned int reg; 178 179 mutex_lock(&data->lock); 180 181 /* Protect against event capture. */ 182 if (vcnl3020_is_in_periodic_mode(data)) { 183 rc = -EBUSY; 184 goto err_unlock; 185 } 186 187 rc = regmap_write(data->regmap, VCNL_COMMAND, VCNL_PS_OD); 188 if (rc) 189 goto err_unlock; 190 191 /* wait for data to become ready */ 192 rc = regmap_read_poll_timeout(data->regmap, VCNL_COMMAND, reg, 193 reg & VCNL_PS_RDY, VCNL_POLL_US, 194 VCNL_ON_DEMAND_TIMEOUT_US); 195 if (rc) { 196 dev_err(data->dev, 197 "Error (%d) reading vcnl3020 command register\n", rc); 198 goto err_unlock; 199 } 200 201 /* high & low result bytes read */ 202 rc = regmap_bulk_read(data->regmap, VCNL_PS_RESULT_HI, &data->buf, 203 sizeof(data->buf)); 204 if (rc) 205 goto err_unlock; 206 207 *val = be16_to_cpu(data->buf); 208 209err_unlock: 210 mutex_unlock(&data->lock); 211 212 return rc; 213} 214 215static int vcnl3020_read_proxy_samp_freq(struct vcnl3020_data *data, int *val, 216 int *val2) 217{ 218 int rc; 219 unsigned int prox_rate; 220 221 rc = regmap_read(data->regmap, VCNL_PROXIMITY_RATE, &prox_rate); 222 if (rc) 223 return rc; 224 225 if (prox_rate >= ARRAY_SIZE(vcnl3020_prox_sampling_frequency)) 226 return -EINVAL; 227 228 *val = vcnl3020_prox_sampling_frequency[prox_rate][0]; 229 *val2 = vcnl3020_prox_sampling_frequency[prox_rate][1]; 230 231 return 0; 232} 233 234static int vcnl3020_write_proxy_samp_freq(struct vcnl3020_data *data, int val, 235 int val2) 236{ 237 unsigned int i; 238 int index = -1; 239 int rc; 240 241 mutex_lock(&data->lock); 242 243 /* Protect against event capture. */ 244 if (vcnl3020_is_in_periodic_mode(data)) { 245 rc = -EBUSY; 246 goto err_unlock; 247 } 248 249 for (i = 0; i < ARRAY_SIZE(vcnl3020_prox_sampling_frequency); i++) { 250 if (val == vcnl3020_prox_sampling_frequency[i][0] && 251 val2 == vcnl3020_prox_sampling_frequency[i][1]) { 252 index = i; 253 break; 254 } 255 } 256 257 if (index < 0) { 258 rc = -EINVAL; 259 goto err_unlock; 260 } 261 262 rc = regmap_write(data->regmap, VCNL_PROXIMITY_RATE, index); 263 if (rc) 264 dev_err(data->dev, 265 "Error (%d) writing proximity rate register\n", rc); 266 267err_unlock: 268 mutex_unlock(&data->lock); 269 270 return rc; 271} 272 273static bool vcnl3020_is_thr_enabled(struct vcnl3020_data *data) 274{ 275 int rc; 276 unsigned int icr; 277 278 rc = regmap_read(data->regmap, VCNL_PS_ICR, &icr); 279 if (rc) { 280 dev_err(data->dev, 281 "Error (%d) reading ICR register\n", rc); 282 return false; 283 } 284 285 return !!(icr & VCNL_ICR_THRES_EN); 286} 287 288static int vcnl3020_read_event(struct iio_dev *indio_dev, 289 const struct iio_chan_spec *chan, 290 enum iio_event_type type, 291 enum iio_event_direction dir, 292 enum iio_event_info info, 293 int *val, int *val2) 294{ 295 int rc; 296 struct vcnl3020_data *data = iio_priv(indio_dev); 297 298 switch (info) { 299 case IIO_EV_INFO_VALUE: 300 switch (dir) { 301 case IIO_EV_DIR_RISING: 302 rc = regmap_bulk_read(data->regmap, VCNL_PS_HI_THR_HI, 303 &data->buf, sizeof(data->buf)); 304 if (rc < 0) 305 return rc; 306 *val = be16_to_cpu(data->buf); 307 return IIO_VAL_INT; 308 case IIO_EV_DIR_FALLING: 309 rc = regmap_bulk_read(data->regmap, VCNL_PS_LO_THR_HI, 310 &data->buf, sizeof(data->buf)); 311 if (rc < 0) 312 return rc; 313 *val = be16_to_cpu(data->buf); 314 return IIO_VAL_INT; 315 default: 316 return -EINVAL; 317 } 318 default: 319 return -EINVAL; 320 } 321} 322 323static int vcnl3020_write_event(struct iio_dev *indio_dev, 324 const struct iio_chan_spec *chan, 325 enum iio_event_type type, 326 enum iio_event_direction dir, 327 enum iio_event_info info, 328 int val, int val2) 329{ 330 int rc; 331 struct vcnl3020_data *data = iio_priv(indio_dev); 332 333 mutex_lock(&data->lock); 334 335 switch (info) { 336 case IIO_EV_INFO_VALUE: 337 switch (dir) { 338 case IIO_EV_DIR_RISING: 339 /* 16 bit word/ low * high */ 340 data->buf = cpu_to_be16(val); 341 rc = regmap_bulk_write(data->regmap, VCNL_PS_HI_THR_HI, 342 &data->buf, sizeof(data->buf)); 343 if (rc < 0) 344 goto err_unlock; 345 rc = IIO_VAL_INT; 346 goto err_unlock; 347 case IIO_EV_DIR_FALLING: 348 data->buf = cpu_to_be16(val); 349 rc = regmap_bulk_write(data->regmap, VCNL_PS_LO_THR_HI, 350 &data->buf, sizeof(data->buf)); 351 if (rc < 0) 352 goto err_unlock; 353 rc = IIO_VAL_INT; 354 goto err_unlock; 355 default: 356 rc = -EINVAL; 357 goto err_unlock; 358 } 359 default: 360 rc = -EINVAL; 361 goto err_unlock; 362 } 363err_unlock: 364 mutex_unlock(&data->lock); 365 366 return rc; 367} 368 369static int vcnl3020_enable_periodic(struct iio_dev *indio_dev, 370 struct vcnl3020_data *data) 371{ 372 int rc; 373 int cmd; 374 375 mutex_lock(&data->lock); 376 377 /* Enable periodic measurement of proximity data. */ 378 cmd = VCNL_PS_EN | VCNL_PS_SELFTIMED_EN; 379 380 rc = regmap_write(data->regmap, VCNL_COMMAND, cmd); 381 if (rc) { 382 dev_err(data->dev, 383 "Error (%d) writing command register\n", rc); 384 goto err_unlock; 385 } 386 387 /* 388 * Enable interrupts on threshold, for proximity data by 389 * default. 390 */ 391 rc = regmap_write(data->regmap, VCNL_PS_ICR, VCNL_ICR_THRES_EN); 392 if (rc) 393 dev_err(data->dev, 394 "Error (%d) reading ICR register\n", rc); 395 396err_unlock: 397 mutex_unlock(&data->lock); 398 399 return rc; 400} 401 402static int vcnl3020_disable_periodic(struct iio_dev *indio_dev, 403 struct vcnl3020_data *data) 404{ 405 int rc; 406 407 mutex_lock(&data->lock); 408 409 rc = regmap_write(data->regmap, VCNL_COMMAND, 0); 410 if (rc) { 411 dev_err(data->dev, 412 "Error (%d) writing command register\n", rc); 413 goto err_unlock; 414 } 415 416 rc = regmap_write(data->regmap, VCNL_PS_ICR, 0); 417 if (rc) { 418 dev_err(data->dev, 419 "Error (%d) writing ICR register\n", rc); 420 goto err_unlock; 421 } 422 423 /* Clear interrupt flag bit */ 424 rc = regmap_write(data->regmap, VCNL_ISR, 0); 425 if (rc) 426 dev_err(data->dev, 427 "Error (%d) writing ISR register\n", rc); 428 429err_unlock: 430 mutex_unlock(&data->lock); 431 432 return rc; 433} 434 435static int vcnl3020_config_threshold(struct iio_dev *indio_dev, bool state) 436{ 437 struct vcnl3020_data *data = iio_priv(indio_dev); 438 439 if (state) { 440 return vcnl3020_enable_periodic(indio_dev, data); 441 } else { 442 if (!vcnl3020_is_thr_enabled(data)) 443 return 0; 444 return vcnl3020_disable_periodic(indio_dev, data); 445 } 446} 447 448static int vcnl3020_write_event_config(struct iio_dev *indio_dev, 449 const struct iio_chan_spec *chan, 450 enum iio_event_type type, 451 enum iio_event_direction dir, 452 int state) 453{ 454 switch (chan->type) { 455 case IIO_PROXIMITY: 456 return vcnl3020_config_threshold(indio_dev, state); 457 default: 458 return -EINVAL; 459 } 460} 461 462static int vcnl3020_read_event_config(struct iio_dev *indio_dev, 463 const struct iio_chan_spec *chan, 464 enum iio_event_type type, 465 enum iio_event_direction dir) 466{ 467 struct vcnl3020_data *data = iio_priv(indio_dev); 468 469 switch (chan->type) { 470 case IIO_PROXIMITY: 471 return vcnl3020_is_thr_enabled(data); 472 default: 473 return -EINVAL; 474 } 475} 476 477static const struct iio_event_spec vcnl3020_event_spec[] = { 478 { 479 .type = IIO_EV_TYPE_THRESH, 480 .dir = IIO_EV_DIR_RISING, 481 .mask_separate = BIT(IIO_EV_INFO_VALUE), 482 }, { 483 .type = IIO_EV_TYPE_THRESH, 484 .dir = IIO_EV_DIR_FALLING, 485 .mask_separate = BIT(IIO_EV_INFO_VALUE), 486 }, { 487 .type = IIO_EV_TYPE_THRESH, 488 .dir = IIO_EV_DIR_EITHER, 489 .mask_separate = BIT(IIO_EV_INFO_ENABLE), 490 }, 491}; 492 493static const struct iio_chan_spec vcnl3020_channels[] = { 494 { 495 .type = IIO_PROXIMITY, 496 .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | 497 BIT(IIO_CHAN_INFO_SAMP_FREQ), 498 .info_mask_separate_available = BIT(IIO_CHAN_INFO_SAMP_FREQ), 499 .event_spec = vcnl3020_event_spec, 500 .num_event_specs = ARRAY_SIZE(vcnl3020_event_spec), 501 }, 502}; 503 504static int vcnl3020_read_raw(struct iio_dev *indio_dev, 505 struct iio_chan_spec const *chan, int *val, 506 int *val2, long mask) 507{ 508 int rc; 509 struct vcnl3020_data *data = iio_priv(indio_dev); 510 511 switch (mask) { 512 case IIO_CHAN_INFO_RAW: 513 rc = vcnl3020_measure_proximity(data, val); 514 if (rc) 515 return rc; 516 return IIO_VAL_INT; 517 case IIO_CHAN_INFO_SAMP_FREQ: 518 rc = vcnl3020_read_proxy_samp_freq(data, val, val2); 519 if (rc < 0) 520 return rc; 521 return IIO_VAL_INT_PLUS_MICRO; 522 default: 523 return -EINVAL; 524 } 525} 526 527static int vcnl3020_write_raw(struct iio_dev *indio_dev, 528 struct iio_chan_spec const *chan, 529 int val, int val2, long mask) 530{ 531 struct vcnl3020_data *data = iio_priv(indio_dev); 532 533 switch (mask) { 534 case IIO_CHAN_INFO_SAMP_FREQ: 535 return vcnl3020_write_proxy_samp_freq(data, val, val2); 536 default: 537 return -EINVAL; 538 } 539} 540 541static int vcnl3020_read_avail(struct iio_dev *indio_dev, 542 struct iio_chan_spec const *chan, 543 const int **vals, int *type, int *length, 544 long mask) 545{ 546 switch (mask) { 547 case IIO_CHAN_INFO_SAMP_FREQ: 548 *vals = (int *)vcnl3020_prox_sampling_frequency; 549 *type = IIO_VAL_INT_PLUS_MICRO; 550 *length = 2 * ARRAY_SIZE(vcnl3020_prox_sampling_frequency); 551 return IIO_AVAIL_LIST; 552 default: 553 return -EINVAL; 554 } 555} 556 557static const struct iio_info vcnl3020_info = { 558 .read_raw = vcnl3020_read_raw, 559 .write_raw = vcnl3020_write_raw, 560 .read_avail = vcnl3020_read_avail, 561 .read_event_value = vcnl3020_read_event, 562 .write_event_value = vcnl3020_write_event, 563 .read_event_config = vcnl3020_read_event_config, 564 .write_event_config = vcnl3020_write_event_config, 565}; 566 567static const struct regmap_config vcnl3020_regmap_config = { 568 .reg_bits = 8, 569 .val_bits = 8, 570 .max_register = VCNL_PS_MOD_ADJ, 571}; 572 573static irqreturn_t vcnl3020_handle_irq_thread(int irq, void *p) 574{ 575 struct iio_dev *indio_dev = p; 576 struct vcnl3020_data *data = iio_priv(indio_dev); 577 unsigned int isr; 578 int rc; 579 580 rc = regmap_read(data->regmap, VCNL_ISR, &isr); 581 if (rc) { 582 dev_err(data->dev, "Error (%d) reading reg (0x%x)\n", 583 rc, VCNL_ISR); 584 return IRQ_HANDLED; 585 } 586 587 if (!(isr & VCNL_ICR_THRES_EN)) 588 return IRQ_NONE; 589 590 iio_push_event(indio_dev, 591 IIO_UNMOD_EVENT_CODE(IIO_PROXIMITY, 1, 592 IIO_EV_TYPE_THRESH, 593 IIO_EV_DIR_RISING), 594 iio_get_time_ns(indio_dev)); 595 596 rc = regmap_write(data->regmap, VCNL_ISR, isr & VCNL_ICR_THRES_EN); 597 if (rc) 598 dev_err(data->dev, "Error (%d) writing in reg (0x%x)\n", 599 rc, VCNL_ISR); 600 601 return IRQ_HANDLED; 602} 603 604static int vcnl3020_probe(struct i2c_client *client) 605{ 606 struct vcnl3020_data *data; 607 struct iio_dev *indio_dev; 608 struct regmap *regmap; 609 int rc; 610 611 regmap = devm_regmap_init_i2c(client, &vcnl3020_regmap_config); 612 if (IS_ERR(regmap)) { 613 dev_err(&client->dev, "regmap_init failed\n"); 614 return PTR_ERR(regmap); 615 } 616 617 indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data)); 618 if (!indio_dev) 619 return -ENOMEM; 620 621 data = iio_priv(indio_dev); 622 i2c_set_clientdata(client, indio_dev); 623 data->regmap = regmap; 624 data->dev = &client->dev; 625 626 rc = vcnl3020_init(data); 627 if (rc) 628 return rc; 629 630 indio_dev->info = &vcnl3020_info; 631 indio_dev->channels = vcnl3020_channels; 632 indio_dev->num_channels = ARRAY_SIZE(vcnl3020_channels); 633 indio_dev->name = "vcnl3020"; 634 indio_dev->modes = INDIO_DIRECT_MODE; 635 636 if (client->irq) { 637 rc = devm_request_threaded_irq(&client->dev, client->irq, 638 NULL, vcnl3020_handle_irq_thread, 639 IRQF_ONESHOT, indio_dev->name, 640 indio_dev); 641 if (rc) { 642 dev_err(&client->dev, 643 "Error (%d) irq request failed (%u)\n", rc, 644 client->irq); 645 return rc; 646 } 647 } 648 649 return devm_iio_device_register(&client->dev, indio_dev); 650} 651 652static const struct of_device_id vcnl3020_of_match[] = { 653 { 654 .compatible = "vishay,vcnl3020", 655 }, 656 {} 657}; 658MODULE_DEVICE_TABLE(of, vcnl3020_of_match); 659 660static struct i2c_driver vcnl3020_driver = { 661 .driver = { 662 .name = "vcnl3020", 663 .of_match_table = vcnl3020_of_match, 664 }, 665 .probe_new = vcnl3020_probe, 666}; 667module_i2c_driver(vcnl3020_driver); 668 669MODULE_AUTHOR("Ivan Mikhaylov <i.mikhaylov@yadro.com>"); 670MODULE_DESCRIPTION("Vishay VCNL3020 proximity sensor driver"); 671MODULE_LICENSE("GPL");