Linux IIO development
 help / color / mirror / Atom feed
From: surajsonawane0215@gmail.com
To: jic23@kernel.org
Cc: lars@metafoo.de, surajsonawane0215@gmail.com,
	linux-kernel@vger.kernel.org, linux-iio@vger.kernel.org
Subject: [PATCH 2/2] iio: chemical: Add driver for Sharp GP2Y1010AU0F dust sensor
Date: Sat, 26 Apr 2025 03:42:14 +0530	[thread overview]
Message-ID: <20250425221214.50255-1-surajsonawane0215@gmail.com> (raw)

From: Suraj Sonawane <surajsonawane0215@gmail.com>

Implement support for the Sharp GP2Y1010AU0F optical dust sensor which
measures particulate matter concentration using infrared scattering. The
sensor requires precise 280us LED pulses with ADC sampling at 200us after
LED activation.

The driver provides:
- Raw voltage readings through IIO interface
- Hardware-agnostic operation via GPIO and IIO ADC interfaces
- Power management through regulator framework
- Device Tree binding support

Device operation:
1. Activate IR LED for 280us
2. Wait 40us after LED activation
3. Sample analog output at 200us (peak sensitivity)
4. Convert voltage to dust density via calibration parameters

Tested on BeagleBone Black with:
- P8_12 (GPIO_44) for LED control
- P9_39 (AIN0) for analog output

Datasheet:
https://global.sharp/products/device/lineup/data/pdf/datasheet/gp2y1010au_appl_e.pdf

Signed-off-by: Suraj Sonawane <surajsonawane0215@gmail.com>
---
 MAINTAINERS                     |   7 ++
 drivers/iio/chemical/Kconfig    |  12 +++
 drivers/iio/chemical/Makefile   |   1 +
 drivers/iio/chemical/gp2y1010.c | 183 ++++++++++++++++++++++++++++++++
 4 files changed, 203 insertions(+)
 create mode 100644 drivers/iio/chemical/gp2y1010.c

diff --git a/MAINTAINERS b/MAINTAINERS
index f31aeb6b4..54e0f67e0 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -21974,6 +21974,13 @@ F:	drivers/iio/chemical/sps30.c
 F:	drivers/iio/chemical/sps30_i2c.c
 F:	drivers/iio/chemical/sps30_serial.c
 
+SHARP GP2Y1010AU0F DUST SENSOR DRIVER
+M:	Suraj Sonawane <surajsonawane0215@gmail.com>
+L:	linux-iio@vger.kernel.org
+S:	Maintained
+F:	Documentation/devicetree/bindings/iio/chemical/sharp,gp2y1010au0f.yaml
+F:	drivers/iio/chemical/gp2y1010.c
+
 SERIAL DEVICE BUS
 M:	Rob Herring <robh@kernel.org>
 L:	linux-serial@vger.kernel.org
diff --git a/drivers/iio/chemical/Kconfig b/drivers/iio/chemical/Kconfig
index 330fe0af9..119c6e6d8 100644
--- a/drivers/iio/chemical/Kconfig
+++ b/drivers/iio/chemical/Kconfig
@@ -100,6 +100,18 @@ config ENS160_SPI
 	tristate
 	select REGMAP_SPI
 
+config GP2Y1010AU0F
+	tristate "Sharp GP2Y1010AU0F optical dust sensor"
+	depends on IIO
+	select IIO_BUFFER
+	select IIO_TRIGGERED_BUFFER
+	help
+	  Say Y here to build support for Sharp GP2Y1010AU0F optical dust sensor
+	  that measures particulate matter concentration in air.
+
+	  To compile this driver as a module, choose M here: the module will be
+	  called gp2y1010au0f.
+
 config IAQCORE
 	tristate "AMS iAQ-Core VOC sensors"
 	depends on I2C
diff --git a/drivers/iio/chemical/Makefile b/drivers/iio/chemical/Makefile
index 4866db06b..296bbc0d0 100644
--- a/drivers/iio/chemical/Makefile
+++ b/drivers/iio/chemical/Makefile
@@ -15,6 +15,7 @@ obj-$(CONFIG_ENS160) += ens160_core.o
 obj-$(CONFIG_ENS160_I2C) += ens160_i2c.o
 obj-$(CONFIG_ENS160_SPI) += ens160_spi.o
 obj-$(CONFIG_IAQCORE)		+= ams-iaq-core.o
+obj-$(CONFIG_GP2Y1010AU0F) += gp2y1010.o
 obj-$(CONFIG_PMS7003) += pms7003.o
 obj-$(CONFIG_SCD30_CORE) += scd30_core.o
 obj-$(CONFIG_SCD30_I2C) += scd30_i2c.o
diff --git a/drivers/iio/chemical/gp2y1010.c b/drivers/iio/chemical/gp2y1010.c
new file mode 100644
index 000000000..19c09e0e3
--- /dev/null
+++ b/drivers/iio/chemical/gp2y1010.c
@@ -0,0 +1,183 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2025 Suraj Sonawane <surajsonawane0215@gmail.com>
+ *
+ * Sharp GP2Y1010AU0F Dust Sensor Driver
+ *
+ * Datasheet:
+ * https://global.sharp/products/device/lineup/data/pdf/datasheet/gp2y1010au_appl_e.pdf
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/iio/iio.h>
+#include <linux/iio/sysfs.h>
+#include <linux/iio/consumer.h>
+#include <linux/of_device.h>
+#include <linux/mod_devicetable.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/err.h>
+#include <linux/regulator/consumer.h>
+#include <linux/gpio/consumer.h>
+#include <linux/delay.h>
+
+#define GP2Y1010_LED_PULSE_US    280  /* LED on-time (280us) */
+#define GP2Y1010_SAMPLE_DELAY_US  40  /* Wait 40us after LED on */
+#define GP2Y1010_MEASUREMENT_US  200  /* Measure 200us after LED on */
+
+struct gp2y1010_data {
+	struct gpio_desc *led_gpio;
+	struct iio_dev *indio_dev;
+	struct iio_channel *adc_chan;
+	struct regulator *vdd;
+	int v_clean;  /* Calibration: voltage in clean air (mV) */
+};
+
+static int gp2y1010_power_on(struct gp2y1010_data *data)
+{
+	int ret;
+
+	ret = regulator_enable(data->vdd);
+	if (ret) {
+		dev_err(&data->indio_dev->dev, "Failed to enable vdd regulator\n");
+		return ret;
+	}
+
+	udelay(100); /* Short delay for regulator stability */
+	return 0;
+}
+
+static void gp2y1010_power_off(struct gp2y1010_data *data)
+{
+	regulator_disable(data->vdd);
+}
+
+static int gp2y1010_read_raw(struct iio_dev *indio_dev,
+							 struct iio_chan_spec const *chan,
+							 int *val, int *val2, long mask)
+{
+	struct gp2y1010_data *data = iio_priv(indio_dev);
+	int ret, voltage_mv;
+
+	if (mask != IIO_CHAN_INFO_RAW)
+		return -EINVAL;
+
+    /* Turn on LED */
+	gpiod_set_value(data->led_gpio, 1);
+
+    /* Wait 40us (datasheet: delay after LED on) */
+	udelay(GP2Y1010_SAMPLE_DELAY_US);
+
+    /* Read ADC at 200us (peak sensitivity) */
+	udelay(GP2Y1010_MEASUREMENT_US - GP2Y1010_SAMPLE_DELAY_US);
+	ret = iio_read_channel_processed(data->adc_chan, &voltage_mv);
+	if (ret < 0) {
+		gpiod_set_value(data->led_gpio, 0);
+		return ret;
+	}
+
+    /* Turn off LED (total pulse width = 280us) */
+	gpiod_set_value(data->led_gpio, 0);
+
+    /* Store raw voltage (for debugging) */
+	*val = voltage_mv;
+
+	return IIO_VAL_INT;
+}
+
+static const struct iio_info gp2y1010_info = {
+	.read_raw = gp2y1010_read_raw,
+};
+
+static const struct iio_chan_spec gp2y1010_channels[] = {
+	{
+		.type = IIO_VOLTAGE,
+		.info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
+		.extend_name = "dust",
+	},
+};
+
+static int gp2y1010_probe(struct platform_device *pdev)
+{
+	struct gp2y1010_data *data;
+	struct iio_dev *indio_dev;
+	int ret;
+
+	indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(*data));
+	if (!indio_dev)
+		return -ENOMEM;
+
+	data = iio_priv(indio_dev);
+	data->indio_dev = indio_dev;
+	data->v_clean = 900;  /* Default calibration (adjust per sensor) */
+
+	/* Get LED GPIO */
+	data->led_gpio = devm_gpiod_get(&pdev->dev, "led", GPIOD_OUT_LOW);
+	if (IS_ERR(data->led_gpio))
+		return dev_err_probe(&pdev->dev, PTR_ERR(data->led_gpio),
+							"Failed to get LED GPIO\n");
+
+    /* Get regulator */
+	data->vdd = devm_regulator_get(&pdev->dev, "vdd");
+	if (IS_ERR(data->vdd))
+		return dev_err_probe(&pdev->dev, PTR_ERR(data->vdd),
+							"Failed to get regulator\n");
+
+    /* Power on sensor */
+	ret = gp2y1010_power_on(data);
+	if (ret)
+		return ret;
+
+    /* Get ADC channel */
+	data->adc_chan = devm_iio_channel_get(&pdev->dev, "dust");
+	if (IS_ERR(data->adc_chan)) {
+		gp2y1010_power_off(data);
+		return dev_err_probe(&pdev->dev, PTR_ERR(data->adc_chan),
+							"Failed to get ADC channel\n");
+	}
+
+	/* Setup IIO device */
+	indio_dev->name = "gp2y1010";
+	indio_dev->info = &gp2y1010_info;
+	indio_dev->channels = gp2y1010_channels;
+	indio_dev->num_channels = ARRAY_SIZE(gp2y1010_channels);
+	indio_dev->modes = INDIO_DIRECT_MODE;
+
+	ret = devm_iio_device_register(&pdev->dev, indio_dev);
+	if (ret) {
+		gp2y1010_power_off(data);
+		return ret;
+	}
+
+	return 0;
+}
+
+static void gp2y1010_remove(struct platform_device *pdev)
+{
+	struct gp2y1010_data *data = platform_get_drvdata(pdev);
+
+	gp2y1010_power_off(data);
+}
+
+static const struct of_device_id gp2y1010_of_match[] = {
+	{ .compatible = "sharp,gp2y1010au0f", },
+	{}
+};
+MODULE_DEVICE_TABLE(of, gp2y1010_of_match);
+
+static struct platform_driver gp2y1010_driver = {
+	.driver = {
+		.name = "gp2y1010",
+		.of_match_table = gp2y1010_of_match,
+	},
+	.probe = gp2y1010_probe,
+	.remove = gp2y1010_remove,
+};
+
+module_platform_driver(gp2y1010_driver);
+
+MODULE_AUTHOR("Suraj Sonawane <surajsonawane0215@gmail.com>");
+MODULE_DESCRIPTION("Sharp GP2Y1010AU0F Dust Sensor Driver");
+MODULE_LICENSE("GPL");
-- 
2.34.1


             reply	other threads:[~2025-04-25 22:12 UTC|newest]

Thread overview: 2+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2025-04-25 22:12 surajsonawane0215 [this message]
2025-04-26 11:08 ` [PATCH 2/2] iio: chemical: Add driver for Sharp GP2Y1010AU0F dust sensor Jonathan Cameron

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20250425221214.50255-1-surajsonawane0215@gmail.com \
    --to=surajsonawane0215@gmail.com \
    --cc=jic23@kernel.org \
    --cc=lars@metafoo.de \
    --cc=linux-iio@vger.kernel.org \
    --cc=linux-kernel@vger.kernel.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox