Skip to contentSkip to conversation

iio: chemical: add driver for dsm501/ppd42ns particle sensors

PATCH In review Tomasz Duszynski 4 participants 5 messages Feb 5, 2017 – Feb 6, 2017 IIO

lore
Share & tools
lore
#0 lore raw

PATCH 2/4 iio: chemical: add driver for dsm501/ppd42ns particle sensors

This patch adds support for dsm501 and ppd42ns particle sensors.

Both sensors work on the same principle. Heater (resistor) heats up air in sensor chamber which induces upward flow. Particles convect up through a light beam provided by internal infra-red LED. Light scattered by particles is picked by photodiode and internal electronics outputs PWM signal.

Measuring low time occupancy of the signal during 30s measurement period particles number in cubic meter can be computed.

---
 drivers/iio/chemical/Kconfig  |  10 ++
 drivers/iio/chemical/Makefile |   1 +
 drivers/iio/chemical/dsm501.c | 231 ++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 242 insertions(+)
 create mode 100644 drivers/iio/chemical/dsm501.c
drivers/iio/chemical/Kconfig +10 -0
index cea7f9857a1f..986d612aa77f 100644
@@ -21,6 +21,16 @@ config ATLAS_PH_SENSOR
 	 To compile this driver as module, choose M here: the
 	 module will be called atlas-ph-sensor.
 
+config DSM501
+	tristate "Samyoung DSM501 particle sensor"
+	depends on GPIOLIB
+	help
+	 Say yes here to build support for the Samyoung DSM501
+	 particle sensor.
+
+	 To compile this driver as a module, choose M here: the module
+	 will be called dsm501.
+
 config IAQCORE
 	tristate "AMS iAQ-Core VOC sensors"
 	depends on I2C
drivers/iio/chemical/Makefile +1 -0
index b02202b41289..76f50ff8ba7d 100644
@@ -4,5 +4,6 @@
 
 # When adding new entries keep the list in alphabetical order
 obj-$(CONFIG_ATLAS_PH_SENSOR)	+= atlas-ph-sensor.o
+obj-$(CONFIG_DSM501)		+= dsm501.o
 obj-$(CONFIG_IAQCORE)		+= ams-iaq-core.o
 obj-$(CONFIG_VZ89X)		+= vz89x.o
drivers/iio/chemical/dsm501.c new file +231 -0

231 lines not on this page · raw message

Signature · 1 line
2.11.1
#1 lore raw
Quoted 2 lines

This patch adds support for dsm501 and ppd42ns particle sensors.

quick comments below

Quoted 79 lines

Both sensors work on the same principle. Heater (resistor) heats up air in sensor chamber which induces upward flow. Particles convect up through a light beam provided by internal infra-red LED. Light scattered by particles is picked by photodiode and internal electronics outputs PWM signal.

Measuring low time occupancy of the signal during 30s measurement period particles number in cubic meter can be computed.

---
 drivers/iio/chemical/Kconfig  |  10 ++
 drivers/iio/chemical/Makefile |   1 +
 drivers/iio/chemical/dsm501.c | 231 ++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 242 insertions(+)
 create mode 100644 drivers/iio/chemical/dsm501.c
drivers/iio/chemical/Kconfig +10 -0
index cea7f9857a1f..986d612aa77f 100644
@@ -21,6 +21,16 @@ config ATLAS_PH_SENSOR
 	 To compile this driver as module, choose M here: the
 	 module will be called atlas-ph-sensor.
 
+config DSM501
+	tristate "Samyoung DSM501 particle sensor"
+	depends on GPIOLIB
+	help
+	 Say yes here to build support for the Samyoung DSM501
+	 particle sensor.
+
+	 To compile this driver as a module, choose M here: the module
+	 will be called dsm501.
+
 config IAQCORE
 	tristate "AMS iAQ-Core VOC sensors"
 	depends on I2C
drivers/iio/chemical/Makefile +1 -0
index b02202b41289..76f50ff8ba7d 100644
@@ -4,5 +4,6 @@
 
 # When adding new entries keep the list in alphabetical order
 obj-$(CONFIG_ATLAS_PH_SENSOR)	+= atlas-ph-sensor.o
+obj-$(CONFIG_DSM501)		+= dsm501.o
 obj-$(CONFIG_IAQCORE)		+= ams-iaq-core.o
 obj-$(CONFIG_VZ89X)		+= vz89x.o
drivers/iio/chemical/dsm501.c new file +24 -0
new file mode 100644
index 000000000000..013f6b3bfd48
@@ -0,0 +1,231 @@
+/*
+ * Samyoung DSM501 particle sensor driver
+ *
+ * Copyright (C) 2017 Tomasz Duszynski <[email protected]>
+ *
+ * Datasheets:
+ *  http://www.samyoungsnc.com/products/3-1%20Specification%20DSM501.pdf
+ *  http://wiki.timelab.org/images/f/f9/PPD42NS.pdf
+ *
+ * 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.
+ *
+ */
+
+#include <linux/delay.h>
+#include <linux/gpio/consumer.h>
+#include <linux/iio/iio.h>

drivers/iio/chemical/dsm501.c line 34

Quoted 10 lines
drivers/iio/chemical/dsm501.c new file +10 -0
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/of_device.h>
+#include <linux/platform_device.h>
+#include <linux/timekeeping.h>
+
+#define DSM501_DRV_NAME "dsm501"
+#define	DSM501_IRQ_NAME "dsm501_irq"

replace tab by space

Quoted 16 lines
drivers/iio/chemical/dsm501.c new file +16 -0
+
+#define DSM501_DEFAULT_MEASUREMENT_TIME 30 /* seconds */
+
+struct dsm501_data {
+	ktime_t ts;
+	ktime_t low_time;
+	ktime_t meas_time;
+
+	int irq;
+	struct gpio_desc *gpio;
+
+	struct mutex lock;
+
+	int (*number_concentration)(struct dsm501_data *data);
+};
+

drivers/iio/chemical/dsm501.c line 60

Quoted 10 lines
drivers/iio/chemical/dsm501.c new file +10 -0
+/*
+ * Series of data points in Fig. 8-3 (Low Ratio vs Particle)
+ * can be approximated by following polynomials:
+ *
+ * p(r) = 0 (undefined) for r < 4
+ * p(r) = 2353564.2r - 4373814.7 for 4 <= r < 20
+ * p(r) = 4788112.4r - 53581390 for r >= 20
+ *
+ * Note: Result is in pcs/m3. To convert to pcs/0.01cf multiply
+ *	 by 0.0002831685.

is cf needed?

Quoted 46 lines
drivers/iio/chemical/dsm501.c new file +46 -0
+ */
+static int dsm501_number_concentartion(struct dsm501_data *data)
+{
+	s64 retval = 0, r = div64_s64(ktime_to_ns(data->low_time) * 100,
+				      ktime_to_ns(data->meas_time));
+
+	if (r >= 4 && r < 20)
+		retval = 23535642 * r - 43738147;
+	else if (r >= 20)
+		retval = 47881124 * r - 535813900;
+
+	return div_s64(retval, 10);
+}
+
+/*
+ * Series of data points in Fig. 2 (Lo Pulse Occupancy Time vs Concentration)
+ * can be approximated by following polynomial:
+ *
+ * p(r) = 3844.2r^3 - 16201.3r^2 + 1848746.1r + 52497.2
+ *
+ * Note: Result is in pcs/m3. To convert to pcs/0.01cf multiply
+ *	 by 0.0002831685.
+ */
+static int ppd42ns_number_concentration(struct dsm501_data *data)
+{
+	s64 retval, r3, r2, r = div64_s64(ktime_to_ns(data->low_time) * 100,
+					  ktime_to_ns(data->meas_time));
+
+	r2 = r * r;
+	r3 = r2 * r;
+
+	retval = 38442 * r3;
+	retval -= 162013 * r2;
+	retval += 18487461 * r;
+	retval += 524972;
+
+	return div_s64(retval, 10);
+}
+
+static irqreturn_t dsm501_irq(int irq, void *dev_id)
+{
+	struct dsm501_data *data = iio_priv(dev_id);
+	int val = gpiod_get_value(data->gpio);
+	ktime_t dt, ts = ktime_get();
+
+	if (ktime_to_ns(data->ts) == 0) {

drivers/iio/chemical/dsm501.c line 116

Quoted 10 lines
drivers/iio/chemical/dsm501.c new file +10 -0
+		data->ts = ts;
+		data->low_time = ktime_set(0, 0);
+	}
+
+	if (val) {
+		dt = ktime_sub(ts, data->ts);
+		data->low_time = ktime_add(data->low_time, dt);
+	} else {
+		data->ts = ts;
+	}

{} needed?

Quoted 11 lines
drivers/iio/chemical/dsm501.c new file +11 -0
+
+	return IRQ_HANDLED;
+}
+
+static int dsm501_read_raw(struct iio_dev *indio_dev,
+			   struct iio_chan_spec const *chan, int *val,
+			   int *val2, long mask)
+{
+	struct dsm501_data *data = iio_priv(indio_dev);
+	struct device *dev = indio_dev->dev.parent;
+	unsigned long irqflags = IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING;

drivers/iio/chemical/dsm501.c line 137

Quoted 10 lines
drivers/iio/chemical/dsm501.c new file +10 -0
+	int ret;
+
+
+	switch (mask) {
+	case IIO_CHAN_INFO_PROCESSED:
+		mutex_lock(&data->lock);
+		data->ts = ktime_set(0, 0);
+
+		ret = devm_request_irq(dev, data->irq, dsm501_irq, irqflags,
+				       DSM501_IRQ_NAME, indio_dev);

why devm_()? if the irq is explicitly freed below? requesting the irq for every measurement seems weird even if it only happens every 30s

Quoted 63 lines
drivers/iio/chemical/dsm501.c new file +63 -0
+		if (ret) {
+			dev_err(dev, "Failed to request interrupt %d\n", data->irq);
+			mutex_unlock(&data->lock);
+			return ret;
+		}
+
+		msleep_interruptible(ktime_to_ms(data->meas_time));
+		devm_free_irq(dev, data->irq, indio_dev);
+
+		*val = data->number_concentration(data);
+		mutex_unlock(&data->lock);
+
+		return IIO_VAL_INT;
+	default:
+		return -EINVAL;
+	}
+}
+
+static const struct iio_info dsm501_info = {
+	.driver_module	= THIS_MODULE,
+	.read_raw = dsm501_read_raw,
+};
+
+static const struct iio_chan_spec dsm501_channels[] = {
+	{
+		.type = IIO_NUMBERCONCENTRATION,
+		.info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED),
+	},
+};
+
+static int dsm501_probe(struct platform_device *pdev)
+{
+	struct dsm501_data *data;
+	struct iio_dev *indio_dev;
+	struct device *dev = &pdev->dev;
+
+	indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(*data));
+	if (!indio_dev)
+		return -ENOMEM;
+
+	data = iio_priv(indio_dev);
+	platform_set_drvdata(pdev, indio_dev);
+
+	data->gpio = devm_gpiod_get_index(dev, NULL, 0, GPIOD_IN);
+	if (IS_ERR(data->gpio)) {
+		dev_err(dev, "Failed to get GPIO\n");
+		return PTR_ERR(data->gpio);
+	}
+
+	data->irq = gpiod_to_irq(data->gpio);
+	if (data->irq < 0) {
+		dev_err(dev, "GPIO has no interrupt\n");
+		return data->irq;
+	}
+
+	data->meas_time = ktime_set(DSM501_DEFAULT_MEASUREMENT_TIME, 0);
+	data->number_concentration = of_device_get_match_data(dev);
+	mutex_init(&data->lock);
+
+	indio_dev->name = DSM501_DRV_NAME;
+	indio_dev->dev.parent = dev;
+	indio_dev->info = &dsm501_info;
+	indio_dev->modes = INDIO_DIRECT_MODE;

drivers/iio/chemical/dsm501.c line 210

Quoted 10 lines
drivers/iio/chemical/dsm501.c new file +10 -0
+	indio_dev->channels = dsm501_channels;
+	indio_dev->num_channels = ARRAY_SIZE(dsm501_channels);
+
+	return devm_iio_device_register(&pdev->dev, indio_dev);
+}
+
+static const struct of_device_id dsm501_id[] = {
+	{
+		.compatible = "samyoung,dsm501",
+		.data = dsm501_number_concentartion,

type: concentration

Quoted 22 lines
drivers/iio/chemical/dsm501.c new file +21 -0
+	},
+	{
+		.compatible = "shinyei,ppd42ns",
+		.data = ppd42ns_number_concentration,
+	},
+	{ }
+};
+MODULE_DEVICE_TABLE(of, dsm501_id);
+
+static struct platform_driver dsm501_driver = {
+	.driver	= {
+		.name = DSM501_DRV_NAME,
+		.of_match_table = of_match_ptr(dsm501_id)
+	},
+	.probe = dsm501_probe
+};
+module_platform_driver(dsm501_driver);
+
+MODULE_AUTHOR("Tomasz Duszynski <[email protected]>");
+MODULE_DESCRIPTION("Samyoung DSM501 particle sensor driver");
+MODULE_LICENSE("GPL v2");
Signature · 2 lines
Peter Meerwald-Stadler
+43-664-2444418 (mobile)
#2 lore raw

Thanks for review!

Quoted 88 lines · Peter Meerwald-Stadler
Quoted 2 lines

This patch adds support for dsm501 and ppd42ns particle sensors.

quick comments below
G

Quoted 83 lines

Both sensors work on the same principle. Heater (resistor) heats up air in sensor chamber which induces upward flow. Particles convect up through a light beam provided by internal infra-red LED. Light scattered by particles is picked by photodiode and internal electronics outputs PWM signal.

Measuring low time occupancy of the signal during 30s measurement period particles number in cubic meter can be computed.

---
 drivers/iio/chemical/Kconfig  |  10 ++
 drivers/iio/chemical/Makefile |   1 +
 drivers/iio/chemical/dsm501.c | 231 ++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 242 insertions(+)
 create mode 100644 drivers/iio/chemical/dsm501.c
drivers/iio/chemical/Kconfig +10 -0
index cea7f9857a1f..986d612aa77f 100644
@@ -21,6 +21,16 @@ config ATLAS_PH_SENSOR
 	 To compile this driver as module, choose M here: the
 	 module will be called atlas-ph-sensor.
 
+config DSM501
+	tristate "Samyoung DSM501 particle sensor"
+	depends on GPIOLIB
+	help
+	 Say yes here to build support for the Samyoung DSM501
+	 particle sensor.
+
+	 To compile this driver as a module, choose M here: the module
+	 will be called dsm501.
+
 config IAQCORE
 	tristate "AMS iAQ-Core VOC sensors"
 	depends on I2C
drivers/iio/chemical/Makefile +1 -0
index b02202b41289..76f50ff8ba7d 100644
@@ -4,5 +4,6 @@
 
 # When adding new entries keep the list in alphabetical order
 obj-$(CONFIG_ATLAS_PH_SENSOR)	+= atlas-ph-sensor.o
+obj-$(CONFIG_DSM501)		+= dsm501.o
 obj-$(CONFIG_IAQCORE)		+= ams-iaq-core.o
 obj-$(CONFIG_VZ89X)		+= vz89x.o
drivers/iio/chemical/dsm501.c new file +28 -0
new file mode 100644
index 000000000000..013f6b3bfd48
@@ -0,0 +1,231 @@
+/*
+ * Samyoung DSM501 particle sensor driver
+ *
+ * Copyright (C) 2017 Tomasz Duszynski <[email protected]>
+ *
+ * Datasheets:
+ *  http://www.samyoungsnc.com/products/3-1%20Specification%20DSM501.pdf
+ *  http://wiki.timelab.org/images/f/f9/PPD42NS.pdf
+ *
+ * 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.
+ *
+ */
+
+#include <linux/delay.h>
+#include <linux/gpio/consumer.h>
+#include <linux/iio/iio.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
Quoted 8 lines

drivers/iio/chemical/dsm501.c line 34

Quoted 6 lines
drivers/iio/chemical/dsm501.c new file +6 -0
+#include <linux/of_device.h>
+#include <linux/platform_device.h>
+#include <linux/timekeeping.h>
+
+#define DSM501_DRV_NAME "dsm501"
+#define	DSM501_IRQ_NAME "dsm501_irq"

replace tab by space

Ack.

Quoted 21 lines
Quoted 21 lines
drivers/iio/chemical/dsm501.c new file +20 -0
+
+#define DSM501_DEFAULT_MEASUREMENT_TIME 30 /* seconds */
+
+struct dsm501_data {
+	ktime_t ts;
+	ktime_t low_time;
+	ktime_t meas_time;
+
+	int irq;
+	struct gpio_desc *gpio;
+
+	struct mutex lock;
+
+	int (*number_concentration)(struct dsm501_data *data);
+};
+
+/*
+ * Series of data points in Fig. 8-3 (Low Ratio vs Particle)
+ * can be approximated by following polynomials:
+ *
Quoted 8 lines

drivers/iio/chemical/dsm501.c line 60

Quoted 6 lines
drivers/iio/chemical/dsm501.c new file +6 -0
+ * p(r) = 0 (undefined) for r < 4
+ * p(r) = 2353564.2r - 4373814.7 for 4 <= r < 20
+ * p(r) = 4788112.4r - 53581390 for r >= 20
+ *
+ * Note: Result is in pcs/m3. To convert to pcs/0.01cf multiply
+ *	 by 0.0002831685.

is cf needed?

No. I've put that sidenote so that others looking at figures in datahseets won't scratch their heads why units here are pcs/m3 and pcs/0.01cf there. Anyway I am happy to drop that part.

Quoted 51 lines
Quoted 51 lines
drivers/iio/chemical/dsm501.c new file +50 -0
+ */
+static int dsm501_number_concentartion(struct dsm501_data *data)
+{
+	s64 retval = 0, r = div64_s64(ktime_to_ns(data->low_time) * 100,
+				      ktime_to_ns(data->meas_time));
+
+	if (r >= 4 && r < 20)
+		retval = 23535642 * r - 43738147;
+	else if (r >= 20)
+		retval = 47881124 * r - 535813900;
+
+	return div_s64(retval, 10);
+}
+
+/*
+ * Series of data points in Fig. 2 (Lo Pulse Occupancy Time vs Concentration)
+ * can be approximated by following polynomial:
+ *
+ * p(r) = 3844.2r^3 - 16201.3r^2 + 1848746.1r + 52497.2
+ *
+ * Note: Result is in pcs/m3. To convert to pcs/0.01cf multiply
+ *	 by 0.0002831685.
+ */
+static int ppd42ns_number_concentration(struct dsm501_data *data)
+{
+	s64 retval, r3, r2, r = div64_s64(ktime_to_ns(data->low_time) * 100,
+					  ktime_to_ns(data->meas_time));
+
+	r2 = r * r;
+	r3 = r2 * r;
+
+	retval = 38442 * r3;
+	retval -= 162013 * r2;
+	retval += 18487461 * r;
+	retval += 524972;
+
+	return div_s64(retval, 10);
+}
+
+static irqreturn_t dsm501_irq(int irq, void *dev_id)
+{
+	struct dsm501_data *data = iio_priv(dev_id);
+	int val = gpiod_get_value(data->gpio);
+	ktime_t dt, ts = ktime_get();
+
+	if (ktime_to_ns(data->ts) == 0) {
+		data->ts = ts;
+		data->low_time = ktime_set(0, 0);
+	}
+
Quoted 8 lines

drivers/iio/chemical/dsm501.c line 116

Quoted 6 lines
drivers/iio/chemical/dsm501.c new file +6 -0
+	if (val) {
+		dt = ktime_sub(ts, data->ts);
+		data->low_time = ktime_add(data->low_time, dt);
+	} else {
+		data->ts = ts;
+	}

{} needed?

According to CodingStyle yes. {} could be dropped if there was single statement under if (val) { ...

Quoted 18 lines
Quoted 18 lines
drivers/iio/chemical/dsm501.c new file +17 -0
+
+	return IRQ_HANDLED;
+}
+
+static int dsm501_read_raw(struct iio_dev *indio_dev,
+			   struct iio_chan_spec const *chan, int *val,
+			   int *val2, long mask)
+{
+	struct dsm501_data *data = iio_priv(indio_dev);
+	struct device *dev = indio_dev->dev.parent;
+	unsigned long irqflags = IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING;
+	int ret;
+
+
+	switch (mask) {
+	case IIO_CHAN_INFO_PROCESSED:
+		mutex_lock(&data->lock);
Quoted 8 lines

drivers/iio/chemical/dsm501.c line 137

Quoted 4 lines
drivers/iio/chemical/dsm501.c new file +4 -0
+		data->ts = ktime_set(0, 0);
+
+		ret = devm_request_irq(dev, data->irq, dsm501_irq, irqflags,
+				       DSM501_IRQ_NAME, indio_dev);

why devm_()? if the irq is explicitly freed below? requesting the irq for every measurement seems weird even if it only happens every 30s

Ack.

Quoted 68 lines
Quoted 68 lines
drivers/iio/chemical/dsm501.c new file +67 -0
+		if (ret) {
+			dev_err(dev, "Failed to request interrupt %d\n", data->irq);
+			mutex_unlock(&data->lock);
+			return ret;
+		}
+
+		msleep_interruptible(ktime_to_ms(data->meas_time));
+		devm_free_irq(dev, data->irq, indio_dev);
+
+		*val = data->number_concentration(data);
+		mutex_unlock(&data->lock);
+
+		return IIO_VAL_INT;
+	default:
+		return -EINVAL;
+	}
+}
+
+static const struct iio_info dsm501_info = {
+	.driver_module	= THIS_MODULE,
+	.read_raw = dsm501_read_raw,
+};
+
+static const struct iio_chan_spec dsm501_channels[] = {
+	{
+		.type = IIO_NUMBERCONCENTRATION,
+		.info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED),
+	},
+};
+
+static int dsm501_probe(struct platform_device *pdev)
+{
+	struct dsm501_data *data;
+	struct iio_dev *indio_dev;
+	struct device *dev = &pdev->dev;
+
+	indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(*data));
+	if (!indio_dev)
+		return -ENOMEM;
+
+	data = iio_priv(indio_dev);
+	platform_set_drvdata(pdev, indio_dev);
+
+	data->gpio = devm_gpiod_get_index(dev, NULL, 0, GPIOD_IN);
+	if (IS_ERR(data->gpio)) {
+		dev_err(dev, "Failed to get GPIO\n");
+		return PTR_ERR(data->gpio);
+	}
+
+	data->irq = gpiod_to_irq(data->gpio);
+	if (data->irq < 0) {
+		dev_err(dev, "GPIO has no interrupt\n");
+		return data->irq;
+	}
+
+	data->meas_time = ktime_set(DSM501_DEFAULT_MEASUREMENT_TIME, 0);
+	data->number_concentration = of_device_get_match_data(dev);
+	mutex_init(&data->lock);
+
+	indio_dev->name = DSM501_DRV_NAME;
+	indio_dev->dev.parent = dev;
+	indio_dev->info = &dsm501_info;
+	indio_dev->modes = INDIO_DIRECT_MODE;
+	indio_dev->channels = dsm501_channels;
+	indio_dev->num_channels = ARRAY_SIZE(dsm501_channels);
+
+	return devm_iio_device_register(&pdev->dev, indio_dev);
Quoted 8 lines

drivers/iio/chemical/dsm501.c line 210

Quoted 6 lines
drivers/iio/chemical/dsm501.c new file +6 -0
+}
+
+static const struct of_device_id dsm501_id[] = {
+	{
+		.compatible = "samyoung,dsm501",
+		.data = dsm501_number_concentartion,

type: concentration

Are you refering to function naming here? *_number_concentration was named after returned physical quantity.

Quoted 28 lines
Quoted 13 lines
drivers/iio/chemical/dsm501.c new file +11 -0
+	},
+	{
+		.compatible = "shinyei,ppd42ns",
+		.data = ppd42ns_number_concentration,
+	},
+	{ }
+};
+MODULE_DEVICE_TABLE(of, dsm501_id);
+
+static struct platform_driver dsm501_driver = {
+	.driver	= {

drivers/iio/chemical/dsm501.c line 231

Quoted 10 lines
drivers/iio/chemical/dsm501.c new file +10 -0
+		.name = DSM501_DRV_NAME,
+		.of_match_table = of_match_ptr(dsm501_id)
+	},
+	.probe = dsm501_probe
+};
+module_platform_driver(dsm501_driver);
+
+MODULE_AUTHOR("Tomasz Duszynski <[email protected]>");
+MODULE_DESCRIPTION("Samyoung DSM501 particle sensor driver");
+MODULE_LICENSE("GPL v2");

--

Peter Meerwald-Stadler
+43-664-2444418 (mobile)

#3 lore raw
Quoted 13 lines · Tomasz Duszynski

=20
Thanks for review!
=20

Quoted 8 lines · Peter Meerwald-Stadler

=20

Quoted 1 line

This patch adds support for dsm501 and ppd42ns particle sensors.

=20
quick comments below
G

Quoted 2 lines

Both sensors work on the same principle. Heater (resistor) heats up air in sensor chamber which induces upward flow. Particles convect up throug=

h

Quoted 8 lines
Quoted 8 lines
Quoted 8 lines

a light beam provided by internal infra-red LED. Light scattered by particles is picked by photodiode and internal electronics outputs PWM signal.
=20
Measuring low time occupancy of the signal during 30s measurement period=

particles number in cubic meter can be computed.
=20

This a SI unit?

Also this maybe could be better off in drivers/iio/light... as much I like t=
o have other people in drivers/iio/chemical =F0=9F=98=80

Quoted 5 lines
Quoted 5 lines
Quoted 5 lines

---

drivers/iio/chemical/Kconfig  |  10 ++
drivers/iio/chemical/Makefile |   1 +
drivers/iio/chemical/dsm501.c | 231 ++++++++++++++++++++++++++++++++++++=

++++++

Quoted 18 lines
Quoted 18 lines
Quoted 18 lines
3 files changed, 242 insertions(+)

create mode 100644 drivers/iio/chemical/dsm501.c
=20

drivers/iio/chemical/Kconfig= renamed from drivers/iio/chemical/Kconfig +0 -0

index cea7f9857a1f..986d612aa77f 100644

drivers/iio/chemical/Kconfig +0 -0
@@ -21,6 +21,16 @@ config ATLAS_PH_SENSOR
    To compile this driver as module, choose M here: the
    module will be called atlas-ph-sensor.

=20

drivers/iio/chemical/Kconfig +6 -0
+config DSM501
+    tristate "Samyoung DSM501 particle sensor"
+    depends on GPIOLIB
+    help
+     Say yes here to build support for the Samyoung DSM501
+     particle sensor.
Quoted 8 lines
Quoted 8 lines

drivers/iio/chemical/Makefi=

Quoted 8 lines
drivers/iio/chemical/Kconfig +4 -0
+
+     To compile this driver as a module, choose M here: the module
+     will be called dsm501.
+

config IAQCORE
tristate "AMS iAQ-Core VOC sensors"
depends on I2C

drivers/iio/chemical/Makefi= renamed from drivers/iio/chemical/Makefile +0 -0

le

Quoted 11 lines
Quoted 11 lines

drivers/iio/chemical/dsm501=

Quoted 11 lines

index b02202b41289..76f50ff8ba7d 100644

drivers/iio/chemical/Makefile +0 -0
@@ -4,5 +4,6 @@

=20
# When adding new entries keep the list in alphabetical order
obj-$(CONFIG_ATLAS_PH_SENSOR) +=3D atlas-ph-sensor.o
+obj-$(CONFIG_DSM501) +=3D dsm501.o
obj-$(CONFIG_IAQCORE) +=3D ams-iaq-core.o
obj-$(CONFIG_VZ89X) +=3D vz89x.o

drivers/iio/chemical/dsm501= renamed from drivers/iio/chemical/dsm501.c +0 -0

.c

Quoted 4 lines
Quoted 4 lines
Quoted 4 lines

new file mode 100644
index 000000000000..013f6b3bfd48

drivers/iio/chemical/dsm501.c new file +0 -0
Quoted 8 lines
Quoted 8 lines

drivers/iio/chemical/dsm501.c line 7

Quoted 8 lines
drivers/iio/chemical/dsm501.c new file +7 -0
@@ -0,0 +1,231 @@
+/*
+ * Samyoung DSM501 particle sensor driver
+ *
+ * Copyright (C) 2017 Tomasz Duszynski <[email protected]>
+ *
+ * Datasheets:
+ *  http://www.samyoungsnc.com/products/3-1%20Specification%20DSM501.pd=

f

Quoted 61 lines
Quoted 23 lines
Quoted 23 lines
drivers/iio/chemical/dsm501.c new file +21 -0
+ *  http://wiki.timelab.org/images/f/f9/PPD42NS.pdf
+ *
+ * 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.
+ *
+ */
+
+#include <linux/delay.h>
+#include <linux/gpio/consumer.h>
+#include <linux/iio/iio.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
Quoted 8 lines

drivers/iio/chemical/dsm501.c line 34

Quoted 6 lines
drivers/iio/chemical/dsm501.c new file +6 -0
+#include <linux/of_device.h>
+#include <linux/platform_device.h>
+#include <linux/timekeeping.h>
+
+#define DSM501_DRV_NAME "dsm501"
+#define    DSM501_IRQ_NAME "dsm501_irq"

=20
replace tab by space

Ack.

Quoted 21 lines

=20

Quoted 20 lines
drivers/iio/chemical/dsm501.c new file +20 -0
+
+#define DSM501_DEFAULT_MEASUREMENT_TIME 30 /* seconds */
+
+struct dsm501_data {
+    ktime_t ts;
+    ktime_t low_time;
+    ktime_t meas_time;
+
+    int irq;
+    struct gpio_desc *gpio;
+
+    struct mutex lock;
+
+    int (*number_concentration)(struct dsm501_data *data);
+};
+
+/*
+ * Series of data points in Fig. 8-3 (Low Ratio vs Particle)
+ * can be approximated by following polynomials:
+ *
Quoted 8 lines
Quoted 6 lines

+ * p(r) =3D 0 (undefined) for r < 4
+ * p(r) =3D 2353564.2r - 4373814.7 for 4 <=3D r < 20
+ * p(r) =3D 4788112.4r - 53581390 for r >=3D 20
+ *
+ * Note: Result is in pcs/m3. To convert to pcs/0.01cf multiply
+ * by 0.0002831685.

=20
is cf needed?

Quoted 8 lines

No. I've put that sidenote so that others looking at figures in datahseets won't scratch their heads why units here are pcs/m3 and pcs/0.01cf there. Anyway I am happy to drop that part.

Quoted 5 lines

=20

drivers/iio/chemical/dsm501.c

Quoted 4 lines

+ */

drivers/iio/chemical/dsm501.c new file +3 -0
+static int dsm501_number_concentartion(struct dsm501_data *data)
+{
+    s64 retval =3D 0, r =3D div64_s64(ktime_to_ns(data->low_time) * 100=

,

Quoted 4 lines
Quoted 4 lines
Quoted 4 lines
drivers/iio/chemical/dsm501.c new file +4 -0
+                      ktime_to_ns(data->meas_time));
+
+    if (r >=3D 4 && r < 20)
+        retval =3D 23535642 * r - 43738147;
Quoted 8 lines
Quoted 8 lines

drivers/iio/chemical/dsm501.c

Quoted 8 lines
drivers/iio/chemical/dsm501.c new file +8 -0
+    else if (r >=3D 20)
+        retval =3D 47881124 * r - 535813900;
+
+    return div_s64(retval, 10);
+}
+
+/*
+ * Series of data points in Fig. 2 (Lo Pulse Occupancy Time vs Concentr=

ation)

Quoted 10 lines
Quoted 10 lines

drivers/iio/chemical/dsm501.c

Quoted 10 lines

+ * can be approximated by following polynomial:
+ *
+ * p(r) =3D 3844.2r^3 - 16201.3r^2 + 1848746.1r + 52497.2
+ *
+ * Note: Result is in pcs/m3. To convert to pcs/0.01cf multiply
+ * by 0.0002831685.
+ */

drivers/iio/chemical/dsm501.c new file +3 -0
+static int ppd42ns_number_concentration(struct dsm501_data *data)
+{
+    s64 retval, r3, r2, r =3D div64_s64(ktime_to_ns(data->low_time) * 1=

00,

Quoted 35 lines
Quoted 24 lines
Quoted 24 lines
drivers/iio/chemical/dsm501.c new file +24 -0
+                      ktime_to_ns(data->meas_time));
+
+    r2 =3D r * r;
+    r3 =3D r2 * r;
+
+    retval =3D 38442 * r3;
+    retval -=3D 162013 * r2;
+    retval +=3D 18487461 * r;
+    retval +=3D 524972;
+
+    return div_s64(retval, 10);
+}
+
+static irqreturn_t dsm501_irq(int irq, void *dev_id)
+{
+    struct dsm501_data *data =3D iio_priv(dev_id);
+    int val =3D gpiod_get_value(data->gpio);
+    ktime_t dt, ts =3D ktime_get();
+
+    if (ktime_to_ns(data->ts) =3D=3D 0) {
+        data->ts =3D ts;
+        data->low_time =3D ktime_set(0, 0);
+    }
+
Quoted 8 lines

drivers/iio/chemical/dsm501.c line 116

Quoted 6 lines
drivers/iio/chemical/dsm501.c new file +6 -0
+    if (val) {
+        dt =3D ktime_sub(ts, data->ts);
+        data->low_time =3D ktime_add(data->low_time, dt);
+    } else {
+        data->ts =3D ts;
+    }

=20
{} needed?

According to CodingStyle yes. {} could be dropped if there was single statement under if (val) { ...

Quoted 1 line

=20

Quoted 11 lines
Quoted 11 lines

drivers/iio/chemical/dsm501.c

Quoted 11 lines
drivers/iio/chemical/dsm501.c new file +11 -0
+
+    return IRQ_HANDLED;
+}
+
+static int dsm501_read_raw(struct iio_dev *indio_dev,
+               struct iio_chan_spec const *chan, int *val,
+               int *val2, long mask)
+{
+    struct dsm501_data *data =3D iio_priv(indio_dev);
+    struct device *dev =3D indio_dev->dev.parent;
+    unsigned long irqflags =3D IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLI=

NG;

Quoted 14 lines
Quoted 6 lines
Quoted 6 lines
drivers/iio/chemical/dsm501.c new file +6 -0
+    int ret;
+
+
+    switch (mask) {
+    case IIO_CHAN_INFO_PROCESSED:
+        mutex_lock(&data->lock);
Quoted 8 lines

drivers/iio/chemical/dsm501.c line 137

Quoted 4 lines
drivers/iio/chemical/dsm501.c new file +4 -0
+        data->ts =3D ktime_set(0, 0);
+
+        ret =3D devm_request_irq(dev, data->irq, dsm501_irq, irqflags,
+                       DSM501_IRQ_NAME, indio_dev);

=20
why devm_()? if the irq is explicitly freed below? requesting the irq for every measurement seems weird even if it only happens every 30s

Quoted 4 lines

Ack.

Quoted 3 lines

=20

Quoted 2 lines
drivers/iio/chemical/dsm501.c new file +2 -0
+        if (ret) {
+            dev_err(dev, "Failed to request interrupt %d\n", data->irq)=

;

Quoted 73 lines
Quoted 65 lines
Quoted 65 lines
drivers/iio/chemical/dsm501.c new file +65 -0
+            mutex_unlock(&data->lock);
+            return ret;
+        }
+
+        msleep_interruptible(ktime_to_ms(data->meas_time));
+        devm_free_irq(dev, data->irq, indio_dev);
+
+        *val =3D data->number_concentration(data);
+        mutex_unlock(&data->lock);
+
+        return IIO_VAL_INT;
+    default:
+        return -EINVAL;
+    }
+}
+
+static const struct iio_info dsm501_info =3D {
+    .driver_module    =3D THIS_MODULE,
+    .read_raw =3D dsm501_read_raw,
+};
+
+static const struct iio_chan_spec dsm501_channels[] =3D {
+    {
+        .type =3D IIO_NUMBERCONCENTRATION,
+        .info_mask_separate =3D BIT(IIO_CHAN_INFO_PROCESSED),
+    },
+};
+
+static int dsm501_probe(struct platform_device *pdev)
+{
+    struct dsm501_data *data;
+    struct iio_dev *indio_dev;
+    struct device *dev =3D &pdev->dev;
+
+    indio_dev =3D devm_iio_device_alloc(&pdev->dev, sizeof(*data));
+    if (!indio_dev)
+        return -ENOMEM;
+
+    data =3D iio_priv(indio_dev);
+    platform_set_drvdata(pdev, indio_dev);
+
+    data->gpio =3D devm_gpiod_get_index(dev, NULL, 0, GPIOD_IN);
+    if (IS_ERR(data->gpio)) {
+        dev_err(dev, "Failed to get GPIO\n");
+        return PTR_ERR(data->gpio);
+    }
+
+    data->irq =3D gpiod_to_irq(data->gpio);
+    if (data->irq < 0) {
+        dev_err(dev, "GPIO has no interrupt\n");
+        return data->irq;
+    }
+
+    data->meas_time =3D ktime_set(DSM501_DEFAULT_MEASUREMENT_TIME, 0);
+    data->number_concentration =3D of_device_get_match_data(dev);
+    mutex_init(&data->lock);
+
+    indio_dev->name =3D DSM501_DRV_NAME;
+    indio_dev->dev.parent =3D dev;
+    indio_dev->info =3D &dsm501_info;
+    indio_dev->modes =3D INDIO_DIRECT_MODE;
+    indio_dev->channels =3D dsm501_channels;
+    indio_dev->num_channels =3D ARRAY_SIZE(dsm501_channels);
+
+    return devm_iio_device_register(&pdev->dev, indio_dev);
Quoted 8 lines

drivers/iio/chemical/dsm501.c

Quoted 6 lines
drivers/iio/chemical/dsm501.c new file +6 -0
+}
+
+static const struct of_device_id dsm501_id[] =3D {
+    {
+        .compatible =3D "samyoung,dsm501",
+        .data =3D dsm501_number_concentartion,

=20
type: concentration

Quoted 1 line

Are you refering to function naming here? *_number_concentration was name=

d

Quoted 33 lines

after returned physical quantity.

Quoted 20 lines

=20

Quoted 19 lines
drivers/iio/chemical/dsm501.c new file +19 -0
+    },
+    {
+        .compatible =3D "shinyei,ppd42ns",
+        .data =3D ppd42ns_number_concentration,
+    },
+    { }
+};
+MODULE_DEVICE_TABLE(of, dsm501_id);
+
+static struct platform_driver dsm501_driver =3D {
+    .driver    =3D {
+        .name =3D DSM501_DRV_NAME,
+        .of_match_table =3D of_match_ptr(dsm501_id)
+    },
+    .probe =3D dsm501_probe
+};
+module_platform_driver(dsm501_driver);
+
+MODULE_AUTHOR("Tomasz Duszynski <[email protected]>");
Quoted 8 lines
Quoted 3 lines
drivers/iio/chemical/dsm501.c new file +2 -0
+MODULE_DESCRIPTION("Samyoung DSM501 particle sensor driver");
+MODULE_LICENSE("GPL v2");

=20

=20
--
=20
Peter Meerwald-Stadler
+43-664-2444418 (mobile)

--
To unsubscribe from this list: send the line "unsubscribe linux-iio" in the body of a message to [email protected]
More majordomo info at http://vger.kernel.org/majordomo-info.html

maintainer
#4 lore raw
Quoted 17 lines · Matt Ranostay
Quoted 17 lines · Tomasz Duszynski

Thanks for review!

Quoted 12 lines · Peter Meerwald-Stadler
Quoted 2 lines

This patch adds support for dsm501 and ppd42ns particle sensors.

quick comments below
G

Quoted 6 lines

Both sensors work on the same principle. Heater (resistor) heats up air in sensor chamber which induces upward flow. Particles convect up through a light beam provided by internal infra-red LED. Light scattered by particles is picked by photodiode and internal electronics outputs PWM signal.

Quoted 8 lines
Quoted 3 lines
Quoted 3 lines
Quoted 3 lines

Measuring low time occupancy of the signal during 30s measurement period particles number in cubic meter can be computed.

This a SI unit?

Also this maybe could be better off in drivers/iio/light... as much I like to have other people in drivers/iio/chemical 😀

Hmm. It's an odd one certainly. Bit like the pulse sensors that also use something derived from light in a non trivial way.

I'm not sure, but my gut feeling is to go with use case and short of having a new particle sensing category I think chemical is probably just about the best place for this one

Anyhow, interesting device. Another one where it sort of feels like it ought to be tied to dedicated pulse counter hardware but I guess it's slow enough that this works.

Jonathan

Quoted 318 lines
Quoted 307 lines
Quoted 74 lines
Quoted 74 lines

---

drivers/iio/chemical/Kconfig  |  10 ++
drivers/iio/chemical/Makefile |   1 +
drivers/iio/chemical/dsm501.c | 231 ++++++++++++++++++++++++++++++++++++++++++
3 files changed, 242 insertions(+)

create mode 100644 drivers/iio/chemical/dsm501.c

drivers/iio/chemical/Kconfig +10 -0
index cea7f9857a1f..986d612aa77f 100644
@@ -21,6 +21,16 @@ config ATLAS_PH_SENSOR
    To compile this driver as module, choose M here: the
    module will be called atlas-ph-sensor.
 
+config DSM501
+    tristate "Samyoung DSM501 particle sensor"
+    depends on GPIOLIB
+    help
+     Say yes here to build support for the Samyoung DSM501
+     particle sensor.
+
+     To compile this driver as a module, choose M here: the module
+     will be called dsm501.
+

config IAQCORE
tristate "AMS iAQ-Core VOC sensors"
depends on I2C

drivers/iio/chemical/Makefile +0 -0
index b02202b41289..76f50ff8ba7d 100644
@@ -4,5 +4,6 @@
 
 # When adding new entries keep the list in alphabetical order

obj-$(CONFIG_ATLAS_PH_SENSOR) += atlas-ph-sensor.o
+obj-$(CONFIG_DSM501) += dsm501.o
obj-$(CONFIG_IAQCORE) += ams-iaq-core.o
obj-$(CONFIG_VZ89X) += vz89x.o

drivers/iio/chemical/dsm501.c new file +28 -0
new file mode 100644
index 000000000000..013f6b3bfd48
@@ -0,0 +1,231 @@
+/*
+ * Samyoung DSM501 particle sensor driver
+ *
+ * Copyright (C) 2017 Tomasz Duszynski <[email protected]>
+ *
+ * Datasheets:
+ *  http://www.samyoungsnc.com/products/3-1%20Specification%20DSM501.pdf
+ *  http://wiki.timelab.org/images/f/f9/PPD42NS.pdf
+ *
+ * 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.
+ *
+ */
+
+#include <linux/delay.h>
+#include <linux/gpio/consumer.h>
+#include <linux/iio/iio.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
Quoted 8 lines

drivers/iio/chemical/dsm501.c line 34

Quoted 6 lines
drivers/iio/chemical/dsm501.c new file +6 -0
+#include <linux/of_device.h>
+#include <linux/platform_device.h>
+#include <linux/timekeeping.h>
+
+#define DSM501_DRV_NAME "dsm501"
+#define    DSM501_IRQ_NAME "dsm501_irq"

replace tab by space

Ack.

Quoted 21 lines
Quoted 21 lines
drivers/iio/chemical/dsm501.c new file +20 -0
+
+#define DSM501_DEFAULT_MEASUREMENT_TIME 30 /* seconds */
+
+struct dsm501_data {
+    ktime_t ts;
+    ktime_t low_time;
+    ktime_t meas_time;
+
+    int irq;
+    struct gpio_desc *gpio;
+
+    struct mutex lock;
+
+    int (*number_concentration)(struct dsm501_data *data);
+};
+
+/*
+ * Series of data points in Fig. 8-3 (Low Ratio vs Particle)
+ * can be approximated by following polynomials:
+ *
Quoted 8 lines

drivers/iio/chemical/dsm501.c line 60

Quoted 6 lines
drivers/iio/chemical/dsm501.c new file +6 -0
+ * p(r) = 0 (undefined) for r < 4
+ * p(r) = 2353564.2r - 4373814.7 for 4 <= r < 20
+ * p(r) = 4788112.4r - 53581390 for r >= 20
+ *
+ * Note: Result is in pcs/m3. To convert to pcs/0.01cf multiply
+ *     by 0.0002831685.

is cf needed?

No. I've put that sidenote so that others looking at figures in datahseets won't scratch their heads why units here are pcs/m3 and pcs/0.01cf there. Anyway I am happy to drop that part.

Quoted 51 lines
Quoted 51 lines
drivers/iio/chemical/dsm501.c new file +50 -0
+ */
+static int dsm501_number_concentartion(struct dsm501_data *data)
+{
+    s64 retval = 0, r = div64_s64(ktime_to_ns(data->low_time) * 100,
+                      ktime_to_ns(data->meas_time));
+
+    if (r >= 4 && r < 20)
+        retval = 23535642 * r - 43738147;
+    else if (r >= 20)
+        retval = 47881124 * r - 535813900;
+
+    return div_s64(retval, 10);
+}
+
+/*
+ * Series of data points in Fig. 2 (Lo Pulse Occupancy Time vs Concentration)
+ * can be approximated by following polynomial:
+ *
+ * p(r) = 3844.2r^3 - 16201.3r^2 + 1848746.1r + 52497.2
+ *
+ * Note: Result is in pcs/m3. To convert to pcs/0.01cf multiply
+ *     by 0.0002831685.
+ */
+static int ppd42ns_number_concentration(struct dsm501_data *data)
+{
+    s64 retval, r3, r2, r = div64_s64(ktime_to_ns(data->low_time) * 100,
+                      ktime_to_ns(data->meas_time));
+
+    r2 = r * r;
+    r3 = r2 * r;
+
+    retval = 38442 * r3;
+    retval -= 162013 * r2;
+    retval += 18487461 * r;
+    retval += 524972;
+
+    return div_s64(retval, 10);
+}
+
+static irqreturn_t dsm501_irq(int irq, void *dev_id)
+{
+    struct dsm501_data *data = iio_priv(dev_id);
+    int val = gpiod_get_value(data->gpio);
+    ktime_t dt, ts = ktime_get();
+
+    if (ktime_to_ns(data->ts) == 0) {
+        data->ts = ts;
+        data->low_time = ktime_set(0, 0);
+    }
+
Quoted 8 lines

drivers/iio/chemical/dsm501.c line 116

Quoted 6 lines
drivers/iio/chemical/dsm501.c new file +6 -0
+    if (val) {
+        dt = ktime_sub(ts, data->ts);
+        data->low_time = ktime_add(data->low_time, dt);
+    } else {
+        data->ts = ts;
+    }

{} needed?

According to CodingStyle yes. {} could be dropped if there was single statement under if (val) { ...

Quoted 18 lines
Quoted 18 lines
drivers/iio/chemical/dsm501.c new file +17 -0
+
+    return IRQ_HANDLED;
+}
+
+static int dsm501_read_raw(struct iio_dev *indio_dev,
+               struct iio_chan_spec const *chan, int *val,
+               int *val2, long mask)
+{
+    struct dsm501_data *data = iio_priv(indio_dev);
+    struct device *dev = indio_dev->dev.parent;
+    unsigned long irqflags = IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING;
+    int ret;
+
+
+    switch (mask) {
+    case IIO_CHAN_INFO_PROCESSED:
+        mutex_lock(&data->lock);
Quoted 8 lines

drivers/iio/chemical/dsm501.c line 137

Quoted 4 lines
drivers/iio/chemical/dsm501.c new file +4 -0
+        data->ts = ktime_set(0, 0);
+
+        ret = devm_request_irq(dev, data->irq, dsm501_irq, irqflags,
+                       DSM501_IRQ_NAME, indio_dev);

why devm_()? if the irq is explicitly freed below? requesting the irq for every measurement seems weird even if it only happens every 30s

Ack.

Quoted 68 lines
Quoted 68 lines
drivers/iio/chemical/dsm501.c new file +67 -0
+        if (ret) {
+            dev_err(dev, "Failed to request interrupt %d\n", data->irq);
+            mutex_unlock(&data->lock);
+            return ret;
+        }
+
+        msleep_interruptible(ktime_to_ms(data->meas_time));
+        devm_free_irq(dev, data->irq, indio_dev);
+
+        *val = data->number_concentration(data);
+        mutex_unlock(&data->lock);
+
+        return IIO_VAL_INT;
+    default:
+        return -EINVAL;
+    }
+}
+
+static const struct iio_info dsm501_info = {
+    .driver_module    = THIS_MODULE,
+    .read_raw = dsm501_read_raw,
+};
+
+static const struct iio_chan_spec dsm501_channels[] = {
+    {
+        .type = IIO_NUMBERCONCENTRATION,
+        .info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED),
+    },
+};
+
+static int dsm501_probe(struct platform_device *pdev)
+{
+    struct dsm501_data *data;
+    struct iio_dev *indio_dev;
+    struct device *dev = &pdev->dev;
+
+    indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(*data));
+    if (!indio_dev)
+        return -ENOMEM;
+
+    data = iio_priv(indio_dev);
+    platform_set_drvdata(pdev, indio_dev);
+
+    data->gpio = devm_gpiod_get_index(dev, NULL, 0, GPIOD_IN);
+    if (IS_ERR(data->gpio)) {
+        dev_err(dev, "Failed to get GPIO\n");
+        return PTR_ERR(data->gpio);
+    }
+
+    data->irq = gpiod_to_irq(data->gpio);
+    if (data->irq < 0) {
+        dev_err(dev, "GPIO has no interrupt\n");
+        return data->irq;
+    }
+
+    data->meas_time = ktime_set(DSM501_DEFAULT_MEASUREMENT_TIME, 0);
+    data->number_concentration = of_device_get_match_data(dev);
+    mutex_init(&data->lock);
+
+    indio_dev->name = DSM501_DRV_NAME;
+    indio_dev->dev.parent = dev;
+    indio_dev->info = &dsm501_info;
+    indio_dev->modes = INDIO_DIRECT_MODE;
+    indio_dev->channels = dsm501_channels;
+    indio_dev->num_channels = ARRAY_SIZE(dsm501_channels);
+
+    return devm_iio_device_register(&pdev->dev, indio_dev);
Quoted 8 lines

drivers/iio/chemical/dsm501.c line 210

Quoted 6 lines
drivers/iio/chemical/dsm501.c new file +6 -0
+}
+
+static const struct of_device_id dsm501_id[] = {
+    {
+        .compatible = "samyoung,dsm501",
+        .data = dsm501_number_concentartion,

type: concentration

Are you refering to function naming here? *_number_concentration was named after returned physical quantity.

Quoted 26 lines
Quoted 13 lines
drivers/iio/chemical/dsm501.c new file +11 -0
+    },
+    {
+        .compatible = "shinyei,ppd42ns",
+        .data = ppd42ns_number_concentration,
+    },
+    { }
+};
+MODULE_DEVICE_TABLE(of, dsm501_id);
+
+static struct platform_driver dsm501_driver = {
+    .driver    = {

drivers/iio/chemical/dsm501.c line 231

Quoted 10 lines
drivers/iio/chemical/dsm501.c new file +10 -0
+        .name = DSM501_DRV_NAME,
+        .of_match_table = of_match_ptr(dsm501_id)
+    },
+    .probe = dsm501_probe
+};
+module_platform_driver(dsm501_driver);
+
+MODULE_AUTHOR("Tomasz Duszynski <[email protected]>");
+MODULE_DESCRIPTION("Samyoung DSM501 particle sensor driver");
+MODULE_LICENSE("GPL v2");

--

Quoted 6 lines
Quoted 2 lines

Peter Meerwald-Stadler
+43-664-2444418 (mobile)

--
To unsubscribe from this list: send the line "unsubscribe linux-iio" in the body of a message to [email protected]
More majordomo info at http://vger.kernel.org/majordomo-info.html

--
To unsubscribe from this list: send the line "unsubscribe linux-iio" in the body of a message to [email protected]
More majordomo info at http://vger.kernel.org/majordomo-info.html