linux/drivers/iio/proximity/pulsedlight-lidar-lite-v2.c
<<
>>
Prefs
   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
  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 *) &reg;
  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 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, 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, &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
 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                       const struct i2c_device_id *id)
 258{
 259        struct lidar_data *data;
 260        struct iio_dev *indio_dev;
 261        int ret;
 262
 263        indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data));
 264        if (!indio_dev)
 265                return -ENOMEM;
 266        data = iio_priv(indio_dev);
 267
 268        if (i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
 269                data->xfer = lidar_i2c_xfer;
 270                data->i2c_enabled = 1;
 271        } else if (i2c_check_functionality(client->adapter,
 272                                I2C_FUNC_SMBUS_WORD_DATA | I2C_FUNC_SMBUS_BYTE))
 273                data->xfer = lidar_smbus_xfer;
 274        else
 275                return -EOPNOTSUPP;
 276
 277        indio_dev->info = &lidar_info;
 278        indio_dev->name = LIDAR_DRV_NAME;
 279        indio_dev->channels = lidar_channels;
 280        indio_dev->num_channels = ARRAY_SIZE(lidar_channels);
 281        indio_dev->modes = INDIO_DIRECT_MODE;
 282
 283        i2c_set_clientdata(client, indio_dev);
 284
 285        data->client = client;
 286        data->indio_dev = indio_dev;
 287
 288        ret = iio_triggered_buffer_setup(indio_dev, NULL,
 289                                         lidar_trigger_handler, NULL);
 290        if (ret)
 291                return ret;
 292
 293        ret = iio_device_register(indio_dev);
 294        if (ret)
 295                goto error_unreg_buffer;
 296
 297        pm_runtime_set_autosuspend_delay(&client->dev, 1000);
 298        pm_runtime_use_autosuspend(&client->dev);
 299
 300        ret = pm_runtime_set_active(&client->dev);
 301        if (ret)
 302                goto error_unreg_buffer;
 303        pm_runtime_enable(&client->dev);
 304        pm_runtime_idle(&client->dev);
 305
 306        return 0;
 307
 308error_unreg_buffer:
 309        iio_triggered_buffer_cleanup(indio_dev);
 310
 311        return ret;
 312}
 313
 314static int lidar_remove(struct i2c_client *client)
 315{
 316        struct iio_dev *indio_dev = i2c_get_clientdata(client);
 317
 318        iio_device_unregister(indio_dev);
 319        iio_triggered_buffer_cleanup(indio_dev);
 320
 321        pm_runtime_disable(&client->dev);
 322        pm_runtime_set_suspended(&client->dev);
 323
 324        return 0;
 325}
 326
 327static const struct i2c_device_id lidar_id[] = {
 328        {"lidar-lite-v2", 0},
 329        {"lidar-lite-v3", 0},
 330        { },
 331};
 332MODULE_DEVICE_TABLE(i2c, lidar_id);
 333
 334static const struct of_device_id lidar_dt_ids[] = {
 335        { .compatible = "pulsedlight,lidar-lite-v2" },
 336        { .compatible = "grmn,lidar-lite-v3" },
 337        { }
 338};
 339MODULE_DEVICE_TABLE(of, lidar_dt_ids);
 340
 341#ifdef CONFIG_PM
 342static int lidar_pm_runtime_suspend(struct device *dev)
 343{
 344        struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev));
 345        struct lidar_data *data = iio_priv(indio_dev);
 346
 347        return lidar_write_power(data, 0x0f);
 348}
 349
 350static int lidar_pm_runtime_resume(struct device *dev)
 351{
 352        struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev));
 353        struct lidar_data *data = iio_priv(indio_dev);
 354        int ret = lidar_write_power(data, 0);
 355
 356        /* regulator and FPGA needs settling time */
 357        usleep_range(15000, 20000);
 358
 359        return ret;
 360}
 361#endif
 362
 363static const struct dev_pm_ops lidar_pm_ops = {
 364        SET_RUNTIME_PM_OPS(lidar_pm_runtime_suspend,
 365                           lidar_pm_runtime_resume, NULL)
 366};
 367
 368static struct i2c_driver lidar_driver = {
 369        .driver = {
 370                .name   = LIDAR_DRV_NAME,
 371                .of_match_table = lidar_dt_ids,
 372                .pm     = &lidar_pm_ops,
 373        },
 374        .probe          = lidar_probe,
 375        .remove         = lidar_remove,
 376        .id_table       = lidar_id,
 377};
 378module_i2c_driver(lidar_driver);
 379
 380MODULE_AUTHOR("Matt Ranostay <matt.ranostay@konsulko.com>");
 381MODULE_DESCRIPTION("PulsedLight LIDAR sensor");
 382MODULE_LICENSE("GPL");
 383