1 | // SPDX-License-Identifier: GPL-2.0+ |
2 | /* |
3 | * pulsedlight-lidar-lite-v2.c - Support for PulsedLight LIDAR sensor |
4 | * |
5 | * Copyright (C) 2015, 2017-2018 |
6 | * Author: Matt Ranostay <matt.ranostay@konsulko.com> |
7 | * |
8 | * TODO: interrupt mode, and signal strength reporting |
9 | */ |
10 | |
11 | #include <linux/err.h> |
12 | #include <linux/init.h> |
13 | #include <linux/i2c.h> |
14 | #include <linux/delay.h> |
15 | #include <linux/module.h> |
16 | #include <linux/mod_devicetable.h> |
17 | #include <linux/pm_runtime.h> |
18 | #include <linux/iio/iio.h> |
19 | #include <linux/iio/sysfs.h> |
20 | #include <linux/iio/buffer.h> |
21 | #include <linux/iio/trigger.h> |
22 | #include <linux/iio/triggered_buffer.h> |
23 | #include <linux/iio/trigger_consumer.h> |
24 | |
25 | #define LIDAR_REG_CONTROL 0x00 |
26 | #define LIDAR_REG_CONTROL_ACQUIRE BIT(2) |
27 | |
28 | #define LIDAR_REG_STATUS 0x01 |
29 | #define LIDAR_REG_STATUS_INVALID BIT(3) |
30 | #define LIDAR_REG_STATUS_READY BIT(0) |
31 | |
32 | #define LIDAR_REG_DATA_HBYTE 0x0f |
33 | #define LIDAR_REG_DATA_LBYTE 0x10 |
34 | #define LIDAR_REG_DATA_WORD_READ BIT(7) |
35 | |
36 | #define LIDAR_REG_PWR_CONTROL 0x65 |
37 | |
38 | #define LIDAR_DRV_NAME "lidar" |
39 | |
40 | struct lidar_data { |
41 | struct iio_dev *indio_dev; |
42 | struct i2c_client *client; |
43 | |
44 | int (*xfer)(struct lidar_data *data, u8 reg, u8 *val, int len); |
45 | int i2c_enabled; |
46 | |
47 | /* Ensure timestamp is naturally aligned */ |
48 | struct { |
49 | u16 chan; |
50 | s64 timestamp __aligned(8); |
51 | } scan; |
52 | }; |
53 | |
54 | static const struct iio_chan_spec lidar_channels[] = { |
55 | { |
56 | .type = IIO_DISTANCE, |
57 | .info_mask_separate = |
58 | BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE), |
59 | .scan_index = 0, |
60 | .scan_type = { |
61 | .sign = 'u', |
62 | .realbits = 16, |
63 | .storagebits = 16, |
64 | }, |
65 | }, |
66 | IIO_CHAN_SOFT_TIMESTAMP(1), |
67 | }; |
68 | |
69 | static int lidar_i2c_xfer(struct lidar_data *data, u8 reg, u8 *val, int len) |
70 | { |
71 | struct i2c_client *client = data->client; |
72 | struct i2c_msg msg[2]; |
73 | int ret; |
74 | |
75 | msg[0].addr = client->addr; |
76 | msg[0].flags = client->flags | I2C_M_STOP; |
77 | msg[0].len = 1; |
78 | msg[0].buf = (char *) ® |
79 | |
80 | msg[1].addr = client->addr; |
81 | msg[1].flags = client->flags | I2C_M_RD; |
82 | msg[1].len = len; |
83 | msg[1].buf = (char *) val; |
84 | |
85 | ret = i2c_transfer(adap: client->adapter, msgs: msg, num: 2); |
86 | |
87 | return (ret == 2) ? 0 : -EIO; |
88 | } |
89 | |
90 | static int lidar_smbus_xfer(struct lidar_data *data, u8 reg, u8 *val, int len) |
91 | { |
92 | struct i2c_client *client = data->client; |
93 | int ret; |
94 | |
95 | /* |
96 | * Device needs a STOP condition between address write, and data read |
97 | * so in turn i2c_smbus_read_byte_data cannot be used |
98 | */ |
99 | |
100 | while (len--) { |
101 | ret = i2c_smbus_write_byte(client, value: reg++); |
102 | if (ret < 0) { |
103 | dev_err(&client->dev, "cannot write addr value" ); |
104 | return ret; |
105 | } |
106 | |
107 | ret = i2c_smbus_read_byte(client); |
108 | if (ret < 0) { |
109 | dev_err(&client->dev, "cannot read data value" ); |
110 | return ret; |
111 | } |
112 | |
113 | *(val++) = ret; |
114 | } |
115 | |
116 | return 0; |
117 | } |
118 | |
119 | static int lidar_read_byte(struct lidar_data *data, u8 reg) |
120 | { |
121 | int ret; |
122 | u8 val; |
123 | |
124 | ret = data->xfer(data, reg, &val, 1); |
125 | if (ret < 0) |
126 | return ret; |
127 | |
128 | return val; |
129 | } |
130 | |
131 | static inline int lidar_write_control(struct lidar_data *data, int val) |
132 | { |
133 | return i2c_smbus_write_byte_data(client: data->client, LIDAR_REG_CONTROL, value: val); |
134 | } |
135 | |
136 | static inline int lidar_write_power(struct lidar_data *data, int val) |
137 | { |
138 | return i2c_smbus_write_byte_data(client: data->client, |
139 | LIDAR_REG_PWR_CONTROL, value: val); |
140 | } |
141 | |
142 | static int lidar_read_measurement(struct lidar_data *data, u16 *reg) |
143 | { |
144 | __be16 value; |
145 | int ret = data->xfer(data, LIDAR_REG_DATA_HBYTE | |
146 | (data->i2c_enabled ? LIDAR_REG_DATA_WORD_READ : 0), |
147 | (u8 *) &value, 2); |
148 | |
149 | if (!ret) |
150 | *reg = be16_to_cpu(value); |
151 | |
152 | return ret; |
153 | } |
154 | |
155 | static int lidar_get_measurement(struct lidar_data *data, u16 *reg) |
156 | { |
157 | struct i2c_client *client = data->client; |
158 | int tries = 10; |
159 | int ret; |
160 | |
161 | ret = pm_runtime_resume_and_get(dev: &client->dev); |
162 | if (ret < 0) |
163 | return ret; |
164 | |
165 | /* start sample */ |
166 | ret = lidar_write_control(data, LIDAR_REG_CONTROL_ACQUIRE); |
167 | if (ret < 0) { |
168 | dev_err(&client->dev, "cannot send start measurement command" ); |
169 | pm_runtime_put_noidle(dev: &client->dev); |
170 | return ret; |
171 | } |
172 | |
173 | while (tries--) { |
174 | usleep_range(min: 1000, max: 2000); |
175 | |
176 | ret = lidar_read_byte(data, LIDAR_REG_STATUS); |
177 | if (ret < 0) |
178 | break; |
179 | |
180 | /* return -EINVAL since laser is likely pointed out of range */ |
181 | if (ret & LIDAR_REG_STATUS_INVALID) { |
182 | *reg = 0; |
183 | ret = -EINVAL; |
184 | break; |
185 | } |
186 | |
187 | /* sample ready to read */ |
188 | if (!(ret & LIDAR_REG_STATUS_READY)) { |
189 | ret = lidar_read_measurement(data, reg); |
190 | break; |
191 | } |
192 | ret = -EIO; |
193 | } |
194 | pm_runtime_mark_last_busy(dev: &client->dev); |
195 | pm_runtime_put_autosuspend(dev: &client->dev); |
196 | |
197 | return ret; |
198 | } |
199 | |
200 | static int lidar_read_raw(struct iio_dev *indio_dev, |
201 | struct iio_chan_spec const *chan, |
202 | int *val, int *val2, long mask) |
203 | { |
204 | struct lidar_data *data = iio_priv(indio_dev); |
205 | int ret = -EINVAL; |
206 | |
207 | switch (mask) { |
208 | case IIO_CHAN_INFO_RAW: { |
209 | u16 reg; |
210 | |
211 | if (iio_device_claim_direct_mode(indio_dev)) |
212 | return -EBUSY; |
213 | |
214 | ret = lidar_get_measurement(data, reg: ®); |
215 | if (!ret) { |
216 | *val = reg; |
217 | ret = IIO_VAL_INT; |
218 | } |
219 | iio_device_release_direct_mode(indio_dev); |
220 | break; |
221 | } |
222 | case IIO_CHAN_INFO_SCALE: |
223 | *val = 0; |
224 | *val2 = 10000; |
225 | ret = IIO_VAL_INT_PLUS_MICRO; |
226 | break; |
227 | } |
228 | |
229 | return ret; |
230 | } |
231 | |
232 | static irqreturn_t lidar_trigger_handler(int irq, void *private) |
233 | { |
234 | struct iio_poll_func *pf = private; |
235 | struct iio_dev *indio_dev = pf->indio_dev; |
236 | struct lidar_data *data = iio_priv(indio_dev); |
237 | int ret; |
238 | |
239 | ret = lidar_get_measurement(data, reg: &data->scan.chan); |
240 | if (!ret) { |
241 | iio_push_to_buffers_with_timestamp(indio_dev, data: &data->scan, |
242 | timestamp: iio_get_time_ns(indio_dev)); |
243 | } else if (ret != -EINVAL) { |
244 | dev_err(&data->client->dev, "cannot read LIDAR measurement" ); |
245 | } |
246 | |
247 | iio_trigger_notify_done(trig: indio_dev->trig); |
248 | |
249 | return IRQ_HANDLED; |
250 | } |
251 | |
252 | static const struct iio_info lidar_info = { |
253 | .read_raw = lidar_read_raw, |
254 | }; |
255 | |
256 | static int lidar_probe(struct i2c_client *client) |
257 | { |
258 | struct lidar_data *data; |
259 | struct iio_dev *indio_dev; |
260 | int ret; |
261 | |
262 | indio_dev = devm_iio_device_alloc(parent: &client->dev, sizeof_priv: sizeof(*data)); |
263 | if (!indio_dev) |
264 | return -ENOMEM; |
265 | data = iio_priv(indio_dev); |
266 | |
267 | if (i2c_check_functionality(adap: client->adapter, I2C_FUNC_I2C)) { |
268 | data->xfer = lidar_i2c_xfer; |
269 | data->i2c_enabled = 1; |
270 | } else if (i2c_check_functionality(adap: client->adapter, |
271 | I2C_FUNC_SMBUS_WORD_DATA | I2C_FUNC_SMBUS_BYTE)) |
272 | data->xfer = lidar_smbus_xfer; |
273 | else |
274 | return -EOPNOTSUPP; |
275 | |
276 | indio_dev->info = &lidar_info; |
277 | indio_dev->name = LIDAR_DRV_NAME; |
278 | indio_dev->channels = lidar_channels; |
279 | indio_dev->num_channels = ARRAY_SIZE(lidar_channels); |
280 | indio_dev->modes = INDIO_DIRECT_MODE; |
281 | |
282 | i2c_set_clientdata(client, data: indio_dev); |
283 | |
284 | data->client = client; |
285 | data->indio_dev = indio_dev; |
286 | |
287 | ret = iio_triggered_buffer_setup(indio_dev, NULL, |
288 | lidar_trigger_handler, NULL); |
289 | if (ret) |
290 | return ret; |
291 | |
292 | ret = iio_device_register(indio_dev); |
293 | if (ret) |
294 | goto error_unreg_buffer; |
295 | |
296 | pm_runtime_set_autosuspend_delay(dev: &client->dev, delay: 1000); |
297 | pm_runtime_use_autosuspend(dev: &client->dev); |
298 | |
299 | ret = pm_runtime_set_active(dev: &client->dev); |
300 | if (ret) |
301 | goto error_unreg_buffer; |
302 | pm_runtime_enable(dev: &client->dev); |
303 | pm_runtime_idle(dev: &client->dev); |
304 | |
305 | return 0; |
306 | |
307 | error_unreg_buffer: |
308 | iio_triggered_buffer_cleanup(indio_dev); |
309 | |
310 | return ret; |
311 | } |
312 | |
313 | static void lidar_remove(struct i2c_client *client) |
314 | { |
315 | struct iio_dev *indio_dev = i2c_get_clientdata(client); |
316 | |
317 | iio_device_unregister(indio_dev); |
318 | iio_triggered_buffer_cleanup(indio_dev); |
319 | |
320 | pm_runtime_disable(dev: &client->dev); |
321 | pm_runtime_set_suspended(dev: &client->dev); |
322 | } |
323 | |
324 | static const struct i2c_device_id lidar_id[] = { |
325 | {"lidar-lite-v2" , 0}, |
326 | {"lidar-lite-v3" , 0}, |
327 | { }, |
328 | }; |
329 | MODULE_DEVICE_TABLE(i2c, lidar_id); |
330 | |
331 | static const struct of_device_id lidar_dt_ids[] = { |
332 | { .compatible = "pulsedlight,lidar-lite-v2" }, |
333 | { .compatible = "grmn,lidar-lite-v3" }, |
334 | { } |
335 | }; |
336 | MODULE_DEVICE_TABLE(of, lidar_dt_ids); |
337 | |
338 | static int lidar_pm_runtime_suspend(struct device *dev) |
339 | { |
340 | struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev)); |
341 | struct lidar_data *data = iio_priv(indio_dev); |
342 | |
343 | return lidar_write_power(data, val: 0x0f); |
344 | } |
345 | |
346 | static int lidar_pm_runtime_resume(struct device *dev) |
347 | { |
348 | struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev)); |
349 | struct lidar_data *data = iio_priv(indio_dev); |
350 | int ret = lidar_write_power(data, val: 0); |
351 | |
352 | /* regulator and FPGA needs settling time */ |
353 | usleep_range(min: 15000, max: 20000); |
354 | |
355 | return ret; |
356 | } |
357 | |
358 | static const struct dev_pm_ops lidar_pm_ops = { |
359 | RUNTIME_PM_OPS(lidar_pm_runtime_suspend, lidar_pm_runtime_resume, NULL) |
360 | }; |
361 | |
362 | static struct i2c_driver lidar_driver = { |
363 | .driver = { |
364 | .name = LIDAR_DRV_NAME, |
365 | .of_match_table = lidar_dt_ids, |
366 | .pm = pm_ptr(&lidar_pm_ops), |
367 | }, |
368 | .probe = lidar_probe, |
369 | .remove = lidar_remove, |
370 | .id_table = lidar_id, |
371 | }; |
372 | module_i2c_driver(lidar_driver); |
373 | |
374 | MODULE_AUTHOR("Matt Ranostay <matt.ranostay@konsulko.com>" ); |
375 | MODULE_DESCRIPTION("PulsedLight LIDAR sensor" ); |
376 | MODULE_LICENSE("GPL" ); |
377 | |