diff options
Diffstat (limited to 'drivers/iio/proximity')
-rw-r--r-- | drivers/iio/proximity/Kconfig | 10 | ||||
-rw-r--r-- | drivers/iio/proximity/Makefile | 1 | ||||
-rw-r--r-- | drivers/iio/proximity/as3935.c | 45 | ||||
-rw-r--r-- | drivers/iio/proximity/pulsedlight-lidar-lite-v2.c | 1 | ||||
-rw-r--r-- | drivers/iio/proximity/rfd77402.c | 352 | ||||
-rw-r--r-- | drivers/iio/proximity/srf04.c | 1 | ||||
-rw-r--r-- | drivers/iio/proximity/srf08.c | 2 | ||||
-rw-r--r-- | drivers/iio/proximity/sx9500.c | 2 |
8 files changed, 403 insertions, 11 deletions
diff --git a/drivers/iio/proximity/Kconfig b/drivers/iio/proximity/Kconfig index ae070950f920..fcb1c4ba5e41 100644 --- a/drivers/iio/proximity/Kconfig +++ b/drivers/iio/proximity/Kconfig @@ -32,6 +32,16 @@ config LIDAR_LITE_V2 To compile this driver as a module, choose M here: the module will be called pulsedlight-lite-v2 +config RFD77402 + tristate "RFD77402 ToF sensor" + depends on I2C + help + Say Y to build a driver for the RFD77420 Time-of-Flight (distance) + sensor module with I2C interface. + + To compile this driver as a module, choose M here: the + module will be called rfd77402. + config SRF04 tristate "Devantech SRF04 ultrasonic ranger sensor" depends on GPIOLIB diff --git a/drivers/iio/proximity/Makefile b/drivers/iio/proximity/Makefile index ed1b6f4cc209..1b195d84c611 100644 --- a/drivers/iio/proximity/Makefile +++ b/drivers/iio/proximity/Makefile @@ -5,6 +5,7 @@ # When adding new entries keep the list in alphabetical order obj-$(CONFIG_AS3935) += as3935.o obj-$(CONFIG_LIDAR_LITE_V2) += pulsedlight-lidar-lite-v2.o +obj-$(CONFIG_RFD77402) += rfd77402.o obj-$(CONFIG_SRF04) += srf04.o obj-$(CONFIG_SRF08) += srf08.o obj-$(CONFIG_SX9500) += sx9500.o diff --git a/drivers/iio/proximity/as3935.c b/drivers/iio/proximity/as3935.c index 0eeff29b61be..b6249af48014 100644 --- a/drivers/iio/proximity/as3935.c +++ b/drivers/iio/proximity/as3935.c @@ -39,8 +39,12 @@ #define AS3935_AFE_GAIN_MAX 0x1F #define AS3935_AFE_PWR_BIT BIT(0) +#define AS3935_NFLWDTH 0x01 +#define AS3935_NFLWDTH_MASK 0x7f + #define AS3935_INT 0x03 #define AS3935_INT_MASK 0x0f +#define AS3935_DISTURB_INT BIT(2) #define AS3935_EVENT_INT BIT(3) #define AS3935_NOISE_INT BIT(0) @@ -48,6 +52,7 @@ #define AS3935_DATA_MASK 0x3F #define AS3935_TUNE_CAP 0x08 +#define AS3935_DEFAULTS 0x3C #define AS3935_CALIBRATE 0x3D #define AS3935_READ_DATA BIT(14) @@ -62,7 +67,9 @@ struct as3935_state { struct mutex lock; struct delayed_work work; + unsigned long noise_tripped; u32 tune_cap; + u32 nflwdth_reg; u8 buffer[16]; /* 8-bit data + 56-bit padding + 64-bit timestamp */ u8 buf[2] ____cacheline_aligned; }; @@ -145,12 +152,29 @@ static ssize_t as3935_sensor_sensitivity_store(struct device *dev, return len; } +static ssize_t as3935_noise_level_tripped_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct as3935_state *st = iio_priv(dev_to_iio_dev(dev)); + int ret; + + mutex_lock(&st->lock); + ret = sprintf(buf, "%d\n", !time_after(jiffies, st->noise_tripped + HZ)); + mutex_unlock(&st->lock); + + return ret; +} + static IIO_DEVICE_ATTR(sensor_sensitivity, S_IRUGO | S_IWUSR, as3935_sensor_sensitivity_show, as3935_sensor_sensitivity_store, 0); +static IIO_DEVICE_ATTR(noise_level_tripped, S_IRUGO, + as3935_noise_level_tripped_show, NULL, 0); static struct attribute *as3935_attributes[] = { &iio_dev_attr_sensor_sensitivity.dev_attr.attr, + &iio_dev_attr_noise_level_tripped.dev_attr.attr, NULL, }; @@ -197,7 +221,6 @@ static int as3935_read_raw(struct iio_dev *indio_dev, } static const struct iio_info as3935_info = { - .driver_module = THIS_MODULE, .attrs = &as3935_attribute_group, .read_raw = &as3935_read_raw, }; @@ -223,7 +246,6 @@ err_read: } static const struct iio_trigger_ops iio_interrupt_trigger_ops = { - .owner = THIS_MODULE, }; static void as3935_event_work(struct work_struct *work) @@ -246,7 +268,11 @@ static void as3935_event_work(struct work_struct *work) case AS3935_EVENT_INT: iio_trigger_poll_chained(st->trig); break; + case AS3935_DISTURB_INT: case AS3935_NOISE_INT: + mutex_lock(&st->lock); + st->noise_tripped = jiffies; + mutex_unlock(&st->lock); dev_warn(&st->spi->dev, "noise level is too high\n"); break; } @@ -269,15 +295,14 @@ static irqreturn_t as3935_interrupt_handler(int irq, void *private) static void calibrate_as3935(struct as3935_state *st) { - /* mask disturber interrupt bit */ - as3935_write(st, AS3935_INT, BIT(5)); - + as3935_write(st, AS3935_DEFAULTS, 0x96); as3935_write(st, AS3935_CALIBRATE, 0x96); as3935_write(st, AS3935_TUNE_CAP, BIT(5) | (st->tune_cap / TUNE_CAP_DIV)); mdelay(2); as3935_write(st, AS3935_TUNE_CAP, (st->tune_cap / TUNE_CAP_DIV)); + as3935_write(st, AS3935_NFLWDTH, st->nflwdth_reg); } #ifdef CONFIG_PM_SLEEP @@ -370,6 +395,15 @@ static int as3935_probe(struct spi_device *spi) return -EINVAL; } + ret = of_property_read_u32(np, + "ams,nflwdth", &st->nflwdth_reg); + if (!ret && st->nflwdth_reg > AS3935_NFLWDTH_MASK) { + dev_err(&spi->dev, + "invalid nflwdth setting of %d\n", + st->nflwdth_reg); + return -EINVAL; + } + indio_dev->dev.parent = &spi->dev; indio_dev->name = spi_get_device_id(spi)->name; indio_dev->channels = as3935_channels; @@ -384,6 +418,7 @@ static int as3935_probe(struct spi_device *spi) return -ENOMEM; st->trig = trig; + st->noise_tripped = jiffies - HZ; trig->dev.parent = indio_dev->dev.parent; iio_trigger_set_drvdata(trig, indio_dev); trig->ops = &iio_interrupt_trigger_ops; diff --git a/drivers/iio/proximity/pulsedlight-lidar-lite-v2.c b/drivers/iio/proximity/pulsedlight-lidar-lite-v2.c index 36c1ddc251aa..4d56f67b24c6 100644 --- a/drivers/iio/proximity/pulsedlight-lidar-lite-v2.c +++ b/drivers/iio/proximity/pulsedlight-lidar-lite-v2.c @@ -249,7 +249,6 @@ static irqreturn_t lidar_trigger_handler(int irq, void *private) } static const struct iio_info lidar_info = { - .driver_module = THIS_MODULE, .read_raw = lidar_read_raw, }; diff --git a/drivers/iio/proximity/rfd77402.c b/drivers/iio/proximity/rfd77402.c new file mode 100644 index 000000000000..fe29fb1a19a6 --- /dev/null +++ b/drivers/iio/proximity/rfd77402.c @@ -0,0 +1,352 @@ +/* + * rfd77402.c - Support for RF Digital RFD77402 Time-of-Flight (distance) sensor + * + * Copyright 2017 Peter Meerwald-Stadler <pmeerw@pmeerw.net> + * + * This file is subject to the terms and conditions of version 2 of + * the GNU General Public License. See the file COPYING in the main + * directory of this archive for more details. + * + * 7-bit I2C slave address 0x4c + * + * TODO: interrupt + * https://media.digikey.com/pdf/Data%20Sheets/RF%20Digital%20PDFs/RFD77402.pdf + */ + +#include <linux/module.h> +#include <linux/i2c.h> +#include <linux/delay.h> + +#include <linux/iio/iio.h> + +#define RFD77402_DRV_NAME "rfd77402" + +#define RFD77402_ICSR 0x00 /* Interrupt Control Status Register */ +#define RFD77402_ICSR_INT_MODE BIT(2) +#define RFD77402_ICSR_INT_POL BIT(3) +#define RFD77402_ICSR_RESULT BIT(4) +#define RFD77402_ICSR_M2H_MSG BIT(5) +#define RFD77402_ICSR_H2M_MSG BIT(6) +#define RFD77402_ICSR_RESET BIT(7) + +#define RFD77402_CMD_R 0x04 +#define RFD77402_CMD_SINGLE 0x01 +#define RFD77402_CMD_STANDBY 0x10 +#define RFD77402_CMD_MCPU_OFF 0x11 +#define RFD77402_CMD_MCPU_ON 0x12 +#define RFD77402_CMD_RESET BIT(6) +#define RFD77402_CMD_VALID BIT(7) + +#define RFD77402_STATUS_R 0x06 +#define RFD77402_STATUS_PM_MASK GENMASK(4, 0) +#define RFD77402_STATUS_STANDBY 0x00 +#define RFD77402_STATUS_MCPU_OFF 0x10 +#define RFD77402_STATUS_MCPU_ON 0x18 + +#define RFD77402_RESULT_R 0x08 +#define RFD77402_RESULT_DIST_MASK GENMASK(12, 2) +#define RFD77402_RESULT_ERR_MASK GENMASK(14, 13) +#define RFD77402_RESULT_VALID BIT(15) + +#define RFD77402_PMU_CFG 0x14 +#define RFD77402_PMU_MCPU_INIT BIT(9) + +#define RFD77402_I2C_INIT_CFG 0x1c +#define RFD77402_I2C_ADDR_INCR BIT(0) +#define RFD77402_I2C_DATA_INCR BIT(2) +#define RFD77402_I2C_HOST_DEBUG BIT(5) +#define RFD77402_I2C_MCPU_DEBUG BIT(6) + +#define RFD77402_CMD_CFGR_A 0x0c +#define RFD77402_CMD_CFGR_B 0x0e +#define RFD77402_HFCFG_0 0x20 +#define RFD77402_HFCFG_1 0x22 +#define RFD77402_HFCFG_2 0x24 +#define RFD77402_HFCFG_3 0x26 + +#define RFD77402_MOD_CHIP_ID 0x28 + +/* magic configuration values from datasheet */ +static const struct { + u8 reg; + u16 val; +} rf77402_tof_config[] = { + {RFD77402_CMD_CFGR_A, 0xe100}, + {RFD77402_CMD_CFGR_B, 0x10ff}, + {RFD77402_HFCFG_0, 0x07d0}, + {RFD77402_HFCFG_1, 0x5008}, + {RFD77402_HFCFG_2, 0xa041}, + {RFD77402_HFCFG_3, 0x45d4}, +}; + +struct rfd77402_data { + struct i2c_client *client; + /* Serialize reads from the sensor */ + struct mutex lock; +}; + +static const struct iio_chan_spec rfd77402_channels[] = { + { + .type = IIO_DISTANCE, + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | + BIT(IIO_CHAN_INFO_SCALE), + }, +}; + +static int rfd77402_set_state(struct rfd77402_data *data, u8 state, u16 check) +{ + int ret; + + ret = i2c_smbus_write_byte_data(data->client, RFD77402_CMD_R, + state | RFD77402_CMD_VALID); + if (ret < 0) + return ret; + + usleep_range(10000, 20000); + + ret = i2c_smbus_read_word_data(data->client, RFD77402_STATUS_R); + if (ret < 0) + return ret; + if ((ret & RFD77402_STATUS_PM_MASK) != check) + return -ENODEV; + + return 0; +} + +static int rfd77402_measure(struct rfd77402_data *data) +{ + int ret; + int tries = 10; + + ret = rfd77402_set_state(data, RFD77402_CMD_MCPU_ON, + RFD77402_STATUS_MCPU_ON); + if (ret < 0) + return ret; + + ret = i2c_smbus_write_byte_data(data->client, RFD77402_CMD_R, + RFD77402_CMD_SINGLE | + RFD77402_CMD_VALID); + if (ret < 0) + goto err; + + while (tries-- > 0) { + ret = i2c_smbus_read_byte_data(data->client, RFD77402_ICSR); + if (ret < 0) + goto err; + if (ret & RFD77402_ICSR_RESULT) + break; + msleep(20); + } + + if (tries < 0) { + ret = -ETIMEDOUT; + goto err; + } + + ret = i2c_smbus_read_word_data(data->client, RFD77402_RESULT_R); + if (ret < 0) + goto err; + + if ((ret & RFD77402_RESULT_ERR_MASK) || + !(ret & RFD77402_RESULT_VALID)) { + ret = -EIO; + goto err; + } + + return (ret & RFD77402_RESULT_DIST_MASK) >> 2; + +err: + rfd77402_set_state(data, RFD77402_CMD_MCPU_OFF, + RFD77402_STATUS_MCPU_OFF); + return ret; +} + +static int rfd77402_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val, int *val2, long mask) +{ + struct rfd77402_data *data = iio_priv(indio_dev); + int ret; + + switch (mask) { + case IIO_CHAN_INFO_RAW: + mutex_lock(&data->lock); + ret = rfd77402_measure(data); + mutex_unlock(&data->lock); + if (ret < 0) + return ret; + *val = ret; + return IIO_VAL_INT; + case IIO_CHAN_INFO_SCALE: + /* 1 LSB is 1 mm */ + *val = 0; + *val2 = 1000; + return IIO_VAL_INT_PLUS_MICRO; + default: + return -EINVAL; + } +} + +static const struct iio_info rfd77402_info = { + .read_raw = rfd77402_read_raw, +}; + +static int rfd77402_init(struct rfd77402_data *data) +{ + int ret, i; + + ret = rfd77402_set_state(data, RFD77402_CMD_STANDBY, + RFD77402_STATUS_STANDBY); + if (ret < 0) + return ret; + + /* configure INT pad as push-pull, active low */ + ret = i2c_smbus_write_byte_data(data->client, RFD77402_ICSR, + RFD77402_ICSR_INT_MODE); + if (ret < 0) + return ret; + + /* I2C configuration */ + ret = i2c_smbus_write_word_data(data->client, RFD77402_I2C_INIT_CFG, + RFD77402_I2C_ADDR_INCR | + RFD77402_I2C_DATA_INCR | + RFD77402_I2C_HOST_DEBUG | + RFD77402_I2C_MCPU_DEBUG); + if (ret < 0) + return ret; + + /* set initialization */ + ret = i2c_smbus_write_word_data(data->client, RFD77402_PMU_CFG, 0x0500); + if (ret < 0) + return ret; + + ret = rfd77402_set_state(data, RFD77402_CMD_MCPU_OFF, + RFD77402_STATUS_MCPU_OFF); + if (ret < 0) + return ret; + + /* set initialization */ + ret = i2c_smbus_write_word_data(data->client, RFD77402_PMU_CFG, 0x0600); + if (ret < 0) + return ret; + + ret = rfd77402_set_state(data, RFD77402_CMD_MCPU_ON, + RFD77402_STATUS_MCPU_ON); + if (ret < 0) + return ret; + + for (i = 0; i < ARRAY_SIZE(rf77402_tof_config); i++) { + ret = i2c_smbus_write_word_data(data->client, + rf77402_tof_config[i].reg, + rf77402_tof_config[i].val); + if (ret < 0) + return ret; + } + + ret = rfd77402_set_state(data, RFD77402_CMD_STANDBY, + RFD77402_STATUS_STANDBY); + + return ret; +} + +static int rfd77402_powerdown(struct rfd77402_data *data) +{ + return rfd77402_set_state(data, RFD77402_CMD_STANDBY, + RFD77402_STATUS_STANDBY); +} + +static int rfd77402_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct rfd77402_data *data; + struct iio_dev *indio_dev; + int ret; + + ret = i2c_smbus_read_word_data(client, RFD77402_MOD_CHIP_ID); + if (ret < 0) + return ret; + if (ret != 0xad01 && ret != 0xad02) /* known chip ids */ + return -ENODEV; + + indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data)); + if (!indio_dev) + return -ENOMEM; + + data = iio_priv(indio_dev); + i2c_set_clientdata(client, indio_dev); + data->client = client; + mutex_init(&data->lock); + + indio_dev->dev.parent = &client->dev; + indio_dev->info = &rfd77402_info; + indio_dev->channels = rfd77402_channels; + indio_dev->num_channels = ARRAY_SIZE(rfd77402_channels); + indio_dev->name = RFD77402_DRV_NAME; + indio_dev->modes = INDIO_DIRECT_MODE; + + ret = rfd77402_init(data); + if (ret < 0) + return ret; + + ret = iio_device_register(indio_dev); + if (ret) + goto err_powerdown; + + return 0; + +err_powerdown: + rfd77402_powerdown(data); + return ret; +} + +static int rfd77402_remove(struct i2c_client *client) +{ + struct iio_dev *indio_dev = i2c_get_clientdata(client); + + iio_device_unregister(indio_dev); + rfd77402_powerdown(iio_priv(indio_dev)); + + return 0; +} + +#ifdef CONFIG_PM_SLEEP +static int rfd77402_suspend(struct device *dev) +{ + struct rfd77402_data *data = iio_priv(i2c_get_clientdata( + to_i2c_client(dev))); + + return rfd77402_powerdown(data); +} + +static int rfd77402_resume(struct device *dev) +{ + struct rfd77402_data *data = iio_priv(i2c_get_clientdata( + to_i2c_client(dev))); + + return rfd77402_init(data); +} +#endif + +static SIMPLE_DEV_PM_OPS(rfd77402_pm_ops, rfd77402_suspend, rfd77402_resume); + +static const struct i2c_device_id rfd77402_id[] = { + { "rfd77402", 0}, + { } +}; +MODULE_DEVICE_TABLE(i2c, rfd77402_id); + +static struct i2c_driver rfd77402_driver = { + .driver = { + .name = RFD77402_DRV_NAME, + .pm = &rfd77402_pm_ops, + }, + .probe = rfd77402_probe, + .remove = rfd77402_remove, + .id_table = rfd77402_id, +}; + +module_i2c_driver(rfd77402_driver); + +MODULE_AUTHOR("Peter Meerwald-Stadler <pmeerw@pmeerw.net>"); +MODULE_DESCRIPTION("RFD77402 Time-of-Flight sensor driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/iio/proximity/srf04.c b/drivers/iio/proximity/srf04.c index e37667f933b3..09c7b9c095b0 100644 --- a/drivers/iio/proximity/srf04.c +++ b/drivers/iio/proximity/srf04.c @@ -203,7 +203,6 @@ static int srf04_read_raw(struct iio_dev *indio_dev, } static const struct iio_info srf04_iio_info = { - .driver_module = THIS_MODULE, .read_raw = srf04_read_raw, }; diff --git a/drivers/iio/proximity/srf08.c b/drivers/iio/proximity/srf08.c index 9380d545aab1..f2bf783f829a 100644 --- a/drivers/iio/proximity/srf08.c +++ b/drivers/iio/proximity/srf08.c @@ -436,7 +436,6 @@ static const struct iio_chan_spec srf08_channels[] = { static const struct iio_info srf08_info = { .read_raw = srf08_read_raw, .attrs = &srf08_attribute_group, - .driver_module = THIS_MODULE, }; /* @@ -445,7 +444,6 @@ static const struct iio_info srf08_info = { */ static const struct iio_info srf02_info = { .read_raw = srf08_read_raw, - .driver_module = THIS_MODULE, }; static int srf08_probe(struct i2c_client *client, diff --git a/drivers/iio/proximity/sx9500.c b/drivers/iio/proximity/sx9500.c index f42b3a1c75ff..53c5d653e780 100644 --- a/drivers/iio/proximity/sx9500.c +++ b/drivers/iio/proximity/sx9500.c @@ -615,7 +615,6 @@ static const struct attribute_group sx9500_attribute_group = { }; static const struct iio_info sx9500_info = { - .driver_module = THIS_MODULE, .attrs = &sx9500_attribute_group, .read_raw = &sx9500_read_raw, .write_raw = &sx9500_write_raw, @@ -650,7 +649,6 @@ out: static const struct iio_trigger_ops sx9500_trigger_ops = { .set_trigger_state = sx9500_set_trigger_state, - .owner = THIS_MODULE, }; static irqreturn_t sx9500_trigger_handler(int irq, void *private) |