linux/drivers/iio/light/vl6180.c
<<
>>
Prefs
   1/*
   2 * vl6180.c - Support for STMicroelectronics VL6180 ALS, range and proximity
   3 * sensor
   4 *
   5 * Copyright 2017 Peter Meerwald-Stadler <pmeerw@pmeerw.net>
   6 * Copyright 2017 Manivannan Sadhasivam <manivannanece23@gmail.com>
   7 *
   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 * IIO driver for VL6180 (7-bit I2C slave address 0x29)
  13 *
  14 * Range: 0 to 100mm
  15 * ALS: < 1 Lux up to 100 kLux
  16 * IR: 850nm
  17 *
  18 * TODO: irq, threshold events, continuous mode, hardware buffer
  19 */
  20
  21#include <linux/module.h>
  22#include <linux/i2c.h>
  23#include <linux/mutex.h>
  24#include <linux/err.h>
  25#include <linux/of.h>
  26#include <linux/delay.h>
  27
  28#include <linux/iio/iio.h>
  29#include <linux/iio/sysfs.h>
  30
  31#define VL6180_DRV_NAME "vl6180"
  32
  33/* Device identification register and value */
  34#define VL6180_MODEL_ID 0x000
  35#define VL6180_MODEL_ID_VAL 0xb4
  36
  37/* Configuration registers */
  38#define VL6180_INTR_CONFIG 0x014
  39#define VL6180_INTR_CLEAR 0x015
  40#define VL6180_OUT_OF_RESET 0x016
  41#define VL6180_HOLD 0x017
  42#define VL6180_RANGE_START 0x018
  43#define VL6180_ALS_START 0x038
  44#define VL6180_ALS_GAIN 0x03f
  45#define VL6180_ALS_IT 0x040
  46
  47/* Status registers */
  48#define VL6180_RANGE_STATUS 0x04d
  49#define VL6180_ALS_STATUS 0x04e
  50#define VL6180_INTR_STATUS 0x04f
  51
  52/* Result value registers */
  53#define VL6180_ALS_VALUE 0x050
  54#define VL6180_RANGE_VALUE 0x062
  55#define VL6180_RANGE_RATE 0x066
  56
  57/* bits of the RANGE_START and ALS_START register */
  58#define VL6180_MODE_CONT BIT(1) /* continuous mode */
  59#define VL6180_STARTSTOP BIT(0) /* start measurement, auto-reset */
  60
  61/* bits of the INTR_STATUS and INTR_CONFIG register */
  62#define VL6180_ALS_READY BIT(5)
  63#define VL6180_RANGE_READY BIT(2)
  64
  65/* bits of the INTR_CLEAR register */
  66#define VL6180_CLEAR_ERROR BIT(2)
  67#define VL6180_CLEAR_ALS BIT(1)
  68#define VL6180_CLEAR_RANGE BIT(0)
  69
  70/* bits of the HOLD register */
  71#define VL6180_HOLD_ON BIT(0)
  72
  73/* default value for the ALS_IT register */
  74#define VL6180_ALS_IT_100 0x63 /* 100 ms */
  75
  76/* values for the ALS_GAIN register */
  77#define VL6180_ALS_GAIN_1 0x46
  78#define VL6180_ALS_GAIN_1_25 0x45
  79#define VL6180_ALS_GAIN_1_67 0x44
  80#define VL6180_ALS_GAIN_2_5 0x43
  81#define VL6180_ALS_GAIN_5 0x42
  82#define VL6180_ALS_GAIN_10 0x41
  83#define VL6180_ALS_GAIN_20 0x40
  84#define VL6180_ALS_GAIN_40 0x47
  85
  86struct vl6180_data {
  87        struct i2c_client *client;
  88        struct mutex lock;
  89};
  90
  91enum { VL6180_ALS, VL6180_RANGE, VL6180_PROX };
  92
  93/**
  94 * struct vl6180_chan_regs - Registers for accessing channels
  95 * @drdy_mask:                  Data ready bit in status register
  96 * @start_reg:                  Conversion start register
  97 * @value_reg:                  Result value register
  98 * @word:                       Register word length
  99 */
 100struct vl6180_chan_regs {
 101        u8 drdy_mask;
 102        u16 start_reg, value_reg;
 103        bool word;
 104};
 105
 106static const struct vl6180_chan_regs vl6180_chan_regs_table[] = {
 107        [VL6180_ALS] = {
 108                .drdy_mask = VL6180_ALS_READY,
 109                .start_reg = VL6180_ALS_START,
 110                .value_reg = VL6180_ALS_VALUE,
 111                .word = true,
 112        },
 113        [VL6180_RANGE] = {
 114                .drdy_mask = VL6180_RANGE_READY,
 115                .start_reg = VL6180_RANGE_START,
 116                .value_reg = VL6180_RANGE_VALUE,
 117                .word = false,
 118        },
 119        [VL6180_PROX] = {
 120                .drdy_mask = VL6180_RANGE_READY,
 121                .start_reg = VL6180_RANGE_START,
 122                .value_reg = VL6180_RANGE_RATE,
 123                .word = true,
 124        },
 125};
 126
 127static int vl6180_read(struct i2c_client *client, u16 cmd, void *databuf,
 128                       u8 len)
 129{
 130        __be16 cmdbuf = cpu_to_be16(cmd);
 131        struct i2c_msg msgs[2] = {
 132                { .addr = client->addr, .len = sizeof(cmdbuf), .buf = (u8 *) &cmdbuf },
 133                { .addr = client->addr, .len = len, .buf = databuf,
 134                  .flags = I2C_M_RD } };
 135        int ret;
 136
 137        ret = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs));
 138        if (ret < 0)
 139                dev_err(&client->dev, "failed reading register 0x%04x\n", cmd);
 140
 141        return ret;
 142}
 143
 144static int vl6180_read_byte(struct i2c_client *client, u16 cmd)
 145{
 146        u8 data;
 147        int ret;
 148
 149        ret = vl6180_read(client, cmd, &data, sizeof(data));
 150        if (ret < 0)
 151                return ret;
 152
 153        return data;
 154}
 155
 156static int vl6180_read_word(struct i2c_client *client, u16 cmd)
 157{
 158        __be16 data;
 159        int ret;
 160
 161        ret = vl6180_read(client, cmd, &data, sizeof(data));
 162        if (ret < 0)
 163                return ret;
 164
 165        return be16_to_cpu(data);
 166}
 167
 168static int vl6180_write_byte(struct i2c_client *client, u16 cmd, u8 val)
 169{
 170        u8 buf[3];
 171        struct i2c_msg msgs[1] = {
 172                { .addr = client->addr, .len = sizeof(buf), .buf = (u8 *) &buf } };
 173        int ret;
 174
 175        buf[0] = cmd >> 8;
 176        buf[1] = cmd & 0xff;
 177        buf[2] = val;
 178
 179        ret = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs));
 180        if (ret < 0) {
 181                dev_err(&client->dev, "failed writing register 0x%04x\n", cmd);
 182                return ret;
 183        }
 184
 185        return 0;
 186}
 187
 188static int vl6180_write_word(struct i2c_client *client, u16 cmd, u16 val)
 189{
 190        __be16 buf[2];
 191        struct i2c_msg msgs[1] = {
 192                { .addr = client->addr, .len = sizeof(buf), .buf = (u8 *) &buf } };
 193        int ret;
 194
 195        buf[0] = cpu_to_be16(cmd);
 196        buf[1] = cpu_to_be16(val);
 197
 198        ret = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs));
 199        if (ret < 0) {
 200                dev_err(&client->dev, "failed writing register 0x%04x\n", cmd);
 201                return ret;
 202        }
 203
 204        return 0;
 205}
 206
 207static int vl6180_measure(struct vl6180_data *data, int addr)
 208{
 209        struct i2c_client *client = data->client;
 210        int tries = 20, ret;
 211        u16 value;
 212
 213        mutex_lock(&data->lock);
 214        /* Start single shot measurement */
 215        ret = vl6180_write_byte(client,
 216                vl6180_chan_regs_table[addr].start_reg, VL6180_STARTSTOP);
 217        if (ret < 0)
 218                goto fail;
 219
 220        while (tries--) {
 221                ret = vl6180_read_byte(client, VL6180_INTR_STATUS);
 222                if (ret < 0)
 223                        goto fail;
 224
 225                if (ret & vl6180_chan_regs_table[addr].drdy_mask)
 226                        break;
 227                msleep(20);
 228        }
 229
 230        if (tries < 0) {
 231                ret = -EIO;
 232                goto fail;
 233        }
 234
 235        /* Read result value from appropriate registers */
 236        ret = vl6180_chan_regs_table[addr].word ?
 237                vl6180_read_word(client, vl6180_chan_regs_table[addr].value_reg) :
 238                vl6180_read_byte(client, vl6180_chan_regs_table[addr].value_reg);
 239        if (ret < 0)
 240                goto fail;
 241        value = ret;
 242
 243        /* Clear the interrupt flag after data read */
 244        ret = vl6180_write_byte(client, VL6180_INTR_CLEAR,
 245                VL6180_CLEAR_ERROR | VL6180_CLEAR_ALS | VL6180_CLEAR_RANGE);
 246        if (ret < 0)
 247                goto fail;
 248
 249        ret = value;
 250
 251fail:
 252        mutex_unlock(&data->lock);
 253
 254        return ret;
 255}
 256
 257static const struct iio_chan_spec vl6180_channels[] = {
 258        {
 259                .type = IIO_LIGHT,
 260                .address = VL6180_ALS,
 261                .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
 262                        BIT(IIO_CHAN_INFO_INT_TIME) |
 263                        BIT(IIO_CHAN_INFO_SCALE) |
 264                        BIT(IIO_CHAN_INFO_HARDWAREGAIN),
 265        }, {
 266                .type = IIO_DISTANCE,
 267                .address = VL6180_RANGE,
 268                .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
 269                        BIT(IIO_CHAN_INFO_SCALE),
 270        }, {
 271                .type = IIO_PROXIMITY,
 272                .address = VL6180_PROX,
 273                .info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
 274        }
 275};
 276
 277/*
 278 * Columns 3 & 4 represent the same value in decimal and hex notations.
 279 * Kept in order to avoid the datatype conversion while reading the
 280 * hardware_gain.
 281 */
 282static const int vl6180_als_gain[8][4] = {
 283        { 1,    0,      70,     VL6180_ALS_GAIN_1 },
 284        { 1,    250000, 69,     VL6180_ALS_GAIN_1_25 },
 285        { 1,    670000, 68,     VL6180_ALS_GAIN_1_67 },
 286        { 2,    500000, 67,     VL6180_ALS_GAIN_2_5 },
 287        { 5,    0,      66,     VL6180_ALS_GAIN_5 },
 288        { 10,   0,      65,     VL6180_ALS_GAIN_10 },
 289        { 20,   0,      64,     VL6180_ALS_GAIN_20 },
 290        { 40,   0,      71,     VL6180_ALS_GAIN_40 }
 291};
 292
 293static int vl6180_read_raw(struct iio_dev *indio_dev,
 294                                struct iio_chan_spec const *chan,
 295                                int *val, int *val2, long mask)
 296{
 297        struct vl6180_data *data = iio_priv(indio_dev);
 298        int ret, i;
 299
 300        switch (mask) {
 301        case IIO_CHAN_INFO_RAW:
 302                ret = vl6180_measure(data, chan->address);
 303                if (ret < 0)
 304                        return ret;
 305                *val = ret;
 306
 307                return IIO_VAL_INT;
 308        case IIO_CHAN_INFO_INT_TIME:
 309                ret = vl6180_read_word(data->client, VL6180_ALS_IT);
 310                if (ret < 0)
 311                        return ret;
 312                *val = 0; /* 1 count = 1ms (0 = 1ms) */
 313                *val2 = (ret + 1) * 1000; /* convert to seconds */
 314
 315                return IIO_VAL_INT_PLUS_MICRO;
 316        case IIO_CHAN_INFO_SCALE:
 317                switch (chan->type) {
 318                case IIO_LIGHT:
 319                        *val = 0; /* one ALS count is 0.32 Lux */
 320                        *val2 = 320000;
 321                        break;
 322                case IIO_DISTANCE:
 323                        *val = 0; /* sensor reports mm, scale to meter */
 324                        *val2 = 1000;
 325                        break;
 326                default:
 327                        return -EINVAL;
 328                }
 329
 330                return IIO_VAL_INT_PLUS_MICRO;
 331        case IIO_CHAN_INFO_HARDWAREGAIN:
 332                ret = vl6180_read_byte(data->client, VL6180_ALS_GAIN);
 333                if (ret < 0)
 334                        return -EINVAL;
 335                for (i = 0; i < ARRAY_SIZE(vl6180_als_gain); i++) {
 336                        if (ret == vl6180_als_gain[i][2]) {
 337                                *val = vl6180_als_gain[i][0];
 338                                *val2 = vl6180_als_gain[i][1];
 339                        }
 340                }
 341
 342                return IIO_VAL_INT_PLUS_MICRO;
 343        default:
 344                return -EINVAL;
 345        }
 346}
 347
 348static IIO_CONST_ATTR(als_gain_available, "1 1.25 1.67 2.5 5 10 20 40");
 349
 350static struct attribute *vl6180_attributes[] = {
 351        &iio_const_attr_als_gain_available.dev_attr.attr,
 352        NULL
 353};
 354
 355static const struct attribute_group vl6180_attribute_group = {
 356        .attrs = vl6180_attributes,
 357};
 358
 359/* HOLD is needed before updating any config registers */
 360static int vl6180_hold(struct vl6180_data *data, bool hold)
 361{
 362        return vl6180_write_byte(data->client, VL6180_HOLD,
 363                hold ? VL6180_HOLD_ON : 0);
 364}
 365
 366static int vl6180_set_als_gain(struct vl6180_data *data, int val, int val2)
 367{
 368        int i, ret;
 369
 370        for (i = 0; i < ARRAY_SIZE(vl6180_als_gain); i++) {
 371                if (val == vl6180_als_gain[i][0] &&
 372                        val2 == vl6180_als_gain[i][1]) {
 373                        mutex_lock(&data->lock);
 374                        ret = vl6180_hold(data, true);
 375                        if (ret < 0)
 376                                goto fail;
 377                        ret = vl6180_write_byte(data->client, VL6180_ALS_GAIN,
 378                                vl6180_als_gain[i][3]);
 379fail:
 380                        vl6180_hold(data, false);
 381                        mutex_unlock(&data->lock);
 382                        return ret;
 383                }
 384        }
 385
 386        return -EINVAL;
 387}
 388
 389static int vl6180_set_it(struct vl6180_data *data, int val2)
 390{
 391        int ret;
 392
 393        mutex_lock(&data->lock);
 394        ret = vl6180_hold(data, true);
 395        if (ret < 0)
 396                goto fail;
 397        ret = vl6180_write_word(data->client, VL6180_ALS_IT,
 398                (val2 - 500) / 1000); /* write value in ms */
 399fail:
 400        vl6180_hold(data, false);
 401        mutex_unlock(&data->lock);
 402
 403        return ret;
 404}
 405
 406static int vl6180_write_raw(struct iio_dev *indio_dev,
 407                             struct iio_chan_spec const *chan,
 408                             int val, int val2, long mask)
 409{
 410        struct vl6180_data *data = iio_priv(indio_dev);
 411
 412        switch (mask) {
 413        case IIO_CHAN_INFO_INT_TIME:
 414                if (val != 0 || val2 < 500 || val2 >= 512500)
 415                        return -EINVAL;
 416
 417                return vl6180_set_it(data, val2);
 418        case IIO_CHAN_INFO_HARDWAREGAIN:
 419                if (chan->type != IIO_LIGHT)
 420                        return -EINVAL;
 421
 422                return vl6180_set_als_gain(data, val, val2);
 423        default:
 424                return -EINVAL;
 425        }
 426}
 427
 428static const struct iio_info vl6180_info = {
 429        .read_raw = vl6180_read_raw,
 430        .write_raw = vl6180_write_raw,
 431        .attrs = &vl6180_attribute_group,
 432        .driver_module = THIS_MODULE,
 433};
 434
 435static int vl6180_init(struct vl6180_data *data)
 436{
 437        struct i2c_client *client = data->client;
 438        int ret;
 439
 440        ret = vl6180_read_byte(client, VL6180_MODEL_ID);
 441        if (ret < 0)
 442                return ret;
 443
 444        if (ret != VL6180_MODEL_ID_VAL) {
 445                dev_err(&client->dev, "invalid model ID %02x\n", ret);
 446                return -ENODEV;
 447        }
 448
 449        ret = vl6180_hold(data, true);
 450        if (ret < 0)
 451                return ret;
 452
 453        ret = vl6180_read_byte(client, VL6180_OUT_OF_RESET);
 454        if (ret < 0)
 455                return ret;
 456
 457        /*
 458         * Detect false reset condition here. This bit is always set when the
 459         * system comes out of reset.
 460         */
 461        if (ret != 0x01)
 462                dev_info(&client->dev, "device is not fresh out of reset\n");
 463
 464        /* Enable ALS and Range ready interrupts */
 465        ret = vl6180_write_byte(client, VL6180_INTR_CONFIG,
 466                                VL6180_ALS_READY | VL6180_RANGE_READY);
 467        if (ret < 0)
 468                return ret;
 469
 470        /* ALS integration time: 100ms */
 471        ret = vl6180_write_word(client, VL6180_ALS_IT, VL6180_ALS_IT_100);
 472        if (ret < 0)
 473                return ret;
 474
 475        /* ALS gain: 1 */
 476        ret = vl6180_write_byte(client, VL6180_ALS_GAIN, VL6180_ALS_GAIN_1);
 477        if (ret < 0)
 478                return ret;
 479
 480        ret = vl6180_write_byte(client, VL6180_OUT_OF_RESET, 0x00);
 481        if (ret < 0)
 482                return ret;
 483
 484        return vl6180_hold(data, false);
 485}
 486
 487static int vl6180_probe(struct i2c_client *client,
 488                          const struct i2c_device_id *id)
 489{
 490        struct vl6180_data *data;
 491        struct iio_dev *indio_dev;
 492        int ret;
 493
 494        indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data));
 495        if (!indio_dev)
 496                return -ENOMEM;
 497
 498        data = iio_priv(indio_dev);
 499        i2c_set_clientdata(client, indio_dev);
 500        data->client = client;
 501        mutex_init(&data->lock);
 502
 503        indio_dev->dev.parent = &client->dev;
 504        indio_dev->info = &vl6180_info;
 505        indio_dev->channels = vl6180_channels;
 506        indio_dev->num_channels = ARRAY_SIZE(vl6180_channels);
 507        indio_dev->name = VL6180_DRV_NAME;
 508        indio_dev->modes = INDIO_DIRECT_MODE;
 509
 510        ret = vl6180_init(data);
 511        if (ret < 0)
 512                return ret;
 513
 514        return devm_iio_device_register(&client->dev, indio_dev);
 515}
 516
 517static const struct of_device_id vl6180_of_match[] = {
 518        { .compatible = "st,vl6180", },
 519        { },
 520};
 521MODULE_DEVICE_TABLE(of, vl6180_of_match);
 522
 523static const struct i2c_device_id vl6180_id[] = {
 524        { "vl6180", 0 },
 525        { }
 526};
 527MODULE_DEVICE_TABLE(i2c, vl6180_id);
 528
 529static struct i2c_driver vl6180_driver = {
 530        .driver = {
 531                .name   = VL6180_DRV_NAME,
 532                .of_match_table = of_match_ptr(vl6180_of_match),
 533        },
 534        .probe  = vl6180_probe,
 535        .id_table = vl6180_id,
 536};
 537
 538module_i2c_driver(vl6180_driver);
 539
 540MODULE_AUTHOR("Peter Meerwald-Stadler <pmeerw@pmeerw.net>");
 541MODULE_AUTHOR("Manivannan Sadhasivam <manivannanece23@gmail.com>");
 542MODULE_DESCRIPTION("STMicro VL6180 ALS, range and proximity sensor driver");
 543MODULE_LICENSE("GPL");
 544