| .. | .. |
|---|
| 1 | +// SPDX-License-Identifier: GPL-2.0-only |
|---|
| 1 | 2 | /* |
|---|
| 2 | 3 | * vl6180.c - Support for STMicroelectronics VL6180 ALS, range and proximity |
|---|
| 3 | 4 | * sensor |
|---|
| .. | .. |
|---|
| 5 | 6 | * Copyright 2017 Peter Meerwald-Stadler <pmeerw@pmeerw.net> |
|---|
| 6 | 7 | * Copyright 2017 Manivannan Sadhasivam <manivannanece23@gmail.com> |
|---|
| 7 | 8 | * |
|---|
| 8 | | - * This file is subject to the terms and conditions of version 2 of |
|---|
| 9 | | - * the GNU General Public License. See the file COPYING in the main |
|---|
| 10 | | - * directory of this archive for more details. |
|---|
| 11 | | - * |
|---|
| 12 | 9 | * IIO driver for VL6180 (7-bit I2C slave address 0x29) |
|---|
| 13 | 10 | * |
|---|
| 14 | 11 | * Range: 0 to 100mm |
|---|
| 15 | 12 | * ALS: < 1 Lux up to 100 kLux |
|---|
| 16 | 13 | * IR: 850nm |
|---|
| 17 | 14 | * |
|---|
| 18 | | - * TODO: irq, threshold events, continuous mode, hardware buffer |
|---|
| 15 | + * TODO: threshold events, continuous mode |
|---|
| 19 | 16 | */ |
|---|
| 20 | 17 | |
|---|
| 21 | 18 | #include <linux/module.h> |
|---|
| 19 | +#include <linux/mod_devicetable.h> |
|---|
| 22 | 20 | #include <linux/i2c.h> |
|---|
| 23 | 21 | #include <linux/mutex.h> |
|---|
| 24 | 22 | #include <linux/err.h> |
|---|
| .. | .. |
|---|
| 28 | 26 | |
|---|
| 29 | 27 | #include <linux/iio/iio.h> |
|---|
| 30 | 28 | #include <linux/iio/sysfs.h> |
|---|
| 29 | +#include <linux/gpio.h> |
|---|
| 30 | +#include <linux/of_gpio.h> |
|---|
| 31 | +#include <linux/interrupt.h> |
|---|
| 32 | +#include <linux/iio/triggered_buffer.h> |
|---|
| 33 | +#include <linux/iio/kfifo_buf.h> |
|---|
| 34 | +#include <linux/iio/buffer.h> |
|---|
| 31 | 35 | |
|---|
| 32 | 36 | #define VL6180_DRV_NAME "vl6180" |
|---|
| 33 | 37 | |
|---|
| .. | .. |
|---|
| 36 | 40 | #define VL6180_MODEL_ID_VAL 0xb4 |
|---|
| 37 | 41 | |
|---|
| 38 | 42 | /* Configuration registers */ |
|---|
| 43 | +#define VL6180_SYS_MODE_GPIO1 0x011 |
|---|
| 39 | 44 | #define VL6180_INTR_CONFIG 0x014 |
|---|
| 40 | 45 | #define VL6180_INTR_CLEAR 0x015 |
|---|
| 41 | 46 | #define VL6180_OUT_OF_RESET 0x016 |
|---|
| 42 | 47 | #define VL6180_HOLD 0x017 |
|---|
| 43 | 48 | #define VL6180_RANGE_START 0x018 |
|---|
| 49 | +#define VL6180_RANGE_INTER_MES_PERIOD 0x01b |
|---|
| 44 | 50 | #define VL6180_ALS_START 0x038 |
|---|
| 51 | +#define VL6180_ALS_THRESH_HIGH 0x03a |
|---|
| 52 | +#define VL6180_ALS_THRESH_LOW 0x03c |
|---|
| 53 | +#define VL6180_ALS_INTER_MES_PERIOD 0x03e |
|---|
| 45 | 54 | #define VL6180_ALS_GAIN 0x03f |
|---|
| 46 | 55 | #define VL6180_ALS_IT 0x040 |
|---|
| 47 | 56 | |
|---|
| .. | .. |
|---|
| 55 | 64 | #define VL6180_RANGE_VALUE 0x062 |
|---|
| 56 | 65 | #define VL6180_RANGE_RATE 0x066 |
|---|
| 57 | 66 | |
|---|
| 67 | +#define VL6180_RANGE_THRESH_HIGH 0x019 |
|---|
| 68 | +#define VL6180_RANGE_THRESH_LOW 0x01a |
|---|
| 69 | +#define VL6180_RANGE_MAX_CONVERGENCE_TIME 0x01c |
|---|
| 70 | +#define VL6180_RANGE_CROSSTALK_COMPENSATION_RATE 0x01e |
|---|
| 71 | +#define VL6180_RANGE_PART_TO_PART_RANGE_OFFSET 0x024 |
|---|
| 72 | +#define VL6180_RANGE_RANGE_IGNORE_VALID_HEIGHT 0x025 |
|---|
| 73 | +#define VL6180_RANGE_RANGE_IGNORE_THRESHOLD 0x026 |
|---|
| 74 | +#define VL6180_RANGE_MAX_AMBIENT_LEVEL_MULT 0x02c |
|---|
| 75 | +#define VL6180_RANGE_RANGE_CHECK_ENABLES 0x02d |
|---|
| 76 | +#define VL6180_RANGE_VHV_RECALIBRATE 0x02e |
|---|
| 77 | +#define VL6180_RANGE_VHV_REPEAT_RATE 0x031 |
|---|
| 78 | +#define VL6180_READOUT_AVERAGING_SAMPLE_PERIOD 0x10a |
|---|
| 79 | + |
|---|
| 80 | +/* bits of the SYS_MODE_GPIO1 register */ |
|---|
| 81 | +#define VL6180_SYS_GPIO1_POLARITY BIT(5) /* active high */ |
|---|
| 82 | +#define VL6180_SYS_GPIO1_SELECT BIT(4) /* configure GPIO interrupt output */ |
|---|
| 83 | + |
|---|
| 58 | 84 | /* bits of the RANGE_START and ALS_START register */ |
|---|
| 59 | 85 | #define VL6180_MODE_CONT BIT(1) /* continuous mode */ |
|---|
| 60 | 86 | #define VL6180_STARTSTOP BIT(0) /* start measurement, auto-reset */ |
|---|
| 61 | 87 | |
|---|
| 62 | 88 | /* bits of the INTR_STATUS and INTR_CONFIG register */ |
|---|
| 89 | +#define VL6180_ALS_LEVEL_LOW BIT(3) |
|---|
| 90 | +#define VL6180_ALS_LEVEL_HIGH BIT(4) |
|---|
| 91 | +#define VL6180_ALS_OUT_OF_WINDOW (BIT(3) | BIT(4)) |
|---|
| 63 | 92 | #define VL6180_ALS_READY BIT(5) |
|---|
| 93 | +#define VL6180_RANGE_LEVEL_LOW BIT(0) |
|---|
| 94 | +#define VL6180_RANGE_LEVEL_HIGH BIT(1) |
|---|
| 95 | +#define VL6180_RANGE_OUT_OF_WINDOW (BIT(0) | BIT(1)) |
|---|
| 64 | 96 | #define VL6180_RANGE_READY BIT(2) |
|---|
| 97 | +#define VL6180_INT_RANGE_GPIO_MASK GENMASK(2, 0) |
|---|
| 98 | +#define VL6180_INT_ALS_GPIO_MASK GENMASK(5, 3) |
|---|
| 99 | +#define VL6180_INT_ERR_GPIO_MASK GENMASK(7, 6) |
|---|
| 65 | 100 | |
|---|
| 66 | 101 | /* bits of the INTR_CLEAR register */ |
|---|
| 67 | 102 | #define VL6180_CLEAR_ERROR BIT(2) |
|---|
| .. | .. |
|---|
| 89 | 124 | struct mutex lock; |
|---|
| 90 | 125 | unsigned int als_gain_milli; |
|---|
| 91 | 126 | unsigned int als_it_ms; |
|---|
| 127 | + struct gpio_desc *avdd; |
|---|
| 128 | + struct gpio_desc *chip_enable; |
|---|
| 129 | + |
|---|
| 130 | + /* Ensure natural alignment of timestamp */ |
|---|
| 131 | + struct { |
|---|
| 132 | + u16 channels[3]; |
|---|
| 133 | + u16 reserved; |
|---|
| 134 | + s64 ts; |
|---|
| 135 | + } scan; |
|---|
| 92 | 136 | }; |
|---|
| 93 | 137 | |
|---|
| 94 | 138 | enum { VL6180_ALS, VL6180_RANGE, VL6180_PROX }; |
|---|
| .. | .. |
|---|
| 125 | 169 | .value_reg = VL6180_RANGE_RATE, |
|---|
| 126 | 170 | .word = true, |
|---|
| 127 | 171 | }, |
|---|
| 172 | +}; |
|---|
| 173 | + |
|---|
| 174 | +/** |
|---|
| 175 | + * struct vl6180_custom_data - Data for custom initialization |
|---|
| 176 | + * @reg: Register |
|---|
| 177 | + * @val: Value |
|---|
| 178 | + */ |
|---|
| 179 | +struct vl6180_custom_data { |
|---|
| 180 | + u16 reg; |
|---|
| 181 | + u8 val; |
|---|
| 182 | +}; |
|---|
| 183 | + |
|---|
| 184 | +static const struct vl6180_custom_data vl6180_custom_data_table[] = { |
|---|
| 185 | + { .reg = 0x207, .val = 0x01, }, |
|---|
| 186 | + { .reg = 0x208, .val = 0x01, }, |
|---|
| 187 | + { .reg = 0x096, .val = 0x00, }, |
|---|
| 188 | + { .reg = 0x097, .val = 0xfd, }, |
|---|
| 189 | + { .reg = 0x0e3, .val = 0x00, }, |
|---|
| 190 | + { .reg = 0x0e4, .val = 0x04, }, |
|---|
| 191 | + { .reg = 0x0e5, .val = 0x02, }, |
|---|
| 192 | + { .reg = 0x0e6, .val = 0x01, }, |
|---|
| 193 | + { .reg = 0x0e7, .val = 0x03, }, |
|---|
| 194 | + { .reg = 0x0f5, .val = 0x02, }, |
|---|
| 195 | + { .reg = 0x0d9, .val = 0x05, }, |
|---|
| 196 | + { .reg = 0x0db, .val = 0xce, }, |
|---|
| 197 | + { .reg = 0x0dc, .val = 0x03, }, |
|---|
| 198 | + { .reg = 0x0dd, .val = 0xf8, }, |
|---|
| 199 | + { .reg = 0x09f, .val = 0x00, }, |
|---|
| 200 | + { .reg = 0x0a3, .val = 0x3c, }, |
|---|
| 201 | + { .reg = 0x0b7, .val = 0x00, }, |
|---|
| 202 | + { .reg = 0x0bb, .val = 0x3c, }, |
|---|
| 203 | + { .reg = 0x0b2, .val = 0x09, }, |
|---|
| 204 | + { .reg = 0x0ca, .val = 0x09, }, |
|---|
| 205 | + { .reg = 0x198, .val = 0x01, }, |
|---|
| 206 | + { .reg = 0x1b0, .val = 0x17, }, |
|---|
| 207 | + { .reg = 0x1ad, .val = 0x00, }, |
|---|
| 208 | + { .reg = 0x0ff, .val = 0x05, }, |
|---|
| 209 | + { .reg = 0x100, .val = 0x05, }, |
|---|
| 210 | + { .reg = 0x199, .val = 0x05, }, |
|---|
| 211 | + { .reg = 0x1a6, .val = 0x1b, }, |
|---|
| 212 | + { .reg = 0x1ac, .val = 0x3e, }, |
|---|
| 213 | + { .reg = 0x1a7, .val = 0x1f, }, |
|---|
| 214 | + { .reg = 0x030, .val = 0x00, }, |
|---|
| 128 | 215 | }; |
|---|
| 129 | 216 | |
|---|
| 130 | 217 | static int vl6180_read(struct i2c_client *client, u16 cmd, void *databuf, |
|---|
| .. | .. |
|---|
| 265 | 352 | BIT(IIO_CHAN_INFO_INT_TIME) | |
|---|
| 266 | 353 | BIT(IIO_CHAN_INFO_SCALE) | |
|---|
| 267 | 354 | BIT(IIO_CHAN_INFO_HARDWAREGAIN), |
|---|
| 355 | + .scan_index = 0, |
|---|
| 356 | + .scan_type = { |
|---|
| 357 | + .sign = 'u', |
|---|
| 358 | + .realbits = 16, |
|---|
| 359 | + .storagebits = 16, |
|---|
| 360 | + } |
|---|
| 268 | 361 | }, { |
|---|
| 269 | 362 | .type = IIO_DISTANCE, |
|---|
| 270 | 363 | .address = VL6180_RANGE, |
|---|
| 271 | 364 | .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | |
|---|
| 272 | 365 | BIT(IIO_CHAN_INFO_SCALE), |
|---|
| 366 | + .scan_index = 1, |
|---|
| 367 | + .scan_type = { |
|---|
| 368 | + .sign = 'u', |
|---|
| 369 | + .realbits = 16, |
|---|
| 370 | + .storagebits = 16, |
|---|
| 371 | + } |
|---|
| 273 | 372 | }, { |
|---|
| 274 | 373 | .type = IIO_PROXIMITY, |
|---|
| 275 | 374 | .address = VL6180_PROX, |
|---|
| 276 | 375 | .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), |
|---|
| 277 | | - } |
|---|
| 376 | + .scan_index = 2, |
|---|
| 377 | + .scan_type = { |
|---|
| 378 | + .sign = 'u', |
|---|
| 379 | + .realbits = 16, |
|---|
| 380 | + .storagebits = 16, |
|---|
| 381 | + } |
|---|
| 382 | + }, |
|---|
| 383 | + IIO_CHAN_SOFT_TIMESTAMP(3), |
|---|
| 278 | 384 | }; |
|---|
| 279 | 385 | |
|---|
| 280 | 386 | /* |
|---|
| .. | .. |
|---|
| 441 | 547 | .attrs = &vl6180_attribute_group, |
|---|
| 442 | 548 | }; |
|---|
| 443 | 549 | |
|---|
| 550 | +static int vl6180_power_enable(struct vl6180_data *data) |
|---|
| 551 | +{ |
|---|
| 552 | + /* Enable power supply. */ |
|---|
| 553 | + if (!IS_ERR_OR_NULL(data->avdd)) |
|---|
| 554 | + gpiod_set_value_cansleep(data->avdd, 1); |
|---|
| 555 | + |
|---|
| 556 | + /* Power-up default is chip enable (CE). */ |
|---|
| 557 | + if (!IS_ERR_OR_NULL(data->chip_enable)) { |
|---|
| 558 | + gpiod_set_value_cansleep(data->chip_enable, 0); |
|---|
| 559 | + usleep_range(500, 1000); |
|---|
| 560 | + gpiod_set_value_cansleep(data->chip_enable, 1); |
|---|
| 561 | + } |
|---|
| 562 | + |
|---|
| 563 | + return 0; |
|---|
| 564 | +} |
|---|
| 565 | + |
|---|
| 566 | +static int vl6180_custom_init(struct vl6180_data *data) |
|---|
| 567 | +{ |
|---|
| 568 | + struct i2c_client *client = data->client; |
|---|
| 569 | + int ret; |
|---|
| 570 | + int i; |
|---|
| 571 | + |
|---|
| 572 | + /* REGISTER_TUNING_SR03_270514_CustomerView.txt */ |
|---|
| 573 | + for (i = 0; i < ARRAY_SIZE(vl6180_custom_data_table); ++i) { |
|---|
| 574 | + ret = vl6180_write_byte(client, |
|---|
| 575 | + vl6180_custom_data_table[i].reg, |
|---|
| 576 | + vl6180_custom_data_table[i].val); |
|---|
| 577 | + |
|---|
| 578 | + if (ret < 0) |
|---|
| 579 | + break; |
|---|
| 580 | + } |
|---|
| 581 | + |
|---|
| 582 | + return ret; |
|---|
| 583 | +} |
|---|
| 584 | + |
|---|
| 585 | +static int vl6180_range_init(struct vl6180_data *data) |
|---|
| 586 | +{ |
|---|
| 587 | + struct i2c_client *client = data->client; |
|---|
| 588 | + int ret; |
|---|
| 589 | + u8 enables; |
|---|
| 590 | + u8 offset; |
|---|
| 591 | + u8 xtalk = 3; |
|---|
| 592 | + |
|---|
| 593 | + /* Enables polling for ‘New Sample ready’ when measurement completes */ |
|---|
| 594 | + ret = vl6180_write_byte(client, VL6180_SYS_MODE_GPIO1, |
|---|
| 595 | + (VL6180_SYS_GPIO1_POLARITY | |
|---|
| 596 | + VL6180_SYS_GPIO1_SELECT)); |
|---|
| 597 | + if (ret < 0) |
|---|
| 598 | + goto out; |
|---|
| 599 | + |
|---|
| 600 | + /* Set the averaging sample period (compromise between lower noise and |
|---|
| 601 | + * increased execution time), 0x30 equals to 4.3 ms. |
|---|
| 602 | + */ |
|---|
| 603 | + ret = vl6180_write_byte(client, VL6180_READOUT_AVERAGING_SAMPLE_PERIOD, |
|---|
| 604 | + 0x30); |
|---|
| 605 | + if (ret < 0) |
|---|
| 606 | + goto out; |
|---|
| 607 | + |
|---|
| 608 | + /* Sets the # of range measurements after which auto calibration of |
|---|
| 609 | + * system is performed |
|---|
| 610 | + */ |
|---|
| 611 | + ret = vl6180_write_byte(client, VL6180_RANGE_VHV_REPEAT_RATE, 0xff); |
|---|
| 612 | + if (ret < 0) |
|---|
| 613 | + goto out; |
|---|
| 614 | + |
|---|
| 615 | + /* Perform a single temperature calibration of the ranging sensor */ |
|---|
| 616 | + ret = vl6180_write_byte(client, VL6180_RANGE_VHV_RECALIBRATE, 0x01); |
|---|
| 617 | + if (ret < 0) |
|---|
| 618 | + goto out; |
|---|
| 619 | + |
|---|
| 620 | + /* Set SNR limit to 0.06 */ |
|---|
| 621 | + ret = vl6180_write_byte(client, VL6180_RANGE_MAX_AMBIENT_LEVEL_MULT, |
|---|
| 622 | + 0xff); |
|---|
| 623 | + if (ret < 0) |
|---|
| 624 | + goto out; |
|---|
| 625 | + |
|---|
| 626 | + /* Set default ranging inter-measurement period to 100ms */ |
|---|
| 627 | + ret = vl6180_write_byte(client, VL6180_RANGE_INTER_MES_PERIOD, 0x09); |
|---|
| 628 | + if (ret < 0) |
|---|
| 629 | + goto out; |
|---|
| 630 | + |
|---|
| 631 | + /* Copy registers */ |
|---|
| 632 | + /* NOTE: 0x0da, 0x027, 0x0db, 0x028, 0x0dc, 0x029 and 0x0dd are |
|---|
| 633 | + * unavailable on the datasheet. |
|---|
| 634 | + */ |
|---|
| 635 | + ret = vl6180_read_byte(client, VL6180_RANGE_RANGE_IGNORE_THRESHOLD); |
|---|
| 636 | + if (ret < 0) |
|---|
| 637 | + goto out; |
|---|
| 638 | + |
|---|
| 639 | + ret = vl6180_write_byte(client, 0x0da, ret); |
|---|
| 640 | + if (ret < 0) |
|---|
| 641 | + goto out; |
|---|
| 642 | + |
|---|
| 643 | + ret = vl6180_read_byte(client, 0x027); |
|---|
| 644 | + if (ret < 0) |
|---|
| 645 | + goto out; |
|---|
| 646 | + |
|---|
| 647 | + ret = vl6180_write_byte(client, 0x0db, ret); |
|---|
| 648 | + if (ret < 0) |
|---|
| 649 | + goto out; |
|---|
| 650 | + |
|---|
| 651 | + ret = vl6180_read_byte(client, 0x028); |
|---|
| 652 | + if (ret < 0) |
|---|
| 653 | + goto out; |
|---|
| 654 | + |
|---|
| 655 | + ret = vl6180_write_byte(client, 0x0dc, ret); |
|---|
| 656 | + if (ret < 0) |
|---|
| 657 | + goto out; |
|---|
| 658 | + |
|---|
| 659 | + ret = vl6180_read_byte(client, 0x029); |
|---|
| 660 | + if (ret < 0) |
|---|
| 661 | + goto out; |
|---|
| 662 | + |
|---|
| 663 | + ret = vl6180_write_byte(client, 0x0dd, ret); |
|---|
| 664 | + if (ret < 0) |
|---|
| 665 | + goto out; |
|---|
| 666 | + |
|---|
| 667 | + ret = vl6180_write_byte(client, VL6180_RANGE_MAX_CONVERGENCE_TIME, 0x32); |
|---|
| 668 | + if (ret < 0) |
|---|
| 669 | + goto out; |
|---|
| 670 | + |
|---|
| 671 | + ret = vl6180_read_byte(client, VL6180_RANGE_RANGE_CHECK_ENABLES); |
|---|
| 672 | + if (ret < 0) |
|---|
| 673 | + goto out; |
|---|
| 674 | + |
|---|
| 675 | + /* Disable early convergence */ |
|---|
| 676 | + enables = ret & 0xfe; |
|---|
| 677 | + ret = vl6180_write_byte(client, VL6180_RANGE_RANGE_CHECK_ENABLES, enables); |
|---|
| 678 | + if (ret < 0) |
|---|
| 679 | + goto out; |
|---|
| 680 | + |
|---|
| 681 | + ret = vl6180_write_byte(client, VL6180_RANGE_THRESH_HIGH, 0xc8); |
|---|
| 682 | + if (ret < 0) |
|---|
| 683 | + goto out; |
|---|
| 684 | + |
|---|
| 685 | + ret = vl6180_write_byte(client, VL6180_RANGE_THRESH_LOW, 0x00); |
|---|
| 686 | + if (ret < 0) |
|---|
| 687 | + goto out; |
|---|
| 688 | + |
|---|
| 689 | + ret = vl6180_write_byte(client, VL6180_ALS_IT, VL6180_ALS_IT_100); |
|---|
| 690 | + if (ret < 0) |
|---|
| 691 | + goto out; |
|---|
| 692 | + |
|---|
| 693 | + ret = vl6180_write_byte(client, VL6180_ALS_INTER_MES_PERIOD, 0x13); |
|---|
| 694 | + if (ret < 0) |
|---|
| 695 | + goto out; |
|---|
| 696 | + |
|---|
| 697 | + ret = vl6180_write_byte(client, VL6180_ALS_GAIN, VL6180_ALS_GAIN_1); |
|---|
| 698 | + if (ret < 0) |
|---|
| 699 | + goto out; |
|---|
| 700 | + |
|---|
| 701 | + ret = vl6180_write_byte(client, VL6180_ALS_THRESH_LOW, 0x00); |
|---|
| 702 | + if (ret < 0) |
|---|
| 703 | + goto out; |
|---|
| 704 | + |
|---|
| 705 | + ret = vl6180_write_byte(client, VL6180_ALS_THRESH_HIGH, 0xff); |
|---|
| 706 | + if (ret < 0) |
|---|
| 707 | + goto out; |
|---|
| 708 | + |
|---|
| 709 | + /* Cover glass ignore */ |
|---|
| 710 | + ret = vl6180_write_byte(client, |
|---|
| 711 | + VL6180_RANGE_RANGE_IGNORE_VALID_HEIGHT, 0xff); |
|---|
| 712 | + if (ret < 0) |
|---|
| 713 | + goto out; |
|---|
| 714 | + |
|---|
| 715 | + ret = vl6180_read_byte(client, VL6180_RANGE_PART_TO_PART_RANGE_OFFSET); |
|---|
| 716 | + if (ret < 0) |
|---|
| 717 | + goto out; |
|---|
| 718 | + |
|---|
| 719 | + /* Apply default calibration on part to part offset */ |
|---|
| 720 | + offset = ret / 4; |
|---|
| 721 | + ret = vl6180_write_byte(client, VL6180_RANGE_PART_TO_PART_RANGE_OFFSET, |
|---|
| 722 | + offset); |
|---|
| 723 | + if (ret < 0) |
|---|
| 724 | + goto out; |
|---|
| 725 | + |
|---|
| 726 | + ret = vl6180_write_byte(client, |
|---|
| 727 | + VL6180_RANGE_CROSSTALK_COMPENSATION_RATE, |
|---|
| 728 | + 0x00); |
|---|
| 729 | + if (ret < 0) |
|---|
| 730 | + goto out; |
|---|
| 731 | + |
|---|
| 732 | + ret = vl6180_write_byte(client, 0x01f, xtalk); |
|---|
| 733 | + |
|---|
| 734 | +out: |
|---|
| 735 | + return ret; |
|---|
| 736 | +} |
|---|
| 737 | + |
|---|
| 444 | 738 | static int vl6180_init(struct vl6180_data *data) |
|---|
| 445 | 739 | { |
|---|
| 446 | 740 | struct i2c_client *client = data->client; |
|---|
| 447 | 741 | int ret; |
|---|
| 742 | + |
|---|
| 743 | + ret = vl6180_power_enable(data); |
|---|
| 744 | + if (ret) { |
|---|
| 745 | + dev_err(&client->dev, "failed to configure power\n"); |
|---|
| 746 | + return ret; |
|---|
| 747 | + } |
|---|
| 748 | + |
|---|
| 749 | + /* |
|---|
| 750 | + * After the MCU boot sequence the device enters software standby, |
|---|
| 751 | + * host initialization can commence immediately after entering |
|---|
| 752 | + * software standby. |
|---|
| 753 | + */ |
|---|
| 754 | + usleep_range(500, 1000); |
|---|
| 448 | 755 | |
|---|
| 449 | 756 | ret = vl6180_read_byte(client, VL6180_MODEL_ID); |
|---|
| 450 | 757 | if (ret < 0) |
|---|
| .. | .. |
|---|
| 470 | 777 | if (ret != 0x01) |
|---|
| 471 | 778 | dev_info(&client->dev, "device is not fresh out of reset\n"); |
|---|
| 472 | 779 | |
|---|
| 473 | | - /* Enable ALS and Range ready interrupts */ |
|---|
| 474 | | - ret = vl6180_write_byte(client, VL6180_INTR_CONFIG, |
|---|
| 475 | | - VL6180_ALS_READY | VL6180_RANGE_READY); |
|---|
| 476 | | - if (ret < 0) |
|---|
| 477 | | - return ret; |
|---|
| 478 | | - |
|---|
| 479 | 780 | /* ALS integration time: 100ms */ |
|---|
| 480 | 781 | data->als_it_ms = 100; |
|---|
| 481 | 782 | ret = vl6180_write_word(client, VL6180_ALS_IT, VL6180_ALS_IT_100); |
|---|
| .. | .. |
|---|
| 488 | 789 | if (ret < 0) |
|---|
| 489 | 790 | return ret; |
|---|
| 490 | 791 | |
|---|
| 792 | + ret = vl6180_custom_init(data); |
|---|
| 793 | + if (ret < 0) |
|---|
| 794 | + return ret; |
|---|
| 795 | + |
|---|
| 796 | + ret = vl6180_range_init(data); |
|---|
| 797 | + if (ret < 0) |
|---|
| 798 | + return ret; |
|---|
| 799 | + |
|---|
| 800 | + ret = vl6180_write_byte(client, VL6180_RANGE_START, |
|---|
| 801 | + (VL6180_STARTSTOP | VL6180_MODE_CONT)); |
|---|
| 802 | + if (ret < 0) |
|---|
| 803 | + return ret; |
|---|
| 804 | + |
|---|
| 491 | 805 | ret = vl6180_write_byte(client, VL6180_OUT_OF_RESET, 0x00); |
|---|
| 492 | 806 | if (ret < 0) |
|---|
| 493 | 807 | return ret; |
|---|
| .. | .. |
|---|
| 495 | 809 | return vl6180_hold(data, false); |
|---|
| 496 | 810 | } |
|---|
| 497 | 811 | |
|---|
| 812 | +static irqreturn_t vl6180_irq_thread(int irq, void *priv) |
|---|
| 813 | +{ |
|---|
| 814 | + struct vl6180_data *data = priv; |
|---|
| 815 | + struct i2c_client *client = data->client; |
|---|
| 816 | + struct iio_dev *indio_dev = i2c_get_clientdata(client); |
|---|
| 817 | + int ret; |
|---|
| 818 | + u8 val = 0; |
|---|
| 819 | + |
|---|
| 820 | + ret = vl6180_read_byte(client, VL6180_INTR_STATUS); |
|---|
| 821 | + if (ret < 0) |
|---|
| 822 | + goto out; |
|---|
| 823 | + |
|---|
| 824 | + if (ret & VL6180_INT_ALS_GPIO_MASK) |
|---|
| 825 | + val |= VL6180_CLEAR_ALS; |
|---|
| 826 | + |
|---|
| 827 | + if (ret & VL6180_INT_RANGE_GPIO_MASK) |
|---|
| 828 | + val |= VL6180_CLEAR_RANGE; |
|---|
| 829 | + |
|---|
| 830 | + if (ret & VL6180_INT_ERR_GPIO_MASK) |
|---|
| 831 | + val |= VL6180_CLEAR_ERROR; |
|---|
| 832 | + |
|---|
| 833 | + vl6180_write_byte(client, VL6180_INTR_CLEAR, val); |
|---|
| 834 | + |
|---|
| 835 | + ret = vl6180_read_word(client, VL6180_ALS_VALUE); |
|---|
| 836 | + if (ret < 0) |
|---|
| 837 | + goto out; |
|---|
| 838 | + data->scan.channels[VL6180_ALS] = ret; |
|---|
| 839 | + |
|---|
| 840 | + ret = vl6180_read_byte(client, VL6180_RANGE_VALUE); |
|---|
| 841 | + if (ret < 0) |
|---|
| 842 | + goto out; |
|---|
| 843 | + data->scan.channels[VL6180_RANGE] = ret; |
|---|
| 844 | + |
|---|
| 845 | + ret = vl6180_read_word(client, VL6180_RANGE_RATE); |
|---|
| 846 | + if (ret < 0) |
|---|
| 847 | + goto out; |
|---|
| 848 | + data->scan.channels[VL6180_PROX] = ret; |
|---|
| 849 | + |
|---|
| 850 | + iio_push_to_buffers_with_timestamp(indio_dev, &data->scan, |
|---|
| 851 | + ktime_get_boottime_ns()); |
|---|
| 852 | + |
|---|
| 853 | +out: |
|---|
| 854 | + return IRQ_HANDLED; |
|---|
| 855 | +} |
|---|
| 856 | + |
|---|
| 857 | +static int vl6180_buffer_preenable(struct iio_dev *indio_dev) |
|---|
| 858 | +{ |
|---|
| 859 | + struct vl6180_data *data = iio_priv(indio_dev); |
|---|
| 860 | + u8 val; |
|---|
| 861 | + int ret; |
|---|
| 862 | + |
|---|
| 863 | + ret = vl6180_read_byte(data->client, VL6180_INTR_CONFIG); |
|---|
| 864 | + if (ret < 0) |
|---|
| 865 | + return ret; |
|---|
| 866 | + |
|---|
| 867 | + /* Enable ALS and Range ready interrupts */ |
|---|
| 868 | + val = ret | VL6180_ALS_READY | VL6180_RANGE_READY; |
|---|
| 869 | + ret = vl6180_write_byte(data->client, VL6180_INTR_CONFIG, val); |
|---|
| 870 | + |
|---|
| 871 | + return ret; |
|---|
| 872 | +} |
|---|
| 873 | + |
|---|
| 874 | +static int vl6180_buffer_postdisable(struct iio_dev *indio_dev) |
|---|
| 875 | +{ |
|---|
| 876 | + struct vl6180_data *data = iio_priv(indio_dev); |
|---|
| 877 | + u8 val; |
|---|
| 878 | + int ret; |
|---|
| 879 | + |
|---|
| 880 | + ret = vl6180_read_byte(data->client, VL6180_INTR_CONFIG); |
|---|
| 881 | + if (ret < 0) |
|---|
| 882 | + return ret; |
|---|
| 883 | + |
|---|
| 884 | + /* Disable ALS and Range ready interrupts */ |
|---|
| 885 | + val = ret & ~(VL6180_ALS_READY | VL6180_RANGE_READY); |
|---|
| 886 | + ret = vl6180_write_byte(data->client, VL6180_INTR_CONFIG, val); |
|---|
| 887 | + |
|---|
| 888 | + return ret; |
|---|
| 889 | +} |
|---|
| 890 | + |
|---|
| 891 | +static const struct iio_buffer_setup_ops vl6180_buffer_setup_ops = { |
|---|
| 892 | + .preenable = vl6180_buffer_preenable, |
|---|
| 893 | + .postdisable = vl6180_buffer_postdisable, |
|---|
| 894 | +}; |
|---|
| 895 | + |
|---|
| 498 | 896 | static int vl6180_probe(struct i2c_client *client, |
|---|
| 499 | 897 | const struct i2c_device_id *id) |
|---|
| 500 | 898 | { |
|---|
| 501 | 899 | struct vl6180_data *data; |
|---|
| 502 | 900 | struct iio_dev *indio_dev; |
|---|
| 901 | + struct iio_buffer *buffer; |
|---|
| 902 | + u32 type; |
|---|
| 503 | 903 | int ret; |
|---|
| 504 | 904 | |
|---|
| 505 | 905 | indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data)); |
|---|
| .. | .. |
|---|
| 511 | 911 | data->client = client; |
|---|
| 512 | 912 | mutex_init(&data->lock); |
|---|
| 513 | 913 | |
|---|
| 514 | | - indio_dev->dev.parent = &client->dev; |
|---|
| 515 | 914 | indio_dev->info = &vl6180_info; |
|---|
| 516 | 915 | indio_dev->channels = vl6180_channels; |
|---|
| 517 | 916 | indio_dev->num_channels = ARRAY_SIZE(vl6180_channels); |
|---|
| 518 | 917 | indio_dev->name = VL6180_DRV_NAME; |
|---|
| 519 | 918 | indio_dev->modes = INDIO_DIRECT_MODE; |
|---|
| 520 | 919 | |
|---|
| 920 | + /* |
|---|
| 921 | + * NOTE: If the power is controlled by gpio, the power |
|---|
| 922 | + * configuration should match the power-up timing. |
|---|
| 923 | + */ |
|---|
| 924 | + data->avdd = devm_gpiod_get_optional(&client->dev, "avdd", |
|---|
| 925 | + GPIOD_OUT_HIGH); |
|---|
| 926 | + data->chip_enable = devm_gpiod_get_optional(&client->dev, "chip-enable", |
|---|
| 927 | + GPIOD_OUT_HIGH); |
|---|
| 928 | + |
|---|
| 521 | 929 | ret = vl6180_init(data); |
|---|
| 522 | 930 | if (ret < 0) |
|---|
| 523 | 931 | return ret; |
|---|
| 932 | + |
|---|
| 933 | + if (client->irq) { |
|---|
| 934 | + buffer = devm_iio_kfifo_allocate(&client->dev); |
|---|
| 935 | + if (!buffer) |
|---|
| 936 | + return -ENOMEM; |
|---|
| 937 | + |
|---|
| 938 | + iio_device_attach_buffer(indio_dev, buffer); |
|---|
| 939 | + indio_dev->modes |= INDIO_BUFFER_SOFTWARE; |
|---|
| 940 | + indio_dev->setup_ops = &vl6180_buffer_setup_ops; |
|---|
| 941 | + |
|---|
| 942 | + type = irqd_get_trigger_type(irq_get_irq_data(client->irq)); |
|---|
| 943 | + ret = devm_request_threaded_irq(&client->dev, client->irq, |
|---|
| 944 | + NULL, vl6180_irq_thread, |
|---|
| 945 | + type | IRQF_ONESHOT, "vl6180", |
|---|
| 946 | + data); |
|---|
| 947 | + if (ret) { |
|---|
| 948 | + dev_err(&client->dev, |
|---|
| 949 | + "failed to request vl6180 IRQ\n"); |
|---|
| 950 | + return ret; |
|---|
| 951 | + } |
|---|
| 952 | + } |
|---|
| 524 | 953 | |
|---|
| 525 | 954 | return devm_iio_device_register(&client->dev, indio_dev); |
|---|
| 526 | 955 | } |
|---|
| .. | .. |
|---|
| 540 | 969 | static struct i2c_driver vl6180_driver = { |
|---|
| 541 | 970 | .driver = { |
|---|
| 542 | 971 | .name = VL6180_DRV_NAME, |
|---|
| 543 | | - .of_match_table = of_match_ptr(vl6180_of_match), |
|---|
| 972 | + .of_match_table = vl6180_of_match, |
|---|
| 544 | 973 | }, |
|---|
| 545 | 974 | .probe = vl6180_probe, |
|---|
| 546 | 975 | .id_table = vl6180_id, |
|---|