Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

iio: proximity: Add rfd77402 driver

Driver for RF Digital RFD77402 VCSEL (vertical-cavity surface-emitting
laser) Time-of-Flight (ToF) sensor to measure distance up to 2 m with
millimeter precision

Signed-off-by: Peter Meerwald-Stadler <pmeerw@pmeerw.net>
Signed-off-by: Jonathan Cameron <Jonathan.Cameron@huawei.com>

authored by

Peter Meerwald-Stadler and committed by
Jonathan Cameron
79e64188 d35d43d7

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