1 // SPDX-License-Identifier: GPL-2.0-only
3 * vl6180.c - Support for STMicroelectronics VL6180 ALS, range and proximity
6 * Copyright 2017 Peter Meerwald-Stadler <pmeerw@pmeerw.net>
7 * Copyright 2017 Manivannan Sadhasivam <manivannanece23@gmail.com>
9 * IIO driver for VL6180 (7-bit I2C slave address 0x29)
12 * ALS: < 1 Lux up to 100 kLux
15 * TODO: irq, threshold events, continuous mode, hardware buffer
18 #include <linux/module.h>
19 #include <linux/i2c.h>
20 #include <linux/mutex.h>
21 #include <linux/err.h>
23 #include <linux/delay.h>
24 #include <linux/util_macros.h>
26 #include <linux/iio/iio.h>
27 #include <linux/iio/sysfs.h>
29 #define VL6180_DRV_NAME "vl6180"
31 /* Device identification register and value */
32 #define VL6180_MODEL_ID 0x000
33 #define VL6180_MODEL_ID_VAL 0xb4
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
45 /* Status registers */
46 #define VL6180_RANGE_STATUS 0x04d
47 #define VL6180_ALS_STATUS 0x04e
48 #define VL6180_INTR_STATUS 0x04f
50 /* Result value registers */
51 #define VL6180_ALS_VALUE 0x050
52 #define VL6180_RANGE_VALUE 0x062
53 #define VL6180_RANGE_RATE 0x066
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 */
59 /* bits of the INTR_STATUS and INTR_CONFIG register */
60 #define VL6180_ALS_READY BIT(5)
61 #define VL6180_RANGE_READY BIT(2)
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)
68 /* bits of the HOLD register */
69 #define VL6180_HOLD_ON BIT(0)
71 /* default value for the ALS_IT register */
72 #define VL6180_ALS_IT_100 0x63 /* 100 ms */
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
85 struct i2c_client
*client
;
87 unsigned int als_gain_milli
;
88 unsigned int als_it_ms
;
91 enum { VL6180_ALS
, VL6180_RANGE
, VL6180_PROX
};
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
100 struct vl6180_chan_regs
{
102 u16 start_reg
, value_reg
;
106 static const struct vl6180_chan_regs vl6180_chan_regs_table
[] = {
108 .drdy_mask
= VL6180_ALS_READY
,
109 .start_reg
= VL6180_ALS_START
,
110 .value_reg
= VL6180_ALS_VALUE
,
114 .drdy_mask
= VL6180_RANGE_READY
,
115 .start_reg
= VL6180_RANGE_START
,
116 .value_reg
= VL6180_RANGE_VALUE
,
120 .drdy_mask
= VL6180_RANGE_READY
,
121 .start_reg
= VL6180_RANGE_START
,
122 .value_reg
= VL6180_RANGE_RATE
,
127 static int vl6180_read(struct i2c_client
*client
, u16 cmd
, void *databuf
,
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
} };
137 ret
= i2c_transfer(client
->adapter
, msgs
, ARRAY_SIZE(msgs
));
139 dev_err(&client
->dev
, "failed reading register 0x%04x\n", cmd
);
144 static int vl6180_read_byte(struct i2c_client
*client
, u16 cmd
)
149 ret
= vl6180_read(client
, cmd
, &data
, sizeof(data
));
156 static int vl6180_read_word(struct i2c_client
*client
, u16 cmd
)
161 ret
= vl6180_read(client
, cmd
, &data
, sizeof(data
));
165 return be16_to_cpu(data
);
168 static int vl6180_write_byte(struct i2c_client
*client
, u16 cmd
, u8 val
)
171 struct i2c_msg msgs
[1] = {
172 { .addr
= client
->addr
, .len
= sizeof(buf
), .buf
= (u8
*) &buf
} };
179 ret
= i2c_transfer(client
->adapter
, msgs
, ARRAY_SIZE(msgs
));
181 dev_err(&client
->dev
, "failed writing register 0x%04x\n", cmd
);
188 static int vl6180_write_word(struct i2c_client
*client
, u16 cmd
, u16 val
)
191 struct i2c_msg msgs
[1] = {
192 { .addr
= client
->addr
, .len
= sizeof(buf
), .buf
= (u8
*) &buf
} };
195 buf
[0] = cpu_to_be16(cmd
);
196 buf
[1] = cpu_to_be16(val
);
198 ret
= i2c_transfer(client
->adapter
, msgs
, ARRAY_SIZE(msgs
));
200 dev_err(&client
->dev
, "failed writing register 0x%04x\n", cmd
);
207 static int vl6180_measure(struct vl6180_data
*data
, int addr
)
209 struct i2c_client
*client
= data
->client
;
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
);
221 ret
= vl6180_read_byte(client
, VL6180_INTR_STATUS
);
225 if (ret
& vl6180_chan_regs_table
[addr
].drdy_mask
)
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
);
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
);
252 mutex_unlock(&data
->lock
);
257 static const struct iio_chan_spec vl6180_channels
[] = {
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
),
266 .type
= IIO_DISTANCE
,
267 .address
= VL6180_RANGE
,
268 .info_mask_separate
= BIT(IIO_CHAN_INFO_RAW
) |
269 BIT(IIO_CHAN_INFO_SCALE
),
271 .type
= IIO_PROXIMITY
,
272 .address
= VL6180_PROX
,
273 .info_mask_separate
= BIT(IIO_CHAN_INFO_RAW
),
278 * Available Ambient Light Sensor gain settings, 1/1000th, and
279 * corresponding setting for the VL6180_ALS_GAIN register
281 static const int vl6180_als_gain_tab
[8] = {
282 1000, 1250, 1670, 2500, 5000, 10000, 20000, 40000
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
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
)
295 struct vl6180_data
*data
= iio_priv(indio_dev
);
299 case IIO_CHAN_INFO_RAW
:
300 ret
= vl6180_measure(data
, chan
->address
);
306 case IIO_CHAN_INFO_INT_TIME
:
307 *val
= data
->als_it_ms
;
310 return IIO_VAL_FRACTIONAL
;
312 case IIO_CHAN_INFO_SCALE
:
313 switch (chan
->type
) {
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
;
319 return IIO_VAL_FRACTIONAL
;
322 *val
= 0; /* sensor reports mm, scale to meter */
329 return IIO_VAL_INT_PLUS_MICRO
;
330 case IIO_CHAN_INFO_HARDWAREGAIN
:
331 *val
= data
->als_gain_milli
;
334 return IIO_VAL_FRACTIONAL
;
341 static IIO_CONST_ATTR(als_gain_available
, "1 1.25 1.67 2.5 5 10 20 40");
343 static struct attribute
*vl6180_attributes
[] = {
344 &iio_const_attr_als_gain_available
.dev_attr
.attr
,
348 static const struct attribute_group vl6180_attribute_group
= {
349 .attrs
= vl6180_attributes
,
352 /* HOLD is needed before updating any config registers */
353 static int vl6180_hold(struct vl6180_data
*data
, bool hold
)
355 return vl6180_write_byte(data
->client
, VL6180_HOLD
,
356 hold
? VL6180_HOLD_ON
: 0);
359 static int vl6180_set_als_gain(struct vl6180_data
*data
, int val
, int val2
)
363 if (val
< 1 || val
> 40)
366 gain
= (val
* 1000000 + val2
) / 1000;
367 if (gain
< 1 || gain
> 40000)
370 i
= find_closest(gain
, vl6180_als_gain_tab
,
371 ARRAY_SIZE(vl6180_als_gain_tab
));
373 mutex_lock(&data
->lock
);
374 ret
= vl6180_hold(data
, true);
378 ret
= vl6180_write_byte(data
->client
, VL6180_ALS_GAIN
,
379 vl6180_als_gain_tab_bits
[i
]);
382 data
->als_gain_milli
= vl6180_als_gain_tab
[i
];
385 vl6180_hold(data
, false);
386 mutex_unlock(&data
->lock
);
390 static int vl6180_set_it(struct vl6180_data
*data
, int val
, int val2
)
394 it_ms
= (val2
+ 500) / 1000; /* round to ms */
395 if (val
!= 0 || it_ms
< 1 || it_ms
> 512)
398 mutex_lock(&data
->lock
);
399 ret
= vl6180_hold(data
, true);
403 ret
= vl6180_write_word(data
->client
, VL6180_ALS_IT
, it_ms
- 1);
406 data
->als_it_ms
= it_ms
;
409 vl6180_hold(data
, false);
410 mutex_unlock(&data
->lock
);
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
)
419 struct vl6180_data
*data
= iio_priv(indio_dev
);
422 case IIO_CHAN_INFO_INT_TIME
:
423 return vl6180_set_it(data
, val
, val2
);
425 case IIO_CHAN_INFO_HARDWAREGAIN
:
426 if (chan
->type
!= IIO_LIGHT
)
429 return vl6180_set_als_gain(data
, val
, val2
);
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
,
441 static int vl6180_init(struct vl6180_data
*data
)
443 struct i2c_client
*client
= data
->client
;
446 ret
= vl6180_read_byte(client
, VL6180_MODEL_ID
);
450 if (ret
!= VL6180_MODEL_ID_VAL
) {
451 dev_err(&client
->dev
, "invalid model ID %02x\n", ret
);
455 ret
= vl6180_hold(data
, true);
459 ret
= vl6180_read_byte(client
, VL6180_OUT_OF_RESET
);
464 * Detect false reset condition here. This bit is always set when the
465 * system comes out of reset.
468 dev_info(&client
->dev
, "device is not fresh out of reset\n");
470 /* Enable ALS and Range ready interrupts */
471 ret
= vl6180_write_byte(client
, VL6180_INTR_CONFIG
,
472 VL6180_ALS_READY
| VL6180_RANGE_READY
);
476 /* ALS integration time: 100ms */
477 data
->als_it_ms
= 100;
478 ret
= vl6180_write_word(client
, VL6180_ALS_IT
, VL6180_ALS_IT_100
);
483 data
->als_gain_milli
= 1000;
484 ret
= vl6180_write_byte(client
, VL6180_ALS_GAIN
, VL6180_ALS_GAIN_1
);
488 ret
= vl6180_write_byte(client
, VL6180_OUT_OF_RESET
, 0x00);
492 return vl6180_hold(data
, false);
495 static int vl6180_probe(struct i2c_client
*client
,
496 const struct i2c_device_id
*id
)
498 struct vl6180_data
*data
;
499 struct iio_dev
*indio_dev
;
502 indio_dev
= devm_iio_device_alloc(&client
->dev
, sizeof(*data
));
506 data
= iio_priv(indio_dev
);
507 i2c_set_clientdata(client
, indio_dev
);
508 data
->client
= client
;
509 mutex_init(&data
->lock
);
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
;
518 ret
= vl6180_init(data
);
522 return devm_iio_device_register(&client
->dev
, indio_dev
);
525 static const struct of_device_id vl6180_of_match
[] = {
526 { .compatible
= "st,vl6180", },
529 MODULE_DEVICE_TABLE(of
, vl6180_of_match
);
531 static const struct i2c_device_id vl6180_id
[] = {
535 MODULE_DEVICE_TABLE(i2c
, vl6180_id
);
537 static struct i2c_driver vl6180_driver
= {
539 .name
= VL6180_DRV_NAME
,
540 .of_match_table
= of_match_ptr(vl6180_of_match
),
542 .probe
= vl6180_probe
,
543 .id_table
= vl6180_id
,
546 module_i2c_driver(vl6180_driver
);
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");