Gist · 6 / URL: https://gist.050821.xyz/6
Public Gist
UNTESTED: Support for RCWL96XX ultrasonic distance sensor
Expires: Never
goodspeed - created 5 months and 18 days ago
updated file: rcwl96xx1.diff
rcwl96xx1.diff
diff --git a/drivers/iio/proximity/Kconfig b/drivers/iio/proximity/Kconfig
index 2ca3b0bc5eba..947cf7a36e75 100644
--- a/drivers/iio/proximity/Kconfig
+++ b/drivers/iio/proximity/Kconfig
@@ -96,6 +96,20 @@ config PING
 	  To compile this driver as a module, choose M here: the
 	  module will be called ping.
 
+config RCWL96XX
+       tristate "RCWL96XX ultrasonic distance sensor"
+       depends on I2C
+       help
+	  Say Y yo build a driver for the RCWL96XX distance sensor chip
+	  with I2C module.
+
+	  The driver drives the following modules:
+	  - RCWL-1005
+	  - RCWL-1605
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called rcwl96xx.
+
 config RFD77402
 	tristate "RFD77402 ToF sensor"
 	depends on I2C
diff --git a/drivers/iio/proximity/Makefile b/drivers/iio/proximity/Makefile
index f36598380446..a6abe1b7ccc9 100644
--- a/drivers/iio/proximity/Makefile
+++ b/drivers/iio/proximity/Makefile
@@ -11,6 +11,7 @@ obj-$(CONFIG_ISL29501)		+= isl29501.o
 obj-$(CONFIG_LIDAR_LITE_V2)	+= pulsedlight-lidar-lite-v2.o
 obj-$(CONFIG_MB1232)		+= mb1232.o
 obj-$(CONFIG_PING)		+= ping.o
+obj-$(CONFIG_RCWL96XX)		+= rcwl96xx.o
 obj-$(CONFIG_RFD77402)		+= rfd77402.o
 obj-$(CONFIG_SRF04)		+= srf04.o
 obj-$(CONFIG_SRF08)		+= srf08.o
diff --git a/drivers/iio/proximity/rcwl96xx.c b/drivers/iio/proximity/rcwl96xx.c
new file mode 100644
index 000000000000..1e8b2a60adfe
--- /dev/null
+++ b/drivers/iio/proximity/rcwl96xx.c
@@ -0,0 +1,139 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * RCWL96xx: ultrasonic distance sensor (i2c)
+ * Support for RCWL96XX chips that drive RCWL-1005, RCWL-1605 modules.
+ *
+ * Copyright (C) 2024 William Goodspeed <goodspeed@mailo.cat>
+ */
+
+#include <linux/module.h>
+#include <linux/delay.h>
+#include <linux/i2c.h>
+
+#include <linux/iio/iio.h>
+#include <linux/iio/events.h>
+
+#define RCWL96XX_I2C_TRIG_CMD 0x01
+
+struct rcwl96xx_data {
+	struct i2c_client	*client;
+	struct mutex		 lock;
+};
+
+static int rcwl96xx_read(struct rcwl96xx_data *data)
+{
+	u8 raw[3] = { 0 }, cmd[1] = { RCWL96XX_I2C_TRIG_CMD };
+	int ret, distance_mm;
+
+	/* lock against concurrent reading calls */
+	mutex_lock(&data->lock);
+
+	ret = i2c_master_send(data->client, cmd, 1);
+	if (ret < 0) {
+		mutex_unlock(&data->lock);
+		return ret;
+	}
+
+	/* Maximum delay for measurement: 100mS */
+	msleep(100);
+
+	ret = i2c_master_recv(data->client, raw, 3);
+	if (ret < 0) {
+		mutex_unlock(&data->lock);
+		return ret;
+	} else if (ret < 3) {
+		mutex_unlock(&data->lock);
+		return -ENODATA;
+	}
+
+	mutex_unlock(&data->lock);
+
+	/* Distance (mm) = ((BYTE_H << 16) + (BYTE_M << 8) + BYTE_L)/1000 */
+	distance_mm = ((raw[0] << 16) + (raw[1] << 8) + raw[2])/1000;
+
+	return distance_mm;
+}
+
+static int rcwl96xx_read_raw(struct iio_dev *indio_dev,
+			     struct iio_chan_spec const *channel,
+			     int *val,
+			     int *val2,
+			     long info)
+{
+	struct rcwl96xx_data *data = iio_priv(indio_dev);
+	int ret;
+
+	if (channel->type != IIO_DISTANCE)
+		return -EINVAL;
+
+	switch (info) {
+	case IIO_CHAN_INFO_RAW:
+		ret = rcwl96xx_read(data);
+		*val = ret;
+		return IIO_VAL_INT;
+	case IIO_CHAN_INFO_SCALE:
+		*val = 0;
+		*val2 = 1000;
+		return IIO_VAL_INT_PLUS_MICRO;
+	default:
+		return -EINVAL;
+	}
+}
+
+static const struct iio_info rcwl96xx_info = {
+	.read_raw = rcwl96xx_read_raw,
+};
+
+static const struct iio_chan_spec rcwl96xx_chan_spec[] = {
+	{
+		.type = IIO_DISTANCE,
+		.info_mask_separate =
+			BIT(IIO_CHAN_INFO_RAW) |
+			BIT(IIO_CHAN_INFO_SCALE),
+	},
+};
+
+static int rcwl96xx_probe(struct i2c_client *client)
+{
+	struct rcwl96xx_data *data;
+	struct iio_dev *indio_dev;
+
+	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->info = &rcwl96xx_info;
+	indio_dev->channels = rcwl96xx_chan_spec;
+	indio_dev->num_channels = ARRAY_SIZE(rcwl96xx_chan_spec);
+	indio_dev->name = "rcwl96xx";
+	indio_dev->modes = INDIO_DIRECT_MODE;
+
+	return devm_iio_device_register(&client->dev, indio_dev);
+}
+
+static const struct of_device_id rcwl96xx_of_match[] = {
+	{
+		.compatible = "mangze,rcwl96xx",
+	},
+	{}
+};
+MODULE_DEVICE_TABLE(of, rcwl96xx_of_match);
+
+static struct i2c_driver rcwl96xx_driver = {
+	.driver = {
+		.name = "rcwl96xx",
+		.of_match_table = rcwl96xx_of_match,
+	},
+	.probe = rcwl96xx_probe,
+};
+module_i2c_driver(rcwl96xx_driver);
+
+MODULE_AUTHOR("William Goodspeed <goodspeed@mailo.cat>");
+MODULE_DESCRIPTION("Mangze RCWL96XX ultrasonic distance sensor driver");
+MODULE_LICENSE("GPL");