1/*
2 * pulsedlight-lidar-lite-v2.c - Support for PulsedLight LIDAR sensor
3 *
4 * Copyright (C) 2015 Matt Ranostay <mranostay@gmail.com>
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * TODO: runtime pm, interrupt mode, and signal strength reporting
17 */
18
19#include <linux/err.h>
20#include <linux/init.h>
21#include <linux/i2c.h>
22#include <linux/delay.h>
23#include <linux/module.h>
24#include <linux/iio/iio.h>
25#include <linux/iio/sysfs.h>
26#include <linux/iio/buffer.h>
27#include <linux/iio/trigger.h>
28#include <linux/iio/triggered_buffer.h>
29#include <linux/iio/trigger_consumer.h>
30
31#define LIDAR_REG_CONTROL		0x00
32#define LIDAR_REG_CONTROL_ACQUIRE	BIT(2)
33
34#define LIDAR_REG_STATUS		0x01
35#define LIDAR_REG_STATUS_INVALID	BIT(3)
36#define LIDAR_REG_STATUS_READY		BIT(0)
37
38#define LIDAR_REG_DATA_HBYTE	0x0f
39#define LIDAR_REG_DATA_LBYTE	0x10
40
41#define LIDAR_DRV_NAME "lidar"
42
43struct lidar_data {
44	struct iio_dev *indio_dev;
45	struct i2c_client *client;
46
47	u16 buffer[8]; /* 2 byte distance + 8 byte timestamp */
48};
49
50static const struct iio_chan_spec lidar_channels[] = {
51	{
52		.type = IIO_DISTANCE,
53		.info_mask_separate =
54			BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE),
55		.scan_index = 0,
56		.scan_type = {
57			.sign = 'u',
58			.realbits = 16,
59			.storagebits = 16,
60		},
61	},
62	IIO_CHAN_SOFT_TIMESTAMP(1),
63};
64
65static int lidar_read_byte(struct lidar_data *data, int reg)
66{
67	struct i2c_client *client = data->client;
68	int ret;
69
70	/*
71	 * Device needs a STOP condition between address write, and data read
72	 * so in turn i2c_smbus_read_byte_data cannot be used
73	 */
74
75	ret = i2c_smbus_write_byte(client, reg);
76	if (ret < 0) {
77		dev_err(&client->dev, "cannot write addr value");
78		return ret;
79	}
80
81	ret = i2c_smbus_read_byte(client);
82	if (ret < 0)
83		dev_err(&client->dev, "cannot read data value");
84
85	return ret;
86}
87
88static inline int lidar_write_control(struct lidar_data *data, int val)
89{
90	return i2c_smbus_write_byte_data(data->client, LIDAR_REG_CONTROL, val);
91}
92
93static int lidar_read_measurement(struct lidar_data *data, u16 *reg)
94{
95	int ret;
96	int val;
97
98	ret = lidar_read_byte(data, LIDAR_REG_DATA_HBYTE);
99	if (ret < 0)
100		return ret;
101	val = ret << 8;
102
103	ret = lidar_read_byte(data, LIDAR_REG_DATA_LBYTE);
104	if (ret < 0)
105		return ret;
106
107	val |= ret;
108	*reg = val;
109
110	return 0;
111}
112
113static int lidar_get_measurement(struct lidar_data *data, u16 *reg)
114{
115	struct i2c_client *client = data->client;
116	int tries = 10;
117	int ret;
118
119	/* start sample */
120	ret = lidar_write_control(data, LIDAR_REG_CONTROL_ACQUIRE);
121	if (ret < 0) {
122		dev_err(&client->dev, "cannot send start measurement command");
123		return ret;
124	}
125
126	while (tries--) {
127		usleep_range(1000, 2000);
128
129		ret = lidar_read_byte(data, LIDAR_REG_STATUS);
130		if (ret < 0)
131			break;
132
133		/* return -EINVAL since laser is likely pointed out of range */
134		if (ret & LIDAR_REG_STATUS_INVALID) {
135			*reg = 0;
136			ret = -EINVAL;
137			break;
138		}
139
140		/* sample ready to read */
141		if (!(ret & LIDAR_REG_STATUS_READY)) {
142			ret = lidar_read_measurement(data, reg);
143			break;
144		}
145		ret = -EIO;
146	}
147
148	return ret;
149}
150
151static int lidar_read_raw(struct iio_dev *indio_dev,
152			  struct iio_chan_spec const *chan,
153			  int *val, int *val2, long mask)
154{
155	struct lidar_data *data = iio_priv(indio_dev);
156	int ret = -EINVAL;
157
158	mutex_lock(&indio_dev->mlock);
159
160	if (iio_buffer_enabled(indio_dev) && mask == IIO_CHAN_INFO_RAW) {
161		ret = -EBUSY;
162		goto error_busy;
163	}
164
165	switch (mask) {
166	case IIO_CHAN_INFO_RAW: {
167		u16 reg;
168
169		ret = lidar_get_measurement(data, &reg);
170		if (!ret) {
171			*val = reg;
172			ret = IIO_VAL_INT;
173		}
174		break;
175	}
176	case IIO_CHAN_INFO_SCALE:
177		*val = 0;
178		*val2 = 10000;
179		ret = IIO_VAL_INT_PLUS_MICRO;
180		break;
181	}
182
183error_busy:
184	mutex_unlock(&indio_dev->mlock);
185
186	return ret;
187}
188
189static irqreturn_t lidar_trigger_handler(int irq, void *private)
190{
191	struct iio_poll_func *pf = private;
192	struct iio_dev *indio_dev = pf->indio_dev;
193	struct lidar_data *data = iio_priv(indio_dev);
194	int ret;
195
196	ret = lidar_get_measurement(data, data->buffer);
197	if (!ret) {
198		iio_push_to_buffers_with_timestamp(indio_dev, data->buffer,
199						   iio_get_time_ns());
200	} else if (ret != -EINVAL) {
201		dev_err(&data->client->dev, "cannot read LIDAR measurement");
202	}
203
204	iio_trigger_notify_done(indio_dev->trig);
205
206	return IRQ_HANDLED;
207}
208
209static const struct iio_info lidar_info = {
210	.driver_module = THIS_MODULE,
211	.read_raw = lidar_read_raw,
212};
213
214static int lidar_probe(struct i2c_client *client,
215		       const struct i2c_device_id *id)
216{
217	struct lidar_data *data;
218	struct iio_dev *indio_dev;
219	int ret;
220
221	indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data));
222	if (!indio_dev)
223		return -ENOMEM;
224
225	indio_dev->info = &lidar_info;
226	indio_dev->name = LIDAR_DRV_NAME;
227	indio_dev->channels = lidar_channels;
228	indio_dev->num_channels = ARRAY_SIZE(lidar_channels);
229	indio_dev->modes = INDIO_DIRECT_MODE;
230
231	data = iio_priv(indio_dev);
232	i2c_set_clientdata(client, indio_dev);
233
234	data->client = client;
235	data->indio_dev = indio_dev;
236
237	ret = iio_triggered_buffer_setup(indio_dev, NULL,
238					 lidar_trigger_handler, NULL);
239	if (ret)
240		return ret;
241
242	ret = iio_device_register(indio_dev);
243	if (ret)
244		goto error_unreg_buffer;
245
246	return 0;
247
248error_unreg_buffer:
249	iio_triggered_buffer_cleanup(indio_dev);
250
251	return ret;
252}
253
254static int lidar_remove(struct i2c_client *client)
255{
256	struct iio_dev *indio_dev = i2c_get_clientdata(client);
257
258	iio_device_unregister(indio_dev);
259	iio_triggered_buffer_cleanup(indio_dev);
260
261	return 0;
262}
263
264static const struct i2c_device_id lidar_id[] = {
265	{"lidar-lite-v2", 0},
266	{ },
267};
268MODULE_DEVICE_TABLE(i2c, lidar_id);
269
270static const struct of_device_id lidar_dt_ids[] = {
271	{ .compatible = "pulsedlight,lidar-lite-v2" },
272	{ }
273};
274MODULE_DEVICE_TABLE(of, lidar_dt_ids);
275
276static struct i2c_driver lidar_driver = {
277	.driver = {
278		.name	= LIDAR_DRV_NAME,
279		.of_match_table	= of_match_ptr(lidar_dt_ids),
280	},
281	.probe		= lidar_probe,
282	.remove		= lidar_remove,
283	.id_table	= lidar_id,
284};
285module_i2c_driver(lidar_driver);
286
287MODULE_AUTHOR("Matt Ranostay <mranostay@gmail.com>");
288MODULE_DESCRIPTION("PulsedLight LIDAR sensor");
289MODULE_LICENSE("GPL");
290