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