linux/drivers/iio/proximity/rfd77402.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0-only
   2/*
   3 * rfd77402.c - Support for RF Digital RFD77402 Time-of-Flight (distance) sensor
   4 *
   5 * Copyright 2017 Peter Meerwald-Stadler <pmeerw@pmeerw.net>
   6 *
   7 * 7-bit I2C slave address 0x4c
   8 *
   9 * TODO: interrupt
  10 * https://media.digikey.com/pdf/Data%20Sheets/RF%20Digital%20PDFs/RFD77402.pdf
  11 */
  12
  13#include <linux/module.h>
  14#include <linux/i2c.h>
  15#include <linux/delay.h>
  16
  17#include <linux/iio/iio.h>
  18
  19#define RFD77402_DRV_NAME "rfd77402"
  20
  21#define RFD77402_ICSR           0x00 /* Interrupt Control Status Register */
  22#define RFD77402_ICSR_INT_MODE  BIT(2)
  23#define RFD77402_ICSR_INT_POL   BIT(3)
  24#define RFD77402_ICSR_RESULT    BIT(4)
  25#define RFD77402_ICSR_M2H_MSG   BIT(5)
  26#define RFD77402_ICSR_H2M_MSG   BIT(6)
  27#define RFD77402_ICSR_RESET     BIT(7)
  28
  29#define RFD77402_CMD_R          0x04
  30#define RFD77402_CMD_SINGLE     0x01
  31#define RFD77402_CMD_STANDBY    0x10
  32#define RFD77402_CMD_MCPU_OFF   0x11
  33#define RFD77402_CMD_MCPU_ON    0x12
  34#define RFD77402_CMD_RESET      BIT(6)
  35#define RFD77402_CMD_VALID      BIT(7)
  36
  37#define RFD77402_STATUS_R       0x06
  38#define RFD77402_STATUS_PM_MASK GENMASK(4, 0)
  39#define RFD77402_STATUS_STANDBY 0x00
  40#define RFD77402_STATUS_MCPU_OFF        0x10
  41#define RFD77402_STATUS_MCPU_ON 0x18
  42
  43#define RFD77402_RESULT_R       0x08
  44#define RFD77402_RESULT_DIST_MASK       GENMASK(12, 2)
  45#define RFD77402_RESULT_ERR_MASK        GENMASK(14, 13)
  46#define RFD77402_RESULT_VALID   BIT(15)
  47
  48#define RFD77402_PMU_CFG        0x14
  49#define RFD77402_PMU_MCPU_INIT  BIT(9)
  50
  51#define RFD77402_I2C_INIT_CFG   0x1c
  52#define RFD77402_I2C_ADDR_INCR  BIT(0)
  53#define RFD77402_I2C_DATA_INCR  BIT(2)
  54#define RFD77402_I2C_HOST_DEBUG BIT(5)
  55#define RFD77402_I2C_MCPU_DEBUG BIT(6)
  56
  57#define RFD77402_CMD_CFGR_A     0x0c
  58#define RFD77402_CMD_CFGR_B     0x0e
  59#define RFD77402_HFCFG_0        0x20
  60#define RFD77402_HFCFG_1        0x22
  61#define RFD77402_HFCFG_2        0x24
  62#define RFD77402_HFCFG_3        0x26
  63
  64#define RFD77402_MOD_CHIP_ID    0x28
  65
  66/* magic configuration values from datasheet */
  67static const struct {
  68        u8 reg;
  69        u16 val;
  70} rf77402_tof_config[] = {
  71        {RFD77402_CMD_CFGR_A,   0xe100},
  72        {RFD77402_CMD_CFGR_B,   0x10ff},
  73        {RFD77402_HFCFG_0,      0x07d0},
  74        {RFD77402_HFCFG_1,      0x5008},
  75        {RFD77402_HFCFG_2,      0xa041},
  76        {RFD77402_HFCFG_3,      0x45d4},
  77};
  78
  79struct rfd77402_data {
  80        struct i2c_client *client;
  81        /* Serialize reads from the sensor */
  82        struct mutex lock;
  83};
  84
  85static const struct iio_chan_spec rfd77402_channels[] = {
  86        {
  87                .type = IIO_DISTANCE,
  88                .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
  89                                      BIT(IIO_CHAN_INFO_SCALE),
  90        },
  91};
  92
  93static int rfd77402_set_state(struct rfd77402_data *data, u8 state, u16 check)
  94{
  95        int ret;
  96
  97        ret = i2c_smbus_write_byte_data(data->client, RFD77402_CMD_R,
  98                                        state | RFD77402_CMD_VALID);
  99        if (ret < 0)
 100                return ret;
 101
 102        usleep_range(10000, 20000);
 103
 104        ret = i2c_smbus_read_word_data(data->client, RFD77402_STATUS_R);
 105        if (ret < 0)
 106                return ret;
 107        if ((ret & RFD77402_STATUS_PM_MASK) != check)
 108                return -ENODEV;
 109
 110        return 0;
 111}
 112
 113static int rfd77402_measure(struct rfd77402_data *data)
 114{
 115        int ret;
 116        int tries = 10;
 117
 118        ret = rfd77402_set_state(data, RFD77402_CMD_MCPU_ON,
 119                                 RFD77402_STATUS_MCPU_ON);
 120        if (ret < 0)
 121                return ret;
 122
 123        ret = i2c_smbus_write_byte_data(data->client, RFD77402_CMD_R,
 124                                        RFD77402_CMD_SINGLE |
 125                                        RFD77402_CMD_VALID);
 126        if (ret < 0)
 127                goto err;
 128
 129        while (tries-- > 0) {
 130                ret = i2c_smbus_read_byte_data(data->client, RFD77402_ICSR);
 131                if (ret < 0)
 132                        goto err;
 133                if (ret & RFD77402_ICSR_RESULT)
 134                        break;
 135                msleep(20);
 136        }
 137
 138        if (tries < 0) {
 139                ret = -ETIMEDOUT;
 140                goto err;
 141        }
 142
 143        ret = i2c_smbus_read_word_data(data->client, RFD77402_RESULT_R);
 144        if (ret < 0)
 145                goto err;
 146
 147        if ((ret & RFD77402_RESULT_ERR_MASK) ||
 148            !(ret & RFD77402_RESULT_VALID)) {
 149                ret = -EIO;
 150                goto err;
 151        }
 152
 153        return (ret & RFD77402_RESULT_DIST_MASK) >> 2;
 154
 155err:
 156        rfd77402_set_state(data, RFD77402_CMD_MCPU_OFF,
 157                           RFD77402_STATUS_MCPU_OFF);
 158        return ret;
 159}
 160
 161static int rfd77402_read_raw(struct iio_dev *indio_dev,
 162                             struct iio_chan_spec const *chan,
 163                             int *val, int *val2, long mask)
 164{
 165        struct rfd77402_data *data = iio_priv(indio_dev);
 166        int ret;
 167
 168        switch (mask) {
 169        case IIO_CHAN_INFO_RAW:
 170                mutex_lock(&data->lock);
 171                ret = rfd77402_measure(data);
 172                mutex_unlock(&data->lock);
 173                if (ret < 0)
 174                        return ret;
 175                *val = ret;
 176                return IIO_VAL_INT;
 177        case IIO_CHAN_INFO_SCALE:
 178                /* 1 LSB is 1 mm */
 179                *val = 0;
 180                *val2 = 1000;
 181                return IIO_VAL_INT_PLUS_MICRO;
 182        default:
 183                return -EINVAL;
 184        }
 185}
 186
 187static const struct iio_info rfd77402_info = {
 188        .read_raw = rfd77402_read_raw,
 189};
 190
 191static int rfd77402_init(struct rfd77402_data *data)
 192{
 193        int ret, i;
 194
 195        ret = rfd77402_set_state(data, RFD77402_CMD_STANDBY,
 196                                 RFD77402_STATUS_STANDBY);
 197        if (ret < 0)
 198                return ret;
 199
 200        /* configure INT pad as push-pull, active low */
 201        ret = i2c_smbus_write_byte_data(data->client, RFD77402_ICSR,
 202                                        RFD77402_ICSR_INT_MODE);
 203        if (ret < 0)
 204                return ret;
 205
 206        /* I2C configuration */
 207        ret = i2c_smbus_write_word_data(data->client, RFD77402_I2C_INIT_CFG,
 208                                        RFD77402_I2C_ADDR_INCR |
 209                                        RFD77402_I2C_DATA_INCR |
 210                                        RFD77402_I2C_HOST_DEBUG |
 211                                        RFD77402_I2C_MCPU_DEBUG);
 212        if (ret < 0)
 213                return ret;
 214
 215        /* set initialization */
 216        ret = i2c_smbus_write_word_data(data->client, RFD77402_PMU_CFG, 0x0500);
 217        if (ret < 0)
 218                return ret;
 219
 220        ret = rfd77402_set_state(data, RFD77402_CMD_MCPU_OFF,
 221                                 RFD77402_STATUS_MCPU_OFF);
 222        if (ret < 0)
 223                return ret;
 224
 225        /* set initialization */
 226        ret = i2c_smbus_write_word_data(data->client, RFD77402_PMU_CFG, 0x0600);
 227        if (ret < 0)
 228                return ret;
 229
 230        ret = rfd77402_set_state(data, RFD77402_CMD_MCPU_ON,
 231                                 RFD77402_STATUS_MCPU_ON);
 232        if (ret < 0)
 233                return ret;
 234
 235        for (i = 0; i < ARRAY_SIZE(rf77402_tof_config); i++) {
 236                ret = i2c_smbus_write_word_data(data->client,
 237                                                rf77402_tof_config[i].reg,
 238                                                rf77402_tof_config[i].val);
 239                if (ret < 0)
 240                        return ret;
 241        }
 242
 243        ret = rfd77402_set_state(data, RFD77402_CMD_STANDBY,
 244                                 RFD77402_STATUS_STANDBY);
 245
 246        return ret;
 247}
 248
 249static int rfd77402_powerdown(struct rfd77402_data *data)
 250{
 251        return rfd77402_set_state(data, RFD77402_CMD_STANDBY,
 252                                  RFD77402_STATUS_STANDBY);
 253}
 254
 255static int rfd77402_probe(struct i2c_client *client,
 256                          const struct i2c_device_id *id)
 257{
 258        struct rfd77402_data *data;
 259        struct iio_dev *indio_dev;
 260        int ret;
 261
 262        ret = i2c_smbus_read_word_data(client, RFD77402_MOD_CHIP_ID);
 263        if (ret < 0)
 264                return ret;
 265        if (ret != 0xad01 && ret != 0xad02) /* known chip ids */
 266                return -ENODEV;
 267
 268        indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data));
 269        if (!indio_dev)
 270                return -ENOMEM;
 271
 272        data = iio_priv(indio_dev);
 273        i2c_set_clientdata(client, indio_dev);
 274        data->client = client;
 275        mutex_init(&data->lock);
 276
 277        indio_dev->info = &rfd77402_info;
 278        indio_dev->channels = rfd77402_channels;
 279        indio_dev->num_channels = ARRAY_SIZE(rfd77402_channels);
 280        indio_dev->name = RFD77402_DRV_NAME;
 281        indio_dev->modes = INDIO_DIRECT_MODE;
 282
 283        ret = rfd77402_init(data);
 284        if (ret < 0)
 285                return ret;
 286
 287        ret = iio_device_register(indio_dev);
 288        if (ret)
 289                goto err_powerdown;
 290
 291        return 0;
 292
 293err_powerdown:
 294        rfd77402_powerdown(data);
 295        return ret;
 296}
 297
 298static int rfd77402_remove(struct i2c_client *client)
 299{
 300        struct iio_dev *indio_dev = i2c_get_clientdata(client);
 301
 302        iio_device_unregister(indio_dev);
 303        rfd77402_powerdown(iio_priv(indio_dev));
 304
 305        return 0;
 306}
 307
 308#ifdef CONFIG_PM_SLEEP
 309static int rfd77402_suspend(struct device *dev)
 310{
 311        struct rfd77402_data *data = iio_priv(i2c_get_clientdata(
 312                                     to_i2c_client(dev)));
 313
 314        return rfd77402_powerdown(data);
 315}
 316
 317static int rfd77402_resume(struct device *dev)
 318{
 319        struct rfd77402_data *data = iio_priv(i2c_get_clientdata(
 320                                     to_i2c_client(dev)));
 321
 322        return rfd77402_init(data);
 323}
 324#endif
 325
 326static SIMPLE_DEV_PM_OPS(rfd77402_pm_ops, rfd77402_suspend, rfd77402_resume);
 327
 328static const struct i2c_device_id rfd77402_id[] = {
 329        { "rfd77402", 0},
 330        { }
 331};
 332MODULE_DEVICE_TABLE(i2c, rfd77402_id);
 333
 334static struct i2c_driver rfd77402_driver = {
 335        .driver = {
 336                .name   = RFD77402_DRV_NAME,
 337                .pm     = &rfd77402_pm_ops,
 338        },
 339        .probe  = rfd77402_probe,
 340        .remove = rfd77402_remove,
 341        .id_table = rfd77402_id,
 342};
 343
 344module_i2c_driver(rfd77402_driver);
 345
 346MODULE_AUTHOR("Peter Meerwald-Stadler <pmeerw@pmeerw.net>");
 347MODULE_DESCRIPTION("RFD77402 Time-of-Flight sensor driver");
 348MODULE_LICENSE("GPL");
 349