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/mod_devicetable.h>
20 #include <linux/i2c.h>
21 #include <linux/mutex.h>
22 #include <linux/err.h>
24 #include <linux/delay.h>
25 #include <linux/util_macros.h>
27 #include <linux/iio/iio.h>
28 #include <linux/iio/sysfs.h>
30 #define VL6180_DRV_NAME "vl6180"
32 /* Device identification register and value */
33 #define VL6180_MODEL_ID 0x000
34 #define VL6180_MODEL_ID_VAL 0xb4
36 /* Configuration registers */
37 #define VL6180_INTR_CONFIG 0x014
38 #define VL6180_INTR_CLEAR 0x015
39 #define VL6180_OUT_OF_RESET 0x016
40 #define VL6180_HOLD 0x017
41 #define VL6180_RANGE_START 0x018
42 #define VL6180_ALS_START 0x038
43 #define VL6180_ALS_GAIN 0x03f
44 #define VL6180_ALS_IT 0x040
46 /* Status registers */
47 #define VL6180_RANGE_STATUS 0x04d
48 #define VL6180_ALS_STATUS 0x04e
49 #define VL6180_INTR_STATUS 0x04f
51 /* Result value registers */
52 #define VL6180_ALS_VALUE 0x050
53 #define VL6180_RANGE_VALUE 0x062
54 #define VL6180_RANGE_RATE 0x066
56 /* bits of the RANGE_START and ALS_START register */
57 #define VL6180_MODE_CONT BIT(1) /* continuous mode */
58 #define VL6180_STARTSTOP BIT(0) /* start measurement, auto-reset */
60 /* bits of the INTR_STATUS and INTR_CONFIG register */
61 #define VL6180_ALS_READY BIT(5)
62 #define VL6180_RANGE_READY BIT(2)
64 /* bits of the INTR_CLEAR register */
65 #define VL6180_CLEAR_ERROR BIT(2)
66 #define VL6180_CLEAR_ALS BIT(1)
67 #define VL6180_CLEAR_RANGE BIT(0)
69 /* bits of the HOLD register */
70 #define VL6180_HOLD_ON BIT(0)
72 /* default value for the ALS_IT register */
73 #define VL6180_ALS_IT_100 0x63 /* 100 ms */
75 /* values for the ALS_GAIN register */
76 #define VL6180_ALS_GAIN_1 0x46
77 #define VL6180_ALS_GAIN_1_25 0x45
78 #define VL6180_ALS_GAIN_1_67 0x44
79 #define VL6180_ALS_GAIN_2_5 0x43
80 #define VL6180_ALS_GAIN_5 0x42
81 #define VL6180_ALS_GAIN_10 0x41
82 #define VL6180_ALS_GAIN_20 0x40
83 #define VL6180_ALS_GAIN_40 0x47
86 struct i2c_client
*client
;
88 unsigned int als_gain_milli
;
89 unsigned int als_it_ms
;
92 enum { VL6180_ALS
, VL6180_RANGE
, VL6180_PROX
};
95 * struct vl6180_chan_regs - Registers for accessing channels
96 * @drdy_mask: Data ready bit in status register
97 * @start_reg: Conversion start register
98 * @value_reg: Result value register
99 * @word: Register word length
101 struct vl6180_chan_regs
{
103 u16 start_reg
, value_reg
;
107 static const struct vl6180_chan_regs vl6180_chan_regs_table
[] = {
109 .drdy_mask
= VL6180_ALS_READY
,
110 .start_reg
= VL6180_ALS_START
,
111 .value_reg
= VL6180_ALS_VALUE
,
115 .drdy_mask
= VL6180_RANGE_READY
,
116 .start_reg
= VL6180_RANGE_START
,
117 .value_reg
= VL6180_RANGE_VALUE
,
121 .drdy_mask
= VL6180_RANGE_READY
,
122 .start_reg
= VL6180_RANGE_START
,
123 .value_reg
= VL6180_RANGE_RATE
,
128 static int vl6180_read(struct i2c_client
*client
, u16 cmd
, void *databuf
,
131 __be16 cmdbuf
= cpu_to_be16(cmd
);
132 struct i2c_msg msgs
[2] = {
133 { .addr
= client
->addr
, .len
= sizeof(cmdbuf
), .buf
= (u8
*) &cmdbuf
},
134 { .addr
= client
->addr
, .len
= len
, .buf
= databuf
,
135 .flags
= I2C_M_RD
} };
138 ret
= i2c_transfer(client
->adapter
, msgs
, ARRAY_SIZE(msgs
));
140 dev_err(&client
->dev
, "failed reading register 0x%04x\n", cmd
);
145 static int vl6180_read_byte(struct i2c_client
*client
, u16 cmd
)
150 ret
= vl6180_read(client
, cmd
, &data
, sizeof(data
));
157 static int vl6180_read_word(struct i2c_client
*client
, u16 cmd
)
162 ret
= vl6180_read(client
, cmd
, &data
, sizeof(data
));
166 return be16_to_cpu(data
);
169 static int vl6180_write_byte(struct i2c_client
*client
, u16 cmd
, u8 val
)
172 struct i2c_msg msgs
[1] = {
173 { .addr
= client
->addr
, .len
= sizeof(buf
), .buf
= (u8
*) &buf
} };
180 ret
= i2c_transfer(client
->adapter
, msgs
, ARRAY_SIZE(msgs
));
182 dev_err(&client
->dev
, "failed writing register 0x%04x\n", cmd
);
189 static int vl6180_write_word(struct i2c_client
*client
, u16 cmd
, u16 val
)
192 struct i2c_msg msgs
[1] = {
193 { .addr
= client
->addr
, .len
= sizeof(buf
), .buf
= (u8
*) &buf
} };
196 buf
[0] = cpu_to_be16(cmd
);
197 buf
[1] = cpu_to_be16(val
);
199 ret
= i2c_transfer(client
->adapter
, msgs
, ARRAY_SIZE(msgs
));
201 dev_err(&client
->dev
, "failed writing register 0x%04x\n", cmd
);
208 static int vl6180_measure(struct vl6180_data
*data
, int addr
)
210 struct i2c_client
*client
= data
->client
;
214 mutex_lock(&data
->lock
);
215 /* Start single shot measurement */
216 ret
= vl6180_write_byte(client
,
217 vl6180_chan_regs_table
[addr
].start_reg
, VL6180_STARTSTOP
);
222 ret
= vl6180_read_byte(client
, VL6180_INTR_STATUS
);
226 if (ret
& vl6180_chan_regs_table
[addr
].drdy_mask
)
236 /* Read result value from appropriate registers */
237 ret
= vl6180_chan_regs_table
[addr
].word
?
238 vl6180_read_word(client
, vl6180_chan_regs_table
[addr
].value_reg
) :
239 vl6180_read_byte(client
, vl6180_chan_regs_table
[addr
].value_reg
);
244 /* Clear the interrupt flag after data read */
245 ret
= vl6180_write_byte(client
, VL6180_INTR_CLEAR
,
246 VL6180_CLEAR_ERROR
| VL6180_CLEAR_ALS
| VL6180_CLEAR_RANGE
);
253 mutex_unlock(&data
->lock
);
258 static const struct iio_chan_spec vl6180_channels
[] = {
261 .address
= VL6180_ALS
,
262 .info_mask_separate
= BIT(IIO_CHAN_INFO_RAW
) |
263 BIT(IIO_CHAN_INFO_INT_TIME
) |
264 BIT(IIO_CHAN_INFO_SCALE
) |
265 BIT(IIO_CHAN_INFO_HARDWAREGAIN
),
267 .type
= IIO_DISTANCE
,
268 .address
= VL6180_RANGE
,
269 .info_mask_separate
= BIT(IIO_CHAN_INFO_RAW
) |
270 BIT(IIO_CHAN_INFO_SCALE
),
272 .type
= IIO_PROXIMITY
,
273 .address
= VL6180_PROX
,
274 .info_mask_separate
= BIT(IIO_CHAN_INFO_RAW
),
279 * Available Ambient Light Sensor gain settings, 1/1000th, and
280 * corresponding setting for the VL6180_ALS_GAIN register
282 static const int vl6180_als_gain_tab
[8] = {
283 1000, 1250, 1670, 2500, 5000, 10000, 20000, 40000
285 static const u8 vl6180_als_gain_tab_bits
[8] = {
286 VL6180_ALS_GAIN_1
, VL6180_ALS_GAIN_1_25
,
287 VL6180_ALS_GAIN_1_67
, VL6180_ALS_GAIN_2_5
,
288 VL6180_ALS_GAIN_5
, VL6180_ALS_GAIN_10
,
289 VL6180_ALS_GAIN_20
, VL6180_ALS_GAIN_40
292 static int vl6180_read_raw(struct iio_dev
*indio_dev
,
293 struct iio_chan_spec
const *chan
,
294 int *val
, int *val2
, long mask
)
296 struct vl6180_data
*data
= iio_priv(indio_dev
);
300 case IIO_CHAN_INFO_RAW
:
301 ret
= vl6180_measure(data
, chan
->address
);
307 case IIO_CHAN_INFO_INT_TIME
:
308 *val
= data
->als_it_ms
;
311 return IIO_VAL_FRACTIONAL
;
313 case IIO_CHAN_INFO_SCALE
:
314 switch (chan
->type
) {
316 /* one ALS count is 0.32 Lux @ gain 1, IT 100 ms */
317 *val
= 32000; /* 0.32 * 1000 * 100 */
318 *val2
= data
->als_gain_milli
* data
->als_it_ms
;
320 return IIO_VAL_FRACTIONAL
;
323 *val
= 0; /* sensor reports mm, scale to meter */
330 return IIO_VAL_INT_PLUS_MICRO
;
331 case IIO_CHAN_INFO_HARDWAREGAIN
:
332 *val
= data
->als_gain_milli
;
335 return IIO_VAL_FRACTIONAL
;
342 static IIO_CONST_ATTR(als_gain_available
, "1 1.25 1.67 2.5 5 10 20 40");
344 static struct attribute
*vl6180_attributes
[] = {
345 &iio_const_attr_als_gain_available
.dev_attr
.attr
,
349 static const struct attribute_group vl6180_attribute_group
= {
350 .attrs
= vl6180_attributes
,
353 /* HOLD is needed before updating any config registers */
354 static int vl6180_hold(struct vl6180_data
*data
, bool hold
)
356 return vl6180_write_byte(data
->client
, VL6180_HOLD
,
357 hold
? VL6180_HOLD_ON
: 0);
360 static int vl6180_set_als_gain(struct vl6180_data
*data
, int val
, int val2
)
364 if (val
< 1 || val
> 40)
367 gain
= (val
* 1000000 + val2
) / 1000;
368 if (gain
< 1 || gain
> 40000)
371 i
= find_closest(gain
, vl6180_als_gain_tab
,
372 ARRAY_SIZE(vl6180_als_gain_tab
));
374 mutex_lock(&data
->lock
);
375 ret
= vl6180_hold(data
, true);
379 ret
= vl6180_write_byte(data
->client
, VL6180_ALS_GAIN
,
380 vl6180_als_gain_tab_bits
[i
]);
383 data
->als_gain_milli
= vl6180_als_gain_tab
[i
];
386 vl6180_hold(data
, false);
387 mutex_unlock(&data
->lock
);
391 static int vl6180_set_it(struct vl6180_data
*data
, int val
, int val2
)
395 it_ms
= (val2
+ 500) / 1000; /* round to ms */
396 if (val
!= 0 || it_ms
< 1 || it_ms
> 512)
399 mutex_lock(&data
->lock
);
400 ret
= vl6180_hold(data
, true);
404 ret
= vl6180_write_word(data
->client
, VL6180_ALS_IT
, it_ms
- 1);
407 data
->als_it_ms
= it_ms
;
410 vl6180_hold(data
, false);
411 mutex_unlock(&data
->lock
);
416 static int vl6180_write_raw(struct iio_dev
*indio_dev
,
417 struct iio_chan_spec
const *chan
,
418 int val
, int val2
, long mask
)
420 struct vl6180_data
*data
= iio_priv(indio_dev
);
423 case IIO_CHAN_INFO_INT_TIME
:
424 return vl6180_set_it(data
, val
, val2
);
426 case IIO_CHAN_INFO_HARDWAREGAIN
:
427 if (chan
->type
!= IIO_LIGHT
)
430 return vl6180_set_als_gain(data
, val
, val2
);
436 static const struct iio_info vl6180_info
= {
437 .read_raw
= vl6180_read_raw
,
438 .write_raw
= vl6180_write_raw
,
439 .attrs
= &vl6180_attribute_group
,
442 static int vl6180_init(struct vl6180_data
*data
)
444 struct i2c_client
*client
= data
->client
;
447 ret
= vl6180_read_byte(client
, VL6180_MODEL_ID
);
451 if (ret
!= VL6180_MODEL_ID_VAL
) {
452 dev_err(&client
->dev
, "invalid model ID %02x\n", ret
);
456 ret
= vl6180_hold(data
, true);
460 ret
= vl6180_read_byte(client
, VL6180_OUT_OF_RESET
);
465 * Detect false reset condition here. This bit is always set when the
466 * system comes out of reset.
469 dev_info(&client
->dev
, "device is not fresh out of reset\n");
471 /* Enable ALS and Range ready interrupts */
472 ret
= vl6180_write_byte(client
, VL6180_INTR_CONFIG
,
473 VL6180_ALS_READY
| VL6180_RANGE_READY
);
477 /* ALS integration time: 100ms */
478 data
->als_it_ms
= 100;
479 ret
= vl6180_write_word(client
, VL6180_ALS_IT
, VL6180_ALS_IT_100
);
484 data
->als_gain_milli
= 1000;
485 ret
= vl6180_write_byte(client
, VL6180_ALS_GAIN
, VL6180_ALS_GAIN_1
);
489 ret
= vl6180_write_byte(client
, VL6180_OUT_OF_RESET
, 0x00);
493 return vl6180_hold(data
, false);
496 static int vl6180_probe(struct i2c_client
*client
,
497 const struct i2c_device_id
*id
)
499 struct vl6180_data
*data
;
500 struct iio_dev
*indio_dev
;
503 indio_dev
= devm_iio_device_alloc(&client
->dev
, sizeof(*data
));
507 data
= iio_priv(indio_dev
);
508 i2c_set_clientdata(client
, indio_dev
);
509 data
->client
= client
;
510 mutex_init(&data
->lock
);
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
= 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");