v4.19.13 snapshot.
diff --git a/drivers/iio/proximity/Kconfig b/drivers/iio/proximity/Kconfig
new file mode 100644
index 0000000..388ef70
--- /dev/null
+++ b/drivers/iio/proximity/Kconfig
@@ -0,0 +1,95 @@
+#
+# Proximity sensors
+#
+
+menu "Lightning sensors"
+
+config AS3935
+	tristate "AS3935 Franklin lightning sensor"
+	select IIO_BUFFER
+	select IIO_TRIGGERED_BUFFER
+	depends on SPI
+	help
+	  Say Y here to build SPI interface support for the Austrian
+	  Microsystems AS3935 lightning detection sensor.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called as3935
+
+endmenu
+
+menu "Proximity and distance sensors"
+
+config ISL29501
+	tristate "Intersil ISL29501 Time Of Flight sensor"
+	depends on I2C
+	select IIO_BUFFER
+	select IIO_TRIGGERED_BUFFER
+	select IIO_KFIFO_BUF
+	help
+	  Say Y here if you want to build a driver for the Intersil ISL29501
+	  Time of Flight sensor.
+
+	  To compile this driver as a module, choose M here: the module will be
+	  called isl29501.
+
+config LIDAR_LITE_V2
+	tristate "PulsedLight LIDAR sensor"
+	select IIO_BUFFER
+	select IIO_TRIGGERED_BUFFER
+	depends on I2C
+	help
+	  Say Y to build a driver for PulsedLight LIDAR range finding
+	  sensor.
+
+	  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
+	help
+	  Say Y here to build a driver for Devantech SRF04 ultrasonic
+	  ranger sensor. This driver can be used to measure the distance
+	  of objects. It is using two GPIOs.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called srf04.
+
+config SX9500
+	tristate "SX9500 Semtech proximity sensor"
+	select IIO_BUFFER
+	select IIO_TRIGGERED_BUFFER
+	select REGMAP_I2C
+	depends on I2C
+	help
+	  Say Y here to build a driver for Semtech's SX9500 capacitive
+	  proximity/button sensor.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called sx9500.
+
+config SRF08
+	tristate "Devantech SRF02/SRF08/SRF10 ultrasonic ranger sensor"
+	select IIO_BUFFER
+	select IIO_TRIGGERED_BUFFER
+	depends on I2C
+	help
+	  Say Y here to build a driver for Devantech SRF02/SRF08/SRF10
+	  ultrasonic ranger sensors with i2c interface.
+	  This driver can be used to measure the distance of objects.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called srf08.
+
+endmenu
diff --git a/drivers/iio/proximity/Makefile b/drivers/iio/proximity/Makefile
new file mode 100644
index 0000000..cac3d7d
--- /dev/null
+++ b/drivers/iio/proximity/Makefile
@@ -0,0 +1,13 @@
+# SPDX-License-Identifier: GPL-2.0
+#
+# Makefile for IIO proximity sensors
+#
+
+# When adding new entries keep the list in alphabetical order
+obj-$(CONFIG_AS3935)		+= as3935.o
+obj-$(CONFIG_ISL29501)		+= isl29501.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
new file mode 100644
index 0000000..f130388
--- /dev/null
+++ b/drivers/iio/proximity/as3935.c
@@ -0,0 +1,498 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * as3935.c - Support for AS3935 Franklin lightning sensor
+ *
+ * Copyright (C) 2014, 2017-2018
+ * Author: Matt Ranostay <matt.ranostay@konsulko.com>
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/delay.h>
+#include <linux/workqueue.h>
+#include <linux/mutex.h>
+#include <linux/err.h>
+#include <linux/irq.h>
+#include <linux/gpio.h>
+#include <linux/spi/spi.h>
+#include <linux/iio/iio.h>
+#include <linux/iio/sysfs.h>
+#include <linux/iio/trigger.h>
+#include <linux/iio/trigger_consumer.h>
+#include <linux/iio/buffer.h>
+#include <linux/iio/triggered_buffer.h>
+#include <linux/of_gpio.h>
+
+
+#define AS3935_AFE_GAIN		0x00
+#define AS3935_AFE_MASK		0x3F
+#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)
+
+#define AS3935_DATA		0x07
+#define AS3935_DATA_MASK	0x3F
+
+#define AS3935_TUNE_CAP		0x08
+#define AS3935_DEFAULTS		0x3C
+#define AS3935_CALIBRATE	0x3D
+
+#define AS3935_READ_DATA	BIT(14)
+#define AS3935_ADDRESS(x)	((x) << 8)
+
+#define MAX_PF_CAP		120
+#define TUNE_CAP_DIV		8
+
+struct as3935_state {
+	struct spi_device *spi;
+	struct iio_trigger *trig;
+	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;
+};
+
+static const struct iio_chan_spec as3935_channels[] = {
+	{
+		.type           = IIO_PROXIMITY,
+		.info_mask_separate =
+			BIT(IIO_CHAN_INFO_RAW) |
+			BIT(IIO_CHAN_INFO_PROCESSED) |
+			BIT(IIO_CHAN_INFO_SCALE),
+		.scan_index     = 0,
+		.scan_type = {
+			.sign           = 'u',
+			.realbits       = 6,
+			.storagebits    = 8,
+		},
+	},
+	IIO_CHAN_SOFT_TIMESTAMP(1),
+};
+
+static int as3935_read(struct as3935_state *st, unsigned int reg, int *val)
+{
+	u8 cmd;
+	int ret;
+
+	cmd = (AS3935_READ_DATA | AS3935_ADDRESS(reg)) >> 8;
+	ret = spi_w8r8(st->spi, cmd);
+	if (ret < 0)
+		return ret;
+	*val = ret;
+
+	return 0;
+}
+
+static int as3935_write(struct as3935_state *st,
+				unsigned int reg,
+				unsigned int val)
+{
+	u8 *buf = st->buf;
+
+	buf[0] = AS3935_ADDRESS(reg) >> 8;
+	buf[1] = val;
+
+	return spi_write(st->spi, buf, 2);
+}
+
+static ssize_t as3935_sensor_sensitivity_show(struct device *dev,
+					struct device_attribute *attr,
+					char *buf)
+{
+	struct as3935_state *st = iio_priv(dev_to_iio_dev(dev));
+	int val, ret;
+
+	ret = as3935_read(st, AS3935_AFE_GAIN, &val);
+	if (ret)
+		return ret;
+	val = (val & AS3935_AFE_MASK) >> 1;
+
+	return sprintf(buf, "%d\n", val);
+}
+
+static ssize_t as3935_sensor_sensitivity_store(struct device *dev,
+					struct device_attribute *attr,
+					const char *buf, size_t len)
+{
+	struct as3935_state *st = iio_priv(dev_to_iio_dev(dev));
+	unsigned long val;
+	int ret;
+
+	ret = kstrtoul((const char *) buf, 10, &val);
+	if (ret)
+		return -EINVAL;
+
+	if (val > AS3935_AFE_GAIN_MAX)
+		return -EINVAL;
+
+	as3935_write(st, AS3935_AFE_GAIN, val << 1);
+
+	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,
+};
+
+static const struct attribute_group as3935_attribute_group = {
+	.attrs = as3935_attributes,
+};
+
+static int as3935_read_raw(struct iio_dev *indio_dev,
+			   struct iio_chan_spec const *chan,
+			   int *val,
+			   int *val2,
+			   long m)
+{
+	struct as3935_state *st = iio_priv(indio_dev);
+	int ret;
+
+
+	switch (m) {
+	case IIO_CHAN_INFO_PROCESSED:
+	case IIO_CHAN_INFO_RAW:
+		*val2 = 0;
+		ret = as3935_read(st, AS3935_DATA, val);
+		if (ret)
+			return ret;
+
+		/* storm out of range */
+		if (*val == AS3935_DATA_MASK)
+			return -EINVAL;
+
+		if (m == IIO_CHAN_INFO_RAW)
+			return IIO_VAL_INT;
+
+		if (m == IIO_CHAN_INFO_PROCESSED)
+			*val *= 1000;
+		break;
+	case IIO_CHAN_INFO_SCALE:
+		*val = 1000;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	return IIO_VAL_INT;
+}
+
+static const struct iio_info as3935_info = {
+	.attrs = &as3935_attribute_group,
+	.read_raw = &as3935_read_raw,
+};
+
+static irqreturn_t as3935_trigger_handler(int irq, void *private)
+{
+	struct iio_poll_func *pf = private;
+	struct iio_dev *indio_dev = pf->indio_dev;
+	struct as3935_state *st = iio_priv(indio_dev);
+	int val, ret;
+
+	ret = as3935_read(st, AS3935_DATA, &val);
+	if (ret)
+		goto err_read;
+
+	st->buffer[0] = val & AS3935_DATA_MASK;
+	iio_push_to_buffers_with_timestamp(indio_dev, &st->buffer,
+					   iio_get_time_ns(indio_dev));
+err_read:
+	iio_trigger_notify_done(indio_dev->trig);
+
+	return IRQ_HANDLED;
+}
+
+static const struct iio_trigger_ops iio_interrupt_trigger_ops = {
+};
+
+static void as3935_event_work(struct work_struct *work)
+{
+	struct as3935_state *st;
+	int val;
+	int ret;
+
+	st = container_of(work, struct as3935_state, work.work);
+
+	ret = as3935_read(st, AS3935_INT, &val);
+	if (ret) {
+		dev_warn(&st->spi->dev, "read error\n");
+		return;
+	}
+
+	val &= AS3935_INT_MASK;
+
+	switch (val) {
+	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;
+	}
+}
+
+static irqreturn_t as3935_interrupt_handler(int irq, void *private)
+{
+	struct iio_dev *indio_dev = private;
+	struct as3935_state *st = iio_priv(indio_dev);
+
+	/*
+	 * Delay work for >2 milliseconds after an interrupt to allow
+	 * estimated distance to recalculated.
+	 */
+
+	schedule_delayed_work(&st->work, msecs_to_jiffies(3));
+
+	return IRQ_HANDLED;
+}
+
+static void calibrate_as3935(struct as3935_state *st)
+{
+	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
+static int as3935_suspend(struct device *dev)
+{
+	struct iio_dev *indio_dev = dev_get_drvdata(dev);
+	struct as3935_state *st = iio_priv(indio_dev);
+	int val, ret;
+
+	mutex_lock(&st->lock);
+	ret = as3935_read(st, AS3935_AFE_GAIN, &val);
+	if (ret)
+		goto err_suspend;
+	val |= AS3935_AFE_PWR_BIT;
+
+	ret = as3935_write(st, AS3935_AFE_GAIN, val);
+
+err_suspend:
+	mutex_unlock(&st->lock);
+
+	return ret;
+}
+
+static int as3935_resume(struct device *dev)
+{
+	struct iio_dev *indio_dev = dev_get_drvdata(dev);
+	struct as3935_state *st = iio_priv(indio_dev);
+	int val, ret;
+
+	mutex_lock(&st->lock);
+	ret = as3935_read(st, AS3935_AFE_GAIN, &val);
+	if (ret)
+		goto err_resume;
+	val &= ~AS3935_AFE_PWR_BIT;
+	ret = as3935_write(st, AS3935_AFE_GAIN, val);
+
+	calibrate_as3935(st);
+
+err_resume:
+	mutex_unlock(&st->lock);
+
+	return ret;
+}
+
+static SIMPLE_DEV_PM_OPS(as3935_pm_ops, as3935_suspend, as3935_resume);
+#define AS3935_PM_OPS (&as3935_pm_ops)
+
+#else
+#define AS3935_PM_OPS NULL
+#endif
+
+static int as3935_probe(struct spi_device *spi)
+{
+	struct iio_dev *indio_dev;
+	struct iio_trigger *trig;
+	struct as3935_state *st;
+	struct device_node *np = spi->dev.of_node;
+	int ret;
+
+	/* Be sure lightning event interrupt is specified */
+	if (!spi->irq) {
+		dev_err(&spi->dev, "unable to get event interrupt\n");
+		return -EINVAL;
+	}
+
+	indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*st));
+	if (!indio_dev)
+		return -ENOMEM;
+
+	st = iio_priv(indio_dev);
+	st->spi = spi;
+
+	spi_set_drvdata(spi, indio_dev);
+	mutex_init(&st->lock);
+	INIT_DELAYED_WORK(&st->work, as3935_event_work);
+
+	ret = of_property_read_u32(np,
+			"ams,tuning-capacitor-pf", &st->tune_cap);
+	if (ret) {
+		st->tune_cap = 0;
+		dev_warn(&spi->dev,
+			"no tuning-capacitor-pf set, defaulting to %d",
+			st->tune_cap);
+	}
+
+	if (st->tune_cap > MAX_PF_CAP) {
+		dev_err(&spi->dev,
+			"wrong tuning-capacitor-pf setting of %d\n",
+			st->tune_cap);
+		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;
+	indio_dev->num_channels = ARRAY_SIZE(as3935_channels);
+	indio_dev->modes = INDIO_DIRECT_MODE;
+	indio_dev->info = &as3935_info;
+
+	trig = devm_iio_trigger_alloc(&spi->dev, "%s-dev%d",
+				      indio_dev->name, indio_dev->id);
+
+	if (!trig)
+		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;
+
+	ret = iio_trigger_register(trig);
+	if (ret) {
+		dev_err(&spi->dev, "failed to register trigger\n");
+		return ret;
+	}
+
+	ret = iio_triggered_buffer_setup(indio_dev, iio_pollfunc_store_time,
+		&as3935_trigger_handler, NULL);
+
+	if (ret) {
+		dev_err(&spi->dev, "cannot setup iio trigger\n");
+		goto unregister_trigger;
+	}
+
+	calibrate_as3935(st);
+
+	ret = devm_request_irq(&spi->dev, spi->irq,
+				&as3935_interrupt_handler,
+				IRQF_TRIGGER_RISING,
+				dev_name(&spi->dev),
+				indio_dev);
+
+	if (ret) {
+		dev_err(&spi->dev, "unable to request irq\n");
+		goto unregister_buffer;
+	}
+
+	ret = iio_device_register(indio_dev);
+	if (ret < 0) {
+		dev_err(&spi->dev, "unable to register device\n");
+		goto unregister_buffer;
+	}
+	return 0;
+
+unregister_buffer:
+	iio_triggered_buffer_cleanup(indio_dev);
+
+unregister_trigger:
+	iio_trigger_unregister(st->trig);
+
+	return ret;
+}
+
+static int as3935_remove(struct spi_device *spi)
+{
+	struct iio_dev *indio_dev = spi_get_drvdata(spi);
+	struct as3935_state *st = iio_priv(indio_dev);
+
+	iio_device_unregister(indio_dev);
+	iio_triggered_buffer_cleanup(indio_dev);
+	iio_trigger_unregister(st->trig);
+
+	return 0;
+}
+
+static const struct of_device_id as3935_of_match[] = {
+	{ .compatible = "ams,as3935", },
+	{ /* sentinel */ },
+};
+MODULE_DEVICE_TABLE(of, as3935_of_match);
+
+static const struct spi_device_id as3935_id[] = {
+	{"as3935", 0},
+	{},
+};
+MODULE_DEVICE_TABLE(spi, as3935_id);
+
+static struct spi_driver as3935_driver = {
+	.driver = {
+		.name	= "as3935",
+		.of_match_table = of_match_ptr(as3935_of_match),
+		.pm	= AS3935_PM_OPS,
+	},
+	.probe		= as3935_probe,
+	.remove		= as3935_remove,
+	.id_table	= as3935_id,
+};
+module_spi_driver(as3935_driver);
+
+MODULE_AUTHOR("Matt Ranostay <matt.ranostay@konsulko.com>");
+MODULE_DESCRIPTION("AS3935 lightning sensor");
+MODULE_LICENSE("GPL");
diff --git a/drivers/iio/proximity/isl29501.c b/drivers/iio/proximity/isl29501.c
new file mode 100644
index 0000000..e5e9454
--- /dev/null
+++ b/drivers/iio/proximity/isl29501.c
@@ -0,0 +1,1027 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * isl29501.c: ISL29501 Time of Flight sensor driver.
+ *
+ * Copyright (C) 2018
+ * Author: Mathieu Othacehe <m.othacehe@gmail.com>
+ *
+ * 7-bit I2C slave address: 0x57
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/i2c.h>
+#include <linux/err.h>
+#include <linux/of_device.h>
+#include <linux/iio/iio.h>
+#include <linux/iio/sysfs.h>
+
+#include <linux/iio/trigger_consumer.h>
+#include <linux/iio/buffer.h>
+#include <linux/iio/triggered_buffer.h>
+
+/* Control, setting and status registers */
+#define ISL29501_DEVICE_ID			0x00
+#define ISL29501_ID				0x0A
+
+/* Sampling control registers */
+#define ISL29501_INTEGRATION_PERIOD		0x10
+#define ISL29501_SAMPLE_PERIOD			0x11
+
+/* Closed loop calibration registers */
+#define ISL29501_CROSSTALK_I_MSB		0x24
+#define ISL29501_CROSSTALK_I_LSB		0x25
+#define ISL29501_CROSSTALK_I_EXPONENT		0x26
+#define ISL29501_CROSSTALK_Q_MSB		0x27
+#define ISL29501_CROSSTALK_Q_LSB		0x28
+#define ISL29501_CROSSTALK_Q_EXPONENT		0x29
+#define ISL29501_CROSSTALK_GAIN_MSB		0x2A
+#define ISL29501_CROSSTALK_GAIN_LSB		0x2B
+#define ISL29501_MAGNITUDE_REF_EXP		0x2C
+#define ISL29501_MAGNITUDE_REF_MSB		0x2D
+#define ISL29501_MAGNITUDE_REF_LSB		0x2E
+#define ISL29501_PHASE_OFFSET_MSB		0x2F
+#define ISL29501_PHASE_OFFSET_LSB		0x30
+
+/* Analog control registers */
+#define ISL29501_DRIVER_RANGE			0x90
+#define ISL29501_EMITTER_DAC			0x91
+
+#define ISL29501_COMMAND_REGISTER		0xB0
+
+/* Commands */
+#define ISL29501_EMUL_SAMPLE_START_PIN		0x49
+#define ISL29501_RESET_ALL_REGISTERS		0xD7
+#define ISL29501_RESET_INT_SM			0xD1
+
+/* Ambiant light and temperature corrections */
+#define ISL29501_TEMP_REFERENCE			0x31
+#define ISL29501_PHASE_EXPONENT			0x33
+#define ISL29501_TEMP_COEFF_A			0x34
+#define ISL29501_TEMP_COEFF_B			0x39
+#define ISL29501_AMBIANT_COEFF_A		0x36
+#define ISL29501_AMBIANT_COEFF_B		0x3B
+
+/* Data output registers */
+#define ISL29501_DISTANCE_MSB_DATA		0xD1
+#define ISL29501_DISTANCE_LSB_DATA		0xD2
+#define ISL29501_PRECISION_MSB			0xD3
+#define ISL29501_PRECISION_LSB			0xD4
+#define ISL29501_MAGNITUDE_EXPONENT		0xD5
+#define ISL29501_MAGNITUDE_MSB			0xD6
+#define ISL29501_MAGNITUDE_LSB			0xD7
+#define ISL29501_PHASE_MSB			0xD8
+#define ISL29501_PHASE_LSB			0xD9
+#define ISL29501_I_RAW_EXPONENT			0xDA
+#define ISL29501_I_RAW_MSB			0xDB
+#define ISL29501_I_RAW_LSB			0xDC
+#define ISL29501_Q_RAW_EXPONENT			0xDD
+#define ISL29501_Q_RAW_MSB			0xDE
+#define ISL29501_Q_RAW_LSB			0xDF
+#define ISL29501_DIE_TEMPERATURE		0xE2
+#define ISL29501_AMBIENT_LIGHT			0xE3
+#define ISL29501_GAIN_MSB			0xE6
+#define ISL29501_GAIN_LSB			0xE7
+
+#define ISL29501_MAX_EXP_VAL 15
+
+#define ISL29501_INT_TIME_AVAILABLE \
+	"0.00007 0.00014 0.00028 0.00057 0.00114 " \
+	"0.00228 0.00455 0.00910 0.01820 0.03640 " \
+	"0.07281 0.14561"
+
+#define ISL29501_CURRENT_SCALE_AVAILABLE \
+	"0.0039 0.0078 0.0118 0.0157 0.0196 " \
+	"0.0235 0.0275 0.0314 0.0352 0.0392 " \
+	"0.0431 0.0471 0.0510 0.0549 0.0588"
+
+enum isl29501_correction_coeff {
+	COEFF_TEMP_A,
+	COEFF_TEMP_B,
+	COEFF_LIGHT_A,
+	COEFF_LIGHT_B,
+	COEFF_MAX,
+};
+
+struct isl29501_private {
+	struct i2c_client *client;
+	struct mutex lock;
+	/* Exact representation of correction coefficients. */
+	unsigned int shadow_coeffs[COEFF_MAX];
+};
+
+enum isl29501_register_name {
+	REG_DISTANCE,
+	REG_PHASE,
+	REG_TEMPERATURE,
+	REG_AMBIENT_LIGHT,
+	REG_GAIN,
+	REG_GAIN_BIAS,
+	REG_PHASE_EXP,
+	REG_CALIB_PHASE_TEMP_A,
+	REG_CALIB_PHASE_TEMP_B,
+	REG_CALIB_PHASE_LIGHT_A,
+	REG_CALIB_PHASE_LIGHT_B,
+	REG_DISTANCE_BIAS,
+	REG_TEMPERATURE_BIAS,
+	REG_INT_TIME,
+	REG_SAMPLE_TIME,
+	REG_DRIVER_RANGE,
+	REG_EMITTER_DAC,
+};
+
+struct isl29501_register_desc {
+	u8 msb;
+	u8 lsb;
+};
+
+static const struct isl29501_register_desc isl29501_registers[] = {
+	[REG_DISTANCE] = {
+		.msb = ISL29501_DISTANCE_MSB_DATA,
+		.lsb = ISL29501_DISTANCE_LSB_DATA,
+	},
+	[REG_PHASE] = {
+		.msb = ISL29501_PHASE_MSB,
+		.lsb = ISL29501_PHASE_LSB,
+	},
+	[REG_TEMPERATURE] = {
+		.lsb = ISL29501_DIE_TEMPERATURE,
+	},
+	[REG_AMBIENT_LIGHT] = {
+		.lsb = ISL29501_AMBIENT_LIGHT,
+	},
+	[REG_GAIN] = {
+		.msb = ISL29501_GAIN_MSB,
+		.lsb = ISL29501_GAIN_LSB,
+	},
+	[REG_GAIN_BIAS] = {
+		.msb = ISL29501_CROSSTALK_GAIN_MSB,
+		.lsb = ISL29501_CROSSTALK_GAIN_LSB,
+	},
+	[REG_PHASE_EXP] = {
+		.lsb = ISL29501_PHASE_EXPONENT,
+	},
+	[REG_CALIB_PHASE_TEMP_A] = {
+		.lsb = ISL29501_TEMP_COEFF_A,
+	},
+	[REG_CALIB_PHASE_TEMP_B] = {
+		.lsb = ISL29501_TEMP_COEFF_B,
+	},
+	[REG_CALIB_PHASE_LIGHT_A] = {
+		.lsb = ISL29501_AMBIANT_COEFF_A,
+	},
+	[REG_CALIB_PHASE_LIGHT_B] = {
+		.lsb = ISL29501_AMBIANT_COEFF_B,
+	},
+	[REG_DISTANCE_BIAS] = {
+		.msb = ISL29501_PHASE_OFFSET_MSB,
+		.lsb = ISL29501_PHASE_OFFSET_LSB,
+	},
+	[REG_TEMPERATURE_BIAS] = {
+		.lsb = ISL29501_TEMP_REFERENCE,
+	},
+	[REG_INT_TIME] = {
+		.lsb = ISL29501_INTEGRATION_PERIOD,
+	},
+	[REG_SAMPLE_TIME] = {
+		.lsb = ISL29501_SAMPLE_PERIOD,
+	},
+	[REG_DRIVER_RANGE] = {
+		.lsb = ISL29501_DRIVER_RANGE,
+	},
+	[REG_EMITTER_DAC] = {
+		.lsb = ISL29501_EMITTER_DAC,
+	},
+};
+
+static int isl29501_register_read(struct isl29501_private *isl29501,
+				  enum isl29501_register_name name,
+				  u32 *val)
+{
+	const struct isl29501_register_desc *reg = &isl29501_registers[name];
+	u8 msb = 0, lsb = 0;
+	s32 ret;
+
+	mutex_lock(&isl29501->lock);
+	if (reg->msb) {
+		ret = i2c_smbus_read_byte_data(isl29501->client, reg->msb);
+		if (ret < 0)
+			goto err;
+		msb = ret;
+	}
+
+	if (reg->lsb) {
+		ret = i2c_smbus_read_byte_data(isl29501->client, reg->lsb);
+		if (ret < 0)
+			goto err;
+		lsb = ret;
+	}
+	mutex_unlock(&isl29501->lock);
+
+	*val = (msb << 8) + lsb;
+
+	return 0;
+err:
+	mutex_unlock(&isl29501->lock);
+
+	return ret;
+}
+
+static u32 isl29501_register_write(struct isl29501_private *isl29501,
+				   enum isl29501_register_name name,
+				   u32 value)
+{
+	const struct isl29501_register_desc *reg = &isl29501_registers[name];
+	u8 msb, lsb;
+	int ret;
+
+	if (!reg->msb && value > U8_MAX)
+		return -ERANGE;
+
+	if (value > U16_MAX)
+		return -ERANGE;
+
+	if (!reg->msb) {
+		lsb = value & 0xFF;
+	} else {
+		msb = (value >> 8) & 0xFF;
+		lsb = value & 0xFF;
+	}
+
+	mutex_lock(&isl29501->lock);
+	if (reg->msb) {
+		ret = i2c_smbus_write_byte_data(isl29501->client,
+						reg->msb, msb);
+		if (ret < 0)
+			goto err;
+	}
+
+	ret = i2c_smbus_write_byte_data(isl29501->client, reg->lsb, lsb);
+
+err:
+	mutex_unlock(&isl29501->lock);
+	return ret;
+}
+
+static ssize_t isl29501_read_ext(struct iio_dev *indio_dev,
+				 uintptr_t private,
+				 const struct iio_chan_spec *chan,
+				 char *buf)
+{
+	struct isl29501_private *isl29501 = iio_priv(indio_dev);
+	enum isl29501_register_name reg = private;
+	int ret;
+	u32 value, gain, coeff, exp;
+
+	switch (reg) {
+	case REG_GAIN:
+	case REG_GAIN_BIAS:
+		ret = isl29501_register_read(isl29501, reg, &gain);
+		if (ret < 0)
+			return ret;
+
+		value = gain;
+		break;
+	case REG_CALIB_PHASE_TEMP_A:
+	case REG_CALIB_PHASE_TEMP_B:
+	case REG_CALIB_PHASE_LIGHT_A:
+	case REG_CALIB_PHASE_LIGHT_B:
+		ret = isl29501_register_read(isl29501, REG_PHASE_EXP, &exp);
+		if (ret < 0)
+			return ret;
+
+		ret = isl29501_register_read(isl29501, reg, &coeff);
+		if (ret < 0)
+			return ret;
+
+		value = coeff << exp;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	return sprintf(buf, "%u\n", value);
+}
+
+static int isl29501_set_shadow_coeff(struct isl29501_private *isl29501,
+				     enum isl29501_register_name reg,
+				     unsigned int val)
+{
+	enum isl29501_correction_coeff coeff;
+
+	switch (reg) {
+	case REG_CALIB_PHASE_TEMP_A:
+		coeff = COEFF_TEMP_A;
+		break;
+	case REG_CALIB_PHASE_TEMP_B:
+		coeff = COEFF_TEMP_B;
+		break;
+	case REG_CALIB_PHASE_LIGHT_A:
+		coeff = COEFF_LIGHT_A;
+		break;
+	case REG_CALIB_PHASE_LIGHT_B:
+		coeff = COEFF_LIGHT_B;
+		break;
+	default:
+		return -EINVAL;
+	}
+	isl29501->shadow_coeffs[coeff] = val;
+
+	return 0;
+}
+
+static int isl29501_write_coeff(struct isl29501_private *isl29501,
+				enum isl29501_correction_coeff coeff,
+				int val)
+{
+	enum isl29501_register_name reg;
+
+	switch (coeff) {
+	case COEFF_TEMP_A:
+		reg = REG_CALIB_PHASE_TEMP_A;
+		break;
+	case COEFF_TEMP_B:
+		reg = REG_CALIB_PHASE_TEMP_B;
+		break;
+	case COEFF_LIGHT_A:
+		reg = REG_CALIB_PHASE_LIGHT_A;
+		break;
+	case COEFF_LIGHT_B:
+		reg = REG_CALIB_PHASE_LIGHT_B;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	return isl29501_register_write(isl29501, reg, val);
+}
+
+static unsigned int isl29501_find_corr_exp(unsigned int val,
+					   unsigned int max_exp,
+					   unsigned int max_mantissa)
+{
+	unsigned int exp = 1;
+
+	/*
+	 * Correction coefficients are represented under
+	 * mantissa * 2^exponent form, where mantissa and exponent
+	 * are stored in two separate registers of the sensor.
+	 *
+	 * Compute and return the lowest exponent such as:
+	 *	     mantissa = value / 2^exponent
+	 *
+	 *  where mantissa < max_mantissa.
+	 */
+	if (val <= max_mantissa)
+		return 0;
+
+	while ((val >> exp) > max_mantissa) {
+		exp++;
+
+		if (exp > max_exp)
+			return max_exp;
+	}
+
+	return exp;
+}
+
+static ssize_t isl29501_write_ext(struct iio_dev *indio_dev,
+				  uintptr_t private,
+				  const struct iio_chan_spec *chan,
+				  const char *buf, size_t len)
+{
+	struct isl29501_private *isl29501 = iio_priv(indio_dev);
+	enum isl29501_register_name reg = private;
+	unsigned int val;
+	int max_exp = 0;
+	int ret;
+	int i;
+
+	ret = kstrtouint(buf, 10, &val);
+	if (ret)
+		return ret;
+
+	switch (reg) {
+	case REG_GAIN_BIAS:
+		if (val > U16_MAX)
+			return -ERANGE;
+
+		ret = isl29501_register_write(isl29501, reg, val);
+		if (ret < 0)
+			return ret;
+
+		break;
+	case REG_CALIB_PHASE_TEMP_A:
+	case REG_CALIB_PHASE_TEMP_B:
+	case REG_CALIB_PHASE_LIGHT_A:
+	case REG_CALIB_PHASE_LIGHT_B:
+
+		if (val > (U8_MAX << ISL29501_MAX_EXP_VAL))
+			return -ERANGE;
+
+		/* Store the correction coefficient under its exact form. */
+		ret = isl29501_set_shadow_coeff(isl29501, reg, val);
+		if (ret < 0)
+			return ret;
+
+		/*
+		 * Find the highest exponent needed to represent
+		 * correction coefficients.
+		 */
+		for (i = 0; i < COEFF_MAX; i++) {
+			int corr;
+			int corr_exp;
+
+			corr = isl29501->shadow_coeffs[i];
+			corr_exp = isl29501_find_corr_exp(corr,
+							  ISL29501_MAX_EXP_VAL,
+							  U8_MAX / 2);
+			dev_dbg(&isl29501->client->dev,
+				"found exp of corr(%d) = %d\n", corr, corr_exp);
+
+			max_exp = max(max_exp, corr_exp);
+		}
+
+		/*
+		 * Represent every correction coefficient under
+		 * mantissa * 2^max_exponent form and force the
+		 * writing of those coefficients on the sensor.
+		 */
+		for (i = 0; i < COEFF_MAX; i++) {
+			int corr;
+			int mantissa;
+
+			corr = isl29501->shadow_coeffs[i];
+			if (!corr)
+				continue;
+
+			mantissa = corr >> max_exp;
+
+			ret = isl29501_write_coeff(isl29501, i, mantissa);
+			if (ret < 0)
+				return ret;
+		}
+
+		ret = isl29501_register_write(isl29501, REG_PHASE_EXP, max_exp);
+		if (ret < 0)
+			return ret;
+
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	return len;
+}
+
+#define _ISL29501_EXT_INFO(_name, _ident) { \
+	.name = _name, \
+	.read = isl29501_read_ext, \
+	.write = isl29501_write_ext, \
+	.private = _ident, \
+	.shared = IIO_SEPARATE, \
+}
+
+static const struct iio_chan_spec_ext_info isl29501_ext_info[] = {
+	_ISL29501_EXT_INFO("agc_gain", REG_GAIN),
+	_ISL29501_EXT_INFO("agc_gain_bias", REG_GAIN_BIAS),
+	_ISL29501_EXT_INFO("calib_phase_temp_a", REG_CALIB_PHASE_TEMP_A),
+	_ISL29501_EXT_INFO("calib_phase_temp_b", REG_CALIB_PHASE_TEMP_B),
+	_ISL29501_EXT_INFO("calib_phase_light_a", REG_CALIB_PHASE_LIGHT_A),
+	_ISL29501_EXT_INFO("calib_phase_light_b", REG_CALIB_PHASE_LIGHT_B),
+	{ },
+};
+
+#define ISL29501_DISTANCE_SCAN_INDEX 0
+#define ISL29501_TIMESTAMP_SCAN_INDEX 1
+
+static const struct iio_chan_spec isl29501_channels[] = {
+	{
+		.type = IIO_PROXIMITY,
+		.scan_index = ISL29501_DISTANCE_SCAN_INDEX,
+		.info_mask_separate =
+			BIT(IIO_CHAN_INFO_RAW)   |
+			BIT(IIO_CHAN_INFO_SCALE) |
+			BIT(IIO_CHAN_INFO_CALIBBIAS),
+		.scan_type = {
+			.sign = 'u',
+			.realbits = 16,
+			.storagebits = 16,
+			.endianness = IIO_CPU,
+		},
+		.info_mask_shared_by_all = BIT(IIO_CHAN_INFO_INT_TIME) |
+				BIT(IIO_CHAN_INFO_SAMP_FREQ),
+		.ext_info = isl29501_ext_info,
+	},
+	{
+		.type = IIO_PHASE,
+		.scan_index = -1,
+		.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
+				BIT(IIO_CHAN_INFO_SCALE),
+	},
+	{
+		.type = IIO_CURRENT,
+		.scan_index = -1,
+		.output = 1,
+		.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
+				BIT(IIO_CHAN_INFO_SCALE),
+	},
+	{
+		.type = IIO_TEMP,
+		.scan_index = -1,
+		.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
+				BIT(IIO_CHAN_INFO_SCALE)     |
+				BIT(IIO_CHAN_INFO_CALIBBIAS),
+	},
+	{
+		.type = IIO_INTENSITY,
+		.scan_index = -1,
+		.modified = 1,
+		.channel2 = IIO_MOD_LIGHT_CLEAR,
+		.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
+				BIT(IIO_CHAN_INFO_SCALE),
+	},
+	IIO_CHAN_SOFT_TIMESTAMP(ISL29501_TIMESTAMP_SCAN_INDEX),
+};
+
+static int isl29501_reset_registers(struct isl29501_private *isl29501)
+{
+	int ret;
+
+	ret = i2c_smbus_write_byte_data(isl29501->client,
+					ISL29501_COMMAND_REGISTER,
+					ISL29501_RESET_ALL_REGISTERS);
+	if (ret < 0) {
+		dev_err(&isl29501->client->dev,
+			"cannot reset registers %d\n", ret);
+		return ret;
+	}
+
+	ret = i2c_smbus_write_byte_data(isl29501->client,
+					ISL29501_COMMAND_REGISTER,
+					ISL29501_RESET_INT_SM);
+	if (ret < 0)
+		dev_err(&isl29501->client->dev,
+			"cannot reset state machine %d\n", ret);
+
+	return ret;
+}
+
+static int isl29501_begin_acquisition(struct isl29501_private *isl29501)
+{
+	int ret;
+
+	ret = i2c_smbus_write_byte_data(isl29501->client,
+					ISL29501_COMMAND_REGISTER,
+					ISL29501_EMUL_SAMPLE_START_PIN);
+	if (ret < 0)
+		dev_err(&isl29501->client->dev,
+			"cannot begin acquisition %d\n", ret);
+
+	return ret;
+}
+
+static IIO_CONST_ATTR_INT_TIME_AVAIL(ISL29501_INT_TIME_AVAILABLE);
+static IIO_CONST_ATTR(out_current_scale_available,
+		      ISL29501_CURRENT_SCALE_AVAILABLE);
+
+static struct attribute *isl29501_attributes[] = {
+	&iio_const_attr_integration_time_available.dev_attr.attr,
+	&iio_const_attr_out_current_scale_available.dev_attr.attr,
+	NULL
+};
+
+static const struct attribute_group isl29501_attribute_group = {
+	.attrs = isl29501_attributes,
+};
+
+static const int isl29501_current_scale_table[][2] = {
+	{0, 3900}, {0, 7800}, {0, 11800}, {0, 15700},
+	{0, 19600}, {0, 23500}, {0, 27500}, {0, 31400},
+	{0, 35200}, {0, 39200}, {0, 43100}, {0, 47100},
+	{0, 51000}, {0, 54900}, {0, 58800},
+};
+
+static const int isl29501_int_time[][2] = {
+	{0, 70},    /* 0.07 ms */
+	{0, 140},   /* 0.14 ms */
+	{0, 280},   /* 0.28 ms */
+	{0, 570},   /* 0.57 ms */
+	{0, 1140},  /* 1.14 ms */
+	{0, 2280},  /* 2.28 ms */
+	{0, 4550},  /* 4.55 ms */
+	{0, 9100},  /* 9.11 ms */
+	{0, 18200}, /* 18.2 ms */
+	{0, 36400}, /* 36.4 ms */
+	{0, 72810}, /* 72.81 ms */
+	{0, 145610} /* 145.28 ms */
+};
+
+static int isl29501_get_raw(struct isl29501_private *isl29501,
+			    const struct iio_chan_spec *chan,
+			    int *raw)
+{
+	int ret;
+
+	switch (chan->type) {
+	case IIO_PROXIMITY:
+		ret = isl29501_register_read(isl29501, REG_DISTANCE, raw);
+		if (ret < 0)
+			return ret;
+
+		return IIO_VAL_INT;
+	case IIO_INTENSITY:
+		ret = isl29501_register_read(isl29501,
+					     REG_AMBIENT_LIGHT,
+					     raw);
+		if (ret < 0)
+			return ret;
+
+		return IIO_VAL_INT;
+	case IIO_PHASE:
+		ret = isl29501_register_read(isl29501, REG_PHASE, raw);
+		if (ret < 0)
+			return ret;
+
+		return IIO_VAL_INT;
+	case IIO_CURRENT:
+		ret = isl29501_register_read(isl29501, REG_EMITTER_DAC, raw);
+		if (ret < 0)
+			return ret;
+
+		return IIO_VAL_INT;
+	case IIO_TEMP:
+		ret = isl29501_register_read(isl29501, REG_TEMPERATURE, raw);
+		if (ret < 0)
+			return ret;
+
+		return IIO_VAL_INT;
+	default:
+		return -EINVAL;
+	}
+}
+
+static int isl29501_get_scale(struct isl29501_private *isl29501,
+			      const struct iio_chan_spec *chan,
+			      int *val, int *val2)
+{
+	int ret;
+	u32 current_scale;
+
+	switch (chan->type) {
+	case IIO_PROXIMITY:
+		/* distance = raw_distance * 33.31 / 65536 (m) */
+		*val = 3331;
+		*val2 = 6553600;
+
+		return IIO_VAL_FRACTIONAL;
+	case IIO_PHASE:
+		/* phase = raw_phase * 2pi / 65536 (rad) */
+		*val = 0;
+		*val2 = 95874;
+
+		return IIO_VAL_INT_PLUS_NANO;
+	case IIO_INTENSITY:
+		/* light = raw_light * 35 / 10000 (mA) */
+		*val = 35;
+		*val2 = 10000;
+
+		return IIO_VAL_FRACTIONAL;
+	case IIO_CURRENT:
+		ret = isl29501_register_read(isl29501,
+					     REG_DRIVER_RANGE,
+					     &current_scale);
+		if (ret < 0)
+			return ret;
+
+		if (current_scale > ARRAY_SIZE(isl29501_current_scale_table))
+			return -EINVAL;
+
+		if (!current_scale) {
+			*val = 0;
+			*val2 = 0;
+			return IIO_VAL_INT;
+		}
+
+		*val = isl29501_current_scale_table[current_scale - 1][0];
+		*val2 = isl29501_current_scale_table[current_scale - 1][1];
+
+		return IIO_VAL_INT_PLUS_MICRO;
+	case IIO_TEMP:
+		/* temperature = raw_temperature * 125 / 100000 (milli °C) */
+		*val = 125;
+		*val2 = 100000;
+
+		return IIO_VAL_FRACTIONAL;
+	default:
+		return -EINVAL;
+	}
+}
+
+static int isl29501_get_calibbias(struct isl29501_private *isl29501,
+				  const struct iio_chan_spec *chan,
+				  int *bias)
+{
+	switch (chan->type) {
+	case IIO_PROXIMITY:
+		return isl29501_register_read(isl29501,
+					      REG_DISTANCE_BIAS,
+					      bias);
+	case IIO_TEMP:
+		return isl29501_register_read(isl29501,
+					      REG_TEMPERATURE_BIAS,
+					      bias);
+	default:
+		return -EINVAL;
+	}
+}
+
+static int isl29501_get_inttime(struct isl29501_private *isl29501,
+				int *val, int *val2)
+{
+	int ret;
+	u32 inttime;
+
+	ret = isl29501_register_read(isl29501, REG_INT_TIME, &inttime);
+	if (ret < 0)
+		return ret;
+
+	if (inttime >= ARRAY_SIZE(isl29501_int_time))
+		return -EINVAL;
+
+	*val = isl29501_int_time[inttime][0];
+	*val2 = isl29501_int_time[inttime][1];
+
+	return IIO_VAL_INT_PLUS_MICRO;
+}
+
+static int isl29501_get_freq(struct isl29501_private *isl29501,
+			     int *val, int *val2)
+{
+	int ret;
+	int sample_time;
+	unsigned long long freq;
+	u32 temp;
+
+	ret = isl29501_register_read(isl29501, REG_SAMPLE_TIME, &sample_time);
+	if (ret < 0)
+		return ret;
+
+	/* freq = 1 / (0.000450 * (sample_time + 1) * 10^-6) */
+	freq = 1000000ULL * 1000000ULL;
+
+	do_div(freq, 450 * (sample_time + 1));
+
+	temp = do_div(freq, 1000000);
+	*val = freq;
+	*val2 = temp;
+
+	return IIO_VAL_INT_PLUS_MICRO;
+}
+
+static int isl29501_read_raw(struct iio_dev *indio_dev,
+			     struct iio_chan_spec const *chan, int *val,
+			     int *val2, long mask)
+{
+	struct isl29501_private *isl29501 = iio_priv(indio_dev);
+
+	switch (mask) {
+	case IIO_CHAN_INFO_RAW:
+		return isl29501_get_raw(isl29501, chan, val);
+	case IIO_CHAN_INFO_SCALE:
+		return isl29501_get_scale(isl29501, chan, val, val2);
+	case IIO_CHAN_INFO_INT_TIME:
+		return isl29501_get_inttime(isl29501, val, val2);
+	case IIO_CHAN_INFO_SAMP_FREQ:
+		return isl29501_get_freq(isl29501, val, val2);
+	case IIO_CHAN_INFO_CALIBBIAS:
+		return isl29501_get_calibbias(isl29501, chan, val);
+	default:
+		return -EINVAL;
+	}
+}
+
+static int isl29501_set_raw(struct isl29501_private *isl29501,
+			    const struct iio_chan_spec *chan,
+			    int raw)
+{
+	switch (chan->type) {
+	case IIO_CURRENT:
+		return isl29501_register_write(isl29501, REG_EMITTER_DAC, raw);
+	default:
+		return -EINVAL;
+	}
+}
+
+static int isl29501_set_inttime(struct isl29501_private *isl29501,
+				int val, int val2)
+{
+	int i;
+
+	for (i = 0; i < ARRAY_SIZE(isl29501_int_time); i++) {
+		if (isl29501_int_time[i][0] == val &&
+		    isl29501_int_time[i][1] == val2) {
+			return isl29501_register_write(isl29501,
+						       REG_INT_TIME,
+						       i);
+		}
+	}
+
+	return -EINVAL;
+}
+
+static int isl29501_set_scale(struct isl29501_private *isl29501,
+			      const struct iio_chan_spec *chan,
+			      int val, int val2)
+{
+	int i;
+
+	if (chan->type != IIO_CURRENT)
+		return -EINVAL;
+
+	for (i = 0; i < ARRAY_SIZE(isl29501_current_scale_table); i++) {
+		if (isl29501_current_scale_table[i][0] == val &&
+		    isl29501_current_scale_table[i][1] == val2) {
+			return isl29501_register_write(isl29501,
+						       REG_DRIVER_RANGE,
+						       i + 1);
+		}
+	}
+
+	return -EINVAL;
+}
+
+static int isl29501_set_calibbias(struct isl29501_private *isl29501,
+				  const struct iio_chan_spec *chan,
+				  int bias)
+{
+	switch (chan->type) {
+	case IIO_PROXIMITY:
+		return isl29501_register_write(isl29501,
+					      REG_DISTANCE_BIAS,
+					      bias);
+	case IIO_TEMP:
+		return isl29501_register_write(isl29501,
+					       REG_TEMPERATURE_BIAS,
+					       bias);
+	default:
+		return -EINVAL;
+	}
+}
+
+static int isl29501_set_freq(struct isl29501_private *isl29501,
+			     int val, int val2)
+{
+	int freq;
+	unsigned long long sample_time;
+
+	/* sample_freq = 1 / (0.000450 * (sample_time + 1) * 10^-6) */
+	freq = val * 1000000 + val2 % 1000000;
+	sample_time = 2222ULL * 1000000ULL;
+	do_div(sample_time, freq);
+
+	sample_time -= 1;
+
+	if (sample_time > 255)
+		return -ERANGE;
+
+	return isl29501_register_write(isl29501, REG_SAMPLE_TIME, sample_time);
+}
+
+static int isl29501_write_raw(struct iio_dev *indio_dev,
+			      struct iio_chan_spec const *chan,
+			      int val, int val2, long mask)
+{
+	struct isl29501_private *isl29501 = iio_priv(indio_dev);
+
+	switch (mask) {
+	case IIO_CHAN_INFO_RAW:
+		return isl29501_set_raw(isl29501, chan, val);
+	case IIO_CHAN_INFO_INT_TIME:
+		return isl29501_set_inttime(isl29501, val, val2);
+	case IIO_CHAN_INFO_SAMP_FREQ:
+		return isl29501_set_freq(isl29501, val, val2);
+	case IIO_CHAN_INFO_SCALE:
+		return isl29501_set_scale(isl29501, chan, val, val2);
+	case IIO_CHAN_INFO_CALIBBIAS:
+		return isl29501_set_calibbias(isl29501, chan, val);
+	default:
+		return -EINVAL;
+	}
+}
+
+static const struct iio_info isl29501_info = {
+	.read_raw = &isl29501_read_raw,
+	.write_raw = &isl29501_write_raw,
+	.attrs = &isl29501_attribute_group,
+};
+
+static int isl29501_init_chip(struct isl29501_private *isl29501)
+{
+	int ret;
+
+	ret = i2c_smbus_read_byte_data(isl29501->client, ISL29501_DEVICE_ID);
+	if (ret < 0) {
+		dev_err(&isl29501->client->dev, "Error reading device id\n");
+		return ret;
+	}
+
+	if (ret != ISL29501_ID) {
+		dev_err(&isl29501->client->dev,
+			"Wrong chip id, got %x expected %x\n",
+			ret, ISL29501_DEVICE_ID);
+		return -ENODEV;
+	}
+
+	ret = isl29501_reset_registers(isl29501);
+	if (ret < 0)
+		return ret;
+
+	return isl29501_begin_acquisition(isl29501);
+}
+
+static irqreturn_t isl29501_trigger_handler(int irq, void *p)
+{
+	struct iio_poll_func *pf = p;
+	struct iio_dev *indio_dev = pf->indio_dev;
+	struct isl29501_private *isl29501 = iio_priv(indio_dev);
+	const unsigned long *active_mask = indio_dev->active_scan_mask;
+	u32 buffer[4] = {}; /* 1x16-bit + ts */
+
+	if (test_bit(ISL29501_DISTANCE_SCAN_INDEX, active_mask))
+		isl29501_register_read(isl29501, REG_DISTANCE, buffer);
+
+	iio_push_to_buffers_with_timestamp(indio_dev, buffer, pf->timestamp);
+	iio_trigger_notify_done(indio_dev->trig);
+
+	return IRQ_HANDLED;
+}
+
+static int isl29501_probe(struct i2c_client *client,
+			  const struct i2c_device_id *id)
+{
+	struct iio_dev *indio_dev;
+	struct isl29501_private *isl29501;
+	int ret;
+
+	indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*isl29501));
+	if (!indio_dev)
+		return -ENOMEM;
+
+	isl29501 = iio_priv(indio_dev);
+
+	i2c_set_clientdata(client, indio_dev);
+	isl29501->client = client;
+
+	mutex_init(&isl29501->lock);
+
+	ret = isl29501_init_chip(isl29501);
+	if (ret < 0)
+		return ret;
+
+	indio_dev->modes = INDIO_DIRECT_MODE;
+	indio_dev->dev.parent = &client->dev;
+	indio_dev->channels = isl29501_channels;
+	indio_dev->num_channels = ARRAY_SIZE(isl29501_channels);
+	indio_dev->name = client->name;
+	indio_dev->info = &isl29501_info;
+
+	ret = devm_iio_triggered_buffer_setup(&client->dev, indio_dev,
+					      iio_pollfunc_store_time,
+					      isl29501_trigger_handler,
+					      NULL);
+	if (ret < 0) {
+		dev_err(&client->dev, "unable to setup iio triggered buffer\n");
+		return ret;
+	}
+
+	return devm_iio_device_register(&client->dev, indio_dev);
+}
+
+static const struct i2c_device_id isl29501_id[] = {
+	{"isl29501", 0},
+	{}
+};
+
+MODULE_DEVICE_TABLE(i2c, isl29501_id);
+
+#if defined(CONFIG_OF)
+static const struct of_device_id isl29501_i2c_matches[] = {
+	{ .compatible = "renesas,isl29501" },
+	{ }
+};
+MODULE_DEVICE_TABLE(of, isl29501_i2c_matches);
+#endif
+
+static struct i2c_driver isl29501_driver = {
+	.driver = {
+		.name	= "isl29501",
+	},
+	.id_table	= isl29501_id,
+	.probe		= isl29501_probe,
+};
+module_i2c_driver(isl29501_driver);
+
+MODULE_AUTHOR("Mathieu Othacehe <m.othacehe@gmail.com>");
+MODULE_DESCRIPTION("ISL29501 Time of Flight sensor driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/iio/proximity/pulsedlight-lidar-lite-v2.c b/drivers/iio/proximity/pulsedlight-lidar-lite-v2.c
new file mode 100644
index 0000000..47af54f
--- /dev/null
+++ b/drivers/iio/proximity/pulsedlight-lidar-lite-v2.c
@@ -0,0 +1,374 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * pulsedlight-lidar-lite-v2.c - Support for PulsedLight LIDAR sensor
+ *
+ * Copyright (C) 2015, 2017-2018
+ * Author: Matt Ranostay <matt.ranostay@konsulko.com>
+ *
+ * TODO: interrupt mode, and signal strength reporting
+ */
+
+#include <linux/err.h>
+#include <linux/init.h>
+#include <linux/i2c.h>
+#include <linux/delay.h>
+#include <linux/module.h>
+#include <linux/pm_runtime.h>
+#include <linux/iio/iio.h>
+#include <linux/iio/sysfs.h>
+#include <linux/iio/buffer.h>
+#include <linux/iio/trigger.h>
+#include <linux/iio/triggered_buffer.h>
+#include <linux/iio/trigger_consumer.h>
+
+#define LIDAR_REG_CONTROL		0x00
+#define LIDAR_REG_CONTROL_ACQUIRE	BIT(2)
+
+#define LIDAR_REG_STATUS		0x01
+#define LIDAR_REG_STATUS_INVALID	BIT(3)
+#define LIDAR_REG_STATUS_READY		BIT(0)
+
+#define LIDAR_REG_DATA_HBYTE		0x0f
+#define LIDAR_REG_DATA_LBYTE		0x10
+#define LIDAR_REG_DATA_WORD_READ	BIT(7)
+
+#define LIDAR_REG_PWR_CONTROL	0x65
+
+#define LIDAR_DRV_NAME "lidar"
+
+struct lidar_data {
+	struct iio_dev *indio_dev;
+	struct i2c_client *client;
+
+	int (*xfer)(struct lidar_data *data, u8 reg, u8 *val, int len);
+	int i2c_enabled;
+
+	u16 buffer[8]; /* 2 byte distance + 8 byte timestamp */
+};
+
+static const struct iio_chan_spec lidar_channels[] = {
+	{
+		.type = IIO_DISTANCE,
+		.info_mask_separate =
+			BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE),
+		.scan_index = 0,
+		.scan_type = {
+			.sign = 'u',
+			.realbits = 16,
+			.storagebits = 16,
+		},
+	},
+	IIO_CHAN_SOFT_TIMESTAMP(1),
+};
+
+static int lidar_i2c_xfer(struct lidar_data *data, u8 reg, u8 *val, int len)
+{
+	struct i2c_client *client = data->client;
+	struct i2c_msg msg[2];
+	int ret;
+
+	msg[0].addr = client->addr;
+	msg[0].flags = client->flags | I2C_M_STOP;
+	msg[0].len = 1;
+	msg[0].buf  = (char *) &reg;
+
+	msg[1].addr = client->addr;
+	msg[1].flags = client->flags | I2C_M_RD;
+	msg[1].len = len;
+	msg[1].buf = (char *) val;
+
+	ret = i2c_transfer(client->adapter, msg, 2);
+
+	return (ret == 2) ? 0 : -EIO;
+}
+
+static int lidar_smbus_xfer(struct lidar_data *data, u8 reg, u8 *val, int len)
+{
+	struct i2c_client *client = data->client;
+	int ret;
+
+	/*
+	 * Device needs a STOP condition between address write, and data read
+	 * so in turn i2c_smbus_read_byte_data cannot be used
+	 */
+
+	while (len--) {
+		ret = i2c_smbus_write_byte(client, reg++);
+		if (ret < 0) {
+			dev_err(&client->dev, "cannot write addr value");
+			return ret;
+		}
+
+		ret = i2c_smbus_read_byte(client);
+		if (ret < 0) {
+			dev_err(&client->dev, "cannot read data value");
+			return ret;
+		}
+
+		*(val++) = ret;
+	}
+
+	return 0;
+}
+
+static int lidar_read_byte(struct lidar_data *data, u8 reg)
+{
+	int ret;
+	u8 val;
+
+	ret = data->xfer(data, reg, &val, 1);
+	if (ret < 0)
+		return ret;
+
+	return val;
+}
+
+static inline int lidar_write_control(struct lidar_data *data, int val)
+{
+	return i2c_smbus_write_byte_data(data->client, LIDAR_REG_CONTROL, val);
+}
+
+static inline int lidar_write_power(struct lidar_data *data, int val)
+{
+	return i2c_smbus_write_byte_data(data->client,
+					 LIDAR_REG_PWR_CONTROL, val);
+}
+
+static int lidar_read_measurement(struct lidar_data *data, u16 *reg)
+{
+	int ret = data->xfer(data, LIDAR_REG_DATA_HBYTE |
+			(data->i2c_enabled ? LIDAR_REG_DATA_WORD_READ : 0),
+			(u8 *) reg, 2);
+
+	if (!ret)
+		*reg = be16_to_cpu(*reg);
+
+	return ret;
+}
+
+static int lidar_get_measurement(struct lidar_data *data, u16 *reg)
+{
+	struct i2c_client *client = data->client;
+	int tries = 10;
+	int ret;
+
+	pm_runtime_get_sync(&client->dev);
+
+	/* start sample */
+	ret = lidar_write_control(data, LIDAR_REG_CONTROL_ACQUIRE);
+	if (ret < 0) {
+		dev_err(&client->dev, "cannot send start measurement command");
+		return ret;
+	}
+
+	while (tries--) {
+		usleep_range(1000, 2000);
+
+		ret = lidar_read_byte(data, LIDAR_REG_STATUS);
+		if (ret < 0)
+			break;
+
+		/* return -EINVAL since laser is likely pointed out of range */
+		if (ret & LIDAR_REG_STATUS_INVALID) {
+			*reg = 0;
+			ret = -EINVAL;
+			break;
+		}
+
+		/* sample ready to read */
+		if (!(ret & LIDAR_REG_STATUS_READY)) {
+			ret = lidar_read_measurement(data, reg);
+			break;
+		}
+		ret = -EIO;
+	}
+	pm_runtime_mark_last_busy(&client->dev);
+	pm_runtime_put_autosuspend(&client->dev);
+
+	return ret;
+}
+
+static int lidar_read_raw(struct iio_dev *indio_dev,
+			  struct iio_chan_spec const *chan,
+			  int *val, int *val2, long mask)
+{
+	struct lidar_data *data = iio_priv(indio_dev);
+	int ret = -EINVAL;
+
+	switch (mask) {
+	case IIO_CHAN_INFO_RAW: {
+		u16 reg;
+
+		if (iio_device_claim_direct_mode(indio_dev))
+			return -EBUSY;
+
+		ret = lidar_get_measurement(data, &reg);
+		if (!ret) {
+			*val = reg;
+			ret = IIO_VAL_INT;
+		}
+		iio_device_release_direct_mode(indio_dev);
+		break;
+	}
+	case IIO_CHAN_INFO_SCALE:
+		*val = 0;
+		*val2 = 10000;
+		ret = IIO_VAL_INT_PLUS_MICRO;
+		break;
+	}
+
+	return ret;
+}
+
+static irqreturn_t lidar_trigger_handler(int irq, void *private)
+{
+	struct iio_poll_func *pf = private;
+	struct iio_dev *indio_dev = pf->indio_dev;
+	struct lidar_data *data = iio_priv(indio_dev);
+	int ret;
+
+	ret = lidar_get_measurement(data, data->buffer);
+	if (!ret) {
+		iio_push_to_buffers_with_timestamp(indio_dev, data->buffer,
+						   iio_get_time_ns(indio_dev));
+	} else if (ret != -EINVAL) {
+		dev_err(&data->client->dev, "cannot read LIDAR measurement");
+	}
+
+	iio_trigger_notify_done(indio_dev->trig);
+
+	return IRQ_HANDLED;
+}
+
+static const struct iio_info lidar_info = {
+	.read_raw = lidar_read_raw,
+};
+
+static int lidar_probe(struct i2c_client *client,
+		       const struct i2c_device_id *id)
+{
+	struct lidar_data *data;
+	struct iio_dev *indio_dev;
+	int ret;
+
+	indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data));
+	if (!indio_dev)
+		return -ENOMEM;
+	data = iio_priv(indio_dev);
+
+	if (i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
+		data->xfer = lidar_i2c_xfer;
+		data->i2c_enabled = 1;
+	} else if (i2c_check_functionality(client->adapter,
+				I2C_FUNC_SMBUS_WORD_DATA | I2C_FUNC_SMBUS_BYTE))
+		data->xfer = lidar_smbus_xfer;
+	else
+		return -EOPNOTSUPP;
+
+	indio_dev->info = &lidar_info;
+	indio_dev->name = LIDAR_DRV_NAME;
+	indio_dev->channels = lidar_channels;
+	indio_dev->num_channels = ARRAY_SIZE(lidar_channels);
+	indio_dev->dev.parent = &client->dev;
+	indio_dev->modes = INDIO_DIRECT_MODE;
+
+	i2c_set_clientdata(client, indio_dev);
+
+	data->client = client;
+	data->indio_dev = indio_dev;
+
+	ret = iio_triggered_buffer_setup(indio_dev, NULL,
+					 lidar_trigger_handler, NULL);
+	if (ret)
+		return ret;
+
+	ret = iio_device_register(indio_dev);
+	if (ret)
+		goto error_unreg_buffer;
+
+	pm_runtime_set_autosuspend_delay(&client->dev, 1000);
+	pm_runtime_use_autosuspend(&client->dev);
+
+	ret = pm_runtime_set_active(&client->dev);
+	if (ret)
+		goto error_unreg_buffer;
+	pm_runtime_enable(&client->dev);
+	pm_runtime_idle(&client->dev);
+
+	return 0;
+
+error_unreg_buffer:
+	iio_triggered_buffer_cleanup(indio_dev);
+
+	return ret;
+}
+
+static int lidar_remove(struct i2c_client *client)
+{
+	struct iio_dev *indio_dev = i2c_get_clientdata(client);
+
+	iio_device_unregister(indio_dev);
+	iio_triggered_buffer_cleanup(indio_dev);
+
+	pm_runtime_disable(&client->dev);
+	pm_runtime_set_suspended(&client->dev);
+
+	return 0;
+}
+
+static const struct i2c_device_id lidar_id[] = {
+	{"lidar-lite-v2", 0},
+	{"lidar-lite-v3", 0},
+	{ },
+};
+MODULE_DEVICE_TABLE(i2c, lidar_id);
+
+static const struct of_device_id lidar_dt_ids[] = {
+	{ .compatible = "pulsedlight,lidar-lite-v2" },
+	{ .compatible = "grmn,lidar-lite-v3" },
+	{ }
+};
+MODULE_DEVICE_TABLE(of, lidar_dt_ids);
+
+#ifdef CONFIG_PM
+static int lidar_pm_runtime_suspend(struct device *dev)
+{
+	struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev));
+	struct lidar_data *data = iio_priv(indio_dev);
+
+	return lidar_write_power(data, 0x0f);
+}
+
+static int lidar_pm_runtime_resume(struct device *dev)
+{
+	struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev));
+	struct lidar_data *data = iio_priv(indio_dev);
+	int ret = lidar_write_power(data, 0);
+
+	/* regulator and FPGA needs settling time */
+	usleep_range(15000, 20000);
+
+	return ret;
+}
+#endif
+
+static const struct dev_pm_ops lidar_pm_ops = {
+	SET_RUNTIME_PM_OPS(lidar_pm_runtime_suspend,
+			   lidar_pm_runtime_resume, NULL)
+};
+
+static struct i2c_driver lidar_driver = {
+	.driver = {
+		.name	= LIDAR_DRV_NAME,
+		.of_match_table	= of_match_ptr(lidar_dt_ids),
+		.pm	= &lidar_pm_ops,
+	},
+	.probe		= lidar_probe,
+	.remove		= lidar_remove,
+	.id_table	= lidar_id,
+};
+module_i2c_driver(lidar_driver);
+
+MODULE_AUTHOR("Matt Ranostay <matt.ranostay@konsulko.com>");
+MODULE_DESCRIPTION("PulsedLight LIDAR sensor");
+MODULE_LICENSE("GPL");
diff --git a/drivers/iio/proximity/rfd77402.c b/drivers/iio/proximity/rfd77402.c
new file mode 100644
index 0000000..fe29fb1
--- /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
new file mode 100644
index 0000000..09c7b9c
--- /dev/null
+++ b/drivers/iio/proximity/srf04.c
@@ -0,0 +1,303 @@
+/*
+ * SRF04: ultrasonic sensor for distance measuring by using GPIOs
+ *
+ * Copyright (c) 2017 Andreas Klinger <ak@it-klinger.de>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * For details about the device see:
+ * http://www.robot-electronics.co.uk/htm/srf04tech.htm
+ *
+ * the measurement cycle as timing diagram looks like:
+ *
+ *          +---+
+ * GPIO     |   |
+ * trig:  --+   +------------------------------------------------------
+ *          ^   ^
+ *          |<->|
+ *         udelay(10)
+ *
+ * ultra           +-+ +-+ +-+
+ * sonic           | | | | | |
+ * burst: ---------+ +-+ +-+ +-----------------------------------------
+ *                           .
+ * ultra                     .              +-+ +-+ +-+
+ * sonic                     .              | | | | | |
+ * echo:  ----------------------------------+ +-+ +-+ +----------------
+ *                           .                        .
+ *                           +------------------------+
+ * GPIO                      |                        |
+ * echo:  -------------------+                        +---------------
+ *                           ^                        ^
+ *                           interrupt                interrupt
+ *                           (ts_rising)              (ts_falling)
+ *                           |<---------------------->|
+ *                              pulse time measured
+ *                              --> one round trip of ultra sonic waves
+ */
+#include <linux/err.h>
+#include <linux/gpio/consumer.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/property.h>
+#include <linux/sched.h>
+#include <linux/interrupt.h>
+#include <linux/delay.h>
+#include <linux/iio/iio.h>
+#include <linux/iio/sysfs.h>
+
+struct srf04_data {
+	struct device		*dev;
+	struct gpio_desc	*gpiod_trig;
+	struct gpio_desc	*gpiod_echo;
+	struct mutex		lock;
+	int			irqnr;
+	ktime_t			ts_rising;
+	ktime_t			ts_falling;
+	struct completion	rising;
+	struct completion	falling;
+};
+
+static irqreturn_t srf04_handle_irq(int irq, void *dev_id)
+{
+	struct iio_dev *indio_dev = dev_id;
+	struct srf04_data *data = iio_priv(indio_dev);
+	ktime_t now = ktime_get();
+
+	if (gpiod_get_value(data->gpiod_echo)) {
+		data->ts_rising = now;
+		complete(&data->rising);
+	} else {
+		data->ts_falling = now;
+		complete(&data->falling);
+	}
+
+	return IRQ_HANDLED;
+}
+
+static int srf04_read(struct srf04_data *data)
+{
+	int ret;
+	ktime_t ktime_dt;
+	u64 dt_ns;
+	u32 time_ns, distance_mm;
+
+	/*
+	 * just one read-echo-cycle can take place at a time
+	 * ==> lock against concurrent reading calls
+	 */
+	mutex_lock(&data->lock);
+
+	reinit_completion(&data->rising);
+	reinit_completion(&data->falling);
+
+	gpiod_set_value(data->gpiod_trig, 1);
+	udelay(10);
+	gpiod_set_value(data->gpiod_trig, 0);
+
+	/* it cannot take more than 20 ms */
+	ret = wait_for_completion_killable_timeout(&data->rising, HZ/50);
+	if (ret < 0) {
+		mutex_unlock(&data->lock);
+		return ret;
+	} else if (ret == 0) {
+		mutex_unlock(&data->lock);
+		return -ETIMEDOUT;
+	}
+
+	ret = wait_for_completion_killable_timeout(&data->falling, HZ/50);
+	if (ret < 0) {
+		mutex_unlock(&data->lock);
+		return ret;
+	} else if (ret == 0) {
+		mutex_unlock(&data->lock);
+		return -ETIMEDOUT;
+	}
+
+	ktime_dt = ktime_sub(data->ts_falling, data->ts_rising);
+
+	mutex_unlock(&data->lock);
+
+	dt_ns = ktime_to_ns(ktime_dt);
+	/*
+	 * measuring more than 3 meters is beyond the capabilities of
+	 * the sensor
+	 * ==> filter out invalid results for not measuring echos of
+	 *     another us sensor
+	 *
+	 * formula:
+	 *         distance       3 m
+	 * time = ---------- = --------- = 9404389 ns
+	 *          speed       319 m/s
+	 *
+	 * using a minimum speed at -20 °C of 319 m/s
+	 */
+	if (dt_ns > 9404389)
+		return -EIO;
+
+	time_ns = dt_ns;
+
+	/*
+	 * the speed as function of the temperature is approximately:
+	 *
+	 * speed = 331,5 + 0,6 * Temp
+	 *   with Temp in °C
+	 *   and speed in m/s
+	 *
+	 * use 343 m/s as ultrasonic speed at 20 °C here in absence of the
+	 * temperature
+	 *
+	 * therefore:
+	 *             time     343
+	 * distance = ------ * -----
+	 *             10^6       2
+	 *   with time in ns
+	 *   and distance in mm (one way)
+	 *
+	 * because we limit to 3 meters the multiplication with 343 just
+	 * fits into 32 bit
+	 */
+	distance_mm = time_ns * 343 / 2000000;
+
+	return distance_mm;
+}
+
+static int srf04_read_raw(struct iio_dev *indio_dev,
+			    struct iio_chan_spec const *channel, int *val,
+			    int *val2, long info)
+{
+	struct srf04_data *data = iio_priv(indio_dev);
+	int ret;
+
+	if (channel->type != IIO_DISTANCE)
+		return -EINVAL;
+
+	switch (info) {
+	case IIO_CHAN_INFO_RAW:
+		ret = srf04_read(data);
+		if (ret < 0)
+			return ret;
+		*val = ret;
+		return IIO_VAL_INT;
+	case IIO_CHAN_INFO_SCALE:
+		/*
+		 * theoretical maximum resolution is 3 mm
+		 * 1 LSB is 1 mm
+		 */
+		*val = 0;
+		*val2 = 1000;
+		return IIO_VAL_INT_PLUS_MICRO;
+	default:
+		return -EINVAL;
+	}
+}
+
+static const struct iio_info srf04_iio_info = {
+	.read_raw		= srf04_read_raw,
+};
+
+static const struct iio_chan_spec srf04_chan_spec[] = {
+	{
+		.type = IIO_DISTANCE,
+		.info_mask_separate =
+				BIT(IIO_CHAN_INFO_RAW) |
+				BIT(IIO_CHAN_INFO_SCALE),
+	},
+};
+
+static int srf04_probe(struct platform_device *pdev)
+{
+	struct device *dev = &pdev->dev;
+	struct srf04_data *data;
+	struct iio_dev *indio_dev;
+	int ret;
+
+	indio_dev = devm_iio_device_alloc(dev, sizeof(struct srf04_data));
+	if (!indio_dev) {
+		dev_err(dev, "failed to allocate IIO device\n");
+		return -ENOMEM;
+	}
+
+	data = iio_priv(indio_dev);
+	data->dev = dev;
+
+	mutex_init(&data->lock);
+	init_completion(&data->rising);
+	init_completion(&data->falling);
+
+	data->gpiod_trig = devm_gpiod_get(dev, "trig", GPIOD_OUT_LOW);
+	if (IS_ERR(data->gpiod_trig)) {
+		dev_err(dev, "failed to get trig-gpios: err=%ld\n",
+					PTR_ERR(data->gpiod_trig));
+		return PTR_ERR(data->gpiod_trig);
+	}
+
+	data->gpiod_echo = devm_gpiod_get(dev, "echo", GPIOD_IN);
+	if (IS_ERR(data->gpiod_echo)) {
+		dev_err(dev, "failed to get echo-gpios: err=%ld\n",
+					PTR_ERR(data->gpiod_echo));
+		return PTR_ERR(data->gpiod_echo);
+	}
+
+	if (gpiod_cansleep(data->gpiod_echo)) {
+		dev_err(data->dev, "cansleep-GPIOs not supported\n");
+		return -ENODEV;
+	}
+
+	data->irqnr = gpiod_to_irq(data->gpiod_echo);
+	if (data->irqnr < 0) {
+		dev_err(data->dev, "gpiod_to_irq: %d\n", data->irqnr);
+		return data->irqnr;
+	}
+
+	ret = devm_request_irq(dev, data->irqnr, srf04_handle_irq,
+			IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,
+			pdev->name, indio_dev);
+	if (ret < 0) {
+		dev_err(data->dev, "request_irq: %d\n", ret);
+		return ret;
+	}
+
+	platform_set_drvdata(pdev, indio_dev);
+
+	indio_dev->name = "srf04";
+	indio_dev->dev.parent = &pdev->dev;
+	indio_dev->info = &srf04_iio_info;
+	indio_dev->modes = INDIO_DIRECT_MODE;
+	indio_dev->channels = srf04_chan_spec;
+	indio_dev->num_channels = ARRAY_SIZE(srf04_chan_spec);
+
+	return devm_iio_device_register(dev, indio_dev);
+}
+
+static const struct of_device_id of_srf04_match[] = {
+	{ .compatible = "devantech,srf04", },
+	{},
+};
+
+MODULE_DEVICE_TABLE(of, of_srf04_match);
+
+static struct platform_driver srf04_driver = {
+	.probe		= srf04_probe,
+	.driver		= {
+		.name		= "srf04-gpio",
+		.of_match_table	= of_srf04_match,
+	},
+};
+
+module_platform_driver(srf04_driver);
+
+MODULE_AUTHOR("Andreas Klinger <ak@it-klinger.de>");
+MODULE_DESCRIPTION("SRF04 ultrasonic sensor for distance measuring using GPIOs");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:srf04");
diff --git a/drivers/iio/proximity/srf08.c b/drivers/iio/proximity/srf08.c
new file mode 100644
index 0000000..f2bf783
--- /dev/null
+++ b/drivers/iio/proximity/srf08.c
@@ -0,0 +1,563 @@
+/*
+ * srf08.c - Support for Devantech SRFxx ultrasonic ranger
+ *           with i2c interface
+ * actually supported are srf02, srf08, srf10
+ *
+ * Copyright (c) 2016, 2017 Andreas Klinger <ak@it-klinger.de>
+ *
+ * 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.
+ *
+ * For details about the device see:
+ * http://www.robot-electronics.co.uk/htm/srf08tech.html
+ * http://www.robot-electronics.co.uk/htm/srf10tech.htm
+ * http://www.robot-electronics.co.uk/htm/srf02tech.htm
+ */
+
+#include <linux/err.h>
+#include <linux/i2c.h>
+#include <linux/delay.h>
+#include <linux/module.h>
+#include <linux/bitops.h>
+#include <linux/iio/iio.h>
+#include <linux/iio/sysfs.h>
+#include <linux/iio/buffer.h>
+#include <linux/iio/trigger_consumer.h>
+#include <linux/iio/triggered_buffer.h>
+
+/* registers of SRF08 device */
+#define SRF08_WRITE_COMMAND	0x00	/* Command Register */
+#define SRF08_WRITE_MAX_GAIN	0x01	/* Max Gain Register: 0 .. 31 */
+#define SRF08_WRITE_RANGE	0x02	/* Range Register: 0 .. 255 */
+#define SRF08_READ_SW_REVISION	0x00	/* Software Revision */
+#define SRF08_READ_LIGHT	0x01	/* Light Sensor during last echo */
+#define SRF08_READ_ECHO_1_HIGH	0x02	/* Range of first echo received */
+#define SRF08_READ_ECHO_1_LOW	0x03	/* Range of first echo received */
+
+#define SRF08_CMD_RANGING_CM	0x51	/* Ranging Mode - Result in cm */
+
+enum srf08_sensor_type {
+	SRF02,
+	SRF08,
+	SRF10,
+	SRF_MAX_TYPE
+};
+
+struct srf08_chip_info {
+	const int		*sensitivity_avail;
+	int			num_sensitivity_avail;
+	int			sensitivity_default;
+
+	/* default value of Range in mm */
+	int			range_default;
+};
+
+struct srf08_data {
+	struct i2c_client	*client;
+
+	/*
+	 * Gain in the datasheet is called sensitivity here to distinct it
+	 * from the gain used with amplifiers of adc's
+	 */
+	int			sensitivity;
+
+	/* max. Range in mm */
+	int			range_mm;
+	struct mutex		lock;
+
+	/*
+	 * triggered buffer
+	 * 1x16-bit channel + 3x16 padding + 4x16 timestamp
+	 */
+	s16			buffer[8];
+
+	/* Sensor-Type */
+	enum srf08_sensor_type	sensor_type;
+
+	/* Chip-specific information */
+	const struct srf08_chip_info	*chip_info;
+};
+
+/*
+ * in the documentation one can read about the "Gain" of the device
+ * which is used here for amplifying the signal and filtering out unwanted
+ * ones.
+ * But with ADC's this term is already used differently and that's why it
+ * is called "Sensitivity" here.
+ */
+static const struct srf08_chip_info srf02_chip_info = {
+	.sensitivity_avail	= NULL,
+	.num_sensitivity_avail	= 0,
+	.sensitivity_default	= 0,
+
+	.range_default		= 0,
+};
+
+static const int srf08_sensitivity_avail[] = {
+	 94,  97, 100, 103, 107, 110, 114, 118,
+	123, 128, 133, 139, 145, 152, 159, 168,
+	177, 187, 199, 212, 227, 245, 265, 288,
+	317, 352, 395, 450, 524, 626, 777, 1025
+	};
+
+static const struct srf08_chip_info srf08_chip_info = {
+	.sensitivity_avail	= srf08_sensitivity_avail,
+	.num_sensitivity_avail	= ARRAY_SIZE(srf08_sensitivity_avail),
+	.sensitivity_default	= 1025,
+
+	.range_default		= 6020,
+};
+
+static const int srf10_sensitivity_avail[] = {
+	 40,  40,  50,  60,  70,  80, 100, 120,
+	140, 200, 250, 300, 350, 400, 500, 600,
+	700,
+	};
+
+static const struct srf08_chip_info srf10_chip_info = {
+	.sensitivity_avail	= srf10_sensitivity_avail,
+	.num_sensitivity_avail	= ARRAY_SIZE(srf10_sensitivity_avail),
+	.sensitivity_default	= 700,
+
+	.range_default		= 6020,
+};
+
+static int srf08_read_ranging(struct srf08_data *data)
+{
+	struct i2c_client *client = data->client;
+	int ret, i;
+	int waittime;
+
+	mutex_lock(&data->lock);
+
+	ret = i2c_smbus_write_byte_data(data->client,
+			SRF08_WRITE_COMMAND, SRF08_CMD_RANGING_CM);
+	if (ret < 0) {
+		dev_err(&client->dev, "write command - err: %d\n", ret);
+		mutex_unlock(&data->lock);
+		return ret;
+	}
+
+	/*
+	 * we read here until a correct version number shows up as
+	 * suggested by the documentation
+	 *
+	 * with an ultrasonic speed of 343 m/s and a roundtrip of it
+	 * sleep the expected duration and try to read from the device
+	 * if nothing useful is read try it in a shorter grid
+	 *
+	 * polling for not more than 20 ms should be enough
+	 */
+	waittime = 1 + data->range_mm / 172;
+	msleep(waittime);
+	for (i = 0; i < 4; i++) {
+		ret = i2c_smbus_read_byte_data(data->client,
+						SRF08_READ_SW_REVISION);
+
+		/* check if a valid version number is read */
+		if (ret < 255 && ret > 0)
+			break;
+		msleep(5);
+	}
+
+	if (ret >= 255 || ret <= 0) {
+		dev_err(&client->dev, "device not ready\n");
+		mutex_unlock(&data->lock);
+		return -EIO;
+	}
+
+	ret = i2c_smbus_read_word_swapped(data->client,
+						SRF08_READ_ECHO_1_HIGH);
+	if (ret < 0) {
+		dev_err(&client->dev, "cannot read distance: ret=%d\n", ret);
+		mutex_unlock(&data->lock);
+		return ret;
+	}
+
+	mutex_unlock(&data->lock);
+
+	return ret;
+}
+
+static irqreturn_t srf08_trigger_handler(int irq, void *p)
+{
+	struct iio_poll_func *pf = p;
+	struct iio_dev *indio_dev = pf->indio_dev;
+	struct srf08_data *data = iio_priv(indio_dev);
+	s16 sensor_data;
+
+	sensor_data = srf08_read_ranging(data);
+	if (sensor_data < 0)
+		goto err;
+
+	mutex_lock(&data->lock);
+
+	data->buffer[0] = sensor_data;
+	iio_push_to_buffers_with_timestamp(indio_dev,
+						data->buffer, pf->timestamp);
+
+	mutex_unlock(&data->lock);
+err:
+	iio_trigger_notify_done(indio_dev->trig);
+	return IRQ_HANDLED;
+}
+
+static int srf08_read_raw(struct iio_dev *indio_dev,
+			    struct iio_chan_spec const *channel, int *val,
+			    int *val2, long mask)
+{
+	struct srf08_data *data = iio_priv(indio_dev);
+	int ret;
+
+	if (channel->type != IIO_DISTANCE)
+		return -EINVAL;
+
+	switch (mask) {
+	case IIO_CHAN_INFO_RAW:
+		ret = srf08_read_ranging(data);
+		if (ret < 0)
+			return ret;
+		*val = ret;
+		return IIO_VAL_INT;
+	case IIO_CHAN_INFO_SCALE:
+		/* 1 LSB is 1 cm */
+		*val = 0;
+		*val2 = 10000;
+		return IIO_VAL_INT_PLUS_MICRO;
+	default:
+		return -EINVAL;
+	}
+}
+
+static ssize_t srf08_show_range_mm_available(struct device *dev,
+				struct device_attribute *attr, char *buf)
+{
+	return sprintf(buf, "[0.043 0.043 11.008]\n");
+}
+
+static IIO_DEVICE_ATTR(sensor_max_range_available, S_IRUGO,
+				srf08_show_range_mm_available, NULL, 0);
+
+static ssize_t srf08_show_range_mm(struct device *dev,
+				struct device_attribute *attr, char *buf)
+{
+	struct iio_dev *indio_dev = dev_to_iio_dev(dev);
+	struct srf08_data *data = iio_priv(indio_dev);
+
+	return sprintf(buf, "%d.%03d\n", data->range_mm / 1000,
+						data->range_mm % 1000);
+}
+
+/*
+ * set the range of the sensor to an even multiple of 43 mm
+ * which corresponds to 1 LSB in the register
+ *
+ * register value    corresponding range
+ *         0x00             43 mm
+ *         0x01             86 mm
+ *         0x02            129 mm
+ *         ...
+ *         0xFF          11008 mm
+ */
+static ssize_t srf08_write_range_mm(struct srf08_data *data, unsigned int val)
+{
+	int ret;
+	struct i2c_client *client = data->client;
+	unsigned int mod;
+	u8 regval;
+
+	ret = val / 43 - 1;
+	mod = val % 43;
+
+	if (mod || (ret < 0) || (ret > 255))
+		return -EINVAL;
+
+	regval = ret;
+
+	mutex_lock(&data->lock);
+
+	ret = i2c_smbus_write_byte_data(client, SRF08_WRITE_RANGE, regval);
+	if (ret < 0) {
+		dev_err(&client->dev, "write_range - err: %d\n", ret);
+		mutex_unlock(&data->lock);
+		return ret;
+	}
+
+	data->range_mm = val;
+
+	mutex_unlock(&data->lock);
+
+	return 0;
+}
+
+static ssize_t srf08_store_range_mm(struct device *dev,
+					struct device_attribute *attr,
+					const char *buf, size_t len)
+{
+	struct iio_dev *indio_dev = dev_to_iio_dev(dev);
+	struct srf08_data *data = iio_priv(indio_dev);
+	int ret;
+	int integer, fract;
+
+	ret = iio_str_to_fixpoint(buf, 100, &integer, &fract);
+	if (ret)
+		return ret;
+
+	ret = srf08_write_range_mm(data, integer * 1000 + fract);
+	if (ret < 0)
+		return ret;
+
+	return len;
+}
+
+static IIO_DEVICE_ATTR(sensor_max_range, S_IRUGO | S_IWUSR,
+			srf08_show_range_mm, srf08_store_range_mm, 0);
+
+static ssize_t srf08_show_sensitivity_available(struct device *dev,
+				struct device_attribute *attr, char *buf)
+{
+	int i, len = 0;
+	struct iio_dev *indio_dev = dev_to_iio_dev(dev);
+	struct srf08_data *data = iio_priv(indio_dev);
+
+	for (i = 0; i < data->chip_info->num_sensitivity_avail; i++)
+		if (data->chip_info->sensitivity_avail[i])
+			len += sprintf(buf + len, "%d ",
+				data->chip_info->sensitivity_avail[i]);
+
+	len += sprintf(buf + len, "\n");
+
+	return len;
+}
+
+static IIO_DEVICE_ATTR(sensor_sensitivity_available, S_IRUGO,
+				srf08_show_sensitivity_available, NULL, 0);
+
+static ssize_t srf08_show_sensitivity(struct device *dev,
+				struct device_attribute *attr, char *buf)
+{
+	struct iio_dev *indio_dev = dev_to_iio_dev(dev);
+	struct srf08_data *data = iio_priv(indio_dev);
+	int len;
+
+	len = sprintf(buf, "%d\n", data->sensitivity);
+
+	return len;
+}
+
+static ssize_t srf08_write_sensitivity(struct srf08_data *data,
+							unsigned int val)
+{
+	struct i2c_client *client = data->client;
+	int ret, i;
+	u8 regval;
+
+	if (!val)
+		return -EINVAL;
+
+	for (i = 0; i < data->chip_info->num_sensitivity_avail; i++)
+		if (val && (val == data->chip_info->sensitivity_avail[i])) {
+			regval = i;
+			break;
+		}
+
+	if (i >= data->chip_info->num_sensitivity_avail)
+		return -EINVAL;
+
+	mutex_lock(&data->lock);
+
+	ret = i2c_smbus_write_byte_data(client, SRF08_WRITE_MAX_GAIN, regval);
+	if (ret < 0) {
+		dev_err(&client->dev, "write_sensitivity - err: %d\n", ret);
+		mutex_unlock(&data->lock);
+		return ret;
+	}
+
+	data->sensitivity = val;
+
+	mutex_unlock(&data->lock);
+
+	return 0;
+}
+
+static ssize_t srf08_store_sensitivity(struct device *dev,
+						struct device_attribute *attr,
+						const char *buf, size_t len)
+{
+	struct iio_dev *indio_dev = dev_to_iio_dev(dev);
+	struct srf08_data *data = iio_priv(indio_dev);
+	int ret;
+	unsigned int val;
+
+	ret = kstrtouint(buf, 10, &val);
+	if (ret)
+		return ret;
+
+	ret = srf08_write_sensitivity(data, val);
+	if (ret < 0)
+		return ret;
+
+	return len;
+}
+
+static IIO_DEVICE_ATTR(sensor_sensitivity, S_IRUGO | S_IWUSR,
+			srf08_show_sensitivity, srf08_store_sensitivity, 0);
+
+static struct attribute *srf08_attributes[] = {
+	&iio_dev_attr_sensor_max_range.dev_attr.attr,
+	&iio_dev_attr_sensor_max_range_available.dev_attr.attr,
+	&iio_dev_attr_sensor_sensitivity.dev_attr.attr,
+	&iio_dev_attr_sensor_sensitivity_available.dev_attr.attr,
+	NULL,
+};
+
+static const struct attribute_group srf08_attribute_group = {
+	.attrs = srf08_attributes,
+};
+
+static const struct iio_chan_spec srf08_channels[] = {
+	{
+		.type = IIO_DISTANCE,
+		.info_mask_separate =
+				BIT(IIO_CHAN_INFO_RAW) |
+				BIT(IIO_CHAN_INFO_SCALE),
+		.scan_index = 0,
+		.scan_type = {
+			.sign = 's',
+			.realbits = 16,
+			.storagebits = 16,
+			.endianness = IIO_CPU,
+		},
+	},
+	IIO_CHAN_SOFT_TIMESTAMP(1),
+};
+
+static const struct iio_info srf08_info = {
+	.read_raw = srf08_read_raw,
+	.attrs = &srf08_attribute_group,
+};
+
+/*
+ * srf02 don't have an adjustable range or sensitivity,
+ * so we don't need attributes at all
+ */
+static const struct iio_info srf02_info = {
+	.read_raw = srf08_read_raw,
+};
+
+static int srf08_probe(struct i2c_client *client,
+					 const struct i2c_device_id *id)
+{
+	struct iio_dev *indio_dev;
+	struct srf08_data *data;
+	int ret;
+
+	if (!i2c_check_functionality(client->adapter,
+					I2C_FUNC_SMBUS_READ_BYTE_DATA |
+					I2C_FUNC_SMBUS_WRITE_BYTE_DATA |
+					I2C_FUNC_SMBUS_READ_WORD_DATA))
+		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;
+	data->sensor_type = (enum srf08_sensor_type)id->driver_data;
+
+	switch (data->sensor_type) {
+	case SRF02:
+		data->chip_info = &srf02_chip_info;
+		indio_dev->info = &srf02_info;
+		break;
+	case SRF08:
+		data->chip_info = &srf08_chip_info;
+		indio_dev->info = &srf08_info;
+		break;
+	case SRF10:
+		data->chip_info = &srf10_chip_info;
+		indio_dev->info = &srf08_info;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	indio_dev->name = id->name;
+	indio_dev->dev.parent = &client->dev;
+	indio_dev->modes = INDIO_DIRECT_MODE;
+	indio_dev->channels = srf08_channels;
+	indio_dev->num_channels = ARRAY_SIZE(srf08_channels);
+
+	mutex_init(&data->lock);
+
+	ret = devm_iio_triggered_buffer_setup(&client->dev, indio_dev,
+			iio_pollfunc_store_time, srf08_trigger_handler, NULL);
+	if (ret < 0) {
+		dev_err(&client->dev, "setup of iio triggered buffer failed\n");
+		return ret;
+	}
+
+	if (data->chip_info->range_default) {
+		/*
+		 * set default range of device in mm here
+		 * these register values cannot be read from the hardware
+		 * therefore set driver specific default values
+		 *
+		 * srf02 don't have a default value so it'll be omitted
+		 */
+		ret = srf08_write_range_mm(data,
+					data->chip_info->range_default);
+		if (ret < 0)
+			return ret;
+	}
+
+	if (data->chip_info->sensitivity_default) {
+		/*
+		 * set default sensitivity of device here
+		 * these register values cannot be read from the hardware
+		 * therefore set driver specific default values
+		 *
+		 * srf02 don't have a default value so it'll be omitted
+		 */
+		ret = srf08_write_sensitivity(data,
+				data->chip_info->sensitivity_default);
+		if (ret < 0)
+			return ret;
+	}
+
+	return devm_iio_device_register(&client->dev, indio_dev);
+}
+
+static const struct of_device_id of_srf08_match[] = {
+	{ .compatible = "devantech,srf02", (void *)SRF02},
+	{ .compatible = "devantech,srf08", (void *)SRF08},
+	{ .compatible = "devantech,srf10", (void *)SRF10},
+	{},
+};
+
+MODULE_DEVICE_TABLE(of, of_srf08_match);
+
+static const struct i2c_device_id srf08_id[] = {
+	{ "srf02", SRF02 },
+	{ "srf08", SRF08 },
+	{ "srf10", SRF10 },
+	{ }
+};
+MODULE_DEVICE_TABLE(i2c, srf08_id);
+
+static struct i2c_driver srf08_driver = {
+	.driver = {
+		.name	= "srf08",
+		.of_match_table	= of_srf08_match,
+	},
+	.probe = srf08_probe,
+	.id_table = srf08_id,
+};
+module_i2c_driver(srf08_driver);
+
+MODULE_AUTHOR("Andreas Klinger <ak@it-klinger.de>");
+MODULE_DESCRIPTION("Devantech SRF02/SRF08/SRF10 i2c ultrasonic ranger driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/iio/proximity/sx9500.c b/drivers/iio/proximity/sx9500.c
new file mode 100644
index 0000000..ff80409
--- /dev/null
+++ b/drivers/iio/proximity/sx9500.c
@@ -0,0 +1,1081 @@
+/*
+ * Copyright (c) 2014 Intel Corporation
+ *
+ * Driver for Semtech's SX9500 capacitive proximity/button solution.
+ * Datasheet available at
+ * <http://www.semtech.com/images/datasheet/sx9500.pdf>.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation.
+ */
+
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/i2c.h>
+#include <linux/irq.h>
+#include <linux/acpi.h>
+#include <linux/gpio/consumer.h>
+#include <linux/regmap.h>
+#include <linux/pm.h>
+#include <linux/delay.h>
+
+#include <linux/iio/iio.h>
+#include <linux/iio/buffer.h>
+#include <linux/iio/sysfs.h>
+#include <linux/iio/events.h>
+#include <linux/iio/trigger.h>
+#include <linux/iio/triggered_buffer.h>
+#include <linux/iio/trigger_consumer.h>
+
+#define SX9500_DRIVER_NAME		"sx9500"
+#define SX9500_IRQ_NAME			"sx9500_event"
+
+/* Register definitions. */
+#define SX9500_REG_IRQ_SRC		0x00
+#define SX9500_REG_STAT			0x01
+#define SX9500_REG_IRQ_MSK		0x03
+
+#define SX9500_REG_PROX_CTRL0		0x06
+#define SX9500_REG_PROX_CTRL1		0x07
+#define SX9500_REG_PROX_CTRL2		0x08
+#define SX9500_REG_PROX_CTRL3		0x09
+#define SX9500_REG_PROX_CTRL4		0x0a
+#define SX9500_REG_PROX_CTRL5		0x0b
+#define SX9500_REG_PROX_CTRL6		0x0c
+#define SX9500_REG_PROX_CTRL7		0x0d
+#define SX9500_REG_PROX_CTRL8		0x0e
+
+#define SX9500_REG_SENSOR_SEL		0x20
+#define SX9500_REG_USE_MSB		0x21
+#define SX9500_REG_USE_LSB		0x22
+#define SX9500_REG_AVG_MSB		0x23
+#define SX9500_REG_AVG_LSB		0x24
+#define SX9500_REG_DIFF_MSB		0x25
+#define SX9500_REG_DIFF_LSB		0x26
+#define SX9500_REG_OFFSET_MSB		0x27
+#define SX9500_REG_OFFSET_LSB		0x28
+
+#define SX9500_REG_RESET		0x7f
+
+/* Write this to REG_RESET to do a soft reset. */
+#define SX9500_SOFT_RESET		0xde
+
+#define SX9500_SCAN_PERIOD_MASK		GENMASK(6, 4)
+#define SX9500_SCAN_PERIOD_SHIFT	4
+
+/*
+ * These serve for identifying IRQ source in the IRQ_SRC register, and
+ * also for masking the IRQs in the IRQ_MSK register.
+ */
+#define SX9500_CLOSE_IRQ		BIT(6)
+#define SX9500_FAR_IRQ			BIT(5)
+#define SX9500_CONVDONE_IRQ		BIT(3)
+
+#define SX9500_PROXSTAT_SHIFT		4
+#define SX9500_COMPSTAT_MASK		GENMASK(3, 0)
+
+#define SX9500_NUM_CHANNELS		4
+#define SX9500_CHAN_MASK		GENMASK(SX9500_NUM_CHANNELS - 1, 0)
+
+struct sx9500_data {
+	struct mutex mutex;
+	struct i2c_client *client;
+	struct iio_trigger *trig;
+	struct regmap *regmap;
+	struct gpio_desc *gpiod_rst;
+	/*
+	 * Last reading of the proximity status for each channel.  We
+	 * only send an event to user space when this changes.
+	 */
+	bool prox_stat[SX9500_NUM_CHANNELS];
+	bool event_enabled[SX9500_NUM_CHANNELS];
+	bool trigger_enabled;
+	u16 *buffer;
+	/* Remember enabled channels and sample rate during suspend. */
+	unsigned int suspend_ctrl0;
+	struct completion completion;
+	int data_rdy_users, close_far_users;
+	int channel_users[SX9500_NUM_CHANNELS];
+};
+
+static const struct iio_event_spec sx9500_events[] = {
+	{
+		.type = IIO_EV_TYPE_THRESH,
+		.dir = IIO_EV_DIR_EITHER,
+		.mask_separate = BIT(IIO_EV_INFO_ENABLE),
+	},
+};
+
+#define SX9500_CHANNEL(idx)					\
+	{							\
+		.type = IIO_PROXIMITY,				\
+		.info_mask_separate = BIT(IIO_CHAN_INFO_RAW),	\
+		.info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SAMP_FREQ), \
+		.indexed = 1,					\
+		.channel = idx,					\
+		.event_spec = sx9500_events,			\
+		.num_event_specs = ARRAY_SIZE(sx9500_events),	\
+		.scan_index = idx,				\
+		.scan_type = {					\
+			.sign = 'u',				\
+			.realbits = 16,				\
+			.storagebits = 16,			\
+			.shift = 0,				\
+		},						\
+	}
+
+static const struct iio_chan_spec sx9500_channels[] = {
+	SX9500_CHANNEL(0),
+	SX9500_CHANNEL(1),
+	SX9500_CHANNEL(2),
+	SX9500_CHANNEL(3),
+	IIO_CHAN_SOFT_TIMESTAMP(4),
+};
+
+static const struct {
+	int val;
+	int val2;
+} sx9500_samp_freq_table[] = {
+	{33, 333333},
+	{16, 666666},
+	{11, 111111},
+	{8, 333333},
+	{6, 666666},
+	{5, 0},
+	{3, 333333},
+	{2, 500000},
+};
+
+static const unsigned int sx9500_scan_period_table[] = {
+	30, 60, 90, 120, 150, 200, 300, 400,
+};
+
+static const struct regmap_range sx9500_writable_reg_ranges[] = {
+	regmap_reg_range(SX9500_REG_IRQ_MSK, SX9500_REG_IRQ_MSK),
+	regmap_reg_range(SX9500_REG_PROX_CTRL0, SX9500_REG_PROX_CTRL8),
+	regmap_reg_range(SX9500_REG_SENSOR_SEL, SX9500_REG_SENSOR_SEL),
+	regmap_reg_range(SX9500_REG_OFFSET_MSB, SX9500_REG_OFFSET_LSB),
+	regmap_reg_range(SX9500_REG_RESET, SX9500_REG_RESET),
+};
+
+static const struct regmap_access_table sx9500_writeable_regs = {
+	.yes_ranges = sx9500_writable_reg_ranges,
+	.n_yes_ranges = ARRAY_SIZE(sx9500_writable_reg_ranges),
+};
+
+/*
+ * All allocated registers are readable, so we just list unallocated
+ * ones.
+ */
+static const struct regmap_range sx9500_non_readable_reg_ranges[] = {
+	regmap_reg_range(SX9500_REG_STAT + 1, SX9500_REG_STAT + 1),
+	regmap_reg_range(SX9500_REG_IRQ_MSK + 1, SX9500_REG_PROX_CTRL0 - 1),
+	regmap_reg_range(SX9500_REG_PROX_CTRL8 + 1, SX9500_REG_SENSOR_SEL - 1),
+	regmap_reg_range(SX9500_REG_OFFSET_LSB + 1, SX9500_REG_RESET - 1),
+};
+
+static const struct regmap_access_table sx9500_readable_regs = {
+	.no_ranges = sx9500_non_readable_reg_ranges,
+	.n_no_ranges = ARRAY_SIZE(sx9500_non_readable_reg_ranges),
+};
+
+static const struct regmap_range sx9500_volatile_reg_ranges[] = {
+	regmap_reg_range(SX9500_REG_IRQ_SRC, SX9500_REG_STAT),
+	regmap_reg_range(SX9500_REG_USE_MSB, SX9500_REG_OFFSET_LSB),
+	regmap_reg_range(SX9500_REG_RESET, SX9500_REG_RESET),
+};
+
+static const struct regmap_access_table sx9500_volatile_regs = {
+	.yes_ranges = sx9500_volatile_reg_ranges,
+	.n_yes_ranges = ARRAY_SIZE(sx9500_volatile_reg_ranges),
+};
+
+static const struct regmap_config sx9500_regmap_config = {
+	.reg_bits = 8,
+	.val_bits = 8,
+
+	.max_register = SX9500_REG_RESET,
+	.cache_type = REGCACHE_RBTREE,
+
+	.wr_table = &sx9500_writeable_regs,
+	.rd_table = &sx9500_readable_regs,
+	.volatile_table = &sx9500_volatile_regs,
+};
+
+static int sx9500_inc_users(struct sx9500_data *data, int *counter,
+			    unsigned int reg, unsigned int bitmask)
+{
+	(*counter)++;
+	if (*counter != 1)
+		/* Bit is already active, nothing to do. */
+		return 0;
+
+	return regmap_update_bits(data->regmap, reg, bitmask, bitmask);
+}
+
+static int sx9500_dec_users(struct sx9500_data *data, int *counter,
+			    unsigned int reg, unsigned int bitmask)
+{
+	(*counter)--;
+	if (*counter != 0)
+		/* There are more users, do not deactivate. */
+		return 0;
+
+	return regmap_update_bits(data->regmap, reg, bitmask, 0);
+}
+
+static int sx9500_inc_chan_users(struct sx9500_data *data, int chan)
+{
+	return sx9500_inc_users(data, &data->channel_users[chan],
+				SX9500_REG_PROX_CTRL0, BIT(chan));
+}
+
+static int sx9500_dec_chan_users(struct sx9500_data *data, int chan)
+{
+	return sx9500_dec_users(data, &data->channel_users[chan],
+				SX9500_REG_PROX_CTRL0, BIT(chan));
+}
+
+static int sx9500_inc_data_rdy_users(struct sx9500_data *data)
+{
+	return sx9500_inc_users(data, &data->data_rdy_users,
+				SX9500_REG_IRQ_MSK, SX9500_CONVDONE_IRQ);
+}
+
+static int sx9500_dec_data_rdy_users(struct sx9500_data *data)
+{
+	return sx9500_dec_users(data, &data->data_rdy_users,
+				SX9500_REG_IRQ_MSK, SX9500_CONVDONE_IRQ);
+}
+
+static int sx9500_inc_close_far_users(struct sx9500_data *data)
+{
+	return sx9500_inc_users(data, &data->close_far_users,
+				SX9500_REG_IRQ_MSK,
+				SX9500_CLOSE_IRQ | SX9500_FAR_IRQ);
+}
+
+static int sx9500_dec_close_far_users(struct sx9500_data *data)
+{
+	return sx9500_dec_users(data, &data->close_far_users,
+				SX9500_REG_IRQ_MSK,
+				SX9500_CLOSE_IRQ | SX9500_FAR_IRQ);
+}
+
+static int sx9500_read_prox_data(struct sx9500_data *data,
+				 const struct iio_chan_spec *chan,
+				 int *val)
+{
+	int ret;
+	__be16 regval;
+
+	ret = regmap_write(data->regmap, SX9500_REG_SENSOR_SEL, chan->channel);
+	if (ret < 0)
+		return ret;
+
+	ret = regmap_bulk_read(data->regmap, SX9500_REG_USE_MSB, &regval, 2);
+	if (ret < 0)
+		return ret;
+
+	*val = be16_to_cpu(regval);
+
+	return IIO_VAL_INT;
+}
+
+/*
+ * If we have no interrupt support, we have to wait for a scan period
+ * after enabling a channel to get a result.
+ */
+static int sx9500_wait_for_sample(struct sx9500_data *data)
+{
+	int ret;
+	unsigned int val;
+
+	ret = regmap_read(data->regmap, SX9500_REG_PROX_CTRL0, &val);
+	if (ret < 0)
+		return ret;
+
+	val = (val & SX9500_SCAN_PERIOD_MASK) >> SX9500_SCAN_PERIOD_SHIFT;
+
+	msleep(sx9500_scan_period_table[val]);
+
+	return 0;
+}
+
+static int sx9500_read_proximity(struct sx9500_data *data,
+				 const struct iio_chan_spec *chan,
+				 int *val)
+{
+	int ret;
+
+	mutex_lock(&data->mutex);
+
+	ret = sx9500_inc_chan_users(data, chan->channel);
+	if (ret < 0)
+		goto out;
+
+	ret = sx9500_inc_data_rdy_users(data);
+	if (ret < 0)
+		goto out_dec_chan;
+
+	mutex_unlock(&data->mutex);
+
+	if (data->client->irq > 0)
+		ret = wait_for_completion_interruptible(&data->completion);
+	else
+		ret = sx9500_wait_for_sample(data);
+
+	mutex_lock(&data->mutex);
+
+	if (ret < 0)
+		goto out_dec_data_rdy;
+
+	ret = sx9500_read_prox_data(data, chan, val);
+	if (ret < 0)
+		goto out_dec_data_rdy;
+
+	ret = sx9500_dec_data_rdy_users(data);
+	if (ret < 0)
+		goto out_dec_chan;
+
+	ret = sx9500_dec_chan_users(data, chan->channel);
+	if (ret < 0)
+		goto out;
+
+	ret = IIO_VAL_INT;
+
+	goto out;
+
+out_dec_data_rdy:
+	sx9500_dec_data_rdy_users(data);
+out_dec_chan:
+	sx9500_dec_chan_users(data, chan->channel);
+out:
+	mutex_unlock(&data->mutex);
+	reinit_completion(&data->completion);
+
+	return ret;
+}
+
+static int sx9500_read_samp_freq(struct sx9500_data *data,
+				 int *val, int *val2)
+{
+	int ret;
+	unsigned int regval;
+
+	mutex_lock(&data->mutex);
+	ret = regmap_read(data->regmap, SX9500_REG_PROX_CTRL0, &regval);
+	mutex_unlock(&data->mutex);
+
+	if (ret < 0)
+		return ret;
+
+	regval = (regval & SX9500_SCAN_PERIOD_MASK) >> SX9500_SCAN_PERIOD_SHIFT;
+	*val = sx9500_samp_freq_table[regval].val;
+	*val2 = sx9500_samp_freq_table[regval].val2;
+
+	return IIO_VAL_INT_PLUS_MICRO;
+}
+
+static int sx9500_read_raw(struct iio_dev *indio_dev,
+			   const struct iio_chan_spec *chan,
+			   int *val, int *val2, long mask)
+{
+	struct sx9500_data *data = iio_priv(indio_dev);
+	int ret;
+
+	switch (chan->type) {
+	case IIO_PROXIMITY:
+		switch (mask) {
+		case IIO_CHAN_INFO_RAW:
+			ret = iio_device_claim_direct_mode(indio_dev);
+			if (ret)
+				return ret;
+			ret = sx9500_read_proximity(data, chan, val);
+			iio_device_release_direct_mode(indio_dev);
+			return ret;
+		case IIO_CHAN_INFO_SAMP_FREQ:
+			return sx9500_read_samp_freq(data, val, val2);
+		default:
+			return -EINVAL;
+		}
+	default:
+		return -EINVAL;
+	}
+}
+
+static int sx9500_set_samp_freq(struct sx9500_data *data,
+				int val, int val2)
+{
+	int i, ret;
+
+	for (i = 0; i < ARRAY_SIZE(sx9500_samp_freq_table); i++)
+		if (val == sx9500_samp_freq_table[i].val &&
+		    val2 == sx9500_samp_freq_table[i].val2)
+			break;
+
+	if (i == ARRAY_SIZE(sx9500_samp_freq_table))
+		return -EINVAL;
+
+	mutex_lock(&data->mutex);
+
+	ret = regmap_update_bits(data->regmap, SX9500_REG_PROX_CTRL0,
+				 SX9500_SCAN_PERIOD_MASK,
+				 i << SX9500_SCAN_PERIOD_SHIFT);
+
+	mutex_unlock(&data->mutex);
+
+	return ret;
+}
+
+static int sx9500_write_raw(struct iio_dev *indio_dev,
+			    const struct iio_chan_spec *chan,
+			    int val, int val2, long mask)
+{
+	struct sx9500_data *data = iio_priv(indio_dev);
+
+	switch (chan->type) {
+	case IIO_PROXIMITY:
+		switch (mask) {
+		case IIO_CHAN_INFO_SAMP_FREQ:
+			return sx9500_set_samp_freq(data, val, val2);
+		default:
+			return -EINVAL;
+		}
+	default:
+		return -EINVAL;
+	}
+}
+
+static irqreturn_t sx9500_irq_handler(int irq, void *private)
+{
+	struct iio_dev *indio_dev = private;
+	struct sx9500_data *data = iio_priv(indio_dev);
+
+	if (data->trigger_enabled)
+		iio_trigger_poll(data->trig);
+
+	/*
+	 * Even if no event is enabled, we need to wake the thread to
+	 * clear the interrupt state by reading SX9500_REG_IRQ_SRC.  It
+	 * is not possible to do that here because regmap_read takes a
+	 * mutex.
+	 */
+	return IRQ_WAKE_THREAD;
+}
+
+static void sx9500_push_events(struct iio_dev *indio_dev)
+{
+	int ret;
+	unsigned int val, chan;
+	struct sx9500_data *data = iio_priv(indio_dev);
+
+	ret = regmap_read(data->regmap, SX9500_REG_STAT, &val);
+	if (ret < 0) {
+		dev_err(&data->client->dev, "i2c transfer error in irq\n");
+		return;
+	}
+
+	val >>= SX9500_PROXSTAT_SHIFT;
+	for (chan = 0; chan < SX9500_NUM_CHANNELS; chan++) {
+		int dir;
+		u64 ev;
+		bool new_prox = val & BIT(chan);
+
+		if (!data->event_enabled[chan])
+			continue;
+		if (new_prox == data->prox_stat[chan])
+			/* No change on this channel. */
+			continue;
+
+		dir = new_prox ? IIO_EV_DIR_FALLING : IIO_EV_DIR_RISING;
+		ev = IIO_UNMOD_EVENT_CODE(IIO_PROXIMITY, chan,
+					  IIO_EV_TYPE_THRESH, dir);
+		iio_push_event(indio_dev, ev, iio_get_time_ns(indio_dev));
+		data->prox_stat[chan] = new_prox;
+	}
+}
+
+static irqreturn_t sx9500_irq_thread_handler(int irq, void *private)
+{
+	struct iio_dev *indio_dev = private;
+	struct sx9500_data *data = iio_priv(indio_dev);
+	int ret;
+	unsigned int val;
+
+	mutex_lock(&data->mutex);
+
+	ret = regmap_read(data->regmap, SX9500_REG_IRQ_SRC, &val);
+	if (ret < 0) {
+		dev_err(&data->client->dev, "i2c transfer error in irq\n");
+		goto out;
+	}
+
+	if (val & (SX9500_CLOSE_IRQ | SX9500_FAR_IRQ))
+		sx9500_push_events(indio_dev);
+
+	if (val & SX9500_CONVDONE_IRQ)
+		complete(&data->completion);
+
+out:
+	mutex_unlock(&data->mutex);
+
+	return IRQ_HANDLED;
+}
+
+static int sx9500_read_event_config(struct iio_dev *indio_dev,
+				    const struct iio_chan_spec *chan,
+				    enum iio_event_type type,
+				    enum iio_event_direction dir)
+{
+	struct sx9500_data *data = iio_priv(indio_dev);
+
+	if (chan->type != IIO_PROXIMITY || type != IIO_EV_TYPE_THRESH ||
+	    dir != IIO_EV_DIR_EITHER)
+		return -EINVAL;
+
+	return data->event_enabled[chan->channel];
+}
+
+static int sx9500_write_event_config(struct iio_dev *indio_dev,
+				     const struct iio_chan_spec *chan,
+				     enum iio_event_type type,
+				     enum iio_event_direction dir,
+				     int state)
+{
+	struct sx9500_data *data = iio_priv(indio_dev);
+	int ret;
+
+	if (chan->type != IIO_PROXIMITY || type != IIO_EV_TYPE_THRESH ||
+	    dir != IIO_EV_DIR_EITHER)
+		return -EINVAL;
+
+	mutex_lock(&data->mutex);
+
+	if (state == 1) {
+		ret = sx9500_inc_chan_users(data, chan->channel);
+		if (ret < 0)
+			goto out_unlock;
+		ret = sx9500_inc_close_far_users(data);
+		if (ret < 0)
+			goto out_undo_chan;
+	} else {
+		ret = sx9500_dec_chan_users(data, chan->channel);
+		if (ret < 0)
+			goto out_unlock;
+		ret = sx9500_dec_close_far_users(data);
+		if (ret < 0)
+			goto out_undo_chan;
+	}
+
+	data->event_enabled[chan->channel] = state;
+	goto out_unlock;
+
+out_undo_chan:
+	if (state == 1)
+		sx9500_dec_chan_users(data, chan->channel);
+	else
+		sx9500_inc_chan_users(data, chan->channel);
+out_unlock:
+	mutex_unlock(&data->mutex);
+	return ret;
+}
+
+static int sx9500_update_scan_mode(struct iio_dev *indio_dev,
+				   const unsigned long *scan_mask)
+{
+	struct sx9500_data *data = iio_priv(indio_dev);
+
+	mutex_lock(&data->mutex);
+	kfree(data->buffer);
+	data->buffer = kzalloc(indio_dev->scan_bytes, GFP_KERNEL);
+	mutex_unlock(&data->mutex);
+
+	if (data->buffer == NULL)
+		return -ENOMEM;
+
+	return 0;
+}
+
+static IIO_CONST_ATTR_SAMP_FREQ_AVAIL(
+	"2.500000 3.333333 5 6.666666 8.333333 11.111111 16.666666 33.333333");
+
+static struct attribute *sx9500_attributes[] = {
+	&iio_const_attr_sampling_frequency_available.dev_attr.attr,
+	NULL,
+};
+
+static const struct attribute_group sx9500_attribute_group = {
+	.attrs = sx9500_attributes,
+};
+
+static const struct iio_info sx9500_info = {
+	.attrs = &sx9500_attribute_group,
+	.read_raw = &sx9500_read_raw,
+	.write_raw = &sx9500_write_raw,
+	.read_event_config = &sx9500_read_event_config,
+	.write_event_config = &sx9500_write_event_config,
+	.update_scan_mode = &sx9500_update_scan_mode,
+};
+
+static int sx9500_set_trigger_state(struct iio_trigger *trig,
+				    bool state)
+{
+	struct iio_dev *indio_dev = iio_trigger_get_drvdata(trig);
+	struct sx9500_data *data = iio_priv(indio_dev);
+	int ret;
+
+	mutex_lock(&data->mutex);
+
+	if (state)
+		ret = sx9500_inc_data_rdy_users(data);
+	else
+		ret = sx9500_dec_data_rdy_users(data);
+	if (ret < 0)
+		goto out;
+
+	data->trigger_enabled = state;
+
+out:
+	mutex_unlock(&data->mutex);
+
+	return ret;
+}
+
+static const struct iio_trigger_ops sx9500_trigger_ops = {
+	.set_trigger_state = sx9500_set_trigger_state,
+};
+
+static irqreturn_t sx9500_trigger_handler(int irq, void *private)
+{
+	struct iio_poll_func *pf = private;
+	struct iio_dev *indio_dev = pf->indio_dev;
+	struct sx9500_data *data = iio_priv(indio_dev);
+	int val, bit, ret, i = 0;
+
+	mutex_lock(&data->mutex);
+
+	for_each_set_bit(bit, indio_dev->active_scan_mask,
+			 indio_dev->masklength) {
+		ret = sx9500_read_prox_data(data, &indio_dev->channels[bit],
+					    &val);
+		if (ret < 0)
+			goto out;
+
+		data->buffer[i++] = val;
+	}
+
+	iio_push_to_buffers_with_timestamp(indio_dev, data->buffer,
+					   iio_get_time_ns(indio_dev));
+
+out:
+	mutex_unlock(&data->mutex);
+
+	iio_trigger_notify_done(indio_dev->trig);
+
+	return IRQ_HANDLED;
+}
+
+static int sx9500_buffer_preenable(struct iio_dev *indio_dev)
+{
+	struct sx9500_data *data = iio_priv(indio_dev);
+	int ret = 0, i;
+
+	mutex_lock(&data->mutex);
+
+	for (i = 0; i < SX9500_NUM_CHANNELS; i++)
+		if (test_bit(i, indio_dev->active_scan_mask)) {
+			ret = sx9500_inc_chan_users(data, i);
+			if (ret)
+				break;
+		}
+
+	if (ret)
+		for (i = i - 1; i >= 0; i--)
+			if (test_bit(i, indio_dev->active_scan_mask))
+				sx9500_dec_chan_users(data, i);
+
+	mutex_unlock(&data->mutex);
+
+	return ret;
+}
+
+static int sx9500_buffer_predisable(struct iio_dev *indio_dev)
+{
+	struct sx9500_data *data = iio_priv(indio_dev);
+	int ret = 0, i;
+
+	iio_triggered_buffer_predisable(indio_dev);
+
+	mutex_lock(&data->mutex);
+
+	for (i = 0; i < SX9500_NUM_CHANNELS; i++)
+		if (test_bit(i, indio_dev->active_scan_mask)) {
+			ret = sx9500_dec_chan_users(data, i);
+			if (ret)
+				break;
+		}
+
+	if (ret)
+		for (i = i - 1; i >= 0; i--)
+			if (test_bit(i, indio_dev->active_scan_mask))
+				sx9500_inc_chan_users(data, i);
+
+	mutex_unlock(&data->mutex);
+
+	return ret;
+}
+
+static const struct iio_buffer_setup_ops sx9500_buffer_setup_ops = {
+	.preenable = sx9500_buffer_preenable,
+	.postenable = iio_triggered_buffer_postenable,
+	.predisable = sx9500_buffer_predisable,
+};
+
+struct sx9500_reg_default {
+	u8 reg;
+	u8 def;
+};
+
+static const struct sx9500_reg_default sx9500_default_regs[] = {
+	{
+		.reg = SX9500_REG_PROX_CTRL1,
+		/* Shield enabled, small range. */
+		.def = 0x43,
+	},
+	{
+		.reg = SX9500_REG_PROX_CTRL2,
+		/* x8 gain, 167kHz frequency, finest resolution. */
+		.def = 0x77,
+	},
+	{
+		.reg = SX9500_REG_PROX_CTRL3,
+		/* Doze enabled, 2x scan period doze, no raw filter. */
+		.def = 0x40,
+	},
+	{
+		.reg = SX9500_REG_PROX_CTRL4,
+		/* Average threshold. */
+		.def = 0x30,
+	},
+	{
+		.reg = SX9500_REG_PROX_CTRL5,
+		/*
+		 * Debouncer off, lowest average negative filter,
+		 * highest average postive filter.
+		 */
+		.def = 0x0f,
+	},
+	{
+		.reg = SX9500_REG_PROX_CTRL6,
+		/* Proximity detection threshold: 280 */
+		.def = 0x0e,
+	},
+	{
+		.reg = SX9500_REG_PROX_CTRL7,
+		/*
+		 * No automatic compensation, compensate each pin
+		 * independently, proximity hysteresis: 32, close
+		 * debouncer off, far debouncer off.
+		 */
+		.def = 0x00,
+	},
+	{
+		.reg = SX9500_REG_PROX_CTRL8,
+		/* No stuck timeout, no periodic compensation. */
+		.def = 0x00,
+	},
+	{
+		.reg = SX9500_REG_PROX_CTRL0,
+		/* Scan period: 30ms, all sensors disabled. */
+		.def = 0x00,
+	},
+};
+
+/* Activate all channels and perform an initial compensation. */
+static int sx9500_init_compensation(struct iio_dev *indio_dev)
+{
+	struct sx9500_data *data = iio_priv(indio_dev);
+	int i, ret;
+	unsigned int val;
+
+	ret = regmap_update_bits(data->regmap, SX9500_REG_PROX_CTRL0,
+				 SX9500_CHAN_MASK, SX9500_CHAN_MASK);
+	if (ret < 0)
+		return ret;
+
+	for (i = 10; i >= 0; i--) {
+		usleep_range(10000, 20000);
+		ret = regmap_read(data->regmap, SX9500_REG_STAT, &val);
+		if (ret < 0)
+			goto out;
+		if (!(val & SX9500_COMPSTAT_MASK))
+			break;
+	}
+
+	if (i < 0) {
+		dev_err(&data->client->dev, "initial compensation timed out");
+		ret = -ETIMEDOUT;
+	}
+
+out:
+	regmap_update_bits(data->regmap, SX9500_REG_PROX_CTRL0,
+			   SX9500_CHAN_MASK, 0);
+	return ret;
+}
+
+static int sx9500_init_device(struct iio_dev *indio_dev)
+{
+	struct sx9500_data *data = iio_priv(indio_dev);
+	int ret, i;
+	unsigned int val;
+
+	if (data->gpiod_rst) {
+		gpiod_set_value_cansleep(data->gpiod_rst, 0);
+		usleep_range(1000, 2000);
+		gpiod_set_value_cansleep(data->gpiod_rst, 1);
+		usleep_range(1000, 2000);
+	}
+
+	ret = regmap_write(data->regmap, SX9500_REG_IRQ_MSK, 0);
+	if (ret < 0)
+		return ret;
+
+	ret = regmap_write(data->regmap, SX9500_REG_RESET,
+			   SX9500_SOFT_RESET);
+	if (ret < 0)
+		return ret;
+
+	ret = regmap_read(data->regmap, SX9500_REG_IRQ_SRC, &val);
+	if (ret < 0)
+		return ret;
+
+	for (i = 0; i < ARRAY_SIZE(sx9500_default_regs); i++) {
+		ret = regmap_write(data->regmap,
+				   sx9500_default_regs[i].reg,
+				   sx9500_default_regs[i].def);
+		if (ret < 0)
+			return ret;
+	}
+
+	return sx9500_init_compensation(indio_dev);
+}
+
+static const struct acpi_gpio_params reset_gpios = { 0, 0, false };
+static const struct acpi_gpio_params interrupt_gpios = { 2, 0, false };
+
+static const struct acpi_gpio_mapping acpi_sx9500_gpios[] = {
+	{ "reset-gpios", &reset_gpios, 1 },
+	/*
+	 * Some platforms have a bug in ACPI GPIO description making IRQ
+	 * GPIO to be output only. Ask the GPIO core to ignore this limit.
+	 */
+	{ "interrupt-gpios", &interrupt_gpios, 1, ACPI_GPIO_QUIRK_NO_IO_RESTRICTION },
+	{ },
+};
+
+static void sx9500_gpio_probe(struct i2c_client *client,
+			      struct sx9500_data *data)
+{
+	struct gpio_desc *gpiod_int;
+	struct device *dev;
+	int ret;
+
+	if (!client)
+		return;
+
+	dev = &client->dev;
+
+	ret = devm_acpi_dev_add_driver_gpios(dev, acpi_sx9500_gpios);
+	if (ret)
+		dev_dbg(dev, "Unable to add GPIO mapping table\n");
+
+	if (client->irq <= 0) {
+		gpiod_int = devm_gpiod_get(dev, "interrupt", GPIOD_IN);
+		if (IS_ERR(gpiod_int))
+			dev_err(dev, "gpio get irq failed\n");
+		else
+			client->irq = gpiod_to_irq(gpiod_int);
+	}
+
+	data->gpiod_rst = devm_gpiod_get(dev, "reset", GPIOD_OUT_HIGH);
+	if (IS_ERR(data->gpiod_rst)) {
+		dev_warn(dev, "gpio get reset pin failed\n");
+		data->gpiod_rst = NULL;
+	}
+}
+
+static int sx9500_probe(struct i2c_client *client,
+			const struct i2c_device_id *id)
+{
+	int ret;
+	struct iio_dev *indio_dev;
+	struct sx9500_data *data;
+
+	indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data));
+	if (indio_dev == NULL)
+		return -ENOMEM;
+
+	data = iio_priv(indio_dev);
+	data->client = client;
+	mutex_init(&data->mutex);
+	init_completion(&data->completion);
+	data->trigger_enabled = false;
+
+	data->regmap = devm_regmap_init_i2c(client, &sx9500_regmap_config);
+	if (IS_ERR(data->regmap))
+		return PTR_ERR(data->regmap);
+
+	indio_dev->dev.parent = &client->dev;
+	indio_dev->name = SX9500_DRIVER_NAME;
+	indio_dev->channels = sx9500_channels;
+	indio_dev->num_channels = ARRAY_SIZE(sx9500_channels);
+	indio_dev->info = &sx9500_info;
+	indio_dev->modes = INDIO_DIRECT_MODE;
+	i2c_set_clientdata(client, indio_dev);
+
+	sx9500_gpio_probe(client, data);
+
+	ret = sx9500_init_device(indio_dev);
+	if (ret < 0)
+		return ret;
+
+	if (client->irq <= 0)
+		dev_warn(&client->dev, "no valid irq found\n");
+	else {
+		ret = devm_request_threaded_irq(&client->dev, client->irq,
+				sx9500_irq_handler, sx9500_irq_thread_handler,
+				IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
+				SX9500_IRQ_NAME, indio_dev);
+		if (ret < 0)
+			return ret;
+
+		data->trig = devm_iio_trigger_alloc(&client->dev,
+				"%s-dev%d", indio_dev->name, indio_dev->id);
+		if (!data->trig)
+			return -ENOMEM;
+
+		data->trig->dev.parent = &client->dev;
+		data->trig->ops = &sx9500_trigger_ops;
+		iio_trigger_set_drvdata(data->trig, indio_dev);
+
+		ret = iio_trigger_register(data->trig);
+		if (ret)
+			return ret;
+	}
+
+	ret = iio_triggered_buffer_setup(indio_dev, NULL,
+					 sx9500_trigger_handler,
+					 &sx9500_buffer_setup_ops);
+	if (ret < 0)
+		goto out_trigger_unregister;
+
+	ret = iio_device_register(indio_dev);
+	if (ret < 0)
+		goto out_buffer_cleanup;
+
+	return 0;
+
+out_buffer_cleanup:
+	iio_triggered_buffer_cleanup(indio_dev);
+out_trigger_unregister:
+	if (client->irq > 0)
+		iio_trigger_unregister(data->trig);
+
+	return ret;
+}
+
+static int sx9500_remove(struct i2c_client *client)
+{
+	struct iio_dev *indio_dev = i2c_get_clientdata(client);
+	struct sx9500_data *data = iio_priv(indio_dev);
+
+	iio_device_unregister(indio_dev);
+	iio_triggered_buffer_cleanup(indio_dev);
+	if (client->irq > 0)
+		iio_trigger_unregister(data->trig);
+	kfree(data->buffer);
+
+	return 0;
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int sx9500_suspend(struct device *dev)
+{
+	struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev));
+	struct sx9500_data *data = iio_priv(indio_dev);
+	int ret;
+
+	mutex_lock(&data->mutex);
+	ret = regmap_read(data->regmap, SX9500_REG_PROX_CTRL0,
+			  &data->suspend_ctrl0);
+	if (ret < 0)
+		goto out;
+
+	/*
+	 * Scan period doesn't matter because when all the sensors are
+	 * deactivated the device is in sleep mode.
+	 */
+	ret = regmap_write(data->regmap, SX9500_REG_PROX_CTRL0, 0);
+
+out:
+	mutex_unlock(&data->mutex);
+	return ret;
+}
+
+static int sx9500_resume(struct device *dev)
+{
+	struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev));
+	struct sx9500_data *data = iio_priv(indio_dev);
+	int ret;
+
+	mutex_lock(&data->mutex);
+	ret = regmap_write(data->regmap, SX9500_REG_PROX_CTRL0,
+			   data->suspend_ctrl0);
+	mutex_unlock(&data->mutex);
+
+	return ret;
+}
+#endif /* CONFIG_PM_SLEEP */
+
+static const struct dev_pm_ops sx9500_pm_ops = {
+	SET_SYSTEM_SLEEP_PM_OPS(sx9500_suspend, sx9500_resume)
+};
+
+static const struct acpi_device_id sx9500_acpi_match[] = {
+	{"SSX9500", 0},
+	{"SASX9500", 0},
+	{ },
+};
+MODULE_DEVICE_TABLE(acpi, sx9500_acpi_match);
+
+static const struct of_device_id sx9500_of_match[] = {
+	{ .compatible = "semtech,sx9500", },
+	{ }
+};
+MODULE_DEVICE_TABLE(of, sx9500_of_match);
+
+static const struct i2c_device_id sx9500_id[] = {
+	{"sx9500", 0},
+	{ },
+};
+MODULE_DEVICE_TABLE(i2c, sx9500_id);
+
+static struct i2c_driver sx9500_driver = {
+	.driver = {
+		.name	= SX9500_DRIVER_NAME,
+		.acpi_match_table = ACPI_PTR(sx9500_acpi_match),
+		.of_match_table = of_match_ptr(sx9500_of_match),
+		.pm = &sx9500_pm_ops,
+	},
+	.probe		= sx9500_probe,
+	.remove		= sx9500_remove,
+	.id_table	= sx9500_id,
+};
+module_i2c_driver(sx9500_driver);
+
+MODULE_AUTHOR("Vlad Dogaru <vlad.dogaru@intel.com>");
+MODULE_DESCRIPTION("Driver for Semtech SX9500 proximity sensor");
+MODULE_LICENSE("GPL v2");