linux/drivers/iio/proximity/vcnl3020.c
<<
>>
Prefs
   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, &reg);
 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");
 672