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, ®); 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