377 lines · c
1// SPDX-License-Identifier: GPL-2.0+2/*3 * pulsedlight-lidar-lite-v2.c - Support for PulsedLight LIDAR sensor4 *5 * Copyright (C) 2015, 2017-20186 * Author: Matt Ranostay <matt.ranostay@konsulko.com>7 *8 * TODO: interrupt mode, and signal strength reporting9 */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 0x0026#define LIDAR_REG_CONTROL_ACQUIRE BIT(2)27 28#define LIDAR_REG_STATUS 0x0129#define LIDAR_REG_STATUS_INVALID BIT(3)30#define LIDAR_REG_STATUS_READY BIT(0)31 32#define LIDAR_REG_DATA_HBYTE 0x0f33#define LIDAR_REG_DATA_LBYTE 0x1034#define LIDAR_REG_DATA_WORD_READ BIT(7)35 36#define LIDAR_REG_PWR_CONTROL 0x6537 38#define LIDAR_DRV_NAME "lidar"39 40struct 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 54static 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 69static 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(client->adapter, msg, 2);86 87 return (ret == 2) ? 0 : -EIO;88}89 90static 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 read97 * so in turn i2c_smbus_read_byte_data cannot be used98 */99 100 while (len--) {101 ret = i2c_smbus_write_byte(client, 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 119static 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 131static inline int lidar_write_control(struct lidar_data *data, int val)132{133 return i2c_smbus_write_byte_data(data->client, LIDAR_REG_CONTROL, val);134}135 136static inline int lidar_write_power(struct lidar_data *data, int val)137{138 return i2c_smbus_write_byte_data(data->client,139 LIDAR_REG_PWR_CONTROL, val);140}141 142static 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 155static 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(&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(&client->dev);170 return ret;171 }172 173 while (tries--) {174 usleep_range(1000, 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(&client->dev);195 pm_runtime_put_autosuspend(&client->dev);196 197 return ret;198}199 200static 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, ®);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 232static 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, &data->scan.chan);240 if (!ret) {241 iio_push_to_buffers_with_timestamp(indio_dev, &data->scan,242 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(indio_dev->trig);248 249 return IRQ_HANDLED;250}251 252static const struct iio_info lidar_info = {253 .read_raw = lidar_read_raw,254};255 256static 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(&client->dev, sizeof(*data));263 if (!indio_dev)264 return -ENOMEM;265 data = iio_priv(indio_dev);266 267 if (i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {268 data->xfer = lidar_i2c_xfer;269 data->i2c_enabled = 1;270 } else if (i2c_check_functionality(client->adapter,271 I2C_FUNC_SMBUS_WORD_DATA | I2C_FUNC_SMBUS_BYTE))272 data->xfer = lidar_smbus_xfer;273 else274 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, 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(&client->dev, 1000);297 pm_runtime_use_autosuspend(&client->dev);298 299 ret = pm_runtime_set_active(&client->dev);300 if (ret)301 goto error_unreg_buffer;302 pm_runtime_enable(&client->dev);303 pm_runtime_idle(&client->dev);304 305 return 0;306 307error_unreg_buffer:308 iio_triggered_buffer_cleanup(indio_dev);309 310 return ret;311}312 313static 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(&client->dev);321 pm_runtime_set_suspended(&client->dev);322}323 324static const struct i2c_device_id lidar_id[] = {325 { "lidar-lite-v2" },326 { "lidar-lite-v3" },327 { }328};329MODULE_DEVICE_TABLE(i2c, lidar_id);330 331static const struct of_device_id lidar_dt_ids[] = {332 { .compatible = "pulsedlight,lidar-lite-v2" },333 { .compatible = "grmn,lidar-lite-v3" },334 { }335};336MODULE_DEVICE_TABLE(of, lidar_dt_ids);337 338static 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, 0x0f);344}345 346static 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, 0);351 352 /* regulator and FPGA needs settling time */353 usleep_range(15000, 20000);354 355 return ret;356}357 358static const struct dev_pm_ops lidar_pm_ops = {359 RUNTIME_PM_OPS(lidar_pm_runtime_suspend, lidar_pm_runtime_resume, NULL)360};361 362static 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};372module_i2c_driver(lidar_driver);373 374MODULE_AUTHOR("Matt Ranostay <matt.ranostay@konsulko.com>");375MODULE_DESCRIPTION("PulsedLight LIDAR sensor");376MODULE_LICENSE("GPL");377