root/drivers/iio/light/vl6180.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. vl6180_read
  2. vl6180_read_byte
  3. vl6180_read_word
  4. vl6180_write_byte
  5. vl6180_write_word
  6. vl6180_measure
  7. vl6180_read_raw
  8. vl6180_hold
  9. vl6180_set_als_gain
  10. vl6180_set_it
  11. vl6180_write_raw
  12. vl6180_init
  13. vl6180_probe

   1 // SPDX-License-Identifier: GPL-2.0-only
   2 /*
   3  * vl6180.c - Support for STMicroelectronics VL6180 ALS, range and proximity
   4  * sensor
   5  *
   6  * Copyright 2017 Peter Meerwald-Stadler <pmeerw@pmeerw.net>
   7  * Copyright 2017 Manivannan Sadhasivam <manivannanece23@gmail.com>
   8  *
   9  * IIO driver for VL6180 (7-bit I2C slave address 0x29)
  10  *
  11  * Range: 0 to 100mm
  12  * ALS: < 1 Lux up to 100 kLux
  13  * IR: 850nm
  14  *
  15  * TODO: irq, threshold events, continuous mode, hardware buffer
  16  */
  17 
  18 #include <linux/module.h>
  19 #include <linux/i2c.h>
  20 #include <linux/mutex.h>
  21 #include <linux/err.h>
  22 #include <linux/of.h>
  23 #include <linux/delay.h>
  24 #include <linux/util_macros.h>
  25 
  26 #include <linux/iio/iio.h>
  27 #include <linux/iio/sysfs.h>
  28 
  29 #define VL6180_DRV_NAME "vl6180"
  30 
  31 /* Device identification register and value */
  32 #define VL6180_MODEL_ID 0x000
  33 #define VL6180_MODEL_ID_VAL 0xb4
  34 
  35 /* Configuration registers */
  36 #define VL6180_INTR_CONFIG 0x014
  37 #define VL6180_INTR_CLEAR 0x015
  38 #define VL6180_OUT_OF_RESET 0x016
  39 #define VL6180_HOLD 0x017
  40 #define VL6180_RANGE_START 0x018
  41 #define VL6180_ALS_START 0x038
  42 #define VL6180_ALS_GAIN 0x03f
  43 #define VL6180_ALS_IT 0x040
  44 
  45 /* Status registers */
  46 #define VL6180_RANGE_STATUS 0x04d
  47 #define VL6180_ALS_STATUS 0x04e
  48 #define VL6180_INTR_STATUS 0x04f
  49 
  50 /* Result value registers */
  51 #define VL6180_ALS_VALUE 0x050
  52 #define VL6180_RANGE_VALUE 0x062
  53 #define VL6180_RANGE_RATE 0x066
  54 
  55 /* bits of the RANGE_START and ALS_START register */
  56 #define VL6180_MODE_CONT BIT(1) /* continuous mode */
  57 #define VL6180_STARTSTOP BIT(0) /* start measurement, auto-reset */
  58 
  59 /* bits of the INTR_STATUS and INTR_CONFIG register */
  60 #define VL6180_ALS_READY BIT(5)
  61 #define VL6180_RANGE_READY BIT(2)
  62 
  63 /* bits of the INTR_CLEAR register */
  64 #define VL6180_CLEAR_ERROR BIT(2)
  65 #define VL6180_CLEAR_ALS BIT(1)
  66 #define VL6180_CLEAR_RANGE BIT(0)
  67 
  68 /* bits of the HOLD register */
  69 #define VL6180_HOLD_ON BIT(0)
  70 
  71 /* default value for the ALS_IT register */
  72 #define VL6180_ALS_IT_100 0x63 /* 100 ms */
  73 
  74 /* values for the ALS_GAIN register */
  75 #define VL6180_ALS_GAIN_1 0x46
  76 #define VL6180_ALS_GAIN_1_25 0x45
  77 #define VL6180_ALS_GAIN_1_67 0x44
  78 #define VL6180_ALS_GAIN_2_5 0x43
  79 #define VL6180_ALS_GAIN_5 0x42
  80 #define VL6180_ALS_GAIN_10 0x41
  81 #define VL6180_ALS_GAIN_20 0x40
  82 #define VL6180_ALS_GAIN_40 0x47
  83 
  84 struct vl6180_data {
  85         struct i2c_client *client;
  86         struct mutex lock;
  87         unsigned int als_gain_milli;
  88         unsigned int als_it_ms;
  89 };
  90 
  91 enum { 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  */
 100 struct vl6180_chan_regs {
 101         u8 drdy_mask;
 102         u16 start_reg, value_reg;
 103         bool word;
 104 };
 105 
 106 static 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 
 127 static 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 
 144 static 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 
 156 static 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 
 168 static 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 
 188 static 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 
 207 static 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 
 251 fail:
 252         mutex_unlock(&data->lock);
 253 
 254         return ret;
 255 }
 256 
 257 static 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  * Available Ambient Light Sensor gain settings, 1/1000th, and
 279  * corresponding setting for the VL6180_ALS_GAIN register
 280  */
 281 static const int vl6180_als_gain_tab[8] = {
 282         1000, 1250, 1670, 2500, 5000, 10000, 20000, 40000
 283 };
 284 static const u8 vl6180_als_gain_tab_bits[8] = {
 285         VL6180_ALS_GAIN_1,    VL6180_ALS_GAIN_1_25,
 286         VL6180_ALS_GAIN_1_67, VL6180_ALS_GAIN_2_5,
 287         VL6180_ALS_GAIN_5,    VL6180_ALS_GAIN_10,
 288         VL6180_ALS_GAIN_20,   VL6180_ALS_GAIN_40
 289 };
 290 
 291 static int vl6180_read_raw(struct iio_dev *indio_dev,
 292                                 struct iio_chan_spec const *chan,
 293                                 int *val, int *val2, long mask)
 294 {
 295         struct vl6180_data *data = iio_priv(indio_dev);
 296         int ret;
 297 
 298         switch (mask) {
 299         case IIO_CHAN_INFO_RAW:
 300                 ret = vl6180_measure(data, chan->address);
 301                 if (ret < 0)
 302                         return ret;
 303                 *val = ret;
 304 
 305                 return IIO_VAL_INT;
 306         case IIO_CHAN_INFO_INT_TIME:
 307                 *val = data->als_it_ms;
 308                 *val2 = 1000;
 309 
 310                 return IIO_VAL_FRACTIONAL;
 311 
 312         case IIO_CHAN_INFO_SCALE:
 313                 switch (chan->type) {
 314                 case IIO_LIGHT:
 315                         /* one ALS count is 0.32 Lux @ gain 1, IT 100 ms */
 316                         *val = 32000; /* 0.32 * 1000 * 100 */
 317                         *val2 = data->als_gain_milli * data->als_it_ms;
 318 
 319                         return IIO_VAL_FRACTIONAL;
 320 
 321                 case IIO_DISTANCE:
 322                         *val = 0; /* sensor reports mm, scale to meter */
 323                         *val2 = 1000;
 324                         break;
 325                 default:
 326                         return -EINVAL;
 327                 }
 328 
 329                 return IIO_VAL_INT_PLUS_MICRO;
 330         case IIO_CHAN_INFO_HARDWAREGAIN:
 331                 *val = data->als_gain_milli;
 332                 *val2 = 1000;
 333 
 334                 return IIO_VAL_FRACTIONAL;
 335 
 336         default:
 337                 return -EINVAL;
 338         }
 339 }
 340 
 341 static IIO_CONST_ATTR(als_gain_available, "1 1.25 1.67 2.5 5 10 20 40");
 342 
 343 static struct attribute *vl6180_attributes[] = {
 344         &iio_const_attr_als_gain_available.dev_attr.attr,
 345         NULL
 346 };
 347 
 348 static const struct attribute_group vl6180_attribute_group = {
 349         .attrs = vl6180_attributes,
 350 };
 351 
 352 /* HOLD is needed before updating any config registers */
 353 static int vl6180_hold(struct vl6180_data *data, bool hold)
 354 {
 355         return vl6180_write_byte(data->client, VL6180_HOLD,
 356                 hold ? VL6180_HOLD_ON : 0);
 357 }
 358 
 359 static int vl6180_set_als_gain(struct vl6180_data *data, int val, int val2)
 360 {
 361         int i, ret, gain;
 362 
 363         if (val < 1 || val > 40)
 364                 return -EINVAL;
 365 
 366         gain = (val * 1000000 + val2) / 1000;
 367         if (gain < 1 || gain > 40000)
 368                 return -EINVAL;
 369 
 370         i = find_closest(gain, vl6180_als_gain_tab,
 371                          ARRAY_SIZE(vl6180_als_gain_tab));
 372 
 373         mutex_lock(&data->lock);
 374         ret = vl6180_hold(data, true);
 375         if (ret < 0)
 376                 goto fail;
 377 
 378         ret = vl6180_write_byte(data->client, VL6180_ALS_GAIN,
 379                                 vl6180_als_gain_tab_bits[i]);
 380 
 381         if (ret >= 0)
 382                 data->als_gain_milli = vl6180_als_gain_tab[i];
 383 
 384 fail:
 385         vl6180_hold(data, false);
 386         mutex_unlock(&data->lock);
 387         return ret;
 388 }
 389 
 390 static int vl6180_set_it(struct vl6180_data *data, int val, int val2)
 391 {
 392         int ret, it_ms;
 393 
 394         it_ms = (val2 + 500) / 1000; /* round to ms */
 395         if (val != 0 || it_ms < 1 || it_ms > 512)
 396                 return -EINVAL;
 397 
 398         mutex_lock(&data->lock);
 399         ret = vl6180_hold(data, true);
 400         if (ret < 0)
 401                 goto fail;
 402 
 403         ret = vl6180_write_word(data->client, VL6180_ALS_IT, it_ms - 1);
 404 
 405         if (ret >= 0)
 406                 data->als_it_ms = it_ms;
 407 
 408 fail:
 409         vl6180_hold(data, false);
 410         mutex_unlock(&data->lock);
 411 
 412         return ret;
 413 }
 414 
 415 static int vl6180_write_raw(struct iio_dev *indio_dev,
 416                              struct iio_chan_spec const *chan,
 417                              int val, int val2, long mask)
 418 {
 419         struct vl6180_data *data = iio_priv(indio_dev);
 420 
 421         switch (mask) {
 422         case IIO_CHAN_INFO_INT_TIME:
 423                 return vl6180_set_it(data, val, val2);
 424 
 425         case IIO_CHAN_INFO_HARDWAREGAIN:
 426                 if (chan->type != IIO_LIGHT)
 427                         return -EINVAL;
 428 
 429                 return vl6180_set_als_gain(data, val, val2);
 430         default:
 431                 return -EINVAL;
 432         }
 433 }
 434 
 435 static const struct iio_info vl6180_info = {
 436         .read_raw = vl6180_read_raw,
 437         .write_raw = vl6180_write_raw,
 438         .attrs = &vl6180_attribute_group,
 439 };
 440 
 441 static int vl6180_init(struct vl6180_data *data)
 442 {
 443         struct i2c_client *client = data->client;
 444         int ret;
 445 
 446         ret = vl6180_read_byte(client, VL6180_MODEL_ID);
 447         if (ret < 0)
 448                 return ret;
 449 
 450         if (ret != VL6180_MODEL_ID_VAL) {
 451                 dev_err(&client->dev, "invalid model ID %02x\n", ret);
 452                 return -ENODEV;
 453         }
 454 
 455         ret = vl6180_hold(data, true);
 456         if (ret < 0)
 457                 return ret;
 458 
 459         ret = vl6180_read_byte(client, VL6180_OUT_OF_RESET);
 460         if (ret < 0)
 461                 return ret;
 462 
 463         /*
 464          * Detect false reset condition here. This bit is always set when the
 465          * system comes out of reset.
 466          */
 467         if (ret != 0x01)
 468                 dev_info(&client->dev, "device is not fresh out of reset\n");
 469 
 470         /* Enable ALS and Range ready interrupts */
 471         ret = vl6180_write_byte(client, VL6180_INTR_CONFIG,
 472                                 VL6180_ALS_READY | VL6180_RANGE_READY);
 473         if (ret < 0)
 474                 return ret;
 475 
 476         /* ALS integration time: 100ms */
 477         data->als_it_ms = 100;
 478         ret = vl6180_write_word(client, VL6180_ALS_IT, VL6180_ALS_IT_100);
 479         if (ret < 0)
 480                 return ret;
 481 
 482         /* ALS gain: 1 */
 483         data->als_gain_milli = 1000;
 484         ret = vl6180_write_byte(client, VL6180_ALS_GAIN, VL6180_ALS_GAIN_1);
 485         if (ret < 0)
 486                 return ret;
 487 
 488         ret = vl6180_write_byte(client, VL6180_OUT_OF_RESET, 0x00);
 489         if (ret < 0)
 490                 return ret;
 491 
 492         return vl6180_hold(data, false);
 493 }
 494 
 495 static int vl6180_probe(struct i2c_client *client,
 496                           const struct i2c_device_id *id)
 497 {
 498         struct vl6180_data *data;
 499         struct iio_dev *indio_dev;
 500         int ret;
 501 
 502         indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data));
 503         if (!indio_dev)
 504                 return -ENOMEM;
 505 
 506         data = iio_priv(indio_dev);
 507         i2c_set_clientdata(client, indio_dev);
 508         data->client = client;
 509         mutex_init(&data->lock);
 510 
 511         indio_dev->dev.parent = &client->dev;
 512         indio_dev->info = &vl6180_info;
 513         indio_dev->channels = vl6180_channels;
 514         indio_dev->num_channels = ARRAY_SIZE(vl6180_channels);
 515         indio_dev->name = VL6180_DRV_NAME;
 516         indio_dev->modes = INDIO_DIRECT_MODE;
 517 
 518         ret = vl6180_init(data);
 519         if (ret < 0)
 520                 return ret;
 521 
 522         return devm_iio_device_register(&client->dev, indio_dev);
 523 }
 524 
 525 static const struct of_device_id vl6180_of_match[] = {
 526         { .compatible = "st,vl6180", },
 527         { },
 528 };
 529 MODULE_DEVICE_TABLE(of, vl6180_of_match);
 530 
 531 static const struct i2c_device_id vl6180_id[] = {
 532         { "vl6180", 0 },
 533         { }
 534 };
 535 MODULE_DEVICE_TABLE(i2c, vl6180_id);
 536 
 537 static struct i2c_driver vl6180_driver = {
 538         .driver = {
 539                 .name   = VL6180_DRV_NAME,
 540                 .of_match_table = of_match_ptr(vl6180_of_match),
 541         },
 542         .probe  = vl6180_probe,
 543         .id_table = vl6180_id,
 544 };
 545 
 546 module_i2c_driver(vl6180_driver);
 547 
 548 MODULE_AUTHOR("Peter Meerwald-Stadler <pmeerw@pmeerw.net>");
 549 MODULE_AUTHOR("Manivannan Sadhasivam <manivannanece23@gmail.com>");
 550 MODULE_DESCRIPTION("STMicro VL6180 ALS, range and proximity sensor driver");
 551 MODULE_LICENSE("GPL");

/* [<][>][^][v][top][bottom][index][help] */