blob: 5fbda9475ba90f0a5299127dc50a010cde0803b1 [file] [log] [blame]
Song Qiang6eb17c62018-09-18 16:24:21 +08001// SPDX-License-Identifier: GPL-2.0
2/*
3 * Support for ST VL53L0X FlightSense ToF Ranging Sensor on a i2c bus.
4 *
5 * Copyright (C) 2016 STMicroelectronics Imaging Division.
6 * Copyright (C) 2018 Song Qiang <songqiang1304521@gmail.com>
7 *
8 * Datasheet available at
9 * <https://www.st.com/resource/en/datasheet/vl53l0x.pdf>
10 *
11 * Default 7-bit i2c slave address 0x29.
12 *
13 * TODO: FIFO buffer, continuous mode, interrupts, range selection,
14 * sensor ID check.
15 */
16
17#include <linux/delay.h>
18#include <linux/i2c.h>
19#include <linux/module.h>
20
21#include <linux/iio/iio.h>
22
23#define VL_REG_SYSRANGE_START 0x00
24
25#define VL_REG_SYSRANGE_MODE_MASK GENMASK(3, 0)
26#define VL_REG_SYSRANGE_MODE_SINGLESHOT 0x00
27#define VL_REG_SYSRANGE_MODE_START_STOP BIT(0)
28#define VL_REG_SYSRANGE_MODE_BACKTOBACK BIT(1)
29#define VL_REG_SYSRANGE_MODE_TIMED BIT(2)
30#define VL_REG_SYSRANGE_MODE_HISTOGRAM BIT(3)
31
32#define VL_REG_RESULT_INT_STATUS 0x13
33#define VL_REG_RESULT_RANGE_STATUS 0x14
34#define VL_REG_RESULT_RANGE_STATUS_COMPLETE BIT(0)
35
36struct vl53l0x_data {
37 struct i2c_client *client;
38};
39
40static int vl53l0x_read_proximity(struct vl53l0x_data *data,
41 const struct iio_chan_spec *chan,
42 int *val)
43{
44 struct i2c_client *client = data->client;
45 u16 tries = 20;
46 u8 buffer[12];
47 int ret;
48
49 ret = i2c_smbus_write_byte_data(client, VL_REG_SYSRANGE_START, 1);
50 if (ret < 0)
51 return ret;
52
53 do {
54 ret = i2c_smbus_read_byte_data(client,
55 VL_REG_RESULT_RANGE_STATUS);
56 if (ret < 0)
57 return ret;
58
59 if (ret & VL_REG_RESULT_RANGE_STATUS_COMPLETE)
60 break;
61
62 usleep_range(1000, 5000);
63 } while (--tries);
64 if (!tries)
65 return -ETIMEDOUT;
66
67 ret = i2c_smbus_read_i2c_block_data(client, VL_REG_RESULT_RANGE_STATUS,
68 12, buffer);
69 if (ret < 0)
70 return ret;
71 else if (ret != 12)
72 return -EREMOTEIO;
73
74 /* Values should be between 30~1200 in millimeters. */
75 *val = (buffer[10] << 8) + buffer[11];
76
77 return 0;
78}
79
80static const struct iio_chan_spec vl53l0x_channels[] = {
81 {
82 .type = IIO_DISTANCE,
83 .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
84 BIT(IIO_CHAN_INFO_SCALE),
85 },
86};
87
88static int vl53l0x_read_raw(struct iio_dev *indio_dev,
89 const struct iio_chan_spec *chan,
90 int *val, int *val2, long mask)
91{
92 struct vl53l0x_data *data = iio_priv(indio_dev);
93 int ret;
94
95 if (chan->type != IIO_DISTANCE)
96 return -EINVAL;
97
98 switch (mask) {
99 case IIO_CHAN_INFO_RAW:
100 ret = vl53l0x_read_proximity(data, chan, val);
101 if (ret < 0)
102 return ret;
103
104 return IIO_VAL_INT;
105 case IIO_CHAN_INFO_SCALE:
106 *val = 0;
107 *val2 = 1000;
108
109 return IIO_VAL_INT_PLUS_MICRO;
110 default:
111 return -EINVAL;
112 }
113}
114
115static const struct iio_info vl53l0x_info = {
116 .read_raw = vl53l0x_read_raw,
117};
118
119static int vl53l0x_probe(struct i2c_client *client)
120{
121 struct vl53l0x_data *data;
122 struct iio_dev *indio_dev;
123
124 indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data));
125 if (!indio_dev)
126 return -ENOMEM;
127
128 data = iio_priv(indio_dev);
129 data->client = client;
130 i2c_set_clientdata(client, indio_dev);
131
132 if (!i2c_check_functionality(client->adapter,
133 I2C_FUNC_SMBUS_READ_I2C_BLOCK |
134 I2C_FUNC_SMBUS_BYTE_DATA))
135 return -EOPNOTSUPP;
136
Song Qiang6eb17c62018-09-18 16:24:21 +0800137 indio_dev->name = "vl53l0x";
138 indio_dev->info = &vl53l0x_info;
139 indio_dev->channels = vl53l0x_channels;
140 indio_dev->num_channels = ARRAY_SIZE(vl53l0x_channels);
141 indio_dev->modes = INDIO_DIRECT_MODE;
142
143 return devm_iio_device_register(&client->dev, indio_dev);
144}
145
146static const struct of_device_id st_vl53l0x_dt_match[] = {
147 { .compatible = "st,vl53l0x", },
148 { }
149};
150MODULE_DEVICE_TABLE(of, st_vl53l0x_dt_match);
151
152static struct i2c_driver vl53l0x_driver = {
153 .driver = {
154 .name = "vl53l0x-i2c",
155 .of_match_table = st_vl53l0x_dt_match,
156 },
157 .probe_new = vl53l0x_probe,
158};
159module_i2c_driver(vl53l0x_driver);
160
161MODULE_AUTHOR("Song Qiang <songqiang1304521@gmail.com>");
162MODULE_DESCRIPTION("ST vl53l0x ToF ranging sensor driver");
163MODULE_LICENSE("GPL v2");