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 */ 67 static 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 79 struct rfd77402_data { 80 struct i2c_client *client; 81 /* Serialize reads from the sensor */ 82 struct mutex lock; 83 }; 84 85 static 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 93 static 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 113 static 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 155 err: 156 rfd77402_set_state(client, RFD77402_CMD_MCPU_OFF, 157 RFD77402_STATUS_MCPU_OFF); 158 return ret; 159 } 160 161 static 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 187 static const struct iio_info rfd77402_info = { 188 .read_raw = rfd77402_read_raw, 189 }; 190 191 static 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 249 static int rfd77402_powerdown(struct i2c_client *client) 250 { 251 return rfd77402_set_state(client, RFD77402_CMD_STANDBY, 252 RFD77402_STATUS_STANDBY); 253 } 254 255 static void rfd77402_disable(void *client) 256 { 257 rfd77402_powerdown(client); 258 } 259 260 static 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 298 #ifdef CONFIG_PM_SLEEP 299 static int rfd77402_suspend(struct device *dev) 300 { 301 return rfd77402_powerdown(to_i2c_client(dev)); 302 } 303 304 static int rfd77402_resume(struct device *dev) 305 { 306 return rfd77402_init(to_i2c_client(dev)); 307 } 308 #endif 309 310 static SIMPLE_DEV_PM_OPS(rfd77402_pm_ops, rfd77402_suspend, rfd77402_resume); 311 312 static const struct i2c_device_id rfd77402_id[] = { 313 { "rfd77402", 0}, 314 { } 315 }; 316 MODULE_DEVICE_TABLE(i2c, rfd77402_id); 317 318 static struct i2c_driver rfd77402_driver = { 319 .driver = { 320 .name = RFD77402_DRV_NAME, 321 .pm = &rfd77402_pm_ops, 322 }, 323 .probe = rfd77402_probe, 324 .id_table = rfd77402_id, 325 }; 326 327 module_i2c_driver(rfd77402_driver); 328 329 MODULE_AUTHOR("Peter Meerwald-Stadler <pmeerw@pmeerw.net>"); 330 MODULE_DESCRIPTION("RFD77402 Time-of-Flight sensor driver"); 331 MODULE_LICENSE("GPL"); 332