330 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/*3 * rfd77402.c - Support for RF Digital RFD77402 Time-of-Flight (distance) sensor4 *5 * Copyright 2017 Peter Meerwald-Stadler <pmeerw@pmeerw.net>6 *7 * 7-bit I2C slave address 0x4c8 *9 * TODO: interrupt10 * https://media.digikey.com/pdf/Data%20Sheets/RF%20Digital%20PDFs/RFD77402.pdf11 */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 0x0430#define RFD77402_CMD_SINGLE 0x0131#define RFD77402_CMD_STANDBY 0x1032#define RFD77402_CMD_MCPU_OFF 0x1133#define RFD77402_CMD_MCPU_ON 0x1234#define RFD77402_CMD_RESET BIT(6)35#define RFD77402_CMD_VALID BIT(7)36 37#define RFD77402_STATUS_R 0x0638#define RFD77402_STATUS_PM_MASK GENMASK(4, 0)39#define RFD77402_STATUS_STANDBY 0x0040#define RFD77402_STATUS_MCPU_OFF 0x1041#define RFD77402_STATUS_MCPU_ON 0x1842 43#define RFD77402_RESULT_R 0x0844#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 0x1449#define RFD77402_PMU_MCPU_INIT BIT(9)50 51#define RFD77402_I2C_INIT_CFG 0x1c52#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 0x0c58#define RFD77402_CMD_CFGR_B 0x0e59#define RFD77402_HFCFG_0 0x2060#define RFD77402_HFCFG_1 0x2261#define RFD77402_HFCFG_2 0x2462#define RFD77402_HFCFG_3 0x2663 64#define RFD77402_MOD_CHIP_ID 0x2865 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{262 struct rfd77402_data *data;263 struct iio_dev *indio_dev;264 int ret;265 266 ret = i2c_smbus_read_word_data(client, RFD77402_MOD_CHIP_ID);267 if (ret < 0)268 return ret;269 if (ret != 0xad01 && ret != 0xad02) /* known chip ids */270 return -ENODEV;271 272 indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data));273 if (!indio_dev)274 return -ENOMEM;275 276 data = iio_priv(indio_dev);277 data->client = client;278 mutex_init(&data->lock);279 280 indio_dev->info = &rfd77402_info;281 indio_dev->channels = rfd77402_channels;282 indio_dev->num_channels = ARRAY_SIZE(rfd77402_channels);283 indio_dev->name = RFD77402_DRV_NAME;284 indio_dev->modes = INDIO_DIRECT_MODE;285 286 ret = rfd77402_init(client);287 if (ret < 0)288 return ret;289 290 ret = devm_add_action_or_reset(&client->dev, rfd77402_disable, client);291 if (ret)292 return ret;293 294 return devm_iio_device_register(&client->dev, indio_dev);295}296 297static int rfd77402_suspend(struct device *dev)298{299 return rfd77402_powerdown(to_i2c_client(dev));300}301 302static int rfd77402_resume(struct device *dev)303{304 return rfd77402_init(to_i2c_client(dev));305}306 307static DEFINE_SIMPLE_DEV_PM_OPS(rfd77402_pm_ops, rfd77402_suspend,308 rfd77402_resume);309 310static const struct i2c_device_id rfd77402_id[] = {311 { "rfd77402" },312 { }313};314MODULE_DEVICE_TABLE(i2c, rfd77402_id);315 316static struct i2c_driver rfd77402_driver = {317 .driver = {318 .name = RFD77402_DRV_NAME,319 .pm = pm_sleep_ptr(&rfd77402_pm_ops),320 },321 .probe = rfd77402_probe,322 .id_table = rfd77402_id,323};324 325module_i2c_driver(rfd77402_driver);326 327MODULE_AUTHOR("Peter Meerwald-Stadler <pmeerw@pmeerw.net>");328MODULE_DESCRIPTION("RFD77402 Time-of-Flight sensor driver");329MODULE_LICENSE("GPL");330