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