All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH v3 0/7] iio: adc: hi8435: Add Holt HI-8435 threshold detector
@ 2015-07-29 12:54 ` Vladimir Barinov
  0 siblings, 0 replies; 40+ messages in thread
From: Vladimir Barinov @ 2015-07-29 12:54 UTC (permalink / raw)
  To: Jonathan Cameron
  Cc: Hartmut Knaack, Lars-Peter Clausen, Peter Meerwald, Rob Herring,
	Pawel Moll, Mark Rutland, Ian Campbell, Kumar Gala, linux-kernel,
	linux-iio, devicetree, cory.tusar

Hello,

This adds the folowing:
- Holt threshold detector driver for HI-8435 chip
- Add Holt vendor prefix
- Document HI-8435 DT bindings
- Add periodic polling functionality to SYSFS trigger
- Support triggered events
- Add ABI documentation for debounce_time
- Fix typos in sysfs-bus-iio file

PDF file can be found here:
http://www.holtic.com/products/3081-hi-8435.aspx

Vladimir Barinov (7):
[1/7] iio: adc: hi8435: Holt HI-8435 threshold detector
[2/7] dt: Add vendor prefix 'holt'
[3/7] dt: Document Holt HI-8435 bindings
[4/7] iio: trigger: Add periodic polling to SYSFS trigger
[5/7] iio: Support triggered events
[6/7] iio: Add ABI documentation for debounce_time
[7/7] iio: Fix typos in ABI documentation

---
This patchset is against the 'kernel/git/torvalds/linux.git' repo.

 Documentation/ABI/testing/sysfs-bus-iio               |   16 
 Documentation/ABI/testing/sysfs-bus-iio-adc-hi8435    |   61 +
 Documentation/ABI/testing/sysfs-bus-iio-trigger-sysfs |   11 
 Documentation/devicetree/bindings/iio/adc/hi8435.txt  |   25 
 Documentation/devicetree/bindings/vendor-prefixes.txt |    1 
 drivers/iio/Kconfig                                   |    6 
 drivers/iio/Makefile                                  |    1 
 drivers/iio/adc/Kconfig                               |   11 
 drivers/iio/adc/Makefile                              |    1 
 drivers/iio/adc/hi8435.c                              |  659 ++++++++++++++++++
 drivers/iio/industrialio-core.c                       |    4 
 drivers/iio/industrialio-trigger.c                    |   12 
 drivers/iio/industrialio-triggered-event.c            |   68 +
 drivers/iio/trigger/iio-trig-sysfs.c                  |   58 +
 include/linux/iio/iio.h                               |    1 
 include/linux/iio/triggered_event.h                   |   11 
 16 files changed, 939 insertions(+), 7 deletions(-)
 create mode 100644 Documentation/ABI/testing/sysfs-bus-iio-adc-hi8435
 create mode 100644 Documentation/devicetree/bindings/iio/adc/hi8435.txt
 create mode 100644 drivers/iio/adc/hi8435.c
 create mode 100644 drivers/iio/industrialio-triggered-event.c
 create mode 100644 include/linux/iio/triggered_event.h

^ permalink raw reply	[flat|nested] 40+ messages in thread

* [PATCH v3 0/7] iio: adc: hi8435: Add Holt HI-8435 threshold detector
@ 2015-07-29 12:54 ` Vladimir Barinov
  0 siblings, 0 replies; 40+ messages in thread
From: Vladimir Barinov @ 2015-07-29 12:54 UTC (permalink / raw)
  To: Jonathan Cameron
  Cc: Hartmut Knaack, Lars-Peter Clausen, Peter Meerwald, Rob Herring,
	Pawel Moll, Mark Rutland, Ian Campbell, Kumar Gala,
	linux-kernel-u79uwXL29TY76Z2rM5mHXA,
	linux-iio-u79uwXL29TY76Z2rM5mHXA,
	devicetree-u79uwXL29TY76Z2rM5mHXA,
	cory.tusar-J6Z/VSE8EyIAspv4Qr0y0gC/G2K4zDHf

Hello,

This adds the folowing:
- Holt threshold detector driver for HI-8435 chip
- Add Holt vendor prefix
- Document HI-8435 DT bindings
- Add periodic polling functionality to SYSFS trigger
- Support triggered events
- Add ABI documentation for debounce_time
- Fix typos in sysfs-bus-iio file

PDF file can be found here:
http://www.holtic.com/products/3081-hi-8435.aspx

Vladimir Barinov (7):
[1/7] iio: adc: hi8435: Holt HI-8435 threshold detector
[2/7] dt: Add vendor prefix 'holt'
[3/7] dt: Document Holt HI-8435 bindings
[4/7] iio: trigger: Add periodic polling to SYSFS trigger
[5/7] iio: Support triggered events
[6/7] iio: Add ABI documentation for debounce_time
[7/7] iio: Fix typos in ABI documentation

---
This patchset is against the 'kernel/git/torvalds/linux.git' repo.

 Documentation/ABI/testing/sysfs-bus-iio               |   16 
 Documentation/ABI/testing/sysfs-bus-iio-adc-hi8435    |   61 +
 Documentation/ABI/testing/sysfs-bus-iio-trigger-sysfs |   11 
 Documentation/devicetree/bindings/iio/adc/hi8435.txt  |   25 
 Documentation/devicetree/bindings/vendor-prefixes.txt |    1 
 drivers/iio/Kconfig                                   |    6 
 drivers/iio/Makefile                                  |    1 
 drivers/iio/adc/Kconfig                               |   11 
 drivers/iio/adc/Makefile                              |    1 
 drivers/iio/adc/hi8435.c                              |  659 ++++++++++++++++++
 drivers/iio/industrialio-core.c                       |    4 
 drivers/iio/industrialio-trigger.c                    |   12 
 drivers/iio/industrialio-triggered-event.c            |   68 +
 drivers/iio/trigger/iio-trig-sysfs.c                  |   58 +
 include/linux/iio/iio.h                               |    1 
 include/linux/iio/triggered_event.h                   |   11 
 16 files changed, 939 insertions(+), 7 deletions(-)
 create mode 100644 Documentation/ABI/testing/sysfs-bus-iio-adc-hi8435
 create mode 100644 Documentation/devicetree/bindings/iio/adc/hi8435.txt
 create mode 100644 drivers/iio/adc/hi8435.c
 create mode 100644 drivers/iio/industrialio-triggered-event.c
 create mode 100644 include/linux/iio/triggered_event.h

^ permalink raw reply	[flat|nested] 40+ messages in thread

* [PATCH v3 1/7] iio: adc: hi8435: Holt HI-8435 threshold detector
@ 2015-07-29 12:56   ` Vladimir Barinov
  0 siblings, 0 replies; 40+ messages in thread
From: Vladimir Barinov @ 2015-07-29 12:56 UTC (permalink / raw)
  To: Jonathan Cameron
  Cc: Hartmut Knaack, Lars-Peter Clausen, Peter Meerwald, Rob Herring,
	Pawel Moll, Mark Rutland, Ian Campbell, Kumar Gala, linux-kernel,
	linux-iio, devicetree, cory.tusar

Add Holt threshold detector driver for HI-8435 chip

Signed-off-by: Vladimir Barinov <vladimir.barinov@cogentembedded.com>
---
Changes in version 2:
- Added file sysfs-bus-iio-adc-hi8435
- Changed naming from "discrete ADC" to "threshold detector"
- Replaced swab16p/swab32p with be16_to_cpup/be32_to_cpup
- Made *_show and *_store functions static
- moved out from iio buffers to iio events
- removed hi8436/hi8437 chips from the driver
- moved from debounce_soft_delay/enable to debounce_interval via
  IIO_CHAN_INFO_DEBOUNCE_TIME
- added name extention "comparator"
- moved threshold/hysteresis setup via generic iio event sysfs
- added software mask/unmask channel events
- added programming sensor outputs while in test mode via
  IIO_CHAN_INFO_RAW
- added channels .ext_info for programming sensing mode
Changes in version 3:
- fixed typos
- moved <linux/interrupt.h> to match alphabetic order
- fixed missed */event/* prefix in the ABI description
- added missed colon in sysfs-bus-iio-adc-hi8435 file after "What"
- moved in_voltageY_comparator_thresh_either_en and debounce_time
  to sysfs-bus-iio file
- added error checking for hi8435_write[b|w]

 Documentation/ABI/testing/sysfs-bus-iio            |   1 +
 Documentation/ABI/testing/sysfs-bus-iio-adc-hi8435 |  61 ++
 drivers/iio/adc/Kconfig                            |  11 +
 drivers/iio/adc/Makefile                           |   1 +
 drivers/iio/adc/hi8435.c                           | 659 +++++++++++++++++++++
 5 files changed, 742 insertions(+)
 create mode 100644 Documentation/ABI/testing/sysfs-bus-iio-adc-hi8435
 create mode 100644 drivers/iio/adc/hi8435.c

diff --git a/Documentation/ABI/testing/sysfs-bus-iio b/Documentation/ABI/testing/sysfs-bus-iio
index 70c9b1a..09eac44 100644
--- a/Documentation/ABI/testing/sysfs-bus-iio
+++ b/Documentation/ABI/testing/sysfs-bus-iio
@@ -572,6 +572,7 @@ What:		/sys/.../iio:deviceX/events/in_rot_from_north_magnetic_tilt_comp_thresh_r
 What:		/sys/.../iio:deviceX/events/in_rot_from_north_magnetic_tilt_comp_thresh_falling_en
 What:		/sys/.../iio:deviceX/events/in_rot_from_north_true_tilt_comp_thresh_rising_en
 What:		/sys/.../iio:deviceX/events/in_rot_from_north_true_tilt_comp_thresh_falling_en
+What:		/sys/.../iio:deviceX/events/in_voltageY_comparator_thresh_either_en
 What:		/sys/.../iio:deviceX/events/in_voltageY_supply_thresh_rising_en
 What:		/sys/.../iio:deviceX/events/in_voltageY_supply_thresh_falling_en
 What:		/sys/.../iio:deviceX/events/in_voltageY_thresh_rising_en
diff --git a/Documentation/ABI/testing/sysfs-bus-iio-adc-hi8435 b/Documentation/ABI/testing/sysfs-bus-iio-adc-hi8435
new file mode 100644
index 0000000..c59d81d
--- /dev/null
+++ b/Documentation/ABI/testing/sysfs-bus-iio-adc-hi8435
@@ -0,0 +1,61 @@
+What:		/sys/bus/iio/devices/iio:deviceX/in_voltageY_comparator_raw
+Date:		July 2015
+KernelVersion:	4.2.0
+Contact:	source@cogentembedded.com
+Description:
+		Read value is a voltage threshold measurement from channel Y.
+		Could be either 0 if sensor voltage is lower then low voltage
+		threshold or 1 if sensor voltage is higher then high voltage
+		threshold.
+		Write value is a programmed sensor output while in self test
+		mode. Could be either 0 or 1. The programmed value will be read
+		back if /sys/bus/iio/devices/iio:deviceX/test_enable is set to 1.
+
+What:		/sys/bus/iio/devices/iio:deviceX/test_enable
+Date:		July 2015
+KernelVersion:	4.2.0
+Contact:	source@cogentembedded.com
+Description:
+		Enable/disable the HI-8435 self test mode.
+		If enabled the in_voltageY_comparator_raw should be read back
+		accordingly to written value to in_voltageY_comparator_raw.
+
+What:		/sys/bus/iio/devices/iio:deviceX/in_voltageY_comparator_sensing_mode
+Date:		July 2015
+KernelVersion:	4.2.0
+Contact:	source@cogentembedded.com
+Description:
+		Program sensor type for threshold detector inputs.
+		Could be either "GND-Open" or "Supply-Open" mode. Y is a
+		threshold detector input channel. Channels 0..7, 8..15, 16..23
+		and 24..31 has common sensor types.
+
+What:		/sys/bus/iio/devices/iio:deviceX/events/in_voltageY_comparator_thresh_falling_value
+Date:		July 2015
+KernelVersion:	4.2.0
+Contact:	source@cogentembedded.com
+Description:
+		Channel Y low voltage threshold. If sensor input voltage goes lower then
+		this value then the threshold falling event is pushed.
+		Depending on in_voltageY_comparator_sensing_mode the low voltage threshold
+		is separately set for "GND-Open" and "Supply-Open" modes.
+		Channels 0..31 have common low threshold values, but could have different
+		sensing_modes.
+		The low voltage threshold range is between 2..21V.
+		Hysteresis between low and high thresholds can not be lower then 2 and
+		can not be odd.
+
+What:		/sys/bus/iio/devices/iio:deviceX/events/in_voltageY_comparator_thresh_rising_value
+Date:		July 2015
+KernelVersion:	4.2.0
+Contact:	source@cogentembedded.com
+Description:
+		Channel Y high voltage threshold. If sensor input voltage goes higher then
+		this value then the threshold rising event is pushed.
+		Depending on in_voltageY_comparator_sensing_mode the high voltage threshold
+		is separately set for "GND-Open" and "Supply-Open" modes.
+		Channels 0..31 have common high threshold values, but could have different
+		sensing_modes.
+		The high voltage threshold range is between 3..22V.
+		Hysteresis between low and high thresholds can not be lower then 2 and
+		can not be odd.
diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig
index eb0cd89..553c91e 100644
--- a/drivers/iio/adc/Kconfig
+++ b/drivers/iio/adc/Kconfig
@@ -170,6 +170,17 @@ config EXYNOS_ADC
 	  of SoCs for drivers such as the touchscreen and hwmon to use to share
 	  this resource.
 
+config HI8435
+	tristate "Holt Integrated Circuits HI-8435 threshold detector"
+	select IIO_TRIGGERED_EVENT
+	depends on SPI
+	help
+	  If you say yes here you get support for Holt Integrated Circuits
+	  HI-8435 chip.
+
+	  This driver can also be built as a module. If so, the module will be
+	  called hi8435.
+
 config LP8788_ADC
 	tristate "LP8788 ADC driver"
 	depends on MFD_LP8788
diff --git a/drivers/iio/adc/Makefile b/drivers/iio/adc/Makefile
index a096210..00f367aa 100644
--- a/drivers/iio/adc/Makefile
+++ b/drivers/iio/adc/Makefile
@@ -19,6 +19,7 @@ obj-$(CONFIG_BERLIN2_ADC) += berlin2-adc.o
 obj-$(CONFIG_DA9150_GPADC) += da9150-gpadc.o
 obj-$(CONFIG_CC10001_ADC) += cc10001_adc.o
 obj-$(CONFIG_EXYNOS_ADC) += exynos_adc.o
+obj-$(CONFIG_HI8435) += hi8435.o
 obj-$(CONFIG_LP8788_ADC) += lp8788_adc.o
 obj-$(CONFIG_MAX1027) += max1027.o
 obj-$(CONFIG_MAX1363) += max1363.o
diff --git a/drivers/iio/adc/hi8435.c b/drivers/iio/adc/hi8435.c
new file mode 100644
index 0000000..f3dab5a
--- /dev/null
+++ b/drivers/iio/adc/hi8435.c
@@ -0,0 +1,659 @@
+/*
+ * Holt Integrated Circuits HI-8435 threshold detector driver
+ *
+ * Copyright (C) 2015 Zodiac Inflight Innovations
+ * Copyright (C) 2015 Cogent Embedded, Inc.
+ *
+ * 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.
+ */
+
+#include <linux/delay.h>
+#include <linux/iio/events.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/triggered_event.h>
+#include <linux/interrupt.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/of_gpio.h>
+#include <linux/spi/spi.h>
+#include <linux/workqueue.h>
+
+#define DRV_NAME "hi8435"
+
+/* Register offsets for HI-8435 */
+#define HI8435_CTRL_REG			0x02
+#define HI8435_PSEN_REG			0x04
+#define HI8435_TMDATA_REG		0x1E
+#define HI8435_GOCENHYS_REG		0x3A
+#define HI8435_SOCENHYS_REG		0x3C
+#define HI8435_SO7_0_REG		0x10
+#define HI8435_SO15_8_REG		0x12
+#define HI8435_SO23_16_REG		0x14
+#define HI8435_SO31_24_REG		0x16
+#define HI8435_SO31_0_REG		0x78
+
+#define HI8435_WRITE_OPCODE		0x00
+#define HI8435_READ_OPCODE		0x80
+
+/* CTRL register bits */
+#define HI8435_CTRL_TEST		0x01
+#define HI8435_CTRL_SRST		0x02
+
+#define HI8435_DEBOUNCE_DELAY_MAX	1000	/* msec */
+#define HI8435_DEBOUNCE_DELAY_DEF	100	/* msec */
+
+struct hi8435_priv {
+	struct spi_device *spi;
+	struct mutex lock;
+	struct delayed_work work;
+
+	int reset_gpio;
+	int debounce_interval; /* msec */
+	u32 debounce_val; /* prev value to compare during software debounce */
+
+	unsigned long event_scan_mask; /* soft mask/unmask channels events */
+	unsigned int event_prev_val;
+
+	unsigned threshold_lo[2]; /* GND-Open and Supply-Open thresholds */
+	unsigned threshold_hi[2]; /* GND-Open and Supply-Open threshold */
+	u8 reg_buffer[4] ____cacheline_aligned;
+};
+
+static int hi8435_readb(struct hi8435_priv *priv, u8 reg, u8 *val)
+{
+	reg |= HI8435_READ_OPCODE;
+	return spi_write_then_read(priv->spi, &reg, 1, val, 1);
+}
+
+static int hi8435_readw(struct hi8435_priv *priv, u8 reg, u16 *val)
+{
+	int ret;
+
+	reg |= HI8435_READ_OPCODE;
+	ret = spi_write_then_read(priv->spi, &reg, 1, val, 2);
+	*val = be16_to_cpup(val);
+
+	return ret;
+}
+
+static int hi8435_readl(struct hi8435_priv *priv, u8 reg, u32 *val)
+{
+	int ret;
+
+	reg |= HI8435_READ_OPCODE;
+	ret = spi_write_then_read(priv->spi, &reg, 1, val, 4);
+	*val = be32_to_cpup(val);
+
+	return ret;
+}
+
+static int hi8435_writeb(struct hi8435_priv *priv, u8 reg, u8 val)
+{
+	priv->reg_buffer[0] = reg | HI8435_WRITE_OPCODE;
+	priv->reg_buffer[1] = val;
+
+	return spi_write(priv->spi, priv->reg_buffer, 2);
+}
+
+static int hi8435_writew(struct hi8435_priv *priv, u8 reg, u16 val)
+{
+	priv->reg_buffer[0] = reg | HI8435_WRITE_OPCODE;
+	priv->reg_buffer[1] = (val >> 8) & 0xff;
+	priv->reg_buffer[2] = val & 0xff;
+
+	return spi_write(priv->spi, priv->reg_buffer, 3);
+}
+
+static ssize_t hi8435_test_enable_show(struct device *dev,
+				       struct device_attribute *attr, char *buf)
+{
+	struct hi8435_priv *priv = iio_priv(dev_to_iio_dev(dev));
+	int ret;
+	u8 reg;
+
+	ret = hi8435_readb(priv, HI8435_CTRL_REG, &reg);
+	if (ret < 0)
+		return ret;
+
+	return sprintf(buf, "%d\n", reg & HI8435_CTRL_TEST);
+}
+
+static ssize_t hi8435_test_enable_store(struct device *dev,
+					struct device_attribute *attr,
+					const char *buf, size_t len)
+{
+	struct hi8435_priv *priv = iio_priv(dev_to_iio_dev(dev));
+	unsigned int val;
+	int ret;
+
+	ret = kstrtouint(buf, 10, &val);
+	if (ret)
+		return ret;
+
+	ret = hi8435_writeb(priv, HI8435_CTRL_REG, val ? HI8435_CTRL_TEST : 0);
+	if (ret < 0)
+		return ret;
+
+	return len;
+}
+
+static IIO_DEVICE_ATTR(test_enable, S_IRUGO | S_IWUSR,
+	hi8435_test_enable_show, hi8435_test_enable_store, 0);
+
+static struct attribute *hi8435_attributes[] = {
+	&iio_dev_attr_test_enable.dev_attr.attr,
+	NULL,
+};
+
+static struct attribute_group hi8435_attribute_group = {
+	.attrs = hi8435_attributes,
+};
+
+static int hi8435_read_raw(struct iio_dev *idev,
+			   const struct iio_chan_spec *chan,
+			   int *val, int *val2, long mask)
+{
+	struct hi8435_priv *priv = iio_priv(idev);
+	int ret;
+
+	switch (mask) {
+	case IIO_CHAN_INFO_RAW:
+		ret = hi8435_readl(priv, HI8435_SO31_0_REG, val);
+		if (ret < 0)
+			return ret;
+		*val = !!(*val & BIT(chan->channel));
+		return IIO_VAL_INT;
+	case IIO_CHAN_INFO_DEBOUNCE_TIME:
+		*val = priv->debounce_interval;
+		return IIO_VAL_INT;
+	default:
+		return -EINVAL;
+	}
+}
+
+static int hi8435_write_raw(struct iio_dev *idev,
+			    const struct iio_chan_spec *chan,
+			    int val, int val2, long mask)
+{
+	struct hi8435_priv *priv = iio_priv(idev);
+
+	switch (mask) {
+	case IIO_CHAN_INFO_RAW:
+		/* program sensors outputs in test mode */
+		return hi8435_writeb(priv, HI8435_TMDATA_REG, val ? 0x1 : 0x2);
+	case IIO_CHAN_INFO_DEBOUNCE_TIME:
+		if (val < 0)
+			return -EINVAL;
+		if (val > HI8435_DEBOUNCE_DELAY_MAX)
+			val = HI8435_DEBOUNCE_DELAY_MAX;
+		priv->debounce_interval = val;
+		return 0;
+	default:
+		return -EINVAL;
+	}
+}
+
+static int hi8435_read_event_config(struct iio_dev *idev,
+				    const struct iio_chan_spec *chan,
+				    enum iio_event_type type,
+				    enum iio_event_direction dir)
+{
+	struct hi8435_priv *priv = iio_priv(idev);
+
+	return !!(priv->event_scan_mask & BIT(chan->channel));
+}
+
+static int hi8435_write_event_config(struct iio_dev *idev,
+				     const struct iio_chan_spec *chan,
+				     enum iio_event_type type,
+				     enum iio_event_direction dir, int state)
+{
+	struct hi8435_priv *priv = iio_priv(idev);
+
+	priv->event_scan_mask &= ~BIT(chan->channel);
+	if (state)
+		priv->event_scan_mask |= BIT(chan->channel);
+
+	return 0;
+}
+
+static int hi8435_read_event_value(struct iio_dev *idev,
+				   const struct iio_chan_spec *chan,
+				   enum iio_event_type type,
+				   enum iio_event_direction dir,
+				   enum iio_event_info info,
+				   int *val, int *val2)
+{
+	struct hi8435_priv *priv = iio_priv(idev);
+	int ret;
+	u8 mode, psen;
+	u16 reg;
+
+	ret = hi8435_readb(priv, HI8435_PSEN_REG, &psen);
+	if (ret < 0)
+		return ret;
+
+	/* Supply-Open or GND-Open sensing mode */
+	mode = !!(psen & BIT(chan->channel / 8));
+
+	ret = hi8435_readw(priv, mode ? HI8435_SOCENHYS_REG :
+				 HI8435_GOCENHYS_REG, &reg);
+	if (ret < 0)
+		return ret;
+
+	if (dir == IIO_EV_DIR_FALLING)
+		*val = ((reg & 0xff) - (reg >> 8)) / 2;
+
+	if (dir == IIO_EV_DIR_RISING)
+		*val = ((reg & 0xff) + (reg >> 8)) / 2;
+
+	return IIO_VAL_INT;
+}
+
+static int hi8435_write_event_value(struct iio_dev *idev,
+				    const struct iio_chan_spec *chan,
+				    enum iio_event_type type,
+				    enum iio_event_direction dir,
+				    enum iio_event_info info,
+				    int val, int val2)
+{
+	struct hi8435_priv *priv = iio_priv(idev);
+	int ret;
+	u8 mode, psen;
+	u16 reg;
+
+	ret = hi8435_readb(priv, HI8435_PSEN_REG, &psen);
+	if (ret < 0)
+		return ret;
+
+	/* Supply-Open or GND-Open sensing mode */
+	mode = !!(psen & BIT(chan->channel / 8));
+
+	ret = hi8435_readw(priv, mode ? HI8435_SOCENHYS_REG :
+				 HI8435_GOCENHYS_REG, &reg);
+	if (ret < 0)
+		return ret;
+
+	if (dir == IIO_EV_DIR_FALLING) {
+		/* falling threshold range 2..21V, hysteresis minimum 2V */
+		if (val < 2 || val > 21 || (val + 1) >= priv->threshold_hi[mode])
+			return -EINVAL;
+
+		if (val == priv->threshold_lo[mode])
+			return 0;
+
+		priv->threshold_lo[mode] = val;
+
+		/* hysteresis must not be odd */
+		if ((priv->threshold_hi[mode] - priv->threshold_lo[mode]) % 2)
+			priv->threshold_hi[mode]--;
+	}
+
+	if (dir == IIO_EV_DIR_RISING) {
+		/* rising threshold range 3..22V, hysteresis minimum 2V */
+		if (val < 3 || val > 22 || val <= (priv->threshold_lo[mode] + 1))
+			return -EINVAL;
+
+		if (val == priv->threshold_hi[mode])
+			return 0;
+
+		priv->threshold_hi[mode] = val;
+
+		/* hysteresis must not be odd */
+		if ((priv->threshold_hi[mode] - priv->threshold_lo[mode]) % 2)
+			priv->threshold_lo[mode]++;
+	}
+
+	/* program thresholds */
+	mutex_lock(&priv->lock);
+
+	ret = hi8435_readw(priv, mode ? HI8435_SOCENHYS_REG :
+				 HI8435_GOCENHYS_REG, &reg);
+	if (ret < 0) {
+		mutex_unlock(&priv->lock);
+		return ret;
+	}
+
+	/* hysteresis */
+	reg = priv->threshold_hi[mode] - priv->threshold_lo[mode];
+	reg <<= 8;
+	/* threshold center */
+	reg |= (priv->threshold_hi[mode] + priv->threshold_lo[mode]);
+
+	ret = hi8435_writew(priv, mode ? HI8435_SOCENHYS_REG :
+				  HI8435_GOCENHYS_REG, reg);
+
+	mutex_unlock(&priv->lock);
+
+	return ret;
+}
+
+static const struct iio_event_spec hi8435_events[] = {
+	{
+		.type = IIO_EV_TYPE_THRESH,
+		.dir = IIO_EV_DIR_RISING,
+		.mask_separate = BIT(IIO_EV_INFO_VALUE),
+	}, {
+		.type = IIO_EV_TYPE_THRESH,
+		.dir = IIO_EV_DIR_FALLING,
+		.mask_separate = BIT(IIO_EV_INFO_VALUE),
+	}, {
+		.type = IIO_EV_TYPE_THRESH,
+		.dir = IIO_EV_DIR_EITHER,
+		.mask_separate = BIT(IIO_EV_INFO_ENABLE),
+	},
+};
+
+static int hi8435_get_sensing_mode(struct iio_dev *idev,
+				   const struct iio_chan_spec *chan)
+{
+	struct hi8435_priv *priv = iio_priv(idev);
+	int ret;
+	u8 reg;
+
+	ret = hi8435_readb(priv, HI8435_PSEN_REG, &reg);
+	if (ret < 0)
+		return ret;
+
+	return !!(reg & BIT(chan->channel / 8));
+}
+
+static int hi8435_set_sensing_mode(struct iio_dev *idev,
+				   const struct iio_chan_spec *chan,
+				   unsigned int mode)
+{
+	struct hi8435_priv *priv = iio_priv(idev);
+	int ret;
+	u8 reg;
+
+	mutex_lock(&priv->lock);
+
+	ret = hi8435_readb(priv, HI8435_PSEN_REG, &reg);
+	if (ret < 0) {
+		mutex_unlock(&priv->lock);
+		return ret;
+	}
+
+	reg &= ~BIT(chan->channel / 8);
+	if (mode)
+		reg |= BIT(chan->channel / 8);
+
+	ret = hi8435_writeb(priv, HI8435_PSEN_REG, reg);
+
+	mutex_unlock(&priv->lock);
+
+	return ret;
+}
+
+static const char * const hi8435_sensing_modes[] = { "GND-Open",
+						     "Supply-Open" };
+
+static const struct iio_enum hi8435_sensing_mode = {
+	.items = hi8435_sensing_modes,
+	.num_items = ARRAY_SIZE(hi8435_sensing_modes),
+	.get = hi8435_get_sensing_mode,
+	.set = hi8435_set_sensing_mode,
+};
+
+static const struct iio_chan_spec_ext_info hi8435_ext_info[] = {
+	IIO_ENUM("sensing_mode", IIO_SEPARATE, &hi8435_sensing_mode),
+	{},
+};
+
+#define HI8435_VOLTAGE_CHANNEL(num)					\
+{									\
+	.type = IIO_VOLTAGE,						\
+	.indexed = 1,							\
+	.channel = num,							\
+	.info_mask_separate = BIT(IIO_CHAN_INFO_RAW),			\
+	.info_mask_shared_by_all = BIT(IIO_CHAN_INFO_DEBOUNCE_TIME),	\
+	.event_spec = hi8435_events,					\
+	.num_event_specs = ARRAY_SIZE(hi8435_events),			\
+	.ext_info = hi8435_ext_info,					\
+	.extend_name = "comparator"					\
+}
+
+static const struct iio_chan_spec hi8435_channels[] = {
+	HI8435_VOLTAGE_CHANNEL(0),
+	HI8435_VOLTAGE_CHANNEL(1),
+	HI8435_VOLTAGE_CHANNEL(2),
+	HI8435_VOLTAGE_CHANNEL(3),
+	HI8435_VOLTAGE_CHANNEL(4),
+	HI8435_VOLTAGE_CHANNEL(5),
+	HI8435_VOLTAGE_CHANNEL(6),
+	HI8435_VOLTAGE_CHANNEL(7),
+	HI8435_VOLTAGE_CHANNEL(8),
+	HI8435_VOLTAGE_CHANNEL(9),
+	HI8435_VOLTAGE_CHANNEL(10),
+	HI8435_VOLTAGE_CHANNEL(11),
+	HI8435_VOLTAGE_CHANNEL(12),
+	HI8435_VOLTAGE_CHANNEL(13),
+	HI8435_VOLTAGE_CHANNEL(14),
+	HI8435_VOLTAGE_CHANNEL(15),
+	HI8435_VOLTAGE_CHANNEL(16),
+	HI8435_VOLTAGE_CHANNEL(17),
+	HI8435_VOLTAGE_CHANNEL(18),
+	HI8435_VOLTAGE_CHANNEL(19),
+	HI8435_VOLTAGE_CHANNEL(20),
+	HI8435_VOLTAGE_CHANNEL(21),
+	HI8435_VOLTAGE_CHANNEL(22),
+	HI8435_VOLTAGE_CHANNEL(23),
+	HI8435_VOLTAGE_CHANNEL(24),
+	HI8435_VOLTAGE_CHANNEL(25),
+	HI8435_VOLTAGE_CHANNEL(26),
+	HI8435_VOLTAGE_CHANNEL(27),
+	HI8435_VOLTAGE_CHANNEL(28),
+	HI8435_VOLTAGE_CHANNEL(29),
+	HI8435_VOLTAGE_CHANNEL(30),
+	HI8435_VOLTAGE_CHANNEL(31),
+	IIO_CHAN_SOFT_TIMESTAMP(32),
+};
+
+static const struct iio_info hi8435_info = {
+	.driver_module = THIS_MODULE,
+	.attrs = &hi8435_attribute_group,
+	.read_raw = hi8435_read_raw,
+	.write_raw = hi8435_write_raw,
+	.read_event_config = &hi8435_read_event_config,
+	.write_event_config = hi8435_write_event_config,
+	.read_event_value = &hi8435_read_event_value,
+	.write_event_value = &hi8435_write_event_value,
+};
+
+static void hi8435_iio_push_event(struct iio_dev *idev, unsigned int val)
+{
+	struct hi8435_priv *priv = iio_priv(idev);
+	enum iio_event_direction dir;
+	unsigned int i;
+	unsigned int status = priv->event_prev_val ^ val;
+
+	if (!status)
+		return;
+
+	for_each_set_bit(i, &priv->event_scan_mask, 32) {
+		if (!(status & BIT(i)))
+			continue;
+
+		dir = val & BIT(i) ? IIO_EV_DIR_RISING : IIO_EV_DIR_FALLING;
+
+		iio_push_event(idev,
+			       IIO_UNMOD_EVENT_CODE(IIO_VOLTAGE, i,
+						    IIO_EV_TYPE_THRESH, dir),
+			       iio_get_time_ns());
+	}
+
+	priv->event_prev_val = val;
+}
+
+static void hi8435_debounce_work(struct work_struct *work)
+{
+	struct hi8435_priv *priv = container_of(work, struct hi8435_priv,
+						work.work);
+	struct iio_dev *idev = spi_get_drvdata(priv->spi);
+	u32 val;
+	int ret;
+
+	ret = hi8435_readl(priv, HI8435_SO31_0_REG, &val);
+	if (ret < 0)
+		return;
+
+	if (val == priv->debounce_val)
+		hi8435_iio_push_event(idev, val);
+	else
+		dev_warn(&priv->spi->dev, "filtered by software debounce");
+}
+
+static irqreturn_t hi8435_trigger_handler(int irq, void *private)
+{
+	struct iio_poll_func *pf = private;
+	struct iio_dev *idev = pf->indio_dev;
+	struct hi8435_priv *priv = iio_priv(idev);
+	u32 val;
+	int ret;
+
+	ret = hi8435_readl(priv, HI8435_SO31_0_REG, &val);
+	if (ret < 0)
+		goto err_read;
+
+	if (priv->debounce_interval) {
+		priv->debounce_val = val;
+		schedule_delayed_work(&priv->work,
+				msecs_to_jiffies(priv->debounce_interval));
+	} else {
+		hi8435_iio_push_event(idev, val);
+	}
+
+err_read:
+	iio_trigger_notify_done(idev->trig);
+
+	return IRQ_HANDLED;
+}
+
+static void hi8435_parse_dt(struct hi8435_priv *priv)
+{
+	struct device_node *np = priv->spi->dev.of_node;
+	int ret;
+
+	ret = of_get_named_gpio(np, "holt,reset-gpios", 0);
+	priv->reset_gpio = ret < 0 ? 0 : ret;
+
+	ret = of_property_read_u32(np, "holt,debounce-interval",
+				   &priv->debounce_interval);
+	if (ret)
+		priv->debounce_interval = 0;
+	if (priv->debounce_interval > HI8435_DEBOUNCE_DELAY_MAX)
+		priv->debounce_interval = HI8435_DEBOUNCE_DELAY_MAX;
+}
+
+static int hi8435_probe(struct spi_device *spi)
+{
+	struct iio_dev *idev;
+	struct hi8435_priv *priv;
+	int ret;
+
+	idev = devm_iio_device_alloc(&spi->dev, sizeof(*priv));
+	if (!idev)
+		return -ENOMEM;
+
+	priv = iio_priv(idev);
+	priv->spi = spi;
+
+	if (spi->dev.of_node)
+		hi8435_parse_dt(priv);
+
+	spi_set_drvdata(spi, idev);
+	mutex_init(&priv->lock);
+	INIT_DELAYED_WORK(&priv->work, hi8435_debounce_work);
+
+	idev->dev.parent	= &spi->dev;
+	idev->name		= spi_get_device_id(spi)->name;
+	idev->modes		= INDIO_DIRECT_MODE;
+	idev->info		= &hi8435_info;
+	idev->channels		= hi8435_channels;
+	idev->num_channels	= ARRAY_SIZE(hi8435_channels);
+
+	if (priv->reset_gpio) {
+		ret = devm_gpio_request(&spi->dev, priv->reset_gpio, idev->name);
+		if (!ret) {
+			/* chip hardware reset */
+			gpio_direction_output(priv->reset_gpio, 0);
+			udelay(5);
+			gpio_direction_output(priv->reset_gpio, 1);
+		}
+	} else {
+		/* chip software reset */
+		hi8435_writeb(priv, HI8435_CTRL_REG, HI8435_CTRL_SRST);
+		/* get out from reset state */
+		hi8435_writeb(priv, HI8435_CTRL_REG, 0);
+	}
+
+	/* unmask all events */
+	priv->event_scan_mask = ~(0);
+	/* initialize default thresholds */
+	priv->threshold_lo[0] = priv->threshold_lo[1] = 2;
+	priv->threshold_hi[0] = priv->threshold_hi[1] = 4;
+	hi8435_writew(priv, HI8435_GOCENHYS_REG, 0x206);
+	hi8435_writew(priv, HI8435_SOCENHYS_REG, 0x206);
+
+	ret = iio_triggered_event_setup(idev, NULL, hi8435_trigger_handler);
+	if (ret)
+		return ret;
+
+	ret = iio_device_register(idev);
+	if (ret < 0) {
+		dev_err(&spi->dev, "unable to register device\n");
+		goto unregister_triggered_event;
+	}
+
+	return 0;
+
+unregister_triggered_event:
+	iio_triggered_event_cleanup(idev);
+	return ret;
+}
+
+static int hi8435_remove(struct spi_device *spi)
+{
+	struct iio_dev *idev = spi_get_drvdata(spi);
+	struct hi8435_priv *priv = iio_priv(idev);
+
+	cancel_delayed_work_sync(&priv->work);
+	iio_device_unregister(idev);
+	iio_triggered_event_cleanup(idev);
+
+	return 0;
+}
+
+static const struct of_device_id hi8435_dt_ids[] = {
+	{ .compatible = "holt,hi8435" },
+	{},
+};
+MODULE_DEVICE_TABLE(of, hi8435_dt_ids);
+
+static const struct spi_device_id hi8435_id[] = {
+	{ "hi8435", 0},
+	{ }
+};
+MODULE_DEVICE_TABLE(spi, hi8435_id);
+
+static struct spi_driver hi8435_driver = {
+	.driver	= {
+		.name		= DRV_NAME,
+		.of_match_table	= of_match_ptr(hi8435_dt_ids),
+	},
+	.probe		= hi8435_probe,
+	.remove		= hi8435_remove,
+	.id_table	= hi8435_id,
+};
+module_spi_driver(hi8435_driver);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Vladimir Barinov");
+MODULE_DESCRIPTION("HI-8435 threshold detector");
-- 
1.9.1


^ permalink raw reply related	[flat|nested] 40+ messages in thread

* [PATCH v3 1/7] iio: adc: hi8435: Holt HI-8435 threshold detector
@ 2015-07-29 12:56   ` Vladimir Barinov
  0 siblings, 0 replies; 40+ messages in thread
From: Vladimir Barinov @ 2015-07-29 12:56 UTC (permalink / raw)
  To: Jonathan Cameron
  Cc: Hartmut Knaack, Lars-Peter Clausen, Peter Meerwald, Rob Herring,
	Pawel Moll, Mark Rutland, Ian Campbell, Kumar Gala,
	linux-kernel-u79uwXL29TY76Z2rM5mHXA,
	linux-iio-u79uwXL29TY76Z2rM5mHXA,
	devicetree-u79uwXL29TY76Z2rM5mHXA,
	cory.tusar-J6Z/VSE8EyIAspv4Qr0y0gC/G2K4zDHf

Add Holt threshold detector driver for HI-8435 chip

Signed-off-by: Vladimir Barinov <vladimir.barinov-M4DtvfQ/ZS1MRgGoP+s0PdBPR1lH4CV8@public.gmane.org>
---
Changes in version 2:
- Added file sysfs-bus-iio-adc-hi8435
- Changed naming from "discrete ADC" to "threshold detector"
- Replaced swab16p/swab32p with be16_to_cpup/be32_to_cpup
- Made *_show and *_store functions static
- moved out from iio buffers to iio events
- removed hi8436/hi8437 chips from the driver
- moved from debounce_soft_delay/enable to debounce_interval via
  IIO_CHAN_INFO_DEBOUNCE_TIME
- added name extention "comparator"
- moved threshold/hysteresis setup via generic iio event sysfs
- added software mask/unmask channel events
- added programming sensor outputs while in test mode via
  IIO_CHAN_INFO_RAW
- added channels .ext_info for programming sensing mode
Changes in version 3:
- fixed typos
- moved <linux/interrupt.h> to match alphabetic order
- fixed missed */event/* prefix in the ABI description
- added missed colon in sysfs-bus-iio-adc-hi8435 file after "What"
- moved in_voltageY_comparator_thresh_either_en and debounce_time
  to sysfs-bus-iio file
- added error checking for hi8435_write[b|w]

 Documentation/ABI/testing/sysfs-bus-iio            |   1 +
 Documentation/ABI/testing/sysfs-bus-iio-adc-hi8435 |  61 ++
 drivers/iio/adc/Kconfig                            |  11 +
 drivers/iio/adc/Makefile                           |   1 +
 drivers/iio/adc/hi8435.c                           | 659 +++++++++++++++++++++
 5 files changed, 742 insertions(+)
 create mode 100644 Documentation/ABI/testing/sysfs-bus-iio-adc-hi8435
 create mode 100644 drivers/iio/adc/hi8435.c

diff --git a/Documentation/ABI/testing/sysfs-bus-iio b/Documentation/ABI/testing/sysfs-bus-iio
index 70c9b1a..09eac44 100644
--- a/Documentation/ABI/testing/sysfs-bus-iio
+++ b/Documentation/ABI/testing/sysfs-bus-iio
@@ -572,6 +572,7 @@ What:		/sys/.../iio:deviceX/events/in_rot_from_north_magnetic_tilt_comp_thresh_r
 What:		/sys/.../iio:deviceX/events/in_rot_from_north_magnetic_tilt_comp_thresh_falling_en
 What:		/sys/.../iio:deviceX/events/in_rot_from_north_true_tilt_comp_thresh_rising_en
 What:		/sys/.../iio:deviceX/events/in_rot_from_north_true_tilt_comp_thresh_falling_en
+What:		/sys/.../iio:deviceX/events/in_voltageY_comparator_thresh_either_en
 What:		/sys/.../iio:deviceX/events/in_voltageY_supply_thresh_rising_en
 What:		/sys/.../iio:deviceX/events/in_voltageY_supply_thresh_falling_en
 What:		/sys/.../iio:deviceX/events/in_voltageY_thresh_rising_en
diff --git a/Documentation/ABI/testing/sysfs-bus-iio-adc-hi8435 b/Documentation/ABI/testing/sysfs-bus-iio-adc-hi8435
new file mode 100644
index 0000000..c59d81d
--- /dev/null
+++ b/Documentation/ABI/testing/sysfs-bus-iio-adc-hi8435
@@ -0,0 +1,61 @@
+What:		/sys/bus/iio/devices/iio:deviceX/in_voltageY_comparator_raw
+Date:		July 2015
+KernelVersion:	4.2.0
+Contact:	source-M4DtvfQ/ZS1MRgGoP+s0PdBPR1lH4CV8@public.gmane.org
+Description:
+		Read value is a voltage threshold measurement from channel Y.
+		Could be either 0 if sensor voltage is lower then low voltage
+		threshold or 1 if sensor voltage is higher then high voltage
+		threshold.
+		Write value is a programmed sensor output while in self test
+		mode. Could be either 0 or 1. The programmed value will be read
+		back if /sys/bus/iio/devices/iio:deviceX/test_enable is set to 1.
+
+What:		/sys/bus/iio/devices/iio:deviceX/test_enable
+Date:		July 2015
+KernelVersion:	4.2.0
+Contact:	source-M4DtvfQ/ZS1MRgGoP+s0PdBPR1lH4CV8@public.gmane.org
+Description:
+		Enable/disable the HI-8435 self test mode.
+		If enabled the in_voltageY_comparator_raw should be read back
+		accordingly to written value to in_voltageY_comparator_raw.
+
+What:		/sys/bus/iio/devices/iio:deviceX/in_voltageY_comparator_sensing_mode
+Date:		July 2015
+KernelVersion:	4.2.0
+Contact:	source-M4DtvfQ/ZS1MRgGoP+s0PdBPR1lH4CV8@public.gmane.org
+Description:
+		Program sensor type for threshold detector inputs.
+		Could be either "GND-Open" or "Supply-Open" mode. Y is a
+		threshold detector input channel. Channels 0..7, 8..15, 16..23
+		and 24..31 has common sensor types.
+
+What:		/sys/bus/iio/devices/iio:deviceX/events/in_voltageY_comparator_thresh_falling_value
+Date:		July 2015
+KernelVersion:	4.2.0
+Contact:	source-M4DtvfQ/ZS1MRgGoP+s0PdBPR1lH4CV8@public.gmane.org
+Description:
+		Channel Y low voltage threshold. If sensor input voltage goes lower then
+		this value then the threshold falling event is pushed.
+		Depending on in_voltageY_comparator_sensing_mode the low voltage threshold
+		is separately set for "GND-Open" and "Supply-Open" modes.
+		Channels 0..31 have common low threshold values, but could have different
+		sensing_modes.
+		The low voltage threshold range is between 2..21V.
+		Hysteresis between low and high thresholds can not be lower then 2 and
+		can not be odd.
+
+What:		/sys/bus/iio/devices/iio:deviceX/events/in_voltageY_comparator_thresh_rising_value
+Date:		July 2015
+KernelVersion:	4.2.0
+Contact:	source-M4DtvfQ/ZS1MRgGoP+s0PdBPR1lH4CV8@public.gmane.org
+Description:
+		Channel Y high voltage threshold. If sensor input voltage goes higher then
+		this value then the threshold rising event is pushed.
+		Depending on in_voltageY_comparator_sensing_mode the high voltage threshold
+		is separately set for "GND-Open" and "Supply-Open" modes.
+		Channels 0..31 have common high threshold values, but could have different
+		sensing_modes.
+		The high voltage threshold range is between 3..22V.
+		Hysteresis between low and high thresholds can not be lower then 2 and
+		can not be odd.
diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig
index eb0cd89..553c91e 100644
--- a/drivers/iio/adc/Kconfig
+++ b/drivers/iio/adc/Kconfig
@@ -170,6 +170,17 @@ config EXYNOS_ADC
 	  of SoCs for drivers such as the touchscreen and hwmon to use to share
 	  this resource.
 
+config HI8435
+	tristate "Holt Integrated Circuits HI-8435 threshold detector"
+	select IIO_TRIGGERED_EVENT
+	depends on SPI
+	help
+	  If you say yes here you get support for Holt Integrated Circuits
+	  HI-8435 chip.
+
+	  This driver can also be built as a module. If so, the module will be
+	  called hi8435.
+
 config LP8788_ADC
 	tristate "LP8788 ADC driver"
 	depends on MFD_LP8788
diff --git a/drivers/iio/adc/Makefile b/drivers/iio/adc/Makefile
index a096210..00f367aa 100644
--- a/drivers/iio/adc/Makefile
+++ b/drivers/iio/adc/Makefile
@@ -19,6 +19,7 @@ obj-$(CONFIG_BERLIN2_ADC) += berlin2-adc.o
 obj-$(CONFIG_DA9150_GPADC) += da9150-gpadc.o
 obj-$(CONFIG_CC10001_ADC) += cc10001_adc.o
 obj-$(CONFIG_EXYNOS_ADC) += exynos_adc.o
+obj-$(CONFIG_HI8435) += hi8435.o
 obj-$(CONFIG_LP8788_ADC) += lp8788_adc.o
 obj-$(CONFIG_MAX1027) += max1027.o
 obj-$(CONFIG_MAX1363) += max1363.o
diff --git a/drivers/iio/adc/hi8435.c b/drivers/iio/adc/hi8435.c
new file mode 100644
index 0000000..f3dab5a
--- /dev/null
+++ b/drivers/iio/adc/hi8435.c
@@ -0,0 +1,659 @@
+/*
+ * Holt Integrated Circuits HI-8435 threshold detector driver
+ *
+ * Copyright (C) 2015 Zodiac Inflight Innovations
+ * Copyright (C) 2015 Cogent Embedded, Inc.
+ *
+ * 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.
+ */
+
+#include <linux/delay.h>
+#include <linux/iio/events.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/triggered_event.h>
+#include <linux/interrupt.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/of_gpio.h>
+#include <linux/spi/spi.h>
+#include <linux/workqueue.h>
+
+#define DRV_NAME "hi8435"
+
+/* Register offsets for HI-8435 */
+#define HI8435_CTRL_REG			0x02
+#define HI8435_PSEN_REG			0x04
+#define HI8435_TMDATA_REG		0x1E
+#define HI8435_GOCENHYS_REG		0x3A
+#define HI8435_SOCENHYS_REG		0x3C
+#define HI8435_SO7_0_REG		0x10
+#define HI8435_SO15_8_REG		0x12
+#define HI8435_SO23_16_REG		0x14
+#define HI8435_SO31_24_REG		0x16
+#define HI8435_SO31_0_REG		0x78
+
+#define HI8435_WRITE_OPCODE		0x00
+#define HI8435_READ_OPCODE		0x80
+
+/* CTRL register bits */
+#define HI8435_CTRL_TEST		0x01
+#define HI8435_CTRL_SRST		0x02
+
+#define HI8435_DEBOUNCE_DELAY_MAX	1000	/* msec */
+#define HI8435_DEBOUNCE_DELAY_DEF	100	/* msec */
+
+struct hi8435_priv {
+	struct spi_device *spi;
+	struct mutex lock;
+	struct delayed_work work;
+
+	int reset_gpio;
+	int debounce_interval; /* msec */
+	u32 debounce_val; /* prev value to compare during software debounce */
+
+	unsigned long event_scan_mask; /* soft mask/unmask channels events */
+	unsigned int event_prev_val;
+
+	unsigned threshold_lo[2]; /* GND-Open and Supply-Open thresholds */
+	unsigned threshold_hi[2]; /* GND-Open and Supply-Open threshold */
+	u8 reg_buffer[4] ____cacheline_aligned;
+};
+
+static int hi8435_readb(struct hi8435_priv *priv, u8 reg, u8 *val)
+{
+	reg |= HI8435_READ_OPCODE;
+	return spi_write_then_read(priv->spi, &reg, 1, val, 1);
+}
+
+static int hi8435_readw(struct hi8435_priv *priv, u8 reg, u16 *val)
+{
+	int ret;
+
+	reg |= HI8435_READ_OPCODE;
+	ret = spi_write_then_read(priv->spi, &reg, 1, val, 2);
+	*val = be16_to_cpup(val);
+
+	return ret;
+}
+
+static int hi8435_readl(struct hi8435_priv *priv, u8 reg, u32 *val)
+{
+	int ret;
+
+	reg |= HI8435_READ_OPCODE;
+	ret = spi_write_then_read(priv->spi, &reg, 1, val, 4);
+	*val = be32_to_cpup(val);
+
+	return ret;
+}
+
+static int hi8435_writeb(struct hi8435_priv *priv, u8 reg, u8 val)
+{
+	priv->reg_buffer[0] = reg | HI8435_WRITE_OPCODE;
+	priv->reg_buffer[1] = val;
+
+	return spi_write(priv->spi, priv->reg_buffer, 2);
+}
+
+static int hi8435_writew(struct hi8435_priv *priv, u8 reg, u16 val)
+{
+	priv->reg_buffer[0] = reg | HI8435_WRITE_OPCODE;
+	priv->reg_buffer[1] = (val >> 8) & 0xff;
+	priv->reg_buffer[2] = val & 0xff;
+
+	return spi_write(priv->spi, priv->reg_buffer, 3);
+}
+
+static ssize_t hi8435_test_enable_show(struct device *dev,
+				       struct device_attribute *attr, char *buf)
+{
+	struct hi8435_priv *priv = iio_priv(dev_to_iio_dev(dev));
+	int ret;
+	u8 reg;
+
+	ret = hi8435_readb(priv, HI8435_CTRL_REG, &reg);
+	if (ret < 0)
+		return ret;
+
+	return sprintf(buf, "%d\n", reg & HI8435_CTRL_TEST);
+}
+
+static ssize_t hi8435_test_enable_store(struct device *dev,
+					struct device_attribute *attr,
+					const char *buf, size_t len)
+{
+	struct hi8435_priv *priv = iio_priv(dev_to_iio_dev(dev));
+	unsigned int val;
+	int ret;
+
+	ret = kstrtouint(buf, 10, &val);
+	if (ret)
+		return ret;
+
+	ret = hi8435_writeb(priv, HI8435_CTRL_REG, val ? HI8435_CTRL_TEST : 0);
+	if (ret < 0)
+		return ret;
+
+	return len;
+}
+
+static IIO_DEVICE_ATTR(test_enable, S_IRUGO | S_IWUSR,
+	hi8435_test_enable_show, hi8435_test_enable_store, 0);
+
+static struct attribute *hi8435_attributes[] = {
+	&iio_dev_attr_test_enable.dev_attr.attr,
+	NULL,
+};
+
+static struct attribute_group hi8435_attribute_group = {
+	.attrs = hi8435_attributes,
+};
+
+static int hi8435_read_raw(struct iio_dev *idev,
+			   const struct iio_chan_spec *chan,
+			   int *val, int *val2, long mask)
+{
+	struct hi8435_priv *priv = iio_priv(idev);
+	int ret;
+
+	switch (mask) {
+	case IIO_CHAN_INFO_RAW:
+		ret = hi8435_readl(priv, HI8435_SO31_0_REG, val);
+		if (ret < 0)
+			return ret;
+		*val = !!(*val & BIT(chan->channel));
+		return IIO_VAL_INT;
+	case IIO_CHAN_INFO_DEBOUNCE_TIME:
+		*val = priv->debounce_interval;
+		return IIO_VAL_INT;
+	default:
+		return -EINVAL;
+	}
+}
+
+static int hi8435_write_raw(struct iio_dev *idev,
+			    const struct iio_chan_spec *chan,
+			    int val, int val2, long mask)
+{
+	struct hi8435_priv *priv = iio_priv(idev);
+
+	switch (mask) {
+	case IIO_CHAN_INFO_RAW:
+		/* program sensors outputs in test mode */
+		return hi8435_writeb(priv, HI8435_TMDATA_REG, val ? 0x1 : 0x2);
+	case IIO_CHAN_INFO_DEBOUNCE_TIME:
+		if (val < 0)
+			return -EINVAL;
+		if (val > HI8435_DEBOUNCE_DELAY_MAX)
+			val = HI8435_DEBOUNCE_DELAY_MAX;
+		priv->debounce_interval = val;
+		return 0;
+	default:
+		return -EINVAL;
+	}
+}
+
+static int hi8435_read_event_config(struct iio_dev *idev,
+				    const struct iio_chan_spec *chan,
+				    enum iio_event_type type,
+				    enum iio_event_direction dir)
+{
+	struct hi8435_priv *priv = iio_priv(idev);
+
+	return !!(priv->event_scan_mask & BIT(chan->channel));
+}
+
+static int hi8435_write_event_config(struct iio_dev *idev,
+				     const struct iio_chan_spec *chan,
+				     enum iio_event_type type,
+				     enum iio_event_direction dir, int state)
+{
+	struct hi8435_priv *priv = iio_priv(idev);
+
+	priv->event_scan_mask &= ~BIT(chan->channel);
+	if (state)
+		priv->event_scan_mask |= BIT(chan->channel);
+
+	return 0;
+}
+
+static int hi8435_read_event_value(struct iio_dev *idev,
+				   const struct iio_chan_spec *chan,
+				   enum iio_event_type type,
+				   enum iio_event_direction dir,
+				   enum iio_event_info info,
+				   int *val, int *val2)
+{
+	struct hi8435_priv *priv = iio_priv(idev);
+	int ret;
+	u8 mode, psen;
+	u16 reg;
+
+	ret = hi8435_readb(priv, HI8435_PSEN_REG, &psen);
+	if (ret < 0)
+		return ret;
+
+	/* Supply-Open or GND-Open sensing mode */
+	mode = !!(psen & BIT(chan->channel / 8));
+
+	ret = hi8435_readw(priv, mode ? HI8435_SOCENHYS_REG :
+				 HI8435_GOCENHYS_REG, &reg);
+	if (ret < 0)
+		return ret;
+
+	if (dir == IIO_EV_DIR_FALLING)
+		*val = ((reg & 0xff) - (reg >> 8)) / 2;
+
+	if (dir == IIO_EV_DIR_RISING)
+		*val = ((reg & 0xff) + (reg >> 8)) / 2;
+
+	return IIO_VAL_INT;
+}
+
+static int hi8435_write_event_value(struct iio_dev *idev,
+				    const struct iio_chan_spec *chan,
+				    enum iio_event_type type,
+				    enum iio_event_direction dir,
+				    enum iio_event_info info,
+				    int val, int val2)
+{
+	struct hi8435_priv *priv = iio_priv(idev);
+	int ret;
+	u8 mode, psen;
+	u16 reg;
+
+	ret = hi8435_readb(priv, HI8435_PSEN_REG, &psen);
+	if (ret < 0)
+		return ret;
+
+	/* Supply-Open or GND-Open sensing mode */
+	mode = !!(psen & BIT(chan->channel / 8));
+
+	ret = hi8435_readw(priv, mode ? HI8435_SOCENHYS_REG :
+				 HI8435_GOCENHYS_REG, &reg);
+	if (ret < 0)
+		return ret;
+
+	if (dir == IIO_EV_DIR_FALLING) {
+		/* falling threshold range 2..21V, hysteresis minimum 2V */
+		if (val < 2 || val > 21 || (val + 1) >= priv->threshold_hi[mode])
+			return -EINVAL;
+
+		if (val == priv->threshold_lo[mode])
+			return 0;
+
+		priv->threshold_lo[mode] = val;
+
+		/* hysteresis must not be odd */
+		if ((priv->threshold_hi[mode] - priv->threshold_lo[mode]) % 2)
+			priv->threshold_hi[mode]--;
+	}
+
+	if (dir == IIO_EV_DIR_RISING) {
+		/* rising threshold range 3..22V, hysteresis minimum 2V */
+		if (val < 3 || val > 22 || val <= (priv->threshold_lo[mode] + 1))
+			return -EINVAL;
+
+		if (val == priv->threshold_hi[mode])
+			return 0;
+
+		priv->threshold_hi[mode] = val;
+
+		/* hysteresis must not be odd */
+		if ((priv->threshold_hi[mode] - priv->threshold_lo[mode]) % 2)
+			priv->threshold_lo[mode]++;
+	}
+
+	/* program thresholds */
+	mutex_lock(&priv->lock);
+
+	ret = hi8435_readw(priv, mode ? HI8435_SOCENHYS_REG :
+				 HI8435_GOCENHYS_REG, &reg);
+	if (ret < 0) {
+		mutex_unlock(&priv->lock);
+		return ret;
+	}
+
+	/* hysteresis */
+	reg = priv->threshold_hi[mode] - priv->threshold_lo[mode];
+	reg <<= 8;
+	/* threshold center */
+	reg |= (priv->threshold_hi[mode] + priv->threshold_lo[mode]);
+
+	ret = hi8435_writew(priv, mode ? HI8435_SOCENHYS_REG :
+				  HI8435_GOCENHYS_REG, reg);
+
+	mutex_unlock(&priv->lock);
+
+	return ret;
+}
+
+static const struct iio_event_spec hi8435_events[] = {
+	{
+		.type = IIO_EV_TYPE_THRESH,
+		.dir = IIO_EV_DIR_RISING,
+		.mask_separate = BIT(IIO_EV_INFO_VALUE),
+	}, {
+		.type = IIO_EV_TYPE_THRESH,
+		.dir = IIO_EV_DIR_FALLING,
+		.mask_separate = BIT(IIO_EV_INFO_VALUE),
+	}, {
+		.type = IIO_EV_TYPE_THRESH,
+		.dir = IIO_EV_DIR_EITHER,
+		.mask_separate = BIT(IIO_EV_INFO_ENABLE),
+	},
+};
+
+static int hi8435_get_sensing_mode(struct iio_dev *idev,
+				   const struct iio_chan_spec *chan)
+{
+	struct hi8435_priv *priv = iio_priv(idev);
+	int ret;
+	u8 reg;
+
+	ret = hi8435_readb(priv, HI8435_PSEN_REG, &reg);
+	if (ret < 0)
+		return ret;
+
+	return !!(reg & BIT(chan->channel / 8));
+}
+
+static int hi8435_set_sensing_mode(struct iio_dev *idev,
+				   const struct iio_chan_spec *chan,
+				   unsigned int mode)
+{
+	struct hi8435_priv *priv = iio_priv(idev);
+	int ret;
+	u8 reg;
+
+	mutex_lock(&priv->lock);
+
+	ret = hi8435_readb(priv, HI8435_PSEN_REG, &reg);
+	if (ret < 0) {
+		mutex_unlock(&priv->lock);
+		return ret;
+	}
+
+	reg &= ~BIT(chan->channel / 8);
+	if (mode)
+		reg |= BIT(chan->channel / 8);
+
+	ret = hi8435_writeb(priv, HI8435_PSEN_REG, reg);
+
+	mutex_unlock(&priv->lock);
+
+	return ret;
+}
+
+static const char * const hi8435_sensing_modes[] = { "GND-Open",
+						     "Supply-Open" };
+
+static const struct iio_enum hi8435_sensing_mode = {
+	.items = hi8435_sensing_modes,
+	.num_items = ARRAY_SIZE(hi8435_sensing_modes),
+	.get = hi8435_get_sensing_mode,
+	.set = hi8435_set_sensing_mode,
+};
+
+static const struct iio_chan_spec_ext_info hi8435_ext_info[] = {
+	IIO_ENUM("sensing_mode", IIO_SEPARATE, &hi8435_sensing_mode),
+	{},
+};
+
+#define HI8435_VOLTAGE_CHANNEL(num)					\
+{									\
+	.type = IIO_VOLTAGE,						\
+	.indexed = 1,							\
+	.channel = num,							\
+	.info_mask_separate = BIT(IIO_CHAN_INFO_RAW),			\
+	.info_mask_shared_by_all = BIT(IIO_CHAN_INFO_DEBOUNCE_TIME),	\
+	.event_spec = hi8435_events,					\
+	.num_event_specs = ARRAY_SIZE(hi8435_events),			\
+	.ext_info = hi8435_ext_info,					\
+	.extend_name = "comparator"					\
+}
+
+static const struct iio_chan_spec hi8435_channels[] = {
+	HI8435_VOLTAGE_CHANNEL(0),
+	HI8435_VOLTAGE_CHANNEL(1),
+	HI8435_VOLTAGE_CHANNEL(2),
+	HI8435_VOLTAGE_CHANNEL(3),
+	HI8435_VOLTAGE_CHANNEL(4),
+	HI8435_VOLTAGE_CHANNEL(5),
+	HI8435_VOLTAGE_CHANNEL(6),
+	HI8435_VOLTAGE_CHANNEL(7),
+	HI8435_VOLTAGE_CHANNEL(8),
+	HI8435_VOLTAGE_CHANNEL(9),
+	HI8435_VOLTAGE_CHANNEL(10),
+	HI8435_VOLTAGE_CHANNEL(11),
+	HI8435_VOLTAGE_CHANNEL(12),
+	HI8435_VOLTAGE_CHANNEL(13),
+	HI8435_VOLTAGE_CHANNEL(14),
+	HI8435_VOLTAGE_CHANNEL(15),
+	HI8435_VOLTAGE_CHANNEL(16),
+	HI8435_VOLTAGE_CHANNEL(17),
+	HI8435_VOLTAGE_CHANNEL(18),
+	HI8435_VOLTAGE_CHANNEL(19),
+	HI8435_VOLTAGE_CHANNEL(20),
+	HI8435_VOLTAGE_CHANNEL(21),
+	HI8435_VOLTAGE_CHANNEL(22),
+	HI8435_VOLTAGE_CHANNEL(23),
+	HI8435_VOLTAGE_CHANNEL(24),
+	HI8435_VOLTAGE_CHANNEL(25),
+	HI8435_VOLTAGE_CHANNEL(26),
+	HI8435_VOLTAGE_CHANNEL(27),
+	HI8435_VOLTAGE_CHANNEL(28),
+	HI8435_VOLTAGE_CHANNEL(29),
+	HI8435_VOLTAGE_CHANNEL(30),
+	HI8435_VOLTAGE_CHANNEL(31),
+	IIO_CHAN_SOFT_TIMESTAMP(32),
+};
+
+static const struct iio_info hi8435_info = {
+	.driver_module = THIS_MODULE,
+	.attrs = &hi8435_attribute_group,
+	.read_raw = hi8435_read_raw,
+	.write_raw = hi8435_write_raw,
+	.read_event_config = &hi8435_read_event_config,
+	.write_event_config = hi8435_write_event_config,
+	.read_event_value = &hi8435_read_event_value,
+	.write_event_value = &hi8435_write_event_value,
+};
+
+static void hi8435_iio_push_event(struct iio_dev *idev, unsigned int val)
+{
+	struct hi8435_priv *priv = iio_priv(idev);
+	enum iio_event_direction dir;
+	unsigned int i;
+	unsigned int status = priv->event_prev_val ^ val;
+
+	if (!status)
+		return;
+
+	for_each_set_bit(i, &priv->event_scan_mask, 32) {
+		if (!(status & BIT(i)))
+			continue;
+
+		dir = val & BIT(i) ? IIO_EV_DIR_RISING : IIO_EV_DIR_FALLING;
+
+		iio_push_event(idev,
+			       IIO_UNMOD_EVENT_CODE(IIO_VOLTAGE, i,
+						    IIO_EV_TYPE_THRESH, dir),
+			       iio_get_time_ns());
+	}
+
+	priv->event_prev_val = val;
+}
+
+static void hi8435_debounce_work(struct work_struct *work)
+{
+	struct hi8435_priv *priv = container_of(work, struct hi8435_priv,
+						work.work);
+	struct iio_dev *idev = spi_get_drvdata(priv->spi);
+	u32 val;
+	int ret;
+
+	ret = hi8435_readl(priv, HI8435_SO31_0_REG, &val);
+	if (ret < 0)
+		return;
+
+	if (val == priv->debounce_val)
+		hi8435_iio_push_event(idev, val);
+	else
+		dev_warn(&priv->spi->dev, "filtered by software debounce");
+}
+
+static irqreturn_t hi8435_trigger_handler(int irq, void *private)
+{
+	struct iio_poll_func *pf = private;
+	struct iio_dev *idev = pf->indio_dev;
+	struct hi8435_priv *priv = iio_priv(idev);
+	u32 val;
+	int ret;
+
+	ret = hi8435_readl(priv, HI8435_SO31_0_REG, &val);
+	if (ret < 0)
+		goto err_read;
+
+	if (priv->debounce_interval) {
+		priv->debounce_val = val;
+		schedule_delayed_work(&priv->work,
+				msecs_to_jiffies(priv->debounce_interval));
+	} else {
+		hi8435_iio_push_event(idev, val);
+	}
+
+err_read:
+	iio_trigger_notify_done(idev->trig);
+
+	return IRQ_HANDLED;
+}
+
+static void hi8435_parse_dt(struct hi8435_priv *priv)
+{
+	struct device_node *np = priv->spi->dev.of_node;
+	int ret;
+
+	ret = of_get_named_gpio(np, "holt,reset-gpios", 0);
+	priv->reset_gpio = ret < 0 ? 0 : ret;
+
+	ret = of_property_read_u32(np, "holt,debounce-interval",
+				   &priv->debounce_interval);
+	if (ret)
+		priv->debounce_interval = 0;
+	if (priv->debounce_interval > HI8435_DEBOUNCE_DELAY_MAX)
+		priv->debounce_interval = HI8435_DEBOUNCE_DELAY_MAX;
+}
+
+static int hi8435_probe(struct spi_device *spi)
+{
+	struct iio_dev *idev;
+	struct hi8435_priv *priv;
+	int ret;
+
+	idev = devm_iio_device_alloc(&spi->dev, sizeof(*priv));
+	if (!idev)
+		return -ENOMEM;
+
+	priv = iio_priv(idev);
+	priv->spi = spi;
+
+	if (spi->dev.of_node)
+		hi8435_parse_dt(priv);
+
+	spi_set_drvdata(spi, idev);
+	mutex_init(&priv->lock);
+	INIT_DELAYED_WORK(&priv->work, hi8435_debounce_work);
+
+	idev->dev.parent	= &spi->dev;
+	idev->name		= spi_get_device_id(spi)->name;
+	idev->modes		= INDIO_DIRECT_MODE;
+	idev->info		= &hi8435_info;
+	idev->channels		= hi8435_channels;
+	idev->num_channels	= ARRAY_SIZE(hi8435_channels);
+
+	if (priv->reset_gpio) {
+		ret = devm_gpio_request(&spi->dev, priv->reset_gpio, idev->name);
+		if (!ret) {
+			/* chip hardware reset */
+			gpio_direction_output(priv->reset_gpio, 0);
+			udelay(5);
+			gpio_direction_output(priv->reset_gpio, 1);
+		}
+	} else {
+		/* chip software reset */
+		hi8435_writeb(priv, HI8435_CTRL_REG, HI8435_CTRL_SRST);
+		/* get out from reset state */
+		hi8435_writeb(priv, HI8435_CTRL_REG, 0);
+	}
+
+	/* unmask all events */
+	priv->event_scan_mask = ~(0);
+	/* initialize default thresholds */
+	priv->threshold_lo[0] = priv->threshold_lo[1] = 2;
+	priv->threshold_hi[0] = priv->threshold_hi[1] = 4;
+	hi8435_writew(priv, HI8435_GOCENHYS_REG, 0x206);
+	hi8435_writew(priv, HI8435_SOCENHYS_REG, 0x206);
+
+	ret = iio_triggered_event_setup(idev, NULL, hi8435_trigger_handler);
+	if (ret)
+		return ret;
+
+	ret = iio_device_register(idev);
+	if (ret < 0) {
+		dev_err(&spi->dev, "unable to register device\n");
+		goto unregister_triggered_event;
+	}
+
+	return 0;
+
+unregister_triggered_event:
+	iio_triggered_event_cleanup(idev);
+	return ret;
+}
+
+static int hi8435_remove(struct spi_device *spi)
+{
+	struct iio_dev *idev = spi_get_drvdata(spi);
+	struct hi8435_priv *priv = iio_priv(idev);
+
+	cancel_delayed_work_sync(&priv->work);
+	iio_device_unregister(idev);
+	iio_triggered_event_cleanup(idev);
+
+	return 0;
+}
+
+static const struct of_device_id hi8435_dt_ids[] = {
+	{ .compatible = "holt,hi8435" },
+	{},
+};
+MODULE_DEVICE_TABLE(of, hi8435_dt_ids);
+
+static const struct spi_device_id hi8435_id[] = {
+	{ "hi8435", 0},
+	{ }
+};
+MODULE_DEVICE_TABLE(spi, hi8435_id);
+
+static struct spi_driver hi8435_driver = {
+	.driver	= {
+		.name		= DRV_NAME,
+		.of_match_table	= of_match_ptr(hi8435_dt_ids),
+	},
+	.probe		= hi8435_probe,
+	.remove		= hi8435_remove,
+	.id_table	= hi8435_id,
+};
+module_spi_driver(hi8435_driver);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Vladimir Barinov");
+MODULE_DESCRIPTION("HI-8435 threshold detector");
-- 
1.9.1

^ permalink raw reply related	[flat|nested] 40+ messages in thread

* [PATCH v3 2/7] dt: Add vendor prefix 'holt'
  2015-07-29 12:54 ` Vladimir Barinov
  (?)
  (?)
@ 2015-07-29 12:57 ` Vladimir Barinov
  -1 siblings, 0 replies; 40+ messages in thread
From: Vladimir Barinov @ 2015-07-29 12:57 UTC (permalink / raw)
  To: Jonathan Cameron
  Cc: Hartmut Knaack, Lars-Peter Clausen, Peter Meerwald, Rob Herring,
	Pawel Moll, Mark Rutland, Ian Campbell, Kumar Gala, linux-kernel,
	linux-iio, devicetree, cory.tusar

Add Holt Integrated Circuits, Inc. to the list of device tree vendor
prefixes

Signed-off-by: Vladimir Barinov <vladimir.barinov@cogentembedded.com>
---
Changes in version 2:
- none
Changes in version 3:
- none

 Documentation/devicetree/bindings/vendor-prefixes.txt | 1 +
 1 file changed, 1 insertion(+)

diff --git a/Documentation/devicetree/bindings/vendor-prefixes.txt b/Documentation/devicetree/bindings/vendor-prefixes.txt
index d444757..bc64cc9 100644
--- a/Documentation/devicetree/bindings/vendor-prefixes.txt
+++ b/Documentation/devicetree/bindings/vendor-prefixes.txt
@@ -99,6 +99,7 @@ himax	Himax Technologies, Inc.
 hisilicon	Hisilicon Limited.
 hit	Hitachi Ltd.
 hitex	Hitex Development Tools
+holt	Holt Integrated Circuits, Inc.
 honeywell	Honeywell
 hp	Hewlett Packard
 i2se	I2SE GmbH
-- 
1.9.1


^ permalink raw reply related	[flat|nested] 40+ messages in thread

* [PATCH v3 3/7] dt: Document Holt HI-8435 bindings
  2015-07-29 12:54 ` Vladimir Barinov
                   ` (2 preceding siblings ...)
  (?)
@ 2015-07-29 12:57 ` Vladimir Barinov
  -1 siblings, 0 replies; 40+ messages in thread
From: Vladimir Barinov @ 2015-07-29 12:57 UTC (permalink / raw)
  To: Jonathan Cameron
  Cc: Hartmut Knaack, Lars-Peter Clausen, Peter Meerwald, Rob Herring,
	Pawel Moll, Mark Rutland, Ian Campbell, Kumar Gala, linux-kernel,
	linux-iio, devicetree, cory.tusar

These bindings can be used to register Holt HI-8435 threshold detector

Signed-off-by: Vladimir Barinov <vladimir.barinov@cogentembedded.com>
---
Changes in version 2:
- renamed file name hi-843x.txt to hi8435.txt
- removed hi-8436,hi-8436,hi-8437
- removed holt,debounce-soft field
- renamed holt,debounc-soft-delay to holt,debounce-interval
- renamed mr-gpio to reset-gpios
Changes in version 3:
- none

 .../devicetree/bindings/iio/adc/hi8435.txt         | 25 ++++++++++++++++++++++
 1 file changed, 25 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/iio/adc/hi8435.txt

diff --git a/Documentation/devicetree/bindings/iio/adc/hi8435.txt b/Documentation/devicetree/bindings/iio/adc/hi8435.txt
new file mode 100644
index 0000000..1d33ad0
--- /dev/null
+++ b/Documentation/devicetree/bindings/iio/adc/hi8435.txt
@@ -0,0 +1,25 @@
+Holt Integrated Circuits HI-8435 threshold detector bindings
+
+Required properties:
+ - compatible: should be "holt,hi8435"
+ - reg: spi chip select number for the device
+
+Recommended properties:
+ - spi-max-frequency: definition as per
+		Documentation/devicetree/bindings/spi/spi-bus.txt
+
+Optional properties:
+ - holt,reset-gpios: GPIO used for controlling the reset pin
+ - holt,debounce-interval: software debounce interval in milliseconds
+
+Example:
+sensor@0 {
+	compatible = "holt,hi8435";
+	reg = <0>;
+
+	holt,reset-gpios = <&gpio6 1 0>;
+
+	holt,debounce-interval = <100>;
+
+	spi-max-frequency = <1000000>;
+};
-- 
1.9.1


^ permalink raw reply related	[flat|nested] 40+ messages in thread

* [PATCH v3 4/7] iio: trigger: Add periodic polling to SYSFS trigger
  2015-07-29 12:54 ` Vladimir Barinov
                   ` (3 preceding siblings ...)
  (?)
@ 2015-07-29 12:57 ` Vladimir Barinov
  2015-08-07 13:42     ` Lars-Peter Clausen
  -1 siblings, 1 reply; 40+ messages in thread
From: Vladimir Barinov @ 2015-07-29 12:57 UTC (permalink / raw)
  To: Jonathan Cameron
  Cc: Hartmut Knaack, Lars-Peter Clausen, Peter Meerwald, Rob Herring,
	Pawel Moll, Mark Rutland, Ian Campbell, Kumar Gala, linux-kernel,
	linux-iio, devicetree, cory.tusar

Add periodic polling functionality to SYSFS trigger

Signed-off-by: Vladimir Barinov <vladimir.barinov@cogentembedded.com>
---
Changes in version 2:
- initially added
Changes in version 3:
- fixed typos in ABI description

 .../ABI/testing/sysfs-bus-iio-trigger-sysfs        | 11 ++++
 drivers/iio/trigger/iio-trig-sysfs.c               | 58 ++++++++++++++++++++++
 2 files changed, 69 insertions(+)

diff --git a/Documentation/ABI/testing/sysfs-bus-iio-trigger-sysfs b/Documentation/ABI/testing/sysfs-bus-iio-trigger-sysfs
index 5235e6c..03c18eb 100644
--- a/Documentation/ABI/testing/sysfs-bus-iio-trigger-sysfs
+++ b/Documentation/ABI/testing/sysfs-bus-iio-trigger-sysfs
@@ -9,3 +9,14 @@ Description:
 		automated testing or in situations, where other trigger methods
 		are not applicable. For example no RTC or spare GPIOs.
 		X is the IIO index of the trigger.
+
+What:		/sys/bus/iio/devices/triggerX/trigger_poll
+KernelVersion:	4.2.0
+Contact:	linux-iio@vger.kernel.org
+Description:
+		This file is provided by the iio-trig-sysfs stand-alone trigger
+		driver. Writing this file with positive value (in milliseconds)
+		will start periodic event triggering of the driver, associated
+		with this trigger. Writing this file with 0 will stop periodic
+		triggering.
+		X is the IIO index of the trigger.
diff --git a/drivers/iio/trigger/iio-trig-sysfs.c b/drivers/iio/trigger/iio-trig-sysfs.c
index 3dfab2b..ea79311 100644
--- a/drivers/iio/trigger/iio-trig-sysfs.c
+++ b/drivers/iio/trigger/iio-trig-sysfs.c
@@ -18,6 +18,8 @@
 struct iio_sysfs_trig {
 	struct iio_trigger *trig;
 	struct irq_work work;
+	struct delayed_work poll_work;
+	unsigned int poll_interval; /* msec */
 	int id;
 	struct list_head l;
 };
@@ -110,10 +112,63 @@ static ssize_t iio_sysfs_trigger_poll(struct device *dev,
 	return count;
 }
 
+static void iio_sysfs_trigger_queue_poll_work(struct iio_sysfs_trig *trig)
+{
+	unsigned long delay;
+
+	delay = msecs_to_jiffies(trig->poll_interval);
+	if (delay >= HZ)
+		delay = round_jiffies_relative(delay);
+
+	queue_delayed_work(system_freezable_wq, &trig->poll_work, delay);
+}
+
+static void iio_sysfs_trigger_poll_work(struct work_struct *work)
+{
+	struct iio_sysfs_trig *trig = container_of(work, struct iio_sysfs_trig,
+						   poll_work.work);
+
+	irq_work_queue(&trig->work);
+	iio_sysfs_trigger_queue_poll_work(trig);
+}
+
+static ssize_t iio_sysfs_trigger_get_poll(struct device *dev,
+		struct device_attribute *attr, char *buf)
+{
+	struct iio_trigger *trig = to_iio_trigger(dev);
+	struct iio_sysfs_trig *sysfs_trig = iio_trigger_get_drvdata(trig);
+
+	return sprintf(buf, "%d\n", sysfs_trig->poll_interval);
+}
+
+static ssize_t iio_sysfs_trigger_set_poll(struct device *dev,
+		struct device_attribute *attr, const char *buf, size_t count)
+{
+	struct iio_trigger *trig = to_iio_trigger(dev);
+	struct iio_sysfs_trig *sysfs_trig = iio_trigger_get_drvdata(trig);
+	unsigned int interval;
+	int err;
+
+	err = kstrtouint(buf, 0, &interval);
+	if (err)
+		return err;
+
+	sysfs_trig->poll_interval = interval;
+
+	cancel_delayed_work_sync(&sysfs_trig->poll_work);
+	if (sysfs_trig->poll_interval > 0)
+		iio_sysfs_trigger_queue_poll_work(sysfs_trig);
+
+	return count;
+}
+
 static DEVICE_ATTR(trigger_now, S_IWUSR, NULL, iio_sysfs_trigger_poll);
+static DEVICE_ATTR(trigger_poll, S_IRUGO | S_IWUSR, iio_sysfs_trigger_get_poll,
+						    iio_sysfs_trigger_set_poll);
 
 static struct attribute *iio_sysfs_trigger_attrs[] = {
 	&dev_attr_trigger_now.attr,
+	&dev_attr_trigger_poll.attr,
 	NULL,
 };
 
@@ -164,6 +219,7 @@ static int iio_sysfs_trigger_probe(int id)
 	iio_trigger_set_drvdata(t->trig, t);
 
 	init_irq_work(&t->work, iio_sysfs_trigger_work);
+	INIT_DELAYED_WORK(&t->poll_work, iio_sysfs_trigger_poll_work);
 
 	ret = iio_trigger_register(t->trig);
 	if (ret)
@@ -198,6 +254,8 @@ static int iio_sysfs_trigger_remove(int id)
 		return -EINVAL;
 	}
 
+	cancel_delayed_work_sync(&t->poll_work);
+
 	iio_trigger_unregister(t->trig);
 	iio_trigger_free(t->trig);
 
-- 
1.9.1


^ permalink raw reply related	[flat|nested] 40+ messages in thread

* [PATCH v3 5/7] iio: Support triggered events
  2015-07-29 12:54 ` Vladimir Barinov
                   ` (4 preceding siblings ...)
  (?)
@ 2015-07-29 12:57 ` Vladimir Barinov
  2015-08-07 13:45     ` Lars-Peter Clausen
  2015-08-16  9:20     ` Jonathan Cameron
  -1 siblings, 2 replies; 40+ messages in thread
From: Vladimir Barinov @ 2015-07-29 12:57 UTC (permalink / raw)
  To: Jonathan Cameron
  Cc: Hartmut Knaack, Lars-Peter Clausen, Peter Meerwald, Rob Herring,
	Pawel Moll, Mark Rutland, Ian Campbell, Kumar Gala, linux-kernel,
	linux-iio, devicetree, cory.tusar

Support triggered events.

This is useful for chips that don't have their own interrupt sources.
It allows to use generic/standalone iio triggers for those drivers.

Signed-off-by: Vladimir Barinov <vladimir.barinov@cogentembedded.com>
---
Changes in version 2:
- initially added
Changes in version 3:
- fixed grammar in patch description
- changed license header to match "GNU Public License v2 or later"

 drivers/iio/Kconfig                        |  6 +++
 drivers/iio/Makefile                       |  1 +
 drivers/iio/industrialio-core.c            |  4 +-
 drivers/iio/industrialio-trigger.c         | 12 +++++-
 drivers/iio/industrialio-triggered-event.c | 68 ++++++++++++++++++++++++++++++
 include/linux/iio/iio.h                    |  1 +
 include/linux/iio/triggered_event.h        | 11 +++++
 7 files changed, 99 insertions(+), 4 deletions(-)
 create mode 100644 drivers/iio/industrialio-triggered-event.c
 create mode 100644 include/linux/iio/triggered_event.h

diff --git a/drivers/iio/Kconfig b/drivers/iio/Kconfig
index 4011eff..8fcc92f 100644
--- a/drivers/iio/Kconfig
+++ b/drivers/iio/Kconfig
@@ -58,6 +58,12 @@ config IIO_CONSUMERS_PER_TRIGGER
 	This value controls the maximum number of consumers that a
 	given trigger may handle. Default is 2.
 
+config IIO_TRIGGERED_EVENT
+	tristate
+	select IIO_TRIGGER
+	help
+	  Provides helper functions for setting up triggered events.
+
 source "drivers/iio/accel/Kconfig"
 source "drivers/iio/adc/Kconfig"
 source "drivers/iio/amplifiers/Kconfig"
diff --git a/drivers/iio/Makefile b/drivers/iio/Makefile
index 698afc2..40dc13e 100644
--- a/drivers/iio/Makefile
+++ b/drivers/iio/Makefile
@@ -9,6 +9,7 @@ industrialio-$(CONFIG_IIO_TRIGGER) += industrialio-trigger.o
 industrialio-$(CONFIG_IIO_BUFFER_CB) += buffer_cb.o
 
 obj-$(CONFIG_IIO_TRIGGERED_BUFFER) += industrialio-triggered-buffer.o
+obj-$(CONFIG_IIO_TRIGGERED_EVENT) += industrialio-triggered-event.o
 obj-$(CONFIG_IIO_KFIFO_BUF) += kfifo_buf.o
 
 obj-y += accel/
diff --git a/drivers/iio/industrialio-core.c b/drivers/iio/industrialio-core.c
index 3524b0d..54d71ea 100644
--- a/drivers/iio/industrialio-core.c
+++ b/drivers/iio/industrialio-core.c
@@ -948,7 +948,7 @@ static void iio_device_unregister_sysfs(struct iio_dev *indio_dev)
 static void iio_dev_release(struct device *device)
 {
 	struct iio_dev *indio_dev = dev_to_iio_dev(device);
-	if (indio_dev->modes & INDIO_BUFFER_TRIGGERED)
+	if (indio_dev->modes & (INDIO_BUFFER_TRIGGERED | INDIO_EVENT_TRIGGERED))
 		iio_device_unregister_trigger_consumer(indio_dev);
 	iio_device_unregister_eventset(indio_dev);
 	iio_device_unregister_sysfs(indio_dev);
@@ -1218,7 +1218,7 @@ int iio_device_register(struct iio_dev *indio_dev)
 			"Failed to register event set\n");
 		goto error_free_sysfs;
 	}
-	if (indio_dev->modes & INDIO_BUFFER_TRIGGERED)
+	if (indio_dev->modes & (INDIO_BUFFER_TRIGGERED | INDIO_EVENT_TRIGGERED))
 		iio_device_register_trigger_consumer(indio_dev);
 
 	if ((indio_dev->modes & INDIO_ALL_BUFFER_MODES) &&
diff --git a/drivers/iio/industrialio-trigger.c b/drivers/iio/industrialio-trigger.c
index d31098e..72b63e7 100644
--- a/drivers/iio/industrialio-trigger.c
+++ b/drivers/iio/industrialio-trigger.c
@@ -345,10 +345,18 @@ static ssize_t iio_trigger_write_current(struct device *dev,
 
 	indio_dev->trig = trig;
 
-	if (oldtrig)
+	if (oldtrig) {
+		if (indio_dev->currentmode == INDIO_EVENT_TRIGGERED)
+			iio_trigger_detach_poll_func(oldtrig,
+						     indio_dev->pollfunc);
 		iio_trigger_put(oldtrig);
-	if (indio_dev->trig)
+	}
+	if (indio_dev->trig) {
 		iio_trigger_get(indio_dev->trig);
+		if (indio_dev->currentmode == INDIO_EVENT_TRIGGERED)
+			iio_trigger_attach_poll_func(indio_dev->trig,
+						     indio_dev->pollfunc);
+	}
 
 	return len;
 }
diff --git a/drivers/iio/industrialio-triggered-event.c b/drivers/iio/industrialio-triggered-event.c
new file mode 100644
index 0000000..1e5ad33
--- /dev/null
+++ b/drivers/iio/industrialio-triggered-event.c
@@ -0,0 +1,68 @@
+ /*
+ * Copyright (C) 2015 Cogent Embedded, Inc.
+ *
+ * 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.
+ */
+
+#include <linux/kernel.h>
+#include <linux/export.h>
+#include <linux/module.h>
+#include <linux/iio/iio.h>
+#include <linux/iio/triggered_event.h>
+#include <linux/iio/trigger_consumer.h>
+
+/**
+ * iio_triggered_event_setup() - Setup pollfunc for triggered event
+ * @indio_dev:		IIO device structure
+ * @pollfunc_bh:	Function which will be used as pollfunc bottom half
+ * @pollfunc_th:	Function which will be used as pollfunc top half
+ *
+ * This function combines some common tasks which will normally be performed
+ * when setting up a triggered event. It will allocate the pollfunc and
+ * set mode to use it for triggered event.
+ *
+ * Before calling this function the indio_dev structure should already be
+ * completely initialized, but not yet registered. In practice this means that
+ * this function should be called right before iio_device_register().
+ *
+ * To free the resources allocated by this function call
+ * iio_triggered_event_cleanup().
+ */
+int iio_triggered_event_setup(struct iio_dev *indio_dev,
+	irqreturn_t (*pollfunc_bh)(int irq, void *p),
+	irqreturn_t (*pollfunc_th)(int irq, void *p))
+{
+	indio_dev->pollfunc = iio_alloc_pollfunc(pollfunc_bh,
+						 pollfunc_th,
+						 IRQF_ONESHOT,
+						 indio_dev,
+						 "%s_consumer%d",
+						 indio_dev->name,
+						 indio_dev->id);
+	if (indio_dev->pollfunc == NULL)
+		return -ENOMEM;
+
+	/* Flag that pollfunc is used for triggered event */
+	indio_dev->modes |= INDIO_EVENT_TRIGGERED;
+	indio_dev->currentmode = INDIO_EVENT_TRIGGERED;
+
+	return 0;
+}
+EXPORT_SYMBOL(iio_triggered_event_setup);
+
+/**
+ * iio_triggered_event_cleanup() - Free resources allocated by iio_triggered_event_setup()
+ * @indio_dev: IIO device structure
+ */
+void iio_triggered_event_cleanup(struct iio_dev *indio_dev)
+{
+	iio_dealloc_pollfunc(indio_dev->pollfunc);
+}
+EXPORT_SYMBOL(iio_triggered_event_cleanup);
+
+MODULE_AUTHOR("Vladimir Barinov");
+MODULE_DESCRIPTION("IIO helper functions for setting up triggered events");
+MODULE_LICENSE("GPL");
diff --git a/include/linux/iio/iio.h b/include/linux/iio/iio.h
index f791482..b691ee0 100644
--- a/include/linux/iio/iio.h
+++ b/include/linux/iio/iio.h
@@ -294,6 +294,7 @@ static inline s64 iio_get_time_ns(void)
 #define INDIO_BUFFER_TRIGGERED		0x02
 #define INDIO_BUFFER_SOFTWARE		0x04
 #define INDIO_BUFFER_HARDWARE		0x08
+#define INDIO_EVENT_TRIGGERED		0x10
 
 #define INDIO_ALL_BUFFER_MODES					\
 	(INDIO_BUFFER_TRIGGERED | INDIO_BUFFER_HARDWARE | INDIO_BUFFER_SOFTWARE)
diff --git a/include/linux/iio/triggered_event.h b/include/linux/iio/triggered_event.h
new file mode 100644
index 0000000..e9894e9
--- /dev/null
+++ b/include/linux/iio/triggered_event.h
@@ -0,0 +1,11 @@
+#ifndef _LINUX_IIO_TRIGGERED_EVENT_H_
+#define _LINUX_IIO_TRIGGERED_EVENT_H_
+
+#include <linux/interrupt.h>
+
+int iio_triggered_event_setup(struct iio_dev *indio_dev,
+	irqreturn_t (*pollfunc_bh)(int irq, void *p),
+	irqreturn_t (*pollfunc_th)(int irq, void *p));
+void iio_triggered_event_cleanup(struct iio_dev *indio_dev);
+
+#endif
-- 
1.9.1


^ permalink raw reply related	[flat|nested] 40+ messages in thread

* [PATCH v3 6/7] iio: Add ABI documentation for debounce_time
  2015-07-29 12:54 ` Vladimir Barinov
                   ` (5 preceding siblings ...)
  (?)
@ 2015-07-29 12:57 ` Vladimir Barinov
  -1 siblings, 0 replies; 40+ messages in thread
From: Vladimir Barinov @ 2015-07-29 12:57 UTC (permalink / raw)
  To: Jonathan Cameron
  Cc: Hartmut Knaack, Lars-Peter Clausen, Peter Meerwald, Rob Herring,
	Pawel Moll, Mark Rutland, Ian Campbell, Kumar Gala, linux-kernel,
	linux-iio, devicetree, cory.tusar

Add ABI documentation for debounce_time

Signed-off-by: Vladimir Barinov <vladimir.barinov@cogentembedded.com>
---
Changes in version 3:
- initially added

 Documentation/ABI/testing/sysfs-bus-iio | 9 +++++++++
 1 file changed, 9 insertions(+)

diff --git a/Documentation/ABI/testing/sysfs-bus-iio b/Documentation/ABI/testing/sysfs-bus-iio
index 09eac44..65bf49e 100644
--- a/Documentation/ABI/testing/sysfs-bus-iio
+++ b/Documentation/ABI/testing/sysfs-bus-iio
@@ -539,6 +539,15 @@ Description:
 		specified and unique if the output corresponds to a single
 		channel.
 
+What:		/sys/bus/iio/devices/iio:deviceX/debounce_time
+Date:		July 2015
+KernelVersion:	4.2.0
+Contact:	linux-iio@vger.kernel.org
+Description:
+		Specifies number of milliseconds in which we perform hardware or
+		software debouncing. If value is set to 0 then debouncing is
+		disabled.
+
 What:		/sys/bus/iio/devices/iio:deviceX/events
 KernelVersion:	2.6.35
 Contact:	linux-iio@vger.kernel.org
-- 
1.9.1


^ permalink raw reply related	[flat|nested] 40+ messages in thread

* [PATCH v3 7/7] iio: Fix typos in ABI documentation
  2015-07-29 12:54 ` Vladimir Barinov
                   ` (6 preceding siblings ...)
  (?)
@ 2015-07-29 12:57 ` Vladimir Barinov
  2015-08-08 18:39     ` Jonathan Cameron
  -1 siblings, 1 reply; 40+ messages in thread
From: Vladimir Barinov @ 2015-07-29 12:57 UTC (permalink / raw)
  To: Jonathan Cameron
  Cc: Hartmut Knaack, Lars-Peter Clausen, Peter Meerwald, Rob Herring,
	Pawel Moll, Mark Rutland, Ian Campbell, Kumar Gala, linux-kernel,
	linux-iio, devicetree, cory.tusar

Fix typos in ABI documentation

Signed-off-by: Vladimir Barinov <vladimir.barinov@cogentembedded.com>
---
Changes in version 3:
- initially added

 Documentation/ABI/testing/sysfs-bus-iio | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/Documentation/ABI/testing/sysfs-bus-iio b/Documentation/ABI/testing/sysfs-bus-iio
index 65bf49e..71a48b7 100644
--- a/Documentation/ABI/testing/sysfs-bus-iio
+++ b/Documentation/ABI/testing/sysfs-bus-iio
@@ -488,7 +488,7 @@ Contact:	linux-iio@vger.kernel.org
 Description:
 		Specifies the output powerdown mode.
 		DAC output stage is disconnected from the amplifier and
-		1kohm_to_gnd: connected	to ground via an 1kOhm resistor,
+		1kohm_to_gnd: connected to ground via an 1kOhm resistor,
 		6kohm_to_gnd: connected to ground via a 6kOhm resistor,
 		20kohm_to_gnd: connected to ground via a 20kOhm resistor,
 		100kohm_to_gnd: connected to ground via an 100kOhm resistor,
@@ -498,9 +498,9 @@ Description:
 		outX_powerdown_mode_available. If Y is not present the
 		mode is shared across all outputs.
 
-What:		/sys/.../iio:deviceX/out_votlageY_powerdown_mode_available
+What:		/sys/.../iio:deviceX/out_voltageY_powerdown_mode_available
 What:		/sys/.../iio:deviceX/out_voltage_powerdown_mode_available
-What:		/sys/.../iio:deviceX/out_altvotlageY_powerdown_mode_available
+What:		/sys/.../iio:deviceX/out_altvoltageY_powerdown_mode_available
 What:		/sys/.../iio:deviceX/out_altvoltage_powerdown_mode_available
 KernelVersion:	2.6.38
 Contact:	linux-iio@vger.kernel.org
-- 
1.9.1


^ permalink raw reply related	[flat|nested] 40+ messages in thread

* Re: [PATCH v3 4/7] iio: trigger: Add periodic polling to SYSFS trigger
@ 2015-08-07 13:42     ` Lars-Peter Clausen
  0 siblings, 0 replies; 40+ messages in thread
From: Lars-Peter Clausen @ 2015-08-07 13:42 UTC (permalink / raw)
  To: Vladimir Barinov, Jonathan Cameron
  Cc: Hartmut Knaack, Peter Meerwald, Rob Herring, Pawel Moll,
	Mark Rutland, Ian Campbell, Kumar Gala, linux-kernel, linux-iio,
	devicetree, cory.tusar, Daniel Baluta

On 07/29/2015 02:57 PM, Vladimir Barinov wrote:
> Add periodic polling functionality to SYSFS trigger

Daniel's hrtimer trigger should be used for this instead of modifying the
sysfs trigger timer. See https://lkml.org/lkml/2015/5/8/367

He said he'll send out a new version shortly.

- Lars


^ permalink raw reply	[flat|nested] 40+ messages in thread

* Re: [PATCH v3 4/7] iio: trigger: Add periodic polling to SYSFS trigger
@ 2015-08-07 13:42     ` Lars-Peter Clausen
  0 siblings, 0 replies; 40+ messages in thread
From: Lars-Peter Clausen @ 2015-08-07 13:42 UTC (permalink / raw)
  To: Vladimir Barinov, Jonathan Cameron
  Cc: Hartmut Knaack, Peter Meerwald, Rob Herring, Pawel Moll,
	Mark Rutland, Ian Campbell, Kumar Gala,
	linux-kernel-u79uwXL29TY76Z2rM5mHXA,
	linux-iio-u79uwXL29TY76Z2rM5mHXA,
	devicetree-u79uwXL29TY76Z2rM5mHXA,
	cory.tusar-J6Z/VSE8EyIAspv4Qr0y0gC/G2K4zDHf, Daniel Baluta

On 07/29/2015 02:57 PM, Vladimir Barinov wrote:
> Add periodic polling functionality to SYSFS trigger

Daniel's hrtimer trigger should be used for this instead of modifying the
sysfs trigger timer. See https://lkml.org/lkml/2015/5/8/367

He said he'll send out a new version shortly.

- Lars

^ permalink raw reply	[flat|nested] 40+ messages in thread

* Re: [PATCH v3 5/7] iio: Support triggered events
@ 2015-08-07 13:45     ` Lars-Peter Clausen
  0 siblings, 0 replies; 40+ messages in thread
From: Lars-Peter Clausen @ 2015-08-07 13:45 UTC (permalink / raw)
  To: Vladimir Barinov, Jonathan Cameron
  Cc: Hartmut Knaack, Peter Meerwald, Rob Herring, Pawel Moll,
	Mark Rutland, Ian Campbell, Kumar Gala, linux-kernel, linux-iio,
	devicetree, cory.tusar

Hi,

On 07/29/2015 02:57 PM, Vladimir Barinov wrote:
> Support triggered events.
> 
> This is useful for chips that don't have their own interrupt sources.
> It allows to use generic/standalone iio triggers for those drivers.
> 

Thanks for the patch. Can you describe in more detail how
INDIO_EVENT_TRIGGERED differs from the INDIO_BUFFER_TRIGGERED. By just
looking at the source code I don't understand quite why it is necessary.

Thanks,
- Lars


^ permalink raw reply	[flat|nested] 40+ messages in thread

* Re: [PATCH v3 5/7] iio: Support triggered events
@ 2015-08-07 13:45     ` Lars-Peter Clausen
  0 siblings, 0 replies; 40+ messages in thread
From: Lars-Peter Clausen @ 2015-08-07 13:45 UTC (permalink / raw)
  To: Vladimir Barinov, Jonathan Cameron
  Cc: Hartmut Knaack, Peter Meerwald, Rob Herring, Pawel Moll,
	Mark Rutland, Ian Campbell, Kumar Gala,
	linux-kernel-u79uwXL29TY76Z2rM5mHXA,
	linux-iio-u79uwXL29TY76Z2rM5mHXA,
	devicetree-u79uwXL29TY76Z2rM5mHXA,
	cory.tusar-J6Z/VSE8EyIAspv4Qr0y0gC/G2K4zDHf

Hi,

On 07/29/2015 02:57 PM, Vladimir Barinov wrote:
> Support triggered events.
> 
> This is useful for chips that don't have their own interrupt sources.
> It allows to use generic/standalone iio triggers for those drivers.
> 

Thanks for the patch. Can you describe in more detail how
INDIO_EVENT_TRIGGERED differs from the INDIO_BUFFER_TRIGGERED. By just
looking at the source code I don't understand quite why it is necessary.

Thanks,
- Lars

^ permalink raw reply	[flat|nested] 40+ messages in thread

* Re: [PATCH v3 5/7] iio: Support triggered events
  2015-08-07 13:45     ` Lars-Peter Clausen
  (?)
@ 2015-08-07 16:10     ` Vladimir Barinov
  2015-08-16  9:05         ` Jonathan Cameron
  -1 siblings, 1 reply; 40+ messages in thread
From: Vladimir Barinov @ 2015-08-07 16:10 UTC (permalink / raw)
  To: Lars-Peter Clausen, Jonathan Cameron
  Cc: Hartmut Knaack, Peter Meerwald, Rob Herring, Pawel Moll,
	Mark Rutland, Ian Campbell, Kumar Gala, linux-kernel, linux-iio,
	devicetree, cory.tusar

Hi Lars,

On 07.08.2015 16:45, Lars-Peter Clausen wrote:
> Hi,
>
> On 07/29/2015 02:57 PM, Vladimir Barinov wrote:
>> Support triggered events.
>>
>> This is useful for chips that don't have their own interrupt sources.
>> It allows to use generic/standalone iio triggers for those drivers.
>>
> Thanks for the patch. Can you describe in more detail how
> INDIO_EVENT_TRIGGERED differs from the INDIO_BUFFER_TRIGGERED. By just
> looking at the source code I don't understand quite why it is necessary.
This is needed for the case when iio buffer is not used and used only 
iio event.

The difference to INDIO_BUFFER_TRIGGERED is that for triggered buffer 
the trigger poll function is attached (using 
iio_trigger_attach_poll_func) only in
industrialio-buffer.c using number of steps/conditions starting by 
issuing 1 to buffer/enable sysfs path.

For chips that do not use iio buffer but use only iio events the 
iio_trigger_attach_poll_func never called.

In case of using INDIO_BUFFER_TRIGGERED the poll func is 
attached/detached via industrialio-buffer.c
In case of using INDIO_EVENT_TRIGGERED the poll func is 
attached/detached via industrialio-trigger.c during set/change the 
current trigger.

Regards,
Vladimir

>
> Thanks,
> - Lars
>


^ permalink raw reply	[flat|nested] 40+ messages in thread

* Re: [PATCH v3 4/7] iio: trigger: Add periodic polling to SYSFS trigger
  2015-08-07 13:42     ` Lars-Peter Clausen
  (?)
@ 2015-08-08 17:26     ` Jonathan Cameron
  -1 siblings, 0 replies; 40+ messages in thread
From: Jonathan Cameron @ 2015-08-08 17:26 UTC (permalink / raw)
  To: Lars-Peter Clausen, Vladimir Barinov
  Cc: Hartmut Knaack, Peter Meerwald, Rob Herring, Pawel Moll,
	Mark Rutland, Ian Campbell, Kumar Gala, linux-kernel, linux-iio,
	devicetree, cory.tusar, Daniel Baluta

On 07/08/15 14:42, Lars-Peter Clausen wrote:
> On 07/29/2015 02:57 PM, Vladimir Barinov wrote:
>> Add periodic polling functionality to SYSFS trigger
> 
> Daniel's hrtimer trigger should be used for this instead of modifying the
> sysfs trigger timer. See https://lkml.org/lkml/2015/5/8/367
> 
> He said he'll send out a new version shortly.
Agreed.  I'd like to keep these two trigger types separate as they
conceptually are quite different things.

Jonathan


^ permalink raw reply	[flat|nested] 40+ messages in thread

* Re: [PATCH v3 0/7] iio: adc: hi8435: Add Holt HI-8435 threshold detector
@ 2015-08-08 17:28   ` Jonathan Cameron
  0 siblings, 0 replies; 40+ messages in thread
From: Jonathan Cameron @ 2015-08-08 17:28 UTC (permalink / raw)
  To: Vladimir Barinov
  Cc: Hartmut Knaack, Lars-Peter Clausen, Peter Meerwald, Rob Herring,
	Pawel Moll, Mark Rutland, Ian Campbell, Kumar Gala, linux-kernel,
	linux-iio, devicetree, cory.tusar

On 29/07/15 13:54, Vladimir Barinov wrote:
> Hello,
> 
> This adds the folowing:
> - Holt threshold detector driver for HI-8435 chip
> - Add Holt vendor prefix
> - Document HI-8435 DT bindings
> - Add periodic polling functionality to SYSFS trigger
> - Support triggered events
> - Add ABI documentation for debounce_time
> - Fix typos in sysfs-bus-iio file
> 
> PDF file can be found here:
> http://www.holtic.com/products/3081-hi-8435.aspx
> 
> Vladimir Barinov (7):
> [1/7] iio: adc: hi8435: Holt HI-8435 threshold detector
> [2/7] dt: Add vendor prefix 'holt'
> [3/7] dt: Document Holt HI-8435 bindings
> [4/7] iio: trigger: Add periodic polling to SYSFS trigger
> [5/7] iio: Support triggered events
> [6/7] iio: Add ABI documentation for debounce_time
> [7/7] iio: Fix typos in ABI documentation
Please order your patch set so that it may be bisected.
That is, if we apply each patch in the order presented, there
should never be a point at which we cannot build the code.

The driver in patch 1 uses elements from later in the series.

Jonathan
> 
> ---
> This patchset is against the 'kernel/git/torvalds/linux.git' repo.
> 
>  Documentation/ABI/testing/sysfs-bus-iio               |   16 
>  Documentation/ABI/testing/sysfs-bus-iio-adc-hi8435    |   61 +
>  Documentation/ABI/testing/sysfs-bus-iio-trigger-sysfs |   11 
>  Documentation/devicetree/bindings/iio/adc/hi8435.txt  |   25 
>  Documentation/devicetree/bindings/vendor-prefixes.txt |    1 
>  drivers/iio/Kconfig                                   |    6 
>  drivers/iio/Makefile                                  |    1 
>  drivers/iio/adc/Kconfig                               |   11 
>  drivers/iio/adc/Makefile                              |    1 
>  drivers/iio/adc/hi8435.c                              |  659 ++++++++++++++++++
>  drivers/iio/industrialio-core.c                       |    4 
>  drivers/iio/industrialio-trigger.c                    |   12 
>  drivers/iio/industrialio-triggered-event.c            |   68 +
>  drivers/iio/trigger/iio-trig-sysfs.c                  |   58 +
>  include/linux/iio/iio.h                               |    1 
>  include/linux/iio/triggered_event.h                   |   11 
>  16 files changed, 939 insertions(+), 7 deletions(-)
>  create mode 100644 Documentation/ABI/testing/sysfs-bus-iio-adc-hi8435
>  create mode 100644 Documentation/devicetree/bindings/iio/adc/hi8435.txt
>  create mode 100644 drivers/iio/adc/hi8435.c
>  create mode 100644 drivers/iio/industrialio-triggered-event.c
>  create mode 100644 include/linux/iio/triggered_event.h
> --
> To unsubscribe from this list: send the line "unsubscribe linux-iio" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html
> 


^ permalink raw reply	[flat|nested] 40+ messages in thread

* Re: [PATCH v3 0/7] iio: adc: hi8435: Add Holt HI-8435 threshold detector
@ 2015-08-08 17:28   ` Jonathan Cameron
  0 siblings, 0 replies; 40+ messages in thread
From: Jonathan Cameron @ 2015-08-08 17:28 UTC (permalink / raw)
  To: Vladimir Barinov
  Cc: Hartmut Knaack, Lars-Peter Clausen, Peter Meerwald, Rob Herring,
	Pawel Moll, Mark Rutland, Ian Campbell, Kumar Gala,
	linux-kernel-u79uwXL29TY76Z2rM5mHXA,
	linux-iio-u79uwXL29TY76Z2rM5mHXA,
	devicetree-u79uwXL29TY76Z2rM5mHXA,
	cory.tusar-J6Z/VSE8EyIAspv4Qr0y0gC/G2K4zDHf

On 29/07/15 13:54, Vladimir Barinov wrote:
> Hello,
> 
> This adds the folowing:
> - Holt threshold detector driver for HI-8435 chip
> - Add Holt vendor prefix
> - Document HI-8435 DT bindings
> - Add periodic polling functionality to SYSFS trigger
> - Support triggered events
> - Add ABI documentation for debounce_time
> - Fix typos in sysfs-bus-iio file
> 
> PDF file can be found here:
> http://www.holtic.com/products/3081-hi-8435.aspx
> 
> Vladimir Barinov (7):
> [1/7] iio: adc: hi8435: Holt HI-8435 threshold detector
> [2/7] dt: Add vendor prefix 'holt'
> [3/7] dt: Document Holt HI-8435 bindings
> [4/7] iio: trigger: Add periodic polling to SYSFS trigger
> [5/7] iio: Support triggered events
> [6/7] iio: Add ABI documentation for debounce_time
> [7/7] iio: Fix typos in ABI documentation
Please order your patch set so that it may be bisected.
That is, if we apply each patch in the order presented, there
should never be a point at which we cannot build the code.

The driver in patch 1 uses elements from later in the series.

Jonathan
> 
> ---
> This patchset is against the 'kernel/git/torvalds/linux.git' repo.
> 
>  Documentation/ABI/testing/sysfs-bus-iio               |   16 
>  Documentation/ABI/testing/sysfs-bus-iio-adc-hi8435    |   61 +
>  Documentation/ABI/testing/sysfs-bus-iio-trigger-sysfs |   11 
>  Documentation/devicetree/bindings/iio/adc/hi8435.txt  |   25 
>  Documentation/devicetree/bindings/vendor-prefixes.txt |    1 
>  drivers/iio/Kconfig                                   |    6 
>  drivers/iio/Makefile                                  |    1 
>  drivers/iio/adc/Kconfig                               |   11 
>  drivers/iio/adc/Makefile                              |    1 
>  drivers/iio/adc/hi8435.c                              |  659 ++++++++++++++++++
>  drivers/iio/industrialio-core.c                       |    4 
>  drivers/iio/industrialio-trigger.c                    |   12 
>  drivers/iio/industrialio-triggered-event.c            |   68 +
>  drivers/iio/trigger/iio-trig-sysfs.c                  |   58 +
>  include/linux/iio/iio.h                               |    1 
>  include/linux/iio/triggered_event.h                   |   11 
>  16 files changed, 939 insertions(+), 7 deletions(-)
>  create mode 100644 Documentation/ABI/testing/sysfs-bus-iio-adc-hi8435
>  create mode 100644 Documentation/devicetree/bindings/iio/adc/hi8435.txt
>  create mode 100644 drivers/iio/adc/hi8435.c
>  create mode 100644 drivers/iio/industrialio-triggered-event.c
>  create mode 100644 include/linux/iio/triggered_event.h
> --
> To unsubscribe from this list: send the line "unsubscribe linux-iio" in
> the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html
> 

^ permalink raw reply	[flat|nested] 40+ messages in thread

* Re: [PATCH v3 1/7] iio: adc: hi8435: Holt HI-8435 threshold detector
@ 2015-08-08 17:56     ` Jonathan Cameron
  0 siblings, 0 replies; 40+ messages in thread
From: Jonathan Cameron @ 2015-08-08 17:56 UTC (permalink / raw)
  To: Vladimir Barinov
  Cc: Hartmut Knaack, Lars-Peter Clausen, Peter Meerwald, Rob Herring,
	Pawel Moll, Mark Rutland, Ian Campbell, Kumar Gala, linux-kernel,
	linux-iio, devicetree, cory.tusar

On 29/07/15 13:56, Vladimir Barinov wrote:
> Add Holt threshold detector driver for HI-8435 chip
> 
> Signed-off-by: Vladimir Barinov <vladimir.barinov@cogentembedded.com>
Because it usually reads better that way, I review from the bottom.
Might make more sense if you read the comments that way around ;)

I'm going to guess that the typical usecase for this device is in realtime
systems where polling gives a nice predictable timing (hence the lack of any
interrupt support).  These typically operate as 'scanning' devices
(e.g. you update all state at approximately the same time, by reading
one input after another in some predefined order).

Does the use of events actually map well to this use case?  You are taking
something that (other than the results being a bit different) is basically
a digital I/O interface and mapping it (sort of) to an interrupt chip
when it isn't nothing of the sort.

I'd like more opinions on this, but my gut reaction is that we would
be better making the normal buffered infrastructure handle this data
properly rather than pushing it through the events intrastructure.

Your solution is neat and tidy in someways but feels like it is mashing
data into a particular format.

To add to the complexity we could have debounce logic.  If we mapped to a
fill the buffer with a scan mode, how would we handle this?
My guess is that it would simply delay transistions.  Perhaps we even
support reading both the value right now and the debounced value if that
is possible?

However, here the debounce is all software.  To my mind, move this into
userspace entirely. We aren't dealing with big data here so it's hardly
going to be particularly challenging.

So my gut feeling is definitely we need to make the buffer infrastructure
handle 1 bit values (in groups) then push this data out that way.

Still I would like other opinions on this!

Jonathan

> ---
> Changes in version 2:
> - Added file sysfs-bus-iio-adc-hi8435
> - Changed naming from "discrete ADC" to "threshold detector"
> - Replaced swab16p/swab32p with be16_to_cpup/be32_to_cpup
> - Made *_show and *_store functions static
> - moved out from iio buffers to iio events
> - removed hi8436/hi8437 chips from the driver
> - moved from debounce_soft_delay/enable to debounce_interval via
>   IIO_CHAN_INFO_DEBOUNCE_TIME
> - added name extention "comparator"
> - moved threshold/hysteresis setup via generic iio event sysfs
> - added software mask/unmask channel events
> - added programming sensor outputs while in test mode via
>   IIO_CHAN_INFO_RAW
> - added channels .ext_info for programming sensing mode
> Changes in version 3:
> - fixed typos
> - moved <linux/interrupt.h> to match alphabetic order
> - fixed missed */event/* prefix in the ABI description
> - added missed colon in sysfs-bus-iio-adc-hi8435 file after "What"
> - moved in_voltageY_comparator_thresh_either_en and debounce_time
>   to sysfs-bus-iio file
> - added error checking for hi8435_write[b|w]
> 
>  Documentation/ABI/testing/sysfs-bus-iio            |   1 +
>  Documentation/ABI/testing/sysfs-bus-iio-adc-hi8435 |  61 ++
>  drivers/iio/adc/Kconfig                            |  11 +
>  drivers/iio/adc/Makefile                           |   1 +
>  drivers/iio/adc/hi8435.c                           | 659 +++++++++++++++++++++
>  5 files changed, 742 insertions(+)
>  create mode 100644 Documentation/ABI/testing/sysfs-bus-iio-adc-hi8435
>  create mode 100644 drivers/iio/adc/hi8435.c
> 
> diff --git a/Documentation/ABI/testing/sysfs-bus-iio b/Documentation/ABI/testing/sysfs-bus-iio
> index 70c9b1a..09eac44 100644
> --- a/Documentation/ABI/testing/sysfs-bus-iio
> +++ b/Documentation/ABI/testing/sysfs-bus-iio
> @@ -572,6 +572,7 @@ What:		/sys/.../iio:deviceX/events/in_rot_from_north_magnetic_tilt_comp_thresh_r
>  What:		/sys/.../iio:deviceX/events/in_rot_from_north_magnetic_tilt_comp_thresh_falling_en
>  What:		/sys/.../iio:deviceX/events/in_rot_from_north_true_tilt_comp_thresh_rising_en
>  What:		/sys/.../iio:deviceX/events/in_rot_from_north_true_tilt_comp_thresh_falling_en
> +What:		/sys/.../iio:deviceX/events/in_voltageY_comparator_thresh_either_en
>  What:		/sys/.../iio:deviceX/events/in_voltageY_supply_thresh_rising_en
>  What:		/sys/.../iio:deviceX/events/in_voltageY_supply_thresh_falling_en
>  What:		/sys/.../iio:deviceX/events/in_voltageY_thresh_rising_en
> diff --git a/Documentation/ABI/testing/sysfs-bus-iio-adc-hi8435 b/Documentation/ABI/testing/sysfs-bus-iio-adc-hi8435
> new file mode 100644
> index 0000000..c59d81d
> --- /dev/null
> +++ b/Documentation/ABI/testing/sysfs-bus-iio-adc-hi8435
> @@ -0,0 +1,61 @@
> +What:		/sys/bus/iio/devices/iio:deviceX/in_voltageY_comparator_raw
> +Date:		July 2015
> +KernelVersion:	4.2.0
> +Contact:	source@cogentembedded.com
> +Description:
> +		Read value is a voltage threshold measurement from channel Y.
> +		Could be either 0 if sensor voltage is lower then low voltage
> +		threshold or 1 if sensor voltage is higher then high voltage
> +		threshold.
> +		Write value is a programmed sensor output while in self test
> +		mode. Could be either 0 or 1. The programmed value will be read
> +		back if /sys/bus/iio/devices/iio:deviceX/test_enable is set to 1.
> +
> +What:		/sys/bus/iio/devices/iio:deviceX/test_enable
> +Date:		July 2015
> +KernelVersion:	4.2.0
> +Contact:	source@cogentembedded.com
> +Description:
> +		Enable/disable the HI-8435 self test mode.
> +		If enabled the in_voltageY_comparator_raw should be read back
> +		accordingly to written value to in_voltageY_comparator_raw.
> +
> +What:		/sys/bus/iio/devices/iio:deviceX/in_voltageY_comparator_sensing_mode
> +Date:		July 2015
> +KernelVersion:	4.2.0
> +Contact:	source@cogentembedded.com
> +Description:
> +		Program sensor type for threshold detector inputs.
> +		Could be either "GND-Open" or "Supply-Open" mode. Y is a
> +		threshold detector input channel. Channels 0..7, 8..15, 16..23
> +		and 24..31 has common sensor types.
> +
> +What:		/sys/bus/iio/devices/iio:deviceX/events/in_voltageY_comparator_thresh_falling_value
> +Date:		July 2015
> +KernelVersion:	4.2.0
> +Contact:	source@cogentembedded.com
> +Description:
> +		Channel Y low voltage threshold. If sensor input voltage goes lower then
> +		this value then the threshold falling event is pushed.
> +		Depending on in_voltageY_comparator_sensing_mode the low voltage threshold
> +		is separately set for "GND-Open" and "Supply-Open" modes.
> +		Channels 0..31 have common low threshold values, but could have different
> +		sensing_modes.
> +		The low voltage threshold range is between 2..21V.
> +		Hysteresis between low and high thresholds can not be lower then 2 and
> +		can not be odd.
> +
> +What:		/sys/bus/iio/devices/iio:deviceX/events/in_voltageY_comparator_thresh_rising_value
> +Date:		July 2015
> +KernelVersion:	4.2.0
> +Contact:	source@cogentembedded.com
> +Description:
> +		Channel Y high voltage threshold. If sensor input voltage goes higher then
> +		this value then the threshold rising event is pushed.
> +		Depending on in_voltageY_comparator_sensing_mode the high voltage threshold
> +		is separately set for "GND-Open" and "Supply-Open" modes.
> +		Channels 0..31 have common high threshold values, but could have different
> +		sensing_modes.
> +		The high voltage threshold range is between 3..22V.
> +		Hysteresis between low and high thresholds can not be lower then 2 and
> +		can not be odd.
> diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig
> index eb0cd89..553c91e 100644
> --- a/drivers/iio/adc/Kconfig
> +++ b/drivers/iio/adc/Kconfig
> @@ -170,6 +170,17 @@ config EXYNOS_ADC
>  	  of SoCs for drivers such as the touchscreen and hwmon to use to share
>  	  this resource.
>  
> +config HI8435
> +	tristate "Holt Integrated Circuits HI-8435 threshold detector"
> +	select IIO_TRIGGERED_EVENT
> +	depends on SPI
> +	help
> +	  If you say yes here you get support for Holt Integrated Circuits
> +	  HI-8435 chip.
> +
> +	  This driver can also be built as a module. If so, the module will be
> +	  called hi8435.
> +
>  config LP8788_ADC
>  	tristate "LP8788 ADC driver"
>  	depends on MFD_LP8788
> diff --git a/drivers/iio/adc/Makefile b/drivers/iio/adc/Makefile
> index a096210..00f367aa 100644
> --- a/drivers/iio/adc/Makefile
> +++ b/drivers/iio/adc/Makefile
> @@ -19,6 +19,7 @@ obj-$(CONFIG_BERLIN2_ADC) += berlin2-adc.o
>  obj-$(CONFIG_DA9150_GPADC) += da9150-gpadc.o
>  obj-$(CONFIG_CC10001_ADC) += cc10001_adc.o
>  obj-$(CONFIG_EXYNOS_ADC) += exynos_adc.o
> +obj-$(CONFIG_HI8435) += hi8435.o
>  obj-$(CONFIG_LP8788_ADC) += lp8788_adc.o
>  obj-$(CONFIG_MAX1027) += max1027.o
>  obj-$(CONFIG_MAX1363) += max1363.o
> diff --git a/drivers/iio/adc/hi8435.c b/drivers/iio/adc/hi8435.c
> new file mode 100644
> index 0000000..f3dab5a
> --- /dev/null
> +++ b/drivers/iio/adc/hi8435.c
> @@ -0,0 +1,659 @@
> +/*
> + * Holt Integrated Circuits HI-8435 threshold detector driver
> + *
> + * Copyright (C) 2015 Zodiac Inflight Innovations
> + * Copyright (C) 2015 Cogent Embedded, Inc.
> + *
> + * 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.
> + */
> +
> +#include <linux/delay.h>
> +#include <linux/iio/events.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/triggered_event.h>
> +#include <linux/interrupt.h>
> +#include <linux/module.h>
> +#include <linux/of.h>
> +#include <linux/of_device.h>
> +#include <linux/of_gpio.h>
> +#include <linux/spi/spi.h>
> +#include <linux/workqueue.h>
> +
> +#define DRV_NAME "hi8435"
> +
> +/* Register offsets for HI-8435 */
> +#define HI8435_CTRL_REG			0x02
> +#define HI8435_PSEN_REG			0x04
> +#define HI8435_TMDATA_REG		0x1E
> +#define HI8435_GOCENHYS_REG		0x3A
> +#define HI8435_SOCENHYS_REG		0x3C
Hmm. These aren't in the docs that I can immediately acquire...
Any chance of some more meaningful naming?
> +#define HI8435_SO7_0_REG		0x10
> +#define HI8435_SO15_8_REG		0x12
 +#define HI8435_SO23_16_REG		0x14
> +#define HI8435_SO31_24_REG		0x16
> +#define HI8435_SO31_0_REG		0x78
> +
> +#define HI8435_WRITE_OPCODE		0x00
> +#define HI8435_READ_OPCODE		0x80
> +
> +/* CTRL register bits */
> +#define HI8435_CTRL_TEST		0x01
> +#define HI8435_CTRL_SRST		0x02
> +
> +#define HI8435_DEBOUNCE_DELAY_MAX	1000	/* msec */
> +#define HI8435_DEBOUNCE_DELAY_DEF	100	/* msec */
> +
> +struct hi8435_priv {
> +	struct spi_device *spi;
> +	struct mutex lock;
> +	struct delayed_work work;
> +
> +	int reset_gpio;
> +	int debounce_interval; /* msec */
> +	u32 debounce_val; /* prev value to compare during software debounce */
> +
> +	unsigned long event_scan_mask; /* soft mask/unmask channels events */
> +	unsigned int event_prev_val;
> +
> +	unsigned threshold_lo[2]; /* GND-Open and Supply-Open thresholds */
> +	unsigned threshold_hi[2]; /* GND-Open and Supply-Open threshold */
> +	u8 reg_buffer[4] ____cacheline_aligned;
> +};
> +
> +static int hi8435_readb(struct hi8435_priv *priv, u8 reg, u8 *val)
> +{
> +	reg |= HI8435_READ_OPCODE;
> +	return spi_write_then_read(priv->spi, &reg, 1, val, 1);
> +}
> +
> +static int hi8435_readw(struct hi8435_priv *priv, u8 reg, u16 *val)
> +{
> +	int ret;
> +
> +	reg |= HI8435_READ_OPCODE;
> +	ret = spi_write_then_read(priv->spi, &reg, 1, val, 2);
> +	*val = be16_to_cpup(val);
> +
> +	return ret;
> +}
> +
> +static int hi8435_readl(struct hi8435_priv *priv, u8 reg, u32 *val)
> +{
> +	int ret;
> +
> +	reg |= HI8435_READ_OPCODE;
> +	ret = spi_write_then_read(priv->spi, &reg, 1, val, 4);
> +	*val = be32_to_cpup(val);
> +
> +	return ret;
> +}
> +
> +static int hi8435_writeb(struct hi8435_priv *priv, u8 reg, u8 val)
> +{
> +	priv->reg_buffer[0] = reg | HI8435_WRITE_OPCODE;
> +	priv->reg_buffer[1] = val;
> +
> +	return spi_write(priv->spi, priv->reg_buffer, 2);
> +}
> +
> +static int hi8435_writew(struct hi8435_priv *priv, u8 reg, u16 val)
> +{
> +	priv->reg_buffer[0] = reg | HI8435_WRITE_OPCODE;
> +	priv->reg_buffer[1] = (val >> 8) & 0xff;
> +	priv->reg_buffer[2] = val & 0xff;
> +
> +	return spi_write(priv->spi, priv->reg_buffer, 3);
> +}
> +
> +static ssize_t hi8435_test_enable_show(struct device *dev,
> +				       struct device_attribute *attr, char *buf)
> +{
> +	struct hi8435_priv *priv = iio_priv(dev_to_iio_dev(dev));
> +	int ret;
> +	u8 reg;
> +
> +	ret = hi8435_readb(priv, HI8435_CTRL_REG, &reg);
> +	if (ret < 0)
> +		return ret;
> +
> +	return sprintf(buf, "%d\n", reg & HI8435_CTRL_TEST);
> +}
> +
> +static ssize_t hi8435_test_enable_store(struct device *dev,
> +					struct device_attribute *attr,
> +					const char *buf, size_t len)
> +{
> +	struct hi8435_priv *priv = iio_priv(dev_to_iio_dev(dev));
> +	unsigned int val;
> +	int ret;
> +
> +	ret = kstrtouint(buf, 10, &val);
> +	if (ret)
> +		return ret;
> +
> +	ret = hi8435_writeb(priv, HI8435_CTRL_REG, val ? HI8435_CTRL_TEST : 0);
> +	if (ret < 0)
> +		return ret;
> +
> +	return len;
> +}
> +
> +static IIO_DEVICE_ATTR(test_enable, S_IRUGO | S_IWUSR,
> +	hi8435_test_enable_show, hi8435_test_enable_store, 0);
> +
> +static struct attribute *hi8435_attributes[] = {
> +	&iio_dev_attr_test_enable.dev_attr.attr,
> +	NULL,
> +};
> +
> +static struct attribute_group hi8435_attribute_group = {
> +	.attrs = hi8435_attributes,
> +};
> +
> +static int hi8435_read_raw(struct iio_dev *idev,
> +			   const struct iio_chan_spec *chan,
> +			   int *val, int *val2, long mask)
> +{
> +	struct hi8435_priv *priv = iio_priv(idev);
> +	int ret;
> +
> +	switch (mask) {
> +	case IIO_CHAN_INFO_RAW:
> +		ret = hi8435_readl(priv, HI8435_SO31_0_REG, val);
> +		if (ret < 0)
> +			return ret;
> +		*val = !!(*val & BIT(chan->channel));
> +		return IIO_VAL_INT;
> +	case IIO_CHAN_INFO_DEBOUNCE_TIME:
> +		*val = priv->debounce_interval;
> +		return IIO_VAL_INT;
> +	default:
> +		return -EINVAL;
> +	}
> +}
> +
> +static int hi8435_write_raw(struct iio_dev *idev,
> +			    const struct iio_chan_spec *chan,
> +			    int val, int val2, long mask)
> +{
> +	struct hi8435_priv *priv = iio_priv(idev);
> +
> +	switch (mask) {
> +	case IIO_CHAN_INFO_RAW:
> +		/* program sensors outputs in test mode */
> +		return hi8435_writeb(priv, HI8435_TMDATA_REG, val ? 0x1 : 0x2);
> +	case IIO_CHAN_INFO_DEBOUNCE_TIME:
> +		if (val < 0)
> +			return -EINVAL;
> +		if (val > HI8435_DEBOUNCE_DELAY_MAX)
> +			val = HI8435_DEBOUNCE_DELAY_MAX;
> +		priv->debounce_interval = val;
> +		return 0;
> +	default:
> +		return -EINVAL;
> +	}
> +}
> +
> +static int hi8435_read_event_config(struct iio_dev *idev,
> +				    const struct iio_chan_spec *chan,
> +				    enum iio_event_type type,
> +				    enum iio_event_direction dir)
> +{
> +	struct hi8435_priv *priv = iio_priv(idev);
> +
> +	return !!(priv->event_scan_mask & BIT(chan->channel));
> +}
> +
> +static int hi8435_write_event_config(struct iio_dev *idev,
> +				     const struct iio_chan_spec *chan,
> +				     enum iio_event_type type,
> +				     enum iio_event_direction dir, int state)
> +{
> +	struct hi8435_priv *priv = iio_priv(idev);
> +
> +	priv->event_scan_mask &= ~BIT(chan->channel);
> +	if (state)
> +		priv->event_scan_mask |= BIT(chan->channel);
> +
> +	return 0;
> +}
> +
> +static int hi8435_read_event_value(struct iio_dev *idev,
> +				   const struct iio_chan_spec *chan,
> +				   enum iio_event_type type,
> +				   enum iio_event_direction dir,
> +				   enum iio_event_info info,
> +				   int *val, int *val2)
> +{
> +	struct hi8435_priv *priv = iio_priv(idev);
> +	int ret;
> +	u8 mode, psen;
> +	u16 reg;
> +
> +	ret = hi8435_readb(priv, HI8435_PSEN_REG, &psen);
> +	if (ret < 0)
> +		return ret;
> +
> +	/* Supply-Open or GND-Open sensing mode */
> +	mode = !!(psen & BIT(chan->channel / 8));
> +
> +	ret = hi8435_readw(priv, mode ? HI8435_SOCENHYS_REG :
> +				 HI8435_GOCENHYS_REG, &reg);
> +	if (ret < 0)
> +		return ret;
> +
> +	if (dir == IIO_EV_DIR_FALLING)
> +		*val = ((reg & 0xff) - (reg >> 8)) / 2;
> +
> +	if (dir == IIO_EV_DIR_RISING)
> +		*val = ((reg & 0xff) + (reg >> 8)) / 2;
> +
> +	return IIO_VAL_INT;
> +}
> +
> +static int hi8435_write_event_value(struct iio_dev *idev,
> +				    const struct iio_chan_spec *chan,
> +				    enum iio_event_type type,
> +				    enum iio_event_direction dir,
> +				    enum iio_event_info info,
> +				    int val, int val2)
> +{
> +	struct hi8435_priv *priv = iio_priv(idev);
> +	int ret;
> +	u8 mode, psen;
> +	u16 reg;
> +
> +	ret = hi8435_readb(priv, HI8435_PSEN_REG, &psen);
> +	if (ret < 0)
> +		return ret;
> +
> +	/* Supply-Open or GND-Open sensing mode */
> +	mode = !!(psen & BIT(chan->channel / 8));
> +
> +	ret = hi8435_readw(priv, mode ? HI8435_SOCENHYS_REG :
> +				 HI8435_GOCENHYS_REG, &reg);
> +	if (ret < 0)
> +		return ret;
> +
> +	if (dir == IIO_EV_DIR_FALLING) {
> +		/* falling threshold range 2..21V, hysteresis minimum 2V */
> +		if (val < 2 || val > 21 || (val + 1) >= priv->threshold_hi[mode])
> +			return -EINVAL;
> +
> +		if (val == priv->threshold_lo[mode])
> +			return 0;
> +
> +		priv->threshold_lo[mode] = val;
> +
> +		/* hysteresis must not be odd */
> +		if ((priv->threshold_hi[mode] - priv->threshold_lo[mode]) % 2)
> +			priv->threshold_hi[mode]--;
> +	}
> +
> +	if (dir == IIO_EV_DIR_RISING) {
> +		/* rising threshold range 3..22V, hysteresis minimum 2V */
> +		if (val < 3 || val > 22 || val <= (priv->threshold_lo[mode] + 1))
> +			return -EINVAL;
> +
> +		if (val == priv->threshold_hi[mode])
> +			return 0;
> +
> +		priv->threshold_hi[mode] = val;
> +
> +		/* hysteresis must not be odd */
> +		if ((priv->threshold_hi[mode] - priv->threshold_lo[mode]) % 2)
> +			priv->threshold_lo[mode]++;
> +	}
> +
> +	/* program thresholds */
> +	mutex_lock(&priv->lock);
> +
> +	ret = hi8435_readw(priv, mode ? HI8435_SOCENHYS_REG :
> +				 HI8435_GOCENHYS_REG, &reg);
> +	if (ret < 0) {
> +		mutex_unlock(&priv->lock);
> +		return ret;
> +	}
> +
> +	/* hysteresis */
> +	reg = priv->threshold_hi[mode] - priv->threshold_lo[mode];
> +	reg <<= 8;
> +	/* threshold center */
> +	reg |= (priv->threshold_hi[mode] + priv->threshold_lo[mode]);
> +
> +	ret = hi8435_writew(priv, mode ? HI8435_SOCENHYS_REG :
> +				  HI8435_GOCENHYS_REG, reg);
> +
> +	mutex_unlock(&priv->lock);
> +
> +	return ret;
> +}
> +
> +static const struct iio_event_spec hi8435_events[] = {
> +	{
> +		.type = IIO_EV_TYPE_THRESH,
> +		.dir = IIO_EV_DIR_RISING,
> +		.mask_separate = BIT(IIO_EV_INFO_VALUE),
> +	}, {
> +		.type = IIO_EV_TYPE_THRESH,
> +		.dir = IIO_EV_DIR_FALLING,
> +		.mask_separate = BIT(IIO_EV_INFO_VALUE),
> +	}, {
> +		.type = IIO_EV_TYPE_THRESH,
> +		.dir = IIO_EV_DIR_EITHER,
> +		.mask_separate = BIT(IIO_EV_INFO_ENABLE),
> +	},
> +};
> +
> +static int hi8435_get_sensing_mode(struct iio_dev *idev,
> +				   const struct iio_chan_spec *chan)
> +{
> +	struct hi8435_priv *priv = iio_priv(idev);
> +	int ret;
> +	u8 reg;
> +
> +	ret = hi8435_readb(priv, HI8435_PSEN_REG, &reg);
> +	if (ret < 0)
> +		return ret;
> +
> +	return !!(reg & BIT(chan->channel / 8));
> +}
> +
> +static int hi8435_set_sensing_mode(struct iio_dev *idev,
> +				   const struct iio_chan_spec *chan,
> +				   unsigned int mode)
> +{
> +	struct hi8435_priv *priv = iio_priv(idev);
> +	int ret;
> +	u8 reg;
> +
> +	mutex_lock(&priv->lock);
> +
> +	ret = hi8435_readb(priv, HI8435_PSEN_REG, &reg);
> +	if (ret < 0) {
> +		mutex_unlock(&priv->lock);
> +		return ret;
> +	}
> +
> +	reg &= ~BIT(chan->channel / 8);
> +	if (mode)
> +		reg |= BIT(chan->channel / 8);
> +
> +	ret = hi8435_writeb(priv, HI8435_PSEN_REG, reg);
> +
> +	mutex_unlock(&priv->lock);
> +
> +	return ret;
> +}
> +
> +static const char * const hi8435_sensing_modes[] = { "GND-Open",
> +						     "Supply-Open" };
> +
> +static const struct iio_enum hi8435_sensing_mode = {
> +	.items = hi8435_sensing_modes,
> +	.num_items = ARRAY_SIZE(hi8435_sensing_modes),
> +	.get = hi8435_get_sensing_mode,
> +	.set = hi8435_set_sensing_mode,
> +};
> +
> +static const struct iio_chan_spec_ext_info hi8435_ext_info[] = {
> +	IIO_ENUM("sensing_mode", IIO_SEPARATE, &hi8435_sensing_mode),
> +	{},
> +};
> +
> +#define HI8435_VOLTAGE_CHANNEL(num)					\
> +{									\
> +	.type = IIO_VOLTAGE,						\
> +	.indexed = 1,							\
> +	.channel = num,							\
> +	.info_mask_separate = BIT(IIO_CHAN_INFO_RAW),			\
> +	.info_mask_shared_by_all = BIT(IIO_CHAN_INFO_DEBOUNCE_TIME),	\
> +	.event_spec = hi8435_events,					\
> +	.num_event_specs = ARRAY_SIZE(hi8435_events),			\
> +	.ext_info = hi8435_ext_info,					\
> +	.extend_name = "comparator"					\
I'm really not sure about this approach.  Can see where you are coming
from. We already have an events interface supporting these things so
why not use it?   I'm just doubtful it is an efficient option or a clean
interface (would like more opinions on this!)
> +}
> +
> +static const struct iio_chan_spec hi8435_channels[] = {
> +	HI8435_VOLTAGE_CHANNEL(0),
> +	HI8435_VOLTAGE_CHANNEL(1),
> +	HI8435_VOLTAGE_CHANNEL(2),
> +	HI8435_VOLTAGE_CHANNEL(3),
> +	HI8435_VOLTAGE_CHANNEL(4),
> +	HI8435_VOLTAGE_CHANNEL(5),
> +	HI8435_VOLTAGE_CHANNEL(6),
> +	HI8435_VOLTAGE_CHANNEL(7),
> +	HI8435_VOLTAGE_CHANNEL(8),
> +	HI8435_VOLTAGE_CHANNEL(9),
> +	HI8435_VOLTAGE_CHANNEL(10),
> +	HI8435_VOLTAGE_CHANNEL(11),
> +	HI8435_VOLTAGE_CHANNEL(12),
> +	HI8435_VOLTAGE_CHANNEL(13),
> +	HI8435_VOLTAGE_CHANNEL(14),
> +	HI8435_VOLTAGE_CHANNEL(15),
> +	HI8435_VOLTAGE_CHANNEL(16),
> +	HI8435_VOLTAGE_CHANNEL(17),
> +	HI8435_VOLTAGE_CHANNEL(18),
> +	HI8435_VOLTAGE_CHANNEL(19),
> +	HI8435_VOLTAGE_CHANNEL(20),
> +	HI8435_VOLTAGE_CHANNEL(21),
> +	HI8435_VOLTAGE_CHANNEL(22),
> +	HI8435_VOLTAGE_CHANNEL(23),
> +	HI8435_VOLTAGE_CHANNEL(24),
> +	HI8435_VOLTAGE_CHANNEL(25),
> +	HI8435_VOLTAGE_CHANNEL(26),
> +	HI8435_VOLTAGE_CHANNEL(27),
> +	HI8435_VOLTAGE_CHANNEL(28),
> +	HI8435_VOLTAGE_CHANNEL(29),
> +	HI8435_VOLTAGE_CHANNEL(30),
> +	HI8435_VOLTAGE_CHANNEL(31),
> +	IIO_CHAN_SOFT_TIMESTAMP(32),
> +};
> +
> +static const struct iio_info hi8435_info = {
> +	.driver_module = THIS_MODULE,
> +	.attrs = &hi8435_attribute_group,
> +	.read_raw = hi8435_read_raw,
> +	.write_raw = hi8435_write_raw,
> +	.read_event_config = &hi8435_read_event_config,
> +	.write_event_config = hi8435_write_event_config,
> +	.read_event_value = &hi8435_read_event_value,
> +	.write_event_value = &hi8435_write_event_value,
> +};
> +
> +static void hi8435_iio_push_event(struct iio_dev *idev, unsigned int val)
> +{
> +	struct hi8435_priv *priv = iio_priv(idev);
> +	enum iio_event_direction dir;
> +	unsigned int i;
> +	unsigned int status = priv->event_prev_val ^ val;
> +
> +	if (!status)
> +		return;
> +
> +	for_each_set_bit(i, &priv->event_scan_mask, 32) {
> +		if (!(status & BIT(i)))
> +			continue;
> +
> +		dir = val & BIT(i) ? IIO_EV_DIR_RISING : IIO_EV_DIR_FALLING;
> +
> +		iio_push_event(idev,
> +			       IIO_UNMOD_EVENT_CODE(IIO_VOLTAGE, i,
> +						    IIO_EV_TYPE_THRESH, dir),
> +			       iio_get_time_ns());
> +	}
> +
> +	priv->event_prev_val = val;
> +}
> +
> +static void hi8435_debounce_work(struct work_struct *work)
> +{
> +	struct hi8435_priv *priv = container_of(work, struct hi8435_priv,
> +						work.work);
> +	struct iio_dev *idev = spi_get_drvdata(priv->spi);
> +	u32 val;
> +	int ret;
> +
> +	ret = hi8435_readl(priv, HI8435_SO31_0_REG, &val);
> +	if (ret < 0)
> +		return;
> +
> +	if (val == priv->debounce_val)
> +		hi8435_iio_push_event(idev, val);
> +	else
> +		dev_warn(&priv->spi->dev, "filtered by software debounce");
Definitely not.  Warning every time a standard event occurs?  Not
a good idea.  Use the debug stuff if you want a way of knowing this
happened, then it will silent under normal use.

> +}
> +
> +static irqreturn_t hi8435_trigger_handler(int irq, void *private)
> +{
> +	struct iio_poll_func *pf = private;
> +	struct iio_dev *idev = pf->indio_dev;
> +	struct hi8435_priv *priv = iio_priv(idev);
> +	u32 val;
> +	int ret;
> +
> +	ret = hi8435_readl(priv, HI8435_SO31_0_REG, &val);
> +	if (ret < 0)
> +		goto err_read;
> +
> +	if (priv->debounce_interval) {
> +		priv->debounce_val = val;
> +		schedule_delayed_work(&priv->work,
> +				msecs_to_jiffies(priv->debounce_interval));
This is another element that makes me doubt that the event interface
is the way to do this.  I'd much rather we pushed the debouncing out
to userspace where cleverer things can be done (and more adaptive things).
Here to keep the event flow low you have to do it in the kernel.

> +	} else {
> +		hi8435_iio_push_event(idev, val);
> +	}
> +
> +err_read:
> +	iio_trigger_notify_done(idev->trig);
> +
> +	return IRQ_HANDLED;
> +}
> +
> +static void hi8435_parse_dt(struct hi8435_priv *priv)
> +{
> +	struct device_node *np = priv->spi->dev.of_node;
> +	int ret;
> +
> +	ret = of_get_named_gpio(np, "holt,reset-gpios", 0);
> +	priv->reset_gpio = ret < 0 ? 0 : ret;
Silly question, but can gpio0 exist on a board?  I suspect you
may need an additional variable to hold that this request failed
rather than using the magic value of 0.
> +
> +	ret = of_property_read_u32(np, "holt,debounce-interval",
> +				   &priv->debounce_interval);
> +	if (ret)
> +		priv->debounce_interval = 0;
> +	if (priv->debounce_interval > HI8435_DEBOUNCE_DELAY_MAX)
> +		priv->debounce_interval = HI8435_DEBOUNCE_DELAY_MAX;
> +}
> +
> +static int hi8435_probe(struct spi_device *spi)
> +{
> +	struct iio_dev *idev;
> +	struct hi8435_priv *priv;
> +	int ret;
> +
> +	idev = devm_iio_device_alloc(&spi->dev, sizeof(*priv));
> +	if (!idev)
> +		return -ENOMEM;
> +
> +	priv = iio_priv(idev);
> +	priv->spi = spi;
> +
> +	if (spi->dev.of_node)
> +		hi8435_parse_dt(priv);
> +
> +	spi_set_drvdata(spi, idev);
> +	mutex_init(&priv->lock);
> +	INIT_DELAYED_WORK(&priv->work, hi8435_debounce_work);
> +
> +	idev->dev.parent	= &spi->dev;
> +	idev->name		= spi_get_device_id(spi)->name;
> +	idev->modes		= INDIO_DIRECT_MODE;
> +	idev->info		= &hi8435_info;
> +	idev->channels		= hi8435_channels;
> +	idev->num_channels	= ARRAY_SIZE(hi8435_channels);
> +
> +	if (priv->reset_gpio) {
> +		ret = devm_gpio_request(&spi->dev, priv->reset_gpio, idev->name);
> +		if (!ret) {
> +			/* chip hardware reset */
> +			gpio_direction_output(priv->reset_gpio, 0);
> +			udelay(5);
> +			gpio_direction_output(priv->reset_gpio, 1);
> +		}
> +	} else {
> +		/* chip software reset */
> +		hi8435_writeb(priv, HI8435_CTRL_REG, HI8435_CTRL_SRST);
> +		/* get out from reset state */
> +		hi8435_writeb(priv, HI8435_CTRL_REG, 0);
> +	}
> +
> +	/* unmask all events */
> +	priv->event_scan_mask = ~(0);
> +	/* initialize default thresholds */
> +	priv->threshold_lo[0] = priv->threshold_lo[1] = 2;
> +	priv->threshold_hi[0] = priv->threshold_hi[1] = 4;
These seem very random.  Why these values or 
> +	hi8435_writew(priv, HI8435_GOCENHYS_REG, 0x206);
What is 0x206?  Again, I guess a default. I'd like to see a comment
here saying what real world value it corresponds to.

> +	hi8435_writew(priv, HI8435_SOCENHYS_REG, 0x206);
> +
I am as yet unconvinced that using triggers to poll events is a terribly
good idea.  Feels rather like we are doing this due to a deficiency in
the buffer interface than because sending events makes sense here.

> +	ret = iio_triggered_event_setup(idev, NULL, hi8435_trigger_handler);
> +	if (ret)
> +		return ret;
> +
> +	ret = iio_device_register(idev);
> +	if (ret < 0) {
> +		dev_err(&spi->dev, "unable to register device\n");
> +		goto unregister_triggered_event;
> +	}
> +
> +	return 0;
> +
> +unregister_triggered_event:
> +	iio_triggered_event_cleanup(idev);
> +	return ret;
> +}
> +
> +static int hi8435_remove(struct spi_device *spi)
> +{
> +	struct iio_dev *idev = spi_get_drvdata(spi);
> +	struct hi8435_priv *priv = iio_priv(idev);
> +
> +	cancel_delayed_work_sync(&priv->work);
> +	iio_device_unregister(idev);
> +	iio_triggered_event_cleanup(idev);
> +
> +	return 0;
> +}
> +
> +static const struct of_device_id hi8435_dt_ids[] = {
> +	{ .compatible = "holt,hi8435" },
> +	{},
> +};
> +MODULE_DEVICE_TABLE(of, hi8435_dt_ids);
> +
> +static const struct spi_device_id hi8435_id[] = {
> +	{ "hi8435", 0},
> +	{ }
> +};
> +MODULE_DEVICE_TABLE(spi, hi8435_id);
> +
> +static struct spi_driver hi8435_driver = {
> +	.driver	= {
> +		.name		= DRV_NAME,
> +		.of_match_table	= of_match_ptr(hi8435_dt_ids),
> +	},
> +	.probe		= hi8435_probe,
> +	.remove		= hi8435_remove,
> +	.id_table	= hi8435_id,
> +};
> +module_spi_driver(hi8435_driver);
> +
> +MODULE_LICENSE("GPL");
> +MODULE_AUTHOR("Vladimir Barinov");
> +MODULE_DESCRIPTION("HI-8435 threshold detector");
> 


^ permalink raw reply	[flat|nested] 40+ messages in thread

* Re: [PATCH v3 1/7] iio: adc: hi8435: Holt HI-8435 threshold detector
@ 2015-08-08 17:56     ` Jonathan Cameron
  0 siblings, 0 replies; 40+ messages in thread
From: Jonathan Cameron @ 2015-08-08 17:56 UTC (permalink / raw)
  To: Vladimir Barinov
  Cc: Hartmut Knaack, Lars-Peter Clausen, Peter Meerwald, Rob Herring,
	Pawel Moll, Mark Rutland, Ian Campbell, Kumar Gala,
	linux-kernel-u79uwXL29TY76Z2rM5mHXA,
	linux-iio-u79uwXL29TY76Z2rM5mHXA,
	devicetree-u79uwXL29TY76Z2rM5mHXA,
	cory.tusar-J6Z/VSE8EyIAspv4Qr0y0gC/G2K4zDHf

On 29/07/15 13:56, Vladimir Barinov wrote:
> Add Holt threshold detector driver for HI-8435 chip
> 
> Signed-off-by: Vladimir Barinov <vladimir.barinov-M4DtvfQ/ZS1MRgGoP+s0PdBPR1lH4CV8@public.gmane.org>
Because it usually reads better that way, I review from the bottom.
Might make more sense if you read the comments that way around ;)

I'm going to guess that the typical usecase for this device is in realtime
systems where polling gives a nice predictable timing (hence the lack of any
interrupt support).  These typically operate as 'scanning' devices
(e.g. you update all state at approximately the same time, by reading
one input after another in some predefined order).

Does the use of events actually map well to this use case?  You are taking
something that (other than the results being a bit different) is basically
a digital I/O interface and mapping it (sort of) to an interrupt chip
when it isn't nothing of the sort.

I'd like more opinions on this, but my gut reaction is that we would
be better making the normal buffered infrastructure handle this data
properly rather than pushing it through the events intrastructure.

Your solution is neat and tidy in someways but feels like it is mashing
data into a particular format.

To add to the complexity we could have debounce logic.  If we mapped to a
fill the buffer with a scan mode, how would we handle this?
My guess is that it would simply delay transistions.  Perhaps we even
support reading both the value right now and the debounced value if that
is possible?

However, here the debounce is all software.  To my mind, move this into
userspace entirely. We aren't dealing with big data here so it's hardly
going to be particularly challenging.

So my gut feeling is definitely we need to make the buffer infrastructure
handle 1 bit values (in groups) then push this data out that way.

Still I would like other opinions on this!

Jonathan

> ---
> Changes in version 2:
> - Added file sysfs-bus-iio-adc-hi8435
> - Changed naming from "discrete ADC" to "threshold detector"
> - Replaced swab16p/swab32p with be16_to_cpup/be32_to_cpup
> - Made *_show and *_store functions static
> - moved out from iio buffers to iio events
> - removed hi8436/hi8437 chips from the driver
> - moved from debounce_soft_delay/enable to debounce_interval via
>   IIO_CHAN_INFO_DEBOUNCE_TIME
> - added name extention "comparator"
> - moved threshold/hysteresis setup via generic iio event sysfs
> - added software mask/unmask channel events
> - added programming sensor outputs while in test mode via
>   IIO_CHAN_INFO_RAW
> - added channels .ext_info for programming sensing mode
> Changes in version 3:
> - fixed typos
> - moved <linux/interrupt.h> to match alphabetic order
> - fixed missed */event/* prefix in the ABI description
> - added missed colon in sysfs-bus-iio-adc-hi8435 file after "What"
> - moved in_voltageY_comparator_thresh_either_en and debounce_time
>   to sysfs-bus-iio file
> - added error checking for hi8435_write[b|w]
> 
>  Documentation/ABI/testing/sysfs-bus-iio            |   1 +
>  Documentation/ABI/testing/sysfs-bus-iio-adc-hi8435 |  61 ++
>  drivers/iio/adc/Kconfig                            |  11 +
>  drivers/iio/adc/Makefile                           |   1 +
>  drivers/iio/adc/hi8435.c                           | 659 +++++++++++++++++++++
>  5 files changed, 742 insertions(+)
>  create mode 100644 Documentation/ABI/testing/sysfs-bus-iio-adc-hi8435
>  create mode 100644 drivers/iio/adc/hi8435.c
> 
> diff --git a/Documentation/ABI/testing/sysfs-bus-iio b/Documentation/ABI/testing/sysfs-bus-iio
> index 70c9b1a..09eac44 100644
> --- a/Documentation/ABI/testing/sysfs-bus-iio
> +++ b/Documentation/ABI/testing/sysfs-bus-iio
> @@ -572,6 +572,7 @@ What:		/sys/.../iio:deviceX/events/in_rot_from_north_magnetic_tilt_comp_thresh_r
>  What:		/sys/.../iio:deviceX/events/in_rot_from_north_magnetic_tilt_comp_thresh_falling_en
>  What:		/sys/.../iio:deviceX/events/in_rot_from_north_true_tilt_comp_thresh_rising_en
>  What:		/sys/.../iio:deviceX/events/in_rot_from_north_true_tilt_comp_thresh_falling_en
> +What:		/sys/.../iio:deviceX/events/in_voltageY_comparator_thresh_either_en
>  What:		/sys/.../iio:deviceX/events/in_voltageY_supply_thresh_rising_en
>  What:		/sys/.../iio:deviceX/events/in_voltageY_supply_thresh_falling_en
>  What:		/sys/.../iio:deviceX/events/in_voltageY_thresh_rising_en
> diff --git a/Documentation/ABI/testing/sysfs-bus-iio-adc-hi8435 b/Documentation/ABI/testing/sysfs-bus-iio-adc-hi8435
> new file mode 100644
> index 0000000..c59d81d
> --- /dev/null
> +++ b/Documentation/ABI/testing/sysfs-bus-iio-adc-hi8435
> @@ -0,0 +1,61 @@
> +What:		/sys/bus/iio/devices/iio:deviceX/in_voltageY_comparator_raw
> +Date:		July 2015
> +KernelVersion:	4.2.0
> +Contact:	source-M4DtvfQ/ZS1MRgGoP+s0PdBPR1lH4CV8@public.gmane.org
> +Description:
> +		Read value is a voltage threshold measurement from channel Y.
> +		Could be either 0 if sensor voltage is lower then low voltage
> +		threshold or 1 if sensor voltage is higher then high voltage
> +		threshold.
> +		Write value is a programmed sensor output while in self test
> +		mode. Could be either 0 or 1. The programmed value will be read
> +		back if /sys/bus/iio/devices/iio:deviceX/test_enable is set to 1.
> +
> +What:		/sys/bus/iio/devices/iio:deviceX/test_enable
> +Date:		July 2015
> +KernelVersion:	4.2.0
> +Contact:	source-M4DtvfQ/ZS1MRgGoP+s0PdBPR1lH4CV8@public.gmane.org
> +Description:
> +		Enable/disable the HI-8435 self test mode.
> +		If enabled the in_voltageY_comparator_raw should be read back
> +		accordingly to written value to in_voltageY_comparator_raw.
> +
> +What:		/sys/bus/iio/devices/iio:deviceX/in_voltageY_comparator_sensing_mode
> +Date:		July 2015
> +KernelVersion:	4.2.0
> +Contact:	source-M4DtvfQ/ZS1MRgGoP+s0PdBPR1lH4CV8@public.gmane.org
> +Description:
> +		Program sensor type for threshold detector inputs.
> +		Could be either "GND-Open" or "Supply-Open" mode. Y is a
> +		threshold detector input channel. Channels 0..7, 8..15, 16..23
> +		and 24..31 has common sensor types.
> +
> +What:		/sys/bus/iio/devices/iio:deviceX/events/in_voltageY_comparator_thresh_falling_value
> +Date:		July 2015
> +KernelVersion:	4.2.0
> +Contact:	source-M4DtvfQ/ZS1MRgGoP+s0PdBPR1lH4CV8@public.gmane.org
> +Description:
> +		Channel Y low voltage threshold. If sensor input voltage goes lower then
> +		this value then the threshold falling event is pushed.
> +		Depending on in_voltageY_comparator_sensing_mode the low voltage threshold
> +		is separately set for "GND-Open" and "Supply-Open" modes.
> +		Channels 0..31 have common low threshold values, but could have different
> +		sensing_modes.
> +		The low voltage threshold range is between 2..21V.
> +		Hysteresis between low and high thresholds can not be lower then 2 and
> +		can not be odd.
> +
> +What:		/sys/bus/iio/devices/iio:deviceX/events/in_voltageY_comparator_thresh_rising_value
> +Date:		July 2015
> +KernelVersion:	4.2.0
> +Contact:	source-M4DtvfQ/ZS1MRgGoP+s0PdBPR1lH4CV8@public.gmane.org
> +Description:
> +		Channel Y high voltage threshold. If sensor input voltage goes higher then
> +		this value then the threshold rising event is pushed.
> +		Depending on in_voltageY_comparator_sensing_mode the high voltage threshold
> +		is separately set for "GND-Open" and "Supply-Open" modes.
> +		Channels 0..31 have common high threshold values, but could have different
> +		sensing_modes.
> +		The high voltage threshold range is between 3..22V.
> +		Hysteresis between low and high thresholds can not be lower then 2 and
> +		can not be odd.
> diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig
> index eb0cd89..553c91e 100644
> --- a/drivers/iio/adc/Kconfig
> +++ b/drivers/iio/adc/Kconfig
> @@ -170,6 +170,17 @@ config EXYNOS_ADC
>  	  of SoCs for drivers such as the touchscreen and hwmon to use to share
>  	  this resource.
>  
> +config HI8435
> +	tristate "Holt Integrated Circuits HI-8435 threshold detector"
> +	select IIO_TRIGGERED_EVENT
> +	depends on SPI
> +	help
> +	  If you say yes here you get support for Holt Integrated Circuits
> +	  HI-8435 chip.
> +
> +	  This driver can also be built as a module. If so, the module will be
> +	  called hi8435.
> +
>  config LP8788_ADC
>  	tristate "LP8788 ADC driver"
>  	depends on MFD_LP8788
> diff --git a/drivers/iio/adc/Makefile b/drivers/iio/adc/Makefile
> index a096210..00f367aa 100644
> --- a/drivers/iio/adc/Makefile
> +++ b/drivers/iio/adc/Makefile
> @@ -19,6 +19,7 @@ obj-$(CONFIG_BERLIN2_ADC) += berlin2-adc.o
>  obj-$(CONFIG_DA9150_GPADC) += da9150-gpadc.o
>  obj-$(CONFIG_CC10001_ADC) += cc10001_adc.o
>  obj-$(CONFIG_EXYNOS_ADC) += exynos_adc.o
> +obj-$(CONFIG_HI8435) += hi8435.o
>  obj-$(CONFIG_LP8788_ADC) += lp8788_adc.o
>  obj-$(CONFIG_MAX1027) += max1027.o
>  obj-$(CONFIG_MAX1363) += max1363.o
> diff --git a/drivers/iio/adc/hi8435.c b/drivers/iio/adc/hi8435.c
> new file mode 100644
> index 0000000..f3dab5a
> --- /dev/null
> +++ b/drivers/iio/adc/hi8435.c
> @@ -0,0 +1,659 @@
> +/*
> + * Holt Integrated Circuits HI-8435 threshold detector driver
> + *
> + * Copyright (C) 2015 Zodiac Inflight Innovations
> + * Copyright (C) 2015 Cogent Embedded, Inc.
> + *
> + * 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.
> + */
> +
> +#include <linux/delay.h>
> +#include <linux/iio/events.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/triggered_event.h>
> +#include <linux/interrupt.h>
> +#include <linux/module.h>
> +#include <linux/of.h>
> +#include <linux/of_device.h>
> +#include <linux/of_gpio.h>
> +#include <linux/spi/spi.h>
> +#include <linux/workqueue.h>
> +
> +#define DRV_NAME "hi8435"
> +
> +/* Register offsets for HI-8435 */
> +#define HI8435_CTRL_REG			0x02
> +#define HI8435_PSEN_REG			0x04
> +#define HI8435_TMDATA_REG		0x1E
> +#define HI8435_GOCENHYS_REG		0x3A
> +#define HI8435_SOCENHYS_REG		0x3C
Hmm. These aren't in the docs that I can immediately acquire...
Any chance of some more meaningful naming?
> +#define HI8435_SO7_0_REG		0x10
> +#define HI8435_SO15_8_REG		0x12
 +#define HI8435_SO23_16_REG		0x14
> +#define HI8435_SO31_24_REG		0x16
> +#define HI8435_SO31_0_REG		0x78
> +
> +#define HI8435_WRITE_OPCODE		0x00
> +#define HI8435_READ_OPCODE		0x80
> +
> +/* CTRL register bits */
> +#define HI8435_CTRL_TEST		0x01
> +#define HI8435_CTRL_SRST		0x02
> +
> +#define HI8435_DEBOUNCE_DELAY_MAX	1000	/* msec */
> +#define HI8435_DEBOUNCE_DELAY_DEF	100	/* msec */
> +
> +struct hi8435_priv {
> +	struct spi_device *spi;
> +	struct mutex lock;
> +	struct delayed_work work;
> +
> +	int reset_gpio;
> +	int debounce_interval; /* msec */
> +	u32 debounce_val; /* prev value to compare during software debounce */
> +
> +	unsigned long event_scan_mask; /* soft mask/unmask channels events */
> +	unsigned int event_prev_val;
> +
> +	unsigned threshold_lo[2]; /* GND-Open and Supply-Open thresholds */
> +	unsigned threshold_hi[2]; /* GND-Open and Supply-Open threshold */
> +	u8 reg_buffer[4] ____cacheline_aligned;
> +};
> +
> +static int hi8435_readb(struct hi8435_priv *priv, u8 reg, u8 *val)
> +{
> +	reg |= HI8435_READ_OPCODE;
> +	return spi_write_then_read(priv->spi, &reg, 1, val, 1);
> +}
> +
> +static int hi8435_readw(struct hi8435_priv *priv, u8 reg, u16 *val)
> +{
> +	int ret;
> +
> +	reg |= HI8435_READ_OPCODE;
> +	ret = spi_write_then_read(priv->spi, &reg, 1, val, 2);
> +	*val = be16_to_cpup(val);
> +
> +	return ret;
> +}
> +
> +static int hi8435_readl(struct hi8435_priv *priv, u8 reg, u32 *val)
> +{
> +	int ret;
> +
> +	reg |= HI8435_READ_OPCODE;
> +	ret = spi_write_then_read(priv->spi, &reg, 1, val, 4);
> +	*val = be32_to_cpup(val);
> +
> +	return ret;
> +}
> +
> +static int hi8435_writeb(struct hi8435_priv *priv, u8 reg, u8 val)
> +{
> +	priv->reg_buffer[0] = reg | HI8435_WRITE_OPCODE;
> +	priv->reg_buffer[1] = val;
> +
> +	return spi_write(priv->spi, priv->reg_buffer, 2);
> +}
> +
> +static int hi8435_writew(struct hi8435_priv *priv, u8 reg, u16 val)
> +{
> +	priv->reg_buffer[0] = reg | HI8435_WRITE_OPCODE;
> +	priv->reg_buffer[1] = (val >> 8) & 0xff;
> +	priv->reg_buffer[2] = val & 0xff;
> +
> +	return spi_write(priv->spi, priv->reg_buffer, 3);
> +}
> +
> +static ssize_t hi8435_test_enable_show(struct device *dev,
> +				       struct device_attribute *attr, char *buf)
> +{
> +	struct hi8435_priv *priv = iio_priv(dev_to_iio_dev(dev));
> +	int ret;
> +	u8 reg;
> +
> +	ret = hi8435_readb(priv, HI8435_CTRL_REG, &reg);
> +	if (ret < 0)
> +		return ret;
> +
> +	return sprintf(buf, "%d\n", reg & HI8435_CTRL_TEST);
> +}
> +
> +static ssize_t hi8435_test_enable_store(struct device *dev,
> +					struct device_attribute *attr,
> +					const char *buf, size_t len)
> +{
> +	struct hi8435_priv *priv = iio_priv(dev_to_iio_dev(dev));
> +	unsigned int val;
> +	int ret;
> +
> +	ret = kstrtouint(buf, 10, &val);
> +	if (ret)
> +		return ret;
> +
> +	ret = hi8435_writeb(priv, HI8435_CTRL_REG, val ? HI8435_CTRL_TEST : 0);
> +	if (ret < 0)
> +		return ret;
> +
> +	return len;
> +}
> +
> +static IIO_DEVICE_ATTR(test_enable, S_IRUGO | S_IWUSR,
> +	hi8435_test_enable_show, hi8435_test_enable_store, 0);
> +
> +static struct attribute *hi8435_attributes[] = {
> +	&iio_dev_attr_test_enable.dev_attr.attr,
> +	NULL,
> +};
> +
> +static struct attribute_group hi8435_attribute_group = {
> +	.attrs = hi8435_attributes,
> +};
> +
> +static int hi8435_read_raw(struct iio_dev *idev,
> +			   const struct iio_chan_spec *chan,
> +			   int *val, int *val2, long mask)
> +{
> +	struct hi8435_priv *priv = iio_priv(idev);
> +	int ret;
> +
> +	switch (mask) {
> +	case IIO_CHAN_INFO_RAW:
> +		ret = hi8435_readl(priv, HI8435_SO31_0_REG, val);
> +		if (ret < 0)
> +			return ret;
> +		*val = !!(*val & BIT(chan->channel));
> +		return IIO_VAL_INT;
> +	case IIO_CHAN_INFO_DEBOUNCE_TIME:
> +		*val = priv->debounce_interval;
> +		return IIO_VAL_INT;
> +	default:
> +		return -EINVAL;
> +	}
> +}
> +
> +static int hi8435_write_raw(struct iio_dev *idev,
> +			    const struct iio_chan_spec *chan,
> +			    int val, int val2, long mask)
> +{
> +	struct hi8435_priv *priv = iio_priv(idev);
> +
> +	switch (mask) {
> +	case IIO_CHAN_INFO_RAW:
> +		/* program sensors outputs in test mode */
> +		return hi8435_writeb(priv, HI8435_TMDATA_REG, val ? 0x1 : 0x2);
> +	case IIO_CHAN_INFO_DEBOUNCE_TIME:
> +		if (val < 0)
> +			return -EINVAL;
> +		if (val > HI8435_DEBOUNCE_DELAY_MAX)
> +			val = HI8435_DEBOUNCE_DELAY_MAX;
> +		priv->debounce_interval = val;
> +		return 0;
> +	default:
> +		return -EINVAL;
> +	}
> +}
> +
> +static int hi8435_read_event_config(struct iio_dev *idev,
> +				    const struct iio_chan_spec *chan,
> +				    enum iio_event_type type,
> +				    enum iio_event_direction dir)
> +{
> +	struct hi8435_priv *priv = iio_priv(idev);
> +
> +	return !!(priv->event_scan_mask & BIT(chan->channel));
> +}
> +
> +static int hi8435_write_event_config(struct iio_dev *idev,
> +				     const struct iio_chan_spec *chan,
> +				     enum iio_event_type type,
> +				     enum iio_event_direction dir, int state)
> +{
> +	struct hi8435_priv *priv = iio_priv(idev);
> +
> +	priv->event_scan_mask &= ~BIT(chan->channel);
> +	if (state)
> +		priv->event_scan_mask |= BIT(chan->channel);
> +
> +	return 0;
> +}
> +
> +static int hi8435_read_event_value(struct iio_dev *idev,
> +				   const struct iio_chan_spec *chan,
> +				   enum iio_event_type type,
> +				   enum iio_event_direction dir,
> +				   enum iio_event_info info,
> +				   int *val, int *val2)
> +{
> +	struct hi8435_priv *priv = iio_priv(idev);
> +	int ret;
> +	u8 mode, psen;
> +	u16 reg;
> +
> +	ret = hi8435_readb(priv, HI8435_PSEN_REG, &psen);
> +	if (ret < 0)
> +		return ret;
> +
> +	/* Supply-Open or GND-Open sensing mode */
> +	mode = !!(psen & BIT(chan->channel / 8));
> +
> +	ret = hi8435_readw(priv, mode ? HI8435_SOCENHYS_REG :
> +				 HI8435_GOCENHYS_REG, &reg);
> +	if (ret < 0)
> +		return ret;
> +
> +	if (dir == IIO_EV_DIR_FALLING)
> +		*val = ((reg & 0xff) - (reg >> 8)) / 2;
> +
> +	if (dir == IIO_EV_DIR_RISING)
> +		*val = ((reg & 0xff) + (reg >> 8)) / 2;
> +
> +	return IIO_VAL_INT;
> +}
> +
> +static int hi8435_write_event_value(struct iio_dev *idev,
> +				    const struct iio_chan_spec *chan,
> +				    enum iio_event_type type,
> +				    enum iio_event_direction dir,
> +				    enum iio_event_info info,
> +				    int val, int val2)
> +{
> +	struct hi8435_priv *priv = iio_priv(idev);
> +	int ret;
> +	u8 mode, psen;
> +	u16 reg;
> +
> +	ret = hi8435_readb(priv, HI8435_PSEN_REG, &psen);
> +	if (ret < 0)
> +		return ret;
> +
> +	/* Supply-Open or GND-Open sensing mode */
> +	mode = !!(psen & BIT(chan->channel / 8));
> +
> +	ret = hi8435_readw(priv, mode ? HI8435_SOCENHYS_REG :
> +				 HI8435_GOCENHYS_REG, &reg);
> +	if (ret < 0)
> +		return ret;
> +
> +	if (dir == IIO_EV_DIR_FALLING) {
> +		/* falling threshold range 2..21V, hysteresis minimum 2V */
> +		if (val < 2 || val > 21 || (val + 1) >= priv->threshold_hi[mode])
> +			return -EINVAL;
> +
> +		if (val == priv->threshold_lo[mode])
> +			return 0;
> +
> +		priv->threshold_lo[mode] = val;
> +
> +		/* hysteresis must not be odd */
> +		if ((priv->threshold_hi[mode] - priv->threshold_lo[mode]) % 2)
> +			priv->threshold_hi[mode]--;
> +	}
> +
> +	if (dir == IIO_EV_DIR_RISING) {
> +		/* rising threshold range 3..22V, hysteresis minimum 2V */
> +		if (val < 3 || val > 22 || val <= (priv->threshold_lo[mode] + 1))
> +			return -EINVAL;
> +
> +		if (val == priv->threshold_hi[mode])
> +			return 0;
> +
> +		priv->threshold_hi[mode] = val;
> +
> +		/* hysteresis must not be odd */
> +		if ((priv->threshold_hi[mode] - priv->threshold_lo[mode]) % 2)
> +			priv->threshold_lo[mode]++;
> +	}
> +
> +	/* program thresholds */
> +	mutex_lock(&priv->lock);
> +
> +	ret = hi8435_readw(priv, mode ? HI8435_SOCENHYS_REG :
> +				 HI8435_GOCENHYS_REG, &reg);
> +	if (ret < 0) {
> +		mutex_unlock(&priv->lock);
> +		return ret;
> +	}
> +
> +	/* hysteresis */
> +	reg = priv->threshold_hi[mode] - priv->threshold_lo[mode];
> +	reg <<= 8;
> +	/* threshold center */
> +	reg |= (priv->threshold_hi[mode] + priv->threshold_lo[mode]);
> +
> +	ret = hi8435_writew(priv, mode ? HI8435_SOCENHYS_REG :
> +				  HI8435_GOCENHYS_REG, reg);
> +
> +	mutex_unlock(&priv->lock);
> +
> +	return ret;
> +}
> +
> +static const struct iio_event_spec hi8435_events[] = {
> +	{
> +		.type = IIO_EV_TYPE_THRESH,
> +		.dir = IIO_EV_DIR_RISING,
> +		.mask_separate = BIT(IIO_EV_INFO_VALUE),
> +	}, {
> +		.type = IIO_EV_TYPE_THRESH,
> +		.dir = IIO_EV_DIR_FALLING,
> +		.mask_separate = BIT(IIO_EV_INFO_VALUE),
> +	}, {
> +		.type = IIO_EV_TYPE_THRESH,
> +		.dir = IIO_EV_DIR_EITHER,
> +		.mask_separate = BIT(IIO_EV_INFO_ENABLE),
> +	},
> +};
> +
> +static int hi8435_get_sensing_mode(struct iio_dev *idev,
> +				   const struct iio_chan_spec *chan)
> +{
> +	struct hi8435_priv *priv = iio_priv(idev);
> +	int ret;
> +	u8 reg;
> +
> +	ret = hi8435_readb(priv, HI8435_PSEN_REG, &reg);
> +	if (ret < 0)
> +		return ret;
> +
> +	return !!(reg & BIT(chan->channel / 8));
> +}
> +
> +static int hi8435_set_sensing_mode(struct iio_dev *idev,
> +				   const struct iio_chan_spec *chan,
> +				   unsigned int mode)
> +{
> +	struct hi8435_priv *priv = iio_priv(idev);
> +	int ret;
> +	u8 reg;
> +
> +	mutex_lock(&priv->lock);
> +
> +	ret = hi8435_readb(priv, HI8435_PSEN_REG, &reg);
> +	if (ret < 0) {
> +		mutex_unlock(&priv->lock);
> +		return ret;
> +	}
> +
> +	reg &= ~BIT(chan->channel / 8);
> +	if (mode)
> +		reg |= BIT(chan->channel / 8);
> +
> +	ret = hi8435_writeb(priv, HI8435_PSEN_REG, reg);
> +
> +	mutex_unlock(&priv->lock);
> +
> +	return ret;
> +}
> +
> +static const char * const hi8435_sensing_modes[] = { "GND-Open",
> +						     "Supply-Open" };
> +
> +static const struct iio_enum hi8435_sensing_mode = {
> +	.items = hi8435_sensing_modes,
> +	.num_items = ARRAY_SIZE(hi8435_sensing_modes),
> +	.get = hi8435_get_sensing_mode,
> +	.set = hi8435_set_sensing_mode,
> +};
> +
> +static const struct iio_chan_spec_ext_info hi8435_ext_info[] = {
> +	IIO_ENUM("sensing_mode", IIO_SEPARATE, &hi8435_sensing_mode),
> +	{},
> +};
> +
> +#define HI8435_VOLTAGE_CHANNEL(num)					\
> +{									\
> +	.type = IIO_VOLTAGE,						\
> +	.indexed = 1,							\
> +	.channel = num,							\
> +	.info_mask_separate = BIT(IIO_CHAN_INFO_RAW),			\
> +	.info_mask_shared_by_all = BIT(IIO_CHAN_INFO_DEBOUNCE_TIME),	\
> +	.event_spec = hi8435_events,					\
> +	.num_event_specs = ARRAY_SIZE(hi8435_events),			\
> +	.ext_info = hi8435_ext_info,					\
> +	.extend_name = "comparator"					\
I'm really not sure about this approach.  Can see where you are coming
from. We already have an events interface supporting these things so
why not use it?   I'm just doubtful it is an efficient option or a clean
interface (would like more opinions on this!)
> +}
> +
> +static const struct iio_chan_spec hi8435_channels[] = {
> +	HI8435_VOLTAGE_CHANNEL(0),
> +	HI8435_VOLTAGE_CHANNEL(1),
> +	HI8435_VOLTAGE_CHANNEL(2),
> +	HI8435_VOLTAGE_CHANNEL(3),
> +	HI8435_VOLTAGE_CHANNEL(4),
> +	HI8435_VOLTAGE_CHANNEL(5),
> +	HI8435_VOLTAGE_CHANNEL(6),
> +	HI8435_VOLTAGE_CHANNEL(7),
> +	HI8435_VOLTAGE_CHANNEL(8),
> +	HI8435_VOLTAGE_CHANNEL(9),
> +	HI8435_VOLTAGE_CHANNEL(10),
> +	HI8435_VOLTAGE_CHANNEL(11),
> +	HI8435_VOLTAGE_CHANNEL(12),
> +	HI8435_VOLTAGE_CHANNEL(13),
> +	HI8435_VOLTAGE_CHANNEL(14),
> +	HI8435_VOLTAGE_CHANNEL(15),
> +	HI8435_VOLTAGE_CHANNEL(16),
> +	HI8435_VOLTAGE_CHANNEL(17),
> +	HI8435_VOLTAGE_CHANNEL(18),
> +	HI8435_VOLTAGE_CHANNEL(19),
> +	HI8435_VOLTAGE_CHANNEL(20),
> +	HI8435_VOLTAGE_CHANNEL(21),
> +	HI8435_VOLTAGE_CHANNEL(22),
> +	HI8435_VOLTAGE_CHANNEL(23),
> +	HI8435_VOLTAGE_CHANNEL(24),
> +	HI8435_VOLTAGE_CHANNEL(25),
> +	HI8435_VOLTAGE_CHANNEL(26),
> +	HI8435_VOLTAGE_CHANNEL(27),
> +	HI8435_VOLTAGE_CHANNEL(28),
> +	HI8435_VOLTAGE_CHANNEL(29),
> +	HI8435_VOLTAGE_CHANNEL(30),
> +	HI8435_VOLTAGE_CHANNEL(31),
> +	IIO_CHAN_SOFT_TIMESTAMP(32),
> +};
> +
> +static const struct iio_info hi8435_info = {
> +	.driver_module = THIS_MODULE,
> +	.attrs = &hi8435_attribute_group,
> +	.read_raw = hi8435_read_raw,
> +	.write_raw = hi8435_write_raw,
> +	.read_event_config = &hi8435_read_event_config,
> +	.write_event_config = hi8435_write_event_config,
> +	.read_event_value = &hi8435_read_event_value,
> +	.write_event_value = &hi8435_write_event_value,
> +};
> +
> +static void hi8435_iio_push_event(struct iio_dev *idev, unsigned int val)
> +{
> +	struct hi8435_priv *priv = iio_priv(idev);
> +	enum iio_event_direction dir;
> +	unsigned int i;
> +	unsigned int status = priv->event_prev_val ^ val;
> +
> +	if (!status)
> +		return;
> +
> +	for_each_set_bit(i, &priv->event_scan_mask, 32) {
> +		if (!(status & BIT(i)))
> +			continue;
> +
> +		dir = val & BIT(i) ? IIO_EV_DIR_RISING : IIO_EV_DIR_FALLING;
> +
> +		iio_push_event(idev,
> +			       IIO_UNMOD_EVENT_CODE(IIO_VOLTAGE, i,
> +						    IIO_EV_TYPE_THRESH, dir),
> +			       iio_get_time_ns());
> +	}
> +
> +	priv->event_prev_val = val;
> +}
> +
> +static void hi8435_debounce_work(struct work_struct *work)
> +{
> +	struct hi8435_priv *priv = container_of(work, struct hi8435_priv,
> +						work.work);
> +	struct iio_dev *idev = spi_get_drvdata(priv->spi);
> +	u32 val;
> +	int ret;
> +
> +	ret = hi8435_readl(priv, HI8435_SO31_0_REG, &val);
> +	if (ret < 0)
> +		return;
> +
> +	if (val == priv->debounce_val)
> +		hi8435_iio_push_event(idev, val);
> +	else
> +		dev_warn(&priv->spi->dev, "filtered by software debounce");
Definitely not.  Warning every time a standard event occurs?  Not
a good idea.  Use the debug stuff if you want a way of knowing this
happened, then it will silent under normal use.

> +}
> +
> +static irqreturn_t hi8435_trigger_handler(int irq, void *private)
> +{
> +	struct iio_poll_func *pf = private;
> +	struct iio_dev *idev = pf->indio_dev;
> +	struct hi8435_priv *priv = iio_priv(idev);
> +	u32 val;
> +	int ret;
> +
> +	ret = hi8435_readl(priv, HI8435_SO31_0_REG, &val);
> +	if (ret < 0)
> +		goto err_read;
> +
> +	if (priv->debounce_interval) {
> +		priv->debounce_val = val;
> +		schedule_delayed_work(&priv->work,
> +				msecs_to_jiffies(priv->debounce_interval));
This is another element that makes me doubt that the event interface
is the way to do this.  I'd much rather we pushed the debouncing out
to userspace where cleverer things can be done (and more adaptive things).
Here to keep the event flow low you have to do it in the kernel.

> +	} else {
> +		hi8435_iio_push_event(idev, val);
> +	}
> +
> +err_read:
> +	iio_trigger_notify_done(idev->trig);
> +
> +	return IRQ_HANDLED;
> +}
> +
> +static void hi8435_parse_dt(struct hi8435_priv *priv)
> +{
> +	struct device_node *np = priv->spi->dev.of_node;
> +	int ret;
> +
> +	ret = of_get_named_gpio(np, "holt,reset-gpios", 0);
> +	priv->reset_gpio = ret < 0 ? 0 : ret;
Silly question, but can gpio0 exist on a board?  I suspect you
may need an additional variable to hold that this request failed
rather than using the magic value of 0.
> +
> +	ret = of_property_read_u32(np, "holt,debounce-interval",
> +				   &priv->debounce_interval);
> +	if (ret)
> +		priv->debounce_interval = 0;
> +	if (priv->debounce_interval > HI8435_DEBOUNCE_DELAY_MAX)
> +		priv->debounce_interval = HI8435_DEBOUNCE_DELAY_MAX;
> +}
> +
> +static int hi8435_probe(struct spi_device *spi)
> +{
> +	struct iio_dev *idev;
> +	struct hi8435_priv *priv;
> +	int ret;
> +
> +	idev = devm_iio_device_alloc(&spi->dev, sizeof(*priv));
> +	if (!idev)
> +		return -ENOMEM;
> +
> +	priv = iio_priv(idev);
> +	priv->spi = spi;
> +
> +	if (spi->dev.of_node)
> +		hi8435_parse_dt(priv);
> +
> +	spi_set_drvdata(spi, idev);
> +	mutex_init(&priv->lock);
> +	INIT_DELAYED_WORK(&priv->work, hi8435_debounce_work);
> +
> +	idev->dev.parent	= &spi->dev;
> +	idev->name		= spi_get_device_id(spi)->name;
> +	idev->modes		= INDIO_DIRECT_MODE;
> +	idev->info		= &hi8435_info;
> +	idev->channels		= hi8435_channels;
> +	idev->num_channels	= ARRAY_SIZE(hi8435_channels);
> +
> +	if (priv->reset_gpio) {
> +		ret = devm_gpio_request(&spi->dev, priv->reset_gpio, idev->name);
> +		if (!ret) {
> +			/* chip hardware reset */
> +			gpio_direction_output(priv->reset_gpio, 0);
> +			udelay(5);
> +			gpio_direction_output(priv->reset_gpio, 1);
> +		}
> +	} else {
> +		/* chip software reset */
> +		hi8435_writeb(priv, HI8435_CTRL_REG, HI8435_CTRL_SRST);
> +		/* get out from reset state */
> +		hi8435_writeb(priv, HI8435_CTRL_REG, 0);
> +	}
> +
> +	/* unmask all events */
> +	priv->event_scan_mask = ~(0);
> +	/* initialize default thresholds */
> +	priv->threshold_lo[0] = priv->threshold_lo[1] = 2;
> +	priv->threshold_hi[0] = priv->threshold_hi[1] = 4;
These seem very random.  Why these values or 
> +	hi8435_writew(priv, HI8435_GOCENHYS_REG, 0x206);
What is 0x206?  Again, I guess a default. I'd like to see a comment
here saying what real world value it corresponds to.

> +	hi8435_writew(priv, HI8435_SOCENHYS_REG, 0x206);
> +
I am as yet unconvinced that using triggers to poll events is a terribly
good idea.  Feels rather like we are doing this due to a deficiency in
the buffer interface than because sending events makes sense here.

> +	ret = iio_triggered_event_setup(idev, NULL, hi8435_trigger_handler);
> +	if (ret)
> +		return ret;
> +
> +	ret = iio_device_register(idev);
> +	if (ret < 0) {
> +		dev_err(&spi->dev, "unable to register device\n");
> +		goto unregister_triggered_event;
> +	}
> +
> +	return 0;
> +
> +unregister_triggered_event:
> +	iio_triggered_event_cleanup(idev);
> +	return ret;
> +}
> +
> +static int hi8435_remove(struct spi_device *spi)
> +{
> +	struct iio_dev *idev = spi_get_drvdata(spi);
> +	struct hi8435_priv *priv = iio_priv(idev);
> +
> +	cancel_delayed_work_sync(&priv->work);
> +	iio_device_unregister(idev);
> +	iio_triggered_event_cleanup(idev);
> +
> +	return 0;
> +}
> +
> +static const struct of_device_id hi8435_dt_ids[] = {
> +	{ .compatible = "holt,hi8435" },
> +	{},
> +};
> +MODULE_DEVICE_TABLE(of, hi8435_dt_ids);
> +
> +static const struct spi_device_id hi8435_id[] = {
> +	{ "hi8435", 0},
> +	{ }
> +};
> +MODULE_DEVICE_TABLE(spi, hi8435_id);
> +
> +static struct spi_driver hi8435_driver = {
> +	.driver	= {
> +		.name		= DRV_NAME,
> +		.of_match_table	= of_match_ptr(hi8435_dt_ids),
> +	},
> +	.probe		= hi8435_probe,
> +	.remove		= hi8435_remove,
> +	.id_table	= hi8435_id,
> +};
> +module_spi_driver(hi8435_driver);
> +
> +MODULE_LICENSE("GPL");
> +MODULE_AUTHOR("Vladimir Barinov");
> +MODULE_DESCRIPTION("HI-8435 threshold detector");
> 

^ permalink raw reply	[flat|nested] 40+ messages in thread

* Re: [PATCH v3 7/7] iio: Fix typos in ABI documentation
@ 2015-08-08 18:39     ` Jonathan Cameron
  0 siblings, 0 replies; 40+ messages in thread
From: Jonathan Cameron @ 2015-08-08 18:39 UTC (permalink / raw)
  To: Vladimir Barinov
  Cc: Hartmut Knaack, Lars-Peter Clausen, Peter Meerwald, Rob Herring,
	Pawel Moll, Mark Rutland, Ian Campbell, Kumar Gala, linux-kernel,
	linux-iio, devicetree, cory.tusar

On 29/07/15 13:57, Vladimir Barinov wrote:
> Fix typos in ABI documentation
> 
> Signed-off-by: Vladimir Barinov <vladimir.barinov@cogentembedded.com>
Applied to the togreg branch of iio.git

Did take me a while to work out what the first one was ;)
git diff --word-diff-regex=. HEAD~1

Turns out to be a handy addition to the armoury!

Jonathan 


> ---
> Changes in version 3:
> - initially added
> 
>  Documentation/ABI/testing/sysfs-bus-iio | 6 +++---
>  1 file changed, 3 insertions(+), 3 deletions(-)
> 
> diff --git a/Documentation/ABI/testing/sysfs-bus-iio b/Documentation/ABI/testing/sysfs-bus-iio
> index 65bf49e..71a48b7 100644
> --- a/Documentation/ABI/testing/sysfs-bus-iio
> +++ b/Documentation/ABI/testing/sysfs-bus-iio
> @@ -488,7 +488,7 @@ Contact:	linux-iio@vger.kernel.org
>  Description:
>  		Specifies the output powerdown mode.
>  		DAC output stage is disconnected from the amplifier and
> -		1kohm_to_gnd: connected	to ground via an 1kOhm resistor,
> +		1kohm_to_gnd: connected to ground via an 1kOhm resistor,
>  		6kohm_to_gnd: connected to ground via a 6kOhm resistor,
>  		20kohm_to_gnd: connected to ground via a 20kOhm resistor,
>  		100kohm_to_gnd: connected to ground via an 100kOhm resistor,
> @@ -498,9 +498,9 @@ Description:
>  		outX_powerdown_mode_available. If Y is not present the
>  		mode is shared across all outputs.
>  
> -What:		/sys/.../iio:deviceX/out_votlageY_powerdown_mode_available
> +What:		/sys/.../iio:deviceX/out_voltageY_powerdown_mode_available
>  What:		/sys/.../iio:deviceX/out_voltage_powerdown_mode_available
> -What:		/sys/.../iio:deviceX/out_altvotlageY_powerdown_mode_available
> +What:		/sys/.../iio:deviceX/out_altvoltageY_powerdown_mode_available
>  What:		/sys/.../iio:deviceX/out_altvoltage_powerdown_mode_available
>  KernelVersion:	2.6.38
>  Contact:	linux-iio@vger.kernel.org
> 


^ permalink raw reply	[flat|nested] 40+ messages in thread

* Re: [PATCH v3 7/7] iio: Fix typos in ABI documentation
@ 2015-08-08 18:39     ` Jonathan Cameron
  0 siblings, 0 replies; 40+ messages in thread
From: Jonathan Cameron @ 2015-08-08 18:39 UTC (permalink / raw)
  To: Vladimir Barinov
  Cc: Hartmut Knaack, Lars-Peter Clausen, Peter Meerwald, Rob Herring,
	Pawel Moll, Mark Rutland, Ian Campbell, Kumar Gala,
	linux-kernel-u79uwXL29TY76Z2rM5mHXA,
	linux-iio-u79uwXL29TY76Z2rM5mHXA,
	devicetree-u79uwXL29TY76Z2rM5mHXA,
	cory.tusar-J6Z/VSE8EyIAspv4Qr0y0gC/G2K4zDHf

On 29/07/15 13:57, Vladimir Barinov wrote:
> Fix typos in ABI documentation
> 
> Signed-off-by: Vladimir Barinov <vladimir.barinov-M4DtvfQ/ZS1MRgGoP+s0PdBPR1lH4CV8@public.gmane.org>
Applied to the togreg branch of iio.git

Did take me a while to work out what the first one was ;)
git diff --word-diff-regex=. HEAD~1

Turns out to be a handy addition to the armoury!

Jonathan 


> ---
> Changes in version 3:
> - initially added
> 
>  Documentation/ABI/testing/sysfs-bus-iio | 6 +++---
>  1 file changed, 3 insertions(+), 3 deletions(-)
> 
> diff --git a/Documentation/ABI/testing/sysfs-bus-iio b/Documentation/ABI/testing/sysfs-bus-iio
> index 65bf49e..71a48b7 100644
> --- a/Documentation/ABI/testing/sysfs-bus-iio
> +++ b/Documentation/ABI/testing/sysfs-bus-iio
> @@ -488,7 +488,7 @@ Contact:	linux-iio-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
>  Description:
>  		Specifies the output powerdown mode.
>  		DAC output stage is disconnected from the amplifier and
> -		1kohm_to_gnd: connected	to ground via an 1kOhm resistor,
> +		1kohm_to_gnd: connected to ground via an 1kOhm resistor,
>  		6kohm_to_gnd: connected to ground via a 6kOhm resistor,
>  		20kohm_to_gnd: connected to ground via a 20kOhm resistor,
>  		100kohm_to_gnd: connected to ground via an 100kOhm resistor,
> @@ -498,9 +498,9 @@ Description:
>  		outX_powerdown_mode_available. If Y is not present the
>  		mode is shared across all outputs.
>  
> -What:		/sys/.../iio:deviceX/out_votlageY_powerdown_mode_available
> +What:		/sys/.../iio:deviceX/out_voltageY_powerdown_mode_available
>  What:		/sys/.../iio:deviceX/out_voltage_powerdown_mode_available
> -What:		/sys/.../iio:deviceX/out_altvotlageY_powerdown_mode_available
> +What:		/sys/.../iio:deviceX/out_altvoltageY_powerdown_mode_available
>  What:		/sys/.../iio:deviceX/out_altvoltage_powerdown_mode_available
>  KernelVersion:	2.6.38
>  Contact:	linux-iio-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
> 

^ permalink raw reply	[flat|nested] 40+ messages in thread

* Re: [PATCH v3 1/7] iio: adc: hi8435: Holt HI-8435 threshold detector
@ 2015-08-11 12:21       ` Lars-Peter Clausen
  0 siblings, 0 replies; 40+ messages in thread
From: Lars-Peter Clausen @ 2015-08-11 12:21 UTC (permalink / raw)
  To: Jonathan Cameron, Vladimir Barinov
  Cc: Hartmut Knaack, Peter Meerwald, Rob Herring, Pawel Moll,
	Mark Rutland, Ian Campbell, Kumar Gala, linux-kernel, linux-iio,
	devicetree, cory.tusar

On 08/08/2015 07:56 PM, Jonathan Cameron wrote:
> On 29/07/15 13:56, Vladimir Barinov wrote:
>> Add Holt threshold detector driver for HI-8435 chip
>>
>> Signed-off-by: Vladimir Barinov <vladimir.barinov@cogentembedded.com>
> Because it usually reads better that way, I review from the bottom.
> Might make more sense if you read the comments that way around ;)
> 
> I'm going to guess that the typical usecase for this device is in realtime
> systems where polling gives a nice predictable timing (hence the lack of any
> interrupt support).  These typically operate as 'scanning' devices
> (e.g. you update all state at approximately the same time, by reading
> one input after another in some predefined order).
> 
> Does the use of events actually map well to this use case?  You are taking
> something that (other than the results being a bit different) is basically
> a digital I/O interface and mapping it (sort of) to an interrupt chip
> when it isn't nothing of the sort.
> 
> I'd like more opinions on this, but my gut reaction is that we would
> be better making the normal buffered infrastructure handle this data
> properly rather than pushing it through the events intrastructure.
> 
> Your solution is neat and tidy in someways but feels like it is mashing
> data into a particular format.
> 
> To add to the complexity we could have debounce logic.  If we mapped to a
> fill the buffer with a scan mode, how would we handle this?
> My guess is that it would simply delay transistions.  Perhaps we even
> support reading both the value right now and the debounced value if that
> is possible?
> 
> However, here the debounce is all software.  To my mind, move this into
> userspace entirely. We aren't dealing with big data here so it's hardly
> going to be particularly challenging.
> 
> So my gut feeling is definitely we need to make the buffer infrastructure
> handle 1 bit values (in groups) then push this data out that way.
> 
> Still I would like other opinions on this!

Having thought about this a bit having support for event triggers seems like
a nice addition to me. When you think about it it is not too different from
buffer triggers. Some devices are able to generate their own trigger events
using a IRQ, others might need a software controlled trigger. You could
argue that the same is true for events. Having support for software based
event triggers e.g. allows support for devices which have events and have an
IRQ, but where the IRQ is simply not connected.

Whether it makes sense for this device is a different question though I guess.

Since this is a threshold detector events seem to be the right approach to
me. You could have similar hardware which actually generates IRQs, so you'd
have the same interface. Additionally if we expects changes only to occur at
a much lower rate than the polling rate only sending something to userspace
when it changes keeps the amount of data to transfer lower. On the other
hand if changes happen a lot using the event based approach would certainly
create a higher load. And another thing to consider is that some
applications might be interested in getting the raw data. So in conclusion,
I don't know ;). Both approaches seem sensible enough to me.

[...]
>> static void hi8435_debounce_work(struct work_struct *work)
>> +{
>> +	struct hi8435_priv *priv = container_of(work, struct hi8435_priv,
>> +						work.work);
>> +	struct iio_dev *idev = spi_get_drvdata(priv->spi);
>> +	u32 val;
>> +	int ret;
>> +
>> +	ret = hi8435_readl(priv, HI8435_SO31_0_REG, &val);
>> +	if (ret < 0)
>> +		return;
>> +
>> +	if (val == priv->debounce_val)
>> +		hi8435_iio_push_event(idev, val);
>> +	else
>> +		dev_warn(&priv->spi->dev, "filtered by software debounce");
> Definitely not.  Warning every time a standard event occurs?  Not
> a good idea.  Use the debug stuff if you want a way of knowing this
> happened, then it will silent under normal use.
> 
>> +}
>> +
>> +static irqreturn_t hi8435_trigger_handler(int irq, void *private)
>> +{
>> +	struct iio_poll_func *pf = private;
>> +	struct iio_dev *idev = pf->indio_dev;
>> +	struct hi8435_priv *priv = iio_priv(idev);
>> +	u32 val;
>> +	int ret;
>> +
>> +	ret = hi8435_readl(priv, HI8435_SO31_0_REG, &val);
>> +	if (ret < 0)
>> +		goto err_read;
>> +
>> +	if (priv->debounce_interval) {
>> +		priv->debounce_val = val;
>> +		schedule_delayed_work(&priv->work,
>> +				msecs_to_jiffies(priv->debounce_interval));
> This is another element that makes me doubt that the event interface
> is the way to do this.  I'd much rather we pushed the debouncing out
> to userspace where cleverer things can be done (and more adaptive things).
> Here to keep the event flow low you have to do it in the kernel.

Yes, debouncing should probably not be done in kernel space and the debounce
interval should not be a devicetree property.

> 
>> +	} else {
>> +		hi8435_iio_push_event(idev, val);
>> +	}
>> +
>> +err_read:
>> +	iio_trigger_notify_done(idev->trig);
>> +
>> +	return IRQ_HANDLED;
>> +}
>> +
>> +static void hi8435_parse_dt(struct hi8435_priv *priv)
>> +{
>> +	struct device_node *np = priv->spi->dev.of_node;
>> +	int ret;
>> +
>> +	ret = of_get_named_gpio(np, "holt,reset-gpios", 0);
>> +	priv->reset_gpio = ret < 0 ? 0 : ret;
> Silly question, but can gpio0 exist on a board?  I suspect you
> may need an additional variable to hold that this request failed
> rather than using the magic value of 0.

It can, but new stuff should just use the GPIO descriptor API where NULL can
be used to indicate a invalid GPIO.

- Lars


^ permalink raw reply	[flat|nested] 40+ messages in thread

* Re: [PATCH v3 1/7] iio: adc: hi8435: Holt HI-8435 threshold detector
@ 2015-08-11 12:21       ` Lars-Peter Clausen
  0 siblings, 0 replies; 40+ messages in thread
From: Lars-Peter Clausen @ 2015-08-11 12:21 UTC (permalink / raw)
  To: Jonathan Cameron, Vladimir Barinov
  Cc: Hartmut Knaack, Peter Meerwald, Rob Herring, Pawel Moll,
	Mark Rutland, Ian Campbell, Kumar Gala,
	linux-kernel-u79uwXL29TY76Z2rM5mHXA,
	linux-iio-u79uwXL29TY76Z2rM5mHXA,
	devicetree-u79uwXL29TY76Z2rM5mHXA,
	cory.tusar-J6Z/VSE8EyIAspv4Qr0y0gC/G2K4zDHf

On 08/08/2015 07:56 PM, Jonathan Cameron wrote:
> On 29/07/15 13:56, Vladimir Barinov wrote:
>> Add Holt threshold detector driver for HI-8435 chip
>>
>> Signed-off-by: Vladimir Barinov <vladimir.barinov-M4DtvfQ/ZS1MRgGoP+s0PdBPR1lH4CV8@public.gmane.org>
> Because it usually reads better that way, I review from the bottom.
> Might make more sense if you read the comments that way around ;)
> 
> I'm going to guess that the typical usecase for this device is in realtime
> systems where polling gives a nice predictable timing (hence the lack of any
> interrupt support).  These typically operate as 'scanning' devices
> (e.g. you update all state at approximately the same time, by reading
> one input after another in some predefined order).
> 
> Does the use of events actually map well to this use case?  You are taking
> something that (other than the results being a bit different) is basically
> a digital I/O interface and mapping it (sort of) to an interrupt chip
> when it isn't nothing of the sort.
> 
> I'd like more opinions on this, but my gut reaction is that we would
> be better making the normal buffered infrastructure handle this data
> properly rather than pushing it through the events intrastructure.
> 
> Your solution is neat and tidy in someways but feels like it is mashing
> data into a particular format.
> 
> To add to the complexity we could have debounce logic.  If we mapped to a
> fill the buffer with a scan mode, how would we handle this?
> My guess is that it would simply delay transistions.  Perhaps we even
> support reading both the value right now and the debounced value if that
> is possible?
> 
> However, here the debounce is all software.  To my mind, move this into
> userspace entirely. We aren't dealing with big data here so it's hardly
> going to be particularly challenging.
> 
> So my gut feeling is definitely we need to make the buffer infrastructure
> handle 1 bit values (in groups) then push this data out that way.
> 
> Still I would like other opinions on this!

Having thought about this a bit having support for event triggers seems like
a nice addition to me. When you think about it it is not too different from
buffer triggers. Some devices are able to generate their own trigger events
using a IRQ, others might need a software controlled trigger. You could
argue that the same is true for events. Having support for software based
event triggers e.g. allows support for devices which have events and have an
IRQ, but where the IRQ is simply not connected.

Whether it makes sense for this device is a different question though I guess.

Since this is a threshold detector events seem to be the right approach to
me. You could have similar hardware which actually generates IRQs, so you'd
have the same interface. Additionally if we expects changes only to occur at
a much lower rate than the polling rate only sending something to userspace
when it changes keeps the amount of data to transfer lower. On the other
hand if changes happen a lot using the event based approach would certainly
create a higher load. And another thing to consider is that some
applications might be interested in getting the raw data. So in conclusion,
I don't know ;). Both approaches seem sensible enough to me.

[...]
>> static void hi8435_debounce_work(struct work_struct *work)
>> +{
>> +	struct hi8435_priv *priv = container_of(work, struct hi8435_priv,
>> +						work.work);
>> +	struct iio_dev *idev = spi_get_drvdata(priv->spi);
>> +	u32 val;
>> +	int ret;
>> +
>> +	ret = hi8435_readl(priv, HI8435_SO31_0_REG, &val);
>> +	if (ret < 0)
>> +		return;
>> +
>> +	if (val == priv->debounce_val)
>> +		hi8435_iio_push_event(idev, val);
>> +	else
>> +		dev_warn(&priv->spi->dev, "filtered by software debounce");
> Definitely not.  Warning every time a standard event occurs?  Not
> a good idea.  Use the debug stuff if you want a way of knowing this
> happened, then it will silent under normal use.
> 
>> +}
>> +
>> +static irqreturn_t hi8435_trigger_handler(int irq, void *private)
>> +{
>> +	struct iio_poll_func *pf = private;
>> +	struct iio_dev *idev = pf->indio_dev;
>> +	struct hi8435_priv *priv = iio_priv(idev);
>> +	u32 val;
>> +	int ret;
>> +
>> +	ret = hi8435_readl(priv, HI8435_SO31_0_REG, &val);
>> +	if (ret < 0)
>> +		goto err_read;
>> +
>> +	if (priv->debounce_interval) {
>> +		priv->debounce_val = val;
>> +		schedule_delayed_work(&priv->work,
>> +				msecs_to_jiffies(priv->debounce_interval));
> This is another element that makes me doubt that the event interface
> is the way to do this.  I'd much rather we pushed the debouncing out
> to userspace where cleverer things can be done (and more adaptive things).
> Here to keep the event flow low you have to do it in the kernel.

Yes, debouncing should probably not be done in kernel space and the debounce
interval should not be a devicetree property.

> 
>> +	} else {
>> +		hi8435_iio_push_event(idev, val);
>> +	}
>> +
>> +err_read:
>> +	iio_trigger_notify_done(idev->trig);
>> +
>> +	return IRQ_HANDLED;
>> +}
>> +
>> +static void hi8435_parse_dt(struct hi8435_priv *priv)
>> +{
>> +	struct device_node *np = priv->spi->dev.of_node;
>> +	int ret;
>> +
>> +	ret = of_get_named_gpio(np, "holt,reset-gpios", 0);
>> +	priv->reset_gpio = ret < 0 ? 0 : ret;
> Silly question, but can gpio0 exist on a board?  I suspect you
> may need an additional variable to hold that this request failed
> rather than using the magic value of 0.

It can, but new stuff should just use the GPIO descriptor API where NULL can
be used to indicate a invalid GPIO.

- Lars

^ permalink raw reply	[flat|nested] 40+ messages in thread

* Re: [PATCH v3 1/7] iio: adc: hi8435: Holt HI-8435 threshold detector
  2015-08-08 17:56     ` Jonathan Cameron
  (?)
  (?)
@ 2015-08-11 14:37     ` Vladimir Barinov
  2015-08-16  9:00         ` Jonathan Cameron
  -1 siblings, 1 reply; 40+ messages in thread
From: Vladimir Barinov @ 2015-08-11 14:37 UTC (permalink / raw)
  To: Jonathan Cameron
  Cc: Hartmut Knaack, Lars-Peter Clausen, Peter Meerwald, Rob Herring,
	Pawel Moll, Mark Rutland, Ian Campbell, Kumar Gala, linux-kernel,
	linux-iio, devicetree, cory.tusar

Hi Jonathan,

Thank you for the review.

On 08.08.2015 20:56, Jonathan Cameron wrote:
> On 29/07/15 13:56, Vladimir Barinov wrote:
>> Add Holt threshold detector driver for HI-8435 chip
>>
>> Signed-off-by: Vladimir Barinov <vladimir.barinov@cogentembedded.com>
> Because it usually reads better that way, I review from the bottom.
> Might make more sense if you read the comments that way around ;)
I did try to match this way.
At some places the comments below the code, but in other places the 
comments are above the code :)
>
> I'm going to guess that the typical usecase for this device is in realtime
> systems where polling gives a nice predictable timing (hence the lack of any
> interrupt support).  These typically operate as 'scanning' devices
> (e.g. you update all state at approximately the same time, by reading
> one input after another in some predefined order).
Probably you are right about 8435 h/w design or there were other reason 
why this functionality was dropped in comparison to 8429 chip.
The Holt chip hi-8429 has both hardware interrupt and hardware debounce 
enable lines, but it has less amount of sensors (just 8 outputs).

>
> Does the use of events actually map well to this use case?  You are taking
> something that (other than the results being a bit different) is basically
> a digital I/O interface and mapping it (sort of) to an interrupt chip
> when it isn't nothing of the sort.
a) Using hardware point of view.

I was thinking that it matches the events a little bit more then buffer, 
because the chip senses for threshold changes
(passing threshold high or threshold low margins) and then notifies the 
upper layer.
The bad point here is that the chip does not have interrupt line for 
sensor state change (i.e. threshold passing).

If it would have the interrupt line for sensor state changes then the 
event interface map this case best way.

b) Using s/w point of view

I answer relying on current iio stack implementation.
I do understand that the  8435 chip is not the common/usual iio client 
like adc/dac/light/others, so I'm trying to
match the existent iio stack.

 From s/w point of view it does not matter much difference between event 
or buffer interface for this chip because
in provides 1-bit change and does not have it's own interrupt source line.

The event interface has generic interface to fill in falling/rising 
thresholds but buffer interface does not.

The buffer interface has already has the trigger poll func support but 
event interface does not.
So I've tried to implement the triggered event in iio stack.
BTW: Doesn't the triggered event support match the usecase with 
iio_interrupt_trigger? Or it does not make sense to have triggered 
events (even with irqtrig) at all?

>
> I'd like more opinions on this, but my gut reaction is that we would
> be better making the normal buffered infrastructure handle this data
> properly rather than pushing it through the events intrastructure.
>
> Your solution is neat and tidy in someways but feels like it is mashing
> data into a particular format.
Probably that's all because the chip lacks smth from buffer interface 
approach (the data should diferent the 1-bit) and
smth from event interface approach (lack of h/w interrupt line)

>
> To add to the complexity we could have debounce logic.  If we mapped to a
> fill the buffer with a scan mode, how would we handle this?
> My guess is that it would simply delay transistions.  Perhaps we even
> support reading both the value right now and the debounced value if that
> is possible?
I did handle it in the driver the same way in both cases (buffer and 
event interfaces):
event/buffer trigger issues interrupt and then the driver checks value 
right now and after delay (debounce_interval).

If we do not plan to put debounced values to buffer then we may cache 
during debouncing procedure and then
return via IIO_CHAN_INFO_DEBOUNCE_VALUE (or other info name). Probably 
even with timestamp.

>
> However, here the debounce is all software.  To my mind, move this into
> userspace entirely. We aren't dealing with big data here so it's hardly
> going to be particularly challenging.
I will drop it on your demand in favour to encourage chipmakers to 
provide h/w debounce,
but this was much easy/efficient to have it in kernel space, since
during run generic_buffer sample (or other) we got the new data and then 
we have to start timer, then timer expires
and we read raw values from sysfs manually to compare the one that came 
from buffer and one after debounce
delay.

>
> So my gut feeling is definitely we need to make the buffer infrastructure
> handle 1 bit values (in groups) then push this data out that way.
I will wait for your final decision.

>
> Still I would like other opinions on this!
>
> Jonathan
>
>> ---
>> Changes in version 2:
>> - Added file sysfs-bus-iio-adc-hi8435
>> - Changed naming from "discrete ADC" to "threshold detector"
>> - Replaced swab16p/swab32p with be16_to_cpup/be32_to_cpup
>> - Made *_show and *_store functions static
>> - moved out from iio buffers to iio events
>> - removed hi8436/hi8437 chips from the driver
>> - moved from debounce_soft_delay/enable to debounce_interval via
>>    IIO_CHAN_INFO_DEBOUNCE_TIME
>> - added name extention "comparator"
>> - moved threshold/hysteresis setup via generic iio event sysfs
>> - added software mask/unmask channel events
>> - added programming sensor outputs while in test mode via
>>    IIO_CHAN_INFO_RAW
>> - added channels .ext_info for programming sensing mode
>> Changes in version 3:
>> - fixed typos
>> - moved <linux/interrupt.h> to match alphabetic order
>> - fixed missed */event/* prefix in the ABI description
>> - added missed colon in sysfs-bus-iio-adc-hi8435 file after "What"
>> - moved in_voltageY_comparator_thresh_either_en and debounce_time
>>    to sysfs-bus-iio file
>> - added error checking for hi8435_write[b|w]
>>
>>   Documentation/ABI/testing/sysfs-bus-iio            |   1 +
>>   Documentation/ABI/testing/sysfs-bus-iio-adc-hi8435 |  61 ++
>>   drivers/iio/adc/Kconfig                            |  11 +
>>   drivers/iio/adc/Makefile                           |   1 +
>>   drivers/iio/adc/hi8435.c                           | 659 +++++++++++++++++++++
>>   5 files changed, 742 insertions(+)
>>   create mode 100644 Documentation/ABI/testing/sysfs-bus-iio-adc-hi8435
>>   create mode 100644 drivers/iio/adc/hi8435.c
>>
>> diff --git a/Documentation/ABI/testing/sysfs-bus-iio b/Documentation/ABI/testing/sysfs-bus-iio
>> index 70c9b1a..09eac44 100644
>> --- a/Documentation/ABI/testing/sysfs-bus-iio
>> +++ b/Documentation/ABI/testing/sysfs-bus-iio
>> @@ -572,6 +572,7 @@ What:		/sys/.../iio:deviceX/events/in_rot_from_north_magnetic_tilt_comp_thresh_r
>>   What:		/sys/.../iio:deviceX/events/in_rot_from_north_magnetic_tilt_comp_thresh_falling_en
>>   What:		/sys/.../iio:deviceX/events/in_rot_from_north_true_tilt_comp_thresh_rising_en
>>   What:		/sys/.../iio:deviceX/events/in_rot_from_north_true_tilt_comp_thresh_falling_en
>> +What:		/sys/.../iio:deviceX/events/in_voltageY_comparator_thresh_either_en
>>   What:		/sys/.../iio:deviceX/events/in_voltageY_supply_thresh_rising_en
>>   What:		/sys/.../iio:deviceX/events/in_voltageY_supply_thresh_falling_en
>>   What:		/sys/.../iio:deviceX/events/in_voltageY_thresh_rising_en
>> diff --git a/Documentation/ABI/testing/sysfs-bus-iio-adc-hi8435 b/Documentation/ABI/testing/sysfs-bus-iio-adc-hi8435
>> new file mode 100644
>> index 0000000..c59d81d
>> --- /dev/null
>> +++ b/Documentation/ABI/testing/sysfs-bus-iio-adc-hi8435
>> @@ -0,0 +1,61 @@
>> +What:		/sys/bus/iio/devices/iio:deviceX/in_voltageY_comparator_raw
>> +Date:		July 2015
>> +KernelVersion:	4.2.0
>> +Contact:	source@cogentembedded.com
>> +Description:
>> +		Read value is a voltage threshold measurement from channel Y.
>> +		Could be either 0 if sensor voltage is lower then low voltage
>> +		threshold or 1 if sensor voltage is higher then high voltage
>> +		threshold.
>> +		Write value is a programmed sensor output while in self test
>> +		mode. Could be either 0 or 1. The programmed value will be read
>> +		back if /sys/bus/iio/devices/iio:deviceX/test_enable is set to 1.
>> +
>> +What:		/sys/bus/iio/devices/iio:deviceX/test_enable
>> +Date:		July 2015
>> +KernelVersion:	4.2.0
>> +Contact:	source@cogentembedded.com
>> +Description:
>> +		Enable/disable the HI-8435 self test mode.
>> +		If enabled the in_voltageY_comparator_raw should be read back
>> +		accordingly to written value to in_voltageY_comparator_raw.
>> +
>> +What:		/sys/bus/iio/devices/iio:deviceX/in_voltageY_comparator_sensing_mode
>> +Date:		July 2015
>> +KernelVersion:	4.2.0
>> +Contact:	source@cogentembedded.com
>> +Description:
>> +		Program sensor type for threshold detector inputs.
>> +		Could be either "GND-Open" or "Supply-Open" mode. Y is a
>> +		threshold detector input channel. Channels 0..7, 8..15, 16..23
>> +		and 24..31 has common sensor types.
>> +
>> +What:		/sys/bus/iio/devices/iio:deviceX/events/in_voltageY_comparator_thresh_falling_value
>> +Date:		July 2015
>> +KernelVersion:	4.2.0
>> +Contact:	source@cogentembedded.com
>> +Description:
>> +		Channel Y low voltage threshold. If sensor input voltage goes lower then
>> +		this value then the threshold falling event is pushed.
>> +		Depending on in_voltageY_comparator_sensing_mode the low voltage threshold
>> +		is separately set for "GND-Open" and "Supply-Open" modes.
>> +		Channels 0..31 have common low threshold values, but could have different
>> +		sensing_modes.
>> +		The low voltage threshold range is between 2..21V.
>> +		Hysteresis between low and high thresholds can not be lower then 2 and
>> +		can not be odd.
>> +
>> +What:		/sys/bus/iio/devices/iio:deviceX/events/in_voltageY_comparator_thresh_rising_value
>> +Date:		July 2015
>> +KernelVersion:	4.2.0
>> +Contact:	source@cogentembedded.com
>> +Description:
>> +		Channel Y high voltage threshold. If sensor input voltage goes higher then
>> +		this value then the threshold rising event is pushed.
>> +		Depending on in_voltageY_comparator_sensing_mode the high voltage threshold
>> +		is separately set for "GND-Open" and "Supply-Open" modes.
>> +		Channels 0..31 have common high threshold values, but could have different
>> +		sensing_modes.
>> +		The high voltage threshold range is between 3..22V.
>> +		Hysteresis between low and high thresholds can not be lower then 2 and
>> +		can not be odd.
>> diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig
>> index eb0cd89..553c91e 100644
>> --- a/drivers/iio/adc/Kconfig
>> +++ b/drivers/iio/adc/Kconfig
>> @@ -170,6 +170,17 @@ config EXYNOS_ADC
>>   	  of SoCs for drivers such as the touchscreen and hwmon to use to share
>>   	  this resource.
>>   
>> +config HI8435
>> +	tristate "Holt Integrated Circuits HI-8435 threshold detector"
>> +	select IIO_TRIGGERED_EVENT
>> +	depends on SPI
>> +	help
>> +	  If you say yes here you get support for Holt Integrated Circuits
>> +	  HI-8435 chip.
>> +
>> +	  This driver can also be built as a module. If so, the module will be
>> +	  called hi8435.
>> +
>>   config LP8788_ADC
>>   	tristate "LP8788 ADC driver"
>>   	depends on MFD_LP8788
>> diff --git a/drivers/iio/adc/Makefile b/drivers/iio/adc/Makefile
>> index a096210..00f367aa 100644
>> --- a/drivers/iio/adc/Makefile
>> +++ b/drivers/iio/adc/Makefile
>> @@ -19,6 +19,7 @@ obj-$(CONFIG_BERLIN2_ADC) += berlin2-adc.o
>>   obj-$(CONFIG_DA9150_GPADC) += da9150-gpadc.o
>>   obj-$(CONFIG_CC10001_ADC) += cc10001_adc.o
>>   obj-$(CONFIG_EXYNOS_ADC) += exynos_adc.o
>> +obj-$(CONFIG_HI8435) += hi8435.o
>>   obj-$(CONFIG_LP8788_ADC) += lp8788_adc.o
>>   obj-$(CONFIG_MAX1027) += max1027.o
>>   obj-$(CONFIG_MAX1363) += max1363.o
>> diff --git a/drivers/iio/adc/hi8435.c b/drivers/iio/adc/hi8435.c
>> new file mode 100644
>> index 0000000..f3dab5a
>> --- /dev/null
>> +++ b/drivers/iio/adc/hi8435.c
>> @@ -0,0 +1,659 @@
>> +/*
>> + * Holt Integrated Circuits HI-8435 threshold detector driver
>> + *
>> + * Copyright (C) 2015 Zodiac Inflight Innovations
>> + * Copyright (C) 2015 Cogent Embedded, Inc.
>> + *
>> + * 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.
>> + */
>> +
>> +#include <linux/delay.h>
>> +#include <linux/iio/events.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/triggered_event.h>
>> +#include <linux/interrupt.h>
>> +#include <linux/module.h>
>> +#include <linux/of.h>
>> +#include <linux/of_device.h>
>> +#include <linux/of_gpio.h>
>> +#include <linux/spi/spi.h>
>> +#include <linux/workqueue.h>
>> +
>> +#define DRV_NAME "hi8435"
>> +
>> +/* Register offsets for HI-8435 */
>> +#define HI8435_CTRL_REG			0x02
>> +#define HI8435_PSEN_REG			0x04
>> +#define HI8435_TMDATA_REG		0x1E
>> +#define HI8435_GOCENHYS_REG		0x3A
>> +#define HI8435_SOCENHYS_REG		0x3C
> Hmm. These aren't in the docs that I can immediately acquire...
> Any chance of some more meaningful naming?
I did follow the register naming in the chip datasheet:
http://www.holtic.com/products/3081-hi-8435.aspx

The SO7_0 means "Sensor status bank 0 register: SO<7:0>".
Does HI8435_STATUS_BANK0_REG work?
Or should I just add comments instead of changing definition?
>> +#define HI8435_SO7_0_REG		0x10
>> +#define HI8435_SO15_8_REG		0x12
>   +#define HI8435_SO23_16_REG		0x14
>> +#define HI8435_SO31_24_REG		0x16
>> +#define HI8435_SO31_0_REG		0x78
>> +
>> +#define HI8435_WRITE_OPCODE		0x00
>> +#define HI8435_READ_OPCODE		0x80
>> +
>> +/* CTRL register bits */
>> +#define HI8435_CTRL_TEST		0x01
>> +#define HI8435_CTRL_SRST		0x02
>> +
>> +#define HI8435_DEBOUNCE_DELAY_MAX	1000	/* msec */
>> +#define HI8435_DEBOUNCE_DELAY_DEF	100	/* msec */
>> +
>> +struct hi8435_priv {
>> +	struct spi_device *spi;
>> +	struct mutex lock;
>> +	struct delayed_work work;
>> +
>> +	int reset_gpio;
>> +	int debounce_interval; /* msec */
>> +	u32 debounce_val; /* prev value to compare during software debounce */
>> +
>> +	unsigned long event_scan_mask; /* soft mask/unmask channels events */
>> +	unsigned int event_prev_val;
>> +
>> +	unsigned threshold_lo[2]; /* GND-Open and Supply-Open thresholds */
>> +	unsigned threshold_hi[2]; /* GND-Open and Supply-Open threshold */
>> +	u8 reg_buffer[4] ____cacheline_aligned;
>> +};
>> +
>> +static int hi8435_readb(struct hi8435_priv *priv, u8 reg, u8 *val)
>> +{
>> +	reg |= HI8435_READ_OPCODE;
>> +	return spi_write_then_read(priv->spi, &reg, 1, val, 1);
>> +}
>> +
>> +static int hi8435_readw(struct hi8435_priv *priv, u8 reg, u16 *val)
>> +{
>> +	int ret;
>> +
>> +	reg |= HI8435_READ_OPCODE;
>> +	ret = spi_write_then_read(priv->spi, &reg, 1, val, 2);
>> +	*val = be16_to_cpup(val);
>> +
>> +	return ret;
>> +}
>> +
>> +static int hi8435_readl(struct hi8435_priv *priv, u8 reg, u32 *val)
>> +{
>> +	int ret;
>> +
>> +	reg |= HI8435_READ_OPCODE;
>> +	ret = spi_write_then_read(priv->spi, &reg, 1, val, 4);
>> +	*val = be32_to_cpup(val);
>> +
>> +	return ret;
>> +}
>> +
>> +static int hi8435_writeb(struct hi8435_priv *priv, u8 reg, u8 val)
>> +{
>> +	priv->reg_buffer[0] = reg | HI8435_WRITE_OPCODE;
>> +	priv->reg_buffer[1] = val;
>> +
>> +	return spi_write(priv->spi, priv->reg_buffer, 2);
>> +}
>> +
>> +static int hi8435_writew(struct hi8435_priv *priv, u8 reg, u16 val)
>> +{
>> +	priv->reg_buffer[0] = reg | HI8435_WRITE_OPCODE;
>> +	priv->reg_buffer[1] = (val >> 8) & 0xff;
>> +	priv->reg_buffer[2] = val & 0xff;
>> +
>> +	return spi_write(priv->spi, priv->reg_buffer, 3);
>> +}
>> +
>> +static ssize_t hi8435_test_enable_show(struct device *dev,
>> +				       struct device_attribute *attr, char *buf)
>> +{
>> +	struct hi8435_priv *priv = iio_priv(dev_to_iio_dev(dev));
>> +	int ret;
>> +	u8 reg;
>> +
>> +	ret = hi8435_readb(priv, HI8435_CTRL_REG, &reg);
>> +	if (ret < 0)
>> +		return ret;
>> +
>> +	return sprintf(buf, "%d\n", reg & HI8435_CTRL_TEST);
>> +}
>> +
>> +static ssize_t hi8435_test_enable_store(struct device *dev,
>> +					struct device_attribute *attr,
>> +					const char *buf, size_t len)
>> +{
>> +	struct hi8435_priv *priv = iio_priv(dev_to_iio_dev(dev));
>> +	unsigned int val;
>> +	int ret;
>> +
>> +	ret = kstrtouint(buf, 10, &val);
>> +	if (ret)
>> +		return ret;
>> +
>> +	ret = hi8435_writeb(priv, HI8435_CTRL_REG, val ? HI8435_CTRL_TEST : 0);
>> +	if (ret < 0)
>> +		return ret;
>> +
>> +	return len;
>> +}
>> +
>> +static IIO_DEVICE_ATTR(test_enable, S_IRUGO | S_IWUSR,
>> +	hi8435_test_enable_show, hi8435_test_enable_store, 0);
>> +
>> +static struct attribute *hi8435_attributes[] = {
>> +	&iio_dev_attr_test_enable.dev_attr.attr,
>> +	NULL,
>> +};
>> +
>> +static struct attribute_group hi8435_attribute_group = {
>> +	.attrs = hi8435_attributes,
>> +};
>> +
>> +static int hi8435_read_raw(struct iio_dev *idev,
>> +			   const struct iio_chan_spec *chan,
>> +			   int *val, int *val2, long mask)
>> +{
>> +	struct hi8435_priv *priv = iio_priv(idev);
>> +	int ret;
>> +
>> +	switch (mask) {
>> +	case IIO_CHAN_INFO_RAW:
>> +		ret = hi8435_readl(priv, HI8435_SO31_0_REG, val);
>> +		if (ret < 0)
>> +			return ret;
>> +		*val = !!(*val & BIT(chan->channel));
>> +		return IIO_VAL_INT;
>> +	case IIO_CHAN_INFO_DEBOUNCE_TIME:
>> +		*val = priv->debounce_interval;
>> +		return IIO_VAL_INT;
>> +	default:
>> +		return -EINVAL;
>> +	}
>> +}
>> +
>> +static int hi8435_write_raw(struct iio_dev *idev,
>> +			    const struct iio_chan_spec *chan,
>> +			    int val, int val2, long mask)
>> +{
>> +	struct hi8435_priv *priv = iio_priv(idev);
>> +
>> +	switch (mask) {
>> +	case IIO_CHAN_INFO_RAW:
>> +		/* program sensors outputs in test mode */
>> +		return hi8435_writeb(priv, HI8435_TMDATA_REG, val ? 0x1 : 0x2);
>> +	case IIO_CHAN_INFO_DEBOUNCE_TIME:
>> +		if (val < 0)
>> +			return -EINVAL;
>> +		if (val > HI8435_DEBOUNCE_DELAY_MAX)
>> +			val = HI8435_DEBOUNCE_DELAY_MAX;
>> +		priv->debounce_interval = val;
>> +		return 0;
>> +	default:
>> +		return -EINVAL;
>> +	}
>> +}
>> +
>> +static int hi8435_read_event_config(struct iio_dev *idev,
>> +				    const struct iio_chan_spec *chan,
>> +				    enum iio_event_type type,
>> +				    enum iio_event_direction dir)
>> +{
>> +	struct hi8435_priv *priv = iio_priv(idev);
>> +
>> +	return !!(priv->event_scan_mask & BIT(chan->channel));
>> +}
>> +
>> +static int hi8435_write_event_config(struct iio_dev *idev,
>> +				     const struct iio_chan_spec *chan,
>> +				     enum iio_event_type type,
>> +				     enum iio_event_direction dir, int state)
>> +{
>> +	struct hi8435_priv *priv = iio_priv(idev);
>> +
>> +	priv->event_scan_mask &= ~BIT(chan->channel);
>> +	if (state)
>> +		priv->event_scan_mask |= BIT(chan->channel);
>> +
>> +	return 0;
>> +}
>> +
>> +static int hi8435_read_event_value(struct iio_dev *idev,
>> +				   const struct iio_chan_spec *chan,
>> +				   enum iio_event_type type,
>> +				   enum iio_event_direction dir,
>> +				   enum iio_event_info info,
>> +				   int *val, int *val2)
>> +{
>> +	struct hi8435_priv *priv = iio_priv(idev);
>> +	int ret;
>> +	u8 mode, psen;
>> +	u16 reg;
>> +
>> +	ret = hi8435_readb(priv, HI8435_PSEN_REG, &psen);
>> +	if (ret < 0)
>> +		return ret;
>> +
>> +	/* Supply-Open or GND-Open sensing mode */
>> +	mode = !!(psen & BIT(chan->channel / 8));
>> +
>> +	ret = hi8435_readw(priv, mode ? HI8435_SOCENHYS_REG :
>> +				 HI8435_GOCENHYS_REG, &reg);
>> +	if (ret < 0)
>> +		return ret;
>> +
>> +	if (dir == IIO_EV_DIR_FALLING)
>> +		*val = ((reg & 0xff) - (reg >> 8)) / 2;
>> +
>> +	if (dir == IIO_EV_DIR_RISING)
>> +		*val = ((reg & 0xff) + (reg >> 8)) / 2;
>> +
>> +	return IIO_VAL_INT;
>> +}
>> +
>> +static int hi8435_write_event_value(struct iio_dev *idev,
>> +				    const struct iio_chan_spec *chan,
>> +				    enum iio_event_type type,
>> +				    enum iio_event_direction dir,
>> +				    enum iio_event_info info,
>> +				    int val, int val2)
>> +{
>> +	struct hi8435_priv *priv = iio_priv(idev);
>> +	int ret;
>> +	u8 mode, psen;
>> +	u16 reg;
>> +
>> +	ret = hi8435_readb(priv, HI8435_PSEN_REG, &psen);
>> +	if (ret < 0)
>> +		return ret;
>> +
>> +	/* Supply-Open or GND-Open sensing mode */
>> +	mode = !!(psen & BIT(chan->channel / 8));
>> +
>> +	ret = hi8435_readw(priv, mode ? HI8435_SOCENHYS_REG :
>> +				 HI8435_GOCENHYS_REG, &reg);
>> +	if (ret < 0)
>> +		return ret;
>> +
>> +	if (dir == IIO_EV_DIR_FALLING) {
>> +		/* falling threshold range 2..21V, hysteresis minimum 2V */
>> +		if (val < 2 || val > 21 || (val + 1) >= priv->threshold_hi[mode])
>> +			return -EINVAL;
>> +
>> +		if (val == priv->threshold_lo[mode])
>> +			return 0;
>> +
>> +		priv->threshold_lo[mode] = val;
>> +
>> +		/* hysteresis must not be odd */
>> +		if ((priv->threshold_hi[mode] - priv->threshold_lo[mode]) % 2)
>> +			priv->threshold_hi[mode]--;
>> +	}
>> +
>> +	if (dir == IIO_EV_DIR_RISING) {
>> +		/* rising threshold range 3..22V, hysteresis minimum 2V */
>> +		if (val < 3 || val > 22 || val <= (priv->threshold_lo[mode] + 1))
>> +			return -EINVAL;
>> +
>> +		if (val == priv->threshold_hi[mode])
>> +			return 0;
>> +
>> +		priv->threshold_hi[mode] = val;
>> +
>> +		/* hysteresis must not be odd */
>> +		if ((priv->threshold_hi[mode] - priv->threshold_lo[mode]) % 2)
>> +			priv->threshold_lo[mode]++;
>> +	}
>> +
>> +	/* program thresholds */
>> +	mutex_lock(&priv->lock);
>> +
>> +	ret = hi8435_readw(priv, mode ? HI8435_SOCENHYS_REG :
>> +				 HI8435_GOCENHYS_REG, &reg);
>> +	if (ret < 0) {
>> +		mutex_unlock(&priv->lock);
>> +		return ret;
>> +	}
>> +
>> +	/* hysteresis */
>> +	reg = priv->threshold_hi[mode] - priv->threshold_lo[mode];
>> +	reg <<= 8;
>> +	/* threshold center */
>> +	reg |= (priv->threshold_hi[mode] + priv->threshold_lo[mode]);
>> +
>> +	ret = hi8435_writew(priv, mode ? HI8435_SOCENHYS_REG :
>> +				  HI8435_GOCENHYS_REG, reg);
>> +
>> +	mutex_unlock(&priv->lock);
>> +
>> +	return ret;
>> +}
>> +
>> +static const struct iio_event_spec hi8435_events[] = {
>> +	{
>> +		.type = IIO_EV_TYPE_THRESH,
>> +		.dir = IIO_EV_DIR_RISING,
>> +		.mask_separate = BIT(IIO_EV_INFO_VALUE),
>> +	}, {
>> +		.type = IIO_EV_TYPE_THRESH,
>> +		.dir = IIO_EV_DIR_FALLING,
>> +		.mask_separate = BIT(IIO_EV_INFO_VALUE),
>> +	}, {
>> +		.type = IIO_EV_TYPE_THRESH,
>> +		.dir = IIO_EV_DIR_EITHER,
>> +		.mask_separate = BIT(IIO_EV_INFO_ENABLE),
>> +	},
>> +};
>> +
>> +static int hi8435_get_sensing_mode(struct iio_dev *idev,
>> +				   const struct iio_chan_spec *chan)
>> +{
>> +	struct hi8435_priv *priv = iio_priv(idev);
>> +	int ret;
>> +	u8 reg;
>> +
>> +	ret = hi8435_readb(priv, HI8435_PSEN_REG, &reg);
>> +	if (ret < 0)
>> +		return ret;
>> +
>> +	return !!(reg & BIT(chan->channel / 8));
>> +}
>> +
>> +static int hi8435_set_sensing_mode(struct iio_dev *idev,
>> +				   const struct iio_chan_spec *chan,
>> +				   unsigned int mode)
>> +{
>> +	struct hi8435_priv *priv = iio_priv(idev);
>> +	int ret;
>> +	u8 reg;
>> +
>> +	mutex_lock(&priv->lock);
>> +
>> +	ret = hi8435_readb(priv, HI8435_PSEN_REG, &reg);
>> +	if (ret < 0) {
>> +		mutex_unlock(&priv->lock);
>> +		return ret;
>> +	}
>> +
>> +	reg &= ~BIT(chan->channel / 8);
>> +	if (mode)
>> +		reg |= BIT(chan->channel / 8);
>> +
>> +	ret = hi8435_writeb(priv, HI8435_PSEN_REG, reg);
>> +
>> +	mutex_unlock(&priv->lock);
>> +
>> +	return ret;
>> +}
>> +
>> +static const char * const hi8435_sensing_modes[] = { "GND-Open",
>> +						     "Supply-Open" };
>> +
>> +static const struct iio_enum hi8435_sensing_mode = {
>> +	.items = hi8435_sensing_modes,
>> +	.num_items = ARRAY_SIZE(hi8435_sensing_modes),
>> +	.get = hi8435_get_sensing_mode,
>> +	.set = hi8435_set_sensing_mode,
>> +};
>> +
>> +static const struct iio_chan_spec_ext_info hi8435_ext_info[] = {
>> +	IIO_ENUM("sensing_mode", IIO_SEPARATE, &hi8435_sensing_mode),
>> +	{},
>> +};
>> +
>> +#define HI8435_VOLTAGE_CHANNEL(num)					\
>> +{									\
>> +	.type = IIO_VOLTAGE,						\
>> +	.indexed = 1,							\
>> +	.channel = num,							\
>> +	.info_mask_separate = BIT(IIO_CHAN_INFO_RAW),			\
>> +	.info_mask_shared_by_all = BIT(IIO_CHAN_INFO_DEBOUNCE_TIME),	\
>> +	.event_spec = hi8435_events,					\
>> +	.num_event_specs = ARRAY_SIZE(hi8435_events),			\
>> +	.ext_info = hi8435_ext_info,					\
>> +	.extend_name = "comparator"					\
> I'm really not sure about this approach.  Can see where you are coming
> from. We already have an events interface supporting these things so
> why not use it?   I'm just doubtful it is an efficient option or a clean
> interface (would like more opinions on this!)
Sorry, I did not understand here.
Is it about .extend_name ?
I did add "comparator" extention to have attributes look like 
"in_voltage0_comparator_raw"
>> +}
>> +
>> +static const struct iio_chan_spec hi8435_channels[] = {
>> +	HI8435_VOLTAGE_CHANNEL(0),
>> +	HI8435_VOLTAGE_CHANNEL(1),
>> +	HI8435_VOLTAGE_CHANNEL(2),
>> +	HI8435_VOLTAGE_CHANNEL(3),
>> +	HI8435_VOLTAGE_CHANNEL(4),
>> +	HI8435_VOLTAGE_CHANNEL(5),
>> +	HI8435_VOLTAGE_CHANNEL(6),
>> +	HI8435_VOLTAGE_CHANNEL(7),
>> +	HI8435_VOLTAGE_CHANNEL(8),
>> +	HI8435_VOLTAGE_CHANNEL(9),
>> +	HI8435_VOLTAGE_CHANNEL(10),
>> +	HI8435_VOLTAGE_CHANNEL(11),
>> +	HI8435_VOLTAGE_CHANNEL(12),
>> +	HI8435_VOLTAGE_CHANNEL(13),
>> +	HI8435_VOLTAGE_CHANNEL(14),
>> +	HI8435_VOLTAGE_CHANNEL(15),
>> +	HI8435_VOLTAGE_CHANNEL(16),
>> +	HI8435_VOLTAGE_CHANNEL(17),
>> +	HI8435_VOLTAGE_CHANNEL(18),
>> +	HI8435_VOLTAGE_CHANNEL(19),
>> +	HI8435_VOLTAGE_CHANNEL(20),
>> +	HI8435_VOLTAGE_CHANNEL(21),
>> +	HI8435_VOLTAGE_CHANNEL(22),
>> +	HI8435_VOLTAGE_CHANNEL(23),
>> +	HI8435_VOLTAGE_CHANNEL(24),
>> +	HI8435_VOLTAGE_CHANNEL(25),
>> +	HI8435_VOLTAGE_CHANNEL(26),
>> +	HI8435_VOLTAGE_CHANNEL(27),
>> +	HI8435_VOLTAGE_CHANNEL(28),
>> +	HI8435_VOLTAGE_CHANNEL(29),
>> +	HI8435_VOLTAGE_CHANNEL(30),
>> +	HI8435_VOLTAGE_CHANNEL(31),
>> +	IIO_CHAN_SOFT_TIMESTAMP(32),
>> +};
>> +
>> +static const struct iio_info hi8435_info = {
>> +	.driver_module = THIS_MODULE,
>> +	.attrs = &hi8435_attribute_group,
>> +	.read_raw = hi8435_read_raw,
>> +	.write_raw = hi8435_write_raw,
>> +	.read_event_config = &hi8435_read_event_config,
>> +	.write_event_config = hi8435_write_event_config,
>> +	.read_event_value = &hi8435_read_event_value,
>> +	.write_event_value = &hi8435_write_event_value,
>> +};
>> +
>> +static void hi8435_iio_push_event(struct iio_dev *idev, unsigned int val)
>> +{
>> +	struct hi8435_priv *priv = iio_priv(idev);
>> +	enum iio_event_direction dir;
>> +	unsigned int i;
>> +	unsigned int status = priv->event_prev_val ^ val;
>> +
>> +	if (!status)
>> +		return;
>> +
>> +	for_each_set_bit(i, &priv->event_scan_mask, 32) {
>> +		if (!(status & BIT(i)))
>> +			continue;
>> +
>> +		dir = val & BIT(i) ? IIO_EV_DIR_RISING : IIO_EV_DIR_FALLING;
>> +
>> +		iio_push_event(idev,
>> +			       IIO_UNMOD_EVENT_CODE(IIO_VOLTAGE, i,
>> +						    IIO_EV_TYPE_THRESH, dir),
>> +			       iio_get_time_ns());
>> +	}
>> +
>> +	priv->event_prev_val = val;
>> +}
>> +
>> +static void hi8435_debounce_work(struct work_struct *work)
>> +{
>> +	struct hi8435_priv *priv = container_of(work, struct hi8435_priv,
>> +						work.work);
>> +	struct iio_dev *idev = spi_get_drvdata(priv->spi);
>> +	u32 val;
>> +	int ret;
>> +
>> +	ret = hi8435_readl(priv, HI8435_SO31_0_REG, &val);
>> +	if (ret < 0)
>> +		return;
>> +
>> +	if (val == priv->debounce_val)
>> +		hi8435_iio_push_event(idev, val);
>> +	else
>> +		dev_warn(&priv->spi->dev, "filtered by software debounce");
> Definitely not.  Warning every time a standard event occurs?  Not
> a good idea.  Use the debug stuff if you want a way of knowing this
> happened, then it will silent under normal use.
I'd say it is not standard event, but occasional/debounce event.
Anyway I agree with you.

>> +}
>> +
>> +static irqreturn_t hi8435_trigger_handler(int irq, void *private)
>> +{
>> +	struct iio_poll_func *pf = private;
>> +	struct iio_dev *idev = pf->indio_dev;
>> +	struct hi8435_priv *priv = iio_priv(idev);
>> +	u32 val;
>> +	int ret;
>> +
>> +	ret = hi8435_readl(priv, HI8435_SO31_0_REG, &val);
>> +	if (ret < 0)
>> +		goto err_read;
>> +
>> +	if (priv->debounce_interval) {
>> +		priv->debounce_val = val;
>> +		schedule_delayed_work(&priv->work,
>> +				msecs_to_jiffies(priv->debounce_interval));
> This is another element that makes me doubt that the event interface
> is the way to do this.  I'd much rather we pushed the debouncing out
> to userspace where cleverer things can be done (and more adaptive things).
> Here to keep the event flow low you have to do it in the kernel.
Yes, it is helpful for reducing the data flow (buffer data or event data)
Many kernel drivers support s/w event debouncing, f.e. usb cable 
plug-in, or other connectors.

>> +	} else {
>> +		hi8435_iio_push_event(idev, val);
>> +	}
>> +
>> +err_read:
>> +	iio_trigger_notify_done(idev->trig);
>> +
>> +	return IRQ_HANDLED;
>> +}
>> +
>> +static void hi8435_parse_dt(struct hi8435_priv *priv)
>> +{
>> +	struct device_node *np = priv->spi->dev.of_node;
>> +	int ret;
>> +
>> +	ret = of_get_named_gpio(np, "holt,reset-gpios", 0);
>> +	priv->reset_gpio = ret < 0 ? 0 : ret;
> Silly question, but can gpio0 exist on a board?  I suspect you
> may need an additional variable to hold that this request failed
> rather than using the magic value of 0.
I will do handle this case, thank you.
>> +
>> +	ret = of_property_read_u32(np, "holt,debounce-interval",
>> +				   &priv->debounce_interval);
>> +	if (ret)
>> +		priv->debounce_interval = 0;
>> +	if (priv->debounce_interval > HI8435_DEBOUNCE_DELAY_MAX)
>> +		priv->debounce_interval = HI8435_DEBOUNCE_DELAY_MAX;
>> +}
>> +
>> +static int hi8435_probe(struct spi_device *spi)
>> +{
>> +	struct iio_dev *idev;
>> +	struct hi8435_priv *priv;
>> +	int ret;
>> +
>> +	idev = devm_iio_device_alloc(&spi->dev, sizeof(*priv));
>> +	if (!idev)
>> +		return -ENOMEM;
>> +
>> +	priv = iio_priv(idev);
>> +	priv->spi = spi;
>> +
>> +	if (spi->dev.of_node)
>> +		hi8435_parse_dt(priv);
>> +
>> +	spi_set_drvdata(spi, idev);
>> +	mutex_init(&priv->lock);
>> +	INIT_DELAYED_WORK(&priv->work, hi8435_debounce_work);
>> +
>> +	idev->dev.parent	= &spi->dev;
>> +	idev->name		= spi_get_device_id(spi)->name;
>> +	idev->modes		= INDIO_DIRECT_MODE;
>> +	idev->info		= &hi8435_info;
>> +	idev->channels		= hi8435_channels;
>> +	idev->num_channels	= ARRAY_SIZE(hi8435_channels);
>> +
>> +	if (priv->reset_gpio) {
>> +		ret = devm_gpio_request(&spi->dev, priv->reset_gpio, idev->name);
>> +		if (!ret) {
>> +			/* chip hardware reset */
>> +			gpio_direction_output(priv->reset_gpio, 0);
>> +			udelay(5);
>> +			gpio_direction_output(priv->reset_gpio, 1);
>> +		}
>> +	} else {
>> +		/* chip software reset */
>> +		hi8435_writeb(priv, HI8435_CTRL_REG, HI8435_CTRL_SRST);
>> +		/* get out from reset state */
>> +		hi8435_writeb(priv, HI8435_CTRL_REG, 0);
>> +	}
>> +
>> +	/* unmask all events */
>> +	priv->event_scan_mask = ~(0);
>> +	/* initialize default thresholds */
>> +	priv->threshold_lo[0] = priv->threshold_lo[1] = 2;
>> +	priv->threshold_hi[0] = priv->threshold_hi[1] = 4;
> These seem very random.  Why these values or
The aim for thresholds initialization is that they are in undefined 
state after h/w reset and
the driver allows userspace to change thresholds (high or low) separately.

There is a restriction in the chip - the hysteresis can not be even.
If the hysteresis is set to even value then it get's into lock state and 
not functional anymore. (I've figured this out empirically).
So we need to initialize thresholds (aka hysteresis + it's center) to 
some initial value to be able to change
high and low thresholds separately and avoid even values for hysteresis.

I will also add these comments.
>> +	hi8435_writew(priv, HI8435_GOCENHYS_REG, 0x206);
> What is 0x206?  Again, I guess a default. I'd like to see a comment
> here saying what real world value it corresponds to.
Yes it is default values.

The 0x206 is a code for HI threshold voltage = 4V and low voltage 
threshold = 2V.
The register HI8435_SOCENHYS_REG describes the voltage hysteresis of the 
sensor and the hysteresis center.
The 0x206 is an encoded value for 2V-to-4V voltage threshold.

I will add comments.
>> +	hi8435_writew(priv, HI8435_SOCENHYS_REG, 0x206);
>> +
> I am as yet unconvinced that using triggers to poll events is a terribly
> good idea.  Feels rather like we are doing this due to a deficiency in
> the buffer interface than because sending events makes sense here.
Actually the first version of this driver was against the buffer interface.
I did try to use your tip and switched to event interface :)

Do you think I should switch to buffer interface back?
>> +	ret = iio_triggered_event_setup(idev, NULL, hi8435_trigger_handler);
>> +	if (ret)
>> +		return ret;
>> +
>> +	ret = iio_device_register(idev);
>> +	if (ret < 0) {
>> +		dev_err(&spi->dev, "unable to register device\n");
>> +		goto unregister_triggered_event;
>> +	}
>> +
>> +	return 0;
>> +
>> +unregister_triggered_event:
>> +	iio_triggered_event_cleanup(idev);
>> +	return ret;
>> +}
>> +
>> +static int hi8435_remove(struct spi_device *spi)
>> +{
>> +	struct iio_dev *idev = spi_get_drvdata(spi);
>> +	struct hi8435_priv *priv = iio_priv(idev);
>> +
>> +	cancel_delayed_work_sync(&priv->work);
>> +	iio_device_unregister(idev);
>> +	iio_triggered_event_cleanup(idev);
>> +
>> +	return 0;
>> +}
>> +
>> +static const struct of_device_id hi8435_dt_ids[] = {
>> +	{ .compatible = "holt,hi8435" },
>> +	{},
>> +};
>> +MODULE_DEVICE_TABLE(of, hi8435_dt_ids);
>> +
>> +static const struct spi_device_id hi8435_id[] = {
>> +	{ "hi8435", 0},
>> +	{ }
>> +};
>> +MODULE_DEVICE_TABLE(spi, hi8435_id);
>> +
>> +static struct spi_driver hi8435_driver = {
>> +	.driver	= {
>> +		.name		= DRV_NAME,
>> +		.of_match_table	= of_match_ptr(hi8435_dt_ids),
>> +	},
>> +	.probe		= hi8435_probe,
>> +	.remove		= hi8435_remove,
>> +	.id_table	= hi8435_id,
>> +};
>> +module_spi_driver(hi8435_driver);
>> +
>> +MODULE_LICENSE("GPL");
>> +MODULE_AUTHOR("Vladimir Barinov");
>> +MODULE_DESCRIPTION("HI-8435 threshold detector");
>>


^ permalink raw reply	[flat|nested] 40+ messages in thread

* Re: [PATCH v3 1/7] iio: adc: hi8435: Holt HI-8435 threshold detector
  2015-08-11 12:21       ` Lars-Peter Clausen
  (?)
@ 2015-08-11 17:01       ` Jonathan Cameron
  -1 siblings, 0 replies; 40+ messages in thread
From: Jonathan Cameron @ 2015-08-11 17:01 UTC (permalink / raw)
  To: Lars-Peter Clausen, Jonathan Cameron, Vladimir Barinov
  Cc: Hartmut Knaack, Peter Meerwald, Rob Herring, Pawel Moll,
	Mark Rutland, Ian Campbell, Kumar Gala, linux-kernel, linux-iio,
	devicetree, cory.tusar



On 11 August 2015 13:21:08 BST, Lars-Peter Clausen <lars@metafoo.de> wrote:
>On 08/08/2015 07:56 PM, Jonathan Cameron wrote:
>> On 29/07/15 13:56, Vladimir Barinov wrote:
>>> Add Holt threshold detector driver for HI-8435 chip
>>>
>>> Signed-off-by: Vladimir Barinov
><vladimir.barinov@cogentembedded.com>
>> Because it usually reads better that way, I review from the bottom.
>> Might make more sense if you read the comments that way around ;)
>> 
>> I'm going to guess that the typical usecase for this device is in
>realtime
>> systems where polling gives a nice predictable timing (hence the lack
>of any
>> interrupt support).  These typically operate as 'scanning' devices
>> (e.g. you update all state at approximately the same time, by reading
>> one input after another in some predefined order).
>> 
>> Does the use of events actually map well to this use case?  You are
>taking
>> something that (other than the results being a bit different) is
>basically
>> a digital I/O interface and mapping it (sort of) to an interrupt chip
>> when it isn't nothing of the sort.
>> 
>> I'd like more opinions on this, but my gut reaction is that we would
>> be better making the normal buffered infrastructure handle this data
>> properly rather than pushing it through the events intrastructure.
>> 
>> Your solution is neat and tidy in someways but feels like it is
>mashing
>> data into a particular format.
>> 
>> To add to the complexity we could have debounce logic.  If we mapped
>to a
>> fill the buffer with a scan mode, how would we handle this?
>> My guess is that it would simply delay transistions.  Perhaps we even
>> support reading both the value right now and the debounced value if
>that
>> is possible?
>> 
>> However, here the debounce is all software.  To my mind, move this
>into
>> userspace entirely. We aren't dealing with big data here so it's
>hardly
>> going to be particularly challenging.
>> 
>> So my gut feeling is definitely we need to make the buffer
>infrastructure
>> handle 1 bit values (in groups) then push this data out that way.
>> 
>> Still I would like other opinions on this!
>
>Having thought about this a bit having support for event triggers seems
>like
>a nice addition to me. When you think about it it is not too different
>from
>buffer triggers. Some devices are able to generate their own trigger
>events
>using a IRQ, others might need a software controlled trigger. You could
>argue that the same is true for events. Having support for software
>based
>event triggers e.g. allows support for devices which have events and
>have an
>IRQ, but where the IRQ is simply not connected.
>
>Whether it makes sense for this device is a different question though I
>guess.
>
>Since this is a threshold detector events seem to be the right approach
>to
>me. You could have similar hardware which actually generates IRQs, so
>you'd
>have the same interface. Additionally if we expects changes only to
>occur at
>a much lower rate than the polling rate only sending something to
>userspace
>when it changes keeps the amount of data to transfer lower. On the
>other
>hand if changes happen a lot using the event based approach would
>certainly
>create a higher load. And another thing to consider is that some
>applications might be interested in getting the raw data. So in
>conclusion,
>I don't know ;). Both approaches seem sensible enough to me.

I am coming around to the view that events make some sense in this case.

Definitely also want 1 bit channel support in IIO though.
>
>[...]
>>> static void hi8435_debounce_work(struct work_struct *work)
>>> +{
>>> +	struct hi8435_priv *priv = container_of(work, struct hi8435_priv,
>>> +						work.work);
>>> +	struct iio_dev *idev = spi_get_drvdata(priv->spi);
>>> +	u32 val;
>>> +	int ret;
>>> +
>>> +	ret = hi8435_readl(priv, HI8435_SO31_0_REG, &val);
>>> +	if (ret < 0)
>>> +		return;
>>> +
>>> +	if (val == priv->debounce_val)
>>> +		hi8435_iio_push_event(idev, val);
>>> +	else
>>> +		dev_warn(&priv->spi->dev, "filtered by software debounce");
>> Definitely not.  Warning every time a standard event occurs?  Not
>> a good idea.  Use the debug stuff if you want a way of knowing this
>> happened, then it will silent under normal use.
>> 
>>> +}
>>> +
>>> +static irqreturn_t hi8435_trigger_handler(int irq, void *private)
>>> +{
>>> +	struct iio_poll_func *pf = private;
>>> +	struct iio_dev *idev = pf->indio_dev;
>>> +	struct hi8435_priv *priv = iio_priv(idev);
>>> +	u32 val;
>>> +	int ret;
>>> +
>>> +	ret = hi8435_readl(priv, HI8435_SO31_0_REG, &val);
>>> +	if (ret < 0)
>>> +		goto err_read;
>>> +
>>> +	if (priv->debounce_interval) {
>>> +		priv->debounce_val = val;
>>> +		schedule_delayed_work(&priv->work,
>>> +				msecs_to_jiffies(priv->debounce_interval));
>> This is another element that makes me doubt that the event interface
>> is the way to do this.  I'd much rather we pushed the debouncing out
>> to userspace where cleverer things can be done (and more adaptive
>things).
>> Here to keep the event flow low you have to do it in the kernel.
>
>Yes, debouncing should probably not be done in kernel space and the
>debounce
>interval should not be a devicetree property.
>
>> 
>>> +	} else {
>>> +		hi8435_iio_push_event(idev, val);
>>> +	}
>>> +
>>> +err_read:
>>> +	iio_trigger_notify_done(idev->trig);
>>> +
>>> +	return IRQ_HANDLED;
>>> +}
>>> +
>>> +static void hi8435_parse_dt(struct hi8435_priv *priv)
>>> +{
>>> +	struct device_node *np = priv->spi->dev.of_node;
>>> +	int ret;
>>> +
>>> +	ret = of_get_named_gpio(np, "holt,reset-gpios", 0);
>>> +	priv->reset_gpio = ret < 0 ? 0 : ret;
>> Silly question, but can gpio0 exist on a board?  I suspect you
>> may need an additional variable to hold that this request failed
>> rather than using the magic value of 0.
>
>It can, but new stuff should just use the GPIO descriptor API where
>NULL can
>be used to indicate a invalid GPIO.
>
>- Lars

-- 
Sent from my Android device with K-9 Mail. Please excuse my brevity.

^ permalink raw reply	[flat|nested] 40+ messages in thread

* Re: [PATCH v3 1/7] iio: adc: hi8435: Holt HI-8435 threshold detector
@ 2015-08-16  9:00         ` Jonathan Cameron
  0 siblings, 0 replies; 40+ messages in thread
From: Jonathan Cameron @ 2015-08-16  9:00 UTC (permalink / raw)
  To: Vladimir Barinov
  Cc: Hartmut Knaack, Lars-Peter Clausen, Peter Meerwald, Rob Herring,
	Pawel Moll, Mark Rutland, Ian Campbell, Kumar Gala, linux-kernel,
	linux-iio, devicetree, cory.tusar

On 11/08/15 15:37, Vladimir Barinov wrote:
> Hi Jonathan,
> 
> Thank you for the review.
Sorry I was being so indecisive.  Always take a while when something
'new' turns up to gather opinions and select the best option.  You
are just the unlucky guy who happened to hit the question first!

Anyhow, I'm basically happy (now) with the events approach though
I would suggest some modifications to how exactly it is done
(see inline).

Jonathan
> 
> On 08.08.2015 20:56, Jonathan Cameron wrote:
>> On 29/07/15 13:56, Vladimir Barinov wrote:
>>> Add Holt threshold detector driver for HI-8435 chip
>>>
>>> Signed-off-by: Vladimir Barinov <vladimir.barinov@cogentembedded.com>
>> Because it usually reads better that way, I review from the bottom.
>> Might make more sense if you read the comments that way around ;)
> I did try to match this way.
> At some places the comments below the code, but in other places the comments are above the code :)
Yeah, I've never been consistent ;)
>>
>> I'm going to guess that the typical usecase for this device is in realtime
>> systems where polling gives a nice predictable timing (hence the lack of any
>> interrupt support).  These typically operate as 'scanning' devices
>> (e.g. you update all state at approximately the same time, by reading
>> one input after another in some predefined order).
> Probably you are right about 8435 h/w design or there were other
> reason why this functionality was dropped in comparison to 8429
> chip. The Holt chip hi-8429 has both hardware interrupt and hardware
> debounce enable lines, but it has less amount of sensors (just 8
> outputs).
>>
>> Does the use of events actually map well to this use case?  You are taking
>> something that (other than the results being a bit different) is basically
>> a digital I/O interface and mapping it (sort of) to an interrupt chip
>> when it isn't nothing of the sort.
> a) Using hardware point of view.
> 
> I was thinking that it matches the events a little bit more then
> buffer, because the chip senses for threshold changes (passing
> threshold high or threshold low margins) and then notifies the upper
> layer. The bad point here is that the chip does not have interrupt
> line for sensor state change (i.e. threshold passing).
>
> If it would have the interrupt line for sensor state changes then the
> event interface map this case best way.
Agreed.  I'm coming round to the argument Lars made that we should end
up with the interface being the same whether or not there is an interrupt
there.  Actually I suspect in the long run we will end up with both
a buffered interface and an event interface as they simply have different
usecases for the same hardware (which is why we are having these lengthy
discussions in the first place!)

> b) Using s/w point of view
> 
> I answer relying on current iio stack implementation. I do understand
> that the 8435 chip is not the common/usual iio client like
> adc/dac/light/others, so I'm trying to match the existent iio stack.
Yes.  It's certainly throwing up lots of questions!
> 
> From s/w point of view it does not matter much difference between
> event or buffer interface for this chip because in provides 1-bit
> change and does not have it's own interrupt source line.
Yes, just requires the userspace side to track the state if it wants
to know.  Actually the one nasty is initialization. When the events
are first enabled I guess we fire them all once (if any limits are currently
breached).  That way the state can always be known.
>
> The event interface has generic interface to fill in falling/rising
> thresholds but buffer interface does not.
We can always add more ABI.

> The buffer interface has already has the trigger poll func support
> but event interface does not. So I've tried to implement the
> triggered event in iio stack. BTW: Doesn't the triggered event
> support match the usecase with iio_interrupt_trigger? Or it does not
> make sense to have triggered events (even with irqtrig) at all?
Initially I was unsure about this, but Lars and you have convinced me.
I'm happy having that in the subsystem.  Can also be used for devices
which have an interrupt but which didn't get wired for some reason rather
than having them have some internal poll loop (as we have done up to now).

 
>>
>> I'd like more opinions on this, but my gut reaction is that we would
>> be better making the normal buffered infrastructure handle this data
>> properly rather than pushing it through the events intrastructure.
>>
>> Your solution is neat and tidy in someways but feels like it is mashing
>> data into a particular format.
> Probably that's all because the chip lacks smth from buffer interface
> approach (the data should diferent the 1-bit) and smth from event
> interface approach (lack of h/w interrupt line)
Agreed.
>>
>> To add to the complexity we could have debounce logic.  If we mapped to a
>> fill the buffer with a scan mode, how would we handle this?
>> My guess is that it would simply delay transistions.  Perhaps we even
>> support reading both the value right now and the debounced value if that
>> is possible?

> I did handle it in the driver the same way in both cases (buffer and
> event interfaces): event/buffer trigger issues interrupt and then the
> driver checks value right now and after delay (debounce_interval).
I am yet to be convinced we want software debounce in the kernel.  This
is something that the input subsystem has always had.  Partly the aim of
IIO was to provide a slim system where all this stuff was moved to userspace.

Ultimately the ideal option to my mind is to finally write an inkernel
interface for the events data flow (as we have for buffered data) then
we can have a generic attachable debounce unit independent of any particular
driver.  Hohum.  Now all I need is someone to write it or a few weeks off
work ;) 
> If we do not plan to put debounced values to buffer then we may cache
> during debouncing procedure and then return via
> IIO_CHAN_INFO_DEBOUNCE_VALUE (or other info name). Probably even with
> timestamp.

>>
>> However, here the debounce is all software.  To my mind, move this into
>> userspace entirely. We aren't dealing with big data here so it's hardly
>> going to be particularly challenging.
> I will drop it on your demand in favour to encourage chipmakers to provide h/w debounce,
> but this was much easy/efficient to have it in kernel space, since
> during run generic_buffer sample (or other) we got the new data and then we have to start timer, then timer expires
> and we read raw values from sysfs manually to compare the one that came from buffer and one after debounce
> delay.
If you are clocking evenly and we were using buffering you'd simply do it as readback in time
rather than the other way around - thus no timer at all.  Just each time check the last N
samples to see if they are all high or all low or similar.

> 
>>
>> So my gut feeling is definitely we need to make the buffer infrastructure
>> handle 1 bit values (in groups) then push this data out that way.
> I will wait for your final decision.
Lets proceed with the event version.  If someone has a strong usecase
for adding buffering as well, there is nothing stopping the driver doing both
that I can see (once we have the core infrastructure in place).
> 
>>
>> Still I would like other opinions on this!
>>
>> Jonathan
>>
>>> ---
>>> Changes in version 2:
>>> - Added file sysfs-bus-iio-adc-hi8435
>>> - Changed naming from "discrete ADC" to "threshold detector"
>>> - Replaced swab16p/swab32p with be16_to_cpup/be32_to_cpup
>>> - Made *_show and *_store functions static
>>> - moved out from iio buffers to iio events
>>> - removed hi8436/hi8437 chips from the driver
>>> - moved from debounce_soft_delay/enable to debounce_interval via
>>>    IIO_CHAN_INFO_DEBOUNCE_TIME
>>> - added name extention "comparator"
>>> - moved threshold/hysteresis setup via generic iio event sysfs
>>> - added software mask/unmask channel events
>>> - added programming sensor outputs while in test mode via
>>>    IIO_CHAN_INFO_RAW
>>> - added channels .ext_info for programming sensing mode
>>> Changes in version 3:
>>> - fixed typos
>>> - moved <linux/interrupt.h> to match alphabetic order
>>> - fixed missed */event/* prefix in the ABI description
>>> - added missed colon in sysfs-bus-iio-adc-hi8435 file after "What"
>>> - moved in_voltageY_comparator_thresh_either_en and debounce_time
>>>    to sysfs-bus-iio file
>>> - added error checking for hi8435_write[b|w]
>>>
>>>   Documentation/ABI/testing/sysfs-bus-iio            |   1 +
>>>   Documentation/ABI/testing/sysfs-bus-iio-adc-hi8435 |  61 ++
>>>   drivers/iio/adc/Kconfig                            |  11 +
>>>   drivers/iio/adc/Makefile                           |   1 +
>>>   drivers/iio/adc/hi8435.c                           | 659 +++++++++++++++++++++
>>>   5 files changed, 742 insertions(+)
>>>   create mode 100644 Documentation/ABI/testing/sysfs-bus-iio-adc-hi8435
>>>   create mode 100644 drivers/iio/adc/hi8435.c
>>>
>>> diff --git a/Documentation/ABI/testing/sysfs-bus-iio b/Documentation/ABI/testing/sysfs-bus-iio
>>> index 70c9b1a..09eac44 100644
>>> --- a/Documentation/ABI/testing/sysfs-bus-iio
>>> +++ b/Documentation/ABI/testing/sysfs-bus-iio
>>> @@ -572,6 +572,7 @@ What:        /sys/.../iio:deviceX/events/in_rot_from_north_magnetic_tilt_comp_thresh_r
>>>   What:        /sys/.../iio:deviceX/events/in_rot_from_north_magnetic_tilt_comp_thresh_falling_en
>>>   What:        /sys/.../iio:deviceX/events/in_rot_from_north_true_tilt_comp_thresh_rising_en
>>>   What:        /sys/.../iio:deviceX/events/in_rot_from_north_true_tilt_comp_thresh_falling_en
>>> +What:        /sys/.../iio:deviceX/events/in_voltageY_comparator_thresh_either_en
>>>   What:        /sys/.../iio:deviceX/events/in_voltageY_supply_thresh_rising_en
>>>   What:        /sys/.../iio:deviceX/events/in_voltageY_supply_thresh_falling_en
>>>   What:        /sys/.../iio:deviceX/events/in_voltageY_thresh_rising_en
>>> diff --git a/Documentation/ABI/testing/sysfs-bus-iio-adc-hi8435 b/Documentation/ABI/testing/sysfs-bus-iio-adc-hi8435
>>> new file mode 100644
>>> index 0000000..c59d81d
>>> --- /dev/null
>>> +++ b/Documentation/ABI/testing/sysfs-bus-iio-adc-hi8435
>>> @@ -0,0 +1,61 @@
>>> +What:        /sys/bus/iio/devices/iio:deviceX/in_voltageY_comparator_raw
We really try to use the extended names infrequently and normally only
for things like 'this voltage channel is internally connected to the power
supply rail'.  Don't think we need it here.   As suggested below, if we
are going events then we don't want to expose a _raw reading except via
in the 'events' directory where it would be a 'current event status'.
Anyhow, I talk about that futher down.

>>> +Date:        July 2015
>>> +KernelVersion:    4.2.0
>>> +Contact:    source@cogentembedded.com
>>> +Description:
>>> +        Read value is a voltage threshold measurement from channel Y.
>>> +        Could be either 0 if sensor voltage is lower then low voltage
>>> +        threshold or 1 if sensor voltage is higher then high voltage
>>> +        threshold.
>>> +        Write value is a programmed sensor output while in self test
>>> +        mode. Could be either 0 or 1. The programmed value will be read
>>> +        back if /sys/bus/iio/devices/iio:deviceX/test_enable is set to 1.
>>> +
>>> +What:        /sys/bus/iio/devices/iio:deviceX/test_enable
>>> +Date:        July 2015
>>> +KernelVersion:    4.2.0
>>> +Contact:    source@cogentembedded.com
>>> +Description:
>>> +        Enable/disable the HI-8435 self test mode.
>>> +        If enabled the in_voltageY_comparator_raw should be read back
>>> +        accordingly to written value to in_voltageY_comparator_raw.

Hmm. Normally we just do self tests at startup.  So set some patterns, read them
and then verify they are fine.  Drops the need for a userspace interface
that can get a little confusing (writing to what is normally a read
only attribute).  Would this work here?

Alternatively, just add a debugfs interface to allow this to be set rather than
exposing it via sysfs all the time?

>>> +
>>> +What:        /sys/bus/iio/devices/iio:deviceX/in_voltageY_comparator_sensing_mode
>>> +Date:        July 2015
>>> +KernelVersion:    4.2.0
>>> +Contact:    source@cogentembedded.com
>>> +Description:
>>> +        Program sensor type for threshold detector inputs.
>>> +        Could be either "GND-Open" or "Supply-Open" mode. Y is a
>>> +        threshold detector input channel. Channels 0..7, 8..15, 16..23
>>> +        and 24..31 has common sensor types.
>>> +
>>> +What:        /sys/bus/iio/devices/iio:deviceX/events/in_voltageY_comparator_thresh_falling_value
This is where the comparator extended name looks a bit silly as we are
clearly comparing it anyway due to the thresh_falling part ;)
>>> +Date:        July 2015
>>> +KernelVersion:    4.2.0
>>> +Contact:    source@cogentembedded.com
>>> +Description:
>>> +        Channel Y low voltage threshold. If sensor input voltage goes lower then
>>> +        this value then the threshold falling event is pushed.
>>> +        Depending on in_voltageY_comparator_sensing_mode the low voltage threshold
>>> +        is separately set for "GND-Open" and "Supply-Open" modes.
>>> +        Channels 0..31 have common low threshold values, but could have different
>>> +        sensing_modes.
>>> +        The low voltage threshold range is between 2..21V.
>>> +        Hysteresis between low and high thresholds can not be lower then 2 and
>>> +        can not be odd.
>>> +
>>> +What:        /sys/bus/iio/devices/iio:deviceX/events/in_voltageY_comparator_thresh_rising_value
>>> +Date:        July 2015
>>> +KernelVersion:    4.2.0
>>> +Contact:    source@cogentembedded.com
>>> +Description:
>>> +        Channel Y high voltage threshold. If sensor input voltage goes higher then
>>> +        this value then the threshold rising event is pushed.
>>> +        Depending on in_voltageY_comparator_sensing_mode the high voltage threshold
>>> +        is separately set for "GND-Open" and "Supply-Open" modes.
>>> +        Channels 0..31 have common high threshold values, but could have different
>>> +        sensing_modes.
>>> +        The high voltage threshold range is between 3..22V.
>>> +        Hysteresis between low and high thresholds can not be lower then 2 and
>>> +        can not be odd.
>>> diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig
>>> index eb0cd89..553c91e 100644
>>> --- a/drivers/iio/adc/Kconfig
>>> +++ b/drivers/iio/adc/Kconfig
>>> @@ -170,6 +170,17 @@ config EXYNOS_ADC
>>>         of SoCs for drivers such as the touchscreen and hwmon to use to share
>>>         this resource.
>>>   +config HI8435
>>> +    tristate "Holt Integrated Circuits HI-8435 threshold detector"
>>> +    select IIO_TRIGGERED_EVENT
>>> +    depends on SPI
>>> +    help
>>> +      If you say yes here you get support for Holt Integrated Circuits
>>> +      HI-8435 chip.
>>> +
>>> +      This driver can also be built as a module. If so, the module will be
>>> +      called hi8435.
>>> +
>>>   config LP8788_ADC
>>>       tristate "LP8788 ADC driver"
>>>       depends on MFD_LP8788
>>> diff --git a/drivers/iio/adc/Makefile b/drivers/iio/adc/Makefile
>>> index a096210..00f367aa 100644
>>> --- a/drivers/iio/adc/Makefile
>>> +++ b/drivers/iio/adc/Makefile
>>> @@ -19,6 +19,7 @@ obj-$(CONFIG_BERLIN2_ADC) += berlin2-adc.o
>>>   obj-$(CONFIG_DA9150_GPADC) += da9150-gpadc.o
>>>   obj-$(CONFIG_CC10001_ADC) += cc10001_adc.o
>>>   obj-$(CONFIG_EXYNOS_ADC) += exynos_adc.o
>>> +obj-$(CONFIG_HI8435) += hi8435.o
>>>   obj-$(CONFIG_LP8788_ADC) += lp8788_adc.o
>>>   obj-$(CONFIG_MAX1027) += max1027.o
>>>   obj-$(CONFIG_MAX1363) += max1363.o
>>> diff --git a/drivers/iio/adc/hi8435.c b/drivers/iio/adc/hi8435.c
>>> new file mode 100644
>>> index 0000000..f3dab5a
>>> --- /dev/null
>>> +++ b/drivers/iio/adc/hi8435.c
>>> @@ -0,0 +1,659 @@
>>> +/*
>>> + * Holt Integrated Circuits HI-8435 threshold detector driver
>>> + *
>>> + * Copyright (C) 2015 Zodiac Inflight Innovations
>>> + * Copyright (C) 2015 Cogent Embedded, Inc.
>>> + *
>>> + * 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.
>>> + */
>>> +
>>> +#include <linux/delay.h>
>>> +#include <linux/iio/events.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/triggered_event.h>
>>> +#include <linux/interrupt.h>
>>> +#include <linux/module.h>
>>> +#include <linux/of.h>
>>> +#include <linux/of_device.h>
>>> +#include <linux/of_gpio.h>
>>> +#include <linux/spi/spi.h>
>>> +#include <linux/workqueue.h>
>>> +
>>> +#define DRV_NAME "hi8435"
>>> +
>>> +/* Register offsets for HI-8435 */
>>> +#define HI8435_CTRL_REG            0x02
>>> +#define HI8435_PSEN_REG            0x04
>>> +#define HI8435_TMDATA_REG        0x1E
>>> +#define HI8435_GOCENHYS_REG        0x3A
>>> +#define HI8435_SOCENHYS_REG        0x3C
>> Hmm. These aren't in the docs that I can immediately acquire...
>> Any chance of some more meaningful naming?
> I did follow the register naming in the chip datasheet:
> http://www.holtic.com/products/3081-hi-8435.aspx
> 
> The SO7_0 means "Sensor status bank 0 register: SO<7:0>".
> Does HI8435_STATUS_BANK0_REG work?
Ah, I missed the meaning entirely by not noticing they were 8 bits
each ;)  This current naming is fine.
> Or should I just add comments instead of changing definition?
>>> +#define HI8435_SO7_0_REG        0x10
>>> +#define HI8435_SO15_8_REG        0x12
>>   +#define HI8435_SO23_16_REG        0x14
>>> +#define HI8435_SO31_24_REG        0x16
>>> +#define HI8435_SO31_0_REG        0x78
>>> +
>>> +#define HI8435_WRITE_OPCODE        0x00
>>> +#define HI8435_READ_OPCODE        0x80
>>> +
>>> +/* CTRL register bits */
>>> +#define HI8435_CTRL_TEST        0x01
>>> +#define HI8435_CTRL_SRST        0x02
>>> +
>>> +#define HI8435_DEBOUNCE_DELAY_MAX    1000    /* msec */
>>> +#define HI8435_DEBOUNCE_DELAY_DEF    100    /* msec */
>>> +
>>> +struct hi8435_priv {
>>> +    struct spi_device *spi;
>>> +    struct mutex lock;
>>> +    struct delayed_work work;
>>> +
>>> +    int reset_gpio;
>>> +    int debounce_interval; /* msec */
>>> +    u32 debounce_val; /* prev value to compare during software debounce */
>>> +
>>> +    unsigned long event_scan_mask; /* soft mask/unmask channels events */
>>> +    unsigned int event_prev_val;
>>> +
>>> +    unsigned threshold_lo[2]; /* GND-Open and Supply-Open thresholds */
>>> +    unsigned threshold_hi[2]; /* GND-Open and Supply-Open threshold */
>>> +    u8 reg_buffer[4] ____cacheline_aligned;
>>> +};
>>> +
>>> +static int hi8435_readb(struct hi8435_priv *priv, u8 reg, u8 *val)
>>> +{
>>> +    reg |= HI8435_READ_OPCODE;
>>> +    return spi_write_then_read(priv->spi, &reg, 1, val, 1);
>>> +}
>>> +
>>> +static int hi8435_readw(struct hi8435_priv *priv, u8 reg, u16 *val)
>>> +{
>>> +    int ret;
>>> +
>>> +    reg |= HI8435_READ_OPCODE;
>>> +    ret = spi_write_then_read(priv->spi, &reg, 1, val, 2);
>>> +    *val = be16_to_cpup(val);
>>> +
>>> +    return ret;
>>> +}
>>> +
>>> +static int hi8435_readl(struct hi8435_priv *priv, u8 reg, u32 *val)
>>> +{
>>> +    int ret;
>>> +
>>> +    reg |= HI8435_READ_OPCODE;
>>> +    ret = spi_write_then_read(priv->spi, &reg, 1, val, 4);
>>> +    *val = be32_to_cpup(val);
>>> +
>>> +    return ret;
>>> +}
>>> +
>>> +static int hi8435_writeb(struct hi8435_priv *priv, u8 reg, u8 val)
>>> +{
>>> +    priv->reg_buffer[0] = reg | HI8435_WRITE_OPCODE;
>>> +    priv->reg_buffer[1] = val;
>>> +
>>> +    return spi_write(priv->spi, priv->reg_buffer, 2);
>>> +}
>>> +
>>> +static int hi8435_writew(struct hi8435_priv *priv, u8 reg, u16 val)
>>> +{
>>> +    priv->reg_buffer[0] = reg | HI8435_WRITE_OPCODE;
>>> +    priv->reg_buffer[1] = (val >> 8) & 0xff;
>>> +    priv->reg_buffer[2] = val & 0xff;
>>> +
>>> +    return spi_write(priv->spi, priv->reg_buffer, 3);
>>> +}
>>> +
>>> +static ssize_t hi8435_test_enable_show(struct device *dev,
>>> +                       struct device_attribute *attr, char *buf)
>>> +{
>>> +    struct hi8435_priv *priv = iio_priv(dev_to_iio_dev(dev));
>>> +    int ret;
>>> +    u8 reg;
>>> +
>>> +    ret = hi8435_readb(priv, HI8435_CTRL_REG, &reg);
>>> +    if (ret < 0)
>>> +        return ret;
>>> +
>>> +    return sprintf(buf, "%d\n", reg & HI8435_CTRL_TEST);
>>> +}
>>> +
>>> +static ssize_t hi8435_test_enable_store(struct device *dev,
>>> +                    struct device_attribute *attr,
>>> +                    const char *buf, size_t len)
>>> +{
>>> +    struct hi8435_priv *priv = iio_priv(dev_to_iio_dev(dev));
>>> +    unsigned int val;
>>> +    int ret;
>>> +
>>> +    ret = kstrtouint(buf, 10, &val);
>>> +    if (ret)
>>> +        return ret;
>>> +
>>> +    ret = hi8435_writeb(priv, HI8435_CTRL_REG, val ? HI8435_CTRL_TEST : 0);
>>> +    if (ret < 0)
>>> +        return ret;
>>> +
>>> +    return len;
>>> +}
>>> +
>>> +static IIO_DEVICE_ATTR(test_enable, S_IRUGO | S_IWUSR,
>>> +    hi8435_test_enable_show, hi8435_test_enable_store, 0);
>>> +
>>> +static struct attribute *hi8435_attributes[] = {
>>> +    &iio_dev_attr_test_enable.dev_attr.attr,
>>> +    NULL,
>>> +};
>>> +
>>> +static struct attribute_group hi8435_attribute_group = {
>>> +    .attrs = hi8435_attributes,
>>> +};
>>> +
>>> +static int hi8435_read_raw(struct iio_dev *idev,
>>> +               const struct iio_chan_spec *chan,
>>> +               int *val, int *val2, long mask)
>>> +{
>>> +    struct hi8435_priv *priv = iio_priv(idev);
>>> +    int ret;
>>> +
>>> +    switch (mask) {
>>> +    case IIO_CHAN_INFO_RAW:
>>> +        ret = hi8435_readl(priv, HI8435_SO31_0_REG, val);
>>> +        if (ret < 0)
>>> +            return ret;
>>> +        *val = !!(*val & BIT(chan->channel));
>>> +        return IIO_VAL_INT;

This is where we end up in an odd possition having elected to report these
as 'events'.
We are reporting a 'last event state' via a read raw rather than as part
of the events infrastructure. I wonder if instead we want to allow the
events stuff to give us a 'am I true now signal'.
Would be easy enough to add and would give a clear interface.

IIO_EV_INFO_CURRENT_STATE and attibute naming something like
in_voltageY_thresh_rising_state
which reads either 1 or 0?

For other devices we might need to take into account the
we are not currently measuring but that can be done by an error
return.

>>> +    case IIO_CHAN_INFO_DEBOUNCE_TIME:
>>> +        *val = priv->debounce_interval;
>>> +        return IIO_VAL_INT;
>>> +    default:
>>> +        return -EINVAL;
>>> +    }
>>> +}
>>> +
>>> +static int hi8435_write_raw(struct iio_dev *idev,
>>> +                const struct iio_chan_spec *chan,
>>> +                int val, int val2, long mask)
>>> +{
>>> +    struct hi8435_priv *priv = iio_priv(idev);
>>> +
>>> +    switch (mask) {
>>> +    case IIO_CHAN_INFO_RAW:
>>> +        /* program sensors outputs in test mode */
>>> +        return hi8435_writeb(priv, HI8435_TMDATA_REG, val ? 0x1 : 0x2);
>>> +    case IIO_CHAN_INFO_DEBOUNCE_TIME:
>>> +        if (val < 0)
>>> +            return -EINVAL;
>>> +        if (val > HI8435_DEBOUNCE_DELAY_MAX)
>>> +            val = HI8435_DEBOUNCE_DELAY_MAX;
>>> +        priv->debounce_interval = val;
>>> +        return 0;
>>> +    default:
>>> +        return -EINVAL;
>>> +    }
>>> +}
>>> +
>>> +static int hi8435_read_event_config(struct iio_dev *idev,
>>> +                    const struct iio_chan_spec *chan,
>>> +                    enum iio_event_type type,
>>> +                    enum iio_event_direction dir)
>>> +{
>>> +    struct hi8435_priv *priv = iio_priv(idev);
>>> +
>>> +    return !!(priv->event_scan_mask & BIT(chan->channel));
>>> +}
>>> +
>>> +static int hi8435_write_event_config(struct iio_dev *idev,
>>> +                     const struct iio_chan_spec *chan,
>>> +                     enum iio_event_type type,
>>> +                     enum iio_event_direction dir, int state)
>>> +{
>>> +    struct hi8435_priv *priv = iio_priv(idev);
>>> +
>>> +    priv->event_scan_mask &= ~BIT(chan->channel);
>>> +    if (state)
>>> +        priv->event_scan_mask |= BIT(chan->channel);
>>> +
>>> +    return 0;
>>> +}
>>> +
>>> +static int hi8435_read_event_value(struct iio_dev *idev,
>>> +                   const struct iio_chan_spec *chan,
>>> +                   enum iio_event_type type,
>>> +                   enum iio_event_direction dir,
>>> +                   enum iio_event_info info,
>>> +                   int *val, int *val2)
>>> +{
>>> +    struct hi8435_priv *priv = iio_priv(idev);
>>> +    int ret;
>>> +    u8 mode, psen;
>>> +    u16 reg;
>>> +
>>> +    ret = hi8435_readb(priv, HI8435_PSEN_REG, &psen);
>>> +    if (ret < 0)
>>> +        return ret;
>>> +
>>> +    /* Supply-Open or GND-Open sensing mode */
>>> +    mode = !!(psen & BIT(chan->channel / 8));
>>> +
>>> +    ret = hi8435_readw(priv, mode ? HI8435_SOCENHYS_REG :
>>> +                 HI8435_GOCENHYS_REG, &reg);
>>> +    if (ret < 0)
>>> +        return ret;
>>> +
>>> +    if (dir == IIO_EV_DIR_FALLING)
>>> +        *val = ((reg & 0xff) - (reg >> 8)) / 2;
>>> +
>>> +    if (dir == IIO_EV_DIR_RISING)
>>> +        *val = ((reg & 0xff) + (reg >> 8)) / 2;
>>> +
>>> +    return IIO_VAL_INT;
>>> +}
>>> +
>>> +static int hi8435_write_event_value(struct iio_dev *idev,
>>> +                    const struct iio_chan_spec *chan,
>>> +                    enum iio_event_type type,
>>> +                    enum iio_event_direction dir,
>>> +                    enum iio_event_info info,
>>> +                    int val, int val2)
>>> +{
>>> +    struct hi8435_priv *priv = iio_priv(idev);
>>> +    int ret;
>>> +    u8 mode, psen;
>>> +    u16 reg;
>>> +
>>> +    ret = hi8435_readb(priv, HI8435_PSEN_REG, &psen);
>>> +    if (ret < 0)
>>> +        return ret;
>>> +
>>> +    /* Supply-Open or GND-Open sensing mode */
>>> +    mode = !!(psen & BIT(chan->channel / 8));
>>> +
>>> +    ret = hi8435_readw(priv, mode ? HI8435_SOCENHYS_REG :
>>> +                 HI8435_GOCENHYS_REG, &reg);
>>> +    if (ret < 0)
>>> +        return ret;
>>> +
>>> +    if (dir == IIO_EV_DIR_FALLING) {
>>> +        /* falling threshold range 2..21V, hysteresis minimum 2V */
>>> +        if (val < 2 || val > 21 || (val + 1) >= priv->threshold_hi[mode])
>>> +            return -EINVAL;
>>> +
>>> +        if (val == priv->threshold_lo[mode])
>>> +            return 0;
>>> +
>>> +        priv->threshold_lo[mode] = val;
>>> +
>>> +        /* hysteresis must not be odd */
>>> +        if ((priv->threshold_hi[mode] - priv->threshold_lo[mode]) % 2)
>>> +            priv->threshold_hi[mode]--;
>>> +    }
>>> +
>>> +    if (dir == IIO_EV_DIR_RISING) {
>>> +        /* rising threshold range 3..22V, hysteresis minimum 2V */
>>> +        if (val < 3 || val > 22 || val <= (priv->threshold_lo[mode] + 1))
>>> +            return -EINVAL;
>>> +
>>> +        if (val == priv->threshold_hi[mode])
>>> +            return 0;
>>> +
>>> +        priv->threshold_hi[mode] = val;
>>> +
>>> +        /* hysteresis must not be odd */
>>> +        if ((priv->threshold_hi[mode] - priv->threshold_lo[mode]) % 2)
>>> +            priv->threshold_lo[mode]++;
>>> +    }
>>> +
>>> +    /* program thresholds */
>>> +    mutex_lock(&priv->lock);
>>> +
>>> +    ret = hi8435_readw(priv, mode ? HI8435_SOCENHYS_REG :
>>> +                 HI8435_GOCENHYS_REG, &reg);
>>> +    if (ret < 0) {
>>> +        mutex_unlock(&priv->lock);
>>> +        return ret;
>>> +    }
>>> +
>>> +    /* hysteresis */
>>> +    reg = priv->threshold_hi[mode] - priv->threshold_lo[mode];
>>> +    reg <<= 8;
>>> +    /* threshold center */
>>> +    reg |= (priv->threshold_hi[mode] + priv->threshold_lo[mode]);
>>> +
>>> +    ret = hi8435_writew(priv, mode ? HI8435_SOCENHYS_REG :
>>> +                  HI8435_GOCENHYS_REG, reg);
>>> +
>>> +    mutex_unlock(&priv->lock);
>>> +
>>> +    return ret;
>>> +}
>>> +
>>> +static const struct iio_event_spec hi8435_events[] = {
>>> +    {
>>> +        .type = IIO_EV_TYPE_THRESH,
>>> +        .dir = IIO_EV_DIR_RISING,
>>> +        .mask_separate = BIT(IIO_EV_INFO_VALUE),
>>> +    }, {
>>> +        .type = IIO_EV_TYPE_THRESH,
>>> +        .dir = IIO_EV_DIR_FALLING,
>>> +        .mask_separate = BIT(IIO_EV_INFO_VALUE),
>>> +    }, {
>>> +        .type = IIO_EV_TYPE_THRESH,
>>> +        .dir = IIO_EV_DIR_EITHER,
>>> +        .mask_separate = BIT(IIO_EV_INFO_ENABLE),
>>> +    },
>>> +};
>>> +
>>> +static int hi8435_get_sensing_mode(struct iio_dev *idev,
>>> +                   const struct iio_chan_spec *chan)
>>> +{
>>> +    struct hi8435_priv *priv = iio_priv(idev);
>>> +    int ret;
>>> +    u8 reg;
>>> +
>>> +    ret = hi8435_readb(priv, HI8435_PSEN_REG, &reg);
>>> +    if (ret < 0)
>>> +        return ret;
>>> +
>>> +    return !!(reg & BIT(chan->channel / 8));
>>> +}
>>> +
>>> +static int hi8435_set_sensing_mode(struct iio_dev *idev,
>>> +                   const struct iio_chan_spec *chan,
>>> +                   unsigned int mode)
>>> +{
>>> +    struct hi8435_priv *priv = iio_priv(idev);
>>> +    int ret;
>>> +    u8 reg;
>>> +
>>> +    mutex_lock(&priv->lock);
>>> +
>>> +    ret = hi8435_readb(priv, HI8435_PSEN_REG, &reg);
>>> +    if (ret < 0) {
>>> +        mutex_unlock(&priv->lock);
>>> +        return ret;
>>> +    }
>>> +
>>> +    reg &= ~BIT(chan->channel / 8);
>>> +    if (mode)
>>> +        reg |= BIT(chan->channel / 8);
>>> +
>>> +    ret = hi8435_writeb(priv, HI8435_PSEN_REG, reg);
>>> +
>>> +    mutex_unlock(&priv->lock);
>>> +
>>> +    return ret;
>>> +}
>>> +
>>> +static const char * const hi8435_sensing_modes[] = { "GND-Open",
>>> +                             "Supply-Open" };
>>> +
>>> +static const struct iio_enum hi8435_sensing_mode = {
>>> +    .items = hi8435_sensing_modes,
>>> +    .num_items = ARRAY_SIZE(hi8435_sensing_modes),
>>> +    .get = hi8435_get_sensing_mode,
>>> +    .set = hi8435_set_sensing_mode,
>>> +};
>>> +
>>> +static const struct iio_chan_spec_ext_info hi8435_ext_info[] = {
>>> +    IIO_ENUM("sensing_mode", IIO_SEPARATE, &hi8435_sensing_mode),
>>> +    {},
>>> +};
>>> +
>>> +#define HI8435_VOLTAGE_CHANNEL(num)                    \
>>> +{                                    \
>>> +    .type = IIO_VOLTAGE,                        \
>>> +    .indexed = 1,                            \
>>> +    .channel = num,                            \
>>> +    .info_mask_separate = BIT(IIO_CHAN_INFO_RAW),            \
If we are using events, why have the raw access at all?
>>> +    .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_DEBOUNCE_TIME),    \
>>> +    .event_spec = hi8435_events,                    \
>>> +    .num_event_specs = ARRAY_SIZE(hi8435_events),            \
>>> +    .ext_info = hi8435_ext_info,                    \
>>> +    .extend_name = "comparator"                    \
>> I'm really not sure about this approach.  Can see where you are coming
>> from. We already have an events interface supporting these things so
>> why not use it?   I'm just doubtful it is an efficient option or a clean
>> interface (would like more opinions on this!)
> Sorry, I did not understand here.
> Is it about .extend_name ?
> I did add "comparator" extention to have attributes look like "in_voltage0_comparator_raw"
I'd drop it, particularly if we also drop the raw access.
>>> +}
>>> +
>>> +static const struct iio_chan_spec hi8435_channels[] = {
>>> +    HI8435_VOLTAGE_CHANNEL(0),
>>> +    HI8435_VOLTAGE_CHANNEL(1),
>>> +    HI8435_VOLTAGE_CHANNEL(2),
>>> +    HI8435_VOLTAGE_CHANNEL(3),
>>> +    HI8435_VOLTAGE_CHANNEL(4),
>>> +    HI8435_VOLTAGE_CHANNEL(5),
>>> +    HI8435_VOLTAGE_CHANNEL(6),
>>> +    HI8435_VOLTAGE_CHANNEL(7),
>>> +    HI8435_VOLTAGE_CHANNEL(8),
>>> +    HI8435_VOLTAGE_CHANNEL(9),
>>> +    HI8435_VOLTAGE_CHANNEL(10),
>>> +    HI8435_VOLTAGE_CHANNEL(11),
>>> +    HI8435_VOLTAGE_CHANNEL(12),
>>> +    HI8435_VOLTAGE_CHANNEL(13),
>>> +    HI8435_VOLTAGE_CHANNEL(14),
>>> +    HI8435_VOLTAGE_CHANNEL(15),
>>> +    HI8435_VOLTAGE_CHANNEL(16),
>>> +    HI8435_VOLTAGE_CHANNEL(17),
>>> +    HI8435_VOLTAGE_CHANNEL(18),
>>> +    HI8435_VOLTAGE_CHANNEL(19),
>>> +    HI8435_VOLTAGE_CHANNEL(20),
>>> +    HI8435_VOLTAGE_CHANNEL(21),
>>> +    HI8435_VOLTAGE_CHANNEL(22),
>>> +    HI8435_VOLTAGE_CHANNEL(23),
>>> +    HI8435_VOLTAGE_CHANNEL(24),
>>> +    HI8435_VOLTAGE_CHANNEL(25),
>>> +    HI8435_VOLTAGE_CHANNEL(26),
>>> +    HI8435_VOLTAGE_CHANNEL(27),
>>> +    HI8435_VOLTAGE_CHANNEL(28),
>>> +    HI8435_VOLTAGE_CHANNEL(29),
>>> +    HI8435_VOLTAGE_CHANNEL(30),
>>> +    HI8435_VOLTAGE_CHANNEL(31),
>>> +    IIO_CHAN_SOFT_TIMESTAMP(32),
>>> +};
>>> +
>>> +static const struct iio_info hi8435_info = {
>>> +    .driver_module = THIS_MODULE,
>>> +    .attrs = &hi8435_attribute_group,
>>> +    .read_raw = hi8435_read_raw,
>>> +    .write_raw = hi8435_write_raw,
>>> +    .read_event_config = &hi8435_read_event_config,
>>> +    .write_event_config = hi8435_write_event_config,
>>> +    .read_event_value = &hi8435_read_event_value,
>>> +    .write_event_value = &hi8435_write_event_value,
>>> +};
>>> +
>>> +static void hi8435_iio_push_event(struct iio_dev *idev, unsigned int val)
>>> +{
>>> +    struct hi8435_priv *priv = iio_priv(idev);
>>> +    enum iio_event_direction dir;
>>> +    unsigned int i;
>>> +    unsigned int status = priv->event_prev_val ^ val;
>>> +
>>> +    if (!status)
>>> +        return;
>>> +
>>> +    for_each_set_bit(i, &priv->event_scan_mask, 32) {
>>> +        if (!(status & BIT(i)))
>>> +            continue;
>>> +
>>> +        dir = val & BIT(i) ? IIO_EV_DIR_RISING : IIO_EV_DIR_FALLING;
>>> +
>>> +        iio_push_event(idev,
>>> +                   IIO_UNMOD_EVENT_CODE(IIO_VOLTAGE, i,
>>> +                            IIO_EV_TYPE_THRESH, dir),
>>> +                   iio_get_time_ns());
>>> +    }
>>> +
>>> +    priv->event_prev_val = val;
>>> +}
>>> +
>>> +static void hi8435_debounce_work(struct work_struct *work)
>>> +{
>>> +    struct hi8435_priv *priv = container_of(work, struct hi8435_priv,
>>> +                        work.work);
>>> +    struct iio_dev *idev = spi_get_drvdata(priv->spi);
>>> +    u32 val;
>>> +    int ret;
>>> +
>>> +    ret = hi8435_readl(priv, HI8435_SO31_0_REG, &val);
>>> +    if (ret < 0)
>>> +        return;
>>> +
>>> +    if (val == priv->debounce_val)
>>> +        hi8435_iio_push_event(idev, val);
>>> +    else
>>> +        dev_warn(&priv->spi->dev, "filtered by software debounce");
>> Definitely not.  Warning every time a standard event occurs?  Not
>> a good idea.  Use the debug stuff if you want a way of knowing this
>> happened, then it will silent under normal use.
> I'd say it is not standard event, but occasional/debounce event.
> Anyway I agree with you.
> 
>>> +}
>>> +
>>> +static irqreturn_t hi8435_trigger_handler(int irq, void *private)
>>> +{
>>> +    struct iio_poll_func *pf = private;
>>> +    struct iio_dev *idev = pf->indio_dev;
>>> +    struct hi8435_priv *priv = iio_priv(idev);
>>> +    u32 val;
>>> +    int ret;
>>> +
>>> +    ret = hi8435_readl(priv, HI8435_SO31_0_REG, &val);
>>> +    if (ret < 0)
>>> +        goto err_read;
>>> +
>>> +    if (priv->debounce_interval) {
>>> +        priv->debounce_val = val;
>>> +        schedule_delayed_work(&priv->work,
>>> +                msecs_to_jiffies(priv->debounce_interval));
>> This is another element that makes me doubt that the event interface
>> is the way to do this.  I'd much rather we pushed the debouncing out
>> to userspace where cleverer things can be done (and more adaptive things).
>> Here to keep the event flow low you have to do it in the kernel.
> Yes, it is helpful for reducing the data flow (buffer data or event data)
> Many kernel drivers support s/w event debouncing, f.e. usb cable plug-in, or other connectors.
True enough that it is common functionality.  However, we try to keep
the IIO datahandling as minimal as possible and leave the job to userspace.

We aren't likely to be dealing with huge datarates here (as we are effectively
polling at maybe a few hundred Hz).   Also, with the hysterisis set sensibly
seems unlikely that much bounce due to noise will ever occur.  If you get
bounce that overcomes that then you probably have a wiring or logic problem!
> 
>>> +    } else {
>>> +        hi8435_iio_push_event(idev, val);
>>> +    }
>>> +
>>> +err_read:
>>> +    iio_trigger_notify_done(idev->trig);
>>> +
>>> +    return IRQ_HANDLED;
>>> +}
>>> +
>>> +static void hi8435_parse_dt(struct hi8435_priv *priv)
>>> +{
>>> +    struct device_node *np = priv->spi->dev.of_node;
>>> +    int ret;
>>> +
>>> +    ret = of_get_named_gpio(np, "holt,reset-gpios", 0);
>>> +    priv->reset_gpio = ret < 0 ? 0 : ret;
>> Silly question, but can gpio0 exist on a board?  I suspect you
>> may need an additional variable to hold that this request failed
>> rather than using the magic value of 0.
> I will do handle this case, thank you.
Look at what Lars added on this question...
>>> +
>>> +    ret = of_property_read_u32(np, "holt,debounce-interval",
>>> +                   &priv->debounce_interval);
>>> +    if (ret)
>>> +        priv->debounce_interval = 0;
>>> +    if (priv->debounce_interval > HI8435_DEBOUNCE_DELAY_MAX)
>>> +        priv->debounce_interval = HI8435_DEBOUNCE_DELAY_MAX;
>>> +}
>>> +
>>> +static int hi8435_probe(struct spi_device *spi)
>>> +{
>>> +    struct iio_dev *idev;
>>> +    struct hi8435_priv *priv;
>>> +    int ret;
>>> +
>>> +    idev = devm_iio_device_alloc(&spi->dev, sizeof(*priv));
>>> +    if (!idev)
>>> +        return -ENOMEM;
>>> +
>>> +    priv = iio_priv(idev);
>>> +    priv->spi = spi;
>>> +
>>> +    if (spi->dev.of_node)
>>> +        hi8435_parse_dt(priv);
>>> +
>>> +    spi_set_drvdata(spi, idev);
>>> +    mutex_init(&priv->lock);
>>> +    INIT_DELAYED_WORK(&priv->work, hi8435_debounce_work);
>>> +
>>> +    idev->dev.parent    = &spi->dev;
>>> +    idev->name        = spi_get_device_id(spi)->name;
>>> +    idev->modes        = INDIO_DIRECT_MODE;
>>> +    idev->info        = &hi8435_info;
>>> +    idev->channels        = hi8435_channels;
>>> +    idev->num_channels    = ARRAY_SIZE(hi8435_channels);
>>> +
>>> +    if (priv->reset_gpio) {
>>> +        ret = devm_gpio_request(&spi->dev, priv->reset_gpio, idev->name);
>>> +        if (!ret) {
>>> +            /* chip hardware reset */
>>> +            gpio_direction_output(priv->reset_gpio, 0);
>>> +            udelay(5);
>>> +            gpio_direction_output(priv->reset_gpio, 1);
>>> +        }
>>> +    } else {
>>> +        /* chip software reset */
>>> +        hi8435_writeb(priv, HI8435_CTRL_REG, HI8435_CTRL_SRST);
>>> +        /* get out from reset state */
>>> +        hi8435_writeb(priv, HI8435_CTRL_REG, 0);
>>> +    }
>>> +
>>> +    /* unmask all events */
>>> +    priv->event_scan_mask = ~(0);
>>> +    /* initialize default thresholds */
>>> +    priv->threshold_lo[0] = priv->threshold_lo[1] = 2;
>>> +    priv->threshold_hi[0] = priv->threshold_hi[1] = 4;
>> These seem very random.  Why these values or
> The aim for thresholds initialization is that they are in undefined state after h/w reset and
> the driver allows userspace to change thresholds (high or low) separately.
> 
> There is a restriction in the chip - the hysteresis can not be even.
> If the hysteresis is set to even value then it get's into lock state and not functional anymore. (I've figured this out empirically).
*nice*
> So we need to initialize thresholds (aka hysteresis + it's center) to some initial value to be able to change
> high and low thresholds separately and avoid even values for hysteresis.
> 
> I will also add these comments.
Cool
>>> +    hi8435_writew(priv, HI8435_GOCENHYS_REG, 0x206);
>> What is 0x206?  Again, I guess a default. I'd like to see a comment
>> here saying what real world value it corresponds to.
> Yes it is default values.
> 
> The 0x206 is a code for HI threshold voltage = 4V and low voltage threshold = 2V.
> The register HI8435_SOCENHYS_REG describes the voltage hysteresis of the sensor and the hysteresis center.
> The 0x206 is an encoded value for 2V-to-4V voltage threshold.
> 
> I will add comments.
good.
>>> +    hi8435_writew(priv, HI8435_SOCENHYS_REG, 0x206);
>>> +
>> I am as yet unconvinced that using triggers to poll events is a terribly
>> good idea.  Feels rather like we are doing this due to a deficiency in
>> the buffer interface than because sending events makes sense here.
> Actually the first version of this driver was against the buffer interface.
> I did try to use your tip and switched to event interface :)
oops.  
> 
> Do you think I should switch to buffer interface back?
Nope. As discussed above, right now this approach is winning (just ;)
>>> +    ret = iio_triggered_event_setup(idev, NULL, hi8435_trigger_handler);
>>> +    if (ret)
>>> +        return ret;
>>> +
>>> +    ret = iio_device_register(idev);
>>> +    if (ret < 0) {
>>> +        dev_err(&spi->dev, "unable to register device\n");
>>> +        goto unregister_triggered_event;
>>> +    }
>>> +
>>> +    return 0;
>>> +
>>> +unregister_triggered_event:
>>> +    iio_triggered_event_cleanup(idev);
>>> +    return ret;
>>> +}
>>> +
>>> +static int hi8435_remove(struct spi_device *spi)
>>> +{
>>> +    struct iio_dev *idev = spi_get_drvdata(spi);
>>> +    struct hi8435_priv *priv = iio_priv(idev);
>>> +
>>> +    cancel_delayed_work_sync(&priv->work);
>>> +    iio_device_unregister(idev);
>>> +    iio_triggered_event_cleanup(idev);
>>> +
>>> +    return 0;
>>> +}
>>> +
>>> +static const struct of_device_id hi8435_dt_ids[] = {
>>> +    { .compatible = "holt,hi8435" },
>>> +    {},
>>> +};
>>> +MODULE_DEVICE_TABLE(of, hi8435_dt_ids);
>>> +
>>> +static const struct spi_device_id hi8435_id[] = {
>>> +    { "hi8435", 0},
>>> +    { }
>>> +};
>>> +MODULE_DEVICE_TABLE(spi, hi8435_id);
>>> +
>>> +static struct spi_driver hi8435_driver = {
>>> +    .driver    = {
>>> +        .name        = DRV_NAME,
>>> +        .of_match_table    = of_match_ptr(hi8435_dt_ids),
>>> +    },
>>> +    .probe        = hi8435_probe,
>>> +    .remove        = hi8435_remove,
>>> +    .id_table    = hi8435_id,
>>> +};
>>> +module_spi_driver(hi8435_driver);
>>> +
>>> +MODULE_LICENSE("GPL");
>>> +MODULE_AUTHOR("Vladimir Barinov");
>>> +MODULE_DESCRIPTION("HI-8435 threshold detector");
>>>
> 
> -- 
> To unsubscribe from this list: send the line "unsubscribe linux-iio" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html


^ permalink raw reply	[flat|nested] 40+ messages in thread

* Re: [PATCH v3 1/7] iio: adc: hi8435: Holt HI-8435 threshold detector
@ 2015-08-16  9:00         ` Jonathan Cameron
  0 siblings, 0 replies; 40+ messages in thread
From: Jonathan Cameron @ 2015-08-16  9:00 UTC (permalink / raw)
  To: Vladimir Barinov
  Cc: Hartmut Knaack, Lars-Peter Clausen, Peter Meerwald, Rob Herring,
	Pawel Moll, Mark Rutland, Ian Campbell, Kumar Gala,
	linux-kernel-u79uwXL29TY76Z2rM5mHXA,
	linux-iio-u79uwXL29TY76Z2rM5mHXA,
	devicetree-u79uwXL29TY76Z2rM5mHXA,
	cory.tusar-J6Z/VSE8EyIAspv4Qr0y0gC/G2K4zDHf

On 11/08/15 15:37, Vladimir Barinov wrote:
> Hi Jonathan,
> 
> Thank you for the review.
Sorry I was being so indecisive.  Always take a while when something
'new' turns up to gather opinions and select the best option.  You
are just the unlucky guy who happened to hit the question first!

Anyhow, I'm basically happy (now) with the events approach though
I would suggest some modifications to how exactly it is done
(see inline).

Jonathan
> 
> On 08.08.2015 20:56, Jonathan Cameron wrote:
>> On 29/07/15 13:56, Vladimir Barinov wrote:
>>> Add Holt threshold detector driver for HI-8435 chip
>>>
>>> Signed-off-by: Vladimir Barinov <vladimir.barinov-M4DtvfQ/ZS1MRgGoP+s0PdBPR1lH4CV8@public.gmane.org>
>> Because it usually reads better that way, I review from the bottom.
>> Might make more sense if you read the comments that way around ;)
> I did try to match this way.
> At some places the comments below the code, but in other places the comments are above the code :)
Yeah, I've never been consistent ;)
>>
>> I'm going to guess that the typical usecase for this device is in realtime
>> systems where polling gives a nice predictable timing (hence the lack of any
>> interrupt support).  These typically operate as 'scanning' devices
>> (e.g. you update all state at approximately the same time, by reading
>> one input after another in some predefined order).
> Probably you are right about 8435 h/w design or there were other
> reason why this functionality was dropped in comparison to 8429
> chip. The Holt chip hi-8429 has both hardware interrupt and hardware
> debounce enable lines, but it has less amount of sensors (just 8
> outputs).
>>
>> Does the use of events actually map well to this use case?  You are taking
>> something that (other than the results being a bit different) is basically
>> a digital I/O interface and mapping it (sort of) to an interrupt chip
>> when it isn't nothing of the sort.
> a) Using hardware point of view.
> 
> I was thinking that it matches the events a little bit more then
> buffer, because the chip senses for threshold changes (passing
> threshold high or threshold low margins) and then notifies the upper
> layer. The bad point here is that the chip does not have interrupt
> line for sensor state change (i.e. threshold passing).
>
> If it would have the interrupt line for sensor state changes then the
> event interface map this case best way.
Agreed.  I'm coming round to the argument Lars made that we should end
up with the interface being the same whether or not there is an interrupt
there.  Actually I suspect in the long run we will end up with both
a buffered interface and an event interface as they simply have different
usecases for the same hardware (which is why we are having these lengthy
discussions in the first place!)

> b) Using s/w point of view
> 
> I answer relying on current iio stack implementation. I do understand
> that the 8435 chip is not the common/usual iio client like
> adc/dac/light/others, so I'm trying to match the existent iio stack.
Yes.  It's certainly throwing up lots of questions!
> 
> From s/w point of view it does not matter much difference between
> event or buffer interface for this chip because in provides 1-bit
> change and does not have it's own interrupt source line.
Yes, just requires the userspace side to track the state if it wants
to know.  Actually the one nasty is initialization. When the events
are first enabled I guess we fire them all once (if any limits are currently
breached).  That way the state can always be known.
>
> The event interface has generic interface to fill in falling/rising
> thresholds but buffer interface does not.
We can always add more ABI.

> The buffer interface has already has the trigger poll func support
> but event interface does not. So I've tried to implement the
> triggered event in iio stack. BTW: Doesn't the triggered event
> support match the usecase with iio_interrupt_trigger? Or it does not
> make sense to have triggered events (even with irqtrig) at all?
Initially I was unsure about this, but Lars and you have convinced me.
I'm happy having that in the subsystem.  Can also be used for devices
which have an interrupt but which didn't get wired for some reason rather
than having them have some internal poll loop (as we have done up to now).

 
>>
>> I'd like more opinions on this, but my gut reaction is that we would
>> be better making the normal buffered infrastructure handle this data
>> properly rather than pushing it through the events intrastructure.
>>
>> Your solution is neat and tidy in someways but feels like it is mashing
>> data into a particular format.
> Probably that's all because the chip lacks smth from buffer interface
> approach (the data should diferent the 1-bit) and smth from event
> interface approach (lack of h/w interrupt line)
Agreed.
>>
>> To add to the complexity we could have debounce logic.  If we mapped to a
>> fill the buffer with a scan mode, how would we handle this?
>> My guess is that it would simply delay transistions.  Perhaps we even
>> support reading both the value right now and the debounced value if that
>> is possible?

> I did handle it in the driver the same way in both cases (buffer and
> event interfaces): event/buffer trigger issues interrupt and then the
> driver checks value right now and after delay (debounce_interval).
I am yet to be convinced we want software debounce in the kernel.  This
is something that the input subsystem has always had.  Partly the aim of
IIO was to provide a slim system where all this stuff was moved to userspace.

Ultimately the ideal option to my mind is to finally write an inkernel
interface for the events data flow (as we have for buffered data) then
we can have a generic attachable debounce unit independent of any particular
driver.  Hohum.  Now all I need is someone to write it or a few weeks off
work ;) 
> If we do not plan to put debounced values to buffer then we may cache
> during debouncing procedure and then return via
> IIO_CHAN_INFO_DEBOUNCE_VALUE (or other info name). Probably even with
> timestamp.

>>
>> However, here the debounce is all software.  To my mind, move this into
>> userspace entirely. We aren't dealing with big data here so it's hardly
>> going to be particularly challenging.
> I will drop it on your demand in favour to encourage chipmakers to provide h/w debounce,
> but this was much easy/efficient to have it in kernel space, since
> during run generic_buffer sample (or other) we got the new data and then we have to start timer, then timer expires
> and we read raw values from sysfs manually to compare the one that came from buffer and one after debounce
> delay.
If you are clocking evenly and we were using buffering you'd simply do it as readback in time
rather than the other way around - thus no timer at all.  Just each time check the last N
samples to see if they are all high or all low or similar.

> 
>>
>> So my gut feeling is definitely we need to make the buffer infrastructure
>> handle 1 bit values (in groups) then push this data out that way.
> I will wait for your final decision.
Lets proceed with the event version.  If someone has a strong usecase
for adding buffering as well, there is nothing stopping the driver doing both
that I can see (once we have the core infrastructure in place).
> 
>>
>> Still I would like other opinions on this!
>>
>> Jonathan
>>
>>> ---
>>> Changes in version 2:
>>> - Added file sysfs-bus-iio-adc-hi8435
>>> - Changed naming from "discrete ADC" to "threshold detector"
>>> - Replaced swab16p/swab32p with be16_to_cpup/be32_to_cpup
>>> - Made *_show and *_store functions static
>>> - moved out from iio buffers to iio events
>>> - removed hi8436/hi8437 chips from the driver
>>> - moved from debounce_soft_delay/enable to debounce_interval via
>>>    IIO_CHAN_INFO_DEBOUNCE_TIME
>>> - added name extention "comparator"
>>> - moved threshold/hysteresis setup via generic iio event sysfs
>>> - added software mask/unmask channel events
>>> - added programming sensor outputs while in test mode via
>>>    IIO_CHAN_INFO_RAW
>>> - added channels .ext_info for programming sensing mode
>>> Changes in version 3:
>>> - fixed typos
>>> - moved <linux/interrupt.h> to match alphabetic order
>>> - fixed missed */event/* prefix in the ABI description
>>> - added missed colon in sysfs-bus-iio-adc-hi8435 file after "What"
>>> - moved in_voltageY_comparator_thresh_either_en and debounce_time
>>>    to sysfs-bus-iio file
>>> - added error checking for hi8435_write[b|w]
>>>
>>>   Documentation/ABI/testing/sysfs-bus-iio            |   1 +
>>>   Documentation/ABI/testing/sysfs-bus-iio-adc-hi8435 |  61 ++
>>>   drivers/iio/adc/Kconfig                            |  11 +
>>>   drivers/iio/adc/Makefile                           |   1 +
>>>   drivers/iio/adc/hi8435.c                           | 659 +++++++++++++++++++++
>>>   5 files changed, 742 insertions(+)
>>>   create mode 100644 Documentation/ABI/testing/sysfs-bus-iio-adc-hi8435
>>>   create mode 100644 drivers/iio/adc/hi8435.c
>>>
>>> diff --git a/Documentation/ABI/testing/sysfs-bus-iio b/Documentation/ABI/testing/sysfs-bus-iio
>>> index 70c9b1a..09eac44 100644
>>> --- a/Documentation/ABI/testing/sysfs-bus-iio
>>> +++ b/Documentation/ABI/testing/sysfs-bus-iio
>>> @@ -572,6 +572,7 @@ What:        /sys/.../iio:deviceX/events/in_rot_from_north_magnetic_tilt_comp_thresh_r
>>>   What:        /sys/.../iio:deviceX/events/in_rot_from_north_magnetic_tilt_comp_thresh_falling_en
>>>   What:        /sys/.../iio:deviceX/events/in_rot_from_north_true_tilt_comp_thresh_rising_en
>>>   What:        /sys/.../iio:deviceX/events/in_rot_from_north_true_tilt_comp_thresh_falling_en
>>> +What:        /sys/.../iio:deviceX/events/in_voltageY_comparator_thresh_either_en
>>>   What:        /sys/.../iio:deviceX/events/in_voltageY_supply_thresh_rising_en
>>>   What:        /sys/.../iio:deviceX/events/in_voltageY_supply_thresh_falling_en
>>>   What:        /sys/.../iio:deviceX/events/in_voltageY_thresh_rising_en
>>> diff --git a/Documentation/ABI/testing/sysfs-bus-iio-adc-hi8435 b/Documentation/ABI/testing/sysfs-bus-iio-adc-hi8435
>>> new file mode 100644
>>> index 0000000..c59d81d
>>> --- /dev/null
>>> +++ b/Documentation/ABI/testing/sysfs-bus-iio-adc-hi8435
>>> @@ -0,0 +1,61 @@
>>> +What:        /sys/bus/iio/devices/iio:deviceX/in_voltageY_comparator_raw
We really try to use the extended names infrequently and normally only
for things like 'this voltage channel is internally connected to the power
supply rail'.  Don't think we need it here.   As suggested below, if we
are going events then we don't want to expose a _raw reading except via
in the 'events' directory where it would be a 'current event status'.
Anyhow, I talk about that futher down.

>>> +Date:        July 2015
>>> +KernelVersion:    4.2.0
>>> +Contact:    source-M4DtvfQ/ZS1MRgGoP+s0PdBPR1lH4CV8@public.gmane.org
>>> +Description:
>>> +        Read value is a voltage threshold measurement from channel Y.
>>> +        Could be either 0 if sensor voltage is lower then low voltage
>>> +        threshold or 1 if sensor voltage is higher then high voltage
>>> +        threshold.
>>> +        Write value is a programmed sensor output while in self test
>>> +        mode. Could be either 0 or 1. The programmed value will be read
>>> +        back if /sys/bus/iio/devices/iio:deviceX/test_enable is set to 1.
>>> +
>>> +What:        /sys/bus/iio/devices/iio:deviceX/test_enable
>>> +Date:        July 2015
>>> +KernelVersion:    4.2.0
>>> +Contact:    source-M4DtvfQ/ZS1MRgGoP+s0PdBPR1lH4CV8@public.gmane.org
>>> +Description:
>>> +        Enable/disable the HI-8435 self test mode.
>>> +        If enabled the in_voltageY_comparator_raw should be read back
>>> +        accordingly to written value to in_voltageY_comparator_raw.

Hmm. Normally we just do self tests at startup.  So set some patterns, read them
and then verify they are fine.  Drops the need for a userspace interface
that can get a little confusing (writing to what is normally a read
only attribute).  Would this work here?

Alternatively, just add a debugfs interface to allow this to be set rather than
exposing it via sysfs all the time?

>>> +
>>> +What:        /sys/bus/iio/devices/iio:deviceX/in_voltageY_comparator_sensing_mode
>>> +Date:        July 2015
>>> +KernelVersion:    4.2.0
>>> +Contact:    source-M4DtvfQ/ZS1MRgGoP+s0PdBPR1lH4CV8@public.gmane.org
>>> +Description:
>>> +        Program sensor type for threshold detector inputs.
>>> +        Could be either "GND-Open" or "Supply-Open" mode. Y is a
>>> +        threshold detector input channel. Channels 0..7, 8..15, 16..23
>>> +        and 24..31 has common sensor types.
>>> +
>>> +What:        /sys/bus/iio/devices/iio:deviceX/events/in_voltageY_comparator_thresh_falling_value
This is where the comparator extended name looks a bit silly as we are
clearly comparing it anyway due to the thresh_falling part ;)
>>> +Date:        July 2015
>>> +KernelVersion:    4.2.0
>>> +Contact:    source-M4DtvfQ/ZS1MRgGoP+s0PdBPR1lH4CV8@public.gmane.org
>>> +Description:
>>> +        Channel Y low voltage threshold. If sensor input voltage goes lower then
>>> +        this value then the threshold falling event is pushed.
>>> +        Depending on in_voltageY_comparator_sensing_mode the low voltage threshold
>>> +        is separately set for "GND-Open" and "Supply-Open" modes.
>>> +        Channels 0..31 have common low threshold values, but could have different
>>> +        sensing_modes.
>>> +        The low voltage threshold range is between 2..21V.
>>> +        Hysteresis between low and high thresholds can not be lower then 2 and
>>> +        can not be odd.
>>> +
>>> +What:        /sys/bus/iio/devices/iio:deviceX/events/in_voltageY_comparator_thresh_rising_value
>>> +Date:        July 2015
>>> +KernelVersion:    4.2.0
>>> +Contact:    source-M4DtvfQ/ZS1MRgGoP+s0PdBPR1lH4CV8@public.gmane.org
>>> +Description:
>>> +        Channel Y high voltage threshold. If sensor input voltage goes higher then
>>> +        this value then the threshold rising event is pushed.
>>> +        Depending on in_voltageY_comparator_sensing_mode the high voltage threshold
>>> +        is separately set for "GND-Open" and "Supply-Open" modes.
>>> +        Channels 0..31 have common high threshold values, but could have different
>>> +        sensing_modes.
>>> +        The high voltage threshold range is between 3..22V.
>>> +        Hysteresis between low and high thresholds can not be lower then 2 and
>>> +        can not be odd.
>>> diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig
>>> index eb0cd89..553c91e 100644
>>> --- a/drivers/iio/adc/Kconfig
>>> +++ b/drivers/iio/adc/Kconfig
>>> @@ -170,6 +170,17 @@ config EXYNOS_ADC
>>>         of SoCs for drivers such as the touchscreen and hwmon to use to share
>>>         this resource.
>>>   +config HI8435
>>> +    tristate "Holt Integrated Circuits HI-8435 threshold detector"
>>> +    select IIO_TRIGGERED_EVENT
>>> +    depends on SPI
>>> +    help
>>> +      If you say yes here you get support for Holt Integrated Circuits
>>> +      HI-8435 chip.
>>> +
>>> +      This driver can also be built as a module. If so, the module will be
>>> +      called hi8435.
>>> +
>>>   config LP8788_ADC
>>>       tristate "LP8788 ADC driver"
>>>       depends on MFD_LP8788
>>> diff --git a/drivers/iio/adc/Makefile b/drivers/iio/adc/Makefile
>>> index a096210..00f367aa 100644
>>> --- a/drivers/iio/adc/Makefile
>>> +++ b/drivers/iio/adc/Makefile
>>> @@ -19,6 +19,7 @@ obj-$(CONFIG_BERLIN2_ADC) += berlin2-adc.o
>>>   obj-$(CONFIG_DA9150_GPADC) += da9150-gpadc.o
>>>   obj-$(CONFIG_CC10001_ADC) += cc10001_adc.o
>>>   obj-$(CONFIG_EXYNOS_ADC) += exynos_adc.o
>>> +obj-$(CONFIG_HI8435) += hi8435.o
>>>   obj-$(CONFIG_LP8788_ADC) += lp8788_adc.o
>>>   obj-$(CONFIG_MAX1027) += max1027.o
>>>   obj-$(CONFIG_MAX1363) += max1363.o
>>> diff --git a/drivers/iio/adc/hi8435.c b/drivers/iio/adc/hi8435.c
>>> new file mode 100644
>>> index 0000000..f3dab5a
>>> --- /dev/null
>>> +++ b/drivers/iio/adc/hi8435.c
>>> @@ -0,0 +1,659 @@
>>> +/*
>>> + * Holt Integrated Circuits HI-8435 threshold detector driver
>>> + *
>>> + * Copyright (C) 2015 Zodiac Inflight Innovations
>>> + * Copyright (C) 2015 Cogent Embedded, Inc.
>>> + *
>>> + * 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.
>>> + */
>>> +
>>> +#include <linux/delay.h>
>>> +#include <linux/iio/events.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/triggered_event.h>
>>> +#include <linux/interrupt.h>
>>> +#include <linux/module.h>
>>> +#include <linux/of.h>
>>> +#include <linux/of_device.h>
>>> +#include <linux/of_gpio.h>
>>> +#include <linux/spi/spi.h>
>>> +#include <linux/workqueue.h>
>>> +
>>> +#define DRV_NAME "hi8435"
>>> +
>>> +/* Register offsets for HI-8435 */
>>> +#define HI8435_CTRL_REG            0x02
>>> +#define HI8435_PSEN_REG            0x04
>>> +#define HI8435_TMDATA_REG        0x1E
>>> +#define HI8435_GOCENHYS_REG        0x3A
>>> +#define HI8435_SOCENHYS_REG        0x3C
>> Hmm. These aren't in the docs that I can immediately acquire...
>> Any chance of some more meaningful naming?
> I did follow the register naming in the chip datasheet:
> http://www.holtic.com/products/3081-hi-8435.aspx
> 
> The SO7_0 means "Sensor status bank 0 register: SO<7:0>".
> Does HI8435_STATUS_BANK0_REG work?
Ah, I missed the meaning entirely by not noticing they were 8 bits
each ;)  This current naming is fine.
> Or should I just add comments instead of changing definition?
>>> +#define HI8435_SO7_0_REG        0x10
>>> +#define HI8435_SO15_8_REG        0x12
>>   +#define HI8435_SO23_16_REG        0x14
>>> +#define HI8435_SO31_24_REG        0x16
>>> +#define HI8435_SO31_0_REG        0x78
>>> +
>>> +#define HI8435_WRITE_OPCODE        0x00
>>> +#define HI8435_READ_OPCODE        0x80
>>> +
>>> +/* CTRL register bits */
>>> +#define HI8435_CTRL_TEST        0x01
>>> +#define HI8435_CTRL_SRST        0x02
>>> +
>>> +#define HI8435_DEBOUNCE_DELAY_MAX    1000    /* msec */
>>> +#define HI8435_DEBOUNCE_DELAY_DEF    100    /* msec */
>>> +
>>> +struct hi8435_priv {
>>> +    struct spi_device *spi;
>>> +    struct mutex lock;
>>> +    struct delayed_work work;
>>> +
>>> +    int reset_gpio;
>>> +    int debounce_interval; /* msec */
>>> +    u32 debounce_val; /* prev value to compare during software debounce */
>>> +
>>> +    unsigned long event_scan_mask; /* soft mask/unmask channels events */
>>> +    unsigned int event_prev_val;
>>> +
>>> +    unsigned threshold_lo[2]; /* GND-Open and Supply-Open thresholds */
>>> +    unsigned threshold_hi[2]; /* GND-Open and Supply-Open threshold */
>>> +    u8 reg_buffer[4] ____cacheline_aligned;
>>> +};
>>> +
>>> +static int hi8435_readb(struct hi8435_priv *priv, u8 reg, u8 *val)
>>> +{
>>> +    reg |= HI8435_READ_OPCODE;
>>> +    return spi_write_then_read(priv->spi, &reg, 1, val, 1);
>>> +}
>>> +
>>> +static int hi8435_readw(struct hi8435_priv *priv, u8 reg, u16 *val)
>>> +{
>>> +    int ret;
>>> +
>>> +    reg |= HI8435_READ_OPCODE;
>>> +    ret = spi_write_then_read(priv->spi, &reg, 1, val, 2);
>>> +    *val = be16_to_cpup(val);
>>> +
>>> +    return ret;
>>> +}
>>> +
>>> +static int hi8435_readl(struct hi8435_priv *priv, u8 reg, u32 *val)
>>> +{
>>> +    int ret;
>>> +
>>> +    reg |= HI8435_READ_OPCODE;
>>> +    ret = spi_write_then_read(priv->spi, &reg, 1, val, 4);
>>> +    *val = be32_to_cpup(val);
>>> +
>>> +    return ret;
>>> +}
>>> +
>>> +static int hi8435_writeb(struct hi8435_priv *priv, u8 reg, u8 val)
>>> +{
>>> +    priv->reg_buffer[0] = reg | HI8435_WRITE_OPCODE;
>>> +    priv->reg_buffer[1] = val;
>>> +
>>> +    return spi_write(priv->spi, priv->reg_buffer, 2);
>>> +}
>>> +
>>> +static int hi8435_writew(struct hi8435_priv *priv, u8 reg, u16 val)
>>> +{
>>> +    priv->reg_buffer[0] = reg | HI8435_WRITE_OPCODE;
>>> +    priv->reg_buffer[1] = (val >> 8) & 0xff;
>>> +    priv->reg_buffer[2] = val & 0xff;
>>> +
>>> +    return spi_write(priv->spi, priv->reg_buffer, 3);
>>> +}
>>> +
>>> +static ssize_t hi8435_test_enable_show(struct device *dev,
>>> +                       struct device_attribute *attr, char *buf)
>>> +{
>>> +    struct hi8435_priv *priv = iio_priv(dev_to_iio_dev(dev));
>>> +    int ret;
>>> +    u8 reg;
>>> +
>>> +    ret = hi8435_readb(priv, HI8435_CTRL_REG, &reg);
>>> +    if (ret < 0)
>>> +        return ret;
>>> +
>>> +    return sprintf(buf, "%d\n", reg & HI8435_CTRL_TEST);
>>> +}
>>> +
>>> +static ssize_t hi8435_test_enable_store(struct device *dev,
>>> +                    struct device_attribute *attr,
>>> +                    const char *buf, size_t len)
>>> +{
>>> +    struct hi8435_priv *priv = iio_priv(dev_to_iio_dev(dev));
>>> +    unsigned int val;
>>> +    int ret;
>>> +
>>> +    ret = kstrtouint(buf, 10, &val);
>>> +    if (ret)
>>> +        return ret;
>>> +
>>> +    ret = hi8435_writeb(priv, HI8435_CTRL_REG, val ? HI8435_CTRL_TEST : 0);
>>> +    if (ret < 0)
>>> +        return ret;
>>> +
>>> +    return len;
>>> +}
>>> +
>>> +static IIO_DEVICE_ATTR(test_enable, S_IRUGO | S_IWUSR,
>>> +    hi8435_test_enable_show, hi8435_test_enable_store, 0);
>>> +
>>> +static struct attribute *hi8435_attributes[] = {
>>> +    &iio_dev_attr_test_enable.dev_attr.attr,
>>> +    NULL,
>>> +};
>>> +
>>> +static struct attribute_group hi8435_attribute_group = {
>>> +    .attrs = hi8435_attributes,
>>> +};
>>> +
>>> +static int hi8435_read_raw(struct iio_dev *idev,
>>> +               const struct iio_chan_spec *chan,
>>> +               int *val, int *val2, long mask)
>>> +{
>>> +    struct hi8435_priv *priv = iio_priv(idev);
>>> +    int ret;
>>> +
>>> +    switch (mask) {
>>> +    case IIO_CHAN_INFO_RAW:
>>> +        ret = hi8435_readl(priv, HI8435_SO31_0_REG, val);
>>> +        if (ret < 0)
>>> +            return ret;
>>> +        *val = !!(*val & BIT(chan->channel));
>>> +        return IIO_VAL_INT;

This is where we end up in an odd possition having elected to report these
as 'events'.
We are reporting a 'last event state' via a read raw rather than as part
of the events infrastructure. I wonder if instead we want to allow the
events stuff to give us a 'am I true now signal'.
Would be easy enough to add and would give a clear interface.

IIO_EV_INFO_CURRENT_STATE and attibute naming something like
in_voltageY_thresh_rising_state
which reads either 1 or 0?

For other devices we might need to take into account the
we are not currently measuring but that can be done by an error
return.

>>> +    case IIO_CHAN_INFO_DEBOUNCE_TIME:
>>> +        *val = priv->debounce_interval;
>>> +        return IIO_VAL_INT;
>>> +    default:
>>> +        return -EINVAL;
>>> +    }
>>> +}
>>> +
>>> +static int hi8435_write_raw(struct iio_dev *idev,
>>> +                const struct iio_chan_spec *chan,
>>> +                int val, int val2, long mask)
>>> +{
>>> +    struct hi8435_priv *priv = iio_priv(idev);
>>> +
>>> +    switch (mask) {
>>> +    case IIO_CHAN_INFO_RAW:
>>> +        /* program sensors outputs in test mode */
>>> +        return hi8435_writeb(priv, HI8435_TMDATA_REG, val ? 0x1 : 0x2);
>>> +    case IIO_CHAN_INFO_DEBOUNCE_TIME:
>>> +        if (val < 0)
>>> +            return -EINVAL;
>>> +        if (val > HI8435_DEBOUNCE_DELAY_MAX)
>>> +            val = HI8435_DEBOUNCE_DELAY_MAX;
>>> +        priv->debounce_interval = val;
>>> +        return 0;
>>> +    default:
>>> +        return -EINVAL;
>>> +    }
>>> +}
>>> +
>>> +static int hi8435_read_event_config(struct iio_dev *idev,
>>> +                    const struct iio_chan_spec *chan,
>>> +                    enum iio_event_type type,
>>> +                    enum iio_event_direction dir)
>>> +{
>>> +    struct hi8435_priv *priv = iio_priv(idev);
>>> +
>>> +    return !!(priv->event_scan_mask & BIT(chan->channel));
>>> +}
>>> +
>>> +static int hi8435_write_event_config(struct iio_dev *idev,
>>> +                     const struct iio_chan_spec *chan,
>>> +                     enum iio_event_type type,
>>> +                     enum iio_event_direction dir, int state)
>>> +{
>>> +    struct hi8435_priv *priv = iio_priv(idev);
>>> +
>>> +    priv->event_scan_mask &= ~BIT(chan->channel);
>>> +    if (state)
>>> +        priv->event_scan_mask |= BIT(chan->channel);
>>> +
>>> +    return 0;
>>> +}
>>> +
>>> +static int hi8435_read_event_value(struct iio_dev *idev,
>>> +                   const struct iio_chan_spec *chan,
>>> +                   enum iio_event_type type,
>>> +                   enum iio_event_direction dir,
>>> +                   enum iio_event_info info,
>>> +                   int *val, int *val2)
>>> +{
>>> +    struct hi8435_priv *priv = iio_priv(idev);
>>> +    int ret;
>>> +    u8 mode, psen;
>>> +    u16 reg;
>>> +
>>> +    ret = hi8435_readb(priv, HI8435_PSEN_REG, &psen);
>>> +    if (ret < 0)
>>> +        return ret;
>>> +
>>> +    /* Supply-Open or GND-Open sensing mode */
>>> +    mode = !!(psen & BIT(chan->channel / 8));
>>> +
>>> +    ret = hi8435_readw(priv, mode ? HI8435_SOCENHYS_REG :
>>> +                 HI8435_GOCENHYS_REG, &reg);
>>> +    if (ret < 0)
>>> +        return ret;
>>> +
>>> +    if (dir == IIO_EV_DIR_FALLING)
>>> +        *val = ((reg & 0xff) - (reg >> 8)) / 2;
>>> +
>>> +    if (dir == IIO_EV_DIR_RISING)
>>> +        *val = ((reg & 0xff) + (reg >> 8)) / 2;
>>> +
>>> +    return IIO_VAL_INT;
>>> +}
>>> +
>>> +static int hi8435_write_event_value(struct iio_dev *idev,
>>> +                    const struct iio_chan_spec *chan,
>>> +                    enum iio_event_type type,
>>> +                    enum iio_event_direction dir,
>>> +                    enum iio_event_info info,
>>> +                    int val, int val2)
>>> +{
>>> +    struct hi8435_priv *priv = iio_priv(idev);
>>> +    int ret;
>>> +    u8 mode, psen;
>>> +    u16 reg;
>>> +
>>> +    ret = hi8435_readb(priv, HI8435_PSEN_REG, &psen);
>>> +    if (ret < 0)
>>> +        return ret;
>>> +
>>> +    /* Supply-Open or GND-Open sensing mode */
>>> +    mode = !!(psen & BIT(chan->channel / 8));
>>> +
>>> +    ret = hi8435_readw(priv, mode ? HI8435_SOCENHYS_REG :
>>> +                 HI8435_GOCENHYS_REG, &reg);
>>> +    if (ret < 0)
>>> +        return ret;
>>> +
>>> +    if (dir == IIO_EV_DIR_FALLING) {
>>> +        /* falling threshold range 2..21V, hysteresis minimum 2V */
>>> +        if (val < 2 || val > 21 || (val + 1) >= priv->threshold_hi[mode])
>>> +            return -EINVAL;
>>> +
>>> +        if (val == priv->threshold_lo[mode])
>>> +            return 0;
>>> +
>>> +        priv->threshold_lo[mode] = val;
>>> +
>>> +        /* hysteresis must not be odd */
>>> +        if ((priv->threshold_hi[mode] - priv->threshold_lo[mode]) % 2)
>>> +            priv->threshold_hi[mode]--;
>>> +    }
>>> +
>>> +    if (dir == IIO_EV_DIR_RISING) {
>>> +        /* rising threshold range 3..22V, hysteresis minimum 2V */
>>> +        if (val < 3 || val > 22 || val <= (priv->threshold_lo[mode] + 1))
>>> +            return -EINVAL;
>>> +
>>> +        if (val == priv->threshold_hi[mode])
>>> +            return 0;
>>> +
>>> +        priv->threshold_hi[mode] = val;
>>> +
>>> +        /* hysteresis must not be odd */
>>> +        if ((priv->threshold_hi[mode] - priv->threshold_lo[mode]) % 2)
>>> +            priv->threshold_lo[mode]++;
>>> +    }
>>> +
>>> +    /* program thresholds */
>>> +    mutex_lock(&priv->lock);
>>> +
>>> +    ret = hi8435_readw(priv, mode ? HI8435_SOCENHYS_REG :
>>> +                 HI8435_GOCENHYS_REG, &reg);
>>> +    if (ret < 0) {
>>> +        mutex_unlock(&priv->lock);
>>> +        return ret;
>>> +    }
>>> +
>>> +    /* hysteresis */
>>> +    reg = priv->threshold_hi[mode] - priv->threshold_lo[mode];
>>> +    reg <<= 8;
>>> +    /* threshold center */
>>> +    reg |= (priv->threshold_hi[mode] + priv->threshold_lo[mode]);
>>> +
>>> +    ret = hi8435_writew(priv, mode ? HI8435_SOCENHYS_REG :
>>> +                  HI8435_GOCENHYS_REG, reg);
>>> +
>>> +    mutex_unlock(&priv->lock);
>>> +
>>> +    return ret;
>>> +}
>>> +
>>> +static const struct iio_event_spec hi8435_events[] = {
>>> +    {
>>> +        .type = IIO_EV_TYPE_THRESH,
>>> +        .dir = IIO_EV_DIR_RISING,
>>> +        .mask_separate = BIT(IIO_EV_INFO_VALUE),
>>> +    }, {
>>> +        .type = IIO_EV_TYPE_THRESH,
>>> +        .dir = IIO_EV_DIR_FALLING,
>>> +        .mask_separate = BIT(IIO_EV_INFO_VALUE),
>>> +    }, {
>>> +        .type = IIO_EV_TYPE_THRESH,
>>> +        .dir = IIO_EV_DIR_EITHER,
>>> +        .mask_separate = BIT(IIO_EV_INFO_ENABLE),
>>> +    },
>>> +};
>>> +
>>> +static int hi8435_get_sensing_mode(struct iio_dev *idev,
>>> +                   const struct iio_chan_spec *chan)
>>> +{
>>> +    struct hi8435_priv *priv = iio_priv(idev);
>>> +    int ret;
>>> +    u8 reg;
>>> +
>>> +    ret = hi8435_readb(priv, HI8435_PSEN_REG, &reg);
>>> +    if (ret < 0)
>>> +        return ret;
>>> +
>>> +    return !!(reg & BIT(chan->channel / 8));
>>> +}
>>> +
>>> +static int hi8435_set_sensing_mode(struct iio_dev *idev,
>>> +                   const struct iio_chan_spec *chan,
>>> +                   unsigned int mode)
>>> +{
>>> +    struct hi8435_priv *priv = iio_priv(idev);
>>> +    int ret;
>>> +    u8 reg;
>>> +
>>> +    mutex_lock(&priv->lock);
>>> +
>>> +    ret = hi8435_readb(priv, HI8435_PSEN_REG, &reg);
>>> +    if (ret < 0) {
>>> +        mutex_unlock(&priv->lock);
>>> +        return ret;
>>> +    }
>>> +
>>> +    reg &= ~BIT(chan->channel / 8);
>>> +    if (mode)
>>> +        reg |= BIT(chan->channel / 8);
>>> +
>>> +    ret = hi8435_writeb(priv, HI8435_PSEN_REG, reg);
>>> +
>>> +    mutex_unlock(&priv->lock);
>>> +
>>> +    return ret;
>>> +}
>>> +
>>> +static const char * const hi8435_sensing_modes[] = { "GND-Open",
>>> +                             "Supply-Open" };
>>> +
>>> +static const struct iio_enum hi8435_sensing_mode = {
>>> +    .items = hi8435_sensing_modes,
>>> +    .num_items = ARRAY_SIZE(hi8435_sensing_modes),
>>> +    .get = hi8435_get_sensing_mode,
>>> +    .set = hi8435_set_sensing_mode,
>>> +};
>>> +
>>> +static const struct iio_chan_spec_ext_info hi8435_ext_info[] = {
>>> +    IIO_ENUM("sensing_mode", IIO_SEPARATE, &hi8435_sensing_mode),
>>> +    {},
>>> +};
>>> +
>>> +#define HI8435_VOLTAGE_CHANNEL(num)                    \
>>> +{                                    \
>>> +    .type = IIO_VOLTAGE,                        \
>>> +    .indexed = 1,                            \
>>> +    .channel = num,                            \
>>> +    .info_mask_separate = BIT(IIO_CHAN_INFO_RAW),            \
If we are using events, why have the raw access at all?
>>> +    .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_DEBOUNCE_TIME),    \
>>> +    .event_spec = hi8435_events,                    \
>>> +    .num_event_specs = ARRAY_SIZE(hi8435_events),            \
>>> +    .ext_info = hi8435_ext_info,                    \
>>> +    .extend_name = "comparator"                    \
>> I'm really not sure about this approach.  Can see where you are coming
>> from. We already have an events interface supporting these things so
>> why not use it?   I'm just doubtful it is an efficient option or a clean
>> interface (would like more opinions on this!)
> Sorry, I did not understand here.
> Is it about .extend_name ?
> I did add "comparator" extention to have attributes look like "in_voltage0_comparator_raw"
I'd drop it, particularly if we also drop the raw access.
>>> +}
>>> +
>>> +static const struct iio_chan_spec hi8435_channels[] = {
>>> +    HI8435_VOLTAGE_CHANNEL(0),
>>> +    HI8435_VOLTAGE_CHANNEL(1),
>>> +    HI8435_VOLTAGE_CHANNEL(2),
>>> +    HI8435_VOLTAGE_CHANNEL(3),
>>> +    HI8435_VOLTAGE_CHANNEL(4),
>>> +    HI8435_VOLTAGE_CHANNEL(5),
>>> +    HI8435_VOLTAGE_CHANNEL(6),
>>> +    HI8435_VOLTAGE_CHANNEL(7),
>>> +    HI8435_VOLTAGE_CHANNEL(8),
>>> +    HI8435_VOLTAGE_CHANNEL(9),
>>> +    HI8435_VOLTAGE_CHANNEL(10),
>>> +    HI8435_VOLTAGE_CHANNEL(11),
>>> +    HI8435_VOLTAGE_CHANNEL(12),
>>> +    HI8435_VOLTAGE_CHANNEL(13),
>>> +    HI8435_VOLTAGE_CHANNEL(14),
>>> +    HI8435_VOLTAGE_CHANNEL(15),
>>> +    HI8435_VOLTAGE_CHANNEL(16),
>>> +    HI8435_VOLTAGE_CHANNEL(17),
>>> +    HI8435_VOLTAGE_CHANNEL(18),
>>> +    HI8435_VOLTAGE_CHANNEL(19),
>>> +    HI8435_VOLTAGE_CHANNEL(20),
>>> +    HI8435_VOLTAGE_CHANNEL(21),
>>> +    HI8435_VOLTAGE_CHANNEL(22),
>>> +    HI8435_VOLTAGE_CHANNEL(23),
>>> +    HI8435_VOLTAGE_CHANNEL(24),
>>> +    HI8435_VOLTAGE_CHANNEL(25),
>>> +    HI8435_VOLTAGE_CHANNEL(26),
>>> +    HI8435_VOLTAGE_CHANNEL(27),
>>> +    HI8435_VOLTAGE_CHANNEL(28),
>>> +    HI8435_VOLTAGE_CHANNEL(29),
>>> +    HI8435_VOLTAGE_CHANNEL(30),
>>> +    HI8435_VOLTAGE_CHANNEL(31),
>>> +    IIO_CHAN_SOFT_TIMESTAMP(32),
>>> +};
>>> +
>>> +static const struct iio_info hi8435_info = {
>>> +    .driver_module = THIS_MODULE,
>>> +    .attrs = &hi8435_attribute_group,
>>> +    .read_raw = hi8435_read_raw,
>>> +    .write_raw = hi8435_write_raw,
>>> +    .read_event_config = &hi8435_read_event_config,
>>> +    .write_event_config = hi8435_write_event_config,
>>> +    .read_event_value = &hi8435_read_event_value,
>>> +    .write_event_value = &hi8435_write_event_value,
>>> +};
>>> +
>>> +static void hi8435_iio_push_event(struct iio_dev *idev, unsigned int val)
>>> +{
>>> +    struct hi8435_priv *priv = iio_priv(idev);
>>> +    enum iio_event_direction dir;
>>> +    unsigned int i;
>>> +    unsigned int status = priv->event_prev_val ^ val;
>>> +
>>> +    if (!status)
>>> +        return;
>>> +
>>> +    for_each_set_bit(i, &priv->event_scan_mask, 32) {
>>> +        if (!(status & BIT(i)))
>>> +            continue;
>>> +
>>> +        dir = val & BIT(i) ? IIO_EV_DIR_RISING : IIO_EV_DIR_FALLING;
>>> +
>>> +        iio_push_event(idev,
>>> +                   IIO_UNMOD_EVENT_CODE(IIO_VOLTAGE, i,
>>> +                            IIO_EV_TYPE_THRESH, dir),
>>> +                   iio_get_time_ns());
>>> +    }
>>> +
>>> +    priv->event_prev_val = val;
>>> +}
>>> +
>>> +static void hi8435_debounce_work(struct work_struct *work)
>>> +{
>>> +    struct hi8435_priv *priv = container_of(work, struct hi8435_priv,
>>> +                        work.work);
>>> +    struct iio_dev *idev = spi_get_drvdata(priv->spi);
>>> +    u32 val;
>>> +    int ret;
>>> +
>>> +    ret = hi8435_readl(priv, HI8435_SO31_0_REG, &val);
>>> +    if (ret < 0)
>>> +        return;
>>> +
>>> +    if (val == priv->debounce_val)
>>> +        hi8435_iio_push_event(idev, val);
>>> +    else
>>> +        dev_warn(&priv->spi->dev, "filtered by software debounce");
>> Definitely not.  Warning every time a standard event occurs?  Not
>> a good idea.  Use the debug stuff if you want a way of knowing this
>> happened, then it will silent under normal use.
> I'd say it is not standard event, but occasional/debounce event.
> Anyway I agree with you.
> 
>>> +}
>>> +
>>> +static irqreturn_t hi8435_trigger_handler(int irq, void *private)
>>> +{
>>> +    struct iio_poll_func *pf = private;
>>> +    struct iio_dev *idev = pf->indio_dev;
>>> +    struct hi8435_priv *priv = iio_priv(idev);
>>> +    u32 val;
>>> +    int ret;
>>> +
>>> +    ret = hi8435_readl(priv, HI8435_SO31_0_REG, &val);
>>> +    if (ret < 0)
>>> +        goto err_read;
>>> +
>>> +    if (priv->debounce_interval) {
>>> +        priv->debounce_val = val;
>>> +        schedule_delayed_work(&priv->work,
>>> +                msecs_to_jiffies(priv->debounce_interval));
>> This is another element that makes me doubt that the event interface
>> is the way to do this.  I'd much rather we pushed the debouncing out
>> to userspace where cleverer things can be done (and more adaptive things).
>> Here to keep the event flow low you have to do it in the kernel.
> Yes, it is helpful for reducing the data flow (buffer data or event data)
> Many kernel drivers support s/w event debouncing, f.e. usb cable plug-in, or other connectors.
True enough that it is common functionality.  However, we try to keep
the IIO datahandling as minimal as possible and leave the job to userspace.

We aren't likely to be dealing with huge datarates here (as we are effectively
polling at maybe a few hundred Hz).   Also, with the hysterisis set sensibly
seems unlikely that much bounce due to noise will ever occur.  If you get
bounce that overcomes that then you probably have a wiring or logic problem!
> 
>>> +    } else {
>>> +        hi8435_iio_push_event(idev, val);
>>> +    }
>>> +
>>> +err_read:
>>> +    iio_trigger_notify_done(idev->trig);
>>> +
>>> +    return IRQ_HANDLED;
>>> +}
>>> +
>>> +static void hi8435_parse_dt(struct hi8435_priv *priv)
>>> +{
>>> +    struct device_node *np = priv->spi->dev.of_node;
>>> +    int ret;
>>> +
>>> +    ret = of_get_named_gpio(np, "holt,reset-gpios", 0);
>>> +    priv->reset_gpio = ret < 0 ? 0 : ret;
>> Silly question, but can gpio0 exist on a board?  I suspect you
>> may need an additional variable to hold that this request failed
>> rather than using the magic value of 0.
> I will do handle this case, thank you.
Look at what Lars added on this question...
>>> +
>>> +    ret = of_property_read_u32(np, "holt,debounce-interval",
>>> +                   &priv->debounce_interval);
>>> +    if (ret)
>>> +        priv->debounce_interval = 0;
>>> +    if (priv->debounce_interval > HI8435_DEBOUNCE_DELAY_MAX)
>>> +        priv->debounce_interval = HI8435_DEBOUNCE_DELAY_MAX;
>>> +}
>>> +
>>> +static int hi8435_probe(struct spi_device *spi)
>>> +{
>>> +    struct iio_dev *idev;
>>> +    struct hi8435_priv *priv;
>>> +    int ret;
>>> +
>>> +    idev = devm_iio_device_alloc(&spi->dev, sizeof(*priv));
>>> +    if (!idev)
>>> +        return -ENOMEM;
>>> +
>>> +    priv = iio_priv(idev);
>>> +    priv->spi = spi;
>>> +
>>> +    if (spi->dev.of_node)
>>> +        hi8435_parse_dt(priv);
>>> +
>>> +    spi_set_drvdata(spi, idev);
>>> +    mutex_init(&priv->lock);
>>> +    INIT_DELAYED_WORK(&priv->work, hi8435_debounce_work);
>>> +
>>> +    idev->dev.parent    = &spi->dev;
>>> +    idev->name        = spi_get_device_id(spi)->name;
>>> +    idev->modes        = INDIO_DIRECT_MODE;
>>> +    idev->info        = &hi8435_info;
>>> +    idev->channels        = hi8435_channels;
>>> +    idev->num_channels    = ARRAY_SIZE(hi8435_channels);
>>> +
>>> +    if (priv->reset_gpio) {
>>> +        ret = devm_gpio_request(&spi->dev, priv->reset_gpio, idev->name);
>>> +        if (!ret) {
>>> +            /* chip hardware reset */
>>> +            gpio_direction_output(priv->reset_gpio, 0);
>>> +            udelay(5);
>>> +            gpio_direction_output(priv->reset_gpio, 1);
>>> +        }
>>> +    } else {
>>> +        /* chip software reset */
>>> +        hi8435_writeb(priv, HI8435_CTRL_REG, HI8435_CTRL_SRST);
>>> +        /* get out from reset state */
>>> +        hi8435_writeb(priv, HI8435_CTRL_REG, 0);
>>> +    }
>>> +
>>> +    /* unmask all events */
>>> +    priv->event_scan_mask = ~(0);
>>> +    /* initialize default thresholds */
>>> +    priv->threshold_lo[0] = priv->threshold_lo[1] = 2;
>>> +    priv->threshold_hi[0] = priv->threshold_hi[1] = 4;
>> These seem very random.  Why these values or
> The aim for thresholds initialization is that they are in undefined state after h/w reset and
> the driver allows userspace to change thresholds (high or low) separately.
> 
> There is a restriction in the chip - the hysteresis can not be even.
> If the hysteresis is set to even value then it get's into lock state and not functional anymore. (I've figured this out empirically).
*nice*
> So we need to initialize thresholds (aka hysteresis + it's center) to some initial value to be able to change
> high and low thresholds separately and avoid even values for hysteresis.
> 
> I will also add these comments.
Cool
>>> +    hi8435_writew(priv, HI8435_GOCENHYS_REG, 0x206);
>> What is 0x206?  Again, I guess a default. I'd like to see a comment
>> here saying what real world value it corresponds to.
> Yes it is default values.
> 
> The 0x206 is a code for HI threshold voltage = 4V and low voltage threshold = 2V.
> The register HI8435_SOCENHYS_REG describes the voltage hysteresis of the sensor and the hysteresis center.
> The 0x206 is an encoded value for 2V-to-4V voltage threshold.
> 
> I will add comments.
good.
>>> +    hi8435_writew(priv, HI8435_SOCENHYS_REG, 0x206);
>>> +
>> I am as yet unconvinced that using triggers to poll events is a terribly
>> good idea.  Feels rather like we are doing this due to a deficiency in
>> the buffer interface than because sending events makes sense here.
> Actually the first version of this driver was against the buffer interface.
> I did try to use your tip and switched to event interface :)
oops.  
> 
> Do you think I should switch to buffer interface back?
Nope. As discussed above, right now this approach is winning (just ;)
>>> +    ret = iio_triggered_event_setup(idev, NULL, hi8435_trigger_handler);
>>> +    if (ret)
>>> +        return ret;
>>> +
>>> +    ret = iio_device_register(idev);
>>> +    if (ret < 0) {
>>> +        dev_err(&spi->dev, "unable to register device\n");
>>> +        goto unregister_triggered_event;
>>> +    }
>>> +
>>> +    return 0;
>>> +
>>> +unregister_triggered_event:
>>> +    iio_triggered_event_cleanup(idev);
>>> +    return ret;
>>> +}
>>> +
>>> +static int hi8435_remove(struct spi_device *spi)
>>> +{
>>> +    struct iio_dev *idev = spi_get_drvdata(spi);
>>> +    struct hi8435_priv *priv = iio_priv(idev);
>>> +
>>> +    cancel_delayed_work_sync(&priv->work);
>>> +    iio_device_unregister(idev);
>>> +    iio_triggered_event_cleanup(idev);
>>> +
>>> +    return 0;
>>> +}
>>> +
>>> +static const struct of_device_id hi8435_dt_ids[] = {
>>> +    { .compatible = "holt,hi8435" },
>>> +    {},
>>> +};
>>> +MODULE_DEVICE_TABLE(of, hi8435_dt_ids);
>>> +
>>> +static const struct spi_device_id hi8435_id[] = {
>>> +    { "hi8435", 0},
>>> +    { }
>>> +};
>>> +MODULE_DEVICE_TABLE(spi, hi8435_id);
>>> +
>>> +static struct spi_driver hi8435_driver = {
>>> +    .driver    = {
>>> +        .name        = DRV_NAME,
>>> +        .of_match_table    = of_match_ptr(hi8435_dt_ids),
>>> +    },
>>> +    .probe        = hi8435_probe,
>>> +    .remove        = hi8435_remove,
>>> +    .id_table    = hi8435_id,
>>> +};
>>> +module_spi_driver(hi8435_driver);
>>> +
>>> +MODULE_LICENSE("GPL");
>>> +MODULE_AUTHOR("Vladimir Barinov");
>>> +MODULE_DESCRIPTION("HI-8435 threshold detector");
>>>
> 
> -- 
> To unsubscribe from this list: send the line "unsubscribe linux-iio" in
> the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply	[flat|nested] 40+ messages in thread

* Re: [PATCH v3 5/7] iio: Support triggered events
@ 2015-08-16  9:05         ` Jonathan Cameron
  0 siblings, 0 replies; 40+ messages in thread
From: Jonathan Cameron @ 2015-08-16  9:05 UTC (permalink / raw)
  To: Vladimir Barinov, Lars-Peter Clausen
  Cc: Hartmut Knaack, Peter Meerwald, Rob Herring, Pawel Moll,
	Mark Rutland, Ian Campbell, Kumar Gala, linux-kernel, linux-iio,
	devicetree, cory.tusar

Vladimir - before I forget, could you try to wrap emails to around 
80 characters per line.  Otherwise we end up with really tricky
to read thread when some are doing so and others aren't!

On 07/08/15 17:10, Vladimir Barinov wrote:
> Hi Lars,
> 
> On 07.08.2015 16:45, Lars-Peter Clausen wrote:
>> Hi,
>>
>> On 07/29/2015 02:57 PM, Vladimir Barinov wrote:
>>> Support triggered events.
>>>
>>> This is useful for chips that don't have their own interrupt sources.
>>> It allows to use generic/standalone iio triggers for those drivers.
>>>
>> Thanks for the patch. Can you describe in more detail how
>> INDIO_EVENT_TRIGGERED differs from the INDIO_BUFFER_TRIGGERED. By just
>> looking at the source code I don't understand quite why it is necessary.
> This is needed for the case when iio buffer is not used and used only
> iio event.
> 
> The difference to INDIO_BUFFER_TRIGGERED is that for triggered buffer
> the trigger poll function is attached (using
> iio_trigger_attach_poll_func) only in industrialio-buffer.c using
> number of steps/conditions starting by issuing 1 to buffer/enable
> sysfs path.
> 
> For chips that do not use iio buffer but use only iio events the
> iio_trigger_attach_poll_func never called.
> 
> In case of using INDIO_BUFFER_TRIGGERED the poll func is
> attached/detached via industrialio-buffer.c In case of using
> INDIO_EVENT_TRIGGERED the poll func is attached/detached via
> industrialio-trigger.c during set/change the current trigger.
> 
> Regards, Vladimir
You've convinced Lars and I agree with his argument that this
is a sensible addition to IIO.
> 

>> Thanks,
>> - Lars
>>
> 
> -- 
> To unsubscribe from this list: send the line "unsubscribe linux-iio" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html


^ permalink raw reply	[flat|nested] 40+ messages in thread

* Re: [PATCH v3 5/7] iio: Support triggered events
@ 2015-08-16  9:05         ` Jonathan Cameron
  0 siblings, 0 replies; 40+ messages in thread
From: Jonathan Cameron @ 2015-08-16  9:05 UTC (permalink / raw)
  To: Vladimir Barinov, Lars-Peter Clausen
  Cc: Hartmut Knaack, Peter Meerwald, Rob Herring, Pawel Moll,
	Mark Rutland, Ian Campbell, Kumar Gala,
	linux-kernel-u79uwXL29TY76Z2rM5mHXA,
	linux-iio-u79uwXL29TY76Z2rM5mHXA,
	devicetree-u79uwXL29TY76Z2rM5mHXA,
	cory.tusar-J6Z/VSE8EyIAspv4Qr0y0gC/G2K4zDHf

Vladimir - before I forget, could you try to wrap emails to around 
80 characters per line.  Otherwise we end up with really tricky
to read thread when some are doing so and others aren't!

On 07/08/15 17:10, Vladimir Barinov wrote:
> Hi Lars,
> 
> On 07.08.2015 16:45, Lars-Peter Clausen wrote:
>> Hi,
>>
>> On 07/29/2015 02:57 PM, Vladimir Barinov wrote:
>>> Support triggered events.
>>>
>>> This is useful for chips that don't have their own interrupt sources.
>>> It allows to use generic/standalone iio triggers for those drivers.
>>>
>> Thanks for the patch. Can you describe in more detail how
>> INDIO_EVENT_TRIGGERED differs from the INDIO_BUFFER_TRIGGERED. By just
>> looking at the source code I don't understand quite why it is necessary.
> This is needed for the case when iio buffer is not used and used only
> iio event.
> 
> The difference to INDIO_BUFFER_TRIGGERED is that for triggered buffer
> the trigger poll function is attached (using
> iio_trigger_attach_poll_func) only in industrialio-buffer.c using
> number of steps/conditions starting by issuing 1 to buffer/enable
> sysfs path.
> 
> For chips that do not use iio buffer but use only iio events the
> iio_trigger_attach_poll_func never called.
> 
> In case of using INDIO_BUFFER_TRIGGERED the poll func is
> attached/detached via industrialio-buffer.c In case of using
> INDIO_EVENT_TRIGGERED the poll func is attached/detached via
> industrialio-trigger.c during set/change the current trigger.
> 
> Regards, Vladimir
You've convinced Lars and I agree with his argument that this
is a sensible addition to IIO.
> 

>> Thanks,
>> - Lars
>>
> 
> -- 
> To unsubscribe from this list: send the line "unsubscribe linux-iio" in
> the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply	[flat|nested] 40+ messages in thread

* Re: [PATCH v3 5/7] iio: Support triggered events
@ 2015-08-16  9:20     ` Jonathan Cameron
  0 siblings, 0 replies; 40+ messages in thread
From: Jonathan Cameron @ 2015-08-16  9:20 UTC (permalink / raw)
  To: Vladimir Barinov
  Cc: Hartmut Knaack, Lars-Peter Clausen, Peter Meerwald, Rob Herring,
	Pawel Moll, Mark Rutland, Ian Campbell, Kumar Gala, linux-kernel,
	linux-iio, devicetree, cory.tusar

On 29/07/15 13:57, Vladimir Barinov wrote:
> Support triggered events.
> 
> This is useful for chips that don't have their own interrupt sources.
> It allows to use generic/standalone iio triggers for those drivers.
As we have concluded this is a good thing to have, I've take a closer
look at it.

I think we need to slightly separate the buffer and event uses of
triggers as it's entirely plausible both might be enabled at the same
time.  Don't think this should be hard however.

I can concieve of weird restrictions from certain hardware (particularly
where hardware buffers are concerned) in which events are not monitored
when we are reading through the buffer but we can tackle that when we
actually need to.

Anyhow, few bits inline.  Thanks for your persistence with this!

Jonathan

> 
> Signed-off-by: Vladimir Barinov <vladimir.barinov@cogentembedded.com>
> ---
> Changes in version 2:
> - initially added
> Changes in version 3:
> - fixed grammar in patch description
> - changed license header to match "GNU Public License v2 or later"
> 
>  drivers/iio/Kconfig                        |  6 +++
>  drivers/iio/Makefile                       |  1 +
>  drivers/iio/industrialio-core.c            |  4 +-
>  drivers/iio/industrialio-trigger.c         | 12 +++++-
>  drivers/iio/industrialio-triggered-event.c | 68 ++++++++++++++++++++++++++++++
>  include/linux/iio/iio.h                    |  1 +
>  include/linux/iio/triggered_event.h        | 11 +++++
>  7 files changed, 99 insertions(+), 4 deletions(-)
>  create mode 100644 drivers/iio/industrialio-triggered-event.c
>  create mode 100644 include/linux/iio/triggered_event.h
> 
> diff --git a/drivers/iio/Kconfig b/drivers/iio/Kconfig
> index 4011eff..8fcc92f 100644
> --- a/drivers/iio/Kconfig
> +++ b/drivers/iio/Kconfig
> @@ -58,6 +58,12 @@ config IIO_CONSUMERS_PER_TRIGGER
>  	This value controls the maximum number of consumers that a
>  	given trigger may handle. Default is 2.
>  
> +config IIO_TRIGGERED_EVENT
> +	tristate
> +	select IIO_TRIGGER
> +	help
> +	  Provides helper functions for setting up triggered events.
> +
>  source "drivers/iio/accel/Kconfig"
>  source "drivers/iio/adc/Kconfig"
>  source "drivers/iio/amplifiers/Kconfig"
> diff --git a/drivers/iio/Makefile b/drivers/iio/Makefile
> index 698afc2..40dc13e 100644
> --- a/drivers/iio/Makefile
> +++ b/drivers/iio/Makefile
> @@ -9,6 +9,7 @@ industrialio-$(CONFIG_IIO_TRIGGER) += industrialio-trigger.o
>  industrialio-$(CONFIG_IIO_BUFFER_CB) += buffer_cb.o
>  
>  obj-$(CONFIG_IIO_TRIGGERED_BUFFER) += industrialio-triggered-buffer.o
> +obj-$(CONFIG_IIO_TRIGGERED_EVENT) += industrialio-triggered-event.o
>  obj-$(CONFIG_IIO_KFIFO_BUF) += kfifo_buf.o
>  
>  obj-y += accel/
> diff --git a/drivers/iio/industrialio-core.c b/drivers/iio/industrialio-core.c
> index 3524b0d..54d71ea 100644
> --- a/drivers/iio/industrialio-core.c
> +++ b/drivers/iio/industrialio-core.c
> @@ -948,7 +948,7 @@ static void iio_device_unregister_sysfs(struct iio_dev *indio_dev)
>  static void iio_dev_release(struct device *device)
>  {
>  	struct iio_dev *indio_dev = dev_to_iio_dev(device);
> -	if (indio_dev->modes & INDIO_BUFFER_TRIGGERED)
> +	if (indio_dev->modes & (INDIO_BUFFER_TRIGGERED | INDIO_EVENT_TRIGGERED))
>  		iio_device_unregister_trigger_consumer(indio_dev);
>  	iio_device_unregister_eventset(indio_dev);
>  	iio_device_unregister_sysfs(indio_dev);
> @@ -1218,7 +1218,7 @@ int iio_device_register(struct iio_dev *indio_dev)
>  			"Failed to register event set\n");
>  		goto error_free_sysfs;
>  	}
> -	if (indio_dev->modes & INDIO_BUFFER_TRIGGERED)
> +	if (indio_dev->modes & (INDIO_BUFFER_TRIGGERED | INDIO_EVENT_TRIGGERED))
>  		iio_device_register_trigger_consumer(indio_dev);
>  
>  	if ((indio_dev->modes & INDIO_ALL_BUFFER_MODES) &&
> diff --git a/drivers/iio/industrialio-trigger.c b/drivers/iio/industrialio-trigger.c
> index d31098e..72b63e7 100644
> --- a/drivers/iio/industrialio-trigger.c
> +++ b/drivers/iio/industrialio-trigger.c
> @@ -345,10 +345,18 @@ static ssize_t iio_trigger_write_current(struct device *dev,
>  
>  	indio_dev->trig = trig;
>  
> -	if (oldtrig)
> +	if (oldtrig) {
> +		if (indio_dev->currentmode == INDIO_EVENT_TRIGGERED)
> +			iio_trigger_detach_poll_func(oldtrig,
> +						     indio_dev->pollfunc);
>  		iio_trigger_put(oldtrig);
> -	if (indio_dev->trig)
> +	}
> +	if (indio_dev->trig) {
>  		iio_trigger_get(indio_dev->trig);
> +		if (indio_dev->currentmode == INDIO_EVENT_TRIGGERED)
> +			iio_trigger_attach_poll_func(indio_dev->trig,
> +						     indio_dev->pollfunc);
> +	}
>  
>  	return len;
>  }
> diff --git a/drivers/iio/industrialio-triggered-event.c b/drivers/iio/industrialio-triggered-event.c
> new file mode 100644
> index 0000000..1e5ad33
> --- /dev/null
> +++ b/drivers/iio/industrialio-triggered-event.c
> @@ -0,0 +1,68 @@
> + /*
> + * Copyright (C) 2015 Cogent Embedded, Inc.
> + *
> + * 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.
> + */
> +
> +#include <linux/kernel.h>
> +#include <linux/export.h>
> +#include <linux/module.h>
> +#include <linux/iio/iio.h>
> +#include <linux/iio/triggered_event.h>
> +#include <linux/iio/trigger_consumer.h>
> +
> +/**
> + * iio_triggered_event_setup() - Setup pollfunc for triggered event
> + * @indio_dev:		IIO device structure
> + * @pollfunc_bh:	Function which will be used as pollfunc bottom half
> + * @pollfunc_th:	Function which will be used as pollfunc top half
> + *
> + * This function combines some common tasks which will normally be performed
> + * when setting up a triggered event. It will allocate the pollfunc and
> + * set mode to use it for triggered event.
> + *
> + * Before calling this function the indio_dev structure should already be
> + * completely initialized, but not yet registered. In practice this means that
> + * this function should be called right before iio_device_register().
> + *
> + * To free the resources allocated by this function call
> + * iio_triggered_event_cleanup().
> + */
> +int iio_triggered_event_setup(struct iio_dev *indio_dev,
> +	irqreturn_t (*pollfunc_bh)(int irq, void *p),
> +	irqreturn_t (*pollfunc_th)(int irq, void *p))
> +{
> +	indio_dev->pollfunc = iio_alloc_pollfunc(pollfunc_bh,
> +						 pollfunc_th,
> +						 IRQF_ONESHOT,
> +						 indio_dev,
> +						 "%s_consumer%d",
> +						 indio_dev->name,
> +						 indio_dev->id);
> +	if (indio_dev->pollfunc == NULL)
> +		return -ENOMEM;
> +
> +	/* Flag that pollfunc is used for triggered event */
> +	indio_dev->modes |= INDIO_EVENT_TRIGGERED;
> +	indio_dev->currentmode = INDIO_EVENT_TRIGGERED;

Can conceive of a corner case here.  What if both buffer
and event are being triggered?  No real problem with
running them off the same trigger, but right now enabling
one will take out the other.

Might also want to not enable triggered event mode
until we enable some events?

Perhaps we need separate indio_dev->event_mode and
indio_dev->current_event_mode fields to make the two
configurable independently?

I get the feeling this particular feature is going to evolve
somewhat as we get a feel for how it is being used.

> +
> +	return 0;
> +}
> +EXPORT_SYMBOL(iio_triggered_event_setup);
> +
> +/**
> + * iio_triggered_event_cleanup() - Free resources allocated by iio_triggered_event_setup()
> + * @indio_dev: IIO device structure
> + */
> +void iio_triggered_event_cleanup(struct iio_dev *indio_dev)
> +{
> +	iio_dealloc_pollfunc(indio_dev->pollfunc);
> +}
> +EXPORT_SYMBOL(iio_triggered_event_cleanup);
> +
> +MODULE_AUTHOR("Vladimir Barinov");
> +MODULE_DESCRIPTION("IIO helper functions for setting up triggered events");
> +MODULE_LICENSE("GPL");
> diff --git a/include/linux/iio/iio.h b/include/linux/iio/iio.h
> index f791482..b691ee0 100644
> --- a/include/linux/iio/iio.h
> +++ b/include/linux/iio/iio.h
> @@ -294,6 +294,7 @@ static inline s64 iio_get_time_ns(void)
>  #define INDIO_BUFFER_TRIGGERED		0x02
>  #define INDIO_BUFFER_SOFTWARE		0x04
>  #define INDIO_BUFFER_HARDWARE		0x08
> +#define INDIO_EVENT_TRIGGERED		0x10
>  
>  #define INDIO_ALL_BUFFER_MODES					\
>  	(INDIO_BUFFER_TRIGGERED | INDIO_BUFFER_HARDWARE | INDIO_BUFFER_SOFTWARE)
> diff --git a/include/linux/iio/triggered_event.h b/include/linux/iio/triggered_event.h
> new file mode 100644
> index 0000000..e9894e9
> --- /dev/null
> +++ b/include/linux/iio/triggered_event.h
> @@ -0,0 +1,11 @@
> +#ifndef _LINUX_IIO_TRIGGERED_EVENT_H_
> +#define _LINUX_IIO_TRIGGERED_EVENT_H_
> +
> +#include <linux/interrupt.h>
> +
> +int iio_triggered_event_setup(struct iio_dev *indio_dev,
> +	irqreturn_t (*pollfunc_bh)(int irq, void *p),
> +	irqreturn_t (*pollfunc_th)(int irq, void *p));
We renamed the parameters of the equivalent buffer function recently
to avoid some confusion.  Could you follow that for this one as well.

> +void iio_triggered_event_cleanup(struct iio_dev *indio_dev);
> +
> +#endif
> 


^ permalink raw reply	[flat|nested] 40+ messages in thread

* Re: [PATCH v3 5/7] iio: Support triggered events
@ 2015-08-16  9:20     ` Jonathan Cameron
  0 siblings, 0 replies; 40+ messages in thread
From: Jonathan Cameron @ 2015-08-16  9:20 UTC (permalink / raw)
  To: Vladimir Barinov
  Cc: Hartmut Knaack, Lars-Peter Clausen, Peter Meerwald, Rob Herring,
	Pawel Moll, Mark Rutland, Ian Campbell, Kumar Gala,
	linux-kernel-u79uwXL29TY76Z2rM5mHXA,
	linux-iio-u79uwXL29TY76Z2rM5mHXA,
	devicetree-u79uwXL29TY76Z2rM5mHXA,
	cory.tusar-J6Z/VSE8EyIAspv4Qr0y0gC/G2K4zDHf

On 29/07/15 13:57, Vladimir Barinov wrote:
> Support triggered events.
> 
> This is useful for chips that don't have their own interrupt sources.
> It allows to use generic/standalone iio triggers for those drivers.
As we have concluded this is a good thing to have, I've take a closer
look at it.

I think we need to slightly separate the buffer and event uses of
triggers as it's entirely plausible both might be enabled at the same
time.  Don't think this should be hard however.

I can concieve of weird restrictions from certain hardware (particularly
where hardware buffers are concerned) in which events are not monitored
when we are reading through the buffer but we can tackle that when we
actually need to.

Anyhow, few bits inline.  Thanks for your persistence with this!

Jonathan

> 
> Signed-off-by: Vladimir Barinov <vladimir.barinov-M4DtvfQ/ZS1MRgGoP+s0PdBPR1lH4CV8@public.gmane.org>
> ---
> Changes in version 2:
> - initially added
> Changes in version 3:
> - fixed grammar in patch description
> - changed license header to match "GNU Public License v2 or later"
> 
>  drivers/iio/Kconfig                        |  6 +++
>  drivers/iio/Makefile                       |  1 +
>  drivers/iio/industrialio-core.c            |  4 +-
>  drivers/iio/industrialio-trigger.c         | 12 +++++-
>  drivers/iio/industrialio-triggered-event.c | 68 ++++++++++++++++++++++++++++++
>  include/linux/iio/iio.h                    |  1 +
>  include/linux/iio/triggered_event.h        | 11 +++++
>  7 files changed, 99 insertions(+), 4 deletions(-)
>  create mode 100644 drivers/iio/industrialio-triggered-event.c
>  create mode 100644 include/linux/iio/triggered_event.h
> 
> diff --git a/drivers/iio/Kconfig b/drivers/iio/Kconfig
> index 4011eff..8fcc92f 100644
> --- a/drivers/iio/Kconfig
> +++ b/drivers/iio/Kconfig
> @@ -58,6 +58,12 @@ config IIO_CONSUMERS_PER_TRIGGER
>  	This value controls the maximum number of consumers that a
>  	given trigger may handle. Default is 2.
>  
> +config IIO_TRIGGERED_EVENT
> +	tristate
> +	select IIO_TRIGGER
> +	help
> +	  Provides helper functions for setting up triggered events.
> +
>  source "drivers/iio/accel/Kconfig"
>  source "drivers/iio/adc/Kconfig"
>  source "drivers/iio/amplifiers/Kconfig"
> diff --git a/drivers/iio/Makefile b/drivers/iio/Makefile
> index 698afc2..40dc13e 100644
> --- a/drivers/iio/Makefile
> +++ b/drivers/iio/Makefile
> @@ -9,6 +9,7 @@ industrialio-$(CONFIG_IIO_TRIGGER) += industrialio-trigger.o
>  industrialio-$(CONFIG_IIO_BUFFER_CB) += buffer_cb.o
>  
>  obj-$(CONFIG_IIO_TRIGGERED_BUFFER) += industrialio-triggered-buffer.o
> +obj-$(CONFIG_IIO_TRIGGERED_EVENT) += industrialio-triggered-event.o
>  obj-$(CONFIG_IIO_KFIFO_BUF) += kfifo_buf.o
>  
>  obj-y += accel/
> diff --git a/drivers/iio/industrialio-core.c b/drivers/iio/industrialio-core.c
> index 3524b0d..54d71ea 100644
> --- a/drivers/iio/industrialio-core.c
> +++ b/drivers/iio/industrialio-core.c
> @@ -948,7 +948,7 @@ static void iio_device_unregister_sysfs(struct iio_dev *indio_dev)
>  static void iio_dev_release(struct device *device)
>  {
>  	struct iio_dev *indio_dev = dev_to_iio_dev(device);
> -	if (indio_dev->modes & INDIO_BUFFER_TRIGGERED)
> +	if (indio_dev->modes & (INDIO_BUFFER_TRIGGERED | INDIO_EVENT_TRIGGERED))
>  		iio_device_unregister_trigger_consumer(indio_dev);
>  	iio_device_unregister_eventset(indio_dev);
>  	iio_device_unregister_sysfs(indio_dev);
> @@ -1218,7 +1218,7 @@ int iio_device_register(struct iio_dev *indio_dev)
>  			"Failed to register event set\n");
>  		goto error_free_sysfs;
>  	}
> -	if (indio_dev->modes & INDIO_BUFFER_TRIGGERED)
> +	if (indio_dev->modes & (INDIO_BUFFER_TRIGGERED | INDIO_EVENT_TRIGGERED))
>  		iio_device_register_trigger_consumer(indio_dev);
>  
>  	if ((indio_dev->modes & INDIO_ALL_BUFFER_MODES) &&
> diff --git a/drivers/iio/industrialio-trigger.c b/drivers/iio/industrialio-trigger.c
> index d31098e..72b63e7 100644
> --- a/drivers/iio/industrialio-trigger.c
> +++ b/drivers/iio/industrialio-trigger.c
> @@ -345,10 +345,18 @@ static ssize_t iio_trigger_write_current(struct device *dev,
>  
>  	indio_dev->trig = trig;
>  
> -	if (oldtrig)
> +	if (oldtrig) {
> +		if (indio_dev->currentmode == INDIO_EVENT_TRIGGERED)
> +			iio_trigger_detach_poll_func(oldtrig,
> +						     indio_dev->pollfunc);
>  		iio_trigger_put(oldtrig);
> -	if (indio_dev->trig)
> +	}
> +	if (indio_dev->trig) {
>  		iio_trigger_get(indio_dev->trig);
> +		if (indio_dev->currentmode == INDIO_EVENT_TRIGGERED)
> +			iio_trigger_attach_poll_func(indio_dev->trig,
> +						     indio_dev->pollfunc);
> +	}
>  
>  	return len;
>  }
> diff --git a/drivers/iio/industrialio-triggered-event.c b/drivers/iio/industrialio-triggered-event.c
> new file mode 100644
> index 0000000..1e5ad33
> --- /dev/null
> +++ b/drivers/iio/industrialio-triggered-event.c
> @@ -0,0 +1,68 @@
> + /*
> + * Copyright (C) 2015 Cogent Embedded, Inc.
> + *
> + * 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.
> + */
> +
> +#include <linux/kernel.h>
> +#include <linux/export.h>
> +#include <linux/module.h>
> +#include <linux/iio/iio.h>
> +#include <linux/iio/triggered_event.h>
> +#include <linux/iio/trigger_consumer.h>
> +
> +/**
> + * iio_triggered_event_setup() - Setup pollfunc for triggered event
> + * @indio_dev:		IIO device structure
> + * @pollfunc_bh:	Function which will be used as pollfunc bottom half
> + * @pollfunc_th:	Function which will be used as pollfunc top half
> + *
> + * This function combines some common tasks which will normally be performed
> + * when setting up a triggered event. It will allocate the pollfunc and
> + * set mode to use it for triggered event.
> + *
> + * Before calling this function the indio_dev structure should already be
> + * completely initialized, but not yet registered. In practice this means that
> + * this function should be called right before iio_device_register().
> + *
> + * To free the resources allocated by this function call
> + * iio_triggered_event_cleanup().
> + */
> +int iio_triggered_event_setup(struct iio_dev *indio_dev,
> +	irqreturn_t (*pollfunc_bh)(int irq, void *p),
> +	irqreturn_t (*pollfunc_th)(int irq, void *p))
> +{
> +	indio_dev->pollfunc = iio_alloc_pollfunc(pollfunc_bh,
> +						 pollfunc_th,
> +						 IRQF_ONESHOT,
> +						 indio_dev,
> +						 "%s_consumer%d",
> +						 indio_dev->name,
> +						 indio_dev->id);
> +	if (indio_dev->pollfunc == NULL)
> +		return -ENOMEM;
> +
> +	/* Flag that pollfunc is used for triggered event */
> +	indio_dev->modes |= INDIO_EVENT_TRIGGERED;
> +	indio_dev->currentmode = INDIO_EVENT_TRIGGERED;

Can conceive of a corner case here.  What if both buffer
and event are being triggered?  No real problem with
running them off the same trigger, but right now enabling
one will take out the other.

Might also want to not enable triggered event mode
until we enable some events?

Perhaps we need separate indio_dev->event_mode and
indio_dev->current_event_mode fields to make the two
configurable independently?

I get the feeling this particular feature is going to evolve
somewhat as we get a feel for how it is being used.

> +
> +	return 0;
> +}
> +EXPORT_SYMBOL(iio_triggered_event_setup);
> +
> +/**
> + * iio_triggered_event_cleanup() - Free resources allocated by iio_triggered_event_setup()
> + * @indio_dev: IIO device structure
> + */
> +void iio_triggered_event_cleanup(struct iio_dev *indio_dev)
> +{
> +	iio_dealloc_pollfunc(indio_dev->pollfunc);
> +}
> +EXPORT_SYMBOL(iio_triggered_event_cleanup);
> +
> +MODULE_AUTHOR("Vladimir Barinov");
> +MODULE_DESCRIPTION("IIO helper functions for setting up triggered events");
> +MODULE_LICENSE("GPL");
> diff --git a/include/linux/iio/iio.h b/include/linux/iio/iio.h
> index f791482..b691ee0 100644
> --- a/include/linux/iio/iio.h
> +++ b/include/linux/iio/iio.h
> @@ -294,6 +294,7 @@ static inline s64 iio_get_time_ns(void)
>  #define INDIO_BUFFER_TRIGGERED		0x02
>  #define INDIO_BUFFER_SOFTWARE		0x04
>  #define INDIO_BUFFER_HARDWARE		0x08
> +#define INDIO_EVENT_TRIGGERED		0x10
>  
>  #define INDIO_ALL_BUFFER_MODES					\
>  	(INDIO_BUFFER_TRIGGERED | INDIO_BUFFER_HARDWARE | INDIO_BUFFER_SOFTWARE)
> diff --git a/include/linux/iio/triggered_event.h b/include/linux/iio/triggered_event.h
> new file mode 100644
> index 0000000..e9894e9
> --- /dev/null
> +++ b/include/linux/iio/triggered_event.h
> @@ -0,0 +1,11 @@
> +#ifndef _LINUX_IIO_TRIGGERED_EVENT_H_
> +#define _LINUX_IIO_TRIGGERED_EVENT_H_
> +
> +#include <linux/interrupt.h>
> +
> +int iio_triggered_event_setup(struct iio_dev *indio_dev,
> +	irqreturn_t (*pollfunc_bh)(int irq, void *p),
> +	irqreturn_t (*pollfunc_th)(int irq, void *p));
We renamed the parameters of the equivalent buffer function recently
to avoid some confusion.  Could you follow that for this one as well.

> +void iio_triggered_event_cleanup(struct iio_dev *indio_dev);
> +
> +#endif
> 

^ permalink raw reply	[flat|nested] 40+ messages in thread

* Re: [PATCH v3 1/7] iio: adc: hi8435: Holt HI-8435 threshold detector
@ 2015-08-16 18:54           ` Vladimir Barinov
  0 siblings, 0 replies; 40+ messages in thread
From: Vladimir Barinov @ 2015-08-16 18:54 UTC (permalink / raw)
  To: Jonathan Cameron
  Cc: Hartmut Knaack, Lars-Peter Clausen, Peter Meerwald, Rob Herring,
	Pawel Moll, Mark Rutland, Ian Campbell, Kumar Gala, linux-kernel,
	linux-iio, devicetree, cory.tusar

Hi Jonathan,

On 16.08.2015 12:00, Jonathan Cameron wrote:
> On 11/08/15 15:37, Vladimir Barinov wrote:
>> Hi Jonathan,
>>
>> Thank you for the review.
> Sorry I was being so indecisive.  Always take a while when something
> 'new' turns up to gather opinions and select the best option.  You
> are just the unlucky guy who happened to hit the question first!
>
> Anyhow, I'm basically happy (now) with the events approach though
> I would suggest some modifications to how exactly it is done
> (see inline).
Nice to hear!
I've got some light in the tunnel :)

Thank you for dealing with this stuff.
Please find one minor questions below.

Regards,
Vladimir

>
> Jonathan
>> On 08.08.2015 20:56, Jonathan Cameron wrote:
>>> On 29/07/15 13:56, Vladimir Barinov wrote:
>>>> Add Holt threshold detector driver for HI-8435 chip
>>>>
>>>> Signed-off-by: Vladimir Barinov <vladimir.barinov@cogentembedded.com>
>>> Because it usually reads better that way, I review from the bottom.
>>> Might make more sense if you read the comments that way around ;)
>> I did try to match this way.
>> At some places the comments below the code, but in other places the comments are above the code :)
> Yeah, I've never been consistent ;)
>>> I'm going to guess that the typical usecase for this device is in realtime
>>> systems where polling gives a nice predictable timing (hence the lack of any
>>> interrupt support).  These typically operate as 'scanning' devices
>>> (e.g. you update all state at approximately the same time, by reading
>>> one input after another in some predefined order).
>> Probably you are right about 8435 h/w design or there were other
>> reason why this functionality was dropped in comparison to 8429
>> chip. The Holt chip hi-8429 has both hardware interrupt and hardware
>> debounce enable lines, but it has less amount of sensors (just 8
>> outputs).
>>> Does the use of events actually map well to this use case?  You are taking
>>> something that (other than the results being a bit different) is basically
>>> a digital I/O interface and mapping it (sort of) to an interrupt chip
>>> when it isn't nothing of the sort.
>> a) Using hardware point of view.
>>
>> I was thinking that it matches the events a little bit more then
>> buffer, because the chip senses for threshold changes (passing
>> threshold high or threshold low margins) and then notifies the upper
>> layer. The bad point here is that the chip does not have interrupt
>> line for sensor state change (i.e. threshold passing).
>>
>> If it would have the interrupt line for sensor state changes then the
>> event interface map this case best way.
> Agreed.  I'm coming round to the argument Lars made that we should end
> up with the interface being the same whether or not there is an interrupt
> there.  Actually I suspect in the long run we will end up with both
> a buffered interface and an event interface as they simply have different
> usecases for the same hardware (which is why we are having these lengthy
> discussions in the first place!)
>
>> b) Using s/w point of view
>>
>> I answer relying on current iio stack implementation. I do understand
>> that the 8435 chip is not the common/usual iio client like
>> adc/dac/light/others, so I'm trying to match the existent iio stack.
> Yes.  It's certainly throwing up lots of questions!
>>  From s/w point of view it does not matter much difference between
>> event or buffer interface for this chip because in provides 1-bit
>> change and does not have it's own interrupt source line.
> Yes, just requires the userspace side to track the state if it wants
> to know.  Actually the one nasty is initialization. When the events
> are first enabled I guess we fire them all once (if any limits are currently
> breached).  That way the state can always be known.
>> The event interface has generic interface to fill in falling/rising
>> thresholds but buffer interface does not.
> We can always add more ABI.
>
>> The buffer interface has already has the trigger poll func support
>> but event interface does not. So I've tried to implement the
>> triggered event in iio stack. BTW: Doesn't the triggered event
>> support match the usecase with iio_interrupt_trigger? Or it does not
>> make sense to have triggered events (even with irqtrig) at all?
> Initially I was unsure about this, but Lars and you have convinced me.
> I'm happy having that in the subsystem.  Can also be used for devices
> which have an interrupt but which didn't get wired for some reason rather
> than having them have some internal poll loop (as we have done up to now).
>
>   
>>> I'd like more opinions on this, but my gut reaction is that we would
>>> be better making the normal buffered infrastructure handle this data
>>> properly rather than pushing it through the events intrastructure.
>>>
>>> Your solution is neat and tidy in someways but feels like it is mashing
>>> data into a particular format.
>> Probably that's all because the chip lacks smth from buffer interface
>> approach (the data should diferent the 1-bit) and smth from event
>> interface approach (lack of h/w interrupt line)
> Agreed.
>>> To add to the complexity we could have debounce logic.  If we mapped to a
>>> fill the buffer with a scan mode, how would we handle this?
>>> My guess is that it would simply delay transistions.  Perhaps we even
>>> support reading both the value right now and the debounced value if that
>>> is possible?
>> I did handle it in the driver the same way in both cases (buffer and
>> event interfaces): event/buffer trigger issues interrupt and then the
>> driver checks value right now and after delay (debounce_interval).
> I am yet to be convinced we want software debounce in the kernel.  This
> is something that the input subsystem has always had.  Partly the aim of
> IIO was to provide a slim system where all this stuff was moved to userspace.
Ok, I will remove the s/w debounce in the driver for now.

>
> Ultimately the ideal option to my mind is to finally write an inkernel
> interface for the events data flow (as we have for buffered data) then
> we can have a generic attachable debounce unit independent of any particular
> driver.  Hohum.  Now all I need is someone to write it or a few weeks off
> work ;)
This is a nice design :)
But much more hard then implement this in the driver.

>> If we do not plan to put debounced values to buffer then we may cache
>> during debouncing procedure and then return via
>> IIO_CHAN_INFO_DEBOUNCE_VALUE (or other info name). Probably even with
>> timestamp.
>>> However, here the debounce is all software.  To my mind, move this into
>>> userspace entirely. We aren't dealing with big data here so it's hardly
>>> going to be particularly challenging.
>> I will drop it on your demand in favour to encourage chipmakers to provide h/w debounce,
>> but this was much easy/efficient to have it in kernel space, since
>> during run generic_buffer sample (or other) we got the new data and then we have to start timer, then timer expires
>> and we read raw values from sysfs manually to compare the one that came from buffer and one after debounce
>> delay.
> If you are clocking evenly and we were using buffering you'd simply do it as readback in time
> rather than the other way around - thus no timer at all.  Just each time check the last N
> samples to see if they are all high or all low or similar.
This is right for high frequency clocking.
If we are clocking evently at low frequency (~1Hz) and the recommended 
debounce interval is 10-100ms then
we should use timer for issuing extra clock inside 10-100ms for 
check/validate the event.
>
>>> So my gut feeling is definitely we need to make the buffer infrastructure
>>> handle 1 bit values (in groups) then push this data out that way.
>> I will wait for your final decision.
> Lets proceed with the event version.  If someone has a strong usecase
> for adding buffering as well, there is nothing stopping the driver doing both
> that I can see (once we have the core infrastructure in place).
Ok, thx.

>>> Still I would like other opinions on this!
>>>
>>> Jonathan
>>>
>>>> ---
>>>> Changes in version 2:
>>>> - Added file sysfs-bus-iio-adc-hi8435
>>>> - Changed naming from "discrete ADC" to "threshold detector"
>>>> - Replaced swab16p/swab32p with be16_to_cpup/be32_to_cpup
>>>> - Made *_show and *_store functions static
>>>> - moved out from iio buffers to iio events
>>>> - removed hi8436/hi8437 chips from the driver
>>>> - moved from debounce_soft_delay/enable to debounce_interval via
>>>>     IIO_CHAN_INFO_DEBOUNCE_TIME
>>>> - added name extention "comparator"
>>>> - moved threshold/hysteresis setup via generic iio event sysfs
>>>> - added software mask/unmask channel events
>>>> - added programming sensor outputs while in test mode via
>>>>     IIO_CHAN_INFO_RAW
>>>> - added channels .ext_info for programming sensing mode
>>>> Changes in version 3:
>>>> - fixed typos
>>>> - moved <linux/interrupt.h> to match alphabetic order
>>>> - fixed missed */event/* prefix in the ABI description
>>>> - added missed colon in sysfs-bus-iio-adc-hi8435 file after "What"
>>>> - moved in_voltageY_comparator_thresh_either_en and debounce_time
>>>>     to sysfs-bus-iio file
>>>> - added error checking for hi8435_write[b|w]
>>>>
>>>>    Documentation/ABI/testing/sysfs-bus-iio            |   1 +
>>>>    Documentation/ABI/testing/sysfs-bus-iio-adc-hi8435 |  61 ++
>>>>    drivers/iio/adc/Kconfig                            |  11 +
>>>>    drivers/iio/adc/Makefile                           |   1 +
>>>>    drivers/iio/adc/hi8435.c                           | 659 +++++++++++++++++++++
>>>>    5 files changed, 742 insertions(+)
>>>>    create mode 100644 Documentation/ABI/testing/sysfs-bus-iio-adc-hi8435
>>>>    create mode 100644 drivers/iio/adc/hi8435.c
>>>>
>>>> diff --git a/Documentation/ABI/testing/sysfs-bus-iio b/Documentation/ABI/testing/sysfs-bus-iio
>>>> index 70c9b1a..09eac44 100644
>>>> --- a/Documentation/ABI/testing/sysfs-bus-iio
>>>> +++ b/Documentation/ABI/testing/sysfs-bus-iio
>>>> @@ -572,6 +572,7 @@ What:        /sys/.../iio:deviceX/events/in_rot_from_north_magnetic_tilt_comp_thresh_r
>>>>    What:        /sys/.../iio:deviceX/events/in_rot_from_north_magnetic_tilt_comp_thresh_falling_en
>>>>    What:        /sys/.../iio:deviceX/events/in_rot_from_north_true_tilt_comp_thresh_rising_en
>>>>    What:        /sys/.../iio:deviceX/events/in_rot_from_north_true_tilt_comp_thresh_falling_en
>>>> +What:        /sys/.../iio:deviceX/events/in_voltageY_comparator_thresh_either_en
>>>>    What:        /sys/.../iio:deviceX/events/in_voltageY_supply_thresh_rising_en
>>>>    What:        /sys/.../iio:deviceX/events/in_voltageY_supply_thresh_falling_en
>>>>    What:        /sys/.../iio:deviceX/events/in_voltageY_thresh_rising_en
>>>> diff --git a/Documentation/ABI/testing/sysfs-bus-iio-adc-hi8435 b/Documentation/ABI/testing/sysfs-bus-iio-adc-hi8435
>>>> new file mode 100644
>>>> index 0000000..c59d81d
>>>> --- /dev/null
>>>> +++ b/Documentation/ABI/testing/sysfs-bus-iio-adc-hi8435
>>>> @@ -0,0 +1,61 @@
>>>> +What:        /sys/bus/iio/devices/iio:deviceX/in_voltageY_comparator_raw
> We really try to use the extended names infrequently and normally only
> for things like 'this voltage channel is internally connected to the power
> supply rail'.  Don't think we need it here.   As suggested below, if we
> are going events then we don't want to expose a _raw reading except via
> in the 'events' directory where it would be a 'current event status'.
> Anyhow, I talk about that futher down.
I will remove *_raw.

>>>> +Date:        July 2015
>>>> +KernelVersion:    4.2.0
>>>> +Contact:    source@cogentembedded.com
>>>> +Description:
>>>> +        Read value is a voltage threshold measurement from channel Y.
>>>> +        Could be either 0 if sensor voltage is lower then low voltage
>>>> +        threshold or 1 if sensor voltage is higher then high voltage
>>>> +        threshold.
>>>> +        Write value is a programmed sensor output while in self test
>>>> +        mode. Could be either 0 or 1. The programmed value will be read
>>>> +        back if /sys/bus/iio/devices/iio:deviceX/test_enable is set to 1.
>>>> +
>>>> +What:        /sys/bus/iio/devices/iio:deviceX/test_enable
>>>> +Date:        July 2015
>>>> +KernelVersion:    4.2.0
>>>> +Contact:    source@cogentembedded.com
>>>> +Description:
>>>> +        Enable/disable the HI-8435 self test mode.
>>>> +        If enabled the in_voltageY_comparator_raw should be read back
>>>> +        accordingly to written value to in_voltageY_comparator_raw.
> Hmm. Normally we just do self tests at startup.  So set some patterns, read them
> and then verify they are fine.  Drops the need for a userspace interface
> that can get a little confusing (writing to what is normally a read
> only attribute).  Would this work here?
>
> Alternatively, just add a debugfs interface to allow this to be set rather than
> exposing it via sysfs all the time?
I will add debugfs or drop it (since I did use write_raw() for setting 
sensor output in test mode)

>
>>>> +
>>>> +What:        /sys/bus/iio/devices/iio:deviceX/in_voltageY_comparator_sensing_mode
>>>> +Date:        July 2015
>>>> +KernelVersion:    4.2.0
>>>> +Contact:    source@cogentembedded.com
>>>> +Description:
>>>> +        Program sensor type for threshold detector inputs.
>>>> +        Could be either "GND-Open" or "Supply-Open" mode. Y is a
>>>> +        threshold detector input channel. Channels 0..7, 8..15, 16..23
>>>> +        and 24..31 has common sensor types.
>>>> +
>>>> +What:        /sys/bus/iio/devices/iio:deviceX/events/in_voltageY_comparator_thresh_falling_value
> This is where the comparator extended name looks a bit silly as we are
> clearly comparing it anyway due to the thresh_falling part ;)
I will remove the extended name.

>>>> +Date:        July 2015
>>>> +KernelVersion:    4.2.0
>>>> +Contact:    source@cogentembedded.com
>>>> +Description:
>>>> +        Channel Y low voltage threshold. If sensor input voltage goes lower then
>>>> +        this value then the threshold falling event is pushed.
>>>> +        Depending on in_voltageY_comparator_sensing_mode the low voltage threshold
>>>> +        is separately set for "GND-Open" and "Supply-Open" modes.
>>>> +        Channels 0..31 have common low threshold values, but could have different
>>>> +        sensing_modes.
>>>> +        The low voltage threshold range is between 2..21V.
>>>> +        Hysteresis between low and high thresholds can not be lower then 2 and
>>>> +        can not be odd.
>>>> +
>>>> +What:        /sys/bus/iio/devices/iio:deviceX/events/in_voltageY_comparator_thresh_rising_value
>>>> +Date:        July 2015
>>>> +KernelVersion:    4.2.0
>>>> +Contact:    source@cogentembedded.com
>>>> +Description:
>>>> +        Channel Y high voltage threshold. If sensor input voltage goes higher then
>>>> +        this value then the threshold rising event is pushed.
>>>> +        Depending on in_voltageY_comparator_sensing_mode the high voltage threshold
>>>> +        is separately set for "GND-Open" and "Supply-Open" modes.
>>>> +        Channels 0..31 have common high threshold values, but could have different
>>>> +        sensing_modes.
>>>> +        The high voltage threshold range is between 3..22V.
>>>> +        Hysteresis between low and high thresholds can not be lower then 2 and
>>>> +        can not be odd.
>>>> diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig
>>>> index eb0cd89..553c91e 100644
>>>> --- a/drivers/iio/adc/Kconfig
>>>> +++ b/drivers/iio/adc/Kconfig
>>>> @@ -170,6 +170,17 @@ config EXYNOS_ADC
>>>>          of SoCs for drivers such as the touchscreen and hwmon to use to share
>>>>          this resource.
>>>>    +config HI8435
>>>> +    tristate "Holt Integrated Circuits HI-8435 threshold detector"
>>>> +    select IIO_TRIGGERED_EVENT
>>>> +    depends on SPI
>>>> +    help
>>>> +      If you say yes here you get support for Holt Integrated Circuits
>>>> +      HI-8435 chip.
>>>> +
>>>> +      This driver can also be built as a module. If so, the module will be
>>>> +      called hi8435.
>>>> +
>>>>    config LP8788_ADC
>>>>        tristate "LP8788 ADC driver"
>>>>        depends on MFD_LP8788
>>>> diff --git a/drivers/iio/adc/Makefile b/drivers/iio/adc/Makefile
>>>> index a096210..00f367aa 100644
>>>> --- a/drivers/iio/adc/Makefile
>>>> +++ b/drivers/iio/adc/Makefile
>>>> @@ -19,6 +19,7 @@ obj-$(CONFIG_BERLIN2_ADC) += berlin2-adc.o
>>>>    obj-$(CONFIG_DA9150_GPADC) += da9150-gpadc.o
>>>>    obj-$(CONFIG_CC10001_ADC) += cc10001_adc.o
>>>>    obj-$(CONFIG_EXYNOS_ADC) += exynos_adc.o
>>>> +obj-$(CONFIG_HI8435) += hi8435.o
>>>>    obj-$(CONFIG_LP8788_ADC) += lp8788_adc.o
>>>>    obj-$(CONFIG_MAX1027) += max1027.o
>>>>    obj-$(CONFIG_MAX1363) += max1363.o
>>>> diff --git a/drivers/iio/adc/hi8435.c b/drivers/iio/adc/hi8435.c
>>>> new file mode 100644
>>>> index 0000000..f3dab5a
>>>> --- /dev/null
>>>> +++ b/drivers/iio/adc/hi8435.c
>>>> @@ -0,0 +1,659 @@
>>>> +/*
>>>> + * Holt Integrated Circuits HI-8435 threshold detector driver
>>>> + *
>>>> + * Copyright (C) 2015 Zodiac Inflight Innovations
>>>> + * Copyright (C) 2015 Cogent Embedded, Inc.
>>>> + *
>>>> + * 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.
>>>> + */
>>>> +
>>>> +#include <linux/delay.h>
>>>> +#include <linux/iio/events.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/triggered_event.h>
>>>> +#include <linux/interrupt.h>
>>>> +#include <linux/module.h>
>>>> +#include <linux/of.h>
>>>> +#include <linux/of_device.h>
>>>> +#include <linux/of_gpio.h>
>>>> +#include <linux/spi/spi.h>
>>>> +#include <linux/workqueue.h>
>>>> +
>>>> +#define DRV_NAME "hi8435"
>>>> +
>>>> +/* Register offsets for HI-8435 */
>>>> +#define HI8435_CTRL_REG            0x02
>>>> +#define HI8435_PSEN_REG            0x04
>>>> +#define HI8435_TMDATA_REG        0x1E
>>>> +#define HI8435_GOCENHYS_REG        0x3A
>>>> +#define HI8435_SOCENHYS_REG        0x3C
>>> Hmm. These aren't in the docs that I can immediately acquire...
>>> Any chance of some more meaningful naming?
>> I did follow the register naming in the chip datasheet:
>> http://www.holtic.com/products/3081-hi-8435.aspx
>>
>> The SO7_0 means "Sensor status bank 0 register: SO<7:0>".
>> Does HI8435_STATUS_BANK0_REG work?
> Ah, I missed the meaning entirely by not noticing they were 8 bits
> each ;)  This current naming is fine.
>> Or should I just add comments instead of changing definition?
>>>> +#define HI8435_SO7_0_REG        0x10
>>>> +#define HI8435_SO15_8_REG        0x12
>>>    +#define HI8435_SO23_16_REG        0x14
>>>> +#define HI8435_SO31_24_REG        0x16
>>>> +#define HI8435_SO31_0_REG        0x78
>>>> +
>>>> +#define HI8435_WRITE_OPCODE        0x00
>>>> +#define HI8435_READ_OPCODE        0x80
>>>> +
>>>> +/* CTRL register bits */
>>>> +#define HI8435_CTRL_TEST        0x01
>>>> +#define HI8435_CTRL_SRST        0x02
>>>> +
>>>> +#define HI8435_DEBOUNCE_DELAY_MAX    1000    /* msec */
>>>> +#define HI8435_DEBOUNCE_DELAY_DEF    100    /* msec */
>>>> +
>>>> +struct hi8435_priv {
>>>> +    struct spi_device *spi;
>>>> +    struct mutex lock;
>>>> +    struct delayed_work work;
>>>> +
>>>> +    int reset_gpio;
>>>> +    int debounce_interval; /* msec */
>>>> +    u32 debounce_val; /* prev value to compare during software debounce */
>>>> +
>>>> +    unsigned long event_scan_mask; /* soft mask/unmask channels events */
>>>> +    unsigned int event_prev_val;
>>>> +
>>>> +    unsigned threshold_lo[2]; /* GND-Open and Supply-Open thresholds */
>>>> +    unsigned threshold_hi[2]; /* GND-Open and Supply-Open threshold */
>>>> +    u8 reg_buffer[4] ____cacheline_aligned;
>>>> +};
>>>> +
>>>> +static int hi8435_readb(struct hi8435_priv *priv, u8 reg, u8 *val)
>>>> +{
>>>> +    reg |= HI8435_READ_OPCODE;
>>>> +    return spi_write_then_read(priv->spi, &reg, 1, val, 1);
>>>> +}
>>>> +
>>>> +static int hi8435_readw(struct hi8435_priv *priv, u8 reg, u16 *val)
>>>> +{
>>>> +    int ret;
>>>> +
>>>> +    reg |= HI8435_READ_OPCODE;
>>>> +    ret = spi_write_then_read(priv->spi, &reg, 1, val, 2);
>>>> +    *val = be16_to_cpup(val);
>>>> +
>>>> +    return ret;
>>>> +}
>>>> +
>>>> +static int hi8435_readl(struct hi8435_priv *priv, u8 reg, u32 *val)
>>>> +{
>>>> +    int ret;
>>>> +
>>>> +    reg |= HI8435_READ_OPCODE;
>>>> +    ret = spi_write_then_read(priv->spi, &reg, 1, val, 4);
>>>> +    *val = be32_to_cpup(val);
>>>> +
>>>> +    return ret;
>>>> +}
>>>> +
>>>> +static int hi8435_writeb(struct hi8435_priv *priv, u8 reg, u8 val)
>>>> +{
>>>> +    priv->reg_buffer[0] = reg | HI8435_WRITE_OPCODE;
>>>> +    priv->reg_buffer[1] = val;
>>>> +
>>>> +    return spi_write(priv->spi, priv->reg_buffer, 2);
>>>> +}
>>>> +
>>>> +static int hi8435_writew(struct hi8435_priv *priv, u8 reg, u16 val)
>>>> +{
>>>> +    priv->reg_buffer[0] = reg | HI8435_WRITE_OPCODE;
>>>> +    priv->reg_buffer[1] = (val >> 8) & 0xff;
>>>> +    priv->reg_buffer[2] = val & 0xff;
>>>> +
>>>> +    return spi_write(priv->spi, priv->reg_buffer, 3);
>>>> +}
>>>> +
>>>> +static ssize_t hi8435_test_enable_show(struct device *dev,
>>>> +                       struct device_attribute *attr, char *buf)
>>>> +{
>>>> +    struct hi8435_priv *priv = iio_priv(dev_to_iio_dev(dev));
>>>> +    int ret;
>>>> +    u8 reg;
>>>> +
>>>> +    ret = hi8435_readb(priv, HI8435_CTRL_REG, &reg);
>>>> +    if (ret < 0)
>>>> +        return ret;
>>>> +
>>>> +    return sprintf(buf, "%d\n", reg & HI8435_CTRL_TEST);
>>>> +}
>>>> +
>>>> +static ssize_t hi8435_test_enable_store(struct device *dev,
>>>> +                    struct device_attribute *attr,
>>>> +                    const char *buf, size_t len)
>>>> +{
>>>> +    struct hi8435_priv *priv = iio_priv(dev_to_iio_dev(dev));
>>>> +    unsigned int val;
>>>> +    int ret;
>>>> +
>>>> +    ret = kstrtouint(buf, 10, &val);
>>>> +    if (ret)
>>>> +        return ret;
>>>> +
>>>> +    ret = hi8435_writeb(priv, HI8435_CTRL_REG, val ? HI8435_CTRL_TEST : 0);
>>>> +    if (ret < 0)
>>>> +        return ret;
>>>> +
>>>> +    return len;
>>>> +}
>>>> +
>>>> +static IIO_DEVICE_ATTR(test_enable, S_IRUGO | S_IWUSR,
>>>> +    hi8435_test_enable_show, hi8435_test_enable_store, 0);
>>>> +
>>>> +static struct attribute *hi8435_attributes[] = {
>>>> +    &iio_dev_attr_test_enable.dev_attr.attr,
>>>> +    NULL,
>>>> +};
>>>> +
>>>> +static struct attribute_group hi8435_attribute_group = {
>>>> +    .attrs = hi8435_attributes,
>>>> +};
>>>> +
>>>> +static int hi8435_read_raw(struct iio_dev *idev,
>>>> +               const struct iio_chan_spec *chan,
>>>> +               int *val, int *val2, long mask)
>>>> +{
>>>> +    struct hi8435_priv *priv = iio_priv(idev);
>>>> +    int ret;
>>>> +
>>>> +    switch (mask) {
>>>> +    case IIO_CHAN_INFO_RAW:
>>>> +        ret = hi8435_readl(priv, HI8435_SO31_0_REG, val);
>>>> +        if (ret < 0)
>>>> +            return ret;
>>>> +        *val = !!(*val & BIT(chan->channel));
>>>> +        return IIO_VAL_INT;
> This is where we end up in an odd possition having elected to report these
> as 'events'.
> We are reporting a 'last event state' via a read raw rather than as part
> of the events infrastructure. I wonder if instead we want to allow the
> events stuff to give us a 'am I true now signal'.
> Would be easy enough to add and would give a clear interface.
I agree.

I was thinking to use it only for getting initial states.
But it is ok if we assume that default state is all 0 and then
make UI changes in accordance to coming events.

Also It might be useful for UI debounce for clocking at low frequency when
debounce interval is much smaller then clocking/polling period.

>
> IIO_EV_INFO_CURRENT_STATE and attibute naming something like
> in_voltageY_thresh_rising_state
> which reads either 1 or 0?
Got it.
I will remove *_raw in favour of new event interface entry.
It should be read-only (read_event_value), right?

May I use the pair callback write_event_value() for my test_mode for
setting each sense output manually?
Or should I put all test mode related stuff into debugfs?

>
> For other devices we might need to take into account the
> we are not currently measuring but that can be done by an error
> return.
>
>>>> +    case IIO_CHAN_INFO_DEBOUNCE_TIME:
>>>> +        *val = priv->debounce_interval;
>>>> +        return IIO_VAL_INT;
>>>> +    default:
>>>> +        return -EINVAL;
>>>> +    }
>>>> +}
>>>> +
>>>> +static int hi8435_write_raw(struct iio_dev *idev,
>>>> +                const struct iio_chan_spec *chan,
>>>> +                int val, int val2, long mask)
>>>> +{
>>>> +    struct hi8435_priv *priv = iio_priv(idev);
>>>> +
>>>> +    switch (mask) {
>>>> +    case IIO_CHAN_INFO_RAW:
>>>> +        /* program sensors outputs in test mode */
>>>> +        return hi8435_writeb(priv, HI8435_TMDATA_REG, val ? 0x1 : 0x2);
>>>> +    case IIO_CHAN_INFO_DEBOUNCE_TIME:
>>>> +        if (val < 0)
>>>> +            return -EINVAL;
>>>> +        if (val > HI8435_DEBOUNCE_DELAY_MAX)
>>>> +            val = HI8435_DEBOUNCE_DELAY_MAX;
>>>> +        priv->debounce_interval = val;
>>>> +        return 0;
>>>> +    default:
>>>> +        return -EINVAL;
>>>> +    }
>>>> +}
>>>> +
>>>> +static int hi8435_read_event_config(struct iio_dev *idev,
>>>> +                    const struct iio_chan_spec *chan,
>>>> +                    enum iio_event_type type,
>>>> +                    enum iio_event_direction dir)
>>>> +{
>>>> +    struct hi8435_priv *priv = iio_priv(idev);
>>>> +
>>>> +    return !!(priv->event_scan_mask & BIT(chan->channel));
>>>> +}
>>>> +
>>>> +static int hi8435_write_event_config(struct iio_dev *idev,
>>>> +                     const struct iio_chan_spec *chan,
>>>> +                     enum iio_event_type type,
>>>> +                     enum iio_event_direction dir, int state)
>>>> +{
>>>> +    struct hi8435_priv *priv = iio_priv(idev);
>>>> +
>>>> +    priv->event_scan_mask &= ~BIT(chan->channel);
>>>> +    if (state)
>>>> +        priv->event_scan_mask |= BIT(chan->channel);
>>>> +
>>>> +    return 0;
>>>> +}
>>>> +
>>>> +static int hi8435_read_event_value(struct iio_dev *idev,
>>>> +                   const struct iio_chan_spec *chan,
>>>> +                   enum iio_event_type type,
>>>> +                   enum iio_event_direction dir,
>>>> +                   enum iio_event_info info,
>>>> +                   int *val, int *val2)
>>>> +{
>>>> +    struct hi8435_priv *priv = iio_priv(idev);
>>>> +    int ret;
>>>> +    u8 mode, psen;
>>>> +    u16 reg;
>>>> +
>>>> +    ret = hi8435_readb(priv, HI8435_PSEN_REG, &psen);
>>>> +    if (ret < 0)
>>>> +        return ret;
>>>> +
>>>> +    /* Supply-Open or GND-Open sensing mode */
>>>> +    mode = !!(psen & BIT(chan->channel / 8));
>>>> +
>>>> +    ret = hi8435_readw(priv, mode ? HI8435_SOCENHYS_REG :
>>>> +                 HI8435_GOCENHYS_REG, &reg);
>>>> +    if (ret < 0)
>>>> +        return ret;
>>>> +
>>>> +    if (dir == IIO_EV_DIR_FALLING)
>>>> +        *val = ((reg & 0xff) - (reg >> 8)) / 2;
>>>> +
>>>> +    if (dir == IIO_EV_DIR_RISING)
>>>> +        *val = ((reg & 0xff) + (reg >> 8)) / 2;
>>>> +
>>>> +    return IIO_VAL_INT;
>>>> +}
>>>> +
>>>> +static int hi8435_write_event_value(struct iio_dev *idev,
>>>> +                    const struct iio_chan_spec *chan,
>>>> +                    enum iio_event_type type,
>>>> +                    enum iio_event_direction dir,
>>>> +                    enum iio_event_info info,
>>>> +                    int val, int val2)
>>>> +{
>>>> +    struct hi8435_priv *priv = iio_priv(idev);
>>>> +    int ret;
>>>> +    u8 mode, psen;
>>>> +    u16 reg;
>>>> +
>>>> +    ret = hi8435_readb(priv, HI8435_PSEN_REG, &psen);
>>>> +    if (ret < 0)
>>>> +        return ret;
>>>> +
>>>> +    /* Supply-Open or GND-Open sensing mode */
>>>> +    mode = !!(psen & BIT(chan->channel / 8));
>>>> +
>>>> +    ret = hi8435_readw(priv, mode ? HI8435_SOCENHYS_REG :
>>>> +                 HI8435_GOCENHYS_REG, &reg);
>>>> +    if (ret < 0)
>>>> +        return ret;
>>>> +
>>>> +    if (dir == IIO_EV_DIR_FALLING) {
>>>> +        /* falling threshold range 2..21V, hysteresis minimum 2V */
>>>> +        if (val < 2 || val > 21 || (val + 1) >= priv->threshold_hi[mode])
>>>> +            return -EINVAL;
>>>> +
>>>> +        if (val == priv->threshold_lo[mode])
>>>> +            return 0;
>>>> +
>>>> +        priv->threshold_lo[mode] = val;
>>>> +
>>>> +        /* hysteresis must not be odd */
>>>> +        if ((priv->threshold_hi[mode] - priv->threshold_lo[mode]) % 2)
>>>> +            priv->threshold_hi[mode]--;
>>>> +    }
>>>> +
>>>> +    if (dir == IIO_EV_DIR_RISING) {
>>>> +        /* rising threshold range 3..22V, hysteresis minimum 2V */
>>>> +        if (val < 3 || val > 22 || val <= (priv->threshold_lo[mode] + 1))
>>>> +            return -EINVAL;
>>>> +
>>>> +        if (val == priv->threshold_hi[mode])
>>>> +            return 0;
>>>> +
>>>> +        priv->threshold_hi[mode] = val;
>>>> +
>>>> +        /* hysteresis must not be odd */
>>>> +        if ((priv->threshold_hi[mode] - priv->threshold_lo[mode]) % 2)
>>>> +            priv->threshold_lo[mode]++;
>>>> +    }
>>>> +
>>>> +    /* program thresholds */
>>>> +    mutex_lock(&priv->lock);
>>>> +
>>>> +    ret = hi8435_readw(priv, mode ? HI8435_SOCENHYS_REG :
>>>> +                 HI8435_GOCENHYS_REG, &reg);
>>>> +    if (ret < 0) {
>>>> +        mutex_unlock(&priv->lock);
>>>> +        return ret;
>>>> +    }
>>>> +
>>>> +    /* hysteresis */
>>>> +    reg = priv->threshold_hi[mode] - priv->threshold_lo[mode];
>>>> +    reg <<= 8;
>>>> +    /* threshold center */
>>>> +    reg |= (priv->threshold_hi[mode] + priv->threshold_lo[mode]);
>>>> +
>>>> +    ret = hi8435_writew(priv, mode ? HI8435_SOCENHYS_REG :
>>>> +                  HI8435_GOCENHYS_REG, reg);
>>>> +
>>>> +    mutex_unlock(&priv->lock);
>>>> +
>>>> +    return ret;
>>>> +}
>>>> +
>>>> +static const struct iio_event_spec hi8435_events[] = {
>>>> +    {
>>>> +        .type = IIO_EV_TYPE_THRESH,
>>>> +        .dir = IIO_EV_DIR_RISING,
>>>> +        .mask_separate = BIT(IIO_EV_INFO_VALUE),
>>>> +    }, {
>>>> +        .type = IIO_EV_TYPE_THRESH,
>>>> +        .dir = IIO_EV_DIR_FALLING,
>>>> +        .mask_separate = BIT(IIO_EV_INFO_VALUE),
>>>> +    }, {
>>>> +        .type = IIO_EV_TYPE_THRESH,
>>>> +        .dir = IIO_EV_DIR_EITHER,
>>>> +        .mask_separate = BIT(IIO_EV_INFO_ENABLE),
>>>> +    },
>>>> +};
>>>> +
>>>> +static int hi8435_get_sensing_mode(struct iio_dev *idev,
>>>> +                   const struct iio_chan_spec *chan)
>>>> +{
>>>> +    struct hi8435_priv *priv = iio_priv(idev);
>>>> +    int ret;
>>>> +    u8 reg;
>>>> +
>>>> +    ret = hi8435_readb(priv, HI8435_PSEN_REG, &reg);
>>>> +    if (ret < 0)
>>>> +        return ret;
>>>> +
>>>> +    return !!(reg & BIT(chan->channel / 8));
>>>> +}
>>>> +
>>>> +static int hi8435_set_sensing_mode(struct iio_dev *idev,
>>>> +                   const struct iio_chan_spec *chan,
>>>> +                   unsigned int mode)
>>>> +{
>>>> +    struct hi8435_priv *priv = iio_priv(idev);
>>>> +    int ret;
>>>> +    u8 reg;
>>>> +
>>>> +    mutex_lock(&priv->lock);
>>>> +
>>>> +    ret = hi8435_readb(priv, HI8435_PSEN_REG, &reg);
>>>> +    if (ret < 0) {
>>>> +        mutex_unlock(&priv->lock);
>>>> +        return ret;
>>>> +    }
>>>> +
>>>> +    reg &= ~BIT(chan->channel / 8);
>>>> +    if (mode)
>>>> +        reg |= BIT(chan->channel / 8);
>>>> +
>>>> +    ret = hi8435_writeb(priv, HI8435_PSEN_REG, reg);
>>>> +
>>>> +    mutex_unlock(&priv->lock);
>>>> +
>>>> +    return ret;
>>>> +}
>>>> +
>>>> +static const char * const hi8435_sensing_modes[] = { "GND-Open",
>>>> +                             "Supply-Open" };
>>>> +
>>>> +static const struct iio_enum hi8435_sensing_mode = {
>>>> +    .items = hi8435_sensing_modes,
>>>> +    .num_items = ARRAY_SIZE(hi8435_sensing_modes),
>>>> +    .get = hi8435_get_sensing_mode,
>>>> +    .set = hi8435_set_sensing_mode,
>>>> +};
>>>> +
>>>> +static const struct iio_chan_spec_ext_info hi8435_ext_info[] = {
>>>> +    IIO_ENUM("sensing_mode", IIO_SEPARATE, &hi8435_sensing_mode),
>>>> +    {},
>>>> +};
>>>> +
>>>> +#define HI8435_VOLTAGE_CHANNEL(num)                    \
>>>> +{                                    \
>>>> +    .type = IIO_VOLTAGE,                        \
>>>> +    .indexed = 1,                            \
>>>> +    .channel = num,                            \
>>>> +    .info_mask_separate = BIT(IIO_CHAN_INFO_RAW),            \
> If we are using events, why have the raw access at all?
I will remove *_raw.

I will probably add  IIO_EV_INFO_CURRENT_STATE.

>>>> +    .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_DEBOUNCE_TIME),    \
>>>> +    .event_spec = hi8435_events,                    \
>>>> +    .num_event_specs = ARRAY_SIZE(hi8435_events),            \
>>>> +    .ext_info = hi8435_ext_info,                    \
>>>> +    .extend_name = "comparator"                    \
>>> I'm really not sure about this approach.  Can see where you are coming
>>> from. We already have an events interface supporting these things so
>>> why not use it?   I'm just doubtful it is an efficient option or a clean
>>> interface (would like more opinions on this!)
>> Sorry, I did not understand here.
>> Is it about .extend_name ?
>> I did add "comparator" extention to have attributes look like "in_voltage0_comparator_raw"
> I'd drop it, particularly if we also drop the raw access.
Now I understand. Thank you for clarification.
I will remove it.

>>>> +}
>>>> +
>>>> +static const struct iio_chan_spec hi8435_channels[] = {
>>>> +    HI8435_VOLTAGE_CHANNEL(0),
>>>> +    HI8435_VOLTAGE_CHANNEL(1),
>>>> +    HI8435_VOLTAGE_CHANNEL(2),
>>>> +    HI8435_VOLTAGE_CHANNEL(3),
>>>> +    HI8435_VOLTAGE_CHANNEL(4),
>>>> +    HI8435_VOLTAGE_CHANNEL(5),
>>>> +    HI8435_VOLTAGE_CHANNEL(6),
>>>> +    HI8435_VOLTAGE_CHANNEL(7),
>>>> +    HI8435_VOLTAGE_CHANNEL(8),
>>>> +    HI8435_VOLTAGE_CHANNEL(9),
>>>> +    HI8435_VOLTAGE_CHANNEL(10),
>>>> +    HI8435_VOLTAGE_CHANNEL(11),
>>>> +    HI8435_VOLTAGE_CHANNEL(12),
>>>> +    HI8435_VOLTAGE_CHANNEL(13),
>>>> +    HI8435_VOLTAGE_CHANNEL(14),
>>>> +    HI8435_VOLTAGE_CHANNEL(15),
>>>> +    HI8435_VOLTAGE_CHANNEL(16),
>>>> +    HI8435_VOLTAGE_CHANNEL(17),
>>>> +    HI8435_VOLTAGE_CHANNEL(18),
>>>> +    HI8435_VOLTAGE_CHANNEL(19),
>>>> +    HI8435_VOLTAGE_CHANNEL(20),
>>>> +    HI8435_VOLTAGE_CHANNEL(21),
>>>> +    HI8435_VOLTAGE_CHANNEL(22),
>>>> +    HI8435_VOLTAGE_CHANNEL(23),
>>>> +    HI8435_VOLTAGE_CHANNEL(24),
>>>> +    HI8435_VOLTAGE_CHANNEL(25),
>>>> +    HI8435_VOLTAGE_CHANNEL(26),
>>>> +    HI8435_VOLTAGE_CHANNEL(27),
>>>> +    HI8435_VOLTAGE_CHANNEL(28),
>>>> +    HI8435_VOLTAGE_CHANNEL(29),
>>>> +    HI8435_VOLTAGE_CHANNEL(30),
>>>> +    HI8435_VOLTAGE_CHANNEL(31),
>>>> +    IIO_CHAN_SOFT_TIMESTAMP(32),
>>>> +};
>>>> +
>>>> +static const struct iio_info hi8435_info = {
>>>> +    .driver_module = THIS_MODULE,
>>>> +    .attrs = &hi8435_attribute_group,
>>>> +    .read_raw = hi8435_read_raw,
>>>> +    .write_raw = hi8435_write_raw,
>>>> +    .read_event_config = &hi8435_read_event_config,
>>>> +    .write_event_config = hi8435_write_event_config,
>>>> +    .read_event_value = &hi8435_read_event_value,
>>>> +    .write_event_value = &hi8435_write_event_value,
>>>> +};
>>>> +
>>>> +static void hi8435_iio_push_event(struct iio_dev *idev, unsigned int val)
>>>> +{
>>>> +    struct hi8435_priv *priv = iio_priv(idev);
>>>> +    enum iio_event_direction dir;
>>>> +    unsigned int i;
>>>> +    unsigned int status = priv->event_prev_val ^ val;
>>>> +
>>>> +    if (!status)
>>>> +        return;
>>>> +
>>>> +    for_each_set_bit(i, &priv->event_scan_mask, 32) {
>>>> +        if (!(status & BIT(i)))
>>>> +            continue;
>>>> +
>>>> +        dir = val & BIT(i) ? IIO_EV_DIR_RISING : IIO_EV_DIR_FALLING;
>>>> +
>>>> +        iio_push_event(idev,
>>>> +                   IIO_UNMOD_EVENT_CODE(IIO_VOLTAGE, i,
>>>> +                            IIO_EV_TYPE_THRESH, dir),
>>>> +                   iio_get_time_ns());
>>>> +    }
>>>> +
>>>> +    priv->event_prev_val = val;
>>>> +}
>>>> +
>>>> +static void hi8435_debounce_work(struct work_struct *work)
>>>> +{
>>>> +    struct hi8435_priv *priv = container_of(work, struct hi8435_priv,
>>>> +                        work.work);
>>>> +    struct iio_dev *idev = spi_get_drvdata(priv->spi);
>>>> +    u32 val;
>>>> +    int ret;
>>>> +
>>>> +    ret = hi8435_readl(priv, HI8435_SO31_0_REG, &val);
>>>> +    if (ret < 0)
>>>> +        return;
>>>> +
>>>> +    if (val == priv->debounce_val)
>>>> +        hi8435_iio_push_event(idev, val);
>>>> +    else
>>>> +        dev_warn(&priv->spi->dev, "filtered by software debounce");
>>> Definitely not.  Warning every time a standard event occurs?  Not
>>> a good idea.  Use the debug stuff if you want a way of knowing this
>>> happened, then it will silent under normal use.
>> I'd say it is not standard event, but occasional/debounce event.
>> Anyway I agree with you.
>>
>>>> +}
>>>> +
>>>> +static irqreturn_t hi8435_trigger_handler(int irq, void *private)
>>>> +{
>>>> +    struct iio_poll_func *pf = private;
>>>> +    struct iio_dev *idev = pf->indio_dev;
>>>> +    struct hi8435_priv *priv = iio_priv(idev);
>>>> +    u32 val;
>>>> +    int ret;
>>>> +
>>>> +    ret = hi8435_readl(priv, HI8435_SO31_0_REG, &val);
>>>> +    if (ret < 0)
>>>> +        goto err_read;
>>>> +
>>>> +    if (priv->debounce_interval) {
>>>> +        priv->debounce_val = val;
>>>> +        schedule_delayed_work(&priv->work,
>>>> +                msecs_to_jiffies(priv->debounce_interval));
>>> This is another element that makes me doubt that the event interface
>>> is the way to do this.  I'd much rather we pushed the debouncing out
>>> to userspace where cleverer things can be done (and more adaptive things).
>>> Here to keep the event flow low you have to do it in the kernel.
>> Yes, it is helpful for reducing the data flow (buffer data or event data)
>> Many kernel drivers support s/w event debouncing, f.e. usb cable plug-in, or other connectors.
> True enough that it is common functionality.  However, we try to keep
> the IIO datahandling as minimal as possible and leave the job to userspace.
>
> We aren't likely to be dealing with huge datarates here (as we are effectively
> polling at maybe a few hundred Hz).   Also, with the hysterisis set sensibly
> seems unlikely that much bounce due to noise will ever occur.  If you get
> bounce that overcomes that then you probably have a wiring or logic problem!
Ok, I see. I will remove it.

>>>> +    } else {
>>>> +        hi8435_iio_push_event(idev, val);
>>>> +    }
>>>> +
>>>> +err_read:
>>>> +    iio_trigger_notify_done(idev->trig);
>>>> +
>>>> +    return IRQ_HANDLED;
>>>> +}
>>>> +
>>>> +static void hi8435_parse_dt(struct hi8435_priv *priv)
>>>> +{
>>>> +    struct device_node *np = priv->spi->dev.of_node;
>>>> +    int ret;
>>>> +
>>>> +    ret = of_get_named_gpio(np, "holt,reset-gpios", 0);
>>>> +    priv->reset_gpio = ret < 0 ? 0 : ret;
>>> Silly question, but can gpio0 exist on a board?  I suspect you
>>> may need an additional variable to hold that this request failed
>>> rather than using the magic value of 0.
>> I will do handle this case, thank you.
> Look at what Lars added on this question...
>>>> +
>>>> +    ret = of_property_read_u32(np, "holt,debounce-interval",
>>>> +                   &priv->debounce_interval);
>>>> +    if (ret)
>>>> +        priv->debounce_interval = 0;
>>>> +    if (priv->debounce_interval > HI8435_DEBOUNCE_DELAY_MAX)
>>>> +        priv->debounce_interval = HI8435_DEBOUNCE_DELAY_MAX;
>>>> +}
>>>> +
>>>> +static int hi8435_probe(struct spi_device *spi)
>>>> +{
>>>> +    struct iio_dev *idev;
>>>> +    struct hi8435_priv *priv;
>>>> +    int ret;
>>>> +
>>>> +    idev = devm_iio_device_alloc(&spi->dev, sizeof(*priv));
>>>> +    if (!idev)
>>>> +        return -ENOMEM;
>>>> +
>>>> +    priv = iio_priv(idev);
>>>> +    priv->spi = spi;
>>>> +
>>>> +    if (spi->dev.of_node)
>>>> +        hi8435_parse_dt(priv);
>>>> +
>>>> +    spi_set_drvdata(spi, idev);
>>>> +    mutex_init(&priv->lock);
>>>> +    INIT_DELAYED_WORK(&priv->work, hi8435_debounce_work);
>>>> +
>>>> +    idev->dev.parent    = &spi->dev;
>>>> +    idev->name        = spi_get_device_id(spi)->name;
>>>> +    idev->modes        = INDIO_DIRECT_MODE;
>>>> +    idev->info        = &hi8435_info;
>>>> +    idev->channels        = hi8435_channels;
>>>> +    idev->num_channels    = ARRAY_SIZE(hi8435_channels);
>>>> +
>>>> +    if (priv->reset_gpio) {
>>>> +        ret = devm_gpio_request(&spi->dev, priv->reset_gpio, idev->name);
>>>> +        if (!ret) {
>>>> +            /* chip hardware reset */
>>>> +            gpio_direction_output(priv->reset_gpio, 0);
>>>> +            udelay(5);
>>>> +            gpio_direction_output(priv->reset_gpio, 1);
>>>> +        }
>>>> +    } else {
>>>> +        /* chip software reset */
>>>> +        hi8435_writeb(priv, HI8435_CTRL_REG, HI8435_CTRL_SRST);
>>>> +        /* get out from reset state */
>>>> +        hi8435_writeb(priv, HI8435_CTRL_REG, 0);
>>>> +    }
>>>> +
>>>> +    /* unmask all events */
>>>> +    priv->event_scan_mask = ~(0);
>>>> +    /* initialize default thresholds */
>>>> +    priv->threshold_lo[0] = priv->threshold_lo[1] = 2;
>>>> +    priv->threshold_hi[0] = priv->threshold_hi[1] = 4;
>>> These seem very random.  Why these values or
>> The aim for thresholds initialization is that they are in undefined state after h/w reset and
>> the driver allows userspace to change thresholds (high or low) separately.
>>
>> There is a restriction in the chip - the hysteresis can not be even.
>> If the hysteresis is set to even value then it get's into lock state and not functional anymore. (I've figured this out empirically).
> *nice*
>> So we need to initialize thresholds (aka hysteresis + it's center) to some initial value to be able to change
>> high and low thresholds separately and avoid even values for hysteresis.
>>
>> I will also add these comments.
> Cool
>>>> +    hi8435_writew(priv, HI8435_GOCENHYS_REG, 0x206);
>>> What is 0x206?  Again, I guess a default. I'd like to see a comment
>>> here saying what real world value it corresponds to.
>> Yes it is default values.
>>
>> The 0x206 is a code for HI threshold voltage = 4V and low voltage threshold = 2V.
>> The register HI8435_SOCENHYS_REG describes the voltage hysteresis of the sensor and the hysteresis center.
>> The 0x206 is an encoded value for 2V-to-4V voltage threshold.
>>
>> I will add comments.
> good.
>>>> +    hi8435_writew(priv, HI8435_SOCENHYS_REG, 0x206);
>>>> +
>>> I am as yet unconvinced that using triggers to poll events is a terribly
>>> good idea.  Feels rather like we are doing this due to a deficiency in
>>> the buffer interface than because sending events makes sense here.
>> Actually the first version of this driver was against the buffer interface.
>> I did try to use your tip and switched to event interface :)
> oops.
>> Do you think I should switch to buffer interface back?
> Nope. As discussed above, right now this approach is winning (just ;)
>>>> +    ret = iio_triggered_event_setup(idev, NULL, hi8435_trigger_handler);
>>>> +    if (ret)
>>>> +        return ret;
>>>> +
>>>> +    ret = iio_device_register(idev);
>>>> +    if (ret < 0) {
>>>> +        dev_err(&spi->dev, "unable to register device\n");
>>>> +        goto unregister_triggered_event;
>>>> +    }
>>>> +
>>>> +    return 0;
>>>> +
>>>> +unregister_triggered_event:
>>>> +    iio_triggered_event_cleanup(idev);
>>>> +    return ret;
>>>> +}
>>>> +
>>>> +static int hi8435_remove(struct spi_device *spi)
>>>> +{
>>>> +    struct iio_dev *idev = spi_get_drvdata(spi);
>>>> +    struct hi8435_priv *priv = iio_priv(idev);
>>>> +
>>>> +    cancel_delayed_work_sync(&priv->work);
>>>> +    iio_device_unregister(idev);
>>>> +    iio_triggered_event_cleanup(idev);
>>>> +
>>>> +    return 0;
>>>> +}
>>>> +
>>>> +static const struct of_device_id hi8435_dt_ids[] = {
>>>> +    { .compatible = "holt,hi8435" },
>>>> +    {},
>>>> +};
>>>> +MODULE_DEVICE_TABLE(of, hi8435_dt_ids);
>>>> +
>>>> +static const struct spi_device_id hi8435_id[] = {
>>>> +    { "hi8435", 0},
>>>> +    { }
>>>> +};
>>>> +MODULE_DEVICE_TABLE(spi, hi8435_id);
>>>> +
>>>> +static struct spi_driver hi8435_driver = {
>>>> +    .driver    = {
>>>> +        .name        = DRV_NAME,
>>>> +        .of_match_table    = of_match_ptr(hi8435_dt_ids),
>>>> +    },
>>>> +    .probe        = hi8435_probe,
>>>> +    .remove        = hi8435_remove,
>>>> +    .id_table    = hi8435_id,
>>>> +};
>>>> +module_spi_driver(hi8435_driver);
>>>> +
>>>> +MODULE_LICENSE("GPL");
>>>> +MODULE_AUTHOR("Vladimir Barinov");
>>>> +MODULE_DESCRIPTION("HI-8435 threshold detector");
>>>>
>> -- 
>> To unsubscribe from this list: send the line "unsubscribe linux-iio" in
>> the body of a message to majordomo@vger.kernel.org
>> More majordomo info at  http://vger.kernel.org/majordomo-info.html


^ permalink raw reply	[flat|nested] 40+ messages in thread

* Re: [PATCH v3 1/7] iio: adc: hi8435: Holt HI-8435 threshold detector
@ 2015-08-16 18:54           ` Vladimir Barinov
  0 siblings, 0 replies; 40+ messages in thread
From: Vladimir Barinov @ 2015-08-16 18:54 UTC (permalink / raw)
  To: Jonathan Cameron
  Cc: Hartmut Knaack, Lars-Peter Clausen, Peter Meerwald, Rob Herring,
	Pawel Moll, Mark Rutland, Ian Campbell, Kumar Gala,
	linux-kernel-u79uwXL29TY76Z2rM5mHXA,
	linux-iio-u79uwXL29TY76Z2rM5mHXA,
	devicetree-u79uwXL29TY76Z2rM5mHXA,
	cory.tusar-J6Z/VSE8EyIAspv4Qr0y0gC/G2K4zDHf

Hi Jonathan,

On 16.08.2015 12:00, Jonathan Cameron wrote:
> On 11/08/15 15:37, Vladimir Barinov wrote:
>> Hi Jonathan,
>>
>> Thank you for the review.
> Sorry I was being so indecisive.  Always take a while when something
> 'new' turns up to gather opinions and select the best option.  You
> are just the unlucky guy who happened to hit the question first!
>
> Anyhow, I'm basically happy (now) with the events approach though
> I would suggest some modifications to how exactly it is done
> (see inline).
Nice to hear!
I've got some light in the tunnel :)

Thank you for dealing with this stuff.
Please find one minor questions below.

Regards,
Vladimir

>
> Jonathan
>> On 08.08.2015 20:56, Jonathan Cameron wrote:
>>> On 29/07/15 13:56, Vladimir Barinov wrote:
>>>> Add Holt threshold detector driver for HI-8435 chip
>>>>
>>>> Signed-off-by: Vladimir Barinov <vladimir.barinov-M4DtvfQ/ZS1MRgGoP+s0PdBPR1lH4CV8@public.gmane.org>
>>> Because it usually reads better that way, I review from the bottom.
>>> Might make more sense if you read the comments that way around ;)
>> I did try to match this way.
>> At some places the comments below the code, but in other places the comments are above the code :)
> Yeah, I've never been consistent ;)
>>> I'm going to guess that the typical usecase for this device is in realtime
>>> systems where polling gives a nice predictable timing (hence the lack of any
>>> interrupt support).  These typically operate as 'scanning' devices
>>> (e.g. you update all state at approximately the same time, by reading
>>> one input after another in some predefined order).
>> Probably you are right about 8435 h/w design or there were other
>> reason why this functionality was dropped in comparison to 8429
>> chip. The Holt chip hi-8429 has both hardware interrupt and hardware
>> debounce enable lines, but it has less amount of sensors (just 8
>> outputs).
>>> Does the use of events actually map well to this use case?  You are taking
>>> something that (other than the results being a bit different) is basically
>>> a digital I/O interface and mapping it (sort of) to an interrupt chip
>>> when it isn't nothing of the sort.
>> a) Using hardware point of view.
>>
>> I was thinking that it matches the events a little bit more then
>> buffer, because the chip senses for threshold changes (passing
>> threshold high or threshold low margins) and then notifies the upper
>> layer. The bad point here is that the chip does not have interrupt
>> line for sensor state change (i.e. threshold passing).
>>
>> If it would have the interrupt line for sensor state changes then the
>> event interface map this case best way.
> Agreed.  I'm coming round to the argument Lars made that we should end
> up with the interface being the same whether or not there is an interrupt
> there.  Actually I suspect in the long run we will end up with both
> a buffered interface and an event interface as they simply have different
> usecases for the same hardware (which is why we are having these lengthy
> discussions in the first place!)
>
>> b) Using s/w point of view
>>
>> I answer relying on current iio stack implementation. I do understand
>> that the 8435 chip is not the common/usual iio client like
>> adc/dac/light/others, so I'm trying to match the existent iio stack.
> Yes.  It's certainly throwing up lots of questions!
>>  From s/w point of view it does not matter much difference between
>> event or buffer interface for this chip because in provides 1-bit
>> change and does not have it's own interrupt source line.
> Yes, just requires the userspace side to track the state if it wants
> to know.  Actually the one nasty is initialization. When the events
> are first enabled I guess we fire them all once (if any limits are currently
> breached).  That way the state can always be known.
>> The event interface has generic interface to fill in falling/rising
>> thresholds but buffer interface does not.
> We can always add more ABI.
>
>> The buffer interface has already has the trigger poll func support
>> but event interface does not. So I've tried to implement the
>> triggered event in iio stack. BTW: Doesn't the triggered event
>> support match the usecase with iio_interrupt_trigger? Or it does not
>> make sense to have triggered events (even with irqtrig) at all?
> Initially I was unsure about this, but Lars and you have convinced me.
> I'm happy having that in the subsystem.  Can also be used for devices
> which have an interrupt but which didn't get wired for some reason rather
> than having them have some internal poll loop (as we have done up to now).
>
>   
>>> I'd like more opinions on this, but my gut reaction is that we would
>>> be better making the normal buffered infrastructure handle this data
>>> properly rather than pushing it through the events intrastructure.
>>>
>>> Your solution is neat and tidy in someways but feels like it is mashing
>>> data into a particular format.
>> Probably that's all because the chip lacks smth from buffer interface
>> approach (the data should diferent the 1-bit) and smth from event
>> interface approach (lack of h/w interrupt line)
> Agreed.
>>> To add to the complexity we could have debounce logic.  If we mapped to a
>>> fill the buffer with a scan mode, how would we handle this?
>>> My guess is that it would simply delay transistions.  Perhaps we even
>>> support reading both the value right now and the debounced value if that
>>> is possible?
>> I did handle it in the driver the same way in both cases (buffer and
>> event interfaces): event/buffer trigger issues interrupt and then the
>> driver checks value right now and after delay (debounce_interval).
> I am yet to be convinced we want software debounce in the kernel.  This
> is something that the input subsystem has always had.  Partly the aim of
> IIO was to provide a slim system where all this stuff was moved to userspace.
Ok, I will remove the s/w debounce in the driver for now.

>
> Ultimately the ideal option to my mind is to finally write an inkernel
> interface for the events data flow (as we have for buffered data) then
> we can have a generic attachable debounce unit independent of any particular
> driver.  Hohum.  Now all I need is someone to write it or a few weeks off
> work ;)
This is a nice design :)
But much more hard then implement this in the driver.

>> If we do not plan to put debounced values to buffer then we may cache
>> during debouncing procedure and then return via
>> IIO_CHAN_INFO_DEBOUNCE_VALUE (or other info name). Probably even with
>> timestamp.
>>> However, here the debounce is all software.  To my mind, move this into
>>> userspace entirely. We aren't dealing with big data here so it's hardly
>>> going to be particularly challenging.
>> I will drop it on your demand in favour to encourage chipmakers to provide h/w debounce,
>> but this was much easy/efficient to have it in kernel space, since
>> during run generic_buffer sample (or other) we got the new data and then we have to start timer, then timer expires
>> and we read raw values from sysfs manually to compare the one that came from buffer and one after debounce
>> delay.
> If you are clocking evenly and we were using buffering you'd simply do it as readback in time
> rather than the other way around - thus no timer at all.  Just each time check the last N
> samples to see if they are all high or all low or similar.
This is right for high frequency clocking.
If we are clocking evently at low frequency (~1Hz) and the recommended 
debounce interval is 10-100ms then
we should use timer for issuing extra clock inside 10-100ms for 
check/validate the event.
>
>>> So my gut feeling is definitely we need to make the buffer infrastructure
>>> handle 1 bit values (in groups) then push this data out that way.
>> I will wait for your final decision.
> Lets proceed with the event version.  If someone has a strong usecase
> for adding buffering as well, there is nothing stopping the driver doing both
> that I can see (once we have the core infrastructure in place).
Ok, thx.

>>> Still I would like other opinions on this!
>>>
>>> Jonathan
>>>
>>>> ---
>>>> Changes in version 2:
>>>> - Added file sysfs-bus-iio-adc-hi8435
>>>> - Changed naming from "discrete ADC" to "threshold detector"
>>>> - Replaced swab16p/swab32p with be16_to_cpup/be32_to_cpup
>>>> - Made *_show and *_store functions static
>>>> - moved out from iio buffers to iio events
>>>> - removed hi8436/hi8437 chips from the driver
>>>> - moved from debounce_soft_delay/enable to debounce_interval via
>>>>     IIO_CHAN_INFO_DEBOUNCE_TIME
>>>> - added name extention "comparator"
>>>> - moved threshold/hysteresis setup via generic iio event sysfs
>>>> - added software mask/unmask channel events
>>>> - added programming sensor outputs while in test mode via
>>>>     IIO_CHAN_INFO_RAW
>>>> - added channels .ext_info for programming sensing mode
>>>> Changes in version 3:
>>>> - fixed typos
>>>> - moved <linux/interrupt.h> to match alphabetic order
>>>> - fixed missed */event/* prefix in the ABI description
>>>> - added missed colon in sysfs-bus-iio-adc-hi8435 file after "What"
>>>> - moved in_voltageY_comparator_thresh_either_en and debounce_time
>>>>     to sysfs-bus-iio file
>>>> - added error checking for hi8435_write[b|w]
>>>>
>>>>    Documentation/ABI/testing/sysfs-bus-iio            |   1 +
>>>>    Documentation/ABI/testing/sysfs-bus-iio-adc-hi8435 |  61 ++
>>>>    drivers/iio/adc/Kconfig                            |  11 +
>>>>    drivers/iio/adc/Makefile                           |   1 +
>>>>    drivers/iio/adc/hi8435.c                           | 659 +++++++++++++++++++++
>>>>    5 files changed, 742 insertions(+)
>>>>    create mode 100644 Documentation/ABI/testing/sysfs-bus-iio-adc-hi8435
>>>>    create mode 100644 drivers/iio/adc/hi8435.c
>>>>
>>>> diff --git a/Documentation/ABI/testing/sysfs-bus-iio b/Documentation/ABI/testing/sysfs-bus-iio
>>>> index 70c9b1a..09eac44 100644
>>>> --- a/Documentation/ABI/testing/sysfs-bus-iio
>>>> +++ b/Documentation/ABI/testing/sysfs-bus-iio
>>>> @@ -572,6 +572,7 @@ What:        /sys/.../iio:deviceX/events/in_rot_from_north_magnetic_tilt_comp_thresh_r
>>>>    What:        /sys/.../iio:deviceX/events/in_rot_from_north_magnetic_tilt_comp_thresh_falling_en
>>>>    What:        /sys/.../iio:deviceX/events/in_rot_from_north_true_tilt_comp_thresh_rising_en
>>>>    What:        /sys/.../iio:deviceX/events/in_rot_from_north_true_tilt_comp_thresh_falling_en
>>>> +What:        /sys/.../iio:deviceX/events/in_voltageY_comparator_thresh_either_en
>>>>    What:        /sys/.../iio:deviceX/events/in_voltageY_supply_thresh_rising_en
>>>>    What:        /sys/.../iio:deviceX/events/in_voltageY_supply_thresh_falling_en
>>>>    What:        /sys/.../iio:deviceX/events/in_voltageY_thresh_rising_en
>>>> diff --git a/Documentation/ABI/testing/sysfs-bus-iio-adc-hi8435 b/Documentation/ABI/testing/sysfs-bus-iio-adc-hi8435
>>>> new file mode 100644
>>>> index 0000000..c59d81d
>>>> --- /dev/null
>>>> +++ b/Documentation/ABI/testing/sysfs-bus-iio-adc-hi8435
>>>> @@ -0,0 +1,61 @@
>>>> +What:        /sys/bus/iio/devices/iio:deviceX/in_voltageY_comparator_raw
> We really try to use the extended names infrequently and normally only
> for things like 'this voltage channel is internally connected to the power
> supply rail'.  Don't think we need it here.   As suggested below, if we
> are going events then we don't want to expose a _raw reading except via
> in the 'events' directory where it would be a 'current event status'.
> Anyhow, I talk about that futher down.
I will remove *_raw.

>>>> +Date:        July 2015
>>>> +KernelVersion:    4.2.0
>>>> +Contact:    source-M4DtvfQ/ZS1MRgGoP+s0PdBPR1lH4CV8@public.gmane.org
>>>> +Description:
>>>> +        Read value is a voltage threshold measurement from channel Y.
>>>> +        Could be either 0 if sensor voltage is lower then low voltage
>>>> +        threshold or 1 if sensor voltage is higher then high voltage
>>>> +        threshold.
>>>> +        Write value is a programmed sensor output while in self test
>>>> +        mode. Could be either 0 or 1. The programmed value will be read
>>>> +        back if /sys/bus/iio/devices/iio:deviceX/test_enable is set to 1.
>>>> +
>>>> +What:        /sys/bus/iio/devices/iio:deviceX/test_enable
>>>> +Date:        July 2015
>>>> +KernelVersion:    4.2.0
>>>> +Contact:    source-M4DtvfQ/ZS1MRgGoP+s0PdBPR1lH4CV8@public.gmane.org
>>>> +Description:
>>>> +        Enable/disable the HI-8435 self test mode.
>>>> +        If enabled the in_voltageY_comparator_raw should be read back
>>>> +        accordingly to written value to in_voltageY_comparator_raw.
> Hmm. Normally we just do self tests at startup.  So set some patterns, read them
> and then verify they are fine.  Drops the need for a userspace interface
> that can get a little confusing (writing to what is normally a read
> only attribute).  Would this work here?
>
> Alternatively, just add a debugfs interface to allow this to be set rather than
> exposing it via sysfs all the time?
I will add debugfs or drop it (since I did use write_raw() for setting 
sensor output in test mode)

>
>>>> +
>>>> +What:        /sys/bus/iio/devices/iio:deviceX/in_voltageY_comparator_sensing_mode
>>>> +Date:        July 2015
>>>> +KernelVersion:    4.2.0
>>>> +Contact:    source-M4DtvfQ/ZS1MRgGoP+s0PdBPR1lH4CV8@public.gmane.org
>>>> +Description:
>>>> +        Program sensor type for threshold detector inputs.
>>>> +        Could be either "GND-Open" or "Supply-Open" mode. Y is a
>>>> +        threshold detector input channel. Channels 0..7, 8..15, 16..23
>>>> +        and 24..31 has common sensor types.
>>>> +
>>>> +What:        /sys/bus/iio/devices/iio:deviceX/events/in_voltageY_comparator_thresh_falling_value
> This is where the comparator extended name looks a bit silly as we are
> clearly comparing it anyway due to the thresh_falling part ;)
I will remove the extended name.

>>>> +Date:        July 2015
>>>> +KernelVersion:    4.2.0
>>>> +Contact:    source-M4DtvfQ/ZS1MRgGoP+s0PdBPR1lH4CV8@public.gmane.org
>>>> +Description:
>>>> +        Channel Y low voltage threshold. If sensor input voltage goes lower then
>>>> +        this value then the threshold falling event is pushed.
>>>> +        Depending on in_voltageY_comparator_sensing_mode the low voltage threshold
>>>> +        is separately set for "GND-Open" and "Supply-Open" modes.
>>>> +        Channels 0..31 have common low threshold values, but could have different
>>>> +        sensing_modes.
>>>> +        The low voltage threshold range is between 2..21V.
>>>> +        Hysteresis between low and high thresholds can not be lower then 2 and
>>>> +        can not be odd.
>>>> +
>>>> +What:        /sys/bus/iio/devices/iio:deviceX/events/in_voltageY_comparator_thresh_rising_value
>>>> +Date:        July 2015
>>>> +KernelVersion:    4.2.0
>>>> +Contact:    source-M4DtvfQ/ZS1MRgGoP+s0PdBPR1lH4CV8@public.gmane.org
>>>> +Description:
>>>> +        Channel Y high voltage threshold. If sensor input voltage goes higher then
>>>> +        this value then the threshold rising event is pushed.
>>>> +        Depending on in_voltageY_comparator_sensing_mode the high voltage threshold
>>>> +        is separately set for "GND-Open" and "Supply-Open" modes.
>>>> +        Channels 0..31 have common high threshold values, but could have different
>>>> +        sensing_modes.
>>>> +        The high voltage threshold range is between 3..22V.
>>>> +        Hysteresis between low and high thresholds can not be lower then 2 and
>>>> +        can not be odd.
>>>> diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig
>>>> index eb0cd89..553c91e 100644
>>>> --- a/drivers/iio/adc/Kconfig
>>>> +++ b/drivers/iio/adc/Kconfig
>>>> @@ -170,6 +170,17 @@ config EXYNOS_ADC
>>>>          of SoCs for drivers such as the touchscreen and hwmon to use to share
>>>>          this resource.
>>>>    +config HI8435
>>>> +    tristate "Holt Integrated Circuits HI-8435 threshold detector"
>>>> +    select IIO_TRIGGERED_EVENT
>>>> +    depends on SPI
>>>> +    help
>>>> +      If you say yes here you get support for Holt Integrated Circuits
>>>> +      HI-8435 chip.
>>>> +
>>>> +      This driver can also be built as a module. If so, the module will be
>>>> +      called hi8435.
>>>> +
>>>>    config LP8788_ADC
>>>>        tristate "LP8788 ADC driver"
>>>>        depends on MFD_LP8788
>>>> diff --git a/drivers/iio/adc/Makefile b/drivers/iio/adc/Makefile
>>>> index a096210..00f367aa 100644
>>>> --- a/drivers/iio/adc/Makefile
>>>> +++ b/drivers/iio/adc/Makefile
>>>> @@ -19,6 +19,7 @@ obj-$(CONFIG_BERLIN2_ADC) += berlin2-adc.o
>>>>    obj-$(CONFIG_DA9150_GPADC) += da9150-gpadc.o
>>>>    obj-$(CONFIG_CC10001_ADC) += cc10001_adc.o
>>>>    obj-$(CONFIG_EXYNOS_ADC) += exynos_adc.o
>>>> +obj-$(CONFIG_HI8435) += hi8435.o
>>>>    obj-$(CONFIG_LP8788_ADC) += lp8788_adc.o
>>>>    obj-$(CONFIG_MAX1027) += max1027.o
>>>>    obj-$(CONFIG_MAX1363) += max1363.o
>>>> diff --git a/drivers/iio/adc/hi8435.c b/drivers/iio/adc/hi8435.c
>>>> new file mode 100644
>>>> index 0000000..f3dab5a
>>>> --- /dev/null
>>>> +++ b/drivers/iio/adc/hi8435.c
>>>> @@ -0,0 +1,659 @@
>>>> +/*
>>>> + * Holt Integrated Circuits HI-8435 threshold detector driver
>>>> + *
>>>> + * Copyright (C) 2015 Zodiac Inflight Innovations
>>>> + * Copyright (C) 2015 Cogent Embedded, Inc.
>>>> + *
>>>> + * 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.
>>>> + */
>>>> +
>>>> +#include <linux/delay.h>
>>>> +#include <linux/iio/events.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/triggered_event.h>
>>>> +#include <linux/interrupt.h>
>>>> +#include <linux/module.h>
>>>> +#include <linux/of.h>
>>>> +#include <linux/of_device.h>
>>>> +#include <linux/of_gpio.h>
>>>> +#include <linux/spi/spi.h>
>>>> +#include <linux/workqueue.h>
>>>> +
>>>> +#define DRV_NAME "hi8435"
>>>> +
>>>> +/* Register offsets for HI-8435 */
>>>> +#define HI8435_CTRL_REG            0x02
>>>> +#define HI8435_PSEN_REG            0x04
>>>> +#define HI8435_TMDATA_REG        0x1E
>>>> +#define HI8435_GOCENHYS_REG        0x3A
>>>> +#define HI8435_SOCENHYS_REG        0x3C
>>> Hmm. These aren't in the docs that I can immediately acquire...
>>> Any chance of some more meaningful naming?
>> I did follow the register naming in the chip datasheet:
>> http://www.holtic.com/products/3081-hi-8435.aspx
>>
>> The SO7_0 means "Sensor status bank 0 register: SO<7:0>".
>> Does HI8435_STATUS_BANK0_REG work?
> Ah, I missed the meaning entirely by not noticing they were 8 bits
> each ;)  This current naming is fine.
>> Or should I just add comments instead of changing definition?
>>>> +#define HI8435_SO7_0_REG        0x10
>>>> +#define HI8435_SO15_8_REG        0x12
>>>    +#define HI8435_SO23_16_REG        0x14
>>>> +#define HI8435_SO31_24_REG        0x16
>>>> +#define HI8435_SO31_0_REG        0x78
>>>> +
>>>> +#define HI8435_WRITE_OPCODE        0x00
>>>> +#define HI8435_READ_OPCODE        0x80
>>>> +
>>>> +/* CTRL register bits */
>>>> +#define HI8435_CTRL_TEST        0x01
>>>> +#define HI8435_CTRL_SRST        0x02
>>>> +
>>>> +#define HI8435_DEBOUNCE_DELAY_MAX    1000    /* msec */
>>>> +#define HI8435_DEBOUNCE_DELAY_DEF    100    /* msec */
>>>> +
>>>> +struct hi8435_priv {
>>>> +    struct spi_device *spi;
>>>> +    struct mutex lock;
>>>> +    struct delayed_work work;
>>>> +
>>>> +    int reset_gpio;
>>>> +    int debounce_interval; /* msec */
>>>> +    u32 debounce_val; /* prev value to compare during software debounce */
>>>> +
>>>> +    unsigned long event_scan_mask; /* soft mask/unmask channels events */
>>>> +    unsigned int event_prev_val;
>>>> +
>>>> +    unsigned threshold_lo[2]; /* GND-Open and Supply-Open thresholds */
>>>> +    unsigned threshold_hi[2]; /* GND-Open and Supply-Open threshold */
>>>> +    u8 reg_buffer[4] ____cacheline_aligned;
>>>> +};
>>>> +
>>>> +static int hi8435_readb(struct hi8435_priv *priv, u8 reg, u8 *val)
>>>> +{
>>>> +    reg |= HI8435_READ_OPCODE;
>>>> +    return spi_write_then_read(priv->spi, &reg, 1, val, 1);
>>>> +}
>>>> +
>>>> +static int hi8435_readw(struct hi8435_priv *priv, u8 reg, u16 *val)
>>>> +{
>>>> +    int ret;
>>>> +
>>>> +    reg |= HI8435_READ_OPCODE;
>>>> +    ret = spi_write_then_read(priv->spi, &reg, 1, val, 2);
>>>> +    *val = be16_to_cpup(val);
>>>> +
>>>> +    return ret;
>>>> +}
>>>> +
>>>> +static int hi8435_readl(struct hi8435_priv *priv, u8 reg, u32 *val)
>>>> +{
>>>> +    int ret;
>>>> +
>>>> +    reg |= HI8435_READ_OPCODE;
>>>> +    ret = spi_write_then_read(priv->spi, &reg, 1, val, 4);
>>>> +    *val = be32_to_cpup(val);
>>>> +
>>>> +    return ret;
>>>> +}
>>>> +
>>>> +static int hi8435_writeb(struct hi8435_priv *priv, u8 reg, u8 val)
>>>> +{
>>>> +    priv->reg_buffer[0] = reg | HI8435_WRITE_OPCODE;
>>>> +    priv->reg_buffer[1] = val;
>>>> +
>>>> +    return spi_write(priv->spi, priv->reg_buffer, 2);
>>>> +}
>>>> +
>>>> +static int hi8435_writew(struct hi8435_priv *priv, u8 reg, u16 val)
>>>> +{
>>>> +    priv->reg_buffer[0] = reg | HI8435_WRITE_OPCODE;
>>>> +    priv->reg_buffer[1] = (val >> 8) & 0xff;
>>>> +    priv->reg_buffer[2] = val & 0xff;
>>>> +
>>>> +    return spi_write(priv->spi, priv->reg_buffer, 3);
>>>> +}
>>>> +
>>>> +static ssize_t hi8435_test_enable_show(struct device *dev,
>>>> +                       struct device_attribute *attr, char *buf)
>>>> +{
>>>> +    struct hi8435_priv *priv = iio_priv(dev_to_iio_dev(dev));
>>>> +    int ret;
>>>> +    u8 reg;
>>>> +
>>>> +    ret = hi8435_readb(priv, HI8435_CTRL_REG, &reg);
>>>> +    if (ret < 0)
>>>> +        return ret;
>>>> +
>>>> +    return sprintf(buf, "%d\n", reg & HI8435_CTRL_TEST);
>>>> +}
>>>> +
>>>> +static ssize_t hi8435_test_enable_store(struct device *dev,
>>>> +                    struct device_attribute *attr,
>>>> +                    const char *buf, size_t len)
>>>> +{
>>>> +    struct hi8435_priv *priv = iio_priv(dev_to_iio_dev(dev));
>>>> +    unsigned int val;
>>>> +    int ret;
>>>> +
>>>> +    ret = kstrtouint(buf, 10, &val);
>>>> +    if (ret)
>>>> +        return ret;
>>>> +
>>>> +    ret = hi8435_writeb(priv, HI8435_CTRL_REG, val ? HI8435_CTRL_TEST : 0);
>>>> +    if (ret < 0)
>>>> +        return ret;
>>>> +
>>>> +    return len;
>>>> +}
>>>> +
>>>> +static IIO_DEVICE_ATTR(test_enable, S_IRUGO | S_IWUSR,
>>>> +    hi8435_test_enable_show, hi8435_test_enable_store, 0);
>>>> +
>>>> +static struct attribute *hi8435_attributes[] = {
>>>> +    &iio_dev_attr_test_enable.dev_attr.attr,
>>>> +    NULL,
>>>> +};
>>>> +
>>>> +static struct attribute_group hi8435_attribute_group = {
>>>> +    .attrs = hi8435_attributes,
>>>> +};
>>>> +
>>>> +static int hi8435_read_raw(struct iio_dev *idev,
>>>> +               const struct iio_chan_spec *chan,
>>>> +               int *val, int *val2, long mask)
>>>> +{
>>>> +    struct hi8435_priv *priv = iio_priv(idev);
>>>> +    int ret;
>>>> +
>>>> +    switch (mask) {
>>>> +    case IIO_CHAN_INFO_RAW:
>>>> +        ret = hi8435_readl(priv, HI8435_SO31_0_REG, val);
>>>> +        if (ret < 0)
>>>> +            return ret;
>>>> +        *val = !!(*val & BIT(chan->channel));
>>>> +        return IIO_VAL_INT;
> This is where we end up in an odd possition having elected to report these
> as 'events'.
> We are reporting a 'last event state' via a read raw rather than as part
> of the events infrastructure. I wonder if instead we want to allow the
> events stuff to give us a 'am I true now signal'.
> Would be easy enough to add and would give a clear interface.
I agree.

I was thinking to use it only for getting initial states.
But it is ok if we assume that default state is all 0 and then
make UI changes in accordance to coming events.

Also It might be useful for UI debounce for clocking at low frequency when
debounce interval is much smaller then clocking/polling period.

>
> IIO_EV_INFO_CURRENT_STATE and attibute naming something like
> in_voltageY_thresh_rising_state
> which reads either 1 or 0?
Got it.
I will remove *_raw in favour of new event interface entry.
It should be read-only (read_event_value), right?

May I use the pair callback write_event_value() for my test_mode for
setting each sense output manually?
Or should I put all test mode related stuff into debugfs?

>
> For other devices we might need to take into account the
> we are not currently measuring but that can be done by an error
> return.
>
>>>> +    case IIO_CHAN_INFO_DEBOUNCE_TIME:
>>>> +        *val = priv->debounce_interval;
>>>> +        return IIO_VAL_INT;
>>>> +    default:
>>>> +        return -EINVAL;
>>>> +    }
>>>> +}
>>>> +
>>>> +static int hi8435_write_raw(struct iio_dev *idev,
>>>> +                const struct iio_chan_spec *chan,
>>>> +                int val, int val2, long mask)
>>>> +{
>>>> +    struct hi8435_priv *priv = iio_priv(idev);
>>>> +
>>>> +    switch (mask) {
>>>> +    case IIO_CHAN_INFO_RAW:
>>>> +        /* program sensors outputs in test mode */
>>>> +        return hi8435_writeb(priv, HI8435_TMDATA_REG, val ? 0x1 : 0x2);
>>>> +    case IIO_CHAN_INFO_DEBOUNCE_TIME:
>>>> +        if (val < 0)
>>>> +            return -EINVAL;
>>>> +        if (val > HI8435_DEBOUNCE_DELAY_MAX)
>>>> +            val = HI8435_DEBOUNCE_DELAY_MAX;
>>>> +        priv->debounce_interval = val;
>>>> +        return 0;
>>>> +    default:
>>>> +        return -EINVAL;
>>>> +    }
>>>> +}
>>>> +
>>>> +static int hi8435_read_event_config(struct iio_dev *idev,
>>>> +                    const struct iio_chan_spec *chan,
>>>> +                    enum iio_event_type type,
>>>> +                    enum iio_event_direction dir)
>>>> +{
>>>> +    struct hi8435_priv *priv = iio_priv(idev);
>>>> +
>>>> +    return !!(priv->event_scan_mask & BIT(chan->channel));
>>>> +}
>>>> +
>>>> +static int hi8435_write_event_config(struct iio_dev *idev,
>>>> +                     const struct iio_chan_spec *chan,
>>>> +                     enum iio_event_type type,
>>>> +                     enum iio_event_direction dir, int state)
>>>> +{
>>>> +    struct hi8435_priv *priv = iio_priv(idev);
>>>> +
>>>> +    priv->event_scan_mask &= ~BIT(chan->channel);
>>>> +    if (state)
>>>> +        priv->event_scan_mask |= BIT(chan->channel);
>>>> +
>>>> +    return 0;
>>>> +}
>>>> +
>>>> +static int hi8435_read_event_value(struct iio_dev *idev,
>>>> +                   const struct iio_chan_spec *chan,
>>>> +                   enum iio_event_type type,
>>>> +                   enum iio_event_direction dir,
>>>> +                   enum iio_event_info info,
>>>> +                   int *val, int *val2)
>>>> +{
>>>> +    struct hi8435_priv *priv = iio_priv(idev);
>>>> +    int ret;
>>>> +    u8 mode, psen;
>>>> +    u16 reg;
>>>> +
>>>> +    ret = hi8435_readb(priv, HI8435_PSEN_REG, &psen);
>>>> +    if (ret < 0)
>>>> +        return ret;
>>>> +
>>>> +    /* Supply-Open or GND-Open sensing mode */
>>>> +    mode = !!(psen & BIT(chan->channel / 8));
>>>> +
>>>> +    ret = hi8435_readw(priv, mode ? HI8435_SOCENHYS_REG :
>>>> +                 HI8435_GOCENHYS_REG, &reg);
>>>> +    if (ret < 0)
>>>> +        return ret;
>>>> +
>>>> +    if (dir == IIO_EV_DIR_FALLING)
>>>> +        *val = ((reg & 0xff) - (reg >> 8)) / 2;
>>>> +
>>>> +    if (dir == IIO_EV_DIR_RISING)
>>>> +        *val = ((reg & 0xff) + (reg >> 8)) / 2;
>>>> +
>>>> +    return IIO_VAL_INT;
>>>> +}
>>>> +
>>>> +static int hi8435_write_event_value(struct iio_dev *idev,
>>>> +                    const struct iio_chan_spec *chan,
>>>> +                    enum iio_event_type type,
>>>> +                    enum iio_event_direction dir,
>>>> +                    enum iio_event_info info,
>>>> +                    int val, int val2)
>>>> +{
>>>> +    struct hi8435_priv *priv = iio_priv(idev);
>>>> +    int ret;
>>>> +    u8 mode, psen;
>>>> +    u16 reg;
>>>> +
>>>> +    ret = hi8435_readb(priv, HI8435_PSEN_REG, &psen);
>>>> +    if (ret < 0)
>>>> +        return ret;
>>>> +
>>>> +    /* Supply-Open or GND-Open sensing mode */
>>>> +    mode = !!(psen & BIT(chan->channel / 8));
>>>> +
>>>> +    ret = hi8435_readw(priv, mode ? HI8435_SOCENHYS_REG :
>>>> +                 HI8435_GOCENHYS_REG, &reg);
>>>> +    if (ret < 0)
>>>> +        return ret;
>>>> +
>>>> +    if (dir == IIO_EV_DIR_FALLING) {
>>>> +        /* falling threshold range 2..21V, hysteresis minimum 2V */
>>>> +        if (val < 2 || val > 21 || (val + 1) >= priv->threshold_hi[mode])
>>>> +            return -EINVAL;
>>>> +
>>>> +        if (val == priv->threshold_lo[mode])
>>>> +            return 0;
>>>> +
>>>> +        priv->threshold_lo[mode] = val;
>>>> +
>>>> +        /* hysteresis must not be odd */
>>>> +        if ((priv->threshold_hi[mode] - priv->threshold_lo[mode]) % 2)
>>>> +            priv->threshold_hi[mode]--;
>>>> +    }
>>>> +
>>>> +    if (dir == IIO_EV_DIR_RISING) {
>>>> +        /* rising threshold range 3..22V, hysteresis minimum 2V */
>>>> +        if (val < 3 || val > 22 || val <= (priv->threshold_lo[mode] + 1))
>>>> +            return -EINVAL;
>>>> +
>>>> +        if (val == priv->threshold_hi[mode])
>>>> +            return 0;
>>>> +
>>>> +        priv->threshold_hi[mode] = val;
>>>> +
>>>> +        /* hysteresis must not be odd */
>>>> +        if ((priv->threshold_hi[mode] - priv->threshold_lo[mode]) % 2)
>>>> +            priv->threshold_lo[mode]++;
>>>> +    }
>>>> +
>>>> +    /* program thresholds */
>>>> +    mutex_lock(&priv->lock);
>>>> +
>>>> +    ret = hi8435_readw(priv, mode ? HI8435_SOCENHYS_REG :
>>>> +                 HI8435_GOCENHYS_REG, &reg);
>>>> +    if (ret < 0) {
>>>> +        mutex_unlock(&priv->lock);
>>>> +        return ret;
>>>> +    }
>>>> +
>>>> +    /* hysteresis */
>>>> +    reg = priv->threshold_hi[mode] - priv->threshold_lo[mode];
>>>> +    reg <<= 8;
>>>> +    /* threshold center */
>>>> +    reg |= (priv->threshold_hi[mode] + priv->threshold_lo[mode]);
>>>> +
>>>> +    ret = hi8435_writew(priv, mode ? HI8435_SOCENHYS_REG :
>>>> +                  HI8435_GOCENHYS_REG, reg);
>>>> +
>>>> +    mutex_unlock(&priv->lock);
>>>> +
>>>> +    return ret;
>>>> +}
>>>> +
>>>> +static const struct iio_event_spec hi8435_events[] = {
>>>> +    {
>>>> +        .type = IIO_EV_TYPE_THRESH,
>>>> +        .dir = IIO_EV_DIR_RISING,
>>>> +        .mask_separate = BIT(IIO_EV_INFO_VALUE),
>>>> +    }, {
>>>> +        .type = IIO_EV_TYPE_THRESH,
>>>> +        .dir = IIO_EV_DIR_FALLING,
>>>> +        .mask_separate = BIT(IIO_EV_INFO_VALUE),
>>>> +    }, {
>>>> +        .type = IIO_EV_TYPE_THRESH,
>>>> +        .dir = IIO_EV_DIR_EITHER,
>>>> +        .mask_separate = BIT(IIO_EV_INFO_ENABLE),
>>>> +    },
>>>> +};
>>>> +
>>>> +static int hi8435_get_sensing_mode(struct iio_dev *idev,
>>>> +                   const struct iio_chan_spec *chan)
>>>> +{
>>>> +    struct hi8435_priv *priv = iio_priv(idev);
>>>> +    int ret;
>>>> +    u8 reg;
>>>> +
>>>> +    ret = hi8435_readb(priv, HI8435_PSEN_REG, &reg);
>>>> +    if (ret < 0)
>>>> +        return ret;
>>>> +
>>>> +    return !!(reg & BIT(chan->channel / 8));
>>>> +}
>>>> +
>>>> +static int hi8435_set_sensing_mode(struct iio_dev *idev,
>>>> +                   const struct iio_chan_spec *chan,
>>>> +                   unsigned int mode)
>>>> +{
>>>> +    struct hi8435_priv *priv = iio_priv(idev);
>>>> +    int ret;
>>>> +    u8 reg;
>>>> +
>>>> +    mutex_lock(&priv->lock);
>>>> +
>>>> +    ret = hi8435_readb(priv, HI8435_PSEN_REG, &reg);
>>>> +    if (ret < 0) {
>>>> +        mutex_unlock(&priv->lock);
>>>> +        return ret;
>>>> +    }
>>>> +
>>>> +    reg &= ~BIT(chan->channel / 8);
>>>> +    if (mode)
>>>> +        reg |= BIT(chan->channel / 8);
>>>> +
>>>> +    ret = hi8435_writeb(priv, HI8435_PSEN_REG, reg);
>>>> +
>>>> +    mutex_unlock(&priv->lock);
>>>> +
>>>> +    return ret;
>>>> +}
>>>> +
>>>> +static const char * const hi8435_sensing_modes[] = { "GND-Open",
>>>> +                             "Supply-Open" };
>>>> +
>>>> +static const struct iio_enum hi8435_sensing_mode = {
>>>> +    .items = hi8435_sensing_modes,
>>>> +    .num_items = ARRAY_SIZE(hi8435_sensing_modes),
>>>> +    .get = hi8435_get_sensing_mode,
>>>> +    .set = hi8435_set_sensing_mode,
>>>> +};
>>>> +
>>>> +static const struct iio_chan_spec_ext_info hi8435_ext_info[] = {
>>>> +    IIO_ENUM("sensing_mode", IIO_SEPARATE, &hi8435_sensing_mode),
>>>> +    {},
>>>> +};
>>>> +
>>>> +#define HI8435_VOLTAGE_CHANNEL(num)                    \
>>>> +{                                    \
>>>> +    .type = IIO_VOLTAGE,                        \
>>>> +    .indexed = 1,                            \
>>>> +    .channel = num,                            \
>>>> +    .info_mask_separate = BIT(IIO_CHAN_INFO_RAW),            \
> If we are using events, why have the raw access at all?
I will remove *_raw.

I will probably add  IIO_EV_INFO_CURRENT_STATE.

>>>> +    .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_DEBOUNCE_TIME),    \
>>>> +    .event_spec = hi8435_events,                    \
>>>> +    .num_event_specs = ARRAY_SIZE(hi8435_events),            \
>>>> +    .ext_info = hi8435_ext_info,                    \
>>>> +    .extend_name = "comparator"                    \
>>> I'm really not sure about this approach.  Can see where you are coming
>>> from. We already have an events interface supporting these things so
>>> why not use it?   I'm just doubtful it is an efficient option or a clean
>>> interface (would like more opinions on this!)
>> Sorry, I did not understand here.
>> Is it about .extend_name ?
>> I did add "comparator" extention to have attributes look like "in_voltage0_comparator_raw"
> I'd drop it, particularly if we also drop the raw access.
Now I understand. Thank you for clarification.
I will remove it.

>>>> +}
>>>> +
>>>> +static const struct iio_chan_spec hi8435_channels[] = {
>>>> +    HI8435_VOLTAGE_CHANNEL(0),
>>>> +    HI8435_VOLTAGE_CHANNEL(1),
>>>> +    HI8435_VOLTAGE_CHANNEL(2),
>>>> +    HI8435_VOLTAGE_CHANNEL(3),
>>>> +    HI8435_VOLTAGE_CHANNEL(4),
>>>> +    HI8435_VOLTAGE_CHANNEL(5),
>>>> +    HI8435_VOLTAGE_CHANNEL(6),
>>>> +    HI8435_VOLTAGE_CHANNEL(7),
>>>> +    HI8435_VOLTAGE_CHANNEL(8),
>>>> +    HI8435_VOLTAGE_CHANNEL(9),
>>>> +    HI8435_VOLTAGE_CHANNEL(10),
>>>> +    HI8435_VOLTAGE_CHANNEL(11),
>>>> +    HI8435_VOLTAGE_CHANNEL(12),
>>>> +    HI8435_VOLTAGE_CHANNEL(13),
>>>> +    HI8435_VOLTAGE_CHANNEL(14),
>>>> +    HI8435_VOLTAGE_CHANNEL(15),
>>>> +    HI8435_VOLTAGE_CHANNEL(16),
>>>> +    HI8435_VOLTAGE_CHANNEL(17),
>>>> +    HI8435_VOLTAGE_CHANNEL(18),
>>>> +    HI8435_VOLTAGE_CHANNEL(19),
>>>> +    HI8435_VOLTAGE_CHANNEL(20),
>>>> +    HI8435_VOLTAGE_CHANNEL(21),
>>>> +    HI8435_VOLTAGE_CHANNEL(22),
>>>> +    HI8435_VOLTAGE_CHANNEL(23),
>>>> +    HI8435_VOLTAGE_CHANNEL(24),
>>>> +    HI8435_VOLTAGE_CHANNEL(25),
>>>> +    HI8435_VOLTAGE_CHANNEL(26),
>>>> +    HI8435_VOLTAGE_CHANNEL(27),
>>>> +    HI8435_VOLTAGE_CHANNEL(28),
>>>> +    HI8435_VOLTAGE_CHANNEL(29),
>>>> +    HI8435_VOLTAGE_CHANNEL(30),
>>>> +    HI8435_VOLTAGE_CHANNEL(31),
>>>> +    IIO_CHAN_SOFT_TIMESTAMP(32),
>>>> +};
>>>> +
>>>> +static const struct iio_info hi8435_info = {
>>>> +    .driver_module = THIS_MODULE,
>>>> +    .attrs = &hi8435_attribute_group,
>>>> +    .read_raw = hi8435_read_raw,
>>>> +    .write_raw = hi8435_write_raw,
>>>> +    .read_event_config = &hi8435_read_event_config,
>>>> +    .write_event_config = hi8435_write_event_config,
>>>> +    .read_event_value = &hi8435_read_event_value,
>>>> +    .write_event_value = &hi8435_write_event_value,
>>>> +};
>>>> +
>>>> +static void hi8435_iio_push_event(struct iio_dev *idev, unsigned int val)
>>>> +{
>>>> +    struct hi8435_priv *priv = iio_priv(idev);
>>>> +    enum iio_event_direction dir;
>>>> +    unsigned int i;
>>>> +    unsigned int status = priv->event_prev_val ^ val;
>>>> +
>>>> +    if (!status)
>>>> +        return;
>>>> +
>>>> +    for_each_set_bit(i, &priv->event_scan_mask, 32) {
>>>> +        if (!(status & BIT(i)))
>>>> +            continue;
>>>> +
>>>> +        dir = val & BIT(i) ? IIO_EV_DIR_RISING : IIO_EV_DIR_FALLING;
>>>> +
>>>> +        iio_push_event(idev,
>>>> +                   IIO_UNMOD_EVENT_CODE(IIO_VOLTAGE, i,
>>>> +                            IIO_EV_TYPE_THRESH, dir),
>>>> +                   iio_get_time_ns());
>>>> +    }
>>>> +
>>>> +    priv->event_prev_val = val;
>>>> +}
>>>> +
>>>> +static void hi8435_debounce_work(struct work_struct *work)
>>>> +{
>>>> +    struct hi8435_priv *priv = container_of(work, struct hi8435_priv,
>>>> +                        work.work);
>>>> +    struct iio_dev *idev = spi_get_drvdata(priv->spi);
>>>> +    u32 val;
>>>> +    int ret;
>>>> +
>>>> +    ret = hi8435_readl(priv, HI8435_SO31_0_REG, &val);
>>>> +    if (ret < 0)
>>>> +        return;
>>>> +
>>>> +    if (val == priv->debounce_val)
>>>> +        hi8435_iio_push_event(idev, val);
>>>> +    else
>>>> +        dev_warn(&priv->spi->dev, "filtered by software debounce");
>>> Definitely not.  Warning every time a standard event occurs?  Not
>>> a good idea.  Use the debug stuff if you want a way of knowing this
>>> happened, then it will silent under normal use.
>> I'd say it is not standard event, but occasional/debounce event.
>> Anyway I agree with you.
>>
>>>> +}
>>>> +
>>>> +static irqreturn_t hi8435_trigger_handler(int irq, void *private)
>>>> +{
>>>> +    struct iio_poll_func *pf = private;
>>>> +    struct iio_dev *idev = pf->indio_dev;
>>>> +    struct hi8435_priv *priv = iio_priv(idev);
>>>> +    u32 val;
>>>> +    int ret;
>>>> +
>>>> +    ret = hi8435_readl(priv, HI8435_SO31_0_REG, &val);
>>>> +    if (ret < 0)
>>>> +        goto err_read;
>>>> +
>>>> +    if (priv->debounce_interval) {
>>>> +        priv->debounce_val = val;
>>>> +        schedule_delayed_work(&priv->work,
>>>> +                msecs_to_jiffies(priv->debounce_interval));
>>> This is another element that makes me doubt that the event interface
>>> is the way to do this.  I'd much rather we pushed the debouncing out
>>> to userspace where cleverer things can be done (and more adaptive things).
>>> Here to keep the event flow low you have to do it in the kernel.
>> Yes, it is helpful for reducing the data flow (buffer data or event data)
>> Many kernel drivers support s/w event debouncing, f.e. usb cable plug-in, or other connectors.
> True enough that it is common functionality.  However, we try to keep
> the IIO datahandling as minimal as possible and leave the job to userspace.
>
> We aren't likely to be dealing with huge datarates here (as we are effectively
> polling at maybe a few hundred Hz).   Also, with the hysterisis set sensibly
> seems unlikely that much bounce due to noise will ever occur.  If you get
> bounce that overcomes that then you probably have a wiring or logic problem!
Ok, I see. I will remove it.

>>>> +    } else {
>>>> +        hi8435_iio_push_event(idev, val);
>>>> +    }
>>>> +
>>>> +err_read:
>>>> +    iio_trigger_notify_done(idev->trig);
>>>> +
>>>> +    return IRQ_HANDLED;
>>>> +}
>>>> +
>>>> +static void hi8435_parse_dt(struct hi8435_priv *priv)
>>>> +{
>>>> +    struct device_node *np = priv->spi->dev.of_node;
>>>> +    int ret;
>>>> +
>>>> +    ret = of_get_named_gpio(np, "holt,reset-gpios", 0);
>>>> +    priv->reset_gpio = ret < 0 ? 0 : ret;
>>> Silly question, but can gpio0 exist on a board?  I suspect you
>>> may need an additional variable to hold that this request failed
>>> rather than using the magic value of 0.
>> I will do handle this case, thank you.
> Look at what Lars added on this question...
>>>> +
>>>> +    ret = of_property_read_u32(np, "holt,debounce-interval",
>>>> +                   &priv->debounce_interval);
>>>> +    if (ret)
>>>> +        priv->debounce_interval = 0;
>>>> +    if (priv->debounce_interval > HI8435_DEBOUNCE_DELAY_MAX)
>>>> +        priv->debounce_interval = HI8435_DEBOUNCE_DELAY_MAX;
>>>> +}
>>>> +
>>>> +static int hi8435_probe(struct spi_device *spi)
>>>> +{
>>>> +    struct iio_dev *idev;
>>>> +    struct hi8435_priv *priv;
>>>> +    int ret;
>>>> +
>>>> +    idev = devm_iio_device_alloc(&spi->dev, sizeof(*priv));
>>>> +    if (!idev)
>>>> +        return -ENOMEM;
>>>> +
>>>> +    priv = iio_priv(idev);
>>>> +    priv->spi = spi;
>>>> +
>>>> +    if (spi->dev.of_node)
>>>> +        hi8435_parse_dt(priv);
>>>> +
>>>> +    spi_set_drvdata(spi, idev);
>>>> +    mutex_init(&priv->lock);
>>>> +    INIT_DELAYED_WORK(&priv->work, hi8435_debounce_work);
>>>> +
>>>> +    idev->dev.parent    = &spi->dev;
>>>> +    idev->name        = spi_get_device_id(spi)->name;
>>>> +    idev->modes        = INDIO_DIRECT_MODE;
>>>> +    idev->info        = &hi8435_info;
>>>> +    idev->channels        = hi8435_channels;
>>>> +    idev->num_channels    = ARRAY_SIZE(hi8435_channels);
>>>> +
>>>> +    if (priv->reset_gpio) {
>>>> +        ret = devm_gpio_request(&spi->dev, priv->reset_gpio, idev->name);
>>>> +        if (!ret) {
>>>> +            /* chip hardware reset */
>>>> +            gpio_direction_output(priv->reset_gpio, 0);
>>>> +            udelay(5);
>>>> +            gpio_direction_output(priv->reset_gpio, 1);
>>>> +        }
>>>> +    } else {
>>>> +        /* chip software reset */
>>>> +        hi8435_writeb(priv, HI8435_CTRL_REG, HI8435_CTRL_SRST);
>>>> +        /* get out from reset state */
>>>> +        hi8435_writeb(priv, HI8435_CTRL_REG, 0);
>>>> +    }
>>>> +
>>>> +    /* unmask all events */
>>>> +    priv->event_scan_mask = ~(0);
>>>> +    /* initialize default thresholds */
>>>> +    priv->threshold_lo[0] = priv->threshold_lo[1] = 2;
>>>> +    priv->threshold_hi[0] = priv->threshold_hi[1] = 4;
>>> These seem very random.  Why these values or
>> The aim for thresholds initialization is that they are in undefined state after h/w reset and
>> the driver allows userspace to change thresholds (high or low) separately.
>>
>> There is a restriction in the chip - the hysteresis can not be even.
>> If the hysteresis is set to even value then it get's into lock state and not functional anymore. (I've figured this out empirically).
> *nice*
>> So we need to initialize thresholds (aka hysteresis + it's center) to some initial value to be able to change
>> high and low thresholds separately and avoid even values for hysteresis.
>>
>> I will also add these comments.
> Cool
>>>> +    hi8435_writew(priv, HI8435_GOCENHYS_REG, 0x206);
>>> What is 0x206?  Again, I guess a default. I'd like to see a comment
>>> here saying what real world value it corresponds to.
>> Yes it is default values.
>>
>> The 0x206 is a code for HI threshold voltage = 4V and low voltage threshold = 2V.
>> The register HI8435_SOCENHYS_REG describes the voltage hysteresis of the sensor and the hysteresis center.
>> The 0x206 is an encoded value for 2V-to-4V voltage threshold.
>>
>> I will add comments.
> good.
>>>> +    hi8435_writew(priv, HI8435_SOCENHYS_REG, 0x206);
>>>> +
>>> I am as yet unconvinced that using triggers to poll events is a terribly
>>> good idea.  Feels rather like we are doing this due to a deficiency in
>>> the buffer interface than because sending events makes sense here.
>> Actually the first version of this driver was against the buffer interface.
>> I did try to use your tip and switched to event interface :)
> oops.
>> Do you think I should switch to buffer interface back?
> Nope. As discussed above, right now this approach is winning (just ;)
>>>> +    ret = iio_triggered_event_setup(idev, NULL, hi8435_trigger_handler);
>>>> +    if (ret)
>>>> +        return ret;
>>>> +
>>>> +    ret = iio_device_register(idev);
>>>> +    if (ret < 0) {
>>>> +        dev_err(&spi->dev, "unable to register device\n");
>>>> +        goto unregister_triggered_event;
>>>> +    }
>>>> +
>>>> +    return 0;
>>>> +
>>>> +unregister_triggered_event:
>>>> +    iio_triggered_event_cleanup(idev);
>>>> +    return ret;
>>>> +}
>>>> +
>>>> +static int hi8435_remove(struct spi_device *spi)
>>>> +{
>>>> +    struct iio_dev *idev = spi_get_drvdata(spi);
>>>> +    struct hi8435_priv *priv = iio_priv(idev);
>>>> +
>>>> +    cancel_delayed_work_sync(&priv->work);
>>>> +    iio_device_unregister(idev);
>>>> +    iio_triggered_event_cleanup(idev);
>>>> +
>>>> +    return 0;
>>>> +}
>>>> +
>>>> +static const struct of_device_id hi8435_dt_ids[] = {
>>>> +    { .compatible = "holt,hi8435" },
>>>> +    {},
>>>> +};
>>>> +MODULE_DEVICE_TABLE(of, hi8435_dt_ids);
>>>> +
>>>> +static const struct spi_device_id hi8435_id[] = {
>>>> +    { "hi8435", 0},
>>>> +    { }
>>>> +};
>>>> +MODULE_DEVICE_TABLE(spi, hi8435_id);
>>>> +
>>>> +static struct spi_driver hi8435_driver = {
>>>> +    .driver    = {
>>>> +        .name        = DRV_NAME,
>>>> +        .of_match_table    = of_match_ptr(hi8435_dt_ids),
>>>> +    },
>>>> +    .probe        = hi8435_probe,
>>>> +    .remove        = hi8435_remove,
>>>> +    .id_table    = hi8435_id,
>>>> +};
>>>> +module_spi_driver(hi8435_driver);
>>>> +
>>>> +MODULE_LICENSE("GPL");
>>>> +MODULE_AUTHOR("Vladimir Barinov");
>>>> +MODULE_DESCRIPTION("HI-8435 threshold detector");
>>>>
>> -- 
>> To unsubscribe from this list: send the line "unsubscribe linux-iio" in
>> the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
>> More majordomo info at  http://vger.kernel.org/majordomo-info.html

--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply	[flat|nested] 40+ messages in thread

* Re: [PATCH v3 5/7] iio: Support triggered events
@ 2015-08-16 20:15       ` Vladimir Barinov
  0 siblings, 0 replies; 40+ messages in thread
From: Vladimir Barinov @ 2015-08-16 20:15 UTC (permalink / raw)
  To: Jonathan Cameron
  Cc: Hartmut Knaack, Lars-Peter Clausen, Peter Meerwald, Rob Herring,
	Pawel Moll, Mark Rutland, Ian Campbell, Kumar Gala, linux-kernel,
	linux-iio, devicetree, cory.tusar

Hi Jonathan,

Thank you for review.
Find some comments below.

Regards,
Vladimir

On 16.08.2015 12:20, Jonathan Cameron wrote:
> On 29/07/15 13:57, Vladimir Barinov wrote:
>> Support triggered events.
>>
>> This is useful for chips that don't have their own interrupt sources.
>> It allows to use generic/standalone iio triggers for those drivers.
> As we have concluded this is a good thing to have, I've take a closer
> look at it.
>
> I think we need to slightly separate the buffer and event uses of
> triggers as it's entirely plausible both might be enabled at the same
> time.  Don't think this should be hard however.
>
> I can concieve of weird restrictions from certain hardware (particularly
> where hardware buffers are concerned) in which events are not monitored
> when we are reading through the buffer but we can tackle that when we
> actually need to.
>
> Anyhow, few bits inline.  Thanks for your persistence with this!
>
> Jonathan
>
>> Signed-off-by: Vladimir Barinov <vladimir.barinov@cogentembedded.com>
>> ---
>> Changes in version 2:
>> - initially added
>> Changes in version 3:
>> - fixed grammar in patch description
>> - changed license header to match "GNU Public License v2 or later"
>>
>>   drivers/iio/Kconfig                        |  6 +++
>>   drivers/iio/Makefile                       |  1 +
>>   drivers/iio/industrialio-core.c            |  4 +-
>>   drivers/iio/industrialio-trigger.c         | 12 +++++-
>>   drivers/iio/industrialio-triggered-event.c | 68 ++++++++++++++++++++++++++++++
>>   include/linux/iio/iio.h                    |  1 +
>>   include/linux/iio/triggered_event.h        | 11 +++++
>>   7 files changed, 99 insertions(+), 4 deletions(-)
>>   create mode 100644 drivers/iio/industrialio-triggered-event.c
>>   create mode 100644 include/linux/iio/triggered_event.h
>>
>> diff --git a/drivers/iio/Kconfig b/drivers/iio/Kconfig
>> index 4011eff..8fcc92f 100644
>> --- a/drivers/iio/Kconfig
>> +++ b/drivers/iio/Kconfig
>> @@ -58,6 +58,12 @@ config IIO_CONSUMERS_PER_TRIGGER
>>   	This value controls the maximum number of consumers that a
>>   	given trigger may handle. Default is 2.
>>   
>> +config IIO_TRIGGERED_EVENT
>> +	tristate
>> +	select IIO_TRIGGER
>> +	help
>> +	  Provides helper functions for setting up triggered events.
>> +
>>   source "drivers/iio/accel/Kconfig"
>>   source "drivers/iio/adc/Kconfig"
>>   source "drivers/iio/amplifiers/Kconfig"
>> diff --git a/drivers/iio/Makefile b/drivers/iio/Makefile
>> index 698afc2..40dc13e 100644
>> --- a/drivers/iio/Makefile
>> +++ b/drivers/iio/Makefile
>> @@ -9,6 +9,7 @@ industrialio-$(CONFIG_IIO_TRIGGER) += industrialio-trigger.o
>>   industrialio-$(CONFIG_IIO_BUFFER_CB) += buffer_cb.o
>>   
>>   obj-$(CONFIG_IIO_TRIGGERED_BUFFER) += industrialio-triggered-buffer.o
>> +obj-$(CONFIG_IIO_TRIGGERED_EVENT) += industrialio-triggered-event.o
>>   obj-$(CONFIG_IIO_KFIFO_BUF) += kfifo_buf.o
>>   
>>   obj-y += accel/
>> diff --git a/drivers/iio/industrialio-core.c b/drivers/iio/industrialio-core.c
>> index 3524b0d..54d71ea 100644
>> --- a/drivers/iio/industrialio-core.c
>> +++ b/drivers/iio/industrialio-core.c
>> @@ -948,7 +948,7 @@ static void iio_device_unregister_sysfs(struct iio_dev *indio_dev)
>>   static void iio_dev_release(struct device *device)
>>   {
>>   	struct iio_dev *indio_dev = dev_to_iio_dev(device);
>> -	if (indio_dev->modes & INDIO_BUFFER_TRIGGERED)
>> +	if (indio_dev->modes & (INDIO_BUFFER_TRIGGERED | INDIO_EVENT_TRIGGERED))
>>   		iio_device_unregister_trigger_consumer(indio_dev);
>>   	iio_device_unregister_eventset(indio_dev);
>>   	iio_device_unregister_sysfs(indio_dev);
>> @@ -1218,7 +1218,7 @@ int iio_device_register(struct iio_dev *indio_dev)
>>   			"Failed to register event set\n");
>>   		goto error_free_sysfs;
>>   	}
>> -	if (indio_dev->modes & INDIO_BUFFER_TRIGGERED)
>> +	if (indio_dev->modes & (INDIO_BUFFER_TRIGGERED | INDIO_EVENT_TRIGGERED))
>>   		iio_device_register_trigger_consumer(indio_dev);
>>   
>>   	if ((indio_dev->modes & INDIO_ALL_BUFFER_MODES) &&
>> diff --git a/drivers/iio/industrialio-trigger.c b/drivers/iio/industrialio-trigger.c
>> index d31098e..72b63e7 100644
>> --- a/drivers/iio/industrialio-trigger.c
>> +++ b/drivers/iio/industrialio-trigger.c
>> @@ -345,10 +345,18 @@ static ssize_t iio_trigger_write_current(struct device *dev,
>>   
>>   	indio_dev->trig = trig;
>>   
>> -	if (oldtrig)
>> +	if (oldtrig) {
>> +		if (indio_dev->currentmode == INDIO_EVENT_TRIGGERED)
>> +			iio_trigger_detach_poll_func(oldtrig,
>> +						     indio_dev->pollfunc);
>>   		iio_trigger_put(oldtrig);
>> -	if (indio_dev->trig)
>> +	}
>> +	if (indio_dev->trig) {
>>   		iio_trigger_get(indio_dev->trig);
>> +		if (indio_dev->currentmode == INDIO_EVENT_TRIGGERED)
>> +			iio_trigger_attach_poll_func(indio_dev->trig,
>> +						     indio_dev->pollfunc);
>> +	}
>>   
>>   	return len;
>>   }
>> diff --git a/drivers/iio/industrialio-triggered-event.c b/drivers/iio/industrialio-triggered-event.c
>> new file mode 100644
>> index 0000000..1e5ad33
>> --- /dev/null
>> +++ b/drivers/iio/industrialio-triggered-event.c
>> @@ -0,0 +1,68 @@
>> + /*
>> + * Copyright (C) 2015 Cogent Embedded, Inc.
>> + *
>> + * 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.
>> + */
>> +
>> +#include <linux/kernel.h>
>> +#include <linux/export.h>
>> +#include <linux/module.h>
>> +#include <linux/iio/iio.h>
>> +#include <linux/iio/triggered_event.h>
>> +#include <linux/iio/trigger_consumer.h>
>> +
>> +/**
>> + * iio_triggered_event_setup() - Setup pollfunc for triggered event
>> + * @indio_dev:		IIO device structure
>> + * @pollfunc_bh:	Function which will be used as pollfunc bottom half
>> + * @pollfunc_th:	Function which will be used as pollfunc top half
>> + *
>> + * This function combines some common tasks which will normally be performed
>> + * when setting up a triggered event. It will allocate the pollfunc and
>> + * set mode to use it for triggered event.
>> + *
>> + * Before calling this function the indio_dev structure should already be
>> + * completely initialized, but not yet registered. In practice this means that
>> + * this function should be called right before iio_device_register().
>> + *
>> + * To free the resources allocated by this function call
>> + * iio_triggered_event_cleanup().
>> + */
>> +int iio_triggered_event_setup(struct iio_dev *indio_dev,
>> +	irqreturn_t (*pollfunc_bh)(int irq, void *p),
>> +	irqreturn_t (*pollfunc_th)(int irq, void *p))
>> +{
>> +	indio_dev->pollfunc = iio_alloc_pollfunc(pollfunc_bh,
>> +						 pollfunc_th,
>> +						 IRQF_ONESHOT,
>> +						 indio_dev,
>> +						 "%s_consumer%d",
>> +						 indio_dev->name,
>> +						 indio_dev->id);
>> +	if (indio_dev->pollfunc == NULL)
>> +		return -ENOMEM;
>> +
>> +	/* Flag that pollfunc is used for triggered event */
>> +	indio_dev->modes |= INDIO_EVENT_TRIGGERED;
>> +	indio_dev->currentmode = INDIO_EVENT_TRIGGERED;
> Can conceive of a corner case here.  What if both buffer
> and event are being triggered?  No real problem with
> running them off the same trigger, but right now enabling
> one will take out the other.
To avoid conflict it should be separate pollfunc
(indio_dev->pollfunc_event = ...) since we do runtime pollfunc
attach/detach for both buffer and event interfaces.

>
> Might also want to not enable triggered event mode
> until we enable some events?
I think it will need to change most drivers that use event interface since
events are enabled inside them via write_event_config() callback or
events may have already been enabled during driver probe.

>
> Perhaps we need separate indio_dev->event_mode and
event_modes ?
This probably not necessary since iio stack checks for flags set in
indio_dev->modes value.
> indio_dev->current_event_mode fields to make the two
> configurable independently?
I think it works, but with separate indio_dev->pollfunc_event.

Should it be inidio_dev->current_event_mode or 
inidio_dev->currentmode_event for
consistency?

I will verify your idea with using one trigger for both event and buffer 
interface on hi8435 driver
by adding buffer interface that I will try internally (will not send).

>
> I get the feeling this particular feature is going to evolve
> somewhat as we get a feel for how it is being used.
>
>> +
>> +	return 0;
>> +}
>> +EXPORT_SYMBOL(iio_triggered_event_setup);
>> +
>> +/**
>> + * iio_triggered_event_cleanup() - Free resources allocated by iio_triggered_event_setup()
>> + * @indio_dev: IIO device structure
>> + */
>> +void iio_triggered_event_cleanup(struct iio_dev *indio_dev)
>> +{
>> +	iio_dealloc_pollfunc(indio_dev->pollfunc);
>> +}
>> +EXPORT_SYMBOL(iio_triggered_event_cleanup);
>> +
>> +MODULE_AUTHOR("Vladimir Barinov");
>> +MODULE_DESCRIPTION("IIO helper functions for setting up triggered events");
>> +MODULE_LICENSE("GPL");
>> diff --git a/include/linux/iio/iio.h b/include/linux/iio/iio.h
>> index f791482..b691ee0 100644
>> --- a/include/linux/iio/iio.h
>> +++ b/include/linux/iio/iio.h
>> @@ -294,6 +294,7 @@ static inline s64 iio_get_time_ns(void)
>>   #define INDIO_BUFFER_TRIGGERED		0x02
>>   #define INDIO_BUFFER_SOFTWARE		0x04
>>   #define INDIO_BUFFER_HARDWARE		0x08
>> +#define INDIO_EVENT_TRIGGERED		0x10
>>   
>>   #define INDIO_ALL_BUFFER_MODES					\
>>   	(INDIO_BUFFER_TRIGGERED | INDIO_BUFFER_HARDWARE | INDIO_BUFFER_SOFTWARE)
>> diff --git a/include/linux/iio/triggered_event.h b/include/linux/iio/triggered_event.h
>> new file mode 100644
>> index 0000000..e9894e9
>> --- /dev/null
>> +++ b/include/linux/iio/triggered_event.h
>> @@ -0,0 +1,11 @@
>> +#ifndef _LINUX_IIO_TRIGGERED_EVENT_H_
>> +#define _LINUX_IIO_TRIGGERED_EVENT_H_
>> +
>> +#include <linux/interrupt.h>
>> +
>> +int iio_triggered_event_setup(struct iio_dev *indio_dev,
>> +	irqreturn_t (*pollfunc_bh)(int irq, void *p),
>> +	irqreturn_t (*pollfunc_th)(int irq, void *p));
> We renamed the parameters of the equivalent buffer function recently
> to avoid some confusion.  Could you follow that for this one as well.
Ok

>
>> +void iio_triggered_event_cleanup(struct iio_dev *indio_dev);
>> +
>> +#endif
>>


^ permalink raw reply	[flat|nested] 40+ messages in thread

* Re: [PATCH v3 5/7] iio: Support triggered events
@ 2015-08-16 20:15       ` Vladimir Barinov
  0 siblings, 0 replies; 40+ messages in thread
From: Vladimir Barinov @ 2015-08-16 20:15 UTC (permalink / raw)
  To: Jonathan Cameron
  Cc: Hartmut Knaack, Lars-Peter Clausen, Peter Meerwald, Rob Herring,
	Pawel Moll, Mark Rutland, Ian Campbell, Kumar Gala,
	linux-kernel-u79uwXL29TY76Z2rM5mHXA,
	linux-iio-u79uwXL29TY76Z2rM5mHXA,
	devicetree-u79uwXL29TY76Z2rM5mHXA,
	cory.tusar-J6Z/VSE8EyIAspv4Qr0y0gC/G2K4zDHf

Hi Jonathan,

Thank you for review.
Find some comments below.

Regards,
Vladimir

On 16.08.2015 12:20, Jonathan Cameron wrote:
> On 29/07/15 13:57, Vladimir Barinov wrote:
>> Support triggered events.
>>
>> This is useful for chips that don't have their own interrupt sources.
>> It allows to use generic/standalone iio triggers for those drivers.
> As we have concluded this is a good thing to have, I've take a closer
> look at it.
>
> I think we need to slightly separate the buffer and event uses of
> triggers as it's entirely plausible both might be enabled at the same
> time.  Don't think this should be hard however.
>
> I can concieve of weird restrictions from certain hardware (particularly
> where hardware buffers are concerned) in which events are not monitored
> when we are reading through the buffer but we can tackle that when we
> actually need to.
>
> Anyhow, few bits inline.  Thanks for your persistence with this!
>
> Jonathan
>
>> Signed-off-by: Vladimir Barinov <vladimir.barinov-M4DtvfQ/ZS1MRgGoP+s0PdBPR1lH4CV8@public.gmane.org>
>> ---
>> Changes in version 2:
>> - initially added
>> Changes in version 3:
>> - fixed grammar in patch description
>> - changed license header to match "GNU Public License v2 or later"
>>
>>   drivers/iio/Kconfig                        |  6 +++
>>   drivers/iio/Makefile                       |  1 +
>>   drivers/iio/industrialio-core.c            |  4 +-
>>   drivers/iio/industrialio-trigger.c         | 12 +++++-
>>   drivers/iio/industrialio-triggered-event.c | 68 ++++++++++++++++++++++++++++++
>>   include/linux/iio/iio.h                    |  1 +
>>   include/linux/iio/triggered_event.h        | 11 +++++
>>   7 files changed, 99 insertions(+), 4 deletions(-)
>>   create mode 100644 drivers/iio/industrialio-triggered-event.c
>>   create mode 100644 include/linux/iio/triggered_event.h
>>
>> diff --git a/drivers/iio/Kconfig b/drivers/iio/Kconfig
>> index 4011eff..8fcc92f 100644
>> --- a/drivers/iio/Kconfig
>> +++ b/drivers/iio/Kconfig
>> @@ -58,6 +58,12 @@ config IIO_CONSUMERS_PER_TRIGGER
>>   	This value controls the maximum number of consumers that a
>>   	given trigger may handle. Default is 2.
>>   
>> +config IIO_TRIGGERED_EVENT
>> +	tristate
>> +	select IIO_TRIGGER
>> +	help
>> +	  Provides helper functions for setting up triggered events.
>> +
>>   source "drivers/iio/accel/Kconfig"
>>   source "drivers/iio/adc/Kconfig"
>>   source "drivers/iio/amplifiers/Kconfig"
>> diff --git a/drivers/iio/Makefile b/drivers/iio/Makefile
>> index 698afc2..40dc13e 100644
>> --- a/drivers/iio/Makefile
>> +++ b/drivers/iio/Makefile
>> @@ -9,6 +9,7 @@ industrialio-$(CONFIG_IIO_TRIGGER) += industrialio-trigger.o
>>   industrialio-$(CONFIG_IIO_BUFFER_CB) += buffer_cb.o
>>   
>>   obj-$(CONFIG_IIO_TRIGGERED_BUFFER) += industrialio-triggered-buffer.o
>> +obj-$(CONFIG_IIO_TRIGGERED_EVENT) += industrialio-triggered-event.o
>>   obj-$(CONFIG_IIO_KFIFO_BUF) += kfifo_buf.o
>>   
>>   obj-y += accel/
>> diff --git a/drivers/iio/industrialio-core.c b/drivers/iio/industrialio-core.c
>> index 3524b0d..54d71ea 100644
>> --- a/drivers/iio/industrialio-core.c
>> +++ b/drivers/iio/industrialio-core.c
>> @@ -948,7 +948,7 @@ static void iio_device_unregister_sysfs(struct iio_dev *indio_dev)
>>   static void iio_dev_release(struct device *device)
>>   {
>>   	struct iio_dev *indio_dev = dev_to_iio_dev(device);
>> -	if (indio_dev->modes & INDIO_BUFFER_TRIGGERED)
>> +	if (indio_dev->modes & (INDIO_BUFFER_TRIGGERED | INDIO_EVENT_TRIGGERED))
>>   		iio_device_unregister_trigger_consumer(indio_dev);
>>   	iio_device_unregister_eventset(indio_dev);
>>   	iio_device_unregister_sysfs(indio_dev);
>> @@ -1218,7 +1218,7 @@ int iio_device_register(struct iio_dev *indio_dev)
>>   			"Failed to register event set\n");
>>   		goto error_free_sysfs;
>>   	}
>> -	if (indio_dev->modes & INDIO_BUFFER_TRIGGERED)
>> +	if (indio_dev->modes & (INDIO_BUFFER_TRIGGERED | INDIO_EVENT_TRIGGERED))
>>   		iio_device_register_trigger_consumer(indio_dev);
>>   
>>   	if ((indio_dev->modes & INDIO_ALL_BUFFER_MODES) &&
>> diff --git a/drivers/iio/industrialio-trigger.c b/drivers/iio/industrialio-trigger.c
>> index d31098e..72b63e7 100644
>> --- a/drivers/iio/industrialio-trigger.c
>> +++ b/drivers/iio/industrialio-trigger.c
>> @@ -345,10 +345,18 @@ static ssize_t iio_trigger_write_current(struct device *dev,
>>   
>>   	indio_dev->trig = trig;
>>   
>> -	if (oldtrig)
>> +	if (oldtrig) {
>> +		if (indio_dev->currentmode == INDIO_EVENT_TRIGGERED)
>> +			iio_trigger_detach_poll_func(oldtrig,
>> +						     indio_dev->pollfunc);
>>   		iio_trigger_put(oldtrig);
>> -	if (indio_dev->trig)
>> +	}
>> +	if (indio_dev->trig) {
>>   		iio_trigger_get(indio_dev->trig);
>> +		if (indio_dev->currentmode == INDIO_EVENT_TRIGGERED)
>> +			iio_trigger_attach_poll_func(indio_dev->trig,
>> +						     indio_dev->pollfunc);
>> +	}
>>   
>>   	return len;
>>   }
>> diff --git a/drivers/iio/industrialio-triggered-event.c b/drivers/iio/industrialio-triggered-event.c
>> new file mode 100644
>> index 0000000..1e5ad33
>> --- /dev/null
>> +++ b/drivers/iio/industrialio-triggered-event.c
>> @@ -0,0 +1,68 @@
>> + /*
>> + * Copyright (C) 2015 Cogent Embedded, Inc.
>> + *
>> + * 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.
>> + */
>> +
>> +#include <linux/kernel.h>
>> +#include <linux/export.h>
>> +#include <linux/module.h>
>> +#include <linux/iio/iio.h>
>> +#include <linux/iio/triggered_event.h>
>> +#include <linux/iio/trigger_consumer.h>
>> +
>> +/**
>> + * iio_triggered_event_setup() - Setup pollfunc for triggered event
>> + * @indio_dev:		IIO device structure
>> + * @pollfunc_bh:	Function which will be used as pollfunc bottom half
>> + * @pollfunc_th:	Function which will be used as pollfunc top half
>> + *
>> + * This function combines some common tasks which will normally be performed
>> + * when setting up a triggered event. It will allocate the pollfunc and
>> + * set mode to use it for triggered event.
>> + *
>> + * Before calling this function the indio_dev structure should already be
>> + * completely initialized, but not yet registered. In practice this means that
>> + * this function should be called right before iio_device_register().
>> + *
>> + * To free the resources allocated by this function call
>> + * iio_triggered_event_cleanup().
>> + */
>> +int iio_triggered_event_setup(struct iio_dev *indio_dev,
>> +	irqreturn_t (*pollfunc_bh)(int irq, void *p),
>> +	irqreturn_t (*pollfunc_th)(int irq, void *p))
>> +{
>> +	indio_dev->pollfunc = iio_alloc_pollfunc(pollfunc_bh,
>> +						 pollfunc_th,
>> +						 IRQF_ONESHOT,
>> +						 indio_dev,
>> +						 "%s_consumer%d",
>> +						 indio_dev->name,
>> +						 indio_dev->id);
>> +	if (indio_dev->pollfunc == NULL)
>> +		return -ENOMEM;
>> +
>> +	/* Flag that pollfunc is used for triggered event */
>> +	indio_dev->modes |= INDIO_EVENT_TRIGGERED;
>> +	indio_dev->currentmode = INDIO_EVENT_TRIGGERED;
> Can conceive of a corner case here.  What if both buffer
> and event are being triggered?  No real problem with
> running them off the same trigger, but right now enabling
> one will take out the other.
To avoid conflict it should be separate pollfunc
(indio_dev->pollfunc_event = ...) since we do runtime pollfunc
attach/detach for both buffer and event interfaces.

>
> Might also want to not enable triggered event mode
> until we enable some events?
I think it will need to change most drivers that use event interface since
events are enabled inside them via write_event_config() callback or
events may have already been enabled during driver probe.

>
> Perhaps we need separate indio_dev->event_mode and
event_modes ?
This probably not necessary since iio stack checks for flags set in
indio_dev->modes value.
> indio_dev->current_event_mode fields to make the two
> configurable independently?
I think it works, but with separate indio_dev->pollfunc_event.

Should it be inidio_dev->current_event_mode or 
inidio_dev->currentmode_event for
consistency?

I will verify your idea with using one trigger for both event and buffer 
interface on hi8435 driver
by adding buffer interface that I will try internally (will not send).

>
> I get the feeling this particular feature is going to evolve
> somewhat as we get a feel for how it is being used.
>
>> +
>> +	return 0;
>> +}
>> +EXPORT_SYMBOL(iio_triggered_event_setup);
>> +
>> +/**
>> + * iio_triggered_event_cleanup() - Free resources allocated by iio_triggered_event_setup()
>> + * @indio_dev: IIO device structure
>> + */
>> +void iio_triggered_event_cleanup(struct iio_dev *indio_dev)
>> +{
>> +	iio_dealloc_pollfunc(indio_dev->pollfunc);
>> +}
>> +EXPORT_SYMBOL(iio_triggered_event_cleanup);
>> +
>> +MODULE_AUTHOR("Vladimir Barinov");
>> +MODULE_DESCRIPTION("IIO helper functions for setting up triggered events");
>> +MODULE_LICENSE("GPL");
>> diff --git a/include/linux/iio/iio.h b/include/linux/iio/iio.h
>> index f791482..b691ee0 100644
>> --- a/include/linux/iio/iio.h
>> +++ b/include/linux/iio/iio.h
>> @@ -294,6 +294,7 @@ static inline s64 iio_get_time_ns(void)
>>   #define INDIO_BUFFER_TRIGGERED		0x02
>>   #define INDIO_BUFFER_SOFTWARE		0x04
>>   #define INDIO_BUFFER_HARDWARE		0x08
>> +#define INDIO_EVENT_TRIGGERED		0x10
>>   
>>   #define INDIO_ALL_BUFFER_MODES					\
>>   	(INDIO_BUFFER_TRIGGERED | INDIO_BUFFER_HARDWARE | INDIO_BUFFER_SOFTWARE)
>> diff --git a/include/linux/iio/triggered_event.h b/include/linux/iio/triggered_event.h
>> new file mode 100644
>> index 0000000..e9894e9
>> --- /dev/null
>> +++ b/include/linux/iio/triggered_event.h
>> @@ -0,0 +1,11 @@
>> +#ifndef _LINUX_IIO_TRIGGERED_EVENT_H_
>> +#define _LINUX_IIO_TRIGGERED_EVENT_H_
>> +
>> +#include <linux/interrupt.h>
>> +
>> +int iio_triggered_event_setup(struct iio_dev *indio_dev,
>> +	irqreturn_t (*pollfunc_bh)(int irq, void *p),
>> +	irqreturn_t (*pollfunc_th)(int irq, void *p));
> We renamed the parameters of the equivalent buffer function recently
> to avoid some confusion.  Could you follow that for this one as well.
Ok

>
>> +void iio_triggered_event_cleanup(struct iio_dev *indio_dev);
>> +
>> +#endif
>>

^ permalink raw reply	[flat|nested] 40+ messages in thread

* Re: [PATCH v3 1/7] iio: adc: hi8435: Holt HI-8435 threshold detector
@ 2015-08-22 13:48             ` Jonathan Cameron
  0 siblings, 0 replies; 40+ messages in thread
From: Jonathan Cameron @ 2015-08-22 13:48 UTC (permalink / raw)
  To: Vladimir Barinov
  Cc: Hartmut Knaack, Lars-Peter Clausen, Peter Meerwald, Rob Herring,
	Pawel Moll, Mark Rutland, Ian Campbell, Kumar Gala, linux-kernel,
	linux-iio, devicetree, cory.tusar

On 16/08/15 19:54, Vladimir Barinov wrote:
> Hi Jonathan,
> 
> On 16.08.2015 12:00, Jonathan Cameron wrote:
>> On 11/08/15 15:37, Vladimir Barinov wrote:
>>> Hi Jonathan,
>>>
>>> Thank you for the review.
>> Sorry I was being so indecisive.  Always take a while when something
>> 'new' turns up to gather opinions and select the best option.  You
>> are just the unlucky guy who happened to hit the question first!
>>
>> Anyhow, I'm basically happy (now) with the events approach though
>> I would suggest some modifications to how exactly it is done
>> (see inline).
> Nice to hear!
> I've got some light in the tunnel :)
Yeah, sorry it took so long to pin down the approach!
> 
> Thank you for dealing with this stuff.
> Please find one minor questions below.
> 
> Regards,
> Vladimir
> 
>>
>> Jonathan
>>> On 08.08.2015 20:56, Jonathan Cameron wrote:
>>>> On 29/07/15 13:56, Vladimir Barinov wrote:
>>>>> Add Holt threshold detector driver for HI-8435 chip
>>>>>
>>>>> Signed-off-by: Vladimir Barinov <vladimir.barinov@cogentembedded.com>
>>>> Because it usually reads better that way, I review from the bottom.
>>>> Might make more sense if you read the comments that way around ;)
>>> I did try to match this way.
>>> At some places the comments below the code, but in other places the comments are above the code :)
>> Yeah, I've never been consistent ;)
>>>> I'm going to guess that the typical usecase for this device is in realtime
>>>> systems where polling gives a nice predictable timing (hence the lack of any
>>>> interrupt support).  These typically operate as 'scanning' devices
>>>> (e.g. you update all state at approximately the same time, by reading
>>>> one input after another in some predefined order).
>>> Probably you are right about 8435 h/w design or there were other
>>> reason why this functionality was dropped in comparison to 8429
>>> chip. The Holt chip hi-8429 has both hardware interrupt and hardware
>>> debounce enable lines, but it has less amount of sensors (just 8
>>> outputs).
>>>> Does the use of events actually map well to this use case?  You are taking
>>>> something that (other than the results being a bit different) is basically
>>>> a digital I/O interface and mapping it (sort of) to an interrupt chip
>>>> when it isn't nothing of the sort.
>>> a) Using hardware point of view.
>>>
>>> I was thinking that it matches the events a little bit more then
>>> buffer, because the chip senses for threshold changes (passing
>>> threshold high or threshold low margins) and then notifies the upper
>>> layer. The bad point here is that the chip does not have interrupt
>>> line for sensor state change (i.e. threshold passing).
>>>
>>> If it would have the interrupt line for sensor state changes then the
>>> event interface map this case best way.
>> Agreed.  I'm coming round to the argument Lars made that we should end
>> up with the interface being the same whether or not there is an interrupt
>> there.  Actually I suspect in the long run we will end up with both
>> a buffered interface and an event interface as they simply have different
>> usecases for the same hardware (which is why we are having these lengthy
>> discussions in the first place!)
>>
>>> b) Using s/w point of view
>>>
>>> I answer relying on current iio stack implementation. I do understand
>>> that the 8435 chip is not the common/usual iio client like
>>> adc/dac/light/others, so I'm trying to match the existent iio stack.
>> Yes.  It's certainly throwing up lots of questions!
>>>  From s/w point of view it does not matter much difference between
>>> event or buffer interface for this chip because in provides 1-bit
>>> change and does not have it's own interrupt source line.
>> Yes, just requires the userspace side to track the state if it wants
>> to know.  Actually the one nasty is initialization. When the events
>> are first enabled I guess we fire them all once (if any limits are currently
>> breached).  That way the state can always be known.
>>> The event interface has generic interface to fill in falling/rising
>>> thresholds but buffer interface does not.
>> We can always add more ABI.
>>
>>> The buffer interface has already has the trigger poll func support
>>> but event interface does not. So I've tried to implement the
>>> triggered event in iio stack. BTW: Doesn't the triggered event
>>> support match the usecase with iio_interrupt_trigger? Or it does not
>>> make sense to have triggered events (even with irqtrig) at all?
>> Initially I was unsure about this, but Lars and you have convinced me.
>> I'm happy having that in the subsystem.  Can also be used for devices
>> which have an interrupt but which didn't get wired for some reason rather
>> than having them have some internal poll loop (as we have done up to now).
>>
>>  
>>>> I'd like more opinions on this, but my gut reaction is that we would
>>>> be better making the normal buffered infrastructure handle this data
>>>> properly rather than pushing it through the events intrastructure.
>>>>
>>>> Your solution is neat and tidy in someways but feels like it is mashing
>>>> data into a particular format.
>>> Probably that's all because the chip lacks smth from buffer interface
>>> approach (the data should diferent the 1-bit) and smth from event
>>> interface approach (lack of h/w interrupt line)
>> Agreed.
>>>> To add to the complexity we could have debounce logic.  If we mapped to a
>>>> fill the buffer with a scan mode, how would we handle this?
>>>> My guess is that it would simply delay transistions.  Perhaps we even
>>>> support reading both the value right now and the debounced value if that
>>>> is possible?
>>> I did handle it in the driver the same way in both cases (buffer and
>>> event interfaces): event/buffer trigger issues interrupt and then the
>>> driver checks value right now and after delay (debounce_interval).
>> I am yet to be convinced we want software debounce in the kernel.  This
>> is something that the input subsystem has always had.  Partly the aim of
>> IIO was to provide a slim system where all this stuff was moved to userspace.
> Ok, I will remove the s/w debounce in the driver for now.
> 
>>
>> Ultimately the ideal option to my mind is to finally write an inkernel
>> interface for the events data flow (as we have for buffered data) then
>> we can have a generic attachable debounce unit independent of any particular
>> driver.  Hohum.  Now all I need is someone to write it or a few weeks off
>> work ;)
> This is a nice design :)
> But much more hard then implement this in the driver.
> 
>>> If we do not plan to put debounced values to buffer then we may cache
>>> during debouncing procedure and then return via
>>> IIO_CHAN_INFO_DEBOUNCE_VALUE (or other info name). Probably even with
>>> timestamp.
>>>> However, here the debounce is all software.  To my mind, move this into
>>>> userspace entirely. We aren't dealing with big data here so it's hardly
>>>> going to be particularly challenging.
>>> I will drop it on your demand in favour to encourage chipmakers to provide h/w debounce,
>>> but this was much easy/efficient to have it in kernel space, since
>>> during run generic_buffer sample (or other) we got the new data and then we have to start timer, then timer expires
>>> and we read raw values from sysfs manually to compare the one that came from buffer and one after debounce
>>> delay.
>> If you are clocking evenly and we were using buffering you'd simply do it as readback in time
>> rather than the other way around - thus no timer at all.  Just each time check the last N
>> samples to see if they are all high or all low or similar.
> This is right for high frequency clocking.
> If we are clocking evently at low frequency (~1Hz) and the recommended debounce interval is 10-100ms then
> we should use timer for issuing extra clock inside 10-100ms for check/validate the event.
>>
>>>> So my gut feeling is definitely we need to make the buffer infrastructure
>>>> handle 1 bit values (in groups) then push this data out that way.
>>> I will wait for your final decision.
>> Lets proceed with the event version.  If someone has a strong usecase
>> for adding buffering as well, there is nothing stopping the driver doing both
>> that I can see (once we have the core infrastructure in place).
> Ok, thx.
> 
>>>> Still I would like other opinions on this!
>>>>
>>>> Jonathan
>>>>
>>>>> ---
>>>>> Changes in version 2:
>>>>> - Added file sysfs-bus-iio-adc-hi8435
>>>>> - Changed naming from "discrete ADC" to "threshold detector"
>>>>> - Replaced swab16p/swab32p with be16_to_cpup/be32_to_cpup
>>>>> - Made *_show and *_store functions static
>>>>> - moved out from iio buffers to iio events
>>>>> - removed hi8436/hi8437 chips from the driver
>>>>> - moved from debounce_soft_delay/enable to debounce_interval via
>>>>>     IIO_CHAN_INFO_DEBOUNCE_TIME
>>>>> - added name extention "comparator"
>>>>> - moved threshold/hysteresis setup via generic iio event sysfs
>>>>> - added software mask/unmask channel events
>>>>> - added programming sensor outputs while in test mode via
>>>>>     IIO_CHAN_INFO_RAW
>>>>> - added channels .ext_info for programming sensing mode
>>>>> Changes in version 3:
>>>>> - fixed typos
>>>>> - moved <linux/interrupt.h> to match alphabetic order
>>>>> - fixed missed */event/* prefix in the ABI description
>>>>> - added missed colon in sysfs-bus-iio-adc-hi8435 file after "What"
>>>>> - moved in_voltageY_comparator_thresh_either_en and debounce_time
>>>>>     to sysfs-bus-iio file
>>>>> - added error checking for hi8435_write[b|w]
>>>>>
>>>>>    Documentation/ABI/testing/sysfs-bus-iio            |   1 +
>>>>>    Documentation/ABI/testing/sysfs-bus-iio-adc-hi8435 |  61 ++
>>>>>    drivers/iio/adc/Kconfig                            |  11 +
>>>>>    drivers/iio/adc/Makefile                           |   1 +
>>>>>    drivers/iio/adc/hi8435.c                           | 659 +++++++++++++++++++++
>>>>>    5 files changed, 742 insertions(+)
>>>>>    create mode 100644 Documentation/ABI/testing/sysfs-bus-iio-adc-hi8435
>>>>>    create mode 100644 drivers/iio/adc/hi8435.c
>>>>>
>>>>> diff --git a/Documentation/ABI/testing/sysfs-bus-iio b/Documentation/ABI/testing/sysfs-bus-iio
>>>>> index 70c9b1a..09eac44 100644
>>>>> --- a/Documentation/ABI/testing/sysfs-bus-iio
>>>>> +++ b/Documentation/ABI/testing/sysfs-bus-iio
>>>>> @@ -572,6 +572,7 @@ What:        /sys/.../iio:deviceX/events/in_rot_from_north_magnetic_tilt_comp_thresh_r
>>>>>    What:        /sys/.../iio:deviceX/events/in_rot_from_north_magnetic_tilt_comp_thresh_falling_en
>>>>>    What:        /sys/.../iio:deviceX/events/in_rot_from_north_true_tilt_comp_thresh_rising_en
>>>>>    What:        /sys/.../iio:deviceX/events/in_rot_from_north_true_tilt_comp_thresh_falling_en
>>>>> +What:        /sys/.../iio:deviceX/events/in_voltageY_comparator_thresh_either_en
>>>>>    What:        /sys/.../iio:deviceX/events/in_voltageY_supply_thresh_rising_en
>>>>>    What:        /sys/.../iio:deviceX/events/in_voltageY_supply_thresh_falling_en
>>>>>    What:        /sys/.../iio:deviceX/events/in_voltageY_thresh_rising_en
>>>>> diff --git a/Documentation/ABI/testing/sysfs-bus-iio-adc-hi8435 b/Documentation/ABI/testing/sysfs-bus-iio-adc-hi8435
>>>>> new file mode 100644
>>>>> index 0000000..c59d81d
>>>>> --- /dev/null
>>>>> +++ b/Documentation/ABI/testing/sysfs-bus-iio-adc-hi8435
>>>>> @@ -0,0 +1,61 @@
>>>>> +What:        /sys/bus/iio/devices/iio:deviceX/in_voltageY_comparator_raw
>> We really try to use the extended names infrequently and normally only
>> for things like 'this voltage channel is internally connected to the power
>> supply rail'.  Don't think we need it here.   As suggested below, if we
>> are going events then we don't want to expose a _raw reading except via
>> in the 'events' directory where it would be a 'current event status'.
>> Anyhow, I talk about that futher down.
> I will remove *_raw.
> 
>>>>> +Date:        July 2015
>>>>> +KernelVersion:    4.2.0
>>>>> +Contact:    source@cogentembedded.com
>>>>> +Description:
>>>>> +        Read value is a voltage threshold measurement from channel Y.
>>>>> +        Could be either 0 if sensor voltage is lower then low voltage
>>>>> +        threshold or 1 if sensor voltage is higher then high voltage
>>>>> +        threshold.
>>>>> +        Write value is a programmed sensor output while in self test
>>>>> +        mode. Could be either 0 or 1. The programmed value will be read
>>>>> +        back if /sys/bus/iio/devices/iio:deviceX/test_enable is set to 1.
>>>>> +
>>>>> +What:        /sys/bus/iio/devices/iio:deviceX/test_enable
>>>>> +Date:        July 2015
>>>>> +KernelVersion:    4.2.0
>>>>> +Contact:    source@cogentembedded.com
>>>>> +Description:
>>>>> +        Enable/disable the HI-8435 self test mode.
>>>>> +        If enabled the in_voltageY_comparator_raw should be read back
>>>>> +        accordingly to written value to in_voltageY_comparator_raw.
>> Hmm. Normally we just do self tests at startup.  So set some patterns, read them
>> and then verify they are fine.  Drops the need for a userspace interface
>> that can get a little confusing (writing to what is normally a read
>> only attribute).  Would this work here?
>>
>> Alternatively, just add a debugfs interface to allow this to be set rather than
>> exposing it via sysfs all the time?
> I will add debugfs or drop it (since I did use write_raw() for setting sensor output in test mode)
> 
>>
>>>>> +
>>>>> +What:        /sys/bus/iio/devices/iio:deviceX/in_voltageY_comparator_sensing_mode
>>>>> +Date:        July 2015
>>>>> +KernelVersion:    4.2.0
>>>>> +Contact:    source@cogentembedded.com
>>>>> +Description:
>>>>> +        Program sensor type for threshold detector inputs.
>>>>> +        Could be either "GND-Open" or "Supply-Open" mode. Y is a
>>>>> +        threshold detector input channel. Channels 0..7, 8..15, 16..23
>>>>> +        and 24..31 has common sensor types.
>>>>> +
>>>>> +What:        /sys/bus/iio/devices/iio:deviceX/events/in_voltageY_comparator_thresh_falling_value
>> This is where the comparator extended name looks a bit silly as we are
>> clearly comparing it anyway due to the thresh_falling part ;)
> I will remove the extended name.
> 
>>>>> +Date:        July 2015
>>>>> +KernelVersion:    4.2.0
>>>>> +Contact:    source@cogentembedded.com
>>>>> +Description:
>>>>> +        Channel Y low voltage threshold. If sensor input voltage goes lower then
>>>>> +        this value then the threshold falling event is pushed.
>>>>> +        Depending on in_voltageY_comparator_sensing_mode the low voltage threshold
>>>>> +        is separately set for "GND-Open" and "Supply-Open" modes.
>>>>> +        Channels 0..31 have common low threshold values, but could have different
>>>>> +        sensing_modes.
>>>>> +        The low voltage threshold range is between 2..21V.
>>>>> +        Hysteresis between low and high thresholds can not be lower then 2 and
>>>>> +        can not be odd.
>>>>> +
>>>>> +What:        /sys/bus/iio/devices/iio:deviceX/events/in_voltageY_comparator_thresh_rising_value
>>>>> +Date:        July 2015
>>>>> +KernelVersion:    4.2.0
>>>>> +Contact:    source@cogentembedded.com
>>>>> +Description:
>>>>> +        Channel Y high voltage threshold. If sensor input voltage goes higher then
>>>>> +        this value then the threshold rising event is pushed.
>>>>> +        Depending on in_voltageY_comparator_sensing_mode the high voltage threshold
>>>>> +        is separately set for "GND-Open" and "Supply-Open" modes.
>>>>> +        Channels 0..31 have common high threshold values, but could have different
>>>>> +        sensing_modes.
>>>>> +        The high voltage threshold range is between 3..22V.
>>>>> +        Hysteresis between low and high thresholds can not be lower then 2 and
>>>>> +        can not be odd.
>>>>> diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig
>>>>> index eb0cd89..553c91e 100644
>>>>> --- a/drivers/iio/adc/Kconfig
>>>>> +++ b/drivers/iio/adc/Kconfig
>>>>> @@ -170,6 +170,17 @@ config EXYNOS_ADC
>>>>>          of SoCs for drivers such as the touchscreen and hwmon to use to share
>>>>>          this resource.
>>>>>    +config HI8435
>>>>> +    tristate "Holt Integrated Circuits HI-8435 threshold detector"
>>>>> +    select IIO_TRIGGERED_EVENT
>>>>> +    depends on SPI
>>>>> +    help
>>>>> +      If you say yes here you get support for Holt Integrated Circuits
>>>>> +      HI-8435 chip.
>>>>> +
>>>>> +      This driver can also be built as a module. If so, the module will be
>>>>> +      called hi8435.
>>>>> +
>>>>>    config LP8788_ADC
>>>>>        tristate "LP8788 ADC driver"
>>>>>        depends on MFD_LP8788
>>>>> diff --git a/drivers/iio/adc/Makefile b/drivers/iio/adc/Makefile
>>>>> index a096210..00f367aa 100644
>>>>> --- a/drivers/iio/adc/Makefile
>>>>> +++ b/drivers/iio/adc/Makefile
>>>>> @@ -19,6 +19,7 @@ obj-$(CONFIG_BERLIN2_ADC) += berlin2-adc.o
>>>>>    obj-$(CONFIG_DA9150_GPADC) += da9150-gpadc.o
>>>>>    obj-$(CONFIG_CC10001_ADC) += cc10001_adc.o
>>>>>    obj-$(CONFIG_EXYNOS_ADC) += exynos_adc.o
>>>>> +obj-$(CONFIG_HI8435) += hi8435.o
>>>>>    obj-$(CONFIG_LP8788_ADC) += lp8788_adc.o
>>>>>    obj-$(CONFIG_MAX1027) += max1027.o
>>>>>    obj-$(CONFIG_MAX1363) += max1363.o
>>>>> diff --git a/drivers/iio/adc/hi8435.c b/drivers/iio/adc/hi8435.c
>>>>> new file mode 100644
>>>>> index 0000000..f3dab5a
>>>>> --- /dev/null
>>>>> +++ b/drivers/iio/adc/hi8435.c
>>>>> @@ -0,0 +1,659 @@
>>>>> +/*
>>>>> + * Holt Integrated Circuits HI-8435 threshold detector driver
>>>>> + *
>>>>> + * Copyright (C) 2015 Zodiac Inflight Innovations
>>>>> + * Copyright (C) 2015 Cogent Embedded, Inc.
>>>>> + *
>>>>> + * 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.
>>>>> + */
>>>>> +
>>>>> +#include <linux/delay.h>
>>>>> +#include <linux/iio/events.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/triggered_event.h>
>>>>> +#include <linux/interrupt.h>
>>>>> +#include <linux/module.h>
>>>>> +#include <linux/of.h>
>>>>> +#include <linux/of_device.h>
>>>>> +#include <linux/of_gpio.h>
>>>>> +#include <linux/spi/spi.h>
>>>>> +#include <linux/workqueue.h>
>>>>> +
>>>>> +#define DRV_NAME "hi8435"
>>>>> +
>>>>> +/* Register offsets for HI-8435 */
>>>>> +#define HI8435_CTRL_REG            0x02
>>>>> +#define HI8435_PSEN_REG            0x04
>>>>> +#define HI8435_TMDATA_REG        0x1E
>>>>> +#define HI8435_GOCENHYS_REG        0x3A
>>>>> +#define HI8435_SOCENHYS_REG        0x3C
>>>> Hmm. These aren't in the docs that I can immediately acquire...
>>>> Any chance of some more meaningful naming?
>>> I did follow the register naming in the chip datasheet:
>>> http://www.holtic.com/products/3081-hi-8435.aspx
>>>
>>> The SO7_0 means "Sensor status bank 0 register: SO<7:0>".
>>> Does HI8435_STATUS_BANK0_REG work?
>> Ah, I missed the meaning entirely by not noticing they were 8 bits
>> each ;)  This current naming is fine.
>>> Or should I just add comments instead of changing definition?
>>>>> +#define HI8435_SO7_0_REG        0x10
>>>>> +#define HI8435_SO15_8_REG        0x12
>>>>    +#define HI8435_SO23_16_REG        0x14
>>>>> +#define HI8435_SO31_24_REG        0x16
>>>>> +#define HI8435_SO31_0_REG        0x78
>>>>> +
>>>>> +#define HI8435_WRITE_OPCODE        0x00
>>>>> +#define HI8435_READ_OPCODE        0x80
>>>>> +
>>>>> +/* CTRL register bits */
>>>>> +#define HI8435_CTRL_TEST        0x01
>>>>> +#define HI8435_CTRL_SRST        0x02
>>>>> +
>>>>> +#define HI8435_DEBOUNCE_DELAY_MAX    1000    /* msec */
>>>>> +#define HI8435_DEBOUNCE_DELAY_DEF    100    /* msec */
>>>>> +
>>>>> +struct hi8435_priv {
>>>>> +    struct spi_device *spi;
>>>>> +    struct mutex lock;
>>>>> +    struct delayed_work work;
>>>>> +
>>>>> +    int reset_gpio;
>>>>> +    int debounce_interval; /* msec */
>>>>> +    u32 debounce_val; /* prev value to compare during software debounce */
>>>>> +
>>>>> +    unsigned long event_scan_mask; /* soft mask/unmask channels events */
>>>>> +    unsigned int event_prev_val;
>>>>> +
>>>>> +    unsigned threshold_lo[2]; /* GND-Open and Supply-Open thresholds */
>>>>> +    unsigned threshold_hi[2]; /* GND-Open and Supply-Open threshold */
>>>>> +    u8 reg_buffer[4] ____cacheline_aligned;
>>>>> +};
>>>>> +
>>>>> +static int hi8435_readb(struct hi8435_priv *priv, u8 reg, u8 *val)
>>>>> +{
>>>>> +    reg |= HI8435_READ_OPCODE;
>>>>> +    return spi_write_then_read(priv->spi, &reg, 1, val, 1);
>>>>> +}
>>>>> +
>>>>> +static int hi8435_readw(struct hi8435_priv *priv, u8 reg, u16 *val)
>>>>> +{
>>>>> +    int ret;
>>>>> +
>>>>> +    reg |= HI8435_READ_OPCODE;
>>>>> +    ret = spi_write_then_read(priv->spi, &reg, 1, val, 2);
>>>>> +    *val = be16_to_cpup(val);
>>>>> +
>>>>> +    return ret;
>>>>> +}
>>>>> +
>>>>> +static int hi8435_readl(struct hi8435_priv *priv, u8 reg, u32 *val)
>>>>> +{
>>>>> +    int ret;
>>>>> +
>>>>> +    reg |= HI8435_READ_OPCODE;
>>>>> +    ret = spi_write_then_read(priv->spi, &reg, 1, val, 4);
>>>>> +    *val = be32_to_cpup(val);
>>>>> +
>>>>> +    return ret;
>>>>> +}
>>>>> +
>>>>> +static int hi8435_writeb(struct hi8435_priv *priv, u8 reg, u8 val)
>>>>> +{
>>>>> +    priv->reg_buffer[0] = reg | HI8435_WRITE_OPCODE;
>>>>> +    priv->reg_buffer[1] = val;
>>>>> +
>>>>> +    return spi_write(priv->spi, priv->reg_buffer, 2);
>>>>> +}
>>>>> +
>>>>> +static int hi8435_writew(struct hi8435_priv *priv, u8 reg, u16 val)
>>>>> +{
>>>>> +    priv->reg_buffer[0] = reg | HI8435_WRITE_OPCODE;
>>>>> +    priv->reg_buffer[1] = (val >> 8) & 0xff;
>>>>> +    priv->reg_buffer[2] = val & 0xff;
>>>>> +
>>>>> +    return spi_write(priv->spi, priv->reg_buffer, 3);
>>>>> +}
>>>>> +
>>>>> +static ssize_t hi8435_test_enable_show(struct device *dev,
>>>>> +                       struct device_attribute *attr, char *buf)
>>>>> +{
>>>>> +    struct hi8435_priv *priv = iio_priv(dev_to_iio_dev(dev));
>>>>> +    int ret;
>>>>> +    u8 reg;
>>>>> +
>>>>> +    ret = hi8435_readb(priv, HI8435_CTRL_REG, &reg);
>>>>> +    if (ret < 0)
>>>>> +        return ret;
>>>>> +
>>>>> +    return sprintf(buf, "%d\n", reg & HI8435_CTRL_TEST);
>>>>> +}
>>>>> +
>>>>> +static ssize_t hi8435_test_enable_store(struct device *dev,
>>>>> +                    struct device_attribute *attr,
>>>>> +                    const char *buf, size_t len)
>>>>> +{
>>>>> +    struct hi8435_priv *priv = iio_priv(dev_to_iio_dev(dev));
>>>>> +    unsigned int val;
>>>>> +    int ret;
>>>>> +
>>>>> +    ret = kstrtouint(buf, 10, &val);
>>>>> +    if (ret)
>>>>> +        return ret;
>>>>> +
>>>>> +    ret = hi8435_writeb(priv, HI8435_CTRL_REG, val ? HI8435_CTRL_TEST : 0);
>>>>> +    if (ret < 0)
>>>>> +        return ret;
>>>>> +
>>>>> +    return len;
>>>>> +}
>>>>> +
>>>>> +static IIO_DEVICE_ATTR(test_enable, S_IRUGO | S_IWUSR,
>>>>> +    hi8435_test_enable_show, hi8435_test_enable_store, 0);
>>>>> +
>>>>> +static struct attribute *hi8435_attributes[] = {
>>>>> +    &iio_dev_attr_test_enable.dev_attr.attr,
>>>>> +    NULL,
>>>>> +};
>>>>> +
>>>>> +static struct attribute_group hi8435_attribute_group = {
>>>>> +    .attrs = hi8435_attributes,
>>>>> +};
>>>>> +
>>>>> +static int hi8435_read_raw(struct iio_dev *idev,
>>>>> +               const struct iio_chan_spec *chan,
>>>>> +               int *val, int *val2, long mask)
>>>>> +{
>>>>> +    struct hi8435_priv *priv = iio_priv(idev);
>>>>> +    int ret;
>>>>> +
>>>>> +    switch (mask) {
>>>>> +    case IIO_CHAN_INFO_RAW:
>>>>> +        ret = hi8435_readl(priv, HI8435_SO31_0_REG, val);
>>>>> +        if (ret < 0)
>>>>> +            return ret;
>>>>> +        *val = !!(*val & BIT(chan->channel));
>>>>> +        return IIO_VAL_INT;
>> This is where we end up in an odd possition having elected to report these
>> as 'events'.
>> We are reporting a 'last event state' via a read raw rather than as part
>> of the events infrastructure. I wonder if instead we want to allow the
>> events stuff to give us a 'am I true now signal'.
>> Would be easy enough to add and would give a clear interface.
> I agree.
> 
> I was thinking to use it only for getting initial states.
> But it is ok if we assume that default state is all 0 and then
> make UI changes in accordance to coming events.
That would be the option that makes most sense to me.
> 
> Also It might be useful for UI debounce for clocking at low frequency when
> debounce interval is much smaller then clocking/polling period.
That would really mean that the polling wasn't fast enough. I can see your
point though that maybe you'd want to ensure a value had been up for a
particular period before accepting it.  Still I doubt this sort of chip is
ever really used for a UI interface!

> 
>>
>> IIO_EV_INFO_CURRENT_STATE and attibute naming something like
>> in_voltageY_thresh_rising_state
>> which reads either 1 or 0?
> Got it.
> I will remove *_raw in favour of new event interface entry.
> It should be read-only (read_event_value), right?
yes
> 
> May I use the pair callback write_event_value() for my test_mode for
> setting each sense output manually?
> Or should I put all test mode related stuff into debugfs?
For now I'd say debugfs.  It's a bit of an odd corner case
so I'm a little anxious not to set a precedence for it in the main
ABI. Debugfs stuff is always more fluid anyway so it will be fine there.
> 
>>
>> For other devices we might need to take into account the
>> we are not currently measuring but that can be done by an error
>> return.
>>
>>>>> +    case IIO_CHAN_INFO_DEBOUNCE_TIME:
>>>>> +        *val = priv->debounce_interval;
>>>>> +        return IIO_VAL_INT;
>>>>> +    default:
>>>>> +        return -EINVAL;
>>>>> +    }
>>>>> +}
>>>>> +
>>>>> +static int hi8435_write_raw(struct iio_dev *idev,
>>>>> +                const struct iio_chan_spec *chan,
>>>>> +                int val, int val2, long mask)
>>>>> +{
>>>>> +    struct hi8435_priv *priv = iio_priv(idev);
>>>>> +
>>>>> +    switch (mask) {
>>>>> +    case IIO_CHAN_INFO_RAW:
>>>>> +        /* program sensors outputs in test mode */
>>>>> +        return hi8435_writeb(priv, HI8435_TMDATA_REG, val ? 0x1 : 0x2);
>>>>> +    case IIO_CHAN_INFO_DEBOUNCE_TIME:
>>>>> +        if (val < 0)
>>>>> +            return -EINVAL;
>>>>> +        if (val > HI8435_DEBOUNCE_DELAY_MAX)
>>>>> +            val = HI8435_DEBOUNCE_DELAY_MAX;
>>>>> +        priv->debounce_interval = val;
>>>>> +        return 0;
>>>>> +    default:
>>>>> +        return -EINVAL;
>>>>> +    }
>>>>> +}
>>>>> +
>>>>> +static int hi8435_read_event_config(struct iio_dev *idev,
>>>>> +                    const struct iio_chan_spec *chan,
>>>>> +                    enum iio_event_type type,
>>>>> +                    enum iio_event_direction dir)
>>>>> +{
>>>>> +    struct hi8435_priv *priv = iio_priv(idev);
>>>>> +
>>>>> +    return !!(priv->event_scan_mask & BIT(chan->channel));
>>>>> +}
>>>>> +
>>>>> +static int hi8435_write_event_config(struct iio_dev *idev,
>>>>> +                     const struct iio_chan_spec *chan,
>>>>> +                     enum iio_event_type type,
>>>>> +                     enum iio_event_direction dir, int state)
>>>>> +{
>>>>> +    struct hi8435_priv *priv = iio_priv(idev);
>>>>> +
>>>>> +    priv->event_scan_mask &= ~BIT(chan->channel);
>>>>> +    if (state)
>>>>> +        priv->event_scan_mask |= BIT(chan->channel);
>>>>> +
>>>>> +    return 0;
>>>>> +}
>>>>> +
>>>>> +static int hi8435_read_event_value(struct iio_dev *idev,
>>>>> +                   const struct iio_chan_spec *chan,
>>>>> +                   enum iio_event_type type,
>>>>> +                   enum iio_event_direction dir,
>>>>> +                   enum iio_event_info info,
>>>>> +                   int *val, int *val2)
>>>>> +{
>>>>> +    struct hi8435_priv *priv = iio_priv(idev);
>>>>> +    int ret;
>>>>> +    u8 mode, psen;
>>>>> +    u16 reg;
>>>>> +
>>>>> +    ret = hi8435_readb(priv, HI8435_PSEN_REG, &psen);
>>>>> +    if (ret < 0)
>>>>> +        return ret;
>>>>> +
>>>>> +    /* Supply-Open or GND-Open sensing mode */
>>>>> +    mode = !!(psen & BIT(chan->channel / 8));
>>>>> +
>>>>> +    ret = hi8435_readw(priv, mode ? HI8435_SOCENHYS_REG :
>>>>> +                 HI8435_GOCENHYS_REG, &reg);
>>>>> +    if (ret < 0)
>>>>> +        return ret;
>>>>> +
>>>>> +    if (dir == IIO_EV_DIR_FALLING)
>>>>> +        *val = ((reg & 0xff) - (reg >> 8)) / 2;
>>>>> +
>>>>> +    if (dir == IIO_EV_DIR_RISING)
>>>>> +        *val = ((reg & 0xff) + (reg >> 8)) / 2;
>>>>> +
>>>>> +    return IIO_VAL_INT;
>>>>> +}
>>>>> +
>>>>> +static int hi8435_write_event_value(struct iio_dev *idev,
>>>>> +                    const struct iio_chan_spec *chan,
>>>>> +                    enum iio_event_type type,
>>>>> +                    enum iio_event_direction dir,
>>>>> +                    enum iio_event_info info,
>>>>> +                    int val, int val2)
>>>>> +{
>>>>> +    struct hi8435_priv *priv = iio_priv(idev);
>>>>> +    int ret;
>>>>> +    u8 mode, psen;
>>>>> +    u16 reg;
>>>>> +
>>>>> +    ret = hi8435_readb(priv, HI8435_PSEN_REG, &psen);
>>>>> +    if (ret < 0)
>>>>> +        return ret;
>>>>> +
>>>>> +    /* Supply-Open or GND-Open sensing mode */
>>>>> +    mode = !!(psen & BIT(chan->channel / 8));
>>>>> +
>>>>> +    ret = hi8435_readw(priv, mode ? HI8435_SOCENHYS_REG :
>>>>> +                 HI8435_GOCENHYS_REG, &reg);
>>>>> +    if (ret < 0)
>>>>> +        return ret;
>>>>> +
>>>>> +    if (dir == IIO_EV_DIR_FALLING) {
>>>>> +        /* falling threshold range 2..21V, hysteresis minimum 2V */
>>>>> +        if (val < 2 || val > 21 || (val + 1) >= priv->threshold_hi[mode])
>>>>> +            return -EINVAL;
>>>>> +
>>>>> +        if (val == priv->threshold_lo[mode])
>>>>> +            return 0;
>>>>> +
>>>>> +        priv->threshold_lo[mode] = val;
>>>>> +
>>>>> +        /* hysteresis must not be odd */
>>>>> +        if ((priv->threshold_hi[mode] - priv->threshold_lo[mode]) % 2)
>>>>> +            priv->threshold_hi[mode]--;
>>>>> +    }
>>>>> +
>>>>> +    if (dir == IIO_EV_DIR_RISING) {
>>>>> +        /* rising threshold range 3..22V, hysteresis minimum 2V */
>>>>> +        if (val < 3 || val > 22 || val <= (priv->threshold_lo[mode] + 1))
>>>>> +            return -EINVAL;
>>>>> +
>>>>> +        if (val == priv->threshold_hi[mode])
>>>>> +            return 0;
>>>>> +
>>>>> +        priv->threshold_hi[mode] = val;
>>>>> +
>>>>> +        /* hysteresis must not be odd */
>>>>> +        if ((priv->threshold_hi[mode] - priv->threshold_lo[mode]) % 2)
>>>>> +            priv->threshold_lo[mode]++;
>>>>> +    }
>>>>> +
>>>>> +    /* program thresholds */
>>>>> +    mutex_lock(&priv->lock);
>>>>> +
>>>>> +    ret = hi8435_readw(priv, mode ? HI8435_SOCENHYS_REG :
>>>>> +                 HI8435_GOCENHYS_REG, &reg);
>>>>> +    if (ret < 0) {
>>>>> +        mutex_unlock(&priv->lock);
>>>>> +        return ret;
>>>>> +    }
>>>>> +
>>>>> +    /* hysteresis */
>>>>> +    reg = priv->threshold_hi[mode] - priv->threshold_lo[mode];
>>>>> +    reg <<= 8;
>>>>> +    /* threshold center */
>>>>> +    reg |= (priv->threshold_hi[mode] + priv->threshold_lo[mode]);
>>>>> +
>>>>> +    ret = hi8435_writew(priv, mode ? HI8435_SOCENHYS_REG :
>>>>> +                  HI8435_GOCENHYS_REG, reg);
>>>>> +
>>>>> +    mutex_unlock(&priv->lock);
>>>>> +
>>>>> +    return ret;
>>>>> +}
>>>>> +
>>>>> +static const struct iio_event_spec hi8435_events[] = {
>>>>> +    {
>>>>> +        .type = IIO_EV_TYPE_THRESH,
>>>>> +        .dir = IIO_EV_DIR_RISING,
>>>>> +        .mask_separate = BIT(IIO_EV_INFO_VALUE),
>>>>> +    }, {
>>>>> +        .type = IIO_EV_TYPE_THRESH,
>>>>> +        .dir = IIO_EV_DIR_FALLING,
>>>>> +        .mask_separate = BIT(IIO_EV_INFO_VALUE),
>>>>> +    }, {
>>>>> +        .type = IIO_EV_TYPE_THRESH,
>>>>> +        .dir = IIO_EV_DIR_EITHER,
>>>>> +        .mask_separate = BIT(IIO_EV_INFO_ENABLE),
>>>>> +    },
>>>>> +};
>>>>> +
>>>>> +static int hi8435_get_sensing_mode(struct iio_dev *idev,
>>>>> +                   const struct iio_chan_spec *chan)
>>>>> +{
>>>>> +    struct hi8435_priv *priv = iio_priv(idev);
>>>>> +    int ret;
>>>>> +    u8 reg;
>>>>> +
>>>>> +    ret = hi8435_readb(priv, HI8435_PSEN_REG, &reg);
>>>>> +    if (ret < 0)
>>>>> +        return ret;
>>>>> +
>>>>> +    return !!(reg & BIT(chan->channel / 8));
>>>>> +}
>>>>> +
>>>>> +static int hi8435_set_sensing_mode(struct iio_dev *idev,
>>>>> +                   const struct iio_chan_spec *chan,
>>>>> +                   unsigned int mode)
>>>>> +{
>>>>> +    struct hi8435_priv *priv = iio_priv(idev);
>>>>> +    int ret;
>>>>> +    u8 reg;
>>>>> +
>>>>> +    mutex_lock(&priv->lock);
>>>>> +
>>>>> +    ret = hi8435_readb(priv, HI8435_PSEN_REG, &reg);
>>>>> +    if (ret < 0) {
>>>>> +        mutex_unlock(&priv->lock);
>>>>> +        return ret;
>>>>> +    }
>>>>> +
>>>>> +    reg &= ~BIT(chan->channel / 8);
>>>>> +    if (mode)
>>>>> +        reg |= BIT(chan->channel / 8);
>>>>> +
>>>>> +    ret = hi8435_writeb(priv, HI8435_PSEN_REG, reg);
>>>>> +
>>>>> +    mutex_unlock(&priv->lock);
>>>>> +
>>>>> +    return ret;
>>>>> +}
>>>>> +
>>>>> +static const char * const hi8435_sensing_modes[] = { "GND-Open",
>>>>> +                             "Supply-Open" };
>>>>> +
>>>>> +static const struct iio_enum hi8435_sensing_mode = {
>>>>> +    .items = hi8435_sensing_modes,
>>>>> +    .num_items = ARRAY_SIZE(hi8435_sensing_modes),
>>>>> +    .get = hi8435_get_sensing_mode,
>>>>> +    .set = hi8435_set_sensing_mode,
>>>>> +};
>>>>> +
>>>>> +static const struct iio_chan_spec_ext_info hi8435_ext_info[] = {
>>>>> +    IIO_ENUM("sensing_mode", IIO_SEPARATE, &hi8435_sensing_mode),
>>>>> +    {},
>>>>> +};
>>>>> +
>>>>> +#define HI8435_VOLTAGE_CHANNEL(num)                    \
>>>>> +{                                    \
>>>>> +    .type = IIO_VOLTAGE,                        \
>>>>> +    .indexed = 1,                            \
>>>>> +    .channel = num,                            \
>>>>> +    .info_mask_separate = BIT(IIO_CHAN_INFO_RAW),            \
>> If we are using events, why have the raw access at all?
> I will remove *_raw.
> 
> I will probably add  IIO_EV_INFO_CURRENT_STATE.
> 
>>>>> +    .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_DEBOUNCE_TIME),    \
>>>>> +    .event_spec = hi8435_events,                    \
>>>>> +    .num_event_specs = ARRAY_SIZE(hi8435_events),            \
>>>>> +    .ext_info = hi8435_ext_info,                    \
>>>>> +    .extend_name = "comparator"                    \
>>>> I'm really not sure about this approach.  Can see where you are coming
>>>> from. We already have an events interface supporting these things so
>>>> why not use it?   I'm just doubtful it is an efficient option or a clean
>>>> interface (would like more opinions on this!)
>>> Sorry, I did not understand here.
>>> Is it about .extend_name ?
>>> I did add "comparator" extention to have attributes look like "in_voltage0_comparator_raw"
>> I'd drop it, particularly if we also drop the raw access.
> Now I understand. Thank you for clarification.
> I will remove it.
> 
>>>>> +}
>>>>> +
>>>>> +static const struct iio_chan_spec hi8435_channels[] = {
>>>>> +    HI8435_VOLTAGE_CHANNEL(0),
>>>>> +    HI8435_VOLTAGE_CHANNEL(1),
>>>>> +    HI8435_VOLTAGE_CHANNEL(2),
>>>>> +    HI8435_VOLTAGE_CHANNEL(3),
>>>>> +    HI8435_VOLTAGE_CHANNEL(4),
>>>>> +    HI8435_VOLTAGE_CHANNEL(5),
>>>>> +    HI8435_VOLTAGE_CHANNEL(6),
>>>>> +    HI8435_VOLTAGE_CHANNEL(7),
>>>>> +    HI8435_VOLTAGE_CHANNEL(8),
>>>>> +    HI8435_VOLTAGE_CHANNEL(9),
>>>>> +    HI8435_VOLTAGE_CHANNEL(10),
>>>>> +    HI8435_VOLTAGE_CHANNEL(11),
>>>>> +    HI8435_VOLTAGE_CHANNEL(12),
>>>>> +    HI8435_VOLTAGE_CHANNEL(13),
>>>>> +    HI8435_VOLTAGE_CHANNEL(14),
>>>>> +    HI8435_VOLTAGE_CHANNEL(15),
>>>>> +    HI8435_VOLTAGE_CHANNEL(16),
>>>>> +    HI8435_VOLTAGE_CHANNEL(17),
>>>>> +    HI8435_VOLTAGE_CHANNEL(18),
>>>>> +    HI8435_VOLTAGE_CHANNEL(19),
>>>>> +    HI8435_VOLTAGE_CHANNEL(20),
>>>>> +    HI8435_VOLTAGE_CHANNEL(21),
>>>>> +    HI8435_VOLTAGE_CHANNEL(22),
>>>>> +    HI8435_VOLTAGE_CHANNEL(23),
>>>>> +    HI8435_VOLTAGE_CHANNEL(24),
>>>>> +    HI8435_VOLTAGE_CHANNEL(25),
>>>>> +    HI8435_VOLTAGE_CHANNEL(26),
>>>>> +    HI8435_VOLTAGE_CHANNEL(27),
>>>>> +    HI8435_VOLTAGE_CHANNEL(28),
>>>>> +    HI8435_VOLTAGE_CHANNEL(29),
>>>>> +    HI8435_VOLTAGE_CHANNEL(30),
>>>>> +    HI8435_VOLTAGE_CHANNEL(31),
>>>>> +    IIO_CHAN_SOFT_TIMESTAMP(32),
>>>>> +};
>>>>> +
>>>>> +static const struct iio_info hi8435_info = {
>>>>> +    .driver_module = THIS_MODULE,
>>>>> +    .attrs = &hi8435_attribute_group,
>>>>> +    .read_raw = hi8435_read_raw,
>>>>> +    .write_raw = hi8435_write_raw,
>>>>> +    .read_event_config = &hi8435_read_event_config,
>>>>> +    .write_event_config = hi8435_write_event_config,
>>>>> +    .read_event_value = &hi8435_read_event_value,
>>>>> +    .write_event_value = &hi8435_write_event_value,
>>>>> +};
>>>>> +
>>>>> +static void hi8435_iio_push_event(struct iio_dev *idev, unsigned int val)
>>>>> +{
>>>>> +    struct hi8435_priv *priv = iio_priv(idev);
>>>>> +    enum iio_event_direction dir;
>>>>> +    unsigned int i;
>>>>> +    unsigned int status = priv->event_prev_val ^ val;
>>>>> +
>>>>> +    if (!status)
>>>>> +        return;
>>>>> +
>>>>> +    for_each_set_bit(i, &priv->event_scan_mask, 32) {
>>>>> +        if (!(status & BIT(i)))
>>>>> +            continue;
>>>>> +
>>>>> +        dir = val & BIT(i) ? IIO_EV_DIR_RISING : IIO_EV_DIR_FALLING;
>>>>> +
>>>>> +        iio_push_event(idev,
>>>>> +                   IIO_UNMOD_EVENT_CODE(IIO_VOLTAGE, i,
>>>>> +                            IIO_EV_TYPE_THRESH, dir),
>>>>> +                   iio_get_time_ns());
>>>>> +    }
>>>>> +
>>>>> +    priv->event_prev_val = val;
>>>>> +}
>>>>> +
>>>>> +static void hi8435_debounce_work(struct work_struct *work)
>>>>> +{
>>>>> +    struct hi8435_priv *priv = container_of(work, struct hi8435_priv,
>>>>> +                        work.work);
>>>>> +    struct iio_dev *idev = spi_get_drvdata(priv->spi);
>>>>> +    u32 val;
>>>>> +    int ret;
>>>>> +
>>>>> +    ret = hi8435_readl(priv, HI8435_SO31_0_REG, &val);
>>>>> +    if (ret < 0)
>>>>> +        return;
>>>>> +
>>>>> +    if (val == priv->debounce_val)
>>>>> +        hi8435_iio_push_event(idev, val);
>>>>> +    else
>>>>> +        dev_warn(&priv->spi->dev, "filtered by software debounce");
>>>> Definitely not.  Warning every time a standard event occurs?  Not
>>>> a good idea.  Use the debug stuff if you want a way of knowing this
>>>> happened, then it will silent under normal use.
>>> I'd say it is not standard event, but occasional/debounce event.
>>> Anyway I agree with you.
>>>
>>>>> +}
>>>>> +
>>>>> +static irqreturn_t hi8435_trigger_handler(int irq, void *private)
>>>>> +{
>>>>> +    struct iio_poll_func *pf = private;
>>>>> +    struct iio_dev *idev = pf->indio_dev;
>>>>> +    struct hi8435_priv *priv = iio_priv(idev);
>>>>> +    u32 val;
>>>>> +    int ret;
>>>>> +
>>>>> +    ret = hi8435_readl(priv, HI8435_SO31_0_REG, &val);
>>>>> +    if (ret < 0)
>>>>> +        goto err_read;
>>>>> +
>>>>> +    if (priv->debounce_interval) {
>>>>> +        priv->debounce_val = val;
>>>>> +        schedule_delayed_work(&priv->work,
>>>>> +                msecs_to_jiffies(priv->debounce_interval));
>>>> This is another element that makes me doubt that the event interface
>>>> is the way to do this.  I'd much rather we pushed the debouncing out
>>>> to userspace where cleverer things can be done (and more adaptive things).
>>>> Here to keep the event flow low you have to do it in the kernel.
>>> Yes, it is helpful for reducing the data flow (buffer data or event data)
>>> Many kernel drivers support s/w event debouncing, f.e. usb cable plug-in, or other connectors.
>> True enough that it is common functionality.  However, we try to keep
>> the IIO datahandling as minimal as possible and leave the job to userspace.
>>
>> We aren't likely to be dealing with huge datarates here (as we are effectively
>> polling at maybe a few hundred Hz).   Also, with the hysterisis set sensibly
>> seems unlikely that much bounce due to noise will ever occur.  If you get
>> bounce that overcomes that then you probably have a wiring or logic problem!
> Ok, I see. I will remove it.
> 
>>>>> +    } else {
>>>>> +        hi8435_iio_push_event(idev, val);
>>>>> +    }
>>>>> +
>>>>> +err_read:
>>>>> +    iio_trigger_notify_done(idev->trig);
>>>>> +
>>>>> +    return IRQ_HANDLED;
>>>>> +}
>>>>> +
>>>>> +static void hi8435_parse_dt(struct hi8435_priv *priv)
>>>>> +{
>>>>> +    struct device_node *np = priv->spi->dev.of_node;
>>>>> +    int ret;
>>>>> +
>>>>> +    ret = of_get_named_gpio(np, "holt,reset-gpios", 0);
>>>>> +    priv->reset_gpio = ret < 0 ? 0 : ret;
>>>> Silly question, but can gpio0 exist on a board?  I suspect you
>>>> may need an additional variable to hold that this request failed
>>>> rather than using the magic value of 0.
>>> I will do handle this case, thank you.
>> Look at what Lars added on this question...
>>>>> +
>>>>> +    ret = of_property_read_u32(np, "holt,debounce-interval",
>>>>> +                   &priv->debounce_interval);
>>>>> +    if (ret)
>>>>> +        priv->debounce_interval = 0;
>>>>> +    if (priv->debounce_interval > HI8435_DEBOUNCE_DELAY_MAX)
>>>>> +        priv->debounce_interval = HI8435_DEBOUNCE_DELAY_MAX;
>>>>> +}
>>>>> +
>>>>> +static int hi8435_probe(struct spi_device *spi)
>>>>> +{
>>>>> +    struct iio_dev *idev;
>>>>> +    struct hi8435_priv *priv;
>>>>> +    int ret;
>>>>> +
>>>>> +    idev = devm_iio_device_alloc(&spi->dev, sizeof(*priv));
>>>>> +    if (!idev)
>>>>> +        return -ENOMEM;
>>>>> +
>>>>> +    priv = iio_priv(idev);
>>>>> +    priv->spi = spi;
>>>>> +
>>>>> +    if (spi->dev.of_node)
>>>>> +        hi8435_parse_dt(priv);
>>>>> +
>>>>> +    spi_set_drvdata(spi, idev);
>>>>> +    mutex_init(&priv->lock);
>>>>> +    INIT_DELAYED_WORK(&priv->work, hi8435_debounce_work);
>>>>> +
>>>>> +    idev->dev.parent    = &spi->dev;
>>>>> +    idev->name        = spi_get_device_id(spi)->name;
>>>>> +    idev->modes        = INDIO_DIRECT_MODE;
>>>>> +    idev->info        = &hi8435_info;
>>>>> +    idev->channels        = hi8435_channels;
>>>>> +    idev->num_channels    = ARRAY_SIZE(hi8435_channels);
>>>>> +
>>>>> +    if (priv->reset_gpio) {
>>>>> +        ret = devm_gpio_request(&spi->dev, priv->reset_gpio, idev->name);
>>>>> +        if (!ret) {
>>>>> +            /* chip hardware reset */
>>>>> +            gpio_direction_output(priv->reset_gpio, 0);
>>>>> +            udelay(5);
>>>>> +            gpio_direction_output(priv->reset_gpio, 1);
>>>>> +        }
>>>>> +    } else {
>>>>> +        /* chip software reset */
>>>>> +        hi8435_writeb(priv, HI8435_CTRL_REG, HI8435_CTRL_SRST);
>>>>> +        /* get out from reset state */
>>>>> +        hi8435_writeb(priv, HI8435_CTRL_REG, 0);
>>>>> +    }
>>>>> +
>>>>> +    /* unmask all events */
>>>>> +    priv->event_scan_mask = ~(0);
>>>>> +    /* initialize default thresholds */
>>>>> +    priv->threshold_lo[0] = priv->threshold_lo[1] = 2;
>>>>> +    priv->threshold_hi[0] = priv->threshold_hi[1] = 4;
>>>> These seem very random.  Why these values or
>>> The aim for thresholds initialization is that they are in undefined state after h/w reset and
>>> the driver allows userspace to change thresholds (high or low) separately.
>>>
>>> There is a restriction in the chip - the hysteresis can not be even.
>>> If the hysteresis is set to even value then it get's into lock state and not functional anymore. (I've figured this out empirically).
>> *nice*
>>> So we need to initialize thresholds (aka hysteresis + it's center) to some initial value to be able to change
>>> high and low thresholds separately and avoid even values for hysteresis.
>>>
>>> I will also add these comments.
>> Cool
>>>>> +    hi8435_writew(priv, HI8435_GOCENHYS_REG, 0x206);
>>>> What is 0x206?  Again, I guess a default. I'd like to see a comment
>>>> here saying what real world value it corresponds to.
>>> Yes it is default values.
>>>
>>> The 0x206 is a code for HI threshold voltage = 4V and low voltage threshold = 2V.
>>> The register HI8435_SOCENHYS_REG describes the voltage hysteresis of the sensor and the hysteresis center.
>>> The 0x206 is an encoded value for 2V-to-4V voltage threshold.
>>>
>>> I will add comments.
>> good.
>>>>> +    hi8435_writew(priv, HI8435_SOCENHYS_REG, 0x206);
>>>>> +
>>>> I am as yet unconvinced that using triggers to poll events is a terribly
>>>> good idea.  Feels rather like we are doing this due to a deficiency in
>>>> the buffer interface than because sending events makes sense here.
>>> Actually the first version of this driver was against the buffer interface.
>>> I did try to use your tip and switched to event interface :)
>> oops.
>>> Do you think I should switch to buffer interface back?
>> Nope. As discussed above, right now this approach is winning (just ;)
>>>>> +    ret = iio_triggered_event_setup(idev, NULL, hi8435_trigger_handler);
>>>>> +    if (ret)
>>>>> +        return ret;
>>>>> +
>>>>> +    ret = iio_device_register(idev);
>>>>> +    if (ret < 0) {
>>>>> +        dev_err(&spi->dev, "unable to register device\n");
>>>>> +        goto unregister_triggered_event;
>>>>> +    }
>>>>> +
>>>>> +    return 0;
>>>>> +
>>>>> +unregister_triggered_event:
>>>>> +    iio_triggered_event_cleanup(idev);
>>>>> +    return ret;
>>>>> +}
>>>>> +
>>>>> +static int hi8435_remove(struct spi_device *spi)
>>>>> +{
>>>>> +    struct iio_dev *idev = spi_get_drvdata(spi);
>>>>> +    struct hi8435_priv *priv = iio_priv(idev);
>>>>> +
>>>>> +    cancel_delayed_work_sync(&priv->work);
>>>>> +    iio_device_unregister(idev);
>>>>> +    iio_triggered_event_cleanup(idev);
>>>>> +
>>>>> +    return 0;
>>>>> +}
>>>>> +
>>>>> +static const struct of_device_id hi8435_dt_ids[] = {
>>>>> +    { .compatible = "holt,hi8435" },
>>>>> +    {},
>>>>> +};
>>>>> +MODULE_DEVICE_TABLE(of, hi8435_dt_ids);
>>>>> +
>>>>> +static const struct spi_device_id hi8435_id[] = {
>>>>> +    { "hi8435", 0},
>>>>> +    { }
>>>>> +};
>>>>> +MODULE_DEVICE_TABLE(spi, hi8435_id);
>>>>> +
>>>>> +static struct spi_driver hi8435_driver = {
>>>>> +    .driver    = {
>>>>> +        .name        = DRV_NAME,
>>>>> +        .of_match_table    = of_match_ptr(hi8435_dt_ids),
>>>>> +    },
>>>>> +    .probe        = hi8435_probe,
>>>>> +    .remove        = hi8435_remove,
>>>>> +    .id_table    = hi8435_id,
>>>>> +};
>>>>> +module_spi_driver(hi8435_driver);
>>>>> +
>>>>> +MODULE_LICENSE("GPL");
>>>>> +MODULE_AUTHOR("Vladimir Barinov");
>>>>> +MODULE_DESCRIPTION("HI-8435 threshold detector");
>>>>>
>>> -- 
>>> To unsubscribe from this list: send the line "unsubscribe linux-iio" in
>>> the body of a message to majordomo@vger.kernel.org
>>> More majordomo info at  http://vger.kernel.org/majordomo-info.html
> 
> 


^ permalink raw reply	[flat|nested] 40+ messages in thread

* Re: [PATCH v3 1/7] iio: adc: hi8435: Holt HI-8435 threshold detector
@ 2015-08-22 13:48             ` Jonathan Cameron
  0 siblings, 0 replies; 40+ messages in thread
From: Jonathan Cameron @ 2015-08-22 13:48 UTC (permalink / raw)
  To: Vladimir Barinov
  Cc: Hartmut Knaack, Lars-Peter Clausen, Peter Meerwald, Rob Herring,
	Pawel Moll, Mark Rutland, Ian Campbell, Kumar Gala,
	linux-kernel-u79uwXL29TY76Z2rM5mHXA,
	linux-iio-u79uwXL29TY76Z2rM5mHXA,
	devicetree-u79uwXL29TY76Z2rM5mHXA,
	cory.tusar-J6Z/VSE8EyIAspv4Qr0y0gC/G2K4zDHf

On 16/08/15 19:54, Vladimir Barinov wrote:
> Hi Jonathan,
> 
> On 16.08.2015 12:00, Jonathan Cameron wrote:
>> On 11/08/15 15:37, Vladimir Barinov wrote:
>>> Hi Jonathan,
>>>
>>> Thank you for the review.
>> Sorry I was being so indecisive.  Always take a while when something
>> 'new' turns up to gather opinions and select the best option.  You
>> are just the unlucky guy who happened to hit the question first!
>>
>> Anyhow, I'm basically happy (now) with the events approach though
>> I would suggest some modifications to how exactly it is done
>> (see inline).
> Nice to hear!
> I've got some light in the tunnel :)
Yeah, sorry it took so long to pin down the approach!
> 
> Thank you for dealing with this stuff.
> Please find one minor questions below.
> 
> Regards,
> Vladimir
> 
>>
>> Jonathan
>>> On 08.08.2015 20:56, Jonathan Cameron wrote:
>>>> On 29/07/15 13:56, Vladimir Barinov wrote:
>>>>> Add Holt threshold detector driver for HI-8435 chip
>>>>>
>>>>> Signed-off-by: Vladimir Barinov <vladimir.barinov-M4DtvfQ/ZS1MRgGoP+s0PdBPR1lH4CV8@public.gmane.org>
>>>> Because it usually reads better that way, I review from the bottom.
>>>> Might make more sense if you read the comments that way around ;)
>>> I did try to match this way.
>>> At some places the comments below the code, but in other places the comments are above the code :)
>> Yeah, I've never been consistent ;)
>>>> I'm going to guess that the typical usecase for this device is in realtime
>>>> systems where polling gives a nice predictable timing (hence the lack of any
>>>> interrupt support).  These typically operate as 'scanning' devices
>>>> (e.g. you update all state at approximately the same time, by reading
>>>> one input after another in some predefined order).
>>> Probably you are right about 8435 h/w design or there were other
>>> reason why this functionality was dropped in comparison to 8429
>>> chip. The Holt chip hi-8429 has both hardware interrupt and hardware
>>> debounce enable lines, but it has less amount of sensors (just 8
>>> outputs).
>>>> Does the use of events actually map well to this use case?  You are taking
>>>> something that (other than the results being a bit different) is basically
>>>> a digital I/O interface and mapping it (sort of) to an interrupt chip
>>>> when it isn't nothing of the sort.
>>> a) Using hardware point of view.
>>>
>>> I was thinking that it matches the events a little bit more then
>>> buffer, because the chip senses for threshold changes (passing
>>> threshold high or threshold low margins) and then notifies the upper
>>> layer. The bad point here is that the chip does not have interrupt
>>> line for sensor state change (i.e. threshold passing).
>>>
>>> If it would have the interrupt line for sensor state changes then the
>>> event interface map this case best way.
>> Agreed.  I'm coming round to the argument Lars made that we should end
>> up with the interface being the same whether or not there is an interrupt
>> there.  Actually I suspect in the long run we will end up with both
>> a buffered interface and an event interface as they simply have different
>> usecases for the same hardware (which is why we are having these lengthy
>> discussions in the first place!)
>>
>>> b) Using s/w point of view
>>>
>>> I answer relying on current iio stack implementation. I do understand
>>> that the 8435 chip is not the common/usual iio client like
>>> adc/dac/light/others, so I'm trying to match the existent iio stack.
>> Yes.  It's certainly throwing up lots of questions!
>>>  From s/w point of view it does not matter much difference between
>>> event or buffer interface for this chip because in provides 1-bit
>>> change and does not have it's own interrupt source line.
>> Yes, just requires the userspace side to track the state if it wants
>> to know.  Actually the one nasty is initialization. When the events
>> are first enabled I guess we fire them all once (if any limits are currently
>> breached).  That way the state can always be known.
>>> The event interface has generic interface to fill in falling/rising
>>> thresholds but buffer interface does not.
>> We can always add more ABI.
>>
>>> The buffer interface has already has the trigger poll func support
>>> but event interface does not. So I've tried to implement the
>>> triggered event in iio stack. BTW: Doesn't the triggered event
>>> support match the usecase with iio_interrupt_trigger? Or it does not
>>> make sense to have triggered events (even with irqtrig) at all?
>> Initially I was unsure about this, but Lars and you have convinced me.
>> I'm happy having that in the subsystem.  Can also be used for devices
>> which have an interrupt but which didn't get wired for some reason rather
>> than having them have some internal poll loop (as we have done up to now).
>>
>>  
>>>> I'd like more opinions on this, but my gut reaction is that we would
>>>> be better making the normal buffered infrastructure handle this data
>>>> properly rather than pushing it through the events intrastructure.
>>>>
>>>> Your solution is neat and tidy in someways but feels like it is mashing
>>>> data into a particular format.
>>> Probably that's all because the chip lacks smth from buffer interface
>>> approach (the data should diferent the 1-bit) and smth from event
>>> interface approach (lack of h/w interrupt line)
>> Agreed.
>>>> To add to the complexity we could have debounce logic.  If we mapped to a
>>>> fill the buffer with a scan mode, how would we handle this?
>>>> My guess is that it would simply delay transistions.  Perhaps we even
>>>> support reading both the value right now and the debounced value if that
>>>> is possible?
>>> I did handle it in the driver the same way in both cases (buffer and
>>> event interfaces): event/buffer trigger issues interrupt and then the
>>> driver checks value right now and after delay (debounce_interval).
>> I am yet to be convinced we want software debounce in the kernel.  This
>> is something that the input subsystem has always had.  Partly the aim of
>> IIO was to provide a slim system where all this stuff was moved to userspace.
> Ok, I will remove the s/w debounce in the driver for now.
> 
>>
>> Ultimately the ideal option to my mind is to finally write an inkernel
>> interface for the events data flow (as we have for buffered data) then
>> we can have a generic attachable debounce unit independent of any particular
>> driver.  Hohum.  Now all I need is someone to write it or a few weeks off
>> work ;)
> This is a nice design :)
> But much more hard then implement this in the driver.
> 
>>> If we do not plan to put debounced values to buffer then we may cache
>>> during debouncing procedure and then return via
>>> IIO_CHAN_INFO_DEBOUNCE_VALUE (or other info name). Probably even with
>>> timestamp.
>>>> However, here the debounce is all software.  To my mind, move this into
>>>> userspace entirely. We aren't dealing with big data here so it's hardly
>>>> going to be particularly challenging.
>>> I will drop it on your demand in favour to encourage chipmakers to provide h/w debounce,
>>> but this was much easy/efficient to have it in kernel space, since
>>> during run generic_buffer sample (or other) we got the new data and then we have to start timer, then timer expires
>>> and we read raw values from sysfs manually to compare the one that came from buffer and one after debounce
>>> delay.
>> If you are clocking evenly and we were using buffering you'd simply do it as readback in time
>> rather than the other way around - thus no timer at all.  Just each time check the last N
>> samples to see if they are all high or all low or similar.
> This is right for high frequency clocking.
> If we are clocking evently at low frequency (~1Hz) and the recommended debounce interval is 10-100ms then
> we should use timer for issuing extra clock inside 10-100ms for check/validate the event.
>>
>>>> So my gut feeling is definitely we need to make the buffer infrastructure
>>>> handle 1 bit values (in groups) then push this data out that way.
>>> I will wait for your final decision.
>> Lets proceed with the event version.  If someone has a strong usecase
>> for adding buffering as well, there is nothing stopping the driver doing both
>> that I can see (once we have the core infrastructure in place).
> Ok, thx.
> 
>>>> Still I would like other opinions on this!
>>>>
>>>> Jonathan
>>>>
>>>>> ---
>>>>> Changes in version 2:
>>>>> - Added file sysfs-bus-iio-adc-hi8435
>>>>> - Changed naming from "discrete ADC" to "threshold detector"
>>>>> - Replaced swab16p/swab32p with be16_to_cpup/be32_to_cpup
>>>>> - Made *_show and *_store functions static
>>>>> - moved out from iio buffers to iio events
>>>>> - removed hi8436/hi8437 chips from the driver
>>>>> - moved from debounce_soft_delay/enable to debounce_interval via
>>>>>     IIO_CHAN_INFO_DEBOUNCE_TIME
>>>>> - added name extention "comparator"
>>>>> - moved threshold/hysteresis setup via generic iio event sysfs
>>>>> - added software mask/unmask channel events
>>>>> - added programming sensor outputs while in test mode via
>>>>>     IIO_CHAN_INFO_RAW
>>>>> - added channels .ext_info for programming sensing mode
>>>>> Changes in version 3:
>>>>> - fixed typos
>>>>> - moved <linux/interrupt.h> to match alphabetic order
>>>>> - fixed missed */event/* prefix in the ABI description
>>>>> - added missed colon in sysfs-bus-iio-adc-hi8435 file after "What"
>>>>> - moved in_voltageY_comparator_thresh_either_en and debounce_time
>>>>>     to sysfs-bus-iio file
>>>>> - added error checking for hi8435_write[b|w]
>>>>>
>>>>>    Documentation/ABI/testing/sysfs-bus-iio            |   1 +
>>>>>    Documentation/ABI/testing/sysfs-bus-iio-adc-hi8435 |  61 ++
>>>>>    drivers/iio/adc/Kconfig                            |  11 +
>>>>>    drivers/iio/adc/Makefile                           |   1 +
>>>>>    drivers/iio/adc/hi8435.c                           | 659 +++++++++++++++++++++
>>>>>    5 files changed, 742 insertions(+)
>>>>>    create mode 100644 Documentation/ABI/testing/sysfs-bus-iio-adc-hi8435
>>>>>    create mode 100644 drivers/iio/adc/hi8435.c
>>>>>
>>>>> diff --git a/Documentation/ABI/testing/sysfs-bus-iio b/Documentation/ABI/testing/sysfs-bus-iio
>>>>> index 70c9b1a..09eac44 100644
>>>>> --- a/Documentation/ABI/testing/sysfs-bus-iio
>>>>> +++ b/Documentation/ABI/testing/sysfs-bus-iio
>>>>> @@ -572,6 +572,7 @@ What:        /sys/.../iio:deviceX/events/in_rot_from_north_magnetic_tilt_comp_thresh_r
>>>>>    What:        /sys/.../iio:deviceX/events/in_rot_from_north_magnetic_tilt_comp_thresh_falling_en
>>>>>    What:        /sys/.../iio:deviceX/events/in_rot_from_north_true_tilt_comp_thresh_rising_en
>>>>>    What:        /sys/.../iio:deviceX/events/in_rot_from_north_true_tilt_comp_thresh_falling_en
>>>>> +What:        /sys/.../iio:deviceX/events/in_voltageY_comparator_thresh_either_en
>>>>>    What:        /sys/.../iio:deviceX/events/in_voltageY_supply_thresh_rising_en
>>>>>    What:        /sys/.../iio:deviceX/events/in_voltageY_supply_thresh_falling_en
>>>>>    What:        /sys/.../iio:deviceX/events/in_voltageY_thresh_rising_en
>>>>> diff --git a/Documentation/ABI/testing/sysfs-bus-iio-adc-hi8435 b/Documentation/ABI/testing/sysfs-bus-iio-adc-hi8435
>>>>> new file mode 100644
>>>>> index 0000000..c59d81d
>>>>> --- /dev/null
>>>>> +++ b/Documentation/ABI/testing/sysfs-bus-iio-adc-hi8435
>>>>> @@ -0,0 +1,61 @@
>>>>> +What:        /sys/bus/iio/devices/iio:deviceX/in_voltageY_comparator_raw
>> We really try to use the extended names infrequently and normally only
>> for things like 'this voltage channel is internally connected to the power
>> supply rail'.  Don't think we need it here.   As suggested below, if we
>> are going events then we don't want to expose a _raw reading except via
>> in the 'events' directory where it would be a 'current event status'.
>> Anyhow, I talk about that futher down.
> I will remove *_raw.
> 
>>>>> +Date:        July 2015
>>>>> +KernelVersion:    4.2.0
>>>>> +Contact:    source-M4DtvfQ/ZS1MRgGoP+s0PdBPR1lH4CV8@public.gmane.org
>>>>> +Description:
>>>>> +        Read value is a voltage threshold measurement from channel Y.
>>>>> +        Could be either 0 if sensor voltage is lower then low voltage
>>>>> +        threshold or 1 if sensor voltage is higher then high voltage
>>>>> +        threshold.
>>>>> +        Write value is a programmed sensor output while in self test
>>>>> +        mode. Could be either 0 or 1. The programmed value will be read
>>>>> +        back if /sys/bus/iio/devices/iio:deviceX/test_enable is set to 1.
>>>>> +
>>>>> +What:        /sys/bus/iio/devices/iio:deviceX/test_enable
>>>>> +Date:        July 2015
>>>>> +KernelVersion:    4.2.0
>>>>> +Contact:    source-M4DtvfQ/ZS1MRgGoP+s0PdBPR1lH4CV8@public.gmane.org
>>>>> +Description:
>>>>> +        Enable/disable the HI-8435 self test mode.
>>>>> +        If enabled the in_voltageY_comparator_raw should be read back
>>>>> +        accordingly to written value to in_voltageY_comparator_raw.
>> Hmm. Normally we just do self tests at startup.  So set some patterns, read them
>> and then verify they are fine.  Drops the need for a userspace interface
>> that can get a little confusing (writing to what is normally a read
>> only attribute).  Would this work here?
>>
>> Alternatively, just add a debugfs interface to allow this to be set rather than
>> exposing it via sysfs all the time?
> I will add debugfs or drop it (since I did use write_raw() for setting sensor output in test mode)
> 
>>
>>>>> +
>>>>> +What:        /sys/bus/iio/devices/iio:deviceX/in_voltageY_comparator_sensing_mode
>>>>> +Date:        July 2015
>>>>> +KernelVersion:    4.2.0
>>>>> +Contact:    source-M4DtvfQ/ZS1MRgGoP+s0PdBPR1lH4CV8@public.gmane.org
>>>>> +Description:
>>>>> +        Program sensor type for threshold detector inputs.
>>>>> +        Could be either "GND-Open" or "Supply-Open" mode. Y is a
>>>>> +        threshold detector input channel. Channels 0..7, 8..15, 16..23
>>>>> +        and 24..31 has common sensor types.
>>>>> +
>>>>> +What:        /sys/bus/iio/devices/iio:deviceX/events/in_voltageY_comparator_thresh_falling_value
>> This is where the comparator extended name looks a bit silly as we are
>> clearly comparing it anyway due to the thresh_falling part ;)
> I will remove the extended name.
> 
>>>>> +Date:        July 2015
>>>>> +KernelVersion:    4.2.0
>>>>> +Contact:    source-M4DtvfQ/ZS1MRgGoP+s0PdBPR1lH4CV8@public.gmane.org
>>>>> +Description:
>>>>> +        Channel Y low voltage threshold. If sensor input voltage goes lower then
>>>>> +        this value then the threshold falling event is pushed.
>>>>> +        Depending on in_voltageY_comparator_sensing_mode the low voltage threshold
>>>>> +        is separately set for "GND-Open" and "Supply-Open" modes.
>>>>> +        Channels 0..31 have common low threshold values, but could have different
>>>>> +        sensing_modes.
>>>>> +        The low voltage threshold range is between 2..21V.
>>>>> +        Hysteresis between low and high thresholds can not be lower then 2 and
>>>>> +        can not be odd.
>>>>> +
>>>>> +What:        /sys/bus/iio/devices/iio:deviceX/events/in_voltageY_comparator_thresh_rising_value
>>>>> +Date:        July 2015
>>>>> +KernelVersion:    4.2.0
>>>>> +Contact:    source-M4DtvfQ/ZS1MRgGoP+s0PdBPR1lH4CV8@public.gmane.org
>>>>> +Description:
>>>>> +        Channel Y high voltage threshold. If sensor input voltage goes higher then
>>>>> +        this value then the threshold rising event is pushed.
>>>>> +        Depending on in_voltageY_comparator_sensing_mode the high voltage threshold
>>>>> +        is separately set for "GND-Open" and "Supply-Open" modes.
>>>>> +        Channels 0..31 have common high threshold values, but could have different
>>>>> +        sensing_modes.
>>>>> +        The high voltage threshold range is between 3..22V.
>>>>> +        Hysteresis between low and high thresholds can not be lower then 2 and
>>>>> +        can not be odd.
>>>>> diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig
>>>>> index eb0cd89..553c91e 100644
>>>>> --- a/drivers/iio/adc/Kconfig
>>>>> +++ b/drivers/iio/adc/Kconfig
>>>>> @@ -170,6 +170,17 @@ config EXYNOS_ADC
>>>>>          of SoCs for drivers such as the touchscreen and hwmon to use to share
>>>>>          this resource.
>>>>>    +config HI8435
>>>>> +    tristate "Holt Integrated Circuits HI-8435 threshold detector"
>>>>> +    select IIO_TRIGGERED_EVENT
>>>>> +    depends on SPI
>>>>> +    help
>>>>> +      If you say yes here you get support for Holt Integrated Circuits
>>>>> +      HI-8435 chip.
>>>>> +
>>>>> +      This driver can also be built as a module. If so, the module will be
>>>>> +      called hi8435.
>>>>> +
>>>>>    config LP8788_ADC
>>>>>        tristate "LP8788 ADC driver"
>>>>>        depends on MFD_LP8788
>>>>> diff --git a/drivers/iio/adc/Makefile b/drivers/iio/adc/Makefile
>>>>> index a096210..00f367aa 100644
>>>>> --- a/drivers/iio/adc/Makefile
>>>>> +++ b/drivers/iio/adc/Makefile
>>>>> @@ -19,6 +19,7 @@ obj-$(CONFIG_BERLIN2_ADC) += berlin2-adc.o
>>>>>    obj-$(CONFIG_DA9150_GPADC) += da9150-gpadc.o
>>>>>    obj-$(CONFIG_CC10001_ADC) += cc10001_adc.o
>>>>>    obj-$(CONFIG_EXYNOS_ADC) += exynos_adc.o
>>>>> +obj-$(CONFIG_HI8435) += hi8435.o
>>>>>    obj-$(CONFIG_LP8788_ADC) += lp8788_adc.o
>>>>>    obj-$(CONFIG_MAX1027) += max1027.o
>>>>>    obj-$(CONFIG_MAX1363) += max1363.o
>>>>> diff --git a/drivers/iio/adc/hi8435.c b/drivers/iio/adc/hi8435.c
>>>>> new file mode 100644
>>>>> index 0000000..f3dab5a
>>>>> --- /dev/null
>>>>> +++ b/drivers/iio/adc/hi8435.c
>>>>> @@ -0,0 +1,659 @@
>>>>> +/*
>>>>> + * Holt Integrated Circuits HI-8435 threshold detector driver
>>>>> + *
>>>>> + * Copyright (C) 2015 Zodiac Inflight Innovations
>>>>> + * Copyright (C) 2015 Cogent Embedded, Inc.
>>>>> + *
>>>>> + * 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.
>>>>> + */
>>>>> +
>>>>> +#include <linux/delay.h>
>>>>> +#include <linux/iio/events.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/triggered_event.h>
>>>>> +#include <linux/interrupt.h>
>>>>> +#include <linux/module.h>
>>>>> +#include <linux/of.h>
>>>>> +#include <linux/of_device.h>
>>>>> +#include <linux/of_gpio.h>
>>>>> +#include <linux/spi/spi.h>
>>>>> +#include <linux/workqueue.h>
>>>>> +
>>>>> +#define DRV_NAME "hi8435"
>>>>> +
>>>>> +/* Register offsets for HI-8435 */
>>>>> +#define HI8435_CTRL_REG            0x02
>>>>> +#define HI8435_PSEN_REG            0x04
>>>>> +#define HI8435_TMDATA_REG        0x1E
>>>>> +#define HI8435_GOCENHYS_REG        0x3A
>>>>> +#define HI8435_SOCENHYS_REG        0x3C
>>>> Hmm. These aren't in the docs that I can immediately acquire...
>>>> Any chance of some more meaningful naming?
>>> I did follow the register naming in the chip datasheet:
>>> http://www.holtic.com/products/3081-hi-8435.aspx
>>>
>>> The SO7_0 means "Sensor status bank 0 register: SO<7:0>".
>>> Does HI8435_STATUS_BANK0_REG work?
>> Ah, I missed the meaning entirely by not noticing they were 8 bits
>> each ;)  This current naming is fine.
>>> Or should I just add comments instead of changing definition?
>>>>> +#define HI8435_SO7_0_REG        0x10
>>>>> +#define HI8435_SO15_8_REG        0x12
>>>>    +#define HI8435_SO23_16_REG        0x14
>>>>> +#define HI8435_SO31_24_REG        0x16
>>>>> +#define HI8435_SO31_0_REG        0x78
>>>>> +
>>>>> +#define HI8435_WRITE_OPCODE        0x00
>>>>> +#define HI8435_READ_OPCODE        0x80
>>>>> +
>>>>> +/* CTRL register bits */
>>>>> +#define HI8435_CTRL_TEST        0x01
>>>>> +#define HI8435_CTRL_SRST        0x02
>>>>> +
>>>>> +#define HI8435_DEBOUNCE_DELAY_MAX    1000    /* msec */
>>>>> +#define HI8435_DEBOUNCE_DELAY_DEF    100    /* msec */
>>>>> +
>>>>> +struct hi8435_priv {
>>>>> +    struct spi_device *spi;
>>>>> +    struct mutex lock;
>>>>> +    struct delayed_work work;
>>>>> +
>>>>> +    int reset_gpio;
>>>>> +    int debounce_interval; /* msec */
>>>>> +    u32 debounce_val; /* prev value to compare during software debounce */
>>>>> +
>>>>> +    unsigned long event_scan_mask; /* soft mask/unmask channels events */
>>>>> +    unsigned int event_prev_val;
>>>>> +
>>>>> +    unsigned threshold_lo[2]; /* GND-Open and Supply-Open thresholds */
>>>>> +    unsigned threshold_hi[2]; /* GND-Open and Supply-Open threshold */
>>>>> +    u8 reg_buffer[4] ____cacheline_aligned;
>>>>> +};
>>>>> +
>>>>> +static int hi8435_readb(struct hi8435_priv *priv, u8 reg, u8 *val)
>>>>> +{
>>>>> +    reg |= HI8435_READ_OPCODE;
>>>>> +    return spi_write_then_read(priv->spi, &reg, 1, val, 1);
>>>>> +}
>>>>> +
>>>>> +static int hi8435_readw(struct hi8435_priv *priv, u8 reg, u16 *val)
>>>>> +{
>>>>> +    int ret;
>>>>> +
>>>>> +    reg |= HI8435_READ_OPCODE;
>>>>> +    ret = spi_write_then_read(priv->spi, &reg, 1, val, 2);
>>>>> +    *val = be16_to_cpup(val);
>>>>> +
>>>>> +    return ret;
>>>>> +}
>>>>> +
>>>>> +static int hi8435_readl(struct hi8435_priv *priv, u8 reg, u32 *val)
>>>>> +{
>>>>> +    int ret;
>>>>> +
>>>>> +    reg |= HI8435_READ_OPCODE;
>>>>> +    ret = spi_write_then_read(priv->spi, &reg, 1, val, 4);
>>>>> +    *val = be32_to_cpup(val);
>>>>> +
>>>>> +    return ret;
>>>>> +}
>>>>> +
>>>>> +static int hi8435_writeb(struct hi8435_priv *priv, u8 reg, u8 val)
>>>>> +{
>>>>> +    priv->reg_buffer[0] = reg | HI8435_WRITE_OPCODE;
>>>>> +    priv->reg_buffer[1] = val;
>>>>> +
>>>>> +    return spi_write(priv->spi, priv->reg_buffer, 2);
>>>>> +}
>>>>> +
>>>>> +static int hi8435_writew(struct hi8435_priv *priv, u8 reg, u16 val)
>>>>> +{
>>>>> +    priv->reg_buffer[0] = reg | HI8435_WRITE_OPCODE;
>>>>> +    priv->reg_buffer[1] = (val >> 8) & 0xff;
>>>>> +    priv->reg_buffer[2] = val & 0xff;
>>>>> +
>>>>> +    return spi_write(priv->spi, priv->reg_buffer, 3);
>>>>> +}
>>>>> +
>>>>> +static ssize_t hi8435_test_enable_show(struct device *dev,
>>>>> +                       struct device_attribute *attr, char *buf)
>>>>> +{
>>>>> +    struct hi8435_priv *priv = iio_priv(dev_to_iio_dev(dev));
>>>>> +    int ret;
>>>>> +    u8 reg;
>>>>> +
>>>>> +    ret = hi8435_readb(priv, HI8435_CTRL_REG, &reg);
>>>>> +    if (ret < 0)
>>>>> +        return ret;
>>>>> +
>>>>> +    return sprintf(buf, "%d\n", reg & HI8435_CTRL_TEST);
>>>>> +}
>>>>> +
>>>>> +static ssize_t hi8435_test_enable_store(struct device *dev,
>>>>> +                    struct device_attribute *attr,
>>>>> +                    const char *buf, size_t len)
>>>>> +{
>>>>> +    struct hi8435_priv *priv = iio_priv(dev_to_iio_dev(dev));
>>>>> +    unsigned int val;
>>>>> +    int ret;
>>>>> +
>>>>> +    ret = kstrtouint(buf, 10, &val);
>>>>> +    if (ret)
>>>>> +        return ret;
>>>>> +
>>>>> +    ret = hi8435_writeb(priv, HI8435_CTRL_REG, val ? HI8435_CTRL_TEST : 0);
>>>>> +    if (ret < 0)
>>>>> +        return ret;
>>>>> +
>>>>> +    return len;
>>>>> +}
>>>>> +
>>>>> +static IIO_DEVICE_ATTR(test_enable, S_IRUGO | S_IWUSR,
>>>>> +    hi8435_test_enable_show, hi8435_test_enable_store, 0);
>>>>> +
>>>>> +static struct attribute *hi8435_attributes[] = {
>>>>> +    &iio_dev_attr_test_enable.dev_attr.attr,
>>>>> +    NULL,
>>>>> +};
>>>>> +
>>>>> +static struct attribute_group hi8435_attribute_group = {
>>>>> +    .attrs = hi8435_attributes,
>>>>> +};
>>>>> +
>>>>> +static int hi8435_read_raw(struct iio_dev *idev,
>>>>> +               const struct iio_chan_spec *chan,
>>>>> +               int *val, int *val2, long mask)
>>>>> +{
>>>>> +    struct hi8435_priv *priv = iio_priv(idev);
>>>>> +    int ret;
>>>>> +
>>>>> +    switch (mask) {
>>>>> +    case IIO_CHAN_INFO_RAW:
>>>>> +        ret = hi8435_readl(priv, HI8435_SO31_0_REG, val);
>>>>> +        if (ret < 0)
>>>>> +            return ret;
>>>>> +        *val = !!(*val & BIT(chan->channel));
>>>>> +        return IIO_VAL_INT;
>> This is where we end up in an odd possition having elected to report these
>> as 'events'.
>> We are reporting a 'last event state' via a read raw rather than as part
>> of the events infrastructure. I wonder if instead we want to allow the
>> events stuff to give us a 'am I true now signal'.
>> Would be easy enough to add and would give a clear interface.
> I agree.
> 
> I was thinking to use it only for getting initial states.
> But it is ok if we assume that default state is all 0 and then
> make UI changes in accordance to coming events.
That would be the option that makes most sense to me.
> 
> Also It might be useful for UI debounce for clocking at low frequency when
> debounce interval is much smaller then clocking/polling period.
That would really mean that the polling wasn't fast enough. I can see your
point though that maybe you'd want to ensure a value had been up for a
particular period before accepting it.  Still I doubt this sort of chip is
ever really used for a UI interface!

> 
>>
>> IIO_EV_INFO_CURRENT_STATE and attibute naming something like
>> in_voltageY_thresh_rising_state
>> which reads either 1 or 0?
> Got it.
> I will remove *_raw in favour of new event interface entry.
> It should be read-only (read_event_value), right?
yes
> 
> May I use the pair callback write_event_value() for my test_mode for
> setting each sense output manually?
> Or should I put all test mode related stuff into debugfs?
For now I'd say debugfs.  It's a bit of an odd corner case
so I'm a little anxious not to set a precedence for it in the main
ABI. Debugfs stuff is always more fluid anyway so it will be fine there.
> 
>>
>> For other devices we might need to take into account the
>> we are not currently measuring but that can be done by an error
>> return.
>>
>>>>> +    case IIO_CHAN_INFO_DEBOUNCE_TIME:
>>>>> +        *val = priv->debounce_interval;
>>>>> +        return IIO_VAL_INT;
>>>>> +    default:
>>>>> +        return -EINVAL;
>>>>> +    }
>>>>> +}
>>>>> +
>>>>> +static int hi8435_write_raw(struct iio_dev *idev,
>>>>> +                const struct iio_chan_spec *chan,
>>>>> +                int val, int val2, long mask)
>>>>> +{
>>>>> +    struct hi8435_priv *priv = iio_priv(idev);
>>>>> +
>>>>> +    switch (mask) {
>>>>> +    case IIO_CHAN_INFO_RAW:
>>>>> +        /* program sensors outputs in test mode */
>>>>> +        return hi8435_writeb(priv, HI8435_TMDATA_REG, val ? 0x1 : 0x2);
>>>>> +    case IIO_CHAN_INFO_DEBOUNCE_TIME:
>>>>> +        if (val < 0)
>>>>> +            return -EINVAL;
>>>>> +        if (val > HI8435_DEBOUNCE_DELAY_MAX)
>>>>> +            val = HI8435_DEBOUNCE_DELAY_MAX;
>>>>> +        priv->debounce_interval = val;
>>>>> +        return 0;
>>>>> +    default:
>>>>> +        return -EINVAL;
>>>>> +    }
>>>>> +}
>>>>> +
>>>>> +static int hi8435_read_event_config(struct iio_dev *idev,
>>>>> +                    const struct iio_chan_spec *chan,
>>>>> +                    enum iio_event_type type,
>>>>> +                    enum iio_event_direction dir)
>>>>> +{
>>>>> +    struct hi8435_priv *priv = iio_priv(idev);
>>>>> +
>>>>> +    return !!(priv->event_scan_mask & BIT(chan->channel));
>>>>> +}
>>>>> +
>>>>> +static int hi8435_write_event_config(struct iio_dev *idev,
>>>>> +                     const struct iio_chan_spec *chan,
>>>>> +                     enum iio_event_type type,
>>>>> +                     enum iio_event_direction dir, int state)
>>>>> +{
>>>>> +    struct hi8435_priv *priv = iio_priv(idev);
>>>>> +
>>>>> +    priv->event_scan_mask &= ~BIT(chan->channel);
>>>>> +    if (state)
>>>>> +        priv->event_scan_mask |= BIT(chan->channel);
>>>>> +
>>>>> +    return 0;
>>>>> +}
>>>>> +
>>>>> +static int hi8435_read_event_value(struct iio_dev *idev,
>>>>> +                   const struct iio_chan_spec *chan,
>>>>> +                   enum iio_event_type type,
>>>>> +                   enum iio_event_direction dir,
>>>>> +                   enum iio_event_info info,
>>>>> +                   int *val, int *val2)
>>>>> +{
>>>>> +    struct hi8435_priv *priv = iio_priv(idev);
>>>>> +    int ret;
>>>>> +    u8 mode, psen;
>>>>> +    u16 reg;
>>>>> +
>>>>> +    ret = hi8435_readb(priv, HI8435_PSEN_REG, &psen);
>>>>> +    if (ret < 0)
>>>>> +        return ret;
>>>>> +
>>>>> +    /* Supply-Open or GND-Open sensing mode */
>>>>> +    mode = !!(psen & BIT(chan->channel / 8));
>>>>> +
>>>>> +    ret = hi8435_readw(priv, mode ? HI8435_SOCENHYS_REG :
>>>>> +                 HI8435_GOCENHYS_REG, &reg);
>>>>> +    if (ret < 0)
>>>>> +        return ret;
>>>>> +
>>>>> +    if (dir == IIO_EV_DIR_FALLING)
>>>>> +        *val = ((reg & 0xff) - (reg >> 8)) / 2;
>>>>> +
>>>>> +    if (dir == IIO_EV_DIR_RISING)
>>>>> +        *val = ((reg & 0xff) + (reg >> 8)) / 2;
>>>>> +
>>>>> +    return IIO_VAL_INT;
>>>>> +}
>>>>> +
>>>>> +static int hi8435_write_event_value(struct iio_dev *idev,
>>>>> +                    const struct iio_chan_spec *chan,
>>>>> +                    enum iio_event_type type,
>>>>> +                    enum iio_event_direction dir,
>>>>> +                    enum iio_event_info info,
>>>>> +                    int val, int val2)
>>>>> +{
>>>>> +    struct hi8435_priv *priv = iio_priv(idev);
>>>>> +    int ret;
>>>>> +    u8 mode, psen;
>>>>> +    u16 reg;
>>>>> +
>>>>> +    ret = hi8435_readb(priv, HI8435_PSEN_REG, &psen);
>>>>> +    if (ret < 0)
>>>>> +        return ret;
>>>>> +
>>>>> +    /* Supply-Open or GND-Open sensing mode */
>>>>> +    mode = !!(psen & BIT(chan->channel / 8));
>>>>> +
>>>>> +    ret = hi8435_readw(priv, mode ? HI8435_SOCENHYS_REG :
>>>>> +                 HI8435_GOCENHYS_REG, &reg);
>>>>> +    if (ret < 0)
>>>>> +        return ret;
>>>>> +
>>>>> +    if (dir == IIO_EV_DIR_FALLING) {
>>>>> +        /* falling threshold range 2..21V, hysteresis minimum 2V */
>>>>> +        if (val < 2 || val > 21 || (val + 1) >= priv->threshold_hi[mode])
>>>>> +            return -EINVAL;
>>>>> +
>>>>> +        if (val == priv->threshold_lo[mode])
>>>>> +            return 0;
>>>>> +
>>>>> +        priv->threshold_lo[mode] = val;
>>>>> +
>>>>> +        /* hysteresis must not be odd */
>>>>> +        if ((priv->threshold_hi[mode] - priv->threshold_lo[mode]) % 2)
>>>>> +            priv->threshold_hi[mode]--;
>>>>> +    }
>>>>> +
>>>>> +    if (dir == IIO_EV_DIR_RISING) {
>>>>> +        /* rising threshold range 3..22V, hysteresis minimum 2V */
>>>>> +        if (val < 3 || val > 22 || val <= (priv->threshold_lo[mode] + 1))
>>>>> +            return -EINVAL;
>>>>> +
>>>>> +        if (val == priv->threshold_hi[mode])
>>>>> +            return 0;
>>>>> +
>>>>> +        priv->threshold_hi[mode] = val;
>>>>> +
>>>>> +        /* hysteresis must not be odd */
>>>>> +        if ((priv->threshold_hi[mode] - priv->threshold_lo[mode]) % 2)
>>>>> +            priv->threshold_lo[mode]++;
>>>>> +    }
>>>>> +
>>>>> +    /* program thresholds */
>>>>> +    mutex_lock(&priv->lock);
>>>>> +
>>>>> +    ret = hi8435_readw(priv, mode ? HI8435_SOCENHYS_REG :
>>>>> +                 HI8435_GOCENHYS_REG, &reg);
>>>>> +    if (ret < 0) {
>>>>> +        mutex_unlock(&priv->lock);
>>>>> +        return ret;
>>>>> +    }
>>>>> +
>>>>> +    /* hysteresis */
>>>>> +    reg = priv->threshold_hi[mode] - priv->threshold_lo[mode];
>>>>> +    reg <<= 8;
>>>>> +    /* threshold center */
>>>>> +    reg |= (priv->threshold_hi[mode] + priv->threshold_lo[mode]);
>>>>> +
>>>>> +    ret = hi8435_writew(priv, mode ? HI8435_SOCENHYS_REG :
>>>>> +                  HI8435_GOCENHYS_REG, reg);
>>>>> +
>>>>> +    mutex_unlock(&priv->lock);
>>>>> +
>>>>> +    return ret;
>>>>> +}
>>>>> +
>>>>> +static const struct iio_event_spec hi8435_events[] = {
>>>>> +    {
>>>>> +        .type = IIO_EV_TYPE_THRESH,
>>>>> +        .dir = IIO_EV_DIR_RISING,
>>>>> +        .mask_separate = BIT(IIO_EV_INFO_VALUE),
>>>>> +    }, {
>>>>> +        .type = IIO_EV_TYPE_THRESH,
>>>>> +        .dir = IIO_EV_DIR_FALLING,
>>>>> +        .mask_separate = BIT(IIO_EV_INFO_VALUE),
>>>>> +    }, {
>>>>> +        .type = IIO_EV_TYPE_THRESH,
>>>>> +        .dir = IIO_EV_DIR_EITHER,
>>>>> +        .mask_separate = BIT(IIO_EV_INFO_ENABLE),
>>>>> +    },
>>>>> +};
>>>>> +
>>>>> +static int hi8435_get_sensing_mode(struct iio_dev *idev,
>>>>> +                   const struct iio_chan_spec *chan)
>>>>> +{
>>>>> +    struct hi8435_priv *priv = iio_priv(idev);
>>>>> +    int ret;
>>>>> +    u8 reg;
>>>>> +
>>>>> +    ret = hi8435_readb(priv, HI8435_PSEN_REG, &reg);
>>>>> +    if (ret < 0)
>>>>> +        return ret;
>>>>> +
>>>>> +    return !!(reg & BIT(chan->channel / 8));
>>>>> +}
>>>>> +
>>>>> +static int hi8435_set_sensing_mode(struct iio_dev *idev,
>>>>> +                   const struct iio_chan_spec *chan,
>>>>> +                   unsigned int mode)
>>>>> +{
>>>>> +    struct hi8435_priv *priv = iio_priv(idev);
>>>>> +    int ret;
>>>>> +    u8 reg;
>>>>> +
>>>>> +    mutex_lock(&priv->lock);
>>>>> +
>>>>> +    ret = hi8435_readb(priv, HI8435_PSEN_REG, &reg);
>>>>> +    if (ret < 0) {
>>>>> +        mutex_unlock(&priv->lock);
>>>>> +        return ret;
>>>>> +    }
>>>>> +
>>>>> +    reg &= ~BIT(chan->channel / 8);
>>>>> +    if (mode)
>>>>> +        reg |= BIT(chan->channel / 8);
>>>>> +
>>>>> +    ret = hi8435_writeb(priv, HI8435_PSEN_REG, reg);
>>>>> +
>>>>> +    mutex_unlock(&priv->lock);
>>>>> +
>>>>> +    return ret;
>>>>> +}
>>>>> +
>>>>> +static const char * const hi8435_sensing_modes[] = { "GND-Open",
>>>>> +                             "Supply-Open" };
>>>>> +
>>>>> +static const struct iio_enum hi8435_sensing_mode = {
>>>>> +    .items = hi8435_sensing_modes,
>>>>> +    .num_items = ARRAY_SIZE(hi8435_sensing_modes),
>>>>> +    .get = hi8435_get_sensing_mode,
>>>>> +    .set = hi8435_set_sensing_mode,
>>>>> +};
>>>>> +
>>>>> +static const struct iio_chan_spec_ext_info hi8435_ext_info[] = {
>>>>> +    IIO_ENUM("sensing_mode", IIO_SEPARATE, &hi8435_sensing_mode),
>>>>> +    {},
>>>>> +};
>>>>> +
>>>>> +#define HI8435_VOLTAGE_CHANNEL(num)                    \
>>>>> +{                                    \
>>>>> +    .type = IIO_VOLTAGE,                        \
>>>>> +    .indexed = 1,                            \
>>>>> +    .channel = num,                            \
>>>>> +    .info_mask_separate = BIT(IIO_CHAN_INFO_RAW),            \
>> If we are using events, why have the raw access at all?
> I will remove *_raw.
> 
> I will probably add  IIO_EV_INFO_CURRENT_STATE.
> 
>>>>> +    .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_DEBOUNCE_TIME),    \
>>>>> +    .event_spec = hi8435_events,                    \
>>>>> +    .num_event_specs = ARRAY_SIZE(hi8435_events),            \
>>>>> +    .ext_info = hi8435_ext_info,                    \
>>>>> +    .extend_name = "comparator"                    \
>>>> I'm really not sure about this approach.  Can see where you are coming
>>>> from. We already have an events interface supporting these things so
>>>> why not use it?   I'm just doubtful it is an efficient option or a clean
>>>> interface (would like more opinions on this!)
>>> Sorry, I did not understand here.
>>> Is it about .extend_name ?
>>> I did add "comparator" extention to have attributes look like "in_voltage0_comparator_raw"
>> I'd drop it, particularly if we also drop the raw access.
> Now I understand. Thank you for clarification.
> I will remove it.
> 
>>>>> +}
>>>>> +
>>>>> +static const struct iio_chan_spec hi8435_channels[] = {
>>>>> +    HI8435_VOLTAGE_CHANNEL(0),
>>>>> +    HI8435_VOLTAGE_CHANNEL(1),
>>>>> +    HI8435_VOLTAGE_CHANNEL(2),
>>>>> +    HI8435_VOLTAGE_CHANNEL(3),
>>>>> +    HI8435_VOLTAGE_CHANNEL(4),
>>>>> +    HI8435_VOLTAGE_CHANNEL(5),
>>>>> +    HI8435_VOLTAGE_CHANNEL(6),
>>>>> +    HI8435_VOLTAGE_CHANNEL(7),
>>>>> +    HI8435_VOLTAGE_CHANNEL(8),
>>>>> +    HI8435_VOLTAGE_CHANNEL(9),
>>>>> +    HI8435_VOLTAGE_CHANNEL(10),
>>>>> +    HI8435_VOLTAGE_CHANNEL(11),
>>>>> +    HI8435_VOLTAGE_CHANNEL(12),
>>>>> +    HI8435_VOLTAGE_CHANNEL(13),
>>>>> +    HI8435_VOLTAGE_CHANNEL(14),
>>>>> +    HI8435_VOLTAGE_CHANNEL(15),
>>>>> +    HI8435_VOLTAGE_CHANNEL(16),
>>>>> +    HI8435_VOLTAGE_CHANNEL(17),
>>>>> +    HI8435_VOLTAGE_CHANNEL(18),
>>>>> +    HI8435_VOLTAGE_CHANNEL(19),
>>>>> +    HI8435_VOLTAGE_CHANNEL(20),
>>>>> +    HI8435_VOLTAGE_CHANNEL(21),
>>>>> +    HI8435_VOLTAGE_CHANNEL(22),
>>>>> +    HI8435_VOLTAGE_CHANNEL(23),
>>>>> +    HI8435_VOLTAGE_CHANNEL(24),
>>>>> +    HI8435_VOLTAGE_CHANNEL(25),
>>>>> +    HI8435_VOLTAGE_CHANNEL(26),
>>>>> +    HI8435_VOLTAGE_CHANNEL(27),
>>>>> +    HI8435_VOLTAGE_CHANNEL(28),
>>>>> +    HI8435_VOLTAGE_CHANNEL(29),
>>>>> +    HI8435_VOLTAGE_CHANNEL(30),
>>>>> +    HI8435_VOLTAGE_CHANNEL(31),
>>>>> +    IIO_CHAN_SOFT_TIMESTAMP(32),
>>>>> +};
>>>>> +
>>>>> +static const struct iio_info hi8435_info = {
>>>>> +    .driver_module = THIS_MODULE,
>>>>> +    .attrs = &hi8435_attribute_group,
>>>>> +    .read_raw = hi8435_read_raw,
>>>>> +    .write_raw = hi8435_write_raw,
>>>>> +    .read_event_config = &hi8435_read_event_config,
>>>>> +    .write_event_config = hi8435_write_event_config,
>>>>> +    .read_event_value = &hi8435_read_event_value,
>>>>> +    .write_event_value = &hi8435_write_event_value,
>>>>> +};
>>>>> +
>>>>> +static void hi8435_iio_push_event(struct iio_dev *idev, unsigned int val)
>>>>> +{
>>>>> +    struct hi8435_priv *priv = iio_priv(idev);
>>>>> +    enum iio_event_direction dir;
>>>>> +    unsigned int i;
>>>>> +    unsigned int status = priv->event_prev_val ^ val;
>>>>> +
>>>>> +    if (!status)
>>>>> +        return;
>>>>> +
>>>>> +    for_each_set_bit(i, &priv->event_scan_mask, 32) {
>>>>> +        if (!(status & BIT(i)))
>>>>> +            continue;
>>>>> +
>>>>> +        dir = val & BIT(i) ? IIO_EV_DIR_RISING : IIO_EV_DIR_FALLING;
>>>>> +
>>>>> +        iio_push_event(idev,
>>>>> +                   IIO_UNMOD_EVENT_CODE(IIO_VOLTAGE, i,
>>>>> +                            IIO_EV_TYPE_THRESH, dir),
>>>>> +                   iio_get_time_ns());
>>>>> +    }
>>>>> +
>>>>> +    priv->event_prev_val = val;
>>>>> +}
>>>>> +
>>>>> +static void hi8435_debounce_work(struct work_struct *work)
>>>>> +{
>>>>> +    struct hi8435_priv *priv = container_of(work, struct hi8435_priv,
>>>>> +                        work.work);
>>>>> +    struct iio_dev *idev = spi_get_drvdata(priv->spi);
>>>>> +    u32 val;
>>>>> +    int ret;
>>>>> +
>>>>> +    ret = hi8435_readl(priv, HI8435_SO31_0_REG, &val);
>>>>> +    if (ret < 0)
>>>>> +        return;
>>>>> +
>>>>> +    if (val == priv->debounce_val)
>>>>> +        hi8435_iio_push_event(idev, val);
>>>>> +    else
>>>>> +        dev_warn(&priv->spi->dev, "filtered by software debounce");
>>>> Definitely not.  Warning every time a standard event occurs?  Not
>>>> a good idea.  Use the debug stuff if you want a way of knowing this
>>>> happened, then it will silent under normal use.
>>> I'd say it is not standard event, but occasional/debounce event.
>>> Anyway I agree with you.
>>>
>>>>> +}
>>>>> +
>>>>> +static irqreturn_t hi8435_trigger_handler(int irq, void *private)
>>>>> +{
>>>>> +    struct iio_poll_func *pf = private;
>>>>> +    struct iio_dev *idev = pf->indio_dev;
>>>>> +    struct hi8435_priv *priv = iio_priv(idev);
>>>>> +    u32 val;
>>>>> +    int ret;
>>>>> +
>>>>> +    ret = hi8435_readl(priv, HI8435_SO31_0_REG, &val);
>>>>> +    if (ret < 0)
>>>>> +        goto err_read;
>>>>> +
>>>>> +    if (priv->debounce_interval) {
>>>>> +        priv->debounce_val = val;
>>>>> +        schedule_delayed_work(&priv->work,
>>>>> +                msecs_to_jiffies(priv->debounce_interval));
>>>> This is another element that makes me doubt that the event interface
>>>> is the way to do this.  I'd much rather we pushed the debouncing out
>>>> to userspace where cleverer things can be done (and more adaptive things).
>>>> Here to keep the event flow low you have to do it in the kernel.
>>> Yes, it is helpful for reducing the data flow (buffer data or event data)
>>> Many kernel drivers support s/w event debouncing, f.e. usb cable plug-in, or other connectors.
>> True enough that it is common functionality.  However, we try to keep
>> the IIO datahandling as minimal as possible and leave the job to userspace.
>>
>> We aren't likely to be dealing with huge datarates here (as we are effectively
>> polling at maybe a few hundred Hz).   Also, with the hysterisis set sensibly
>> seems unlikely that much bounce due to noise will ever occur.  If you get
>> bounce that overcomes that then you probably have a wiring or logic problem!
> Ok, I see. I will remove it.
> 
>>>>> +    } else {
>>>>> +        hi8435_iio_push_event(idev, val);
>>>>> +    }
>>>>> +
>>>>> +err_read:
>>>>> +    iio_trigger_notify_done(idev->trig);
>>>>> +
>>>>> +    return IRQ_HANDLED;
>>>>> +}
>>>>> +
>>>>> +static void hi8435_parse_dt(struct hi8435_priv *priv)
>>>>> +{
>>>>> +    struct device_node *np = priv->spi->dev.of_node;
>>>>> +    int ret;
>>>>> +
>>>>> +    ret = of_get_named_gpio(np, "holt,reset-gpios", 0);
>>>>> +    priv->reset_gpio = ret < 0 ? 0 : ret;
>>>> Silly question, but can gpio0 exist on a board?  I suspect you
>>>> may need an additional variable to hold that this request failed
>>>> rather than using the magic value of 0.
>>> I will do handle this case, thank you.
>> Look at what Lars added on this question...
>>>>> +
>>>>> +    ret = of_property_read_u32(np, "holt,debounce-interval",
>>>>> +                   &priv->debounce_interval);
>>>>> +    if (ret)
>>>>> +        priv->debounce_interval = 0;
>>>>> +    if (priv->debounce_interval > HI8435_DEBOUNCE_DELAY_MAX)
>>>>> +        priv->debounce_interval = HI8435_DEBOUNCE_DELAY_MAX;
>>>>> +}
>>>>> +
>>>>> +static int hi8435_probe(struct spi_device *spi)
>>>>> +{
>>>>> +    struct iio_dev *idev;
>>>>> +    struct hi8435_priv *priv;
>>>>> +    int ret;
>>>>> +
>>>>> +    idev = devm_iio_device_alloc(&spi->dev, sizeof(*priv));
>>>>> +    if (!idev)
>>>>> +        return -ENOMEM;
>>>>> +
>>>>> +    priv = iio_priv(idev);
>>>>> +    priv->spi = spi;
>>>>> +
>>>>> +    if (spi->dev.of_node)
>>>>> +        hi8435_parse_dt(priv);
>>>>> +
>>>>> +    spi_set_drvdata(spi, idev);
>>>>> +    mutex_init(&priv->lock);
>>>>> +    INIT_DELAYED_WORK(&priv->work, hi8435_debounce_work);
>>>>> +
>>>>> +    idev->dev.parent    = &spi->dev;
>>>>> +    idev->name        = spi_get_device_id(spi)->name;
>>>>> +    idev->modes        = INDIO_DIRECT_MODE;
>>>>> +    idev->info        = &hi8435_info;
>>>>> +    idev->channels        = hi8435_channels;
>>>>> +    idev->num_channels    = ARRAY_SIZE(hi8435_channels);
>>>>> +
>>>>> +    if (priv->reset_gpio) {
>>>>> +        ret = devm_gpio_request(&spi->dev, priv->reset_gpio, idev->name);
>>>>> +        if (!ret) {
>>>>> +            /* chip hardware reset */
>>>>> +            gpio_direction_output(priv->reset_gpio, 0);
>>>>> +            udelay(5);
>>>>> +            gpio_direction_output(priv->reset_gpio, 1);
>>>>> +        }
>>>>> +    } else {
>>>>> +        /* chip software reset */
>>>>> +        hi8435_writeb(priv, HI8435_CTRL_REG, HI8435_CTRL_SRST);
>>>>> +        /* get out from reset state */
>>>>> +        hi8435_writeb(priv, HI8435_CTRL_REG, 0);
>>>>> +    }
>>>>> +
>>>>> +    /* unmask all events */
>>>>> +    priv->event_scan_mask = ~(0);
>>>>> +    /* initialize default thresholds */
>>>>> +    priv->threshold_lo[0] = priv->threshold_lo[1] = 2;
>>>>> +    priv->threshold_hi[0] = priv->threshold_hi[1] = 4;
>>>> These seem very random.  Why these values or
>>> The aim for thresholds initialization is that they are in undefined state after h/w reset and
>>> the driver allows userspace to change thresholds (high or low) separately.
>>>
>>> There is a restriction in the chip - the hysteresis can not be even.
>>> If the hysteresis is set to even value then it get's into lock state and not functional anymore. (I've figured this out empirically).
>> *nice*
>>> So we need to initialize thresholds (aka hysteresis + it's center) to some initial value to be able to change
>>> high and low thresholds separately and avoid even values for hysteresis.
>>>
>>> I will also add these comments.
>> Cool
>>>>> +    hi8435_writew(priv, HI8435_GOCENHYS_REG, 0x206);
>>>> What is 0x206?  Again, I guess a default. I'd like to see a comment
>>>> here saying what real world value it corresponds to.
>>> Yes it is default values.
>>>
>>> The 0x206 is a code for HI threshold voltage = 4V and low voltage threshold = 2V.
>>> The register HI8435_SOCENHYS_REG describes the voltage hysteresis of the sensor and the hysteresis center.
>>> The 0x206 is an encoded value for 2V-to-4V voltage threshold.
>>>
>>> I will add comments.
>> good.
>>>>> +    hi8435_writew(priv, HI8435_SOCENHYS_REG, 0x206);
>>>>> +
>>>> I am as yet unconvinced that using triggers to poll events is a terribly
>>>> good idea.  Feels rather like we are doing this due to a deficiency in
>>>> the buffer interface than because sending events makes sense here.
>>> Actually the first version of this driver was against the buffer interface.
>>> I did try to use your tip and switched to event interface :)
>> oops.
>>> Do you think I should switch to buffer interface back?
>> Nope. As discussed above, right now this approach is winning (just ;)
>>>>> +    ret = iio_triggered_event_setup(idev, NULL, hi8435_trigger_handler);
>>>>> +    if (ret)
>>>>> +        return ret;
>>>>> +
>>>>> +    ret = iio_device_register(idev);
>>>>> +    if (ret < 0) {
>>>>> +        dev_err(&spi->dev, "unable to register device\n");
>>>>> +        goto unregister_triggered_event;
>>>>> +    }
>>>>> +
>>>>> +    return 0;
>>>>> +
>>>>> +unregister_triggered_event:
>>>>> +    iio_triggered_event_cleanup(idev);
>>>>> +    return ret;
>>>>> +}
>>>>> +
>>>>> +static int hi8435_remove(struct spi_device *spi)
>>>>> +{
>>>>> +    struct iio_dev *idev = spi_get_drvdata(spi);
>>>>> +    struct hi8435_priv *priv = iio_priv(idev);
>>>>> +
>>>>> +    cancel_delayed_work_sync(&priv->work);
>>>>> +    iio_device_unregister(idev);
>>>>> +    iio_triggered_event_cleanup(idev);
>>>>> +
>>>>> +    return 0;
>>>>> +}
>>>>> +
>>>>> +static const struct of_device_id hi8435_dt_ids[] = {
>>>>> +    { .compatible = "holt,hi8435" },
>>>>> +    {},
>>>>> +};
>>>>> +MODULE_DEVICE_TABLE(of, hi8435_dt_ids);
>>>>> +
>>>>> +static const struct spi_device_id hi8435_id[] = {
>>>>> +    { "hi8435", 0},
>>>>> +    { }
>>>>> +};
>>>>> +MODULE_DEVICE_TABLE(spi, hi8435_id);
>>>>> +
>>>>> +static struct spi_driver hi8435_driver = {
>>>>> +    .driver    = {
>>>>> +        .name        = DRV_NAME,
>>>>> +        .of_match_table    = of_match_ptr(hi8435_dt_ids),
>>>>> +    },
>>>>> +    .probe        = hi8435_probe,
>>>>> +    .remove        = hi8435_remove,
>>>>> +    .id_table    = hi8435_id,
>>>>> +};
>>>>> +module_spi_driver(hi8435_driver);
>>>>> +
>>>>> +MODULE_LICENSE("GPL");
>>>>> +MODULE_AUTHOR("Vladimir Barinov");
>>>>> +MODULE_DESCRIPTION("HI-8435 threshold detector");
>>>>>
>>> -- 
>>> To unsubscribe from this list: send the line "unsubscribe linux-iio" in
>>> the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
>>> More majordomo info at  http://vger.kernel.org/majordomo-info.html
> 
> 

^ permalink raw reply	[flat|nested] 40+ messages in thread

* Re: [PATCH v3 5/7] iio: Support triggered events
@ 2015-08-22 13:52         ` Jonathan Cameron
  0 siblings, 0 replies; 40+ messages in thread
From: Jonathan Cameron @ 2015-08-22 13:52 UTC (permalink / raw)
  To: Vladimir Barinov
  Cc: Hartmut Knaack, Lars-Peter Clausen, Peter Meerwald, Rob Herring,
	Pawel Moll, Mark Rutland, Ian Campbell, Kumar Gala, linux-kernel,
	linux-iio, devicetree, cory.tusar

On 16/08/15 21:15, Vladimir Barinov wrote:
> Hi Jonathan,
> 
> Thank you for review.
> Find some comments below.
> 
> Regards,
> Vladimir
> 
> On 16.08.2015 12:20, Jonathan Cameron wrote:
>> On 29/07/15 13:57, Vladimir Barinov wrote:
>>> Support triggered events.
>>>
>>> This is useful for chips that don't have their own interrupt sources.
>>> It allows to use generic/standalone iio triggers for those drivers.
>> As we have concluded this is a good thing to have, I've take a closer
>> look at it.
>>
>> I think we need to slightly separate the buffer and event uses of
>> triggers as it's entirely plausible both might be enabled at the same
>> time.  Don't think this should be hard however.
>>
>> I can concieve of weird restrictions from certain hardware (particularly
>> where hardware buffers are concerned) in which events are not monitored
>> when we are reading through the buffer but we can tackle that when we
>> actually need to.
>>
>> Anyhow, few bits inline.  Thanks for your persistence with this!
>>
>> Jonathan
>>
>>> Signed-off-by: Vladimir Barinov <vladimir.barinov@cogentembedded.com>
>>> ---
>>> Changes in version 2:
>>> - initially added
>>> Changes in version 3:
>>> - fixed grammar in patch description
>>> - changed license header to match "GNU Public License v2 or later"
>>>
>>>   drivers/iio/Kconfig                        |  6 +++
>>>   drivers/iio/Makefile                       |  1 +
>>>   drivers/iio/industrialio-core.c            |  4 +-
>>>   drivers/iio/industrialio-trigger.c         | 12 +++++-
>>>   drivers/iio/industrialio-triggered-event.c | 68 ++++++++++++++++++++++++++++++
>>>   include/linux/iio/iio.h                    |  1 +
>>>   include/linux/iio/triggered_event.h        | 11 +++++
>>>   7 files changed, 99 insertions(+), 4 deletions(-)
>>>   create mode 100644 drivers/iio/industrialio-triggered-event.c
>>>   create mode 100644 include/linux/iio/triggered_event.h
>>>
>>> diff --git a/drivers/iio/Kconfig b/drivers/iio/Kconfig
>>> index 4011eff..8fcc92f 100644
>>> --- a/drivers/iio/Kconfig
>>> +++ b/drivers/iio/Kconfig
>>> @@ -58,6 +58,12 @@ config IIO_CONSUMERS_PER_TRIGGER
>>>       This value controls the maximum number of consumers that a
>>>       given trigger may handle. Default is 2.
>>>   +config IIO_TRIGGERED_EVENT
>>> +    tristate
>>> +    select IIO_TRIGGER
>>> +    help
>>> +      Provides helper functions for setting up triggered events.
>>> +
>>>   source "drivers/iio/accel/Kconfig"
>>>   source "drivers/iio/adc/Kconfig"
>>>   source "drivers/iio/amplifiers/Kconfig"
>>> diff --git a/drivers/iio/Makefile b/drivers/iio/Makefile
>>> index 698afc2..40dc13e 100644
>>> --- a/drivers/iio/Makefile
>>> +++ b/drivers/iio/Makefile
>>> @@ -9,6 +9,7 @@ industrialio-$(CONFIG_IIO_TRIGGER) += industrialio-trigger.o
>>>   industrialio-$(CONFIG_IIO_BUFFER_CB) += buffer_cb.o
>>>     obj-$(CONFIG_IIO_TRIGGERED_BUFFER) += industrialio-triggered-buffer.o
>>> +obj-$(CONFIG_IIO_TRIGGERED_EVENT) += industrialio-triggered-event.o
>>>   obj-$(CONFIG_IIO_KFIFO_BUF) += kfifo_buf.o
>>>     obj-y += accel/
>>> diff --git a/drivers/iio/industrialio-core.c b/drivers/iio/industrialio-core.c
>>> index 3524b0d..54d71ea 100644
>>> --- a/drivers/iio/industrialio-core.c
>>> +++ b/drivers/iio/industrialio-core.c
>>> @@ -948,7 +948,7 @@ static void iio_device_unregister_sysfs(struct iio_dev *indio_dev)
>>>   static void iio_dev_release(struct device *device)
>>>   {
>>>       struct iio_dev *indio_dev = dev_to_iio_dev(device);
>>> -    if (indio_dev->modes & INDIO_BUFFER_TRIGGERED)
>>> +    if (indio_dev->modes & (INDIO_BUFFER_TRIGGERED | INDIO_EVENT_TRIGGERED))
>>>           iio_device_unregister_trigger_consumer(indio_dev);
>>>       iio_device_unregister_eventset(indio_dev);
>>>       iio_device_unregister_sysfs(indio_dev);
>>> @@ -1218,7 +1218,7 @@ int iio_device_register(struct iio_dev *indio_dev)
>>>               "Failed to register event set\n");
>>>           goto error_free_sysfs;
>>>       }
>>> -    if (indio_dev->modes & INDIO_BUFFER_TRIGGERED)
>>> +    if (indio_dev->modes & (INDIO_BUFFER_TRIGGERED | INDIO_EVENT_TRIGGERED))
>>>           iio_device_register_trigger_consumer(indio_dev);
>>>         if ((indio_dev->modes & INDIO_ALL_BUFFER_MODES) &&
>>> diff --git a/drivers/iio/industrialio-trigger.c b/drivers/iio/industrialio-trigger.c
>>> index d31098e..72b63e7 100644
>>> --- a/drivers/iio/industrialio-trigger.c
>>> +++ b/drivers/iio/industrialio-trigger.c
>>> @@ -345,10 +345,18 @@ static ssize_t iio_trigger_write_current(struct device *dev,
>>>         indio_dev->trig = trig;
>>>   -    if (oldtrig)
>>> +    if (oldtrig) {
>>> +        if (indio_dev->currentmode == INDIO_EVENT_TRIGGERED)
>>> +            iio_trigger_detach_poll_func(oldtrig,
>>> +                             indio_dev->pollfunc);
>>>           iio_trigger_put(oldtrig);
>>> -    if (indio_dev->trig)
>>> +    }
>>> +    if (indio_dev->trig) {
>>>           iio_trigger_get(indio_dev->trig);
>>> +        if (indio_dev->currentmode == INDIO_EVENT_TRIGGERED)
>>> +            iio_trigger_attach_poll_func(indio_dev->trig,
>>> +                             indio_dev->pollfunc);
>>> +    }
>>>         return len;
>>>   }
>>> diff --git a/drivers/iio/industrialio-triggered-event.c b/drivers/iio/industrialio-triggered-event.c
>>> new file mode 100644
>>> index 0000000..1e5ad33
>>> --- /dev/null
>>> +++ b/drivers/iio/industrialio-triggered-event.c
>>> @@ -0,0 +1,68 @@
>>> + /*
>>> + * Copyright (C) 2015 Cogent Embedded, Inc.
>>> + *
>>> + * 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.
>>> + */
>>> +
>>> +#include <linux/kernel.h>
>>> +#include <linux/export.h>
>>> +#include <linux/module.h>
>>> +#include <linux/iio/iio.h>
>>> +#include <linux/iio/triggered_event.h>
>>> +#include <linux/iio/trigger_consumer.h>
>>> +
>>> +/**
>>> + * iio_triggered_event_setup() - Setup pollfunc for triggered event
>>> + * @indio_dev:        IIO device structure
>>> + * @pollfunc_bh:    Function which will be used as pollfunc bottom half
>>> + * @pollfunc_th:    Function which will be used as pollfunc top half
>>> + *
>>> + * This function combines some common tasks which will normally be performed
>>> + * when setting up a triggered event. It will allocate the pollfunc and
>>> + * set mode to use it for triggered event.
>>> + *
>>> + * Before calling this function the indio_dev structure should already be
>>> + * completely initialized, but not yet registered. In practice this means that
>>> + * this function should be called right before iio_device_register().
>>> + *
>>> + * To free the resources allocated by this function call
>>> + * iio_triggered_event_cleanup().
>>> + */
>>> +int iio_triggered_event_setup(struct iio_dev *indio_dev,
>>> +    irqreturn_t (*pollfunc_bh)(int irq, void *p),
>>> +    irqreturn_t (*pollfunc_th)(int irq, void *p))
>>> +{
>>> +    indio_dev->pollfunc = iio_alloc_pollfunc(pollfunc_bh,
>>> +                         pollfunc_th,
>>> +                         IRQF_ONESHOT,
>>> +                         indio_dev,
>>> +                         "%s_consumer%d",
>>> +                         indio_dev->name,
>>> +                         indio_dev->id);
>>> +    if (indio_dev->pollfunc == NULL)
>>> +        return -ENOMEM;
>>> +
>>> +    /* Flag that pollfunc is used for triggered event */
>>> +    indio_dev->modes |= INDIO_EVENT_TRIGGERED;
>>> +    indio_dev->currentmode = INDIO_EVENT_TRIGGERED;
>> Can conceive of a corner case here.  What if both buffer
>> and event are being triggered?  No real problem with
>> running them off the same trigger, but right now enabling
>> one will take out the other.
> To avoid conflict it should be separate pollfunc
> (indio_dev->pollfunc_event = ...) since we do runtime pollfunc
> attach/detach for both buffer and event interfaces.
Agreed.
> 
>>
>> Might also want to not enable triggered event mode
>> until we enable some events?
> I think it will need to change most drivers that use event interface since
> events are enabled inside them via write_event_config() callback or
> events may have already been enabled during driver probe.
Pretty much no devices come up with events enabled (might be a really
stupid one or two out there where there is no control over it other
than masking the interrupt).

Hmm.  True enough, the core has little knowledge of whether events are
enabled or not.  I guess attaching the poll function when it doesn't
do anything (no events enabled) is pretty low cost anyway so lets
leave it as it is.
> 
>>
>> Perhaps we need separate indio_dev->event_mode and
> event_modes ?
> This probably not necessary since iio stack checks for flags set in
> indio_dev->modes value.
>> indio_dev->current_event_mode fields to make the two
>> configurable independently?
> I think it works, but with separate indio_dev->pollfunc_event.
> 
> Should it be inidio_dev->current_event_mode or inidio_dev->currentmode_event for
> consistency?
Which ever looks best to you.
> 
> I will verify your idea with using one trigger for both event and buffer interface on hi8435 driver
> by adding buffer interface that I will try internally (will not send).
>
cool
>>
>> I get the feeling this particular feature is going to evolve
>> somewhat as we get a feel for how it is being used.
>>
>>> +
>>> +    return 0;
>>> +}
>>> +EXPORT_SYMBOL(iio_triggered_event_setup);
>>> +
>>> +/**
>>> + * iio_triggered_event_cleanup() - Free resources allocated by iio_triggered_event_setup()
>>> + * @indio_dev: IIO device structure
>>> + */
>>> +void iio_triggered_event_cleanup(struct iio_dev *indio_dev)
>>> +{
>>> +    iio_dealloc_pollfunc(indio_dev->pollfunc);
>>> +}
>>> +EXPORT_SYMBOL(iio_triggered_event_cleanup);
>>> +
>>> +MODULE_AUTHOR("Vladimir Barinov");
>>> +MODULE_DESCRIPTION("IIO helper functions for setting up triggered events");
>>> +MODULE_LICENSE("GPL");
>>> diff --git a/include/linux/iio/iio.h b/include/linux/iio/iio.h
>>> index f791482..b691ee0 100644
>>> --- a/include/linux/iio/iio.h
>>> +++ b/include/linux/iio/iio.h
>>> @@ -294,6 +294,7 @@ static inline s64 iio_get_time_ns(void)
>>>   #define INDIO_BUFFER_TRIGGERED        0x02
>>>   #define INDIO_BUFFER_SOFTWARE        0x04
>>>   #define INDIO_BUFFER_HARDWARE        0x08
>>> +#define INDIO_EVENT_TRIGGERED        0x10
>>>     #define INDIO_ALL_BUFFER_MODES                    \
>>>       (INDIO_BUFFER_TRIGGERED | INDIO_BUFFER_HARDWARE | INDIO_BUFFER_SOFTWARE)
>>> diff --git a/include/linux/iio/triggered_event.h b/include/linux/iio/triggered_event.h
>>> new file mode 100644
>>> index 0000000..e9894e9
>>> --- /dev/null
>>> +++ b/include/linux/iio/triggered_event.h
>>> @@ -0,0 +1,11 @@
>>> +#ifndef _LINUX_IIO_TRIGGERED_EVENT_H_
>>> +#define _LINUX_IIO_TRIGGERED_EVENT_H_
>>> +
>>> +#include <linux/interrupt.h>
>>> +
>>> +int iio_triggered_event_setup(struct iio_dev *indio_dev,
>>> +    irqreturn_t (*pollfunc_bh)(int irq, void *p),
>>> +    irqreturn_t (*pollfunc_th)(int irq, void *p));
>> We renamed the parameters of the equivalent buffer function recently
>> to avoid some confusion.  Could you follow that for this one as well.
> Ok
> 
>>
>>> +void iio_triggered_event_cleanup(struct iio_dev *indio_dev);
>>> +
>>> +#endif
>>>
> 
> -- 
> To unsubscribe from this list: send the line "unsubscribe linux-iio" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html


^ permalink raw reply	[flat|nested] 40+ messages in thread

* Re: [PATCH v3 5/7] iio: Support triggered events
@ 2015-08-22 13:52         ` Jonathan Cameron
  0 siblings, 0 replies; 40+ messages in thread
From: Jonathan Cameron @ 2015-08-22 13:52 UTC (permalink / raw)
  To: Vladimir Barinov
  Cc: Hartmut Knaack, Lars-Peter Clausen, Peter Meerwald, Rob Herring,
	Pawel Moll, Mark Rutland, Ian Campbell, Kumar Gala,
	linux-kernel-u79uwXL29TY76Z2rM5mHXA,
	linux-iio-u79uwXL29TY76Z2rM5mHXA,
	devicetree-u79uwXL29TY76Z2rM5mHXA,
	cory.tusar-J6Z/VSE8EyIAspv4Qr0y0gC/G2K4zDHf

On 16/08/15 21:15, Vladimir Barinov wrote:
> Hi Jonathan,
> 
> Thank you for review.
> Find some comments below.
> 
> Regards,
> Vladimir
> 
> On 16.08.2015 12:20, Jonathan Cameron wrote:
>> On 29/07/15 13:57, Vladimir Barinov wrote:
>>> Support triggered events.
>>>
>>> This is useful for chips that don't have their own interrupt sources.
>>> It allows to use generic/standalone iio triggers for those drivers.
>> As we have concluded this is a good thing to have, I've take a closer
>> look at it.
>>
>> I think we need to slightly separate the buffer and event uses of
>> triggers as it's entirely plausible both might be enabled at the same
>> time.  Don't think this should be hard however.
>>
>> I can concieve of weird restrictions from certain hardware (particularly
>> where hardware buffers are concerned) in which events are not monitored
>> when we are reading through the buffer but we can tackle that when we
>> actually need to.
>>
>> Anyhow, few bits inline.  Thanks for your persistence with this!
>>
>> Jonathan
>>
>>> Signed-off-by: Vladimir Barinov <vladimir.barinov-M4DtvfQ/ZS1MRgGoP+s0PdBPR1lH4CV8@public.gmane.org>
>>> ---
>>> Changes in version 2:
>>> - initially added
>>> Changes in version 3:
>>> - fixed grammar in patch description
>>> - changed license header to match "GNU Public License v2 or later"
>>>
>>>   drivers/iio/Kconfig                        |  6 +++
>>>   drivers/iio/Makefile                       |  1 +
>>>   drivers/iio/industrialio-core.c            |  4 +-
>>>   drivers/iio/industrialio-trigger.c         | 12 +++++-
>>>   drivers/iio/industrialio-triggered-event.c | 68 ++++++++++++++++++++++++++++++
>>>   include/linux/iio/iio.h                    |  1 +
>>>   include/linux/iio/triggered_event.h        | 11 +++++
>>>   7 files changed, 99 insertions(+), 4 deletions(-)
>>>   create mode 100644 drivers/iio/industrialio-triggered-event.c
>>>   create mode 100644 include/linux/iio/triggered_event.h
>>>
>>> diff --git a/drivers/iio/Kconfig b/drivers/iio/Kconfig
>>> index 4011eff..8fcc92f 100644
>>> --- a/drivers/iio/Kconfig
>>> +++ b/drivers/iio/Kconfig
>>> @@ -58,6 +58,12 @@ config IIO_CONSUMERS_PER_TRIGGER
>>>       This value controls the maximum number of consumers that a
>>>       given trigger may handle. Default is 2.
>>>   +config IIO_TRIGGERED_EVENT
>>> +    tristate
>>> +    select IIO_TRIGGER
>>> +    help
>>> +      Provides helper functions for setting up triggered events.
>>> +
>>>   source "drivers/iio/accel/Kconfig"
>>>   source "drivers/iio/adc/Kconfig"
>>>   source "drivers/iio/amplifiers/Kconfig"
>>> diff --git a/drivers/iio/Makefile b/drivers/iio/Makefile
>>> index 698afc2..40dc13e 100644
>>> --- a/drivers/iio/Makefile
>>> +++ b/drivers/iio/Makefile
>>> @@ -9,6 +9,7 @@ industrialio-$(CONFIG_IIO_TRIGGER) += industrialio-trigger.o
>>>   industrialio-$(CONFIG_IIO_BUFFER_CB) += buffer_cb.o
>>>     obj-$(CONFIG_IIO_TRIGGERED_BUFFER) += industrialio-triggered-buffer.o
>>> +obj-$(CONFIG_IIO_TRIGGERED_EVENT) += industrialio-triggered-event.o
>>>   obj-$(CONFIG_IIO_KFIFO_BUF) += kfifo_buf.o
>>>     obj-y += accel/
>>> diff --git a/drivers/iio/industrialio-core.c b/drivers/iio/industrialio-core.c
>>> index 3524b0d..54d71ea 100644
>>> --- a/drivers/iio/industrialio-core.c
>>> +++ b/drivers/iio/industrialio-core.c
>>> @@ -948,7 +948,7 @@ static void iio_device_unregister_sysfs(struct iio_dev *indio_dev)
>>>   static void iio_dev_release(struct device *device)
>>>   {
>>>       struct iio_dev *indio_dev = dev_to_iio_dev(device);
>>> -    if (indio_dev->modes & INDIO_BUFFER_TRIGGERED)
>>> +    if (indio_dev->modes & (INDIO_BUFFER_TRIGGERED | INDIO_EVENT_TRIGGERED))
>>>           iio_device_unregister_trigger_consumer(indio_dev);
>>>       iio_device_unregister_eventset(indio_dev);
>>>       iio_device_unregister_sysfs(indio_dev);
>>> @@ -1218,7 +1218,7 @@ int iio_device_register(struct iio_dev *indio_dev)
>>>               "Failed to register event set\n");
>>>           goto error_free_sysfs;
>>>       }
>>> -    if (indio_dev->modes & INDIO_BUFFER_TRIGGERED)
>>> +    if (indio_dev->modes & (INDIO_BUFFER_TRIGGERED | INDIO_EVENT_TRIGGERED))
>>>           iio_device_register_trigger_consumer(indio_dev);
>>>         if ((indio_dev->modes & INDIO_ALL_BUFFER_MODES) &&
>>> diff --git a/drivers/iio/industrialio-trigger.c b/drivers/iio/industrialio-trigger.c
>>> index d31098e..72b63e7 100644
>>> --- a/drivers/iio/industrialio-trigger.c
>>> +++ b/drivers/iio/industrialio-trigger.c
>>> @@ -345,10 +345,18 @@ static ssize_t iio_trigger_write_current(struct device *dev,
>>>         indio_dev->trig = trig;
>>>   -    if (oldtrig)
>>> +    if (oldtrig) {
>>> +        if (indio_dev->currentmode == INDIO_EVENT_TRIGGERED)
>>> +            iio_trigger_detach_poll_func(oldtrig,
>>> +                             indio_dev->pollfunc);
>>>           iio_trigger_put(oldtrig);
>>> -    if (indio_dev->trig)
>>> +    }
>>> +    if (indio_dev->trig) {
>>>           iio_trigger_get(indio_dev->trig);
>>> +        if (indio_dev->currentmode == INDIO_EVENT_TRIGGERED)
>>> +            iio_trigger_attach_poll_func(indio_dev->trig,
>>> +                             indio_dev->pollfunc);
>>> +    }
>>>         return len;
>>>   }
>>> diff --git a/drivers/iio/industrialio-triggered-event.c b/drivers/iio/industrialio-triggered-event.c
>>> new file mode 100644
>>> index 0000000..1e5ad33
>>> --- /dev/null
>>> +++ b/drivers/iio/industrialio-triggered-event.c
>>> @@ -0,0 +1,68 @@
>>> + /*
>>> + * Copyright (C) 2015 Cogent Embedded, Inc.
>>> + *
>>> + * 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.
>>> + */
>>> +
>>> +#include <linux/kernel.h>
>>> +#include <linux/export.h>
>>> +#include <linux/module.h>
>>> +#include <linux/iio/iio.h>
>>> +#include <linux/iio/triggered_event.h>
>>> +#include <linux/iio/trigger_consumer.h>
>>> +
>>> +/**
>>> + * iio_triggered_event_setup() - Setup pollfunc for triggered event
>>> + * @indio_dev:        IIO device structure
>>> + * @pollfunc_bh:    Function which will be used as pollfunc bottom half
>>> + * @pollfunc_th:    Function which will be used as pollfunc top half
>>> + *
>>> + * This function combines some common tasks which will normally be performed
>>> + * when setting up a triggered event. It will allocate the pollfunc and
>>> + * set mode to use it for triggered event.
>>> + *
>>> + * Before calling this function the indio_dev structure should already be
>>> + * completely initialized, but not yet registered. In practice this means that
>>> + * this function should be called right before iio_device_register().
>>> + *
>>> + * To free the resources allocated by this function call
>>> + * iio_triggered_event_cleanup().
>>> + */
>>> +int iio_triggered_event_setup(struct iio_dev *indio_dev,
>>> +    irqreturn_t (*pollfunc_bh)(int irq, void *p),
>>> +    irqreturn_t (*pollfunc_th)(int irq, void *p))
>>> +{
>>> +    indio_dev->pollfunc = iio_alloc_pollfunc(pollfunc_bh,
>>> +                         pollfunc_th,
>>> +                         IRQF_ONESHOT,
>>> +                         indio_dev,
>>> +                         "%s_consumer%d",
>>> +                         indio_dev->name,
>>> +                         indio_dev->id);
>>> +    if (indio_dev->pollfunc == NULL)
>>> +        return -ENOMEM;
>>> +
>>> +    /* Flag that pollfunc is used for triggered event */
>>> +    indio_dev->modes |= INDIO_EVENT_TRIGGERED;
>>> +    indio_dev->currentmode = INDIO_EVENT_TRIGGERED;
>> Can conceive of a corner case here.  What if both buffer
>> and event are being triggered?  No real problem with
>> running them off the same trigger, but right now enabling
>> one will take out the other.
> To avoid conflict it should be separate pollfunc
> (indio_dev->pollfunc_event = ...) since we do runtime pollfunc
> attach/detach for both buffer and event interfaces.
Agreed.
> 
>>
>> Might also want to not enable triggered event mode
>> until we enable some events?
> I think it will need to change most drivers that use event interface since
> events are enabled inside them via write_event_config() callback or
> events may have already been enabled during driver probe.
Pretty much no devices come up with events enabled (might be a really
stupid one or two out there where there is no control over it other
than masking the interrupt).

Hmm.  True enough, the core has little knowledge of whether events are
enabled or not.  I guess attaching the poll function when it doesn't
do anything (no events enabled) is pretty low cost anyway so lets
leave it as it is.
> 
>>
>> Perhaps we need separate indio_dev->event_mode and
> event_modes ?
> This probably not necessary since iio stack checks for flags set in
> indio_dev->modes value.
>> indio_dev->current_event_mode fields to make the two
>> configurable independently?
> I think it works, but with separate indio_dev->pollfunc_event.
> 
> Should it be inidio_dev->current_event_mode or inidio_dev->currentmode_event for
> consistency?
Which ever looks best to you.
> 
> I will verify your idea with using one trigger for both event and buffer interface on hi8435 driver
> by adding buffer interface that I will try internally (will not send).
>
cool
>>
>> I get the feeling this particular feature is going to evolve
>> somewhat as we get a feel for how it is being used.
>>
>>> +
>>> +    return 0;
>>> +}
>>> +EXPORT_SYMBOL(iio_triggered_event_setup);
>>> +
>>> +/**
>>> + * iio_triggered_event_cleanup() - Free resources allocated by iio_triggered_event_setup()
>>> + * @indio_dev: IIO device structure
>>> + */
>>> +void iio_triggered_event_cleanup(struct iio_dev *indio_dev)
>>> +{
>>> +    iio_dealloc_pollfunc(indio_dev->pollfunc);
>>> +}
>>> +EXPORT_SYMBOL(iio_triggered_event_cleanup);
>>> +
>>> +MODULE_AUTHOR("Vladimir Barinov");
>>> +MODULE_DESCRIPTION("IIO helper functions for setting up triggered events");
>>> +MODULE_LICENSE("GPL");
>>> diff --git a/include/linux/iio/iio.h b/include/linux/iio/iio.h
>>> index f791482..b691ee0 100644
>>> --- a/include/linux/iio/iio.h
>>> +++ b/include/linux/iio/iio.h
>>> @@ -294,6 +294,7 @@ static inline s64 iio_get_time_ns(void)
>>>   #define INDIO_BUFFER_TRIGGERED        0x02
>>>   #define INDIO_BUFFER_SOFTWARE        0x04
>>>   #define INDIO_BUFFER_HARDWARE        0x08
>>> +#define INDIO_EVENT_TRIGGERED        0x10
>>>     #define INDIO_ALL_BUFFER_MODES                    \
>>>       (INDIO_BUFFER_TRIGGERED | INDIO_BUFFER_HARDWARE | INDIO_BUFFER_SOFTWARE)
>>> diff --git a/include/linux/iio/triggered_event.h b/include/linux/iio/triggered_event.h
>>> new file mode 100644
>>> index 0000000..e9894e9
>>> --- /dev/null
>>> +++ b/include/linux/iio/triggered_event.h
>>> @@ -0,0 +1,11 @@
>>> +#ifndef _LINUX_IIO_TRIGGERED_EVENT_H_
>>> +#define _LINUX_IIO_TRIGGERED_EVENT_H_
>>> +
>>> +#include <linux/interrupt.h>
>>> +
>>> +int iio_triggered_event_setup(struct iio_dev *indio_dev,
>>> +    irqreturn_t (*pollfunc_bh)(int irq, void *p),
>>> +    irqreturn_t (*pollfunc_th)(int irq, void *p));
>> We renamed the parameters of the equivalent buffer function recently
>> to avoid some confusion.  Could you follow that for this one as well.
> Ok
> 
>>
>>> +void iio_triggered_event_cleanup(struct iio_dev *indio_dev);
>>> +
>>> +#endif
>>>
> 
> -- 
> To unsubscribe from this list: send the line "unsubscribe linux-iio" in
> the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply	[flat|nested] 40+ messages in thread

end of thread, other threads:[~2015-08-22 13:52 UTC | newest]

Thread overview: 40+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2015-07-29 12:54 [PATCH v3 0/7] iio: adc: hi8435: Add Holt HI-8435 threshold detector Vladimir Barinov
2015-07-29 12:54 ` Vladimir Barinov
2015-07-29 12:56 ` [PATCH v3 1/7] iio: adc: hi8435: " Vladimir Barinov
2015-07-29 12:56   ` Vladimir Barinov
2015-08-08 17:56   ` Jonathan Cameron
2015-08-08 17:56     ` Jonathan Cameron
2015-08-11 12:21     ` Lars-Peter Clausen
2015-08-11 12:21       ` Lars-Peter Clausen
2015-08-11 17:01       ` Jonathan Cameron
2015-08-11 14:37     ` Vladimir Barinov
2015-08-16  9:00       ` Jonathan Cameron
2015-08-16  9:00         ` Jonathan Cameron
2015-08-16 18:54         ` Vladimir Barinov
2015-08-16 18:54           ` Vladimir Barinov
2015-08-22 13:48           ` Jonathan Cameron
2015-08-22 13:48             ` Jonathan Cameron
2015-07-29 12:57 ` [PATCH v3 2/7] dt: Add vendor prefix 'holt' Vladimir Barinov
2015-07-29 12:57 ` [PATCH v3 3/7] dt: Document Holt HI-8435 bindings Vladimir Barinov
2015-07-29 12:57 ` [PATCH v3 4/7] iio: trigger: Add periodic polling to SYSFS trigger Vladimir Barinov
2015-08-07 13:42   ` Lars-Peter Clausen
2015-08-07 13:42     ` Lars-Peter Clausen
2015-08-08 17:26     ` Jonathan Cameron
2015-07-29 12:57 ` [PATCH v3 5/7] iio: Support triggered events Vladimir Barinov
2015-08-07 13:45   ` Lars-Peter Clausen
2015-08-07 13:45     ` Lars-Peter Clausen
2015-08-07 16:10     ` Vladimir Barinov
2015-08-16  9:05       ` Jonathan Cameron
2015-08-16  9:05         ` Jonathan Cameron
2015-08-16  9:20   ` Jonathan Cameron
2015-08-16  9:20     ` Jonathan Cameron
2015-08-16 20:15     ` Vladimir Barinov
2015-08-16 20:15       ` Vladimir Barinov
2015-08-22 13:52       ` Jonathan Cameron
2015-08-22 13:52         ` Jonathan Cameron
2015-07-29 12:57 ` [PATCH v3 6/7] iio: Add ABI documentation for debounce_time Vladimir Barinov
2015-07-29 12:57 ` [PATCH v3 7/7] iio: Fix typos in ABI documentation Vladimir Barinov
2015-08-08 18:39   ` Jonathan Cameron
2015-08-08 18:39     ` Jonathan Cameron
2015-08-08 17:28 ` [PATCH v3 0/7] iio: adc: hi8435: Add Holt HI-8435 threshold detector Jonathan Cameron
2015-08-08 17:28   ` Jonathan Cameron

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.