rfd77402.c (7704B)
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 i2c_client *client, u8 state, u16 check) 94{ 95 int ret; 96 97 ret = i2c_smbus_write_byte_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(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 i2c_client *client) 114{ 115 int ret; 116 int tries = 10; 117 118 ret = rfd77402_set_state(client, RFD77402_CMD_MCPU_ON, 119 RFD77402_STATUS_MCPU_ON); 120 if (ret < 0) 121 return ret; 122 123 ret = i2c_smbus_write_byte_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(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(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(client, 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->client); 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 i2c_client *client) 192{ 193 int ret, i; 194 195 ret = rfd77402_set_state(client, 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(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(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(client, RFD77402_PMU_CFG, 0x0500); 217 if (ret < 0) 218 return ret; 219 220 ret = rfd77402_set_state(client, 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(client, RFD77402_PMU_CFG, 0x0600); 227 if (ret < 0) 228 return ret; 229 230 ret = rfd77402_set_state(client, 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(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(client, RFD77402_CMD_STANDBY, 244 RFD77402_STATUS_STANDBY); 245 246 return ret; 247} 248 249static int rfd77402_powerdown(struct i2c_client *client) 250{ 251 return rfd77402_set_state(client, RFD77402_CMD_STANDBY, 252 RFD77402_STATUS_STANDBY); 253} 254 255static void rfd77402_disable(void *client) 256{ 257 rfd77402_powerdown(client); 258} 259 260static int rfd77402_probe(struct i2c_client *client, 261 const struct i2c_device_id *id) 262{ 263 struct rfd77402_data *data; 264 struct iio_dev *indio_dev; 265 int ret; 266 267 ret = i2c_smbus_read_word_data(client, RFD77402_MOD_CHIP_ID); 268 if (ret < 0) 269 return ret; 270 if (ret != 0xad01 && ret != 0xad02) /* known chip ids */ 271 return -ENODEV; 272 273 indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data)); 274 if (!indio_dev) 275 return -ENOMEM; 276 277 data = iio_priv(indio_dev); 278 data->client = client; 279 mutex_init(&data->lock); 280 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(client); 288 if (ret < 0) 289 return ret; 290 291 ret = devm_add_action_or_reset(&client->dev, rfd77402_disable, client); 292 if (ret) 293 return ret; 294 295 return devm_iio_device_register(&client->dev, indio_dev); 296} 297 298static int rfd77402_suspend(struct device *dev) 299{ 300 return rfd77402_powerdown(to_i2c_client(dev)); 301} 302 303static int rfd77402_resume(struct device *dev) 304{ 305 return rfd77402_init(to_i2c_client(dev)); 306} 307 308static DEFINE_SIMPLE_DEV_PM_OPS(rfd77402_pm_ops, rfd77402_suspend, 309 rfd77402_resume); 310 311static const struct i2c_device_id rfd77402_id[] = { 312 { "rfd77402", 0 }, 313 { } 314}; 315MODULE_DEVICE_TABLE(i2c, rfd77402_id); 316 317static struct i2c_driver rfd77402_driver = { 318 .driver = { 319 .name = RFD77402_DRV_NAME, 320 .pm = pm_sleep_ptr(&rfd77402_pm_ops), 321 }, 322 .probe = rfd77402_probe, 323 .id_table = rfd77402_id, 324}; 325 326module_i2c_driver(rfd77402_driver); 327 328MODULE_AUTHOR("Peter Meerwald-Stadler <pmeerw@pmeerw.net>"); 329MODULE_DESCRIPTION("RFD77402 Time-of-Flight sensor driver"); 330MODULE_LICENSE("GPL");