All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH v3 0/6] iio: Introduce the generic counter interface
@ 2017-10-05 18:13 William Breathitt Gray
  2017-10-05 18:13 ` [PATCH v3 1/6] iio: Implement counter channel specification and IIO_SIGNAL constant William Breathitt Gray
                   ` (6 more replies)
  0 siblings, 7 replies; 17+ messages in thread
From: William Breathitt Gray @ 2017-10-05 18:13 UTC (permalink / raw)
  To: jic23, benjamin.gaignard, knaack.h, lars, pmeerw
  Cc: linux-iio, linux-kernel, William Breathitt Gray

There have been some significant implementation changes for this version
of the patchset. Here's a brief summary of the updates:

 - Inline comments throughout the industrial-counter.c file; this
   should help clarify some of the code and aid in the review of the
   system architecture

 - Simplification of the driver API; primarily, dynamic updates to the
   Counter Values, Triggers, and Signals lists are no longer allowed --
   all relationships must be defined before the Counter is registered to
   the system

 - Dynamic component registration functions
   (iio_counter_value_register, et al.) have been removed, and now the
   signals, values, and triggers array members serve to register the
   components; signals, values, and triggers were previously named
   init_signals, init_values, and init_triggers respectively -- the
   "init_" prefix was removed now that dynamic registration is no longer
   allowed

 - Requiring static component relationships means linked lists are no
   longer necessary so all the list related code has been removed;
   Signals, Triggers, and Values are now accessed directly from the
   arrays supplied in the iio_counter structure

 - No dynamic additions/substraction of components eliminates the need
   for mutex locks, so those have been removed as well

 - Previously, the entire iio_counter structure was copied and stored as
   the iio_dev private data; now, only a pointer to the supplied
   iio_counter structure is stored

 - devm_iio_counter_register and devm_iio_counter_unregister functions
   were incorporated from Benjamin Gaignard's suggestions

 - Dummy counter driver provided as a reference for a simple counter
   implementation utilizing the Generic Counter interface

In a previous discussion, Benjamin noted some conflicts between the
Generic Counter and the underlying IIO core code: mapping does not
always work out seemlessly and sometimes the IIO core functionality
exposure provided via iio_counter is not quite flexible enough. I intend
to address these shortcomings in the next version of this patchset
hopefully.

In particular, I'm leaning towards separating the Generic Counter
interface from the IIO core dependency entirely. Since the Generic
Counter inteface focuses on a more abstract representation of a counter
device, I don't think it provides a suitable interface to map onto IIO
core, which appears to focus more on a representation of the physical
hardware itself.

Separating Generic Counter from IIO core should allow a driver to use
the IIO core functions (iio_device_register, et al.) to represent the
physical nature of their hardware (voltages, currents, etc.), while also
utilize the Generic Counter interface to represent the abstract
relationships between those Signals and their ultimate counter Values.

In particular, I'm hoping for the Generic Counter system implementation
itself to not require all this hoop-jumping (mapping to IIO core
functions, jumping back to the parent iio_counter, handling non-matching
parameter lists between iio_counter and iio_dev, etc.); that should make
the code simpler to debug, more efficient, and more stable in the
long-run. I will consider these advantages and disadvantages before
committing to a separation however.

Regarding this version of the patchset in particular, I decided to
remove the dynamic component registration functionality since I doubt
that many devices would require such; almost all, if not all, hardware
counters I encountered have static relationships between input lines and
the count value (i.e. specific input lines correlate to specific count
registers).

The requirement that Counter component relationships are
static has opened the possibility of some optimizations in the Generic
Counter interface code:

 - Signals, Triggers, and Values are provided as arrays; these arrays
   could benefit from some amount of sorting based on component IDs

 - Searching for a component can be more efficient in a sorted array;
   currently, the code just walks down the array elements and compare
   the IDs

 - Similar to general searching, the arrays are initially verified to
   gurantee unique IDs; currently, the code walks down the array
   elements and compares the current element ID with all the elements
   before and after -- this could be made more efficient with some
   sorting

There are likely other areas of improvement, so let me know and I'll see
what I can do.

William Breathitt Gray (6):
  iio: Implement counter channel specification and IIO_SIGNAL constant
  iio: Introduce the generic counter interface
  iio: Documentation: Add IIO Generic Counter sysfs documentation
  docs: Add IIO Generic Counter Interface documentation
  iio: Add dummy counter driver
  iio: 104-quad-8: Add IIO generic counter interface support

 .../testing/sysfs-bus-iio-generic-counter-sysfs    |  63 ++
 Documentation/driver-api/iio/generic-counter.txt   | 526 ++++++++++++
 MAINTAINERS                                        |   7 +
 drivers/iio/Kconfig                                |   8 +
 drivers/iio/Makefile                               |   1 +
 drivers/iio/counter/104-quad-8.c                   | 294 ++++++-
 drivers/iio/counter/Kconfig                        |  16 +
 drivers/iio/counter/Makefile                       |   1 +
 drivers/iio/counter/dummy-counter.c                | 293 +++++++
 drivers/iio/industrialio-core.c                    |  14 +-
 drivers/iio/industrialio-counter.c                 | 900 +++++++++++++++++++++
 include/linux/iio/counter.h                        | 166 ++++
 include/linux/iio/iio.h                            |   2 +
 include/uapi/linux/iio/types.h                     |   1 +
 14 files changed, 2274 insertions(+), 18 deletions(-)
 create mode 100644 Documentation/ABI/testing/sysfs-bus-iio-generic-counter-sysfs
 create mode 100644 Documentation/driver-api/iio/generic-counter.txt
 create mode 100644 drivers/iio/counter/dummy-counter.c
 create mode 100644 drivers/iio/industrialio-counter.c
 create mode 100644 include/linux/iio/counter.h

-- 
2.14.1

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

* [PATCH v3 1/6] iio: Implement counter channel specification and IIO_SIGNAL constant
  2017-10-05 18:13 [PATCH v3 0/6] iio: Introduce the generic counter interface William Breathitt Gray
@ 2017-10-05 18:13 ` William Breathitt Gray
  2017-10-08 11:57   ` Jonathan Cameron
  2017-10-05 18:13 ` [PATCH v3 2/6] iio: Introduce the generic counter interface William Breathitt Gray
                   ` (5 subsequent siblings)
  6 siblings, 1 reply; 17+ messages in thread
From: William Breathitt Gray @ 2017-10-05 18:13 UTC (permalink / raw)
  To: jic23, benjamin.gaignard, knaack.h, lars, pmeerw
  Cc: linux-iio, linux-kernel, William Breathitt Gray

Counters are IIO devices that are exposed via a generic counter
interface consisting of one or more counter signals (IIO_SIGNAL) linked
to one or more counter values (IIO_COUNT).

This patch introduces the IIO_SIGNAL constant which represents a counter
device signal line. Additionally, a new "counter" member is added to
struct iio_chan_spec, with relevant support, to indicate that a channel
is part of a counter.

Signed-off-by: William Breathitt Gray <vilhelm.gray@gmail.com>
---
 drivers/iio/industrialio-core.c | 14 +++++++++++++-
 include/linux/iio/iio.h         |  2 ++
 include/uapi/linux/iio/types.h  |  1 +
 3 files changed, 16 insertions(+), 1 deletion(-)

diff --git a/drivers/iio/industrialio-core.c b/drivers/iio/industrialio-core.c
index 7a5aa127c52e..ee508f2070a7 100644
--- a/drivers/iio/industrialio-core.c
+++ b/drivers/iio/industrialio-core.c
@@ -85,6 +85,7 @@ static const char * const iio_chan_type_name_spec[] = {
 	[IIO_COUNT] = "count",
 	[IIO_INDEX] = "index",
 	[IIO_GRAVITY]  = "gravity",
+	[IIO_SIGNAL] = "signal",
 };
 
 static const char * const iio_modifier_names[] = {
@@ -989,7 +990,18 @@ int __iio_device_attr_init(struct device_attribute *dev_attr,
 			break;
 
 		case IIO_SEPARATE:
-			if (chan->indexed)
+			if (chan->counter) {
+				if (!chan->indexed) {
+					WARN(1, "Counter channels must be indexed\n");
+					ret = -EINVAL;
+					goto error_free_full_postfix;
+				}
+				name = kasprintf(GFP_KERNEL, "%s%d-%d_%s",
+						    iio_chan_type_name_spec[chan->type],
+						    chan->channel,
+						    chan->channel2,
+						    full_postfix);
+			} else if (chan->indexed)
 				name = kasprintf(GFP_KERNEL, "%s_%s%d_%s",
 						    iio_direction[chan->output],
 						    iio_chan_type_name_spec[chan->type],
diff --git a/include/linux/iio/iio.h b/include/linux/iio/iio.h
index 50cae8504256..9f949dd74b60 100644
--- a/include/linux/iio/iio.h
+++ b/include/linux/iio/iio.h
@@ -263,6 +263,7 @@ struct iio_event_spec {
  *			attributes but not for event codes.
  * @output:		Channel is output.
  * @differential:	Channel is differential.
+ * @counter:		Channel is part of a counter.
  */
 struct iio_chan_spec {
 	enum iio_chan_type	type;
@@ -295,6 +296,7 @@ struct iio_chan_spec {
 	unsigned		indexed:1;
 	unsigned		output:1;
 	unsigned		differential:1;
+	unsigned		counter:1;
 };
 
 
diff --git a/include/uapi/linux/iio/types.h b/include/uapi/linux/iio/types.h
index ffafd6c25a48..313899652ca7 100644
--- a/include/uapi/linux/iio/types.h
+++ b/include/uapi/linux/iio/types.h
@@ -43,6 +43,7 @@ enum iio_chan_type {
 	IIO_COUNT,
 	IIO_INDEX,
 	IIO_GRAVITY,
+	IIO_SIGNAL,
 };
 
 enum iio_modifier {
-- 
2.14.1

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

* [PATCH v3 2/6] iio: Introduce the generic counter interface
  2017-10-05 18:13 [PATCH v3 0/6] iio: Introduce the generic counter interface William Breathitt Gray
  2017-10-05 18:13 ` [PATCH v3 1/6] iio: Implement counter channel specification and IIO_SIGNAL constant William Breathitt Gray
@ 2017-10-05 18:13 ` William Breathitt Gray
  2017-10-08 14:30   ` Jonathan Cameron
  2017-10-05 18:13 ` [PATCH v3 3/6] iio: Documentation: Add IIO Generic Counter sysfs documentation William Breathitt Gray
                   ` (4 subsequent siblings)
  6 siblings, 1 reply; 17+ messages in thread
From: William Breathitt Gray @ 2017-10-05 18:13 UTC (permalink / raw)
  To: jic23, benjamin.gaignard, knaack.h, lars, pmeerw
  Cc: linux-iio, linux-kernel, William Breathitt Gray

This patch introduces the IIO generic counter interface for supporting
counter devices. The generic counter interface serves as a catch-all to
enable rudimentary support for devices that qualify as counters. More
specific and apt counter interfaces may be developed on top of the
generic counter interface, and those interfaces should be used by
drivers when possible rather than the generic counter interface.

In the context of the IIO generic counter interface, a counter is
defined as a device that reports one or more "counter values" based on
the state changes of one or more "counter signals" as evaluated by a
defined "counter function."

The IIO generic counter interface piggybacks off of the IIO core. This
is primarily used to leverage the existing sysfs setup: the IIO_COUNT
channel attributes represent the Counter Value, while the IIO_SIGNAL
channel attributes represent the Counter Signal; auxilary IIO_COUNT
attributes represent the Counter Signal connections (Triggers) and their
respective state change configurations which trigger an associated
"counter function" evaluation.

The iio_counter_ops structure serves as a container for driver callbacks
to communicate with the device; function callbacks are provided to read
and write various Signals and Values, and set and get the "trigger mode"
and "function mode" for various Triggers and Values respectively.

To support a counter device, a driver must first allocate the available
Counter Signals via iio_counter_signal structures. These Signals should
be stored as an array and set to the signals array member of an
allocated iio_counter structure before the Counter is registered to the
system.

Counter Values may be allocated via iio_counter_value structures, and
respective Counter Signal associations (Triggers) made via
iio_counter_trigger structures. Associated iio_counter_trigger
structures are stored as an array and set to the the triggers array
member of the respective iio_counter_value structure. These
iio_counter_value structures are set to the values array member of an
allocated iio_counter structure before the Counter is registered to the
system.

A counter device is registered to the system by passing the respective
initialized iio_counter structure to the iio_counter_register function;
similarly, the iio_counter_unregister function unregisters the
respective counter. The devm_iio_counter_register and
iio_devm_iio_counter_unregister functions serve as device memory-managed
versions of the iio_counter_register and iio_counter unregister
functions respectively.

Signed-off-by: William Breathitt Gray <vilhelm.gray@gmail.com>
---
 MAINTAINERS                        |   7 +
 drivers/iio/Kconfig                |   8 +
 drivers/iio/Makefile               |   1 +
 drivers/iio/counter/Kconfig        |   1 +
 drivers/iio/industrialio-counter.c | 900 +++++++++++++++++++++++++++++++++++++
 include/linux/iio/counter.h        | 166 +++++++
 6 files changed, 1083 insertions(+)
 create mode 100644 drivers/iio/industrialio-counter.c
 create mode 100644 include/linux/iio/counter.h

diff --git a/MAINTAINERS b/MAINTAINERS
index 2281af4b41b6..8b7c37bed252 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -6693,6 +6693,13 @@ F:	Documentation/ABI/testing/sysfs-bus-iio-adc-envelope-detector
 F:	Documentation/devicetree/bindings/iio/adc/envelope-detector.txt
 F:	drivers/iio/adc/envelope-detector.c
 
+IIO GENERIC COUNTER INTERFACE
+M:	William Breathitt Gray <vilhelm.gray@gmail.com>
+L:	linux-iio@vger.kernel.org
+S:	Maintained
+F:	drivers/iio/industrialio-counter.c
+F:	include/linux/iio/counter.h
+
 IIO MULTIPLEXER
 M:	Peter Rosin <peda@axentia.se>
 L:	linux-iio@vger.kernel.org
diff --git a/drivers/iio/Kconfig b/drivers/iio/Kconfig
index b3c8c6ef0dff..78e01f4f5937 100644
--- a/drivers/iio/Kconfig
+++ b/drivers/iio/Kconfig
@@ -30,6 +30,14 @@ config IIO_CONFIGFS
 	  (e.g. software triggers). For more info see
 	  Documentation/iio/iio_configfs.txt.
 
+config IIO_COUNTER
+	bool "Enable IIO counter support"
+	help
+	  Provides IIO core support for counters. This API provides
+	  a generic interface that serves as the building blocks to
+	  create more complex counter interfaces. Rudimentary support
+	  for counters is enabled.
+
 config IIO_TRIGGER
 	bool "Enable triggered sampling support"
 	help
diff --git a/drivers/iio/Makefile b/drivers/iio/Makefile
index 93c769cd99bf..6427ff38f964 100644
--- a/drivers/iio/Makefile
+++ b/drivers/iio/Makefile
@@ -5,6 +5,7 @@
 obj-$(CONFIG_IIO) += industrialio.o
 industrialio-y := industrialio-core.o industrialio-event.o inkern.o
 industrialio-$(CONFIG_IIO_BUFFER) += industrialio-buffer.o
+industrialio-$(CONFIG_IIO_COUNTER) += industrialio-counter.o
 industrialio-$(CONFIG_IIO_TRIGGER) += industrialio-trigger.o
 
 obj-$(CONFIG_IIO_CONFIGFS) += industrialio-configfs.o
diff --git a/drivers/iio/counter/Kconfig b/drivers/iio/counter/Kconfig
index 474e1ac4e7c0..c8becfe78e28 100644
--- a/drivers/iio/counter/Kconfig
+++ b/drivers/iio/counter/Kconfig
@@ -4,6 +4,7 @@
 # When adding new entries keep the list in alphabetical order
 
 menu "Counters"
+	depends on IIO_COUNTER
 
 config 104_QUAD_8
 	tristate "ACCES 104-QUAD-8 driver"
diff --git a/drivers/iio/industrialio-counter.c b/drivers/iio/industrialio-counter.c
new file mode 100644
index 000000000000..dfb982dae3a8
--- /dev/null
+++ b/drivers/iio/industrialio-counter.c
@@ -0,0 +1,900 @@
+/*
+ * Industrial I/O counter interface functions
+ * Copyright (C) 2017 William Breathitt Gray
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License, version 2, as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ */
+#include <linux/device.h>
+#include <linux/err.h>
+#include <linux/export.h>
+#include <linux/gfp.h>
+#include <linux/kernel.h>
+#include <linux/printk.h>
+#include <linux/slab.h>
+#include <linux/string.h>
+
+#include <linux/iio/iio.h>
+#include <linux/iio/types.h>
+
+#include <linux/iio/counter.h>
+
+#define iio_priv_get_counter(_indio_dev) \
+	(*(struct iio_counter **)iio_priv(_indio_dev))
+
+static struct iio_counter_signal *iio_counter_signal_find_by_id(
+	const struct iio_counter *const counter, const int id)
+{
+	size_t i;
+	struct iio_counter_signal *signal;
+
+	for (i = 0; i < counter->num_signals; i++) {
+		signal = counter->signals + i;
+		if (signal->id == id)
+			return signal;
+	}
+
+	return NULL;
+}
+
+static struct iio_counter_trigger *iio_counter_trigger_find_by_id(
+	const struct iio_counter_value *const value, const int id)
+{
+	size_t i;
+	struct iio_counter_trigger *trigger;
+
+	for (i = 0; i < value->num_triggers; i++) {
+		trigger = value->triggers + i;
+		if (trigger->signal->id == id)
+			return trigger;
+	}
+
+	return NULL;
+}
+
+static struct iio_counter_value *iio_counter_value_find_by_id(
+	const struct iio_counter *const counter, const int id)
+{
+	size_t i;
+	struct iio_counter_value *value;
+
+	for (i = 0; i < counter->num_values; i++) {
+		value = counter->values + i;
+		if (value->id == id)
+			return value;
+	}
+
+	return NULL;
+}
+
+static ssize_t iio_counter_signal_name_read(struct iio_dev *indio_dev,
+	uintptr_t priv, const struct iio_chan_spec *chan, char *buf)
+{
+	struct iio_counter *const counter = iio_priv_get_counter(indio_dev);
+	const struct iio_counter_signal *signal;
+
+	signal = iio_counter_signal_find_by_id(counter, chan->channel2);
+	if (!signal)
+		return -EINVAL;
+
+	return scnprintf(buf, PAGE_SIZE, "%s\n", signal->name);
+}
+
+static ssize_t iio_counter_value_name_read(struct iio_dev *indio_dev,
+	uintptr_t priv, const struct iio_chan_spec *chan, char *buf)
+{
+	struct iio_counter *const counter = iio_priv_get_counter(indio_dev);
+	const struct iio_counter_value *value;
+
+	value = iio_counter_value_find_by_id(counter, chan->channel2);
+	if (!value)
+		return -EINVAL;
+
+	return scnprintf(buf, PAGE_SIZE, "%s\n", value->name);
+}
+
+static ssize_t iio_counter_value_triggers_read(struct iio_dev *indio_dev,
+	uintptr_t priv, const struct iio_chan_spec *chan, char *buf)
+{
+	struct iio_counter *const counter = iio_priv_get_counter(indio_dev);
+	struct iio_counter_value *value;
+	size_t i;
+	const struct iio_counter_trigger *trigger;
+	ssize_t len = 0;
+
+	value = iio_counter_value_find_by_id(counter, chan->channel2);
+	if (!value)
+		return -EINVAL;
+
+	/* Print out a list of every Signal association to Value */
+	for (i = 0; i < value->num_triggers; i++) {
+		trigger = value->triggers + i;
+		len += snprintf(buf + len, PAGE_SIZE - len, "%d\t%s\t%s\n",
+			trigger->signal->id, trigger->signal->name,
+			trigger->trigger_modes[trigger->mode]);
+		if (len >= PAGE_SIZE)
+			return -ENOMEM;
+	}
+
+	return len;
+}
+
+static ssize_t iio_counter_trigger_mode_read(struct iio_dev *indio_dev,
+	uintptr_t priv, const struct iio_chan_spec *chan, char *buf)
+{
+	struct iio_counter *const counter = iio_priv_get_counter(indio_dev);
+	struct iio_counter_value *value;
+	struct iio_counter_trigger *trigger;
+	const int signal_id = *(int *)((void *)priv);
+	int mode;
+
+	if (!counter->ops->trigger_mode_get)
+		return -EINVAL;
+
+	value = iio_counter_value_find_by_id(counter, chan->channel2);
+	if (!value)
+		return -EINVAL;
+
+	trigger = iio_counter_trigger_find_by_id(value, signal_id);
+	if (!trigger)
+		return -EINVAL;
+
+	mode = counter->ops->trigger_mode_get(counter, value, trigger);
+
+	if (mode < 0)
+		return mode;
+	else if (mode >= trigger->num_trigger_modes)
+		return -EINVAL;
+
+	trigger->mode = mode;
+
+	return scnprintf(buf, PAGE_SIZE, "%s\n", trigger->trigger_modes[mode]);
+}
+
+static ssize_t iio_counter_trigger_mode_write(struct iio_dev *indio_dev,
+	uintptr_t priv, const struct iio_chan_spec *chan, const char *buf,
+	size_t len)
+{
+	struct iio_counter *const counter = iio_priv_get_counter(indio_dev);
+	struct iio_counter_value *value;
+	ssize_t err;
+	struct iio_counter_trigger *trigger;
+	const int signal_id = *(int *)((void *)priv);
+	unsigned int mode;
+
+	if (!counter->ops->trigger_mode_set)
+		return -EINVAL;
+
+	value = iio_counter_value_find_by_id(counter, chan->channel2);
+	if (!value)
+		return -EINVAL;
+
+	trigger = iio_counter_trigger_find_by_id(value, signal_id);
+	if (!trigger)
+		return -EINVAL;
+
+	for (mode = 0; mode < trigger->num_trigger_modes; mode++)
+		if (sysfs_streq(buf, trigger->trigger_modes[mode]))
+			break;
+
+	if (mode >= trigger->num_trigger_modes)
+		return -EINVAL;
+
+	err = counter->ops->trigger_mode_set(counter, value, trigger, mode);
+	if (err)
+		return err;
+
+	trigger->mode = mode;
+
+	return len;
+}
+
+static ssize_t iio_counter_trigger_mode_available_read(
+	struct iio_dev *indio_dev, uintptr_t priv,
+	const struct iio_chan_spec *chan, char *buf)
+{
+	struct iio_counter *const counter = iio_priv_get_counter(indio_dev);
+	struct iio_counter_value *value;
+	ssize_t len = 0;
+	struct iio_counter_trigger *trigger;
+	const int signal_id = *(int *)((void *)priv);
+	unsigned int i;
+
+	value = iio_counter_value_find_by_id(counter, chan->channel2);
+	if (!value)
+		return -EINVAL;
+
+	trigger = iio_counter_trigger_find_by_id(value, signal_id);
+	if (!trigger)
+		return -EINVAL;
+
+	for (i = 0; i < trigger->num_trigger_modes; i++)
+		len += scnprintf(buf + len, PAGE_SIZE - len, "%s ",
+			trigger->trigger_modes[i]);
+
+	buf[len - 1] = '\n';
+
+	return len;
+}
+
+static int iio_counter_value_function_set(struct iio_dev *indio_dev,
+	const struct iio_chan_spec *chan, unsigned int mode)
+{
+	struct iio_counter *const counter = iio_priv_get_counter(indio_dev);
+	struct iio_counter_value *value;
+	int err;
+
+	if (!counter->ops->value_function_set)
+		return -EINVAL;
+
+	/* Find relevant Value */
+	value = iio_counter_value_find_by_id(counter, chan->channel2);
+	if (!value)
+		return -EINVAL;
+
+	/* Map IIO core function_set to Generic Counter value_function_set */
+	err = counter->ops->value_function_set(counter, value, mode);
+	if (err)
+		return err;
+
+	value->mode = mode;
+
+	return 0;
+}
+
+static int iio_counter_value_function_get(struct iio_dev *indio_dev,
+	const struct iio_chan_spec *chan)
+{
+	struct iio_counter *const counter = iio_priv_get_counter(indio_dev);
+	struct iio_counter_value *value;
+	int retval;
+
+	if (!counter->ops->value_function_get)
+		return -EINVAL;
+
+	/* Find relevant Value */
+	value = iio_counter_value_find_by_id(counter, chan->channel2);
+	if (!value)
+		return -EINVAL;
+
+	/* Map IIO core function_get to Generic Counter value_function_get */
+	retval = counter->ops->value_function_get(counter, value);
+	if (retval < 0)
+		return retval;
+	else if (retval >= value->num_function_modes)
+		return -EINVAL;
+
+	value->mode = retval;
+
+	return retval;
+}
+
+static int iio_counter_value_ext_info_alloc(struct iio_chan_spec *const chan,
+	struct iio_counter_value *const value)
+{
+	const struct iio_chan_spec_ext_info ext_info_default[] = {
+		{
+			.name = "name",
+			.shared = IIO_SEPARATE,
+			.read = iio_counter_value_name_read
+		},
+		IIO_ENUM("function", IIO_SEPARATE, &value->function_enum),
+		{
+			.name = "function_available",
+			.shared = IIO_SEPARATE,
+			.read = iio_enum_available_read,
+			.private = (void *)&value->function_enum
+		},
+		{
+			.name = "triggers",
+			.shared = IIO_SEPARATE,
+			.read = iio_counter_value_triggers_read
+		}
+	};
+	const size_t num_default = ARRAY_SIZE(ext_info_default);
+	const struct iio_chan_spec_ext_info ext_info_trigger[] = {
+		{
+			.shared = IIO_SEPARATE,
+			.read = iio_counter_trigger_mode_read,
+			.write = iio_counter_trigger_mode_write
+		},
+		{
+			.shared = IIO_SEPARATE,
+			.read = iio_counter_trigger_mode_available_read
+		}
+	};
+	const size_t num_ext_info_trigger = ARRAY_SIZE(ext_info_trigger);
+	const size_t num_triggers_ext_info = num_ext_info_trigger *
+		value->num_triggers;
+	const size_t num_ext_info = num_default + num_triggers_ext_info + 1;
+	int err;
+	struct iio_chan_spec_ext_info *ext_info;
+	const struct iio_counter_trigger *trigger;
+	size_t i;
+	size_t j;
+
+	/* Construct function_enum for Value */
+	value->function_enum.items = value->function_modes;
+	value->function_enum.num_items = value->num_function_modes;
+	value->function_enum.set = iio_counter_value_function_set;
+	value->function_enum.get = iio_counter_value_function_get;
+
+	ext_info = kmalloc_array(num_ext_info, sizeof(*ext_info), GFP_KERNEL);
+	if (!ext_info)
+		return -ENOMEM;
+	ext_info[num_ext_info - 1].name = NULL;
+
+	/* Add ext_info for the name, function, function_available, and triggers
+	 * attributes
+	 */
+	memcpy(ext_info, ext_info_default, sizeof(ext_info_default));
+	/* Add ext_info for the trigger_signalX-Z and
+	 * trigger_signalX-Z_available attributes for each Trigger
+	 */
+	for (i = 0; i < num_triggers_ext_info; i += num_ext_info_trigger)
+		memcpy(ext_info + num_default + i, ext_info_trigger,
+			sizeof(ext_info_trigger));
+
+	/* Set name for each trigger_signalX-Z and trigger_signalX-Z_available
+	 * attribute; store the respective Signal ID address in each ext_info
+	 * private member
+	 */
+	for (i = num_default, j = 0; j < value->num_triggers; i++, j++) {
+		trigger = value->triggers + j;
+
+		ext_info[i].name = kasprintf(GFP_KERNEL, "trigger_signal%d-%d",
+			chan->channel, trigger->signal->id);
+		if (!ext_info[i].name) {
+			err = -ENOMEM;
+			goto err_name_alloc;
+		}
+		ext_info[i].private = (void *)&trigger->signal->id;
+		i++;
+
+		ext_info[i].name = kasprintf(GFP_KERNEL,
+			"trigger_signal%d-%d_available",
+			chan->channel, trigger->signal->id);
+		if (!ext_info[i].name) {
+			err = -ENOMEM;
+			goto err_name_alloc;
+		}
+		ext_info[i].private = (void *)&trigger->signal->id;
+	}
+
+	chan->ext_info = ext_info;
+
+	return 0;
+
+err_name_alloc:
+	while (i-- > num_default)
+		kfree(ext_info[i].name);
+	kfree(ext_info);
+	return err;
+}
+
+static void iio_counter_value_ext_info_free(
+	const struct iio_chan_spec *const channel)
+{
+	size_t i;
+	const char *const prefix = "trigger_signal";
+	const size_t prefix_len = strlen(prefix);
+
+	for (i = 0; channel->ext_info[i].name; i++)
+		if (!strncmp(channel->ext_info[i].name, prefix, prefix_len))
+			kfree(channel->ext_info[i].name);
+	kfree(channel->ext_info);
+}
+
+static const struct iio_chan_spec_ext_info iio_counter_signal_ext_info[] = {
+	{
+		.name = "name",
+		.shared = IIO_SEPARATE,
+		.read = iio_counter_signal_name_read
+	},
+	{}
+};
+
+static int iio_counter_channels_alloc(struct iio_counter *const counter)
+{
+	const size_t num_channels = counter->num_signals + counter->num_values +
+		counter->num_channels;
+	int err;
+	struct iio_chan_spec *channels;
+	size_t j;
+	struct iio_counter_value *value;
+	size_t i = counter->num_channels;
+	const struct iio_counter_signal *signal;
+
+	channels = kcalloc(num_channels, sizeof(*channels), GFP_KERNEL);
+	if (!channels)
+		return -ENOMEM;
+
+	/* If any channels were supplied on Counter registration,
+	 * we add them here to the front of the array
+	 */
+	memcpy(channels, counter->channels,
+		counter->num_channels * sizeof(*counter->channels));
+
+	/* Add channel for each Value */
+	for (j = 0; j < counter->num_values; j++) {
+		value = counter->values + j;
+
+		channels[i].type = IIO_COUNT;
+		channels[i].channel = counter->id;
+		channels[i].channel2 = value->id;
+		channels[i].info_mask_separate = BIT(IIO_CHAN_INFO_RAW);
+		channels[i].indexed = 1;
+		channels[i].counter = 1;
+
+		/* Add channels for Triggers associated with Value */
+		err = iio_counter_value_ext_info_alloc(channels + i, value);
+		if (err)
+			goto err_value_ext_info_alloc;
+
+		i++;
+	}
+
+	/* Add channel for each Signal */
+	for (j = 0; j < counter->num_signals; j++) {
+		signal = counter->signals + j;
+
+		channels[i].type = IIO_SIGNAL;
+		channels[i].channel = counter->id;
+		channels[i].channel2 = signal->id;
+		channels[i].info_mask_separate = BIT(IIO_CHAN_INFO_RAW);
+		channels[i].indexed = 1;
+		channels[i].counter = 1;
+		channels[i].ext_info = iio_counter_signal_ext_info;
+
+		i++;
+	}
+
+	counter->indio_dev->num_channels = num_channels;
+	counter->indio_dev->channels = channels;
+
+	return 0;
+
+err_value_ext_info_alloc:
+	while (i-- > counter->num_channels)
+		iio_counter_value_ext_info_free(channels + i);
+	kfree(channels);
+	return err;
+}
+
+static void iio_counter_channels_free(const struct iio_counter *const counter)
+{
+	size_t i = counter->num_channels + counter->indio_dev->num_channels;
+	const struct iio_chan_spec *const chans = counter->indio_dev->channels;
+
+	while (i-- > counter->num_channels)
+		/* Only IIO_COUNT channels need to be freed here */
+		if (chans[i].type == IIO_COUNT)
+			iio_counter_value_ext_info_free(chans + i);
+
+	kfree(chans);
+}
+
+static int iio_counter_read_raw(struct iio_dev *indio_dev,
+	struct iio_chan_spec const *chan, int *val, int *val2, long mask)
+{
+	struct iio_counter *const counter = iio_priv_get_counter(indio_dev);
+	struct iio_counter_signal *signal;
+	struct iio_counter_value *value;
+
+	if (mask != IIO_CHAN_INFO_RAW)
+		return -EINVAL;
+
+	switch (chan->type) {
+	/* Map read_raw to signal_read for Signal */
+	case IIO_SIGNAL:
+		if (!counter->ops->signal_read)
+			return -EINVAL;
+
+		signal = iio_counter_signal_find_by_id(counter, chan->channel2);
+		if (!signal)
+			return -EINVAL;
+
+		return counter->ops->signal_read(counter, signal, val, val2);
+	/* Map read_raw to value_read for Value */
+	case IIO_COUNT:
+		if (!counter->ops->value_read)
+			return -EINVAL;
+
+		value = iio_counter_value_find_by_id(counter, chan->channel2);
+		if (!value)
+			return -EINVAL;
+
+		return counter->ops->value_read(counter, value, val, val2);
+	/* Map read_raw to read_raw for non-counter channel */
+	default:
+		if (counter->info && counter->info->read_raw)
+			return counter->info->read_raw(indio_dev, chan, val,
+				val2, mask);
+	}
+
+	return -EINVAL;
+}
+
+static int iio_counter_write_raw(struct iio_dev *indio_dev,
+	struct iio_chan_spec const *chan, int val, int val2, long mask)
+{
+	struct iio_counter *const counter = iio_priv_get_counter(indio_dev);
+	struct iio_counter_signal *signal;
+	struct iio_counter_value *value;
+
+	if (mask != IIO_CHAN_INFO_RAW)
+		return -EINVAL;
+
+	switch (chan->type) {
+	/* Map write_raw to signal_write for Signal */
+	case IIO_SIGNAL:
+		if (!counter->ops->signal_write)
+			return -EINVAL;
+
+		signal = iio_counter_signal_find_by_id(counter, chan->channel2);
+		if (!signal)
+			return -EINVAL;
+
+		return counter->ops->signal_write(counter, signal, val, val2);
+	/* Map write_raw to value_write for Value */
+	case IIO_COUNT:
+		if (!counter->ops->value_write)
+			return -EINVAL;
+
+		value = iio_counter_value_find_by_id(counter, chan->channel2);
+		if (!value)
+			return -EINVAL;
+
+		return counter->ops->value_write(counter, value, val, val2);
+	/* Map write_raw to write_raw for non-counter channel */
+	default:
+		if (counter->info && counter->info->write_raw)
+			return counter->info->write_raw(indio_dev, chan, val,
+				val2, mask);
+	}
+
+	return -EINVAL;
+}
+
+static int iio_counter_signals_validate(const struct iio_counter *const counter)
+{
+	size_t i;
+	const struct iio_counter_signal *signal;
+	size_t j;
+	int curr_id;
+
+	/* At least one Signal must be defined */
+	if (!counter->num_signals || !counter->signals) {
+		pr_err("Counter '%d' Signals undefined\n", counter->id);
+		return -EINVAL;
+	}
+
+	for (i = 0; i < counter->num_signals; i++) {
+		signal = counter->signals + i;
+		/* No two Signals may share the same ID */
+		for (j = 0; j < i; j++) {
+			curr_id = counter->signals[j].id;
+			if (curr_id == signal->id) {
+				pr_err("Duplicate Counter '%d' Signal '%d'\n",
+						counter->id, signal->id);
+				return -EEXIST;
+			}
+		}
+		for (j = i + 1; j < counter->num_signals; j++) {
+			curr_id = counter->signals[j].id;
+			if (curr_id == signal->id) {
+				pr_err("Duplicate Counter '%d' Signal '%d'\n",
+						counter->id, signal->id);
+				return -EEXIST;
+			}
+		}
+	}
+
+	return 0;
+}
+
+static int iio_counter_triggers_validate(const int counter_id,
+	const struct iio_counter_value *const value)
+{
+	size_t i;
+	const struct iio_counter_trigger *trigger;
+	size_t j;
+	int curr_id;
+
+	/* At least one Trigger must be defined */
+	if (!value->num_triggers || !value->triggers) {
+		pr_err("Counter '%d' Value '%d' Triggers undefined\n",
+			counter_id, value->id);
+		return -EINVAL;
+	}
+
+	/* Ensure all Triggers have a defined Signal; this prevents a possible
+	 * NULL pointer dereference when later validating Trigger Signal IDs
+	 */
+	for (i = 0; i < value->num_triggers; i++)
+		if (!value->triggers[i].signal) {
+			pr_err("Counter '%d' Trigger '%zu' Signal undefined\n",
+				counter_id, i);
+			return -EINVAL;
+		}
+
+	/* Verify validity of each Trigger */
+	for (i = 0; i < value->num_triggers; i++) {
+		trigger = value->triggers + i;
+		/* No two Trigger Signals may share the same ID */
+		for (j = 0; j < i; j++) {
+			curr_id = value->triggers[j].signal->id;
+			if (curr_id == trigger->signal->id) {
+				pr_err("Signal '%d' is already linked to Counter '%d' Value '%d'\n",
+					trigger->signal->id, counter_id,
+					value->id);
+				return -EEXIST;
+			}
+		}
+		for (j = i + 1; j < value->num_triggers; j++) {
+			curr_id = value->triggers[j].signal->id;
+			if (curr_id == trigger->signal->id) {
+				pr_err("Signal '%d' is already linked to Counter '%d' Value '%d'\n",
+					trigger->signal->id, counter_id,
+					value->id);
+				return -EEXIST;
+			}
+		}
+
+		/* At least one trigger mode must be defined for each Trigger */
+		if (!trigger->num_trigger_modes || !trigger->trigger_modes) {
+			pr_err("Counter '%d' Signal '%d' trigger modes undefined\n",
+				counter_id, trigger->signal->id);
+			return -EINVAL;
+		}
+	}
+
+	return 0;
+}
+
+static int iio_counter_values_validate(const struct iio_counter *const counter)
+{
+	size_t i;
+	const struct iio_counter_value *value;
+	size_t j;
+	int curr_id;
+	int err;
+
+	/* At least one Value must be defined */
+	if (!counter->num_values || !counter->values) {
+		pr_err("Counter '%d' Values undefined\n", counter->id);
+		return -EINVAL;
+	}
+
+	/* Verify validity of each Value */
+	for (i = 0; i < counter->num_values; i++) {
+		value = counter->values + i;
+		/* No two Values may share the same ID */
+		for (j = 0; j < i; j++) {
+			curr_id = counter->values[j].id;
+			if (curr_id == value->id) {
+				pr_err("Duplicate Counter '%d' Value '%d'\n",
+					counter->id, value->id);
+				return -EEXIST;
+			}
+		}
+		for (j = i + 1; j < counter->num_values; j++) {
+			curr_id = counter->values[j].id;
+			if (curr_id == value->id) {
+				pr_err("Duplicate Counter '%d' Value '%d'\n",
+					counter->id, value->id);
+				return -EEXIST;
+			}
+		}
+
+		/* At least one function mode must be defined for each Value */
+		if (!value->num_function_modes || !value->function_modes) {
+			pr_err("Counter '%d' Value '%d' function modes undefined\n",
+				counter->id, value->id);
+			return -EINVAL;
+		}
+
+		/* Verify the Triggers associated with each Value */
+		err = iio_counter_triggers_validate(counter->id, value);
+		if (err)
+			return err;
+	}
+
+	return 0;
+}
+
+/**
+ * iio_counter_register - register Counter to the system
+ * @counter: pointer to IIO Counter to register
+ *
+ * This function piggybacks off of iio_device_register. First, the relevant
+ * Counter members are validated; the signals and values members must be defined
+ * and populated by valid Signal and Value structures respectively. Next, a
+ * struct iio_dev is allocated by a call to iio_device_alloc and initialized for
+ * the Counter, IIO channels are allocated, the Counter address is stored as the
+ * private data, and finally iio_device_register is called.
+ */
+int iio_counter_register(struct iio_counter *const counter)
+{
+	const struct iio_info info_default = {
+		.read_raw = iio_counter_read_raw,
+		.write_raw = iio_counter_write_raw
+	};
+	int err;
+	struct iio_info *info;
+	struct iio_counter **priv;
+
+	if (!counter)
+		return -EINVAL;
+
+	/* Verify that Signals are valid and IDs do not conflict */
+	err = iio_counter_signals_validate(counter);
+	if (err)
+		return err;
+
+	/* Verify that Values are valid and IDs do not conflict;
+	 * Triggers for each Value are also verified for validity
+	 */
+	err = iio_counter_values_validate(counter);
+	if (err)
+		return err;
+
+	counter->indio_dev = iio_device_alloc(sizeof(counter));
+	if (!counter->indio_dev)
+		return -ENOMEM;
+
+	info = kmalloc(sizeof(*info), GFP_KERNEL);
+	if (!info) {
+		err = -ENOMEM;
+		goto err_info_alloc;
+	}
+	/* If an iio_info has been supplied than we use that,
+	 * otherwise we set all callbacks to NULL; iio_counter_read_raw
+	 * and iio_counter_write_raw is used for read_raw and write_raw
+	 * for either case in order to support counter functionality
+	 * (supplied read_raw/write_raw will be called from within
+	 * iio_counter_read_raw/iio_counter_write_raw for non-counter
+	 * channels)
+	 */
+	if (counter->info) {
+		memcpy(info, counter->info, sizeof(*counter->info));
+		info->read_raw = iio_counter_read_raw;
+		info->write_raw = iio_counter_write_raw;
+	} else {
+		memcpy(info, &info_default, sizeof(info_default));
+	}
+
+	counter->indio_dev->info = info;
+	counter->indio_dev->modes = INDIO_DIRECT_MODE;
+	counter->indio_dev->name = counter->name;
+	counter->indio_dev->dev.parent = counter->dev;
+
+	/* IIO channels are allocated and set for Signals, Values, and Triggers;
+	 * any auxiliary IIO channels provided in iio_counter are also set
+	 */
+	err = iio_counter_channels_alloc(counter);
+	if (err)
+		goto err_channels_alloc;
+
+	/* Pointer to the counter is stored in indio_dev as a way to refer
+	 * back to the counter from within various IIO callbacks
+	 */
+	priv = iio_priv(counter->indio_dev);
+	memcpy(priv, &counter, sizeof(*priv));
+
+	err = iio_device_register(counter->indio_dev);
+	if (err)
+		goto err_iio_device_register;
+
+	return 0;
+
+err_iio_device_register:
+	iio_counter_channels_free(counter);
+err_channels_alloc:
+	kfree(info);
+err_info_alloc:
+	iio_device_free(counter->indio_dev);
+	return err;
+}
+EXPORT_SYMBOL(iio_counter_register);
+
+/**
+ * iio_counter_unregister - unregister Counter from the system
+ * @counter: pointer to IIO Counter to unregister
+ *
+ * The Counter is unregistered from the system. The indio_dev is unregistered
+ * and all allocated memory is freed.
+ */
+void iio_counter_unregister(struct iio_counter *const counter)
+{
+	const struct iio_info *const info = counter->indio_dev->info;
+
+	if (!counter)
+		return;
+
+	iio_device_unregister(counter->indio_dev);
+
+	iio_counter_channels_free(counter);
+
+	kfree(info);
+	iio_device_free(counter->indio_dev);
+}
+EXPORT_SYMBOL(iio_counter_unregister);
+
+static void devm_iio_counter_unreg(struct device *dev, void *res)
+{
+	iio_counter_unregister(*(struct iio_counter **)res);
+}
+
+/**
+ * devm_iio_counter_register - Resource-managed iio_counter_register
+ * @dev: Device to allocate iio_counter for
+ * @counter: pointer to IIO Counter to register
+ *
+ * Managed iio_counter_register. The IIO counter registered with this
+ * function is automatically unregistered on driver detach. This function
+ * calls iio_counter_register internally. Refer to that function for more
+ * information.
+ *
+ * If an iio counter registered with this function needs to be unregistered
+ * separately, devm_iio_counter_unregister must be used.
+ *
+ * RETURNS:
+ * 0 on success, negative error number on failure.
+ */
+int devm_iio_counter_register(struct device *dev,
+	struct iio_counter *const counter)
+{
+	struct iio_counter **ptr;
+	int ret;
+
+	ptr = devres_alloc(devm_iio_counter_unreg, sizeof(*ptr), GFP_KERNEL);
+	if (!ptr)
+		return -ENOMEM;
+
+	*ptr = counter;
+	ret = iio_counter_register(counter);
+	if (!ret)
+		devres_add(dev, ptr);
+	else
+		devres_free(ptr);
+
+	return ret;
+}
+EXPORT_SYMBOL(devm_iio_counter_register);
+
+static int devm_iio_counter_match(struct device *dev, void *res, void *data)
+{
+	struct iio_counter **r = res;
+
+	if (!r || !*r) {
+		WARN_ON(!r || !*r);
+		return 0;
+	}
+
+	return *r == data;
+}
+
+/**
+ * devm_iio_counter_unregister - Resource-managed iio_counter_unregister
+ * @dev: Device this iio_counter belongs to
+ * @counter: the iio counter associated with the device
+ *
+ * Unregister iio counter registered with devm_iio_counter_register.
+ */
+void devm_iio_counter_unregister(struct device *dev,
+	struct iio_counter *const counter)
+{
+	int rc;
+
+	rc = devres_release(dev, devm_iio_counter_unreg,
+		devm_iio_counter_match, counter);
+	WARN_ON(rc);
+}
+EXPORT_SYMBOL(devm_iio_counter_unregister);
diff --git a/include/linux/iio/counter.h b/include/linux/iio/counter.h
new file mode 100644
index 000000000000..35645406711a
--- /dev/null
+++ b/include/linux/iio/counter.h
@@ -0,0 +1,166 @@
+/*
+ * Industrial I/O counter interface
+ * Copyright (C) 2017 William Breathitt Gray
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License, version 2, as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ */
+#ifndef _IIO_COUNTER_H_
+#define _IIO_COUNTER_H_
+
+#ifdef CONFIG_IIO_COUNTER
+
+#include <linux/device.h>
+#include <linux/types.h>
+
+#include <linux/iio/iio.h>
+
+/**
+ * struct iio_counter_signal - IIO Counter Signal node
+ * @id:		[DRIVER] unique ID used to identify signal
+ * @name:	[DRIVER] device-specific signal name
+ */
+struct iio_counter_signal {
+	int		id;
+	const char	*name;
+};
+
+/**
+ * struct iio_counter_trigger - IIO Counter Trigger node
+ * @mode:		[DRIVER] current trigger mode state
+ * @trigger_modes:	[DRIVER] available trigger modes
+ * @num_trigger_modes:	[DRIVER] number of modes specified in @trigger_modes
+ * @signal:		[DRIVER] pointer to associated signal
+ */
+struct iio_counter_trigger {
+	unsigned int			mode;
+	const char *const		*trigger_modes;
+	unsigned int			num_trigger_modes;
+	struct iio_counter_signal	*signal;
+};
+
+/**
+ * struct iio_counter_value - IIO Counter Value node
+ * @id:			[DRIVER] unique ID used to identify value
+ * @name:		[DRIVER] device-specific value name
+ * @mode:		[DRIVER] current function mode state
+ * @function_modes:	[DRIVER] available function modes
+ * @num_function_modes:	[DRIVER] number of modes specified in @function_modes
+ * @triggers:		[DRIVER] array of triggers for initialization
+ * @num_triggers:	[DRIVER] number of triggers specified in @triggers
+ * @function_enum:	[INTERN] used internally to generate function attributes
+ */
+struct iio_counter_value {
+	int			id;
+	const char		*name;
+	unsigned int		mode;
+	const char *const	*function_modes;
+	unsigned int		num_function_modes;
+
+	struct iio_counter_trigger	*triggers;
+	size_t				num_triggers;
+
+	struct iio_enum		function_enum;
+};
+
+struct iio_counter;
+
+/**
+ * struct iio_counter_ops - IIO Counter related callbacks
+ * @signal_read:	function to request a signal value from the device.
+ *			Return value will specify the type of value returned by
+ *			the device. val and val2 will contain the elements
+ *			making up the returned value.
+ * @signal_write:	function to write a signal value to the device.
+ *                      Parameters are interpreted the same as signal_read.
+ * @trigger_mode_set:	function to set the trigger mode. mode is the index of
+ *			the requested mode from the value trigger_modes array.
+ * @trigger_mode_get:	function to get the current trigger mode. Return value
+ *			will specify the index of the current mode from the
+ *			value trigger_modes array.
+ * @value_read:		function to request a value value from the device.
+ *			Return value will specify the type of value returned by
+ *			the device. val and val2 will contain the elements
+ *			making up the returned value.
+ * @value_write:	function to write a value value to the device.
+ *                      Parameters are interpreted the same as value_read.
+ * @value_function_set: function to set the value function mode. mode is the
+ *			index of the requested mode from the value
+ *			function_modes array.
+ * @value_function_get: function to get the current value function mode. Return
+ *			value will specify the index of the current mode from
+ *			the value function_modes array.
+ */
+struct iio_counter_ops {
+	int (*signal_read)(struct iio_counter *counter,
+		struct iio_counter_signal *signal, int *val, int *val2);
+	int (*signal_write)(struct iio_counter *counter,
+		struct iio_counter_signal *signal, int val, int val2);
+	int (*trigger_mode_set)(struct iio_counter *counter,
+		struct iio_counter_value *value,
+		struct iio_counter_trigger *trigger, unsigned int mode);
+	int (*trigger_mode_get)(struct iio_counter *counter,
+		struct iio_counter_value *value,
+		struct iio_counter_trigger *trigger);
+	int (*value_read)(struct iio_counter *counter,
+		struct iio_counter_value *value, int *val, int *val2);
+	int (*value_write)(struct iio_counter *counter,
+		struct iio_counter_value *value, int val, int val2);
+	int (*value_function_set)(struct iio_counter *counter,
+		struct iio_counter_value *value, unsigned int mode);
+	int (*value_function_get)(struct iio_counter *counter,
+		struct iio_counter_value *value);
+};
+
+/**
+ * struct iio_counter - IIO Counter data structure
+ * @id:			[DRIVER] unique ID used to identify counter
+ * @name:		[DRIVER] name of the device
+ * @dev:		[DRIVER] device structure, should be assigned a parent
+ *			and owner
+ * @ops:		[DRIVER] callbacks from driver for counter components
+ * @signals:		[DRIVER] array of signals for initialization
+ * @num_signals:	[DRIVER] number of signals specified in @signals
+ * @values:		[DRIVER] array of values for initialization
+ * @num_values:		[DRIVER] number of values specified in @values
+ * @channels:		[DRIVER] channel specification structure table
+ * @num_channels:	[DRIVER] number of channels specified in @channels
+ * @info:		[DRIVER] callbacks and constant info from driver
+ * @indio_dev:		[INTERN] industrial I/O device structure
+ * @driver_data:	[DRIVER] driver data
+ */
+struct iio_counter {
+	int				id;
+	const char			*name;
+	struct device			*dev;
+	const struct iio_counter_ops	*ops;
+
+	struct iio_counter_signal	*signals;
+	size_t				num_signals;
+	struct iio_counter_value	*values;
+	size_t				num_values;
+
+	const struct iio_chan_spec	*channels;
+	size_t				num_channels;
+	const struct iio_info		*info;
+
+	struct iio_dev	*indio_dev;
+	void		*driver_data;
+};
+
+int iio_counter_register(struct iio_counter *const counter);
+void iio_counter_unregister(struct iio_counter *const counter);
+int devm_iio_counter_register(struct device *dev,
+	struct iio_counter *const counter);
+void devm_iio_counter_unregister(struct device *dev,
+	struct iio_counter *const counter);
+
+#endif /* CONFIG_IIO_COUNTER */
+
+#endif /* _IIO_COUNTER_H_ */
-- 
2.14.1

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

* [PATCH v3 3/6] iio: Documentation: Add IIO Generic Counter sysfs documentation
  2017-10-05 18:13 [PATCH v3 0/6] iio: Introduce the generic counter interface William Breathitt Gray
  2017-10-05 18:13 ` [PATCH v3 1/6] iio: Implement counter channel specification and IIO_SIGNAL constant William Breathitt Gray
  2017-10-05 18:13 ` [PATCH v3 2/6] iio: Introduce the generic counter interface William Breathitt Gray
@ 2017-10-05 18:13 ` William Breathitt Gray
  2017-10-08 12:10   ` Jonathan Cameron
  2017-10-05 18:14 ` [PATCH v3 4/6] docs: Add IIO Generic Counter Interface documentation William Breathitt Gray
                   ` (3 subsequent siblings)
  6 siblings, 1 reply; 17+ messages in thread
From: William Breathitt Gray @ 2017-10-05 18:13 UTC (permalink / raw)
  To: jic23, benjamin.gaignard, knaack.h, lars, pmeerw
  Cc: linux-iio, linux-kernel, William Breathitt Gray

This patch adds standard documentation for the userspace sysfs
atrributes of the IIO Generic Counter Interface.

Signed-off-by: William Breathitt Gray <vilhelm.gray@gmail.com>
---
 .../testing/sysfs-bus-iio-generic-counter-sysfs    | 63 ++++++++++++++++++++++
 1 file changed, 63 insertions(+)
 create mode 100644 Documentation/ABI/testing/sysfs-bus-iio-generic-counter-sysfs

diff --git a/Documentation/ABI/testing/sysfs-bus-iio-generic-counter-sysfs b/Documentation/ABI/testing/sysfs-bus-iio-generic-counter-sysfs
new file mode 100644
index 000000000000..b2c09254ec5a
--- /dev/null
+++ b/Documentation/ABI/testing/sysfs-bus-iio-generic-counter-sysfs
@@ -0,0 +1,63 @@
+What:		/sys/bus/iio/devices/iio:deviceX/countX-Y_function
+KernelVersion:	4.14
+Contact:	linux-iio@vger.kernel.org
+Description:
+		Counter function mode for Value Y of Counter X; counter function
+		evaluation triggered by conditions specified by the
+		countX-Y_trigger_signalX-Z attributes.
+
+What:		/sys/bus/iio/devices/iio:deviceX/countX-Y_function_available
+KernelVersion:	4.14
+Contact:	linux-iio@vger.kernel.org
+Description:
+		Discrete set of available function modes for the configuration
+		of the respective Value Y of Counter X are listed in this file.
+
+What:		/sys/bus/iio/devices/iio:deviceX/countX-Y_name
+KernelVersion:	4.14
+Contact:	linux-iio@vger.kernel.org
+Description:
+		Read-only attribute that indicates the device-specific name of
+		Value Y of Counter X.
+
+What:		/sys/bus/iio/devices/iio:deviceX/countX-Y_raw
+KernelVersion:	4.14
+Contact:	linux-iio@vger.kernel.org
+Description:
+		Raw count data of Value Y of Counter X.
+
+What:		/sys/bus/iio/devices/iio:deviceX/countX-Y_triggers
+KernelVersion:	4.14
+Contact:	linux-iio@vger.kernel.org
+Description:
+		List of Signals associated to Value Y of Counter X.
+
+What:		/sys/bus/iio/devices/iio:deviceX/countX-Y_trigger_signalX-Z
+KernelVersion:	4.14
+Contact:	linux-iio@vger.kernel.org
+Description:
+		Trigger mode of Value Y for Signal Z. This attribute indicates
+		the condition of Signal Z that triggers the counter function
+		evaluation for Value Y. Both Value Y and Signal Z are part of
+		Counter X.
+
+What:		/sys/bus/iio/devices/iio:deviceX/countX-Y_trigger_signalX-Z_available
+KernelVersion:	4.14
+Contact:	linux-iio@vger.kernel.org
+Description:
+		Discrete set of available trigger modes are listed in this file
+		for the configuration of the respective Trigger associating
+		Signal Z of Counter X with Value Y of Counter X.
+
+What:		/sys/bus/iio/devices/iio:deviceX/signalX-Y_name
+KernelVersion:	4.14
+Contact:	linux-iio@vger.kernel.org
+Description:
+		Read-only attribute that indicates the device-specific name of
+		Signal Y of Counter X.
+
+What:		/sys/bus/iio/devices/iio:deviceX/signalX-Y_raw
+KernelVersion:	4.14
+Contact:	linux-iio@vger.kernel.org
+Description:
+		Raw signal data of Signal Y of Counter X.
-- 
2.14.1

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

* [PATCH v3 4/6] docs: Add IIO Generic Counter Interface documentation
  2017-10-05 18:13 [PATCH v3 0/6] iio: Introduce the generic counter interface William Breathitt Gray
                   ` (2 preceding siblings ...)
  2017-10-05 18:13 ` [PATCH v3 3/6] iio: Documentation: Add IIO Generic Counter sysfs documentation William Breathitt Gray
@ 2017-10-05 18:14 ` William Breathitt Gray
  2017-10-08 13:19   ` Jonathan Cameron
  2017-10-05 18:14 ` [PATCH v3 5/6] iio: Add dummy counter driver William Breathitt Gray
                   ` (2 subsequent siblings)
  6 siblings, 1 reply; 17+ messages in thread
From: William Breathitt Gray @ 2017-10-05 18:14 UTC (permalink / raw)
  To: jic23, benjamin.gaignard, knaack.h, lars, pmeerw
  Cc: linux-iio, linux-kernel, William Breathitt Gray

This patch adds high-level documentation about the IIO Generic Counter
Interface.

Signed-off-by: William Breathitt Gray <vilhelm.gray@gmail.com>
---
 Documentation/driver-api/iio/generic-counter.txt | 526 +++++++++++++++++++++++
 1 file changed, 526 insertions(+)
 create mode 100644 Documentation/driver-api/iio/generic-counter.txt

diff --git a/Documentation/driver-api/iio/generic-counter.txt b/Documentation/driver-api/iio/generic-counter.txt
new file mode 100644
index 000000000000..0ce43b71e1c7
--- /dev/null
+++ b/Documentation/driver-api/iio/generic-counter.txt
@@ -0,0 +1,526 @@
+=========================
+Generic Counter Interface
+=========================
+
+Introduction
+============
+
+Counter devices are prevalent within a diverse spectrum of industries.
+The ubiquitous presence of these devices necessitates a common interface
+and standard of interaction and exposure. This driver API attempts to
+resolve the issue of duplicate code found among existing counter device
+drivers by introducing a generic counter interface for consumption. The
+generic counter interface enables drivers to support and expose a common
+set of components and functionality present in counter devices.
+
+Theory
+======
+
+Counter devices can vary greatly in design, but regardless of whether
+some devices are quadrature encoder counters or pedometers, all counter
+devices consist of a core set of components. This core set of
+components, shared by all counter devices, is what forms the essence of
+the generic counter interface.
+
+There are three core components to a counter:
+
+        VALUE
+        -----
+        A Value represents the count data for a set of Signals. A Value
+        has a count function mode (e.g. "increment" or "quadrature x4")
+        which respresents the update behavior for the count data. A
+        Value also has a set of one or more associated Signals.
+
+        SIGNAL
+        ------
+        A Signal represents a count input line. A Signal may be
+	associated to one or more Values.
+
+        TRIGGER
+	-------
+        A Trigger represents a Value's count function trigger condition
+        mode (e.g. "rising edge" or "double pulse") for an associated
+        Signal.	If a Signal is associated with a Value, a respective
+        Trigger	instance for that association exists -- albeit perhaps
+        with a trigger condition mode of "none."
+
+A counter is defined as a set of input signals associated to count data
+that are generated by the evaluation of the state of the associated
+input signals as defined by the respective count functions. Within the
+context of the generic counter interface, a counter consists of Values
+each associated to a set of Signals, whose respective Trigger instances
+represent the count function update conditions for the associated
+Values.
+
+Paradigm
+========
+
+The most basic counter device may be expressed as a single Value
+associated with a single Signal via a single Trigger. Take for example
+a hypothetical counter device which simply accumulates a count of rising
+edges on a source input line.
+
+        Value                Trigger        Signal
+        -----                -------        ------
++---------------------+
+| Data: Count         |    Rising Edge     ________
+| Function: Increment |  <-------------   / Source \
+|                     |                  ____________
++---------------------+
+
+In this example, the Signal is a source input line with a pulsing
+voltage, while the Value is a persistent count which increments. The
+Signal is associated with the Value via a respective Trigger. The
+increment function is triggered by the condition specified by the
+Triggered -- in this case a rising edge condition. In summary, the
+counter device existence and behavior is aptly represented by respective
+Value, Signal, and Trigger components: a rising edge condition triggers
+an incrementation function on an accumulating count datum.
+
+A counter device is not limited to a single Signal; in fact, in theory
+an unlimited number of Signals may be associated with a Value. For
+example, a quadrature encoder counter device can keep track of position
+based on the states of two input lines.
+
+           Value                 Trigger      Signal
+           -----                 -------      ------
++-------------------------+
+| Data: Position          |    Both Edges      ___
+| Function: Quadrature x4 |  <-------------   / A \
+|                         |                  _______
+|                         |
+|                         |    Both Edges      ___
+|                         |  <-------------   / B \
+|                         |                  _______
++-------------------------+
+
+In this example, two Signals (quadrature encoder lines A and B) are
+associated to a single Value: a rising or falling edge on either A or B
+triggers the "Quadrature x4" function which determines the direction of
+movement and updates the respective position data. The "Quadrature x4"
+function is likely implemented in the hardware of the quadrature encoder
+counter device; the Value, Triggers, and Signals simply represent this
+hardware behavior and functionality.
+
+Signal associated to the same Value can have differing trigger
+conditions. For example, a quadrature encoder counter device operating
+in a non-quadrature mode could have one input line dedicated for
+movement and a second input line dedicated for direction.
+
+           Value                  Trigger      Signal
+           -----                  -------      ------
++------------------------- +
+| Data: Position           |    Rising Edge     ___
+| Function: Non-quadrature |  <-------------   / A \ (Movement)
+|                          |                  _______
+|                          |
+|                          |       None         ___
+|                          |  <-------------   / B \ (Direction)
+|                          |                  _______
++--------------------------+
+
+Only Signal A triggers the "Non-quadrature" update function, but the
+state of Signal B is still required in order to know the direction in
+order to properly update the position data. So in the end, both Signals
+are associated to the same Value via two respective Triggers, but only
+one Trigger has an active trigger condition while the other is left in a
+"None" condition mode to indicate its respective Signal's availability
+for state evaluation despite its non-triggering mode.
+
+Although the examples thus far have been representations of physical
+devices, this is not a necessity. A counter simply represent the
+evaluation (Value) of input data (Signals) triggered by specific
+conditions (Triggers). A counter can be the representation of more
+abstract components.
+
+For example, suppose a counter representation is desired for a DNA
+sequence analysizer which detects possible genetic diseases.
+
+        Value                     Trigger           Signal
+        -----                     -------           ------
++---------------------+
+| Data: Diseases      |    Gene Transcript (EST)     _____
+| Function: Cancers   |  <-----------------------   / DNA \ (GAAGTGC...)
+|                     |                            _________
++---------------------+
+
+In this scenario, the Signal is a stream of DNA nucleotide bases (As,
+Ts, Cs, and Gs), the Trigger is expressed sequence tags (ESTs), and the
+Value is a list of diseases discovered (in this case the function is
+evaluating for possible cancers). Note how the Signal in this example
+does not represent a physical voltage line, nor does the Trigger
+represent a physical voltage line state change, nor does the Value
+represent a strictly decimal data value.
+
+The DNA sequence analysizer example is contrived to illustrate the
+flexibility of the generic counter paradigm by demonstrating its
+capability of representing abstract concepts; however, physical devices
+are likely to be more fitting for such a representation.
+
+The key concept is that the Signal, Trigger, and Value are abstract
+representations which do not need to be closely married to their
+respective physical sources. This allows the user of a counter to
+divorce themselves from the nuances of physical components (such as
+whether an input line is differential or single-ended) and focus on the
+core idea of what the data and process represent (an accumulated count
+of rising edges).
+
+Userspace Interface
+===================
+
+Several sysfs attributes are generated by the generic counter interface,
+and reside under the /sys/bus/iio/devices/iio:deviceX directory.
+
+Each counter has a respective set of countX-Y and signalX-Y prefixed
+attributes, where X is the id set in the counter structure, and Y is the
+id of the respective Value or Signal.
+
+The generic counter interface sysfs attributes are as follows:
+
+        countX-Y_function: count function mode
+        countX-Y_function_available: available count function modes
+        countX-Y_name: Value name
+        countX-Y_raw: Value data
+        countX-Y_triggers: Value's associated Signals
+        countX-Y_trigger_signalX-Z: Value Y trigger mode for Signal Z
+        countX-Y_trigger_signalX-Z_available: available Value Y trigger
+                                              modes for Signal Z
+        signalX-Y_name: Signal name
+        signalX-Y_raw: Signal data
+
+Through these sysfs attributes, programs and scripts may interact with
+the generic counter paradigm Values, Triggers, and Signals of respective
+counter devices.
+
+Driver API
+==========
+
+Driver authors may utilize the generic counter interface in their code
+by including the include/linux/iio/counter.h header file. This header
+file provides several core data structures and function prototypes for
+defining a generic counter.
+
+struct iio_counter_signal
+-------------------------
+
+This structure defines a generic counter paradigm Signal component;
+typically this will correlate with an input channel on a physical
+counter device. This structure is the simplest to define with only two
+structure members which require explicit configuration:
+
+        id:     Unique ID used to identify the Signal
+
+        name:   Device-specific Signal name (typically the device input
+                channel name)
+
+struct iio_counter_trigger
+--------------------------
+
+This structure defines a generic counter paradigm Trigger component. To
+properly utilize this structure, trigger modes and an associated Signal
+must be defined:
+
+        mode:                   Index of the current trigger mode state
+
+        trigger_modes:          Array of trigger modes each represented
+	                        by a character string
+
+        num_trigger_modes:      Number of trigger modes provided in
+	                        trigger_modes array
+
+        signal:                 Pointer to associated Signal
+
+struct iio_counter_value
+------------------------
+
+This structure defines a generic counter paradigm Value component;
+typically this will correlate with the read data (the "count" value)
+provided by a physical counter device. This structure requires the
+explicit configuration of an ID, name, function modes (the function
+triggered on a Trigger condition), and optionally a set of initial
+associated Triggers:
+
+        id:                     Unique ID used to identify the Signal
+
+        name:                   Device-specific Value name (typically
+	                        the device read channel name)
+
+        mode:                   Index of the current function mode state
+
+        function_modes:         Array of function modes each represented
+	                        by a character string
+
+        num_function_modes:     Number of function modes provided in
+	                        function_modes array
+
+        triggers:               Array of associated Triggers
+
+        num_triggers:           Number of Triggers provided in triggers
+	                        array
+
+struct iio_counter_ops
+----------------------
+
+This structure defines callbacks to interact with the Value, Trigger,
+and Signal components:
+
+        signal_read:            Function to request a signal value from
+	                        the device. Return value will specify
+				the type of value returned by the
+				device. val and val2 will contain the
+				elements making up the returned value.
+
+	signal_write:           Function to write a signal value to the
+	                        device. Parameters are interpreted the
+				same as signal_read.
+
+        trigger_mode_set:       Function to set the trigger mode. mode
+	                        is the index of the requested mode from
+				the value trigger_modes array.
+
+        trigger_mode_get:       Function to get the current trigger
+	                        mode. Return value will specify the
+				index of the current mode from the value
+				trigger_modes array.
+
+        value_read:             Function to request a value value from
+	                        the device. Return value will specify
+				the type of value returned by the
+				device. val and val2 will contain the
+				elements making up the returned value.
+
+        value_write:            Function to write a value value to the
+	                        device. Parameters are interpreted the
+				same as value_read.
+
+        value_function_set:     Function to set the value function mode.
+	                        mode is the index of the requested mode
+				from the value function_modes array.
+
+        value_function_get:     Function to get the current value
+	                        function mode. Return value will specify
+				the index of the current mode from the
+				value function_modes array.
+
+struct iio_counter
+------------------
+
+This is the main data structure for a counter device; access to all
+respective Values, Triggers, and Signals is possible from this
+structure. This structure allows the configuration of an ID, name,
+function callbacks, initial Signals and initial Values, auxiliary IIO
+core channels and callbacks, and driver-specific data:
+
+        id:                     Unique ID used to identify the counter
+
+        name:                   Name of the counter device
+
+        dev:                    Device structure, which should be
+				assigned a parent and owner
+
+        ops:                    Function callbacks for counter
+	                        components (Signal, Trigger, Value)
+
+        signals:                Array of Signals
+
+        num_signals:            Number of Signals specified in signals
+	                        array
+
+        values:                 Array of Values
+
+        num_values:             Number of Values specified in values
+	                        array
+
+        channels:               Optional IIO core channels specification
+	                        structure table
+
+        num_channels:           Number of channels specified in channels
+
+        info:                   IIO core function callbacks and constant
+	                        info from driver
+
+        driver_data:            Driver-specific data
+
+Registration functions
+----------------------
+
+Counters may be registered to the system via the iio_counter_register
+function and subsequently unregistered via the iio_counter_unregister
+function. The devm_iio_counter_register and devm_iio_counter_unregister
+functions serve as device memory-managed versions of the
+iio_counter_register and iio_counter_unregister functions.
+
+An initialized iio_counter structure, which defines the Counter, is
+required to be passed in for registration; the Signals, Values, and
+Triggers for the Counter passed in via the signals and values arrays as
+part of the iio_counter structure. If auxiliary IIO core channels and
+functionality are required, IIO core channels and callbacks may be
+passed in via the channels and info members of the passed-in iio_counter
+structure.
+
+Implementation
+==============
+
+The IIO generic counter interface piggybacks off of the IIO core. This
+is primarily used to leverage the existing sysfs setup: the IIO_COUNT
+channel attributes represent the Counter Value, while the IIO_SIGNAL
+channel attributes represent the Counter Signal; auxilary IIO_COUNT
+attributes represent the Counter Signal connections (Triggers) and their
+respective state change configurations which trigger an associated
+"counter function" evaluation.
+
+The iio_counter_ops structure serves as a container for driver callbacks
+to communicate with the device; function callbacks are provided to read
+and write various Signals and Values, and set and get the "trigger mode"
+and "function mode" for various Triggers and Values respectively.
+
+To support a counter device, a driver must first allocate the available
+Counter Signals via iio_counter_signal structures. These Signals should
+be stored as an array and set to the signals array member of an
+allocated iio_counter structure before the Counter is registered to the
+system.
+
+Counter Values may be allocated via iio_counter_value structures, and
+respective Counter Signal associations (Triggers) made via
+iio_counter_trigger structures. Associated iio_counter_trigger
+structures are stored as an array and set to the the triggers array
+member of the respective iio_counter_value structure. These
+iio_counter_value structures are set to the values array member of an
+allocated iio_counter structure before the Counter is registered to the
+system.
+
+A counter device is registered to the system by passing the respective
+initialized iio_counter structure to the iio_counter_register function;
+similarly, the iio_counter_unregister function unregisters the
+respective counter.
+
+Architecture
+============
+
+Although the IIO Generic Counter Interface utilizes IIO core under the
+hood, driver authors are not necessarily required to interact with IIO
+core data structures and functions directly -- in theory, such details
+of the system are abstracted away. Driver authors only need to concern
+themselves with the Generic Counter specific data structures and
+functions found in the include/linux/iio/counter.h header file.
+
+In other words, the driver API is intended to expose itself sufficiently
+upon the principles and concepts of the generic counter paradigm (i.e.
+Values, Triggers, Signals, etc.) such that it may be indepedent from its
+underlying implementation; theoretically, the IIO core code in the
+implementation could be replaced away in its entirely by an alternative
+implementation all without the need to update existing drivers utilizing
+the Generic Counter Interface driver API.
+
+This paradigm separation however does result in some mapping concerns
+between Generic Counter functions to IIO core functions; in particular,
+parameters for the IIO core functions expect IIO core data structures
+(e.g. iio_dev and iio_chan_spec) which are not provided directly by the
+parameters for the respective Generic Counter functions. This results in
+a somewhat opaque pathway from a iio_counter structure to its associated
+iio_dev in order to support the required IIO core calls.
+
+The following call graphs should help illustrate some of the main IIO
+core dependencies:
+
++----------------------+
+| iio_counter_register |
++----------------------+
+  |  |  |
+  |  |  +-----------------------------+
+  |  +------------------+             |
+  |                     |             |
+  V                     V             V
++------------------+  +----------+  +---------------------+
+| iio_device_alloc |  | iio_priv |  | iio_device_register |
++------------------+  +----------+  +---------------------+
+
+The iio_counter_register function allocates and initializes a new
+iio_dev structure which will serve as the respective Counter's gateway
+to IIO core support. The address of the parent iio_counter structure is
+stored with the iio_dev structure via iio_priv in order to allow access
+back to the Counter from within the IIO core functions. Finally, the
+iio_dev structure is registered via iio_device_register.
+
++-----------------------+   +----------------------+
+| iio_read_channel_info |-->| iio_counter_read_raw |
++-----------------------+   +----------------------+
+                              |  |  |
+  +---------------------------+  |  |
+  |                +-------------+  |
+  |                |               ++
+  |                |               |
+  V                V               V
+  IIO_SIGNAL       IIO_COUNT       IIO_*
++-------------+  +------------+  +----------+
+| signal_read |  | value_read |  | read_raw |
++-------------+  +------------+  +----------+
+
++------------------------+   +-----------------------+
+| iio_write_channel_info |-->| iio_counter_write_raw |
++------------------------+   +-----------------------+
+                               |  |  |
+  +----------------------------+  |  |
+  |                 +-------------+  |
+  |                 |                |
+  |                 |                |
+  V                 V                V
+  IIO_SIGNAL        IIO_COUNT        IIO_*
++--------------+  +-------------+  +-----------+
+| signal_write |  | value_write |  | write_raw |
++--------------+  +-------------+  +-----------+
+
+Normally, the IIO core iio_read_channel_info and iio_write_channel_info
+functions respectiveluy call the driver-supplied read_raw and write_raw
+callbacks directly. Since the generic counter interface serves as an
+abstraction above IIO core, drive authors do not generally directly
+configure a read_raw/write_raw callback.
+
+Instead, the IIO Generic Counter Interface hooks on to the
+iio_read_channel_info and iio_write_channel_info expected read_raw and
+write_raw callbacks respectively via iio_counter_read_raw and
+iio_counter_write_raw. The iio_counter_read_raw and
+iio_counter_write_raw functions then call the respective driver-supplied
+signal_read/value_read and signal_write/value_write callbacks
+respectively for the appropriate IIO_SIGNAL OR IIO_COUNT. If an IIO core
+channel that was not part of the generic counter paradigm was supplied
+via the channels member of the iio_counter structure, then the
+respective driver-supplied (via the iio_counter structure info member)
+read_raw and write_raw are called.
+
++---------------------------+        +----------------------------+
+| iio_read_channel_ext_info |        | iio_write_channel_ext_info |
++---------------------------+        +----------------------------+
+  |                                    |
+  V                                    V
++-------------------------------+    +--------------------------------+
+| iio_counter_trigger_mode_read |    | iio_counter_trigger_mode_write |
++-------------------------------+    +--------------------------------+
+  |                                    |
+  V                                    V
++------------------+                 +------------------+
+| trigger_mode_get |                 | trigger_mode_set |
++------------------+                 +------------------+
+
++---------------+                     +----------------+
+| iio_enum_read |                     | iio_enum_write |
++---------------+                     +----------------+
+  |                                     |
+  V                                     V
++--------------------------------+    +--------------------------------+
+| iio_counter_value_function_get |    | iio_counter_value_function_set |
++--------------------------------+    +--------------------------------+
+  |                                     |
+  V                                     V
++--------------------+                +--------------------+
+| value_function_get |                | value_function_set |
++--------------------+                +--------------------+
+
+The driver-supplied trigger_mode_get and trigger_mode_set callbacks hook
+on to the iio_read_channel_ext_info and iio_write_channel_ext_info
+functions respectively via the iio_counter_trigger_mode_read and
+iio_counter_trigger_mode_write functions. Similarly, the driver-supplied
+value_function_get and value_function set callbacks hook on to the
+iio_enum_read and iio_enum_write functions respectively via the
+iio_counter_value_function_get and iio_counter_value_function set
+functions.
-- 
2.14.1

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

* [PATCH v3 5/6] iio: Add dummy counter driver
  2017-10-05 18:13 [PATCH v3 0/6] iio: Introduce the generic counter interface William Breathitt Gray
                   ` (3 preceding siblings ...)
  2017-10-05 18:14 ` [PATCH v3 4/6] docs: Add IIO Generic Counter Interface documentation William Breathitt Gray
@ 2017-10-05 18:14 ` William Breathitt Gray
  2017-10-08 13:41   ` Jonathan Cameron
  2017-10-05 18:14 ` [PATCH v3 6/6] iio: 104-quad-8: Add IIO generic counter interface support William Breathitt Gray
  2017-10-08 14:38 ` [PATCH v3 0/6] iio: Introduce the generic counter interface Jonathan Cameron
  6 siblings, 1 reply; 17+ messages in thread
From: William Breathitt Gray @ 2017-10-05 18:14 UTC (permalink / raw)
  To: jic23, benjamin.gaignard, knaack.h, lars, pmeerw
  Cc: linux-iio, linux-kernel, William Breathitt Gray

This patch introduces the dummy counter driver. The dummy counter driver
serves as a reference implementation of a driver that utilizes the
Generic Counter interface.

Writing individual '1' and '0' characters to the Signal attributes
allows a user to simulate a typical Counter Signal input stream for
evaluation; the Counter will evaluate the Signal data based on the
respective trigger mode for the associated Signal, and trigger the
associated counter function specified by the respective function mode.
The current Value value may be read, and the Value value preset by a
write.

Signed-off-by: William Breathitt Gray <vilhelm.gray@gmail.com>
---
 drivers/iio/counter/Kconfig         |  15 ++
 drivers/iio/counter/Makefile        |   1 +
 drivers/iio/counter/dummy-counter.c | 293 ++++++++++++++++++++++++++++++++++++
 3 files changed, 309 insertions(+)
 create mode 100644 drivers/iio/counter/dummy-counter.c

diff --git a/drivers/iio/counter/Kconfig b/drivers/iio/counter/Kconfig
index c8becfe78e28..494aed40e9c9 100644
--- a/drivers/iio/counter/Kconfig
+++ b/drivers/iio/counter/Kconfig
@@ -22,6 +22,21 @@ config 104_QUAD_8
 	  The base port addresses for the devices may be configured via the base
 	  array module parameter.
 
+config DUMMY_COUNTER
+	tristate "Dummy counter driver"
+	help
+	  Select this option to enable the dummy counter driver. The dummy
+	  counter driver serves as a reference implementation of a driver that
+	  utilizes the Generic Counter interface.
+
+	  Writing individual '1' and '0' characters to the Signal attributes
+	  allows a user to simulate a typical Counter Signal input stream for
+	  evaluation; the Counter will evaluate the Signal data based on the
+	  respective trigger mode for the associated Signal, and trigger the
+	  associated counter function specified by the respective function mode.
+	  The current Value value may be read, and the Value value preset by a
+	  write.
+
 config STM32_LPTIMER_CNT
 	tristate "STM32 LP Timer encoder counter driver"
 	depends on MFD_STM32_LPTIMER || COMPILE_TEST
diff --git a/drivers/iio/counter/Makefile b/drivers/iio/counter/Makefile
index 1b9a896eb488..8c2ef0115426 100644
--- a/drivers/iio/counter/Makefile
+++ b/drivers/iio/counter/Makefile
@@ -5,4 +5,5 @@
 # When adding new entries keep the list in alphabetical order
 
 obj-$(CONFIG_104_QUAD_8)	+= 104-quad-8.o
+obj-$(CONFIG_DUMMY_COUNTER)	+= dummy-counter.o
 obj-$(CONFIG_STM32_LPTIMER_CNT)	+= stm32-lptimer-cnt.o
diff --git a/drivers/iio/counter/dummy-counter.c b/drivers/iio/counter/dummy-counter.c
new file mode 100644
index 000000000000..6ecc9854894f
--- /dev/null
+++ b/drivers/iio/counter/dummy-counter.c
@@ -0,0 +1,293 @@
+/*
+ * Dummy counter driver
+ * Copyright (C) 2017 William Breathitt Gray
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License, version 2, as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ */
+#include <linux/device.h>
+#include <linux/errno.h>
+#include <linux/iio/counter.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/types.h>
+
+#define DUMCNT_NUM_COUNTERS 2
+/**
+ * struct dumcnt - private data structure
+ * @counter:	instance of the iio_counter
+ * @counts:	array of accumulation values
+ * @states:	array of input line states
+ */
+struct dumcnt {
+	struct iio_counter counter;
+	unsigned int counts[DUMCNT_NUM_COUNTERS];
+	unsigned int states[DUMCNT_NUM_COUNTERS];
+};
+
+static int dumcnt_signal_read(struct iio_counter *counter,
+	struct iio_counter_signal *signal, int *val, int *val2)
+{
+	struct dumcnt *const priv = counter->driver_data;
+	*val = priv->states[signal->id];
+
+	return IIO_VAL_INT;
+}
+
+static int dumcnt_signal_write(struct iio_counter *counter,
+	struct iio_counter_signal *signal, int val, int val2)
+{
+	struct dumcnt *const priv = counter->driver_data;
+	const unsigned int id = signal->id;
+	const unsigned int prev_state = priv->states[id];
+	struct iio_counter_value *const value = counter->values + id;
+	const unsigned int function_mode = value->mode;
+	const unsigned int trigger_mode = value->triggers[0].mode;
+	unsigned int triggered = 0;
+
+	if (val && val != 1)
+		return -EINVAL;
+
+	/* If no state change then just exit */
+	if (prev_state == val)
+		return 0;
+
+	priv->states[id] = val;
+
+	switch (trigger_mode) {
+	/* "none" case */
+	case 0:
+		return 0;
+	/* "rising edge" case */
+	case 1:
+		if (!prev_state)
+			triggered = 1;
+		break;
+	/* "falling edge" case */
+	case 2:
+		if (prev_state)
+			triggered = 1;
+		break;
+	/* "both edges" case */
+	case 3:
+		triggered = 1;
+		break;
+	}
+
+	/* If counter function triggered */
+	if (triggered)
+		/* "increase" case */
+		if (function_mode)
+			priv->counts[id]++;
+		/* "decrease" case */
+		else
+			priv->counts[id]--;
+
+	return 0;
+}
+
+static int dumcnt_trigger_mode_set(struct iio_counter *counter,
+	struct iio_counter_value *value, struct iio_counter_trigger *trigger,
+	unsigned int mode)
+{
+	if (mode >= trigger->num_trigger_modes)
+		return -EINVAL;
+
+	trigger->mode = mode;
+
+	return 0;
+}
+
+static int dumcnt_trigger_mode_get(struct iio_counter *counter,
+	struct iio_counter_value *value, struct iio_counter_trigger *trigger)
+{
+	return trigger->mode;
+}
+
+static int dumcnt_value_read(struct iio_counter *counter,
+	struct iio_counter_value *value, int *val, int *val2)
+{
+	struct dumcnt *const priv = counter->driver_data;
+
+	*val = priv->counts[value->id];
+
+	return IIO_VAL_INT;
+}
+
+static int dumcnt_value_write(struct iio_counter *counter,
+	struct iio_counter_value *value, int val, int val2)
+{
+	struct dumcnt *const priv = counter->driver_data;
+
+	priv->counts[value->id] = val;
+
+	return 0;
+}
+
+static int dumcnt_value_function_set(struct iio_counter *counter,
+	struct iio_counter_value *value, unsigned int mode)
+{
+	if (mode >= value->num_function_modes)
+		return -EINVAL;
+
+	value->mode = mode;
+
+	return 0;
+}
+
+static int dumcnt_value_function_get(struct iio_counter *counter,
+	struct iio_counter_value *value)
+{
+	return value->mode;
+}
+
+static const struct iio_counter_ops dumcnt_ops = {
+	.signal_read = dumcnt_signal_read,
+	.signal_write = dumcnt_signal_write,
+	.trigger_mode_get = dumcnt_trigger_mode_get,
+	.trigger_mode_set = dumcnt_trigger_mode_set,
+	.value_read = dumcnt_value_read,
+	.value_write = dumcnt_value_write,
+	.value_function_set = dumcnt_value_function_set,
+	.value_function_get = dumcnt_value_function_get
+};
+
+static const char *const dumcnt_function_modes[] = {
+	"decrease",
+	"increase"
+};
+
+#define DUMCNT_SIGNAL(_id, _name) {	\
+	.id = _id,			\
+	.name = _name			\
+}
+
+static const struct iio_counter_signal dumcnt_signals[] = {
+	DUMCNT_SIGNAL(0, "Signal A"), DUMCNT_SIGNAL(1, "Signal B")
+};
+
+#define DUMCNT_VALUE(_id, _name) {					\
+	.id = _id,							\
+	.name = _name,							\
+	.mode = 0,							\
+	.function_modes = dumcnt_function_modes,			\
+	.num_function_modes = ARRAY_SIZE(dumcnt_function_modes)		\
+}
+
+static const struct iio_counter_value dumcnt_values[] = {
+	DUMCNT_VALUE(0, "Count A"), DUMCNT_VALUE(1, "Count B")
+};
+
+static const char *const dumcnt_trigger_modes[] = {
+	"none",
+	"rising edge",
+	"falling edge",
+	"both edges"
+};
+
+static int dumcnt_probe(struct platform_device *pdev)
+{
+	struct device *dev = &pdev->dev;
+	struct iio_counter_signal *signals;
+	const size_t num_signals = ARRAY_SIZE(dumcnt_signals);
+	struct iio_counter_value *values;
+	const size_t num_values = ARRAY_SIZE(dumcnt_values);
+	struct iio_counter_trigger *triggers;
+	int i;
+	struct dumcnt *dumcnt;
+
+	signals = devm_kmalloc(dev, sizeof(dumcnt_signals), GFP_KERNEL);
+	if (!signals)
+		return -ENOMEM;
+
+	memcpy(signals, dumcnt_signals, sizeof(dumcnt_signals));
+
+	values = devm_kmalloc(dev, sizeof(dumcnt_values), GFP_KERNEL);
+	if (!values)
+		return -ENOMEM;
+
+	memcpy(values, dumcnt_values, sizeof(dumcnt_values));
+
+	/* Associate values with their respective signals */
+	for (i = 0; i < num_values; i++) {
+		triggers = devm_kmalloc(dev, sizeof(*triggers), GFP_KERNEL);
+		if (!triggers)
+			return -ENOMEM;
+
+		triggers->mode = 0;
+		triggers->trigger_modes = dumcnt_trigger_modes;
+		triggers->num_trigger_modes = ARRAY_SIZE(dumcnt_trigger_modes);
+		triggers->signal = &signals[i];
+
+		values[i].triggers = triggers;
+		values[i].num_triggers = 1;
+	}
+
+	dumcnt = devm_kzalloc(dev, sizeof(*dumcnt), GFP_KERNEL);
+	if (!dumcnt)
+		return -ENOMEM;
+
+	dumcnt->counter.name = dev_name(dev);
+	dumcnt->counter.dev = dev;
+	dumcnt->counter.ops = &dumcnt_ops;
+	dumcnt->counter.signals = signals;
+	dumcnt->counter.num_signals = num_signals;
+	dumcnt->counter.values = values;
+	dumcnt->counter.num_values = num_values;
+	dumcnt->counter.driver_data = dumcnt;
+
+	return devm_iio_counter_register(dev, &dumcnt->counter);
+}
+
+static struct platform_device *dumcnt_device;
+
+static struct platform_driver dumcnt_driver = {
+	.driver = {
+		.name = "104-quad-8"
+	}
+};
+
+static void __exit dumcnt_exit(void)
+{
+	platform_device_unregister(dumcnt_device);
+	platform_driver_unregister(&dumcnt_driver);
+}
+
+static int __init dumcnt_init(void)
+{
+	int err;
+
+	dumcnt_device = platform_device_alloc(dumcnt_driver.driver.name, -1);
+	if (!dumcnt_device)
+		return -ENOMEM;
+
+	err = platform_device_add(dumcnt_device);
+	if (err)
+		goto err_platform_device;
+
+	err = platform_driver_probe(&dumcnt_driver, dumcnt_probe);
+	if (err)
+		goto err_platform_driver;
+
+	return 0;
+
+err_platform_driver:
+	platform_device_del(dumcnt_device);
+err_platform_device:
+	platform_device_put(dumcnt_device);
+	return err;
+}
+
+module_init(dumcnt_init);
+module_exit(dumcnt_exit);
+
+MODULE_AUTHOR("William Breathitt Gray <vilhelm.gray@gmail.com>");
+MODULE_DESCRIPTION("Dummy counter driver");
+MODULE_LICENSE("GPL v2");
-- 
2.14.1

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

* [PATCH v3 6/6] iio: 104-quad-8: Add IIO generic counter interface support
  2017-10-05 18:13 [PATCH v3 0/6] iio: Introduce the generic counter interface William Breathitt Gray
                   ` (4 preceding siblings ...)
  2017-10-05 18:14 ` [PATCH v3 5/6] iio: Add dummy counter driver William Breathitt Gray
@ 2017-10-05 18:14 ` William Breathitt Gray
  2017-10-08 13:44   ` Jonathan Cameron
  2017-10-08 14:38 ` [PATCH v3 0/6] iio: Introduce the generic counter interface Jonathan Cameron
  6 siblings, 1 reply; 17+ messages in thread
From: William Breathitt Gray @ 2017-10-05 18:14 UTC (permalink / raw)
  To: jic23, benjamin.gaignard, knaack.h, lars, pmeerw
  Cc: linux-iio, linux-kernel, William Breathitt Gray

This patch adds support for the IIO generic counter interface to the
104-QUAD-8 driver. The existing 104-QUAD-8 device interface should not
be affected by this patch; all changes are intended as supplemental
additions as perceived by the user.

IIO Counter Signals are defined for all quadrature input pairs
(A and B), as well as index input lines. However, IIO Counter Triggers
are not created for the index input Signals. IIO Counter Values are
created for the eight quadrature channel counts, and their respective
Signals are associated via IIO Counter Triggers.

The new generic counter interface sysfs attributes expose the same
functionality and data available via the existing 104-QUAD-8 device
interface. Four IIO Counter Value function modes are available,
correlating to the four possible quadrature mode configurations:
"non-quadrature," "quadrature x1," "quadrature x2," and "quadrature x4."

Signed-off-by: William Breathitt Gray <vilhelm.gray@gmail.com>
---
 drivers/iio/counter/104-quad-8.c | 294 ++++++++++++++++++++++++++++++++++++---
 1 file changed, 277 insertions(+), 17 deletions(-)

diff --git a/drivers/iio/counter/104-quad-8.c b/drivers/iio/counter/104-quad-8.c
index b56985078d8c..690a520e70df 100644
--- a/drivers/iio/counter/104-quad-8.c
+++ b/drivers/iio/counter/104-quad-8.c
@@ -16,6 +16,7 @@
 #include <linux/bitops.h>
 #include <linux/device.h>
 #include <linux/errno.h>
+#include <linux/iio/counter.h>
 #include <linux/iio/iio.h>
 #include <linux/iio/types.h>
 #include <linux/io.h>
@@ -24,6 +25,7 @@
 #include <linux/kernel.h>
 #include <linux/module.h>
 #include <linux/moduleparam.h>
+#include <linux/string.h>
 #include <linux/types.h>
 
 #define QUAD8_EXTENT 32
@@ -37,6 +39,7 @@ MODULE_PARM_DESC(base, "ACCES 104-QUAD-8 base addresses");
 
 /**
  * struct quad8_iio - IIO device private data structure
+ * @counter:		instance of the iio_counter
  * @preset:		array of preset values
  * @count_mode:		array of count mode configurations
  * @quadrature_mode:	array of quadrature mode configurations
@@ -48,6 +51,7 @@ MODULE_PARM_DESC(base, "ACCES 104-QUAD-8 base addresses");
  * @base:		base port address of the IIO device
  */
 struct quad8_iio {
+	struct iio_counter counter;
 	unsigned int preset[QUAD8_NUM_COUNTERS];
 	unsigned int count_mode[QUAD8_NUM_COUNTERS];
 	unsigned int quadrature_mode[QUAD8_NUM_COUNTERS];
@@ -527,33 +531,289 @@ static const struct iio_chan_spec quad8_channels[] = {
 	QUAD8_COUNT_CHAN(7), QUAD8_INDEX_CHAN(7)
 };
 
+static int quad8_signal_read(struct iio_counter *counter,
+	struct iio_counter_signal *signal, int *val, int *val2)
+{
+	struct quad8_iio *const priv = counter->driver_data;
+
+	if (signal->id < 16)
+		return -EINVAL;
+
+	*val = !!(inb(priv->base + 0x16) & BIT(signal->id - 16));
+
+	return IIO_VAL_INT;
+}
+
+static int quad8_trigger_mode_get(struct iio_counter *counter,
+	struct iio_counter_value *value, struct iio_counter_trigger *trigger)
+{
+	struct quad8_iio *const priv = counter->driver_data;
+	const unsigned int mode = priv->quadrature_mode[value->id];
+	const unsigned int scale = priv->quadrature_scale[value->id];
+	unsigned int direction;
+	const unsigned int flag_addr = priv->base + 2 * value->id + 1;
+	const int signal_id = trigger->signal->id % 2;
+
+	if (mode)
+		switch (scale) {
+		case 0:
+			/* U/D flag: 1 = up, 0 = down */
+			/* direction: 0 = up, 1 = down */
+			direction = !(inb(flag_addr) & BIT(5));
+			if (!signal_id)
+				return direction + 1;
+			break;
+		case 1:
+			if (!signal_id)
+				return 3;
+			break;
+		case 2:
+			return 3;
+		}
+	else
+		if (!signal_id)
+			return 1;
+
+	return 0;
+}
+
+static int quad8_value_read(struct iio_counter *counter,
+	struct iio_counter_value *value, int *val, int *val2)
+{
+	struct quad8_iio *const priv = counter->driver_data;
+	const int base_offset = priv->base + 2 * value->id;
+	unsigned int flags;
+	unsigned int borrow;
+	unsigned int carry;
+	int i;
+
+	flags = inb(base_offset + 1);
+	borrow = flags & BIT(0);
+	carry = !!(flags & BIT(1));
+
+	/* Borrow XOR Carry effectively doubles count range */
+	*val = (borrow ^ carry) << 24;
+
+	/* Reset Byte Pointer; transfer Counter to Output Latch */
+	outb(0x11, base_offset + 1);
+
+	for (i = 0; i < 3; i++)
+		*val |= (unsigned int)inb(base_offset) << (8 * i);
+
+	return IIO_VAL_INT;
+}
+
+static int quad8_value_write(struct iio_counter *counter,
+	struct iio_counter_value *value, int val, int val2)
+{
+	struct quad8_iio *const priv = counter->driver_data;
+	const int base_offset = priv->base + 2 * value->id;
+	int i;
+
+	/* Only 24-bit values are supported */
+	if ((unsigned int)val > 0xFFFFFF)
+		return -EINVAL;
+
+	/* Reset Byte Pointer */
+	outb(0x01, base_offset + 1);
+
+	/* Counter can only be set via Preset Register */
+	for (i = 0; i < 3; i++)
+		outb(val >> (8 * i), base_offset);
+
+	/* Transfer Preset Register to Counter */
+	outb(0x08, base_offset + 1);
+
+	/* Reset Byte Pointer */
+	outb(0x01, base_offset + 1);
+
+	/* Set Preset Register back to original value */
+	val = priv->preset[value->id];
+	for (i = 0; i < 3; i++)
+		outb(val >> (8 * i), base_offset);
+
+	/* Reset Borrow, Carry, Compare, and Sign flags */
+	outb(0x02, base_offset + 1);
+	/* Reset Error flag */
+	outb(0x06, base_offset + 1);
+
+	return 0;
+}
+
+static int quad8_value_function_set(struct iio_counter *counter,
+	struct iio_counter_value *value, unsigned int mode)
+{
+	struct quad8_iio *const priv = counter->driver_data;
+	const unsigned int mode_cfg = mode << 3 |
+		priv->count_mode[value->id] << 1;
+	const unsigned int idr_cfg = priv->index_polarity[value->id] << 1;
+	const int base_offset = priv->base + 2 * value->id + 1;
+
+	if (mode)
+		priv->quadrature_scale[value->id] = mode - 1;
+	else {
+		/* Quadrature scaling only available in quadrature mode */
+		priv->quadrature_scale[value->id] = 0;
+
+		/* Synchronous function not supported in non-quadrature mode */
+		if (priv->synchronous_mode[value->id]) {
+			priv->synchronous_mode[value->id] = 0;
+			outb(0x60 | idr_cfg, base_offset);
+		}
+	}
+
+	priv->quadrature_mode[value->id] = !!mode;
+
+	/* Load mode configuration to Counter Mode Register */
+	outb(0x20 | mode_cfg, base_offset);
+
+	return 0;
+}
+
+static int quad8_value_function_get(struct iio_counter *counter,
+	struct iio_counter_value *value)
+{
+	struct quad8_iio *const priv = counter->driver_data;
+	unsigned int quadrature_mode = priv->quadrature_mode[value->id];
+
+	return (quadrature_mode) ? priv->quadrature_scale[value->id] + 1 : 0;
+}
+
+static const struct iio_counter_ops quad8_ops = {
+	.signal_read = quad8_signal_read,
+	.trigger_mode_get = quad8_trigger_mode_get,
+	.value_read = quad8_value_read,
+	.value_write = quad8_value_write,
+	.value_function_set = quad8_value_function_set,
+	.value_function_get = quad8_value_function_get
+};
+
+static const char *const quad8_function_modes[] = {
+	"non-quadrature",
+	"quadrature x1",
+	"quadrature x2",
+	"quadrature x4"
+};
+
+#define QUAD8_SIGNAL(_id, _name) {	\
+	.id = _id,			\
+	.name = _name			\
+}
+
+static const struct iio_counter_signal quad8_signals[] = {
+	QUAD8_SIGNAL(0, "Channel 1 Quadrature A"),
+	QUAD8_SIGNAL(1, "Channel 1 Quadrature B"),
+	QUAD8_SIGNAL(2, "Channel 2 Quadrature A"),
+	QUAD8_SIGNAL(3, "Channel 2 Quadrature B"),
+	QUAD8_SIGNAL(4, "Channel 3 Quadrature A"),
+	QUAD8_SIGNAL(5, "Channel 3 Quadrature B"),
+	QUAD8_SIGNAL(6, "Channel 4 Quadrature A"),
+	QUAD8_SIGNAL(7, "Channel 4 Quadrature B"),
+	QUAD8_SIGNAL(8, "Channel 5 Quadrature A"),
+	QUAD8_SIGNAL(9, "Channel 5 Quadrature B"),
+	QUAD8_SIGNAL(10, "Channel 6 Quadrature A"),
+	QUAD8_SIGNAL(11, "Channel 6 Quadrature B"),
+	QUAD8_SIGNAL(12, "Channel 7 Quadrature A"),
+	QUAD8_SIGNAL(13, "Channel 7 Quadrature B"),
+	QUAD8_SIGNAL(14, "Channel 8 Quadrature A"),
+	QUAD8_SIGNAL(15, "Channel 8 Quadrature B"),
+	QUAD8_SIGNAL(16, "Channel 1 Index"),
+	QUAD8_SIGNAL(17, "Channel 2 Index"),
+	QUAD8_SIGNAL(18, "Channel 3 Index"),
+	QUAD8_SIGNAL(19, "Channel 4 Index"),
+	QUAD8_SIGNAL(20, "Channel 5 Index"),
+	QUAD8_SIGNAL(21, "Channel 6 Index"),
+	QUAD8_SIGNAL(22, "Channel 7 Index"),
+	QUAD8_SIGNAL(23, "Channel 8 Index")
+};
+
+#define QUAD8_VALUE(_id, _name) {					\
+	.id = _id,							\
+	.name = _name,							\
+	.mode = 0,							\
+	.function_modes = quad8_function_modes,				\
+	.num_function_modes = ARRAY_SIZE(quad8_function_modes)		\
+}
+
+static const struct iio_counter_value quad8_values[] = {
+	QUAD8_VALUE(0, "Channel 1 Count"), QUAD8_VALUE(1, "Channel 2 Count"),
+	QUAD8_VALUE(2, "Channel 3 Count"), QUAD8_VALUE(3, "Channel 4 Count"),
+	QUAD8_VALUE(4, "Channel 5 Count"), QUAD8_VALUE(5, "Channel 6 Count"),
+	QUAD8_VALUE(6, "Channel 7 Count"), QUAD8_VALUE(7, "Channel 8 Count")
+};
+
+static const char *const quad8_trigger_modes[] = {
+	"none",
+	"rising edge",
+	"falling edge",
+	"both edges"
+};
+
 static int quad8_probe(struct device *dev, unsigned int id)
 {
-	struct iio_dev *indio_dev;
-	struct quad8_iio *priv;
+	struct iio_counter_signal *signals;
+	const size_t num_signals = ARRAY_SIZE(quad8_signals);
+	struct iio_counter_value *values;
+	const size_t num_values = ARRAY_SIZE(quad8_values);
+	struct iio_counter_trigger *triggers;
+	struct quad8_iio *quad8iio;
 	int i, j;
 	unsigned int base_offset;
 
-	indio_dev = devm_iio_device_alloc(dev, sizeof(*priv));
-	if (!indio_dev)
-		return -ENOMEM;
-
-	if (!devm_request_region(dev, base[id], QUAD8_EXTENT,
-		dev_name(dev))) {
+	if (!devm_request_region(dev, base[id], QUAD8_EXTENT, dev_name(dev))) {
 		dev_err(dev, "Unable to lock port addresses (0x%X-0x%X)\n",
 			base[id], base[id] + QUAD8_EXTENT);
 		return -EBUSY;
 	}
 
-	indio_dev->info = &quad8_info;
-	indio_dev->modes = INDIO_DIRECT_MODE;
-	indio_dev->num_channels = ARRAY_SIZE(quad8_channels);
-	indio_dev->channels = quad8_channels;
-	indio_dev->name = dev_name(dev);
-	indio_dev->dev.parent = dev;
+	signals = devm_kmalloc(dev, sizeof(quad8_signals), GFP_KERNEL);
+	if (!signals)
+		return -ENOMEM;
+
+	memcpy(signals, quad8_signals, sizeof(quad8_signals));
+
+	values = devm_kmalloc(dev, sizeof(quad8_values), GFP_KERNEL);
+	if (!values)
+		return -ENOMEM;
+
+	memcpy(values, quad8_values, sizeof(quad8_values));
+
+	/* Associate values with their respective signals */
+	for (i = 0; i < num_values; i++) {
+		triggers = devm_kmalloc(dev, 2 * sizeof(*triggers), GFP_KERNEL);
+		if (!triggers)
+			return -ENOMEM;
+
+		/* Starts up in non-quadrature mode */
+		triggers[0].mode = 1;
+		triggers[0].trigger_modes = quad8_trigger_modes;
+		triggers[0].num_trigger_modes = ARRAY_SIZE(quad8_trigger_modes);
+		triggers[0].signal = &signals[2 * i];
+		triggers[1].mode = 0;
+		triggers[1].trigger_modes = quad8_trigger_modes;
+		triggers[1].num_trigger_modes = ARRAY_SIZE(quad8_trigger_modes);
+		triggers[1].signal = &signals[2 * i + 1];
+
+		values[i].triggers = triggers;
+		values[i].num_triggers = 2;
+	}
+
+	quad8iio = devm_kzalloc(dev, sizeof(*quad8iio), GFP_KERNEL);
+	if (!quad8iio)
+		return -ENOMEM;
 
-	priv = iio_priv(indio_dev);
-	priv->base = base[id];
+	quad8iio->counter.name = dev_name(dev);
+	quad8iio->counter.dev = dev;
+	quad8iio->counter.ops = &quad8_ops;
+	quad8iio->counter.signals = signals;
+	quad8iio->counter.num_signals = num_signals;
+	quad8iio->counter.values = values;
+	quad8iio->counter.num_values = num_values;
+	quad8iio->counter.channels = quad8_channels;
+	quad8iio->counter.num_channels = ARRAY_SIZE(quad8_channels);
+	quad8iio->counter.info = &quad8_info;
+	quad8iio->counter.driver_data = quad8iio;
+	quad8iio->base = base[id];
 
 	/* Reset all counters and disable interrupt function */
 	outb(0x01, base[id] + 0x11);
@@ -579,7 +839,7 @@ static int quad8_probe(struct device *dev, unsigned int id)
 	/* Enable all counters */
 	outb(0x00, base[id] + 0x11);
 
-	return devm_iio_device_register(dev, indio_dev);
+	return devm_iio_counter_register(dev, &quad8iio->counter);
 }
 
 static struct isa_driver quad8_driver = {
-- 
2.14.1

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

* Re: [PATCH v3 1/6] iio: Implement counter channel specification and IIO_SIGNAL constant
  2017-10-05 18:13 ` [PATCH v3 1/6] iio: Implement counter channel specification and IIO_SIGNAL constant William Breathitt Gray
@ 2017-10-08 11:57   ` Jonathan Cameron
  0 siblings, 0 replies; 17+ messages in thread
From: Jonathan Cameron @ 2017-10-08 11:57 UTC (permalink / raw)
  To: William Breathitt Gray
  Cc: benjamin.gaignard, knaack.h, lars, pmeerw, linux-iio, linux-kernel

On Thu,  5 Oct 2017 14:13:30 -0400
William Breathitt Gray <vilhelm.gray@gmail.com> wrote:

> Counters are IIO devices that are exposed via a generic counter
> interface consisting of one or more counter signals (IIO_SIGNAL) linked
> to one or more counter values (IIO_COUNT).
> 
> This patch introduces the IIO_SIGNAL constant which represents a counter
> device signal line. Additionally, a new "counter" member is added to
> struct iio_chan_spec, with relevant support, to indicate that a channel
> is part of a counter.
> 
> Signed-off-by: William Breathitt Gray <vilhelm.gray@gmail.com>

I have a feeling this support could be useful more generically
than just for counters.    There are other cases where a couple
of different input signals are combined to give one result and
we don't currently represent that combination.

A complex example would be differential channels where we have
some elements specific to the individual lines (the ability to
apply front end PGA type effects separately) - Not sure I've
seen one of these though ;)

Simpler case is light sensors - two light sensors are combined to
provide an illuminance output (one is typically all wavelengths and
one is infrared only - as we want to remove that from signal).

Some of these devices have multiple sensor inputs and right now
we don't make it clear which ones feed which illuminance values
and we should do.

Now this introduces another issue - those channels are indexed
and modified so already using both channel and channel2.

So... Do we ever need to represent provide the 'counter' element
in an event, (which is where we are short on space in the ABI)?
I don't think we do as channel is unique anyway (I think) so
this isn't a problem.

If not - define a new element to take this index rather than
overloading channel2 (again).

Jonathan

> ---
>  drivers/iio/industrialio-core.c | 14 +++++++++++++-
>  include/linux/iio/iio.h         |  2 ++
>  include/uapi/linux/iio/types.h  |  1 +
>  3 files changed, 16 insertions(+), 1 deletion(-)
> 
> diff --git a/drivers/iio/industrialio-core.c b/drivers/iio/industrialio-core.c
> index 7a5aa127c52e..ee508f2070a7 100644
> --- a/drivers/iio/industrialio-core.c
> +++ b/drivers/iio/industrialio-core.c
> @@ -85,6 +85,7 @@ static const char * const iio_chan_type_name_spec[] = {
>  	[IIO_COUNT] = "count",
>  	[IIO_INDEX] = "index",
>  	[IIO_GRAVITY]  = "gravity",
> +	[IIO_SIGNAL] = "signal",
>  };
>  
>  static const char * const iio_modifier_names[] = {
> @@ -989,7 +990,18 @@ int __iio_device_attr_init(struct device_attribute *dev_attr,
>  			break;
>  
>  		case IIO_SEPARATE:
> -			if (chan->indexed)
> +			if (chan->counter) {
> +				if (!chan->indexed) {
> +					WARN(1, "Counter channels must be indexed\n");
> +					ret = -EINVAL;
> +					goto error_free_full_postfix;
> +				}
> +				name = kasprintf(GFP_KERNEL, "%s%d-%d_%s",

Hmm. - has a meaning in IIO already - perhaps we need a different symbol to represent
this grouping concept?

> +						    iio_chan_type_name_spec[chan->type],
> +						    chan->channel,
> +						    chan->channel2,
> +						    full_postfix);
> +			} else if (chan->indexed)

Ultimately probably want to cover the various shared cases as well.

>  				name = kasprintf(GFP_KERNEL, "%s_%s%d_%s",
>  						    iio_direction[chan->output],
>  						    iio_chan_type_name_spec[chan->type],
> diff --git a/include/linux/iio/iio.h b/include/linux/iio/iio.h
> index 50cae8504256..9f949dd74b60 100644
> --- a/include/linux/iio/iio.h
> +++ b/include/linux/iio/iio.h
> @@ -263,6 +263,7 @@ struct iio_event_spec {
>   *			attributes but not for event codes.
>   * @output:		Channel is output.
>   * @differential:	Channel is differential.
> + * @counter:		Channel is part of a counter.
>   */
>  struct iio_chan_spec {
>  	enum iio_chan_type	type;
> @@ -295,6 +296,7 @@ struct iio_chan_spec {
>  	unsigned		indexed:1;
>  	unsigned		output:1;
>  	unsigned		differential:1;
> +	unsigned		counter:1;
>  };
>  
>  
> diff --git a/include/uapi/linux/iio/types.h b/include/uapi/linux/iio/types.h
> index ffafd6c25a48..313899652ca7 100644
> --- a/include/uapi/linux/iio/types.h
> +++ b/include/uapi/linux/iio/types.h
> @@ -43,6 +43,7 @@ enum iio_chan_type {
>  	IIO_COUNT,
>  	IIO_INDEX,
>  	IIO_GRAVITY,
> +	IIO_SIGNAL,
>  };
>  
>  enum iio_modifier {

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

* Re: [PATCH v3 3/6] iio: Documentation: Add IIO Generic Counter sysfs documentation
  2017-10-05 18:13 ` [PATCH v3 3/6] iio: Documentation: Add IIO Generic Counter sysfs documentation William Breathitt Gray
@ 2017-10-08 12:10   ` Jonathan Cameron
  0 siblings, 0 replies; 17+ messages in thread
From: Jonathan Cameron @ 2017-10-08 12:10 UTC (permalink / raw)
  To: William Breathitt Gray
  Cc: benjamin.gaignard, knaack.h, lars, pmeerw, linux-iio, linux-kernel

On Thu,  5 Oct 2017 14:13:59 -0400
William Breathitt Gray <vilhelm.gray@gmail.com> wrote:

> This patch adds standard documentation for the userspace sysfs
> atrributes of the IIO Generic Counter Interface.
> 
> Signed-off-by: William Breathitt Gray <vilhelm.gray@gmail.com>

It's a good start - but I think we need to expand on some elements.
I'm also worried that repurposing the - in here form being actual
subtraction in the differential channels is confusing.

> ---
>  .../testing/sysfs-bus-iio-generic-counter-sysfs    | 63 ++++++++++++++++++++++
>  1 file changed, 63 insertions(+)
>  create mode 100644 Documentation/ABI/testing/sysfs-bus-iio-generic-counter-sysfs
> 
> diff --git a/Documentation/ABI/testing/sysfs-bus-iio-generic-counter-sysfs b/Documentation/ABI/testing/sysfs-bus-iio-generic-counter-sysfs
> new file mode 100644
> index 000000000000..b2c09254ec5a
> --- /dev/null
> +++ b/Documentation/ABI/testing/sysfs-bus-iio-generic-counter-sysfs
> @@ -0,0 +1,63 @@
> +What:		/sys/bus/iio/devices/iio:deviceX/countX-Y_function
> +KernelVersion:	4.14
> +Contact:	linux-iio@vger.kernel.org
> +Description:
> +		Counter function mode for Value Y of Counter X; counter function
> +		evaluation triggered by conditions specified by the
> +		countX-Y_trigger_signalX-Z attributes.
> +
> +What:		/sys/bus/iio/devices/iio:deviceX/countX-Y_function_available
> +KernelVersion:	4.14
> +Contact:	linux-iio@vger.kernel.org
> +Description:
> +		Discrete set of available function modes for the configuration
> +		of the respective Value Y of Counter X are listed in this file.

Docs need to list what they can be.

> +
> +What:		/sys/bus/iio/devices/iio:deviceX/countX-Y_name
> +KernelVersion:	4.14
> +Contact:	linux-iio@vger.kernel.org
> +Description:
> +		Read-only attribute that indicates the device-specific name of
> +		Value Y of Counter X.
> +
> +What:		/sys/bus/iio/devices/iio:deviceX/countX-Y_raw
> +KernelVersion:	4.14
> +Contact:	linux-iio@vger.kernel.org
> +Description:
> +		Raw count data of Value Y of Counter X.
> +
> +What:		/sys/bus/iio/devices/iio:deviceX/countX-Y_triggers
> +KernelVersion:	4.14
> +Contact:	linux-iio@vger.kernel.org
> +Description:
> +		List of Signals associated to Value Y of Counter X.

That description doesn't seem to match.  This is triggers rather than signals.

> +
> +What:		/sys/bus/iio/devices/iio:deviceX/countX-Y_trigger_signalX-Z
> +KernelVersion:	4.14
> +Contact:	linux-iio@vger.kernel.org
> +Description:
> +		Trigger mode of Value Y for Signal Z. This attribute indicates
> +		the condition of Signal Z that triggers the counter function
> +		evaluation for Value Y. Both Value Y and Signal Z are part of
> +		Counter X.

> +
> +What:		/sys/bus/iio/devices/iio:deviceX/countX-Y_trigger_signalX-Z_available
> +KernelVersion:	4.14
> +Contact:	linux-iio@vger.kernel.org
> +Description:
> +		Discrete set of available trigger modes are listed in this file
> +		for the configuration of the respective Trigger associating
> +		Signal Z of Counter X with Value Y of Counter X.

it's another case where I think overloading the - is getting confusing. Not sure
what the right answer is though!

> +
> +What:		/sys/bus/iio/devices/iio:deviceX/signalX-Y_name
> +KernelVersion:	4.14
> +Contact:	linux-iio@vger.kernel.org
> +Description:
> +		Read-only attribute that indicates the device-specific name of
> +		Signal Y of Counter X.

What is this name for?  This doesn't feel like a counter specific thing, but
rather a generic IIO addition.

> +
> +What:		/sys/bus/iio/devices/iio:deviceX/signalX-Y_raw
> +KernelVersion:	4.14
> +Contact:	linux-iio@vger.kernel.org
> +Description:
> +		Raw signal data of Signal Y of Counter X.

While it might seem clunky - I'd prefer us to keep the channel direction
prefixes throughout.  So that means most of these would end up as
in_signalX-Y_raw etc.

As I mentioned before I think overloading the meaning of - is probably
a bad idea.  Not sure what the right option would be though.
Err.  Something like in_signalX<Y>_raw?  Or do we need to make it really
obvious and end up with something like.
in_signalX<CounterY>_raw - though that would require another level of
info beyond and index to describe that we are dealing with a counter
rather than a light channel for example.

I wonder if we would be better off not using the naming to reflect
the grouping at all, but rather add something like in_signalX_group

in_counterY_groupmembers which lists the members.  Are we trying
to stretch the interface too far in sticking to doing it in naming?
Not sure...

Jonathan

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

* Re: [PATCH v3 4/6] docs: Add IIO Generic Counter Interface documentation
  2017-10-05 18:14 ` [PATCH v3 4/6] docs: Add IIO Generic Counter Interface documentation William Breathitt Gray
@ 2017-10-08 13:19   ` Jonathan Cameron
  0 siblings, 0 replies; 17+ messages in thread
From: Jonathan Cameron @ 2017-10-08 13:19 UTC (permalink / raw)
  To: William Breathitt Gray
  Cc: benjamin.gaignard, knaack.h, lars, pmeerw, linux-iio, linux-kernel

On Thu,  5 Oct 2017 14:14:24 -0400
William Breathitt Gray <vilhelm.gray@gmail.com> wrote:

> This patch adds high-level documentation about the IIO Generic Counter
> Interface.
> 
> Signed-off-by: William Breathitt Gray <vilhelm.gray@gmail.com>

Put it into the relevant index.rst to get and make it an rst file so it
gets built as part of the sphinx docs.

> ---
>  Documentation/driver-api/iio/generic-counter.txt | 526 +++++++++++++++++++++++
>  1 file changed, 526 insertions(+)
>  create mode 100644 Documentation/driver-api/iio/generic-counter.txt
> 
> diff --git a/Documentation/driver-api/iio/generic-counter.txt b/Documentation/driver-api/iio/generic-counter.txt
> new file mode 100644
> index 000000000000..0ce43b71e1c7
> --- /dev/null
> +++ b/Documentation/driver-api/iio/generic-counter.txt
> @@ -0,0 +1,526 @@
> +=========================
> +Generic Counter Interface
> +=========================
> +
> +Introduction
> +============
> +
> +Counter devices are prevalent within a diverse spectrum of industries.
> +The ubiquitous presence of these devices necessitates a common interface
> +and standard of interaction and exposure. This driver API attempts to
> +resolve the issue of duplicate code found among existing counter device
> +drivers by introducing a generic counter interface for consumption. The
> +generic counter interface enables drivers to support and expose a common
> +set of components and functionality present in counter devices.
> +
> +Theory
> +======
> +
> +Counter devices can vary greatly in design, but regardless of whether
> +some devices are quadrature encoder counters or pedometers, all counter
> +devices consist of a core set of components. This core set of
> +components, shared by all counter devices, is what forms the essence of
> +the generic counter interface.

Hmm. Whether it makes sense to move over pedometers is a bit of an open 
question... They tend to be fairly opaque wrt to where their data is
coming from.  We'll see, but I'd kind of prefer we keep it out of the
docs until that case has been fully analysed!

> +
> +There are three core components to a counter:
> +
> +        VALUE
> +        -----
> +        A Value represents the count data for a set of Signals. A Value
> +        has a count function mode (e.g. "increment" or "quadrature x4")
> +        which respresents the update behavior for the count data. A
> +        Value also has a set of one or more associated Signals.

The whole set of valid function modes needs to be documented somewhere.
I don't think it currently is (but I may have missed it and haven't
read all the patches yet!)

Interesting terminology - this Value is what most people would think
of as a counter.  Can we just call it that?  The issue is that we
are thinking from the hardware point of view that says a 'counter block'
can have multiple counters so value is just one of them. I'm not sure
we want to present that terminology to userspace.


> +
> +        SIGNAL
> +        ------
> +        A Signal represents a count input line. A Signal may be
> +	associated to one or more Values.
> +
> +        TRIGGER
> +	-------
> +        A Trigger represents a Value's count function trigger condition
> +        mode (e.g. "rising edge" or "double pulse") for an associated
> +        Signal.	If a Signal is associated with a Value, a respective
> +        Trigger	instance for that association exists -- albeit perhaps
> +        with a trigger condition mode of "none."

Again, we need to enumerate the full acceptable list.  Enforcing it can
be by review, but we need a list that people can refer to or we will get
multiple drivers doing subtly different variants.

> +
> +A counter is defined as a set of input signals associated to count data
> +that are generated by the evaluation of the state of the associated
> +input signals as defined by the respective count functions. Within the
> +context of the generic counter interface, a counter consists of Values
> +each associated to a set of Signals, whose respective Trigger instances
> +represent the count function update conditions for the associated
> +Values.

Could we call this a 'counter group' or 'counter device' or something to
free the term counter up for a single instance of what is currently
called value.

> +
> +Paradigm
> +========
> +
> +The most basic counter device may be expressed as a single Value
> +associated with a single Signal via a single Trigger. Take for example
> +a hypothetical counter device which simply accumulates a count of rising
> +edges on a source input line.

Drop hypothetical - this is a perfectly real example. Plenty of hardware
out there does this!

> +
> +        Value                Trigger        Signal
> +        -----                -------        ------
> ++---------------------+
> +| Data: Count         |    Rising Edge     ________
> +| Function: Increment |  <-------------   / Source \
> +|                     |                  ____________
> ++---------------------+
> +
> +In this example, the Signal is a source input line with a pulsing
> +voltage, while the Value is a persistent count which increments. The
> +Signal is associated with the Value via a respective Trigger. The
> +increment function is triggered by the condition specified by the
> +Triggered -- in this case a rising edge condition. In summary, the
> +counter device existence and behavior is aptly represented by respective
> +Value, Signal, and Trigger components: a rising edge condition triggers
> +an incrementation function on an accumulating count datum.
> +
> +A counter device is not limited to a single Signal; in fact, in theory
> +an unlimited number of Signals may be associated with a Value.
Change that that to many signals.  Otherwise someone pedantic will say
it's limited by the implementation to 2^32 or similar..
> For
> +example, a quadrature encoder counter device can keep track of position
> +based on the states of two input lines.
> +
> +           Value                 Trigger      Signal
> +           -----                 -------      ------
> ++-------------------------+
> +| Data: Position          |    Both Edges      ___
> +| Function: Quadrature x4 |  <-------------   / A \
> +|                         |                  _______
> +|                         |
> +|                         |    Both Edges      ___
> +|                         |  <-------------   / B \
> +|                         |                  _______
> ++-------------------------+
> +
> +In this example, two Signals (quadrature encoder lines A and B) are
> +associated to a single Value: a rising or falling edge on either A or B
> +triggers the "Quadrature x4" function which determines the direction of
> +movement and updates the respective position data. The "Quadrature x4"
> +function is likely implemented in the hardware of the quadrature encoder
> +counter device; the Value, Triggers, and Signals simply represent this
> +hardware behavior and functionality.

Interesting that there is a relationship between valid trigger settings
and valid Value:Function settings.

> +
> +Signal associated to the same Value can have differing trigger
> +conditions. For example, a quadrature encoder counter device operating
> +in a non-quadrature mode could have one input line dedicated for
> +movement and a second input line dedicated for direction.
> +
> +           Value                  Trigger      Signal
> +           -----                  -------      ------
> ++------------------------- +
> +| Data: Position           |    Rising Edge     ___
> +| Function: Non-quadrature |  <-------------   / A \ (Movement)
> +|                          |                  _______
> +|                          |
> +|                          |       None         ___
> +|                          |  <-------------   / B \ (Direction)
> +|                          |                  _______
> ++--------------------------+

Non-quadrature is a pretty odd way of defining it!  I'd suggest
something more positive - pulse-direction would be the standard
terminology.

> +
> +Only Signal A triggers the "Non-quadrature" update function, but the
> +state of Signal B is still required in order to know the direction in
> +order to properly update the position data. So in the end, both Signals
> +are associated to the same Value via two respective Triggers, but only
> +one Trigger has an active trigger condition while the other is left in a
> +"None" condition mode to indicate its respective Signal's availability
> +for state evaluation despite its non-triggering mode.

Interesting - there sill be some confusion around the quadrature cases
though as the 'value' will only change on say rising edge A, but the
internal state machine is tracking the previous changes of edge B.  I'd
suggest we add a note about that somewhere here.

> +
> +Although the examples thus far have been representations of physical
> +devices, this is not a necessity. A counter simply represent the
> +evaluation (Value) of input data (Signals) triggered by specific
> +conditions (Triggers). A counter can be the representation of more
> +abstract components.
> +
> +For example, suppose a counter representation is desired for a DNA
> +sequence analysizer which detects possible genetic diseases.
> +
> +        Value                     Trigger           Signal
> +        -----                     -------           ------
> ++---------------------+
> +| Data: Diseases      |    Gene Transcript (EST)     _____
> +| Function: Cancers   |  <-----------------------   / DNA \ (GAAGTGC...)
> +|                     |                            _________
> ++---------------------+
> +
> +In this scenario, the Signal is a stream of DNA nucleotide bases (As,
> +Ts, Cs, and Gs), the Trigger is expressed sequence tags (ESTs), and the
> +Value is a list of diseases discovered (in this case the function is
> +evaluating for possible cancers). Note how the Signal in this example
> +does not represent a physical voltage line, nor does the Trigger
> +represent a physical voltage line state change, nor does the Value
> +represent a strictly decimal data value.
> +
> +The DNA sequence analysizer example is contrived to illustrate the
> +flexibility of the generic counter paradigm by demonstrating its
> +capability of representing abstract concepts; however, physical devices
> +are likely to be more fitting for such a representation.
> +
> +The key concept is that the Signal, Trigger, and Value are abstract
> +representations which do not need to be closely married to their
> +respective physical sources. This allows the user of a counter to
> +divorce themselves from the nuances of physical components (such as
> +whether an input line is differential or single-ended) and focus on the
> +core idea of what the data and process represent (an accumulated count
> +of rising edges).
> +
> +Userspace Interface
> +===================
> +
> +Several sysfs attributes are generated by the generic counter interface,
> +and reside under the /sys/bus/iio/devices/iio:deviceX directory.
> +
> +Each counter has a respective set of countX-Y and signalX-Y prefixed
> +attributes, where X is the id set in the counter structure, and Y is the
> +id of the respective Value or Signal.
> +
> +The generic counter interface sysfs attributes are as follows:
> +
> +        countX-Y_function: count function mode
> +        countX-Y_function_available: available count function modes
> +        countX-Y_name: Value name
> +        countX-Y_raw: Value data
> +        countX-Y_triggers: Value's associated Signals
> +        countX-Y_trigger_signalX-Z: Value Y trigger mode for Signal Z
> +        countX-Y_trigger_signalX-Z_available: available Value Y trigger
> +                                              modes for Signal Z
> +        signalX-Y_name: Signal name
I mentioned this in one of the other patches - This naming stuff feels
like, if we want to do it we should do it throughout IIO.

Possibly it maps to the data_sheet_name.. Higher level information beyond
this device would to my mind require explicit representation of devices
beyond this - i.e. what is attached to the wires that is generating
things to count.

> +        signalX-Y_raw: Signal data
> +
> +Through these sysfs attributes, programs and scripts may interact with
> +the generic counter paradigm Values, Triggers, and Signals of respective
> +counter devices.
> +
> +Driver API
> +==========
> +
> +Driver authors may utilize the generic counter interface in their code
> +by including the include/linux/iio/counter.h header file. This header
> +file provides several core data structures and function prototypes for
> +defining a generic counter.
> +
> +struct iio_counter_signal
> +-------------------------
> +
> +This structure defines a generic counter paradigm Signal component;
> +typically this will correlate with an input channel on a physical
> +counter device. This structure is the simplest to define with only two
> +structure members which require explicit configuration:
> +
> +        id:     Unique ID used to identify the Signal
> +
> +        name:   Device-specific Signal name (typically the device input
> +                channel name)
> +
> +struct iio_counter_trigger
> +--------------------------
> +
> +This structure defines a generic counter paradigm Trigger component. To
> +properly utilize this structure, trigger modes and an associated Signal
> +must be defined:
> +
> +        mode:                   Index of the current trigger mode state
> +
> +        trigger_modes:          Array of trigger modes each represented
> +	                        by a character string

As mentioned before these have to be explicitly listed somewhere.  They
form userspace ABI.  We must not leave this empty.  Defining these
cleanly and generically may be one of the hardest aspects to agree
on.

> +
> +        num_trigger_modes:      Number of trigger modes provided in
> +	                        trigger_modes array
> +
> +        signal:                 Pointer to associated Signal
> +
> +struct iio_counter_value
> +------------------------
> +
> +This structure defines a generic counter paradigm Value component;
> +typically this will correlate with the read data (the "count" value)
> +provided by a physical counter device. This structure requires the
> +explicit configuration of an ID, name, function modes (the function
> +triggered on a Trigger condition), and optionally a set of initial
> +associated Triggers:
> +
> +        id:                     Unique ID used to identify the Signal
> +
> +        name:                   Device-specific Value name (typically
> +	                        the device read channel name)
> +
> +        mode:                   Index of the current function mode state
> +
> +        function_modes:         Array of function modes each represented
> +	                        by a character string
> +
> +        num_function_modes:     Number of function modes provided in
> +	                        function_modes array
> +
> +        triggers:               Array of associated Triggers
> +
> +        num_triggers:           Number of Triggers provided in triggers
> +	                        array

It might also be convenient to have a flag in here to say if the triggers
are fixed or not.

> +
> +struct iio_counter_ops
> +----------------------

I'd suggest you push these details down in to kernel-doc in the code
and pull it back in here using the relevant bits of markup.
That stuff is a bit clunky but any docs in here of actual elements
etc will probably rot unedited if stuff changes.

> +
> +This structure defines callbacks to interact with the Value, Trigger,
> +and Signal components:
> +
> +        signal_read:            Function to request a signal value from
> +	                        the device. Return value will specify
> +				the type of value returned by the
> +				device. val and val2 will contain the
> +				elements making up the returned value.

Mention IIO_VAL_INT type return values as well.

> +
> +	signal_write:           Function to write a signal value to the
> +	                        device. Parameters are interpreted the
> +				same as signal_read.
> +
> +        trigger_mode_set:       Function to set the trigger mode. mode
> +	                        is the index of the requested mode from
> +				the value trigger_modes array.
> +
> +        trigger_mode_get:       Function to get the current trigger
> +	                        mode. Return value will specify the
> +				index of the current mode from the value
> +				trigger_modes array.
> +
> +        value_read:             Function to request a value value from
> +	                        the device. Return value will specify
> +				the type of value returned by the
> +				device. val and val2 will contain the
> +				elements making up the returned value.

This is the counter I think?  Value is rather vague, counter_read
will be less so.

> +
> +        value_write:            Function to write a value value to the
> +	                        device. Parameters are interpreted the
> +				same as value_read.
> +
> +        value_function_set:     Function to set the value function mode.
> +	                        mode is the index of the requested mode
> +				from the value function_modes array.
> +
> +        value_function_get:     Function to get the current value
> +	                        function mode. Return value will specify
> +				the index of the current mode from the
> +				value function_modes array.
> +
> +struct iio_counter
> +------------------
> +
> +This is the main data structure for a counter device; access to all
> +respective Values, Triggers, and Signals is possible from this
> +structure. This structure allows the configuration of an ID, name,
> +function callbacks, initial Signals and initial Values, auxiliary IIO
> +core channels and callbacks, and driver-specific data:
> +
> +        id:                     Unique ID used to identify the counter
> +
> +        name:                   Name of the counter device
> +
> +        dev:                    Device structure, which should be
> +				assigned a parent and owner
> +
> +        ops:                    Function callbacks for counter
> +	                        components (Signal, Trigger, Value)
> +
> +        signals:                Array of Signals
> +
> +        num_signals:            Number of Signals specified in signals
> +	                        array
> +
> +        values:                 Array of Values
> +
> +        num_values:             Number of Values specified in values
> +	                        array
> +
> +        channels:               Optional IIO core channels specification
> +	                        structure table
> +
> +        num_channels:           Number of channels specified in channels
> +
> +        info:                   IIO core function callbacks and constant
> +	                        info from driver
> +
> +        driver_data:            Driver-specific data
> +
> +Registration functions
> +----------------------
> +
> +Counters may be registered to the system via the iio_counter_register
> +function and subsequently unregistered via the iio_counter_unregister
> +function. The devm_iio_counter_register and devm_iio_counter_unregister
> +functions serve as device memory-managed versions of the
> +iio_counter_register and iio_counter_unregister functions.
> +
> +An initialized iio_counter structure, which defines the Counter, is
> +required to be passed in for registration; the Signals, Values, and
> +Triggers for the Counter passed in via the signals and values arrays as
> +part of the iio_counter structure. If auxiliary IIO core channels and
> +functionality are required, IIO core channels and callbacks may be
> +passed in via the channels and info members of the passed-in iio_counter
> +structure.
> +
> +Implementation
> +==============
> +
> +The IIO generic counter interface piggybacks off of the IIO core. This
> +is primarily used to leverage the existing sysfs setup: the IIO_COUNT
> +channel attributes represent the Counter Value, while the IIO_SIGNAL
> +channel attributes represent the Counter Signal; auxilary IIO_COUNT
> +attributes represent the Counter Signal connections (Triggers) and their
> +respective state change configurations which trigger an associated
> +"counter function" evaluation.

If it makes more sense I wouldn't mind changing the form of that setup
code to be more generic so you can use it without jumping through so many
hoops.

> +
> +The iio_counter_ops structure serves as a container for driver callbacks
> +to communicate with the device; function callbacks are provided to read
> +and write various Signals and Values, and set and get the "trigger mode"
> +and "function mode" for various Triggers and Values respectively.
> +
> +To support a counter device, a driver must first allocate the available
> +Counter Signals via iio_counter_signal structures. These Signals should
> +be stored as an array and set to the signals array member of an
> +allocated iio_counter structure before the Counter is registered to the
> +system.
> +
> +Counter Values may be allocated via iio_counter_value structures, and
> +respective Counter Signal associations (Triggers) made via
> +iio_counter_trigger structures. Associated iio_counter_trigger
> +structures are stored as an array and set to the the triggers array
> +member of the respective iio_counter_value structure. These
> +iio_counter_value structures are set to the values array member of an
> +allocated iio_counter structure before the Counter is registered to the
> +system.
> +
> +A counter device is registered to the system by passing the respective
> +initialized iio_counter structure to the iio_counter_register function;
> +similarly, the iio_counter_unregister function unregisters the
> +respective counter.
> +
> +Architecture
> +============
> +
> +Although the IIO Generic Counter Interface utilizes IIO core under the
> +hood, driver authors are not necessarily required to interact with IIO
> +core data structures and functions directly -- in theory, such details
> +of the system are abstracted away. Driver authors only need to concern
> +themselves with the Generic Counter specific data structures and
> +functions found in the include/linux/iio/counter.h header file.
> +
> +In other words, the driver API is intended to expose itself sufficiently
> +upon the principles and concepts of the generic counter paradigm (i.e.
> +Values, Triggers, Signals, etc.) such that it may be indepedent from its
> +underlying implementation; theoretically, the IIO core code in the
> +implementation could be replaced away in its entirely by an alternative
> +implementation all without the need to update existing drivers utilizing
> +the Generic Counter Interface driver API.

Good.  I hope it's not just in theory ;)  May well make sense in the future
as in particular IIO may get based on some new core infrastructure itself.

> +
> +This paradigm separation however does result in some mapping concerns
> +between Generic Counter functions to IIO core functions; in particular,
> +parameters for the IIO core functions expect IIO core data structures
> +(e.g. iio_dev and iio_chan_spec) which are not provided directly by the
> +parameters for the respective Generic Counter functions. This results in
> +a somewhat opaque pathway from a iio_counter structure to its associated
> +iio_dev in order to support the required IIO core calls.
> +
> +The following call graphs should help illustrate some of the main IIO
> +core dependencies:
> +
> ++----------------------+
> +| iio_counter_register |
> ++----------------------+
> +  |  |  |
> +  |  |  +-----------------------------+
> +  |  +------------------+             |
> +  |                     |             |
> +  V                     V             V
> ++------------------+  +----------+  +---------------------+
> +| iio_device_alloc |  | iio_priv |  | iio_device_register |
> ++------------------+  +----------+  +---------------------+
> +
> +The iio_counter_register function allocates and initializes a new
> +iio_dev structure which will serve as the respective Counter's gateway
> +to IIO core support. The address of the parent iio_counter structure is
> +stored with the iio_dev structure via iio_priv in order to allow access
> +back to the Counter from within the IIO core functions. Finally, the
> +iio_dev structure is registered via iio_device_register.
> +
> ++-----------------------+   +----------------------+
> +| iio_read_channel_info |-->| iio_counter_read_raw |
> ++-----------------------+   +----------------------+
> +                              |  |  |
> +  +---------------------------+  |  |
> +  |                +-------------+  |
> +  |                |               ++
> +  |                |               |
> +  V                V               V
> +  IIO_SIGNAL       IIO_COUNT       IIO_*
> ++-------------+  +------------+  +----------+
> +| signal_read |  | value_read |  | read_raw |
> ++-------------+  +------------+  +----------+
> +
> ++------------------------+   +-----------------------+
> +| iio_write_channel_info |-->| iio_counter_write_raw |
> ++------------------------+   +-----------------------+
> +                               |  |  |
> +  +----------------------------+  |  |
> +  |                 +-------------+  |
> +  |                 |                |
> +  |                 |                |
> +  V                 V                V
> +  IIO_SIGNAL        IIO_COUNT        IIO_*
> ++--------------+  +-------------+  +-----------+
> +| signal_write |  | value_write |  | write_raw |
> ++--------------+  +-------------+  +-----------+
> +
> +Normally, the IIO core iio_read_channel_info and iio_write_channel_info
> +functions respectiveluy call the driver-supplied read_raw and write_raw
> +callbacks directly. Since the generic counter interface serves as an
> +abstraction above IIO core, drive authors do not generally directly
> +configure a read_raw/write_raw callback.
> +
> +Instead, the IIO Generic Counter Interface hooks on to the
> +iio_read_channel_info and iio_write_channel_info expected read_raw and
> +write_raw callbacks respectively via iio_counter_read_raw and
> +iio_counter_write_raw. The iio_counter_read_raw and
> +iio_counter_write_raw functions then call the respective driver-supplied
> +signal_read/value_read and signal_write/value_write callbacks
> +respectively for the appropriate IIO_SIGNAL OR IIO_COUNT. If an IIO core
> +channel that was not part of the generic counter paradigm was supplied
> +via the channels member of the iio_counter structure, then the
> +respective driver-supplied (via the iio_counter structure info member)
> +read_raw and write_raw are called.
> +
> ++---------------------------+        +----------------------------+
> +| iio_read_channel_ext_info |        | iio_write_channel_ext_info |
> ++---------------------------+        +----------------------------+
> +  |                                    |
> +  V                                    V
> ++-------------------------------+    +--------------------------------+
> +| iio_counter_trigger_mode_read |    | iio_counter_trigger_mode_write |
> ++-------------------------------+    +--------------------------------+
> +  |                                    |
> +  V                                    V
> ++------------------+                 +------------------+
> +| trigger_mode_get |                 | trigger_mode_set |
> ++------------------+                 +------------------+
> +
> ++---------------+                     +----------------+
> +| iio_enum_read |                     | iio_enum_write |
> ++---------------+                     +----------------+
> +  |                                     |
> +  V                                     V
> ++--------------------------------+    +--------------------------------+
> +| iio_counter_value_function_get |    | iio_counter_value_function_set |
> ++--------------------------------+    +--------------------------------+
> +  |                                     |
> +  V                                     V
> ++--------------------+                +--------------------+
> +| value_function_get |                | value_function_set |
> ++--------------------+                +--------------------+
> +
> +The driver-supplied trigger_mode_get and trigger_mode_set callbacks hook
> +on to the iio_read_channel_ext_info and iio_write_channel_ext_info
> +functions respectively via the iio_counter_trigger_mode_read and
> +iio_counter_trigger_mode_write functions. Similarly, the driver-supplied
> +value_function_get and value_function set callbacks hook on to the
> +iio_enum_read and iio_enum_write functions respectively via the
> +iio_counter_value_function_get and iio_counter_value_function set
> +functions.

I'm inclined to agree with you when you suggest that separating things
out more may well make sense.

This all seems way to involved just to share a relatively small amount
of code.

The question is whether we loose anything particularly useful if
we drop most of the complexity.  If it makes sense for a counter
to sit on the IIO bus in sysfs that's easily done (triggers in IIO
for example do that already).

The main advantage then becomes one of grouping 'somewhat' related
subsystems together...  Can also share some utility code given
the overall structures are somewhat similar of course.

Not sure...

Jonathan

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

* Re: [PATCH v3 5/6] iio: Add dummy counter driver
  2017-10-05 18:14 ` [PATCH v3 5/6] iio: Add dummy counter driver William Breathitt Gray
@ 2017-10-08 13:41   ` Jonathan Cameron
  2017-10-09 12:35     ` Benjamin Gaignard
  0 siblings, 1 reply; 17+ messages in thread
From: Jonathan Cameron @ 2017-10-08 13:41 UTC (permalink / raw)
  To: William Breathitt Gray
  Cc: benjamin.gaignard, knaack.h, lars, pmeerw, linux-iio, linux-kernel

On Thu,  5 Oct 2017 14:14:38 -0400
William Breathitt Gray <vilhelm.gray@gmail.com> wrote:

> This patch introduces the dummy counter driver. The dummy counter driver
> serves as a reference implementation of a driver that utilizes the
> Generic Counter interface.

This is great - I was planning to write one of these to try out the
interface and you've already done it :)
> 
> Writing individual '1' and '0' characters to the Signal attributes
> allows a user to simulate a typical Counter Signal input stream for
> evaluation; the Counter will evaluate the Signal data based on the
> respective trigger mode for the associated Signal, and trigger the
> associated counter function specified by the respective function mode.
> The current Value value may be read, and the Value value preset by a
> write.
> 
> Signed-off-by: William Breathitt Gray <vilhelm.gray@gmail.com>

Comments are more generic suggestions for improving the example than
general comments on the ABI - that all seems to make reasonable sense
(other than when the documents contain the wonderful
Value value - not confusing at all ;)

> ---
>  drivers/iio/counter/Kconfig         |  15 ++
>  drivers/iio/counter/Makefile        |   1 +
>  drivers/iio/counter/dummy-counter.c | 293 ++++++++++++++++++++++++++++++++++++
>  3 files changed, 309 insertions(+)
>  create mode 100644 drivers/iio/counter/dummy-counter.c
> 
> diff --git a/drivers/iio/counter/Kconfig b/drivers/iio/counter/Kconfig
> index c8becfe78e28..494aed40e9c9 100644
> --- a/drivers/iio/counter/Kconfig
> +++ b/drivers/iio/counter/Kconfig
> @@ -22,6 +22,21 @@ config 104_QUAD_8
>  	  The base port addresses for the devices may be configured via the base
>  	  array module parameter.
>  
> +config DUMMY_COUNTER
> +	tristate "Dummy counter driver"
> +	help
> +	  Select this option to enable the dummy counter driver. The dummy
> +	  counter driver serves as a reference implementation of a driver that
> +	  utilizes the Generic Counter interface.
> +
> +	  Writing individual '1' and '0' characters to the Signal attributes
> +	  allows a user to simulate a typical Counter Signal input stream for
> +	  evaluation; the Counter will evaluate the Signal data based on the
> +	  respective trigger mode for the associated Signal, and trigger the
> +	  associated counter function specified by the respective function mode.
> +	  The current Value value may be read, and the Value value preset by a
> +	  write.
> +
>  config STM32_LPTIMER_CNT
>  	tristate "STM32 LP Timer encoder counter driver"
>  	depends on MFD_STM32_LPTIMER || COMPILE_TEST
> diff --git a/drivers/iio/counter/Makefile b/drivers/iio/counter/Makefile
> index 1b9a896eb488..8c2ef0115426 100644
> --- a/drivers/iio/counter/Makefile
> +++ b/drivers/iio/counter/Makefile
> @@ -5,4 +5,5 @@
>  # When adding new entries keep the list in alphabetical order
>  
>  obj-$(CONFIG_104_QUAD_8)	+= 104-quad-8.o
> +obj-$(CONFIG_DUMMY_COUNTER)	+= dummy-counter.o
>  obj-$(CONFIG_STM32_LPTIMER_CNT)	+= stm32-lptimer-cnt.o
> diff --git a/drivers/iio/counter/dummy-counter.c b/drivers/iio/counter/dummy-counter.c
> new file mode 100644
> index 000000000000..6ecc9854894f
> --- /dev/null
> +++ b/drivers/iio/counter/dummy-counter.c
> @@ -0,0 +1,293 @@
> +/*
> + * Dummy counter driver
> + * Copyright (C) 2017 William Breathitt Gray
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License, version 2, as
> + * published by the Free Software Foundation.
> + *
> + * This program is distributed in the hope that it will be useful, but
> + * WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
> + * General Public License for more details.
> + */
> +#include <linux/device.h>
> +#include <linux/errno.h>
> +#include <linux/iio/counter.h>
> +#include <linux/kernel.h>
> +#include <linux/module.h>
> +#include <linux/platform_device.h>
> +#include <linux/types.h>
> +
> +#define DUMCNT_NUM_COUNTERS 2
> +/**
> + * struct dumcnt - private data structure
> + * @counter:	instance of the iio_counter
> + * @counts:	array of accumulation values
> + * @states:	array of input line states
> + */
> +struct dumcnt {
> +	struct iio_counter counter;
> +	unsigned int counts[DUMCNT_NUM_COUNTERS];
> +	unsigned int states[DUMCNT_NUM_COUNTERS];
> +};
> +
> +static int dumcnt_signal_read(struct iio_counter *counter,
> +	struct iio_counter_signal *signal, int *val, int *val2)
> +{
> +	struct dumcnt *const priv = counter->driver_data;
> +	*val = priv->states[signal->id];
> +
> +	return IIO_VAL_INT;
> +}
> +
> +static int dumcnt_signal_write(struct iio_counter *counter,
> +	struct iio_counter_signal *signal, int val, int val2)
> +{

This is an odd one to have in the generic interface.
What real hardware does writing the state make sense for?
If it is only fake drivers - figure out a way to do it without
having to add to the generic interfaces.

> +	struct dumcnt *const priv = counter->driver_data;
> +	const unsigned int id = signal->id;
> +	const unsigned int prev_state = priv->states[id];
> +	struct iio_counter_value *const value = counter->values + id;
> +	const unsigned int function_mode = value->mode;
> +	const unsigned int trigger_mode = value->triggers[0].mode;
> +	unsigned int triggered = 0;
> +
> +	if (val && val != 1)
> +		return -EINVAL;
> +
> +	/* If no state change then just exit */
> +	if (prev_state == val)
> +		return 0;
> +
> +	priv->states[id] = val;
> +
> +	switch (trigger_mode) {
> +	/* "none" case */
> +	case 0:
> +		return 0;
> +	/* "rising edge" case */
> +	case 1:
> +		if (!prev_state)
> +			triggered = 1;
> +		break;
> +	/* "falling edge" case */
> +	case 2:
> +		if (prev_state)
> +			triggered = 1;
> +		break;
> +	/* "both edges" case */
> +	case 3:
> +		triggered = 1;
> +		break;
> +	}
> +
> +	/* If counter function triggered */
> +	if (triggered)
> +		/* "increase" case */
> +		if (function_mode)
> +			priv->counts[id]++;
> +		/* "decrease" case */
> +		else
> +			priv->counts[id]--;
> +
> +	return 0;
> +}
> +
> +static int dumcnt_trigger_mode_set(struct iio_counter *counter,
> +	struct iio_counter_value *value, struct iio_counter_trigger *trigger,
> +	unsigned int mode)
> +{
> +	if (mode >= trigger->num_trigger_modes)
> +		return -EINVAL;
> +
> +	trigger->mode = mode;
> +
> +	return 0;
> +}
> +
> +static int dumcnt_trigger_mode_get(struct iio_counter *counter,
> +	struct iio_counter_value *value, struct iio_counter_trigger *trigger)
> +{
> +	return trigger->mode;
> +}
> +
> +static int dumcnt_value_read(struct iio_counter *counter,
> +	struct iio_counter_value *value, int *val, int *val2)
> +{
> +	struct dumcnt *const priv = counter->driver_data;
> +
> +	*val = priv->counts[value->id];
> +
> +	return IIO_VAL_INT;
> +}
> +
> +static int dumcnt_value_write(struct iio_counter *counter,
> +	struct iio_counter_value *value, int val, int val2)
> +{
> +	struct dumcnt *const priv = counter->driver_data;
> +
> +	priv->counts[value->id] = val;
> +
> +	return 0;
> +}
> +
> +static int dumcnt_value_function_set(struct iio_counter *counter,
> +	struct iio_counter_value *value, unsigned int mode)
> +{
> +	if (mode >= value->num_function_modes)
> +		return -EINVAL;
> +
> +	value->mode = mode;
> +
> +	return 0;
> +}
> +
> +static int dumcnt_value_function_get(struct iio_counter *counter,
> +	struct iio_counter_value *value)
> +{
> +	return value->mode;

If it's called function in the function name, call it function in 
the structure as well rather than mode.

> +}
> +
> +static const struct iio_counter_ops dumcnt_ops = {
> +	.signal_read = dumcnt_signal_read,
> +	.signal_write = dumcnt_signal_write,
> +	.trigger_mode_get = dumcnt_trigger_mode_get,
> +	.trigger_mode_set = dumcnt_trigger_mode_set,
> +	.value_read = dumcnt_value_read,
> +	.value_write = dumcnt_value_write,
> +	.value_function_set = dumcnt_value_function_set,
> +	.value_function_get = dumcnt_value_function_get
> +};
> +
> +static const char *const dumcnt_function_modes[] = {
> +	"decrease",

I think increment was used somewhere in the docs... It's
clearer, but you need to document this ABI to stop having
subtle variations of it like this (even if I imagined it ;)

> +	"increase"
> +};
> +
> +#define DUMCNT_SIGNAL(_id, _name) {	\
> +	.id = _id,			\
> +	.name = _name			\
> +}
> +
> +static const struct iio_counter_signal dumcnt_signals[] = {
> +	DUMCNT_SIGNAL(0, "Signal A"), DUMCNT_SIGNAL(1, "Signal B")
> +};
> +
> +#define DUMCNT_VALUE(_id, _name) {					\
> +	.id = _id,							\
> +	.name = _name,							\
> +	.mode = 0,							\
> +	.function_modes = dumcnt_function_modes,			\
> +	.num_function_modes = ARRAY_SIZE(dumcnt_function_modes)		\
> +}
> +
> +static const struct iio_counter_value dumcnt_values[] = {
> +	DUMCNT_VALUE(0, "Count A"), DUMCNT_VALUE(1, "Count B")
> +};
> +
> +static const char *const dumcnt_trigger_modes[] = {

As mentioned below, use an enum for the index as then you can make it obvious
what 0 means when you set the mode to it later.

> +	"none",
> +	"rising edge",
> +	"falling edge",
> +	"both edges"
> +};
> +
> +static int dumcnt_probe(struct platform_device *pdev)
> +{
> +	struct device *dev = &pdev->dev;
> +	struct iio_counter_signal *signals;
> +	const size_t num_signals = ARRAY_SIZE(dumcnt_signals);

Don't bother with local variable - makes it less obvious what
is going on.

> +	struct iio_counter_value *values;
> +	const size_t num_values = ARRAY_SIZE(dumcnt_values);

Local variable doesn't add anything and if anything makes
it slightly harder to check what is going on.

> +	struct iio_counter_trigger *triggers;
> +	int i;
> +	struct dumcnt *dumcnt;
> +
> +	signals = devm_kmalloc(dev, sizeof(dumcnt_signals), GFP_KERNEL);
> +	if (!signals)
> +		return -ENOMEM;
> +
> +	memcpy(signals, dumcnt_signals, sizeof(dumcnt_signals));

devm_kmemdup?
> +
> +	values = devm_kmalloc(dev, sizeof(dumcnt_values), GFP_KERNEL);
> +	if (!values)
> +		return -ENOMEM;
> +
> +	memcpy(values, dumcnt_values, sizeof(dumcnt_values));

devm_kmemdup?

> +
> +	/* Associate values with their respective signals */
> +	for (i = 0; i < num_values; i++) {
> +		triggers = devm_kmalloc(dev, sizeof(*triggers), GFP_KERNEL);
> +		if (!triggers)
> +			return -ENOMEM;
> +
> +		triggers->mode = 0;

Use an enum for the dumcn_trigger_modes array index then specify by enum value
here.  Will make it more readable.

> +		triggers->trigger_modes = dumcnt_trigger_modes;
> +		triggers->num_trigger_modes = ARRAY_SIZE(dumcnt_trigger_modes);
> +		triggers->signal = &signals[i];
> +
> +		values[i].triggers = triggers;
> +		values[i].num_triggers = 1;
> +	}
> +
> +	dumcnt = devm_kzalloc(dev, sizeof(*dumcnt), GFP_KERNEL);
> +	if (!dumcnt)
> +		return -ENOMEM;
> +
> +	dumcnt->counter.name = dev_name(dev);
> +	dumcnt->counter.dev = dev;
> +	dumcnt->counter.ops = &dumcnt_ops;
> +	dumcnt->counter.signals = signals;
> +	dumcnt->counter.num_signals = num_signals;
> +	dumcnt->counter.values = values;
> +	dumcnt->counter.num_values = num_values;
> +	dumcnt->counter.driver_data = dumcnt;
> +
> +	return devm_iio_counter_register(dev, &dumcnt->counter);
> +}
> +
> +static struct platform_device *dumcnt_device;

Support multiple instances - nick this stuff from the
IIO dummy driver or more specifically the
industrialio-sw-device.c

> +
> +static struct platform_driver dumcnt_driver = {
> +	.driver = {
> +		.name = "104-quad-8"

Don't do that!  Give it it's own name.

> +	}
> +};
> +
> +static void __exit dumcnt_exit(void)
> +{
> +	platform_device_unregister(dumcnt_device);
> +	platform_driver_unregister(&dumcnt_driver);
> +}
> +
> +static int __init dumcnt_init(void)
> +{
> +	int err;
> +

General thing, but if we are going to upstream this with the subsystem,
make device instantiation happen via configfs.

> +	dumcnt_device = platform_device_alloc(dumcnt_driver.driver.name, -1);
> +	if (!dumcnt_device)
> +		return -ENOMEM;
> +
> +	err = platform_device_add(dumcnt_device);
> +	if (err)
> +		goto err_platform_device;
> +
> +	err = platform_driver_probe(&dumcnt_driver, dumcnt_probe);
> +	if (err)
> +		goto err_platform_driver;
> +
> +	return 0;
> +
> +err_platform_driver:
> +	platform_device_del(dumcnt_device);
> +err_platform_device:
> +	platform_device_put(dumcnt_device);
> +	return err;
> +}
> +
> +module_init(dumcnt_init);
> +module_exit(dumcnt_exit);
> +
> +MODULE_AUTHOR("William Breathitt Gray <vilhelm.gray@gmail.com>");
> +MODULE_DESCRIPTION("Dummy counter driver");
> +MODULE_LICENSE("GPL v2");

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

* Re: [PATCH v3 6/6] iio: 104-quad-8: Add IIO generic counter interface support
  2017-10-05 18:14 ` [PATCH v3 6/6] iio: 104-quad-8: Add IIO generic counter interface support William Breathitt Gray
@ 2017-10-08 13:44   ` Jonathan Cameron
  0 siblings, 0 replies; 17+ messages in thread
From: Jonathan Cameron @ 2017-10-08 13:44 UTC (permalink / raw)
  To: William Breathitt Gray
  Cc: benjamin.gaignard, knaack.h, lars, pmeerw, linux-iio, linux-kernel

On Thu,  5 Oct 2017 14:14:53 -0400
William Breathitt Gray <vilhelm.gray@gmail.com> wrote:

> This patch adds support for the IIO generic counter interface to the
> 104-QUAD-8 driver. The existing 104-QUAD-8 device interface should not
> be affected by this patch; all changes are intended as supplemental
> additions as perceived by the user.
> 
> IIO Counter Signals are defined for all quadrature input pairs
> (A and B), as well as index input lines. However, IIO Counter Triggers
> are not created for the index input Signals. IIO Counter Values are
> created for the eight quadrature channel counts, and their respective
> Signals are associated via IIO Counter Triggers.
> 
> The new generic counter interface sysfs attributes expose the same
> functionality and data available via the existing 104-QUAD-8 device
> interface. Four IIO Counter Value function modes are available,
> correlating to the four possible quadrature mode configurations:
> "non-quadrature," "quadrature x1," "quadrature x2," and "quadrature x4."
> 
> Signed-off-by: William Breathitt Gray <vilhelm.gray@gmail.com>

A few generic comments inline - nothing significant.

Jonathan
> ---
>  drivers/iio/counter/104-quad-8.c | 294 ++++++++++++++++++++++++++++++++++++---
>  1 file changed, 277 insertions(+), 17 deletions(-)
> 
> diff --git a/drivers/iio/counter/104-quad-8.c b/drivers/iio/counter/104-quad-8.c
> index b56985078d8c..690a520e70df 100644
> --- a/drivers/iio/counter/104-quad-8.c
> +++ b/drivers/iio/counter/104-quad-8.c
> @@ -16,6 +16,7 @@
>  #include <linux/bitops.h>
>  #include <linux/device.h>
>  #include <linux/errno.h>
> +#include <linux/iio/counter.h>
>  #include <linux/iio/iio.h>
>  #include <linux/iio/types.h>
>  #include <linux/io.h>
> @@ -24,6 +25,7 @@
>  #include <linux/kernel.h>
>  #include <linux/module.h>
>  #include <linux/moduleparam.h>
> +#include <linux/string.h>
>  #include <linux/types.h>
>  
>  #define QUAD8_EXTENT 32
> @@ -37,6 +39,7 @@ MODULE_PARM_DESC(base, "ACCES 104-QUAD-8 base addresses");
>  
>  /**
>   * struct quad8_iio - IIO device private data structure
> + * @counter:		instance of the iio_counter
>   * @preset:		array of preset values
>   * @count_mode:		array of count mode configurations
>   * @quadrature_mode:	array of quadrature mode configurations
> @@ -48,6 +51,7 @@ MODULE_PARM_DESC(base, "ACCES 104-QUAD-8 base addresses");
>   * @base:		base port address of the IIO device
>   */
>  struct quad8_iio {
> +	struct iio_counter counter;
>  	unsigned int preset[QUAD8_NUM_COUNTERS];
>  	unsigned int count_mode[QUAD8_NUM_COUNTERS];
>  	unsigned int quadrature_mode[QUAD8_NUM_COUNTERS];
> @@ -527,33 +531,289 @@ static const struct iio_chan_spec quad8_channels[] = {
>  	QUAD8_COUNT_CHAN(7), QUAD8_INDEX_CHAN(7)
>  };
>  
> +static int quad8_signal_read(struct iio_counter *counter,
> +	struct iio_counter_signal *signal, int *val, int *val2)
> +{
> +	struct quad8_iio *const priv = counter->driver_data;
> +
> +	if (signal->id < 16)
> +		return -EINVAL;
> +
> +	*val = !!(inb(priv->base + 0x16) & BIT(signal->id - 16));
> +
> +	return IIO_VAL_INT;
> +}
> +
> +static int quad8_trigger_mode_get(struct iio_counter *counter,
> +	struct iio_counter_value *value, struct iio_counter_trigger *trigger)
> +{
> +	struct quad8_iio *const priv = counter->driver_data;
> +	const unsigned int mode = priv->quadrature_mode[value->id];
> +	const unsigned int scale = priv->quadrature_scale[value->id];
> +	unsigned int direction;
> +	const unsigned int flag_addr = priv->base + 2 * value->id + 1;
> +	const int signal_id = trigger->signal->id % 2;
> +
> +	if (mode)
> +		switch (scale) {
> +		case 0:
> +			/* U/D flag: 1 = up, 0 = down */
> +			/* direction: 0 = up, 1 = down */
> +			direction = !(inb(flag_addr) & BIT(5));
> +			if (!signal_id)
> +				return direction + 1;
> +			break;
> +		case 1:
> +			if (!signal_id)
> +				return 3;
> +			break;
> +		case 2:
> +			return 3;
> +		}
> +	else
> +		if (!signal_id)
> +			return 1;
> +
> +	return 0;
> +}
> +
> +static int quad8_value_read(struct iio_counter *counter,
> +	struct iio_counter_value *value, int *val, int *val2)
> +{
> +	struct quad8_iio *const priv = counter->driver_data;
> +	const int base_offset = priv->base + 2 * value->id;
> +	unsigned int flags;
> +	unsigned int borrow;
> +	unsigned int carry;
> +	int i;
> +
> +	flags = inb(base_offset + 1);
> +	borrow = flags & BIT(0);
> +	carry = !!(flags & BIT(1));
> +
> +	/* Borrow XOR Carry effectively doubles count range */
> +	*val = (borrow ^ carry) << 24;
> +
> +	/* Reset Byte Pointer; transfer Counter to Output Latch */
> +	outb(0x11, base_offset + 1);
> +
> +	for (i = 0; i < 3; i++)
> +		*val |= (unsigned int)inb(base_offset) << (8 * i);
> +
> +	return IIO_VAL_INT;
> +}
> +
> +static int quad8_value_write(struct iio_counter *counter,
> +	struct iio_counter_value *value, int val, int val2)
> +{
> +	struct quad8_iio *const priv = counter->driver_data;
> +	const int base_offset = priv->base + 2 * value->id;
> +	int i;
> +
> +	/* Only 24-bit values are supported */
> +	if ((unsigned int)val > 0xFFFFFF)
> +		return -EINVAL;
> +
> +	/* Reset Byte Pointer */
> +	outb(0x01, base_offset + 1);
> +
> +	/* Counter can only be set via Preset Register */
> +	for (i = 0; i < 3; i++)
> +		outb(val >> (8 * i), base_offset);
> +
> +	/* Transfer Preset Register to Counter */
> +	outb(0x08, base_offset + 1);
> +
> +	/* Reset Byte Pointer */
> +	outb(0x01, base_offset + 1);
> +
> +	/* Set Preset Register back to original value */
> +	val = priv->preset[value->id];
> +	for (i = 0; i < 3; i++)
> +		outb(val >> (8 * i), base_offset);
> +
> +	/* Reset Borrow, Carry, Compare, and Sign flags */
> +	outb(0x02, base_offset + 1);
> +	/* Reset Error flag */
> +	outb(0x06, base_offset + 1);
> +
> +	return 0;
> +}
> +
> +static int quad8_value_function_set(struct iio_counter *counter,
> +	struct iio_counter_value *value, unsigned int mode)
> +{
> +	struct quad8_iio *const priv = counter->driver_data;
> +	const unsigned int mode_cfg = mode << 3 |
> +		priv->count_mode[value->id] << 1;
> +	const unsigned int idr_cfg = priv->index_polarity[value->id] << 1;
> +	const int base_offset = priv->base + 2 * value->id + 1;
> +
> +	if (mode)
> +		priv->quadrature_scale[value->id] = mode - 1;
> +	else {
> +		/* Quadrature scaling only available in quadrature mode */
> +		priv->quadrature_scale[value->id] = 0;
> +
> +		/* Synchronous function not supported in non-quadrature mode */
> +		if (priv->synchronous_mode[value->id]) {
> +			priv->synchronous_mode[value->id] = 0;
> +			outb(0x60 | idr_cfg, base_offset);
> +		}
> +	}
> +
> +	priv->quadrature_mode[value->id] = !!mode;
> +
> +	/* Load mode configuration to Counter Mode Register */
> +	outb(0x20 | mode_cfg, base_offset);
> +
> +	return 0;
> +}
> +
> +static int quad8_value_function_get(struct iio_counter *counter,
> +	struct iio_counter_value *value)
> +{
> +	struct quad8_iio *const priv = counter->driver_data;
> +	unsigned int quadrature_mode = priv->quadrature_mode[value->id];
> +
> +	return (quadrature_mode) ? priv->quadrature_scale[value->id] + 1 : 0;
> +}
> +
> +static const struct iio_counter_ops quad8_ops = {
> +	.signal_read = quad8_signal_read,
> +	.trigger_mode_get = quad8_trigger_mode_get,
> +	.value_read = quad8_value_read,
> +	.value_write = quad8_value_write,
> +	.value_function_set = quad8_value_function_set,
> +	.value_function_get = quad8_value_function_get
> +};
> +
> +static const char *const quad8_function_modes[] = {
> +	"non-quadrature",
> +	"quadrature x1",
> +	"quadrature x2",
> +	"quadrature x4"
> +};
> +
> +#define QUAD8_SIGNAL(_id, _name) {	\
> +	.id = _id,			\
> +	.name = _name			\
> +}
> +
> +static const struct iio_counter_signal quad8_signals[] = {
> +	QUAD8_SIGNAL(0, "Channel 1 Quadrature A"),
> +	QUAD8_SIGNAL(1, "Channel 1 Quadrature B"),
> +	QUAD8_SIGNAL(2, "Channel 2 Quadrature A"),
> +	QUAD8_SIGNAL(3, "Channel 2 Quadrature B"),
> +	QUAD8_SIGNAL(4, "Channel 3 Quadrature A"),
> +	QUAD8_SIGNAL(5, "Channel 3 Quadrature B"),
> +	QUAD8_SIGNAL(6, "Channel 4 Quadrature A"),
> +	QUAD8_SIGNAL(7, "Channel 4 Quadrature B"),
> +	QUAD8_SIGNAL(8, "Channel 5 Quadrature A"),
> +	QUAD8_SIGNAL(9, "Channel 5 Quadrature B"),
> +	QUAD8_SIGNAL(10, "Channel 6 Quadrature A"),
> +	QUAD8_SIGNAL(11, "Channel 6 Quadrature B"),
> +	QUAD8_SIGNAL(12, "Channel 7 Quadrature A"),
> +	QUAD8_SIGNAL(13, "Channel 7 Quadrature B"),
> +	QUAD8_SIGNAL(14, "Channel 8 Quadrature A"),
> +	QUAD8_SIGNAL(15, "Channel 8 Quadrature B"),
> +	QUAD8_SIGNAL(16, "Channel 1 Index"),
> +	QUAD8_SIGNAL(17, "Channel 2 Index"),
> +	QUAD8_SIGNAL(18, "Channel 3 Index"),
> +	QUAD8_SIGNAL(19, "Channel 4 Index"),
> +	QUAD8_SIGNAL(20, "Channel 5 Index"),
> +	QUAD8_SIGNAL(21, "Channel 6 Index"),
> +	QUAD8_SIGNAL(22, "Channel 7 Index"),
> +	QUAD8_SIGNAL(23, "Channel 8 Index")
> +};
> +
> +#define QUAD8_VALUE(_id, _name) {					\
> +	.id = _id,							\
> +	.name = _name,							\
> +	.mode = 0,							\
> +	.function_modes = quad8_function_modes,				\
> +	.num_function_modes = ARRAY_SIZE(quad8_function_modes)		\
> +}
> +
> +static const struct iio_counter_value quad8_values[] = {
> +	QUAD8_VALUE(0, "Channel 1 Count"), QUAD8_VALUE(1, "Channel 2 Count"),
> +	QUAD8_VALUE(2, "Channel 3 Count"), QUAD8_VALUE(3, "Channel 4 Count"),
> +	QUAD8_VALUE(4, "Channel 5 Count"), QUAD8_VALUE(5, "Channel 6 Count"),
> +	QUAD8_VALUE(6, "Channel 7 Count"), QUAD8_VALUE(7, "Channel 8 Count")
> +};
> +
> +static const char *const quad8_trigger_modes[] = {
> +	"none",
> +	"rising edge",
> +	"falling edge",
> +	"both edges"
> +};
> +
>  static int quad8_probe(struct device *dev, unsigned int id)
>  {
> -	struct iio_dev *indio_dev;
> -	struct quad8_iio *priv;
> +	struct iio_counter_signal *signals;
> +	const size_t num_signals = ARRAY_SIZE(quad8_signals);
> +	struct iio_counter_value *values;
> +	const size_t num_values = ARRAY_SIZE(quad8_values);
> +	struct iio_counter_trigger *triggers;
> +	struct quad8_iio *quad8iio;
>  	int i, j;
>  	unsigned int base_offset;
>  
> -	indio_dev = devm_iio_device_alloc(dev, sizeof(*priv));
> -	if (!indio_dev)
> -		return -ENOMEM;
> -
> -	if (!devm_request_region(dev, base[id], QUAD8_EXTENT,
> -		dev_name(dev))) {
> +	if (!devm_request_region(dev, base[id], QUAD8_EXTENT, dev_name(dev))) {
>  		dev_err(dev, "Unable to lock port addresses (0x%X-0x%X)\n",
>  			base[id], base[id] + QUAD8_EXTENT);
>  		return -EBUSY;
>  	}
>  
> -	indio_dev->info = &quad8_info;
> -	indio_dev->modes = INDIO_DIRECT_MODE;
> -	indio_dev->num_channels = ARRAY_SIZE(quad8_channels);
> -	indio_dev->channels = quad8_channels;
> -	indio_dev->name = dev_name(dev);
> -	indio_dev->dev.parent = dev;
> +	signals = devm_kmalloc(dev, sizeof(quad8_signals), GFP_KERNEL);
> +	if (!signals)
> +		return -ENOMEM;
> +
> +	memcpy(signals, quad8_signals, sizeof(quad8_signals));
> +
> +	values = devm_kmalloc(dev, sizeof(quad8_values), GFP_KERNEL);
> +	if (!values)
> +		return -ENOMEM;
> +
> +	memcpy(values, quad8_values, sizeof(quad8_values));

devm_kmemdup

> +
> +	/* Associate values with their respective signals */
> +	for (i = 0; i < num_values; i++) {
> +		triggers = devm_kmalloc(dev, 2 * sizeof(*triggers), GFP_KERNEL);
> +		if (!triggers)
> +			return -ENOMEM;
> +
> +		/* Starts up in non-quadrature mode */
> +		triggers[0].mode = 1;

As I suggested for the dummy driver - use an enum, then the enum naming will
make it obvious what the value means here without a comment.

> +		triggers[0].trigger_modes = quad8_trigger_modes;
> +		triggers[0].num_trigger_modes = ARRAY_SIZE(quad8_trigger_modes);
> +		triggers[0].signal = &signals[2 * i];
> +		triggers[1].mode = 0;
> +		triggers[1].trigger_modes = quad8_trigger_modes;
> +		triggers[1].num_trigger_modes = ARRAY_SIZE(quad8_trigger_modes);
> +		triggers[1].signal = &signals[2 * i + 1];
> +
> +		values[i].triggers = triggers;
> +		values[i].num_triggers = 2;
> +	}
> +
> +	quad8iio = devm_kzalloc(dev, sizeof(*quad8iio), GFP_KERNEL);
> +	if (!quad8iio)
> +		return -ENOMEM;
>  
> -	priv = iio_priv(indio_dev);
> -	priv->base = base[id];
> +	quad8iio->counter.name = dev_name(dev);
> +	quad8iio->counter.dev = dev;
> +	quad8iio->counter.ops = &quad8_ops;
> +	quad8iio->counter.signals = signals;
> +	quad8iio->counter.num_signals = num_signals;
> +	quad8iio->counter.values = values;
> +	quad8iio->counter.num_values = num_values;
> +	quad8iio->counter.channels = quad8_channels;
> +	quad8iio->counter.num_channels = ARRAY_SIZE(quad8_channels);
> +	quad8iio->counter.info = &quad8_info;
> +	quad8iio->counter.driver_data = quad8iio;
> +	quad8iio->base = base[id];
>  
>  	/* Reset all counters and disable interrupt function */
>  	outb(0x01, base[id] + 0x11);
> @@ -579,7 +839,7 @@ static int quad8_probe(struct device *dev, unsigned int id)
>  	/* Enable all counters */
>  	outb(0x00, base[id] + 0x11);
>  
> -	return devm_iio_device_register(dev, indio_dev);
> +	return devm_iio_counter_register(dev, &quad8iio->counter);
>  }
>  
>  static struct isa_driver quad8_driver = {

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

* Re: [PATCH v3 2/6] iio: Introduce the generic counter interface
  2017-10-05 18:13 ` [PATCH v3 2/6] iio: Introduce the generic counter interface William Breathitt Gray
@ 2017-10-08 14:30   ` Jonathan Cameron
  2017-10-09 12:56       ` Benjamin Gaignard
  0 siblings, 1 reply; 17+ messages in thread
From: Jonathan Cameron @ 2017-10-08 14:30 UTC (permalink / raw)
  To: William Breathitt Gray
  Cc: benjamin.gaignard, knaack.h, lars, pmeerw, linux-iio, linux-kernel

On Thu,  5 Oct 2017 14:13:44 -0400
William Breathitt Gray <vilhelm.gray@gmail.com> wrote:

> This patch introduces the IIO generic counter interface for supporting
> counter devices. The generic counter interface serves as a catch-all to
> enable rudimentary support for devices that qualify as counters. More
> specific and apt counter interfaces may be developed on top of the
> generic counter interface, and those interfaces should be used by
> drivers when possible rather than the generic counter interface.

I think you probably want to avoid any additional layering and try to
push things down here (maybe with a few utility functions to simplify
common cases), but that's a question for another day!
> 
> In the context of the IIO generic counter interface, a counter is
> defined as a device that reports one or more "counter values" based on
> the state changes of one or more "counter signals" as evaluated by a
> defined "counter function."
> 
> The IIO generic counter interface piggybacks off of the IIO core. This
> is primarily used to leverage the existing sysfs setup: the IIO_COUNT
> channel attributes represent the Counter Value, while the IIO_SIGNAL
> channel attributes represent the Counter Signal; auxilary IIO_COUNT
> attributes represent the Counter Signal connections (Triggers) and their
> respective state change configurations which trigger an associated
> "counter function" evaluation.
> 
> The iio_counter_ops structure serves as a container for driver callbacks
> to communicate with the device; function callbacks are provided to read
> and write various Signals and Values, and set and get the "trigger mode"
> and "function mode" for various Triggers and Values respectively.
> 
> To support a counter device, a driver must first allocate the available
> Counter Signals via iio_counter_signal structures. These Signals should
> be stored as an array and set to the signals array member of an
> allocated iio_counter structure before the Counter is registered to the
> system.
> 
> Counter Values may be allocated via iio_counter_value structures, and
> respective Counter Signal associations (Triggers) made via
> iio_counter_trigger structures. Associated iio_counter_trigger
> structures are stored as an array and set to the the triggers array
> member of the respective iio_counter_value structure. These
> iio_counter_value structures are set to the values array member of an
> allocated iio_counter structure before the Counter is registered to the
> system.
> 
> A counter device is registered to the system by passing the respective
> initialized iio_counter structure to the iio_counter_register function;
> similarly, the iio_counter_unregister function unregisters the
> respective counter. The devm_iio_counter_register and
> iio_devm_iio_counter_unregister functions serve as device memory-managed
> versions of the iio_counter_register and iio_counter unregister
> functions respectively.

All the find by ids are the main complexity introduced by layering this
on IIO that you probably wouldn't have if it wasn't so layered.  Hmm.
I suggest inline that you could allow drivers to provide fast
versions of that search given they can probably infer it directly from
the index.  Might be something to ignore for now though in the interests
of initial simplicity.

Otherwise a few comments inline, but mostly this is coming together
pretty well.  I'm actually surprised the layering on top of IIO didn't
end up more painful than it did. It's not horrendous - which is not
to say you wouldn't be better breaking free of that entirely...
(I'm not sure either way).

I think the big remaining questions are around naming more than anything
else.  I don't like the name value when a user will think they are looking
for a counter.  If you use counter though the current use of counter needs
to change.

Trigger is also, as has been raised, a rather overloaded term. Not totally
obvious what a better term is but we shouldn't reuse that one...

I'll try and think of something if you have no luck!

Jonathan
> 
> Signed-off-by: William Breathitt Gray <vilhelm.gray@gmail.com>
> ---
>  MAINTAINERS                        |   7 +
>  drivers/iio/Kconfig                |   8 +
>  drivers/iio/Makefile               |   1 +
>  drivers/iio/counter/Kconfig        |   1 +
>  drivers/iio/industrialio-counter.c | 900 +++++++++++++++++++++++++++++++++++++
>  include/linux/iio/counter.h        | 166 +++++++
>  6 files changed, 1083 insertions(+)
>  create mode 100644 drivers/iio/industrialio-counter.c
>  create mode 100644 include/linux/iio/counter.h
> 
> diff --git a/MAINTAINERS b/MAINTAINERS
> index 2281af4b41b6..8b7c37bed252 100644
> --- a/MAINTAINERS
> +++ b/MAINTAINERS
> @@ -6693,6 +6693,13 @@ F:	Documentation/ABI/testing/sysfs-bus-iio-adc-envelope-detector
>  F:	Documentation/devicetree/bindings/iio/adc/envelope-detector.txt
>  F:	drivers/iio/adc/envelope-detector.c
>  
> +IIO GENERIC COUNTER INTERFACE
> +M:	William Breathitt Gray <vilhelm.gray@gmail.com>
> +L:	linux-iio@vger.kernel.org
> +S:	Maintained
> +F:	drivers/iio/industrialio-counter.c
> +F:	include/linux/iio/counter.h

Don't forget the directory the drivers are going in ;)
*laughs manically* 
> +
>  IIO MULTIPLEXER
>  M:	Peter Rosin <peda@axentia.se>
>  L:	linux-iio@vger.kernel.org
> diff --git a/drivers/iio/Kconfig b/drivers/iio/Kconfig
> index b3c8c6ef0dff..78e01f4f5937 100644
> --- a/drivers/iio/Kconfig
> +++ b/drivers/iio/Kconfig
> @@ -30,6 +30,14 @@ config IIO_CONFIGFS
>  	  (e.g. software triggers). For more info see
>  	  Documentation/iio/iio_configfs.txt.
>  
> +config IIO_COUNTER
> +	bool "Enable IIO counter support"
> +	help
> +	  Provides IIO core support for counters. This API provides
> +	  a generic interface that serves as the building blocks to
> +	  create more complex counter interfaces. Rudimentary support
> +	  for counters is enabled.
> +
>  config IIO_TRIGGER
>  	bool "Enable triggered sampling support"
>  	help
> diff --git a/drivers/iio/Makefile b/drivers/iio/Makefile
> index 93c769cd99bf..6427ff38f964 100644
> --- a/drivers/iio/Makefile
> +++ b/drivers/iio/Makefile
> @@ -5,6 +5,7 @@
>  obj-$(CONFIG_IIO) += industrialio.o
>  industrialio-y := industrialio-core.o industrialio-event.o inkern.o
>  industrialio-$(CONFIG_IIO_BUFFER) += industrialio-buffer.o
> +industrialio-$(CONFIG_IIO_COUNTER) += industrialio-counter.o
>  industrialio-$(CONFIG_IIO_TRIGGER) += industrialio-trigger.o
>  
>  obj-$(CONFIG_IIO_CONFIGFS) += industrialio-configfs.o
> diff --git a/drivers/iio/counter/Kconfig b/drivers/iio/counter/Kconfig
> index 474e1ac4e7c0..c8becfe78e28 100644
> --- a/drivers/iio/counter/Kconfig
> +++ b/drivers/iio/counter/Kconfig
> @@ -4,6 +4,7 @@
>  # When adding new entries keep the list in alphabetical order
>  
>  menu "Counters"
> +	depends on IIO_COUNTER
>  
>  config 104_QUAD_8
>  	tristate "ACCES 104-QUAD-8 driver"
> diff --git a/drivers/iio/industrialio-counter.c b/drivers/iio/industrialio-counter.c
> new file mode 100644
> index 000000000000..dfb982dae3a8
> --- /dev/null
> +++ b/drivers/iio/industrialio-counter.c
> @@ -0,0 +1,900 @@
> +/*
> + * Industrial I/O counter interface functions
> + * Copyright (C) 2017 William Breathitt Gray
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License, version 2, as
> + * published by the Free Software Foundation.
> + *
> + * This program is distributed in the hope that it will be useful, but
> + * WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
> + * General Public License for more details.
> + */
> +#include <linux/device.h>
> +#include <linux/err.h>
> +#include <linux/export.h>
> +#include <linux/gfp.h>
> +#include <linux/kernel.h>
> +#include <linux/printk.h>
> +#include <linux/slab.h>
> +#include <linux/string.h>
> +
> +#include <linux/iio/iio.h>
> +#include <linux/iio/types.h>
> +
> +#include <linux/iio/counter.h>
> +
> +#define iio_priv_get_counter(_indio_dev) \
> +	(*(struct iio_counter **)iio_priv(_indio_dev))
> +
> +static struct iio_counter_signal *iio_counter_signal_find_by_id(
> +	const struct iio_counter *const counter, const int id)
> +{
> +	size_t i;
> +	struct iio_counter_signal *signal;
> +
> +	for (i = 0; i < counter->num_signals; i++) {
> +		signal = counter->signals + i;
> +		if (signal->id == id)
> +			return signal;
> +	}
> +
> +	return NULL;
> +}
> +
> +static struct iio_counter_trigger *iio_counter_trigger_find_by_id(
> +	const struct iio_counter_value *const value, const int id)
> +{
> +	size_t i;
> +	struct iio_counter_trigger *trigger;
> +
> +	for (i = 0; i < value->num_triggers; i++) {
> +		trigger = value->triggers + i;
> +		if (trigger->signal->id == id)
> +			return trigger;
> +	}
> +
> +	return NULL;
> +}
> +
> +static struct iio_counter_value *iio_counter_value_find_by_id(
> +	const struct iio_counter *const counter, const int id)
> +{
> +	size_t i;
> +	struct iio_counter_value *value;

In most cases this mapping will be entirely obvious to the
driver - perhaps provide an optional callback to let it
provide it directly without any searching?

We could always add this later though if we start getting
drivers with lots of instances of the various parts...

> +
> +	for (i = 0; i < counter->num_values; i++) {
> +		value = counter->values + i;
> +		if (value->id == id)
> +			return value;
> +	}
> +
> +	return NULL;
> +}
> +
> +static ssize_t iio_counter_signal_name_read(struct iio_dev *indio_dev,
> +	uintptr_t priv, const struct iio_chan_spec *chan, char *buf)
> +{
> +	struct iio_counter *const counter = iio_priv_get_counter(indio_dev);
> +	const struct iio_counter_signal *signal;
> +
> +	signal = iio_counter_signal_find_by_id(counter, chan->channel2);
> +	if (!signal)
> +		return -EINVAL;
> +
> +	return scnprintf(buf, PAGE_SIZE, "%s\n", signal->name);
> +}
> +
> +static ssize_t iio_counter_value_name_read(struct iio_dev *indio_dev,
> +	uintptr_t priv, const struct iio_chan_spec *chan, char *buf)
> +{
> +	struct iio_counter *const counter = iio_priv_get_counter(indio_dev);
> +	const struct iio_counter_value *value;
> +
> +	value = iio_counter_value_find_by_id(counter, chan->channel2);
> +	if (!value)
> +		return -EINVAL;
> +
> +	return scnprintf(buf, PAGE_SIZE, "%s\n", value->name);

I mentioned before but I wonder if we can just push name down into
the chan spec itself in some fashion.  Whether to associate it with
existing datasheet_name or not is an open question however.

> +}
> +
> +static ssize_t iio_counter_value_triggers_read(struct iio_dev *indio_dev,
> +	uintptr_t priv, const struct iio_chan_spec *chan, char *buf)
> +{
> +	struct iio_counter *const counter = iio_priv_get_counter(indio_dev);
> +	struct iio_counter_value *value;
> +	size_t i;
> +	const struct iio_counter_trigger *trigger;
> +	ssize_t len = 0;
> +
> +	value = iio_counter_value_find_by_id(counter, chan->channel2);
> +	if (!value)
> +		return -EINVAL;
> +
> +	/* Print out a list of every Signal association to Value */
> +	for (i = 0; i < value->num_triggers; i++) {
> +		trigger = value->triggers + i;
> +		len += snprintf(buf + len, PAGE_SIZE - len, "%d\t%s\t%s\n",
> +			trigger->signal->id, trigger->signal->name,
> +			trigger->trigger_modes[trigger->mode]);
> +		if (len >= PAGE_SIZE)
> +			return -ENOMEM;
> +	}
> +
> +	return len;
> +}
> +
> +static ssize_t iio_counter_trigger_mode_read(struct iio_dev *indio_dev,
> +	uintptr_t priv, const struct iio_chan_spec *chan, char *buf)
> +{
> +	struct iio_counter *const counter = iio_priv_get_counter(indio_dev);
> +	struct iio_counter_value *value;
> +	struct iio_counter_trigger *trigger;
> +	const int signal_id = *(int *)((void *)priv);
> +	int mode;
> +
> +	if (!counter->ops->trigger_mode_get)
> +		return -EINVAL;
> +
> +	value = iio_counter_value_find_by_id(counter, chan->channel2);
> +	if (!value)
> +		return -EINVAL;
> +
> +	trigger = iio_counter_trigger_find_by_id(value, signal_id);
> +	if (!trigger)
> +		return -EINVAL;
> +
> +	mode = counter->ops->trigger_mode_get(counter, value, trigger);
> +
> +	if (mode < 0)
> +		return mode;
> +	else if (mode >= trigger->num_trigger_modes)
> +		return -EINVAL;
> +
> +	trigger->mode = mode;
> +
> +	return scnprintf(buf, PAGE_SIZE, "%s\n", trigger->trigger_modes[mode]);
> +}
> +
> +static ssize_t iio_counter_trigger_mode_write(struct iio_dev *indio_dev,
> +	uintptr_t priv, const struct iio_chan_spec *chan, const char *buf,
> +	size_t len)
> +{
> +	struct iio_counter *const counter = iio_priv_get_counter(indio_dev);
> +	struct iio_counter_value *value;
> +	ssize_t err;
> +	struct iio_counter_trigger *trigger;
> +	const int signal_id = *(int *)((void *)priv);
> +	unsigned int mode;
> +
> +	if (!counter->ops->trigger_mode_set)
> +		return -EINVAL;
> +
> +	value = iio_counter_value_find_by_id(counter, chan->channel2);
> +	if (!value)
> +		return -EINVAL;
> +
> +	trigger = iio_counter_trigger_find_by_id(value, signal_id);
> +	if (!trigger)
> +		return -EINVAL;
> +
> +	for (mode = 0; mode < trigger->num_trigger_modes; mode++)
> +		if (sysfs_streq(buf, trigger->trigger_modes[mode]))
> +			break;
> +
> +	if (mode >= trigger->num_trigger_modes)
> +		return -EINVAL;
> +
> +	err = counter->ops->trigger_mode_set(counter, value, trigger, mode);
> +	if (err)
> +		return err;
> +
> +	trigger->mode = mode;
> +
> +	return len;
> +}
> +
> +static ssize_t iio_counter_trigger_mode_available_read(
> +	struct iio_dev *indio_dev, uintptr_t priv,
> +	const struct iio_chan_spec *chan, char *buf)
> +{
> +	struct iio_counter *const counter = iio_priv_get_counter(indio_dev);
> +	struct iio_counter_value *value;
> +	ssize_t len = 0;
> +	struct iio_counter_trigger *trigger;
> +	const int signal_id = *(int *)((void *)priv);
> +	unsigned int i;
> +
> +	value = iio_counter_value_find_by_id(counter, chan->channel2);
> +	if (!value)
> +		return -EINVAL;
> +
> +	trigger = iio_counter_trigger_find_by_id(value, signal_id);
> +	if (!trigger)
> +		return -EINVAL;
> +
> +	for (i = 0; i < trigger->num_trigger_modes; i++)
> +		len += scnprintf(buf + len, PAGE_SIZE - len, "%s ",
> +			trigger->trigger_modes[i]);
> +
> +	buf[len - 1] = '\n';
> +
> +	return len;
> +}
> +
> +static int iio_counter_value_function_set(struct iio_dev *indio_dev,
> +	const struct iio_chan_spec *chan, unsigned int mode)
> +{
> +	struct iio_counter *const counter = iio_priv_get_counter(indio_dev);
> +	struct iio_counter_value *value;
> +	int err;
> +
> +	if (!counter->ops->value_function_set)
> +		return -EINVAL;
> +
> +	/* Find relevant Value */
> +	value = iio_counter_value_find_by_id(counter, chan->channel2);
> +	if (!value)
> +		return -EINVAL;
> +
> +	/* Map IIO core function_set to Generic Counter value_function_set */
> +	err = counter->ops->value_function_set(counter, value, mode);
> +	if (err)
> +		return err;
> +
> +	value->mode = mode;
> +
> +	return 0;
> +}
> +
> +static int iio_counter_value_function_get(struct iio_dev *indio_dev,
> +	const struct iio_chan_spec *chan)
> +{
> +	struct iio_counter *const counter = iio_priv_get_counter(indio_dev);
> +	struct iio_counter_value *value;
> +	int retval;
> +
> +	if (!counter->ops->value_function_get)
> +		return -EINVAL;
> +
> +	/* Find relevant Value */
> +	value = iio_counter_value_find_by_id(counter, chan->channel2);

Same argument as below - would the driver not be able to do this better?
Often it would know a simple transform to get the right one...


> +	if (!value)
> +		return -EINVAL;
> +
> +	/* Map IIO core function_get to Generic Counter value_function_get */
> +	retval = counter->ops->value_function_get(counter, value);
> +	if (retval < 0)
> +		return retval;
> +	else if (retval >= value->num_function_modes)
> +		return -EINVAL;
> +
> +	value->mode = retval;
> +
> +	return retval;
> +}
> +
> +static int iio_counter_value_ext_info_alloc(struct iio_chan_spec *const chan,
> +	struct iio_counter_value *const value)
> +{
> +	const struct iio_chan_spec_ext_info ext_info_default[] = {
> +		{
> +			.name = "name",
> +			.shared = IIO_SEPARATE,
> +			.read = iio_counter_value_name_read
> +		},
> +		IIO_ENUM("function", IIO_SEPARATE, &value->function_enum),
> +		{
> +			.name = "function_available",
> +			.shared = IIO_SEPARATE,
> +			.read = iio_enum_available_read,
> +			.private = (void *)&value->function_enum
> +		},
> +		{
> +			.name = "triggers",
> +			.shared = IIO_SEPARATE,
> +			.read = iio_counter_value_triggers_read
> +		}
> +	};
> +	const size_t num_default = ARRAY_SIZE(ext_info_default);
> +	const struct iio_chan_spec_ext_info ext_info_trigger[] = {
> +		{
> +			.shared = IIO_SEPARATE,
> +			.read = iio_counter_trigger_mode_read,
> +			.write = iio_counter_trigger_mode_write
> +		},
> +		{
> +			.shared = IIO_SEPARATE,
> +			.read = iio_counter_trigger_mode_available_read
> +		}
> +	};
> +	const size_t num_ext_info_trigger = ARRAY_SIZE(ext_info_trigger);
> +	const size_t num_triggers_ext_info = num_ext_info_trigger *
> +		value->num_triggers;
> +	const size_t num_ext_info = num_default + num_triggers_ext_info + 1;
> +	int err;
> +	struct iio_chan_spec_ext_info *ext_info;
> +	const struct iio_counter_trigger *trigger;
> +	size_t i;
> +	size_t j;
> +
> +	/* Construct function_enum for Value */
> +	value->function_enum.items = value->function_modes;
> +	value->function_enum.num_items = value->num_function_modes;
> +	value->function_enum.set = iio_counter_value_function_set;
> +	value->function_enum.get = iio_counter_value_function_get;
> +
> +	ext_info = kmalloc_array(num_ext_info, sizeof(*ext_info), GFP_KERNEL);
> +	if (!ext_info)
> +		return -ENOMEM;
> +	ext_info[num_ext_info - 1].name = NULL;
> +
> +	/* Add ext_info for the name, function, function_available, and triggers
> +	 * attributes
> +	 */
> +	memcpy(ext_info, ext_info_default, sizeof(ext_info_default));
> +	/* Add ext_info for the trigger_signalX-Z and
> +	 * trigger_signalX-Z_available attributes for each Trigger
> +	 */
> +	for (i = 0; i < num_triggers_ext_info; i += num_ext_info_trigger)
> +		memcpy(ext_info + num_default + i, ext_info_trigger,
> +			sizeof(ext_info_trigger));
> +
> +	/* Set name for each trigger_signalX-Z and trigger_signalX-Z_available
> +	 * attribute; store the respective Signal ID address in each ext_info
> +	 * private member
> +	 */
> +	for (i = num_default, j = 0; j < value->num_triggers; i++, j++) {
> +		trigger = value->triggers + j;
> +
> +		ext_info[i].name = kasprintf(GFP_KERNEL, "trigger_signal%d-%d",
> +			chan->channel, trigger->signal->id);
> +		if (!ext_info[i].name) {
> +			err = -ENOMEM;
> +			goto err_name_alloc;
> +		}
> +		ext_info[i].private = (void *)&trigger->signal->id;
> +		i++;
> +
> +		ext_info[i].name = kasprintf(GFP_KERNEL,
> +			"trigger_signal%d-%d_available",
> +			chan->channel, trigger->signal->id);
> +		if (!ext_info[i].name) {
> +			err = -ENOMEM;
> +			goto err_name_alloc;
> +		}
> +		ext_info[i].private = (void *)&trigger->signal->id;
> +	}
> +
> +	chan->ext_info = ext_info;
> +
> +	return 0;
> +
> +err_name_alloc:
> +	while (i-- > num_default)
> +		kfree(ext_info[i].name);
> +	kfree(ext_info);
> +	return err;
> +}
> +
> +static void iio_counter_value_ext_info_free(
> +	const struct iio_chan_spec *const channel)
> +{
> +	size_t i;
> +	const char *const prefix = "trigger_signal";
> +	const size_t prefix_len = strlen(prefix);
> +
> +	for (i = 0; channel->ext_info[i].name; i++)
> +		if (!strncmp(channel->ext_info[i].name, prefix, prefix_len))
> +			kfree(channel->ext_info[i].name);
> +	kfree(channel->ext_info);
> +}
> +
> +static const struct iio_chan_spec_ext_info iio_counter_signal_ext_info[] = {
> +	{
> +		.name = "name",
> +		.shared = IIO_SEPARATE,
> +		.read = iio_counter_signal_name_read
> +	},
> +	{}
> +};
> +
> +static int iio_counter_channels_alloc(struct iio_counter *const counter)
> +{
> +	const size_t num_channels = counter->num_signals + counter->num_values +
> +		counter->num_channels;
> +	int err;
> +	struct iio_chan_spec *channels;
> +	size_t j;
> +	struct iio_counter_value *value;
> +	size_t i = counter->num_channels;
> +	const struct iio_counter_signal *signal;
> +
> +	channels = kcalloc(num_channels, sizeof(*channels), GFP_KERNEL);

Bring the calculation of num_channels down here - it will make it
more obvious that this allows space for both sources of channel.

> +	if (!channels)
> +		return -ENOMEM;
> +
> +	/* If any channels were supplied on Counter registration,
> +	 * we add them here to the front of the array
> +	 */
> +	memcpy(channels, counter->channels,
> +		counter->num_channels * sizeof(*counter->channels));
> +
> +	/* Add channel for each Value */
> +	for (j = 0; j < counter->num_values; j++) {
> +		value = counter->values + j;
> +
> +		channels[i].type = IIO_COUNT;
> +		channels[i].channel = counter->id;
> +		channels[i].channel2 = value->id;
> +		channels[i].info_mask_separate = BIT(IIO_CHAN_INFO_RAW);
> +		channels[i].indexed = 1;
> +		channels[i].counter = 1;
> +
> +		/* Add channels for Triggers associated with Value */
Add channels?  'i' would need incrementing.   Also that's not what this function
is doing that I can see...
> +		err = iio_counter_value_ext_info_alloc(channels + i, value);
> +		if (err)
> +			goto err_value_ext_info_alloc;
> +
> +		i++;
> +	}
> +
> +	/* Add channel for each Signal */
> +	for (j = 0; j < counter->num_signals; j++) {
> +		signal = counter->signals + j;
> +
> +		channels[i].type = IIO_SIGNAL;
> +		channels[i].channel = counter->id;
> +		channels[i].channel2 = signal->id;
> +		channels[i].info_mask_separate = BIT(IIO_CHAN_INFO_RAW);
> +		channels[i].indexed = 1;
> +		channels[i].counter = 1;
> +		channels[i].ext_info = iio_counter_signal_ext_info;
> +
> +		i++;
> +	}
> +
> +	counter->indio_dev->num_channels = num_channels;
> +	counter->indio_dev->channels = channels;
> +
> +	return 0;
> +
> +err_value_ext_info_alloc:
> +	while (i-- > counter->num_channels)
> +		iio_counter_value_ext_info_free(channels + i);
> +	kfree(channels);
> +	return err;
> +}
> +
> +static void iio_counter_channels_free(const struct iio_counter *const counter)
> +{
> +	size_t i = counter->num_channels + counter->indio_dev->num_channels;
> +	const struct iio_chan_spec *const chans = counter->indio_dev->channels;
> +
> +	while (i-- > counter->num_channels)
> +		/* Only IIO_COUNT channels need to be freed here */
> +		if (chans[i].type == IIO_COUNT)
> +			iio_counter_value_ext_info_free(chans + i);
> +
> +	kfree(chans);
> +}
> +
> +static int iio_counter_read_raw(struct iio_dev *indio_dev,
> +	struct iio_chan_spec const *chan, int *val, int *val2, long mask)
> +{
> +	struct iio_counter *const counter = iio_priv_get_counter(indio_dev);
> +	struct iio_counter_signal *signal;
> +	struct iio_counter_value *value;
> +
> +	if (mask != IIO_CHAN_INFO_RAW)
> +		return -EINVAL;
> +
> +	switch (chan->type) {
> +	/* Map read_raw to signal_read for Signal */
> +	case IIO_SIGNAL:
> +		if (!counter->ops->signal_read)
> +			return -EINVAL;
> +
> +		signal = iio_counter_signal_find_by_id(counter, chan->channel2);
> +		if (!signal)
> +			return -EINVAL;
> +
> +		return counter->ops->signal_read(counter, signal, val, val2);
> +	/* Map read_raw to value_read for Value */
> +	case IIO_COUNT:
> +		if (!counter->ops->value_read)
> +			return -EINVAL;
> +
> +		value = iio_counter_value_find_by_id(counter, chan->channel2);
> +		if (!value)
> +			return -EINVAL;
> +
> +		return counter->ops->value_read(counter, value, val, val2);
> +	/* Map read_raw to read_raw for non-counter channel */
> +	default:
> +		if (counter->info && counter->info->read_raw)
> +			return counter->info->read_raw(indio_dev, chan, val,
> +				val2, mask);
> +	}
> +
> +	return -EINVAL;
> +}
> +
> +static int iio_counter_write_raw(struct iio_dev *indio_dev,
> +	struct iio_chan_spec const *chan, int val, int val2, long mask)
> +{
> +	struct iio_counter *const counter = iio_priv_get_counter(indio_dev);
> +	struct iio_counter_signal *signal;
> +	struct iio_counter_value *value;
> +
> +	if (mask != IIO_CHAN_INFO_RAW)
> +		return -EINVAL;
> +
> +	switch (chan->type) {
> +	/* Map write_raw to signal_write for Signal */
> +	case IIO_SIGNAL:
> +		if (!counter->ops->signal_write)
> +			return -EINVAL;
> +
> +		signal = iio_counter_signal_find_by_id(counter, chan->channel2);
> +		if (!signal)
> +			return -EINVAL;

	Hmm. have to search is certainly a little ugly.  Particularly as the driver
	would be able to maintain this as a lookup.  I'd be tempted to pass the
	signal id down into the callback rather than finding the signal in the core.
	Might turn out messier though... :)

> +
> +		return counter->ops->signal_write(counter, signal, val, val2);
> +	/* Map write_raw to value_write for Value */
> +	case IIO_COUNT:
> +		if (!counter->ops->value_write)
> +			return -EINVAL;
> +
> +		value = iio_counter_value_find_by_id(counter, chan->channel2);

Again, this mapping is a simple lookup in the driver I think, perhaps push
it down there?

> +		if (!value)
> +			return -EINVAL;
> +
> +		return counter->ops->value_write(counter, value, val, val2);
> +	/* Map write_raw to write_raw for non-counter channel */
> +	default:
> +		if (counter->info && counter->info->write_raw)
> +			return counter->info->write_raw(indio_dev, chan, val,
> +				val2, mask);
> +	}
> +
> +	return -EINVAL;
> +}
> +
> +static int iio_counter_signals_validate(const struct iio_counter *const counter)
> +{
> +	size_t i;
> +	const struct iio_counter_signal *signal;
> +	size_t j;
> +	int curr_id;
> +
> +	/* At least one Signal must be defined */
> +	if (!counter->num_signals || !counter->signals) {
> +		pr_err("Counter '%d' Signals undefined\n", counter->id);
> +		return -EINVAL;
> +	}
> +
> +	for (i = 0; i < counter->num_signals; i++) {
> +		signal = counter->signals + i;
> +		/* No two Signals may share the same ID */
> +		for (j = 0; j < i; j++) {
> +			curr_id = counter->signals[j].id;
> +			if (curr_id == signal->id) {
> +				pr_err("Duplicate Counter '%d' Signal '%d'\n",
> +						counter->id, signal->id);
> +				return -EEXIST;
> +			}
> +		}
> +		for (j = i + 1; j < counter->num_signals; j++) {
> +			curr_id = counter->signals[j].id;
> +			if (curr_id == signal->id) {
> +				pr_err("Duplicate Counter '%d' Signal '%d'\n",
> +						counter->id, signal->id);
> +				return -EEXIST;
> +			}
> +		}

Same as below.

> +	}
> +
> +	return 0;
> +}
> +
> +static int iio_counter_triggers_validate(const int counter_id,
> +	const struct iio_counter_value *const value)
> +{
> +	size_t i;
> +	const struct iio_counter_trigger *trigger;
> +	size_t j;
> +	int curr_id;
> +
> +	/* At least one Trigger must be defined */
> +	if (!value->num_triggers || !value->triggers) {
> +		pr_err("Counter '%d' Value '%d' Triggers undefined\n",
> +			counter_id, value->id);
> +		return -EINVAL;
> +	}
> +
> +	/* Ensure all Triggers have a defined Signal; this prevents a possible
> +	 * NULL pointer dereference when later validating Trigger Signal IDs

Fix comment syntax throughout...

> +	 */
> +	for (i = 0; i < value->num_triggers; i++)
> +		if (!value->triggers[i].signal) {
> +			pr_err("Counter '%d' Trigger '%zu' Signal undefined\n",
> +				counter_id, i);
> +			return -EINVAL;
> +		}
> +
> +	/* Verify validity of each Trigger */
> +	for (i = 0; i < value->num_triggers; i++) {
> +		trigger = value->triggers + i;
> +		/* No two Trigger Signals may share the same ID */
> +		for (j = 0; j < i; j++) {
> +			curr_id = value->triggers[j].signal->id;
> +			if (curr_id == trigger->signal->id) {
> +				pr_err("Signal '%d' is already linked to Counter '%d' Value '%d'\n",
> +					trigger->signal->id, counter_id,
> +					value->id);
> +				return -EEXIST;
> +			}
> +		}
> +		for (j = i + 1; j < value->num_triggers; j++) {
> +			curr_id = value->triggers[j].signal->id;
> +			if (curr_id == trigger->signal->id) {
> +				pr_err("Signal '%d' is already linked to Counter '%d' Value '%d'\n",
> +					trigger->signal->id, counter_id,
> +					value->id);
> +				return -EEXIST;
> +			}
> +		}
Again, one loop with the condition changed so it doesn't fault on the i = j case -- see below and
note I'm reviewing backwards...


> +
> +		/* At least one trigger mode must be defined for each Trigger */
> +		if (!trigger->num_trigger_modes || !trigger->trigger_modes) {
> +			pr_err("Counter '%d' Signal '%d' trigger modes undefined\n",
> +				counter_id, trigger->signal->id);
> +			return -EINVAL;
> +		}
> +	}
> +
> +	return 0;
> +}
> +
> +static int iio_counter_values_validate(const struct iio_counter *const counter)
> +{

Hmm. Pretty heavy weight checking.  Ah well, up to you.

> +	size_t i;
> +	const struct iio_counter_value *value;
> +	size_t j;
> +	int curr_id;
> +	int err;
> +
> +	/* At least one Value must be defined */
> +	if (!counter->num_values || !counter->values) {
> +		pr_err("Counter '%d' Values undefined\n", counter->id);
> +		return -EINVAL;
> +	}
> +
> +	/* Verify validity of each Value */
> +	for (i = 0; i < counter->num_values; i++) {
> +		value = counter->values + i;
> +		/* No two Values may share the same ID */
> +		for (j = 0; j < i; j++) {

Single loop with a slightly change to condition
if ((i != j) and (curr_id == value->id))

> +			curr_id = counter->values[j].id;
> +			if (curr_id == value->id) {
> +				pr_err("Duplicate Counter '%d' Value '%d'\n",
> +					counter->id, value->id);
> +				return -EEXIST;
> +			}
> +		}
> +		for (j = i + 1; j < counter->num_values; j++) {
> +			curr_id = counter->values[j].id;
> +			if (curr_id == value->id) {
> +				pr_err("Duplicate Counter '%d' Value '%d'\n",
> +					counter->id, value->id);
> +				return -EEXIST;
> +			}
> +		}
> +
> +		/* At least one function mode must be defined for each Value */
> +		if (!value->num_function_modes || !value->function_modes) {
> +			pr_err("Counter '%d' Value '%d' function modes undefined\n",
> +				counter->id, value->id);
> +			return -EINVAL;
> +		}
> +
> +		/* Verify the Triggers associated with each Value */
> +		err = iio_counter_triggers_validate(counter->id, value);
> +		if (err)
> +			return err;
> +	}
> +
> +	return 0;
> +}
> +
> +/**
> + * iio_counter_register - register Counter to the system
> + * @counter: pointer to IIO Counter to register
> + *
> + * This function piggybacks off of iio_device_register. First, the relevant
> + * Counter members are validated; the signals and values members must be defined
> + * and populated by valid Signal and Value structures respectively. Next, a
> + * struct iio_dev is allocated by a call to iio_device_alloc and initialized for
> + * the Counter, IIO channels are allocated, the Counter address is stored as the
> + * private data, and finally iio_device_register is called.
> + */
> +int iio_counter_register(struct iio_counter *const counter)
> +{
> +	const struct iio_info info_default = {
> +		.read_raw = iio_counter_read_raw,
> +		.write_raw = iio_counter_write_raw
> +	};
> +	int err;
> +	struct iio_info *info;
> +	struct iio_counter **priv;
> +
> +	if (!counter)
> +		return -EINVAL;
> +
> +	/* Verify that Signals are valid and IDs do not conflict */
> +	err = iio_counter_signals_validate(counter);
> +	if (err)
> +		return err;
> +
> +	/* Verify that Values are valid and IDs do not conflict;
Fix multiline comment syntax to standard kernel syntax.
/*
 * Verify
 */
> +	 * Triggers for each Value are also verified for validity
> +	 */
> +	err = iio_counter_values_validate(counter);
> +	if (err)
> +		return err;
> +
> +	counter->indio_dev = iio_device_alloc(sizeof(counter));
> +	if (!counter->indio_dev)
> +		return -ENOMEM;
> +
> +	info = kmalloc(sizeof(*info), GFP_KERNEL);
> +	if (!info) {
> +		err = -ENOMEM;
> +		goto err_info_alloc;
> +	}
> +	/* If an iio_info has been supplied than we use that,
> +	 * otherwise we set all callbacks to NULL; iio_counter_read_raw
> +	 * and iio_counter_write_raw is used for read_raw and write_raw
> +	 * for either case in order to support counter functionality
> +	 * (supplied read_raw/write_raw will be called from within
> +	 * iio_counter_read_raw/iio_counter_write_raw for non-counter
> +	 * channels)
> +	 */

> +	if (counter->info) {
> +		memcpy(info, counter->info, sizeof(*counter->info));
> +		info->read_raw = iio_counter_read_raw;
> +		info->write_raw = iio_counter_write_raw;
> +	} else {
> +		memcpy(info, &info_default, sizeof(info_default));
> +	}
> +
> +	counter->indio_dev->info = info;
> +	counter->indio_dev->modes = INDIO_DIRECT_MODE;
> +	counter->indio_dev->name = counter->name;
> +	counter->indio_dev->dev.parent = counter->dev;
> +
> +	/* IIO channels are allocated and set for Signals, Values, and Triggers;
> +	 * any auxiliary IIO channels provided in iio_counter are also set
> +	 */
> +	err = iio_counter_channels_alloc(counter);
> +	if (err)
> +		goto err_channels_alloc;
> +
> +	/* Pointer to the counter is stored in indio_dev as a way to refer
> +	 * back to the counter from within various IIO callbacks
> +	 */
> +	priv = iio_priv(counter->indio_dev);
> +	memcpy(priv, &counter, sizeof(*priv));
> +
> +	err = iio_device_register(counter->indio_dev);
> +	if (err)
> +		goto err_iio_device_register;
> +
> +	return 0;
> +
> +err_iio_device_register:
> +	iio_counter_channels_free(counter);
> +err_channels_alloc:
> +	kfree(info);
> +err_info_alloc:
> +	iio_device_free(counter->indio_dev);
> +	return err;
> +}
> +EXPORT_SYMBOL(iio_counter_register);
> +
> +/**
> + * iio_counter_unregister - unregister Counter from the system
> + * @counter: pointer to IIO Counter to unregister
> + *
> + * The Counter is unregistered from the system. The indio_dev is unregistered
> + * and all allocated memory is freed.
> + */
> +void iio_counter_unregister(struct iio_counter *const counter)
> +{
> +	const struct iio_info *const info = counter->indio_dev->info;
> +
> +	if (!counter)
> +		return;
> +
> +	iio_device_unregister(counter->indio_dev);
> +
> +	iio_counter_channels_free(counter);
> +
> +	kfree(info);
> +	iio_device_free(counter->indio_dev);
> +}
> +EXPORT_SYMBOL(iio_counter_unregister);
> +
> +static void devm_iio_counter_unreg(struct device *dev, void *res)
> +{
> +	iio_counter_unregister(*(struct iio_counter **)res);
> +}
> +
> +/**
> + * devm_iio_counter_register - Resource-managed iio_counter_register
> + * @dev: Device to allocate iio_counter for
> + * @counter: pointer to IIO Counter to register
> + *
> + * Managed iio_counter_register. The IIO counter registered with this
> + * function is automatically unregistered on driver detach. This function
> + * calls iio_counter_register internally. Refer to that function for more
> + * information.
> + *
> + * If an iio counter registered with this function needs to be unregistered
> + * separately, devm_iio_counter_unregister must be used.
> + *
> + * RETURNS:
> + * 0 on success, negative error number on failure.
> + */
> +int devm_iio_counter_register(struct device *dev,
> +	struct iio_counter *const counter)
> +{
> +	struct iio_counter **ptr;
> +	int ret;
> +
> +	ptr = devres_alloc(devm_iio_counter_unreg, sizeof(*ptr), GFP_KERNEL);
> +	if (!ptr)
> +		return -ENOMEM;
> +
> +	*ptr = counter;
> +	ret = iio_counter_register(counter);
> +	if (!ret)
> +		devres_add(dev, ptr);
> +	else
> +		devres_free(ptr);
> +
> +	return ret;
> +}
> +EXPORT_SYMBOL(devm_iio_counter_register);
> +
> +static int devm_iio_counter_match(struct device *dev, void *res, void *data)
> +{
> +	struct iio_counter **r = res;
> +
> +	if (!r || !*r) {
> +		WARN_ON(!r || !*r);
> +		return 0;
> +	}
> +
> +	return *r == data;
> +}
> +
> +/**
> + * devm_iio_counter_unregister - Resource-managed iio_counter_unregister
> + * @dev: Device this iio_counter belongs to
> + * @counter: the iio counter associated with the device
> + *
> + * Unregister iio counter registered with devm_iio_counter_register.
> + */
> +void devm_iio_counter_unregister(struct device *dev,
> +	struct iio_counter *const counter)
> +{
> +	int rc;
> +
> +	rc = devres_release(dev, devm_iio_counter_unreg,
> +		devm_iio_counter_match, counter);
> +	WARN_ON(rc);
> +}
> +EXPORT_SYMBOL(devm_iio_counter_unregister);
> diff --git a/include/linux/iio/counter.h b/include/linux/iio/counter.h
> new file mode 100644
> index 000000000000..35645406711a
> --- /dev/null
> +++ b/include/linux/iio/counter.h
> @@ -0,0 +1,166 @@
> +/*
> + * Industrial I/O counter interface
> + * Copyright (C) 2017 William Breathitt Gray
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License, version 2, as
> + * published by the Free Software Foundation.
> + *
> + * This program is distributed in the hope that it will be useful, but
> + * WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
> + * General Public License for more details.
> + */
> +#ifndef _IIO_COUNTER_H_
> +#define _IIO_COUNTER_H_
> +
> +#ifdef CONFIG_IIO_COUNTER
> +
> +#include <linux/device.h>
> +#include <linux/types.h>
> +
> +#include <linux/iio/iio.h>
> +
> +/**
> + * struct iio_counter_signal - IIO Counter Signal node
> + * @id:		[DRIVER] unique ID used to identify signal
> + * @name:	[DRIVER] device-specific signal name
> + */
> +struct iio_counter_signal {
> +	int		id;
> +	const char	*name;
> +};
> +
> +/**
> + * struct iio_counter_trigger - IIO Counter Trigger node
> + * @mode:		[DRIVER] current trigger mode state
> + * @trigger_modes:	[DRIVER] available trigger modes
> + * @num_trigger_modes:	[DRIVER] number of modes specified in @trigger_modes
> + * @signal:		[DRIVER] pointer to associated signal
> + */
> +struct iio_counter_trigger {
> +	unsigned int			mode;
> +	const char *const		*trigger_modes;
> +	unsigned int			num_trigger_modes;
> +	struct iio_counter_signal	*signal;

Are signals actually changeable? - Or is that pointer constant?

> +};
> +
> +/**
> + * struct iio_counter_value - IIO Counter Value node
> + * @id:			[DRIVER] unique ID used to identify value
> + * @name:		[DRIVER] device-specific value name
> + * @mode:		[DRIVER] current function mode state
> + * @function_modes:	[DRIVER] available function modes
> + * @num_function_modes:	[DRIVER] number of modes specified in @function_modes
> + * @triggers:		[DRIVER] array of triggers for initialization
> + * @num_triggers:	[DRIVER] number of triggers specified in @triggers
> + * @function_enum:	[INTERN] used internally to generate function attributes
> + */
> +struct iio_counter_value {
> +	int			id;
> +	const char		*name;
> +	unsigned int		mode;
> +	const char *const	*function_modes;
> +	unsigned int		num_function_modes;
> +
> +	struct iio_counter_trigger	*triggers;
> +	size_t				num_triggers;
> +
> +	struct iio_enum		function_enum;
> +};
> +
> +struct iio_counter;
> +
> +/**
> + * struct iio_counter_ops - IIO Counter related callbacks
> + * @signal_read:	function to request a signal value from the device.
> + *			Return value will specify the type of value returned by
> + *			the device. val and val2 will contain the elements
> + *			making up the returned value.

Add some detail on the importance of the return value.

> + * @signal_write:	function to write a signal value to the device.
> + *                      Parameters are interpreted the same as signal_read.

You need a means of establishing the correct format for write (we needed it
in IIO fairly early on too)

> + * @trigger_mode_set:	function to set the trigger mode. mode is the index of
> + *			the requested mode from the value trigger_modes array.
> + * @trigger_mode_get:	function to get the current trigger mode. Return value
> + *			will specify the index of the current mode from the
> + *			value trigger_modes array.
> + * @value_read:		function to request a value value from the device.
> + *			Return value will specify the type of value returned by
> + *			the device. val and val2 will contain the elements
> + *			making up the returned value.
> + * @value_write:	function to write a value value to the device.
> + *                      Parameters are interpreted the same as value_read.
> + * @value_function_set: function to set the value function mode. mode is the
> + *			index of the requested mode from the value
> + *			function_modes array.
> + * @value_function_get: function to get the current value function mode. Return
> + *			value will specify the index of the current mode from
> + *			the value function_modes array.
> + */
> +struct iio_counter_ops {
> +	int (*signal_read)(struct iio_counter *counter,
> +		struct iio_counter_signal *signal, int *val, int *val2);
> +	int (*signal_write)(struct iio_counter *counter,
> +		struct iio_counter_signal *signal, int val, int val2);
> +	int (*trigger_mode_set)(struct iio_counter *counter,
> +		struct iio_counter_value *value,
> +		struct iio_counter_trigger *trigger, unsigned int mode);
> +	int (*trigger_mode_get)(struct iio_counter *counter,
> +		struct iio_counter_value *value,
> +		struct iio_counter_trigger *trigger);
> +	int (*value_read)(struct iio_counter *counter,
> +		struct iio_counter_value *value, int *val, int *val2);
> +	int (*value_write)(struct iio_counter *counter,
> +		struct iio_counter_value *value, int val, int val2);
> +	int (*value_function_set)(struct iio_counter *counter,
> +		struct iio_counter_value *value, unsigned int mode);
> +	int (*value_function_get)(struct iio_counter *counter,
> +		struct iio_counter_value *value);
> +};
> +
> +/**
> + * struct iio_counter - IIO Counter data structure
> + * @id:			[DRIVER] unique ID used to identify counter
> + * @name:		[DRIVER] name of the device
> + * @dev:		[DRIVER] device structure, should be assigned a parent
> + *			and owner
> + * @ops:		[DRIVER] callbacks from driver for counter components
> + * @signals:		[DRIVER] array of signals for initialization
> + * @num_signals:	[DRIVER] number of signals specified in @signals
> + * @values:		[DRIVER] array of values for initialization
> + * @num_values:		[DRIVER] number of values specified in @values
> + * @channels:		[DRIVER] channel specification structure table
> + * @num_channels:	[DRIVER] number of channels specified in @channels
> + * @info:		[DRIVER] callbacks and constant info from driver
> + * @indio_dev:		[INTERN] industrial I/O device structure
> + * @driver_data:	[DRIVER] driver data
> + */
> +struct iio_counter {

Mentioned earlier - I think naming the device counter, is confusing.
The counters should be named counter - call it iio_counter_device
or iio_counter_group or something and keep counter for the things
currently called value.

> +	int				id;
> +	const char			*name;
> +	struct device			*dev;
> +	const struct iio_counter_ops	*ops;
> +
> +	struct iio_counter_signal	*signals;
> +	size_t				num_signals;
> +	struct iio_counter_value	*values;
> +	size_t				num_values;
> +
> +	const struct iio_chan_spec	*channels;
> +	size_t				num_channels;
> +	const struct iio_info		*info;
> +
> +	struct iio_dev	*indio_dev;
> +	void		*driver_data;
> +};
> +
> +int iio_counter_register(struct iio_counter *const counter);
> +void iio_counter_unregister(struct iio_counter *const counter);
> +int devm_iio_counter_register(struct device *dev,
> +	struct iio_counter *const counter);
> +void devm_iio_counter_unregister(struct device *dev,
> +	struct iio_counter *const counter);
> +
> +#endif /* CONFIG_IIO_COUNTER */
> +
> +#endif /* _IIO_COUNTER_H_ */

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

* Re: [PATCH v3 0/6] iio: Introduce the generic counter interface
  2017-10-05 18:13 [PATCH v3 0/6] iio: Introduce the generic counter interface William Breathitt Gray
                   ` (5 preceding siblings ...)
  2017-10-05 18:14 ` [PATCH v3 6/6] iio: 104-quad-8: Add IIO generic counter interface support William Breathitt Gray
@ 2017-10-08 14:38 ` Jonathan Cameron
  6 siblings, 0 replies; 17+ messages in thread
From: Jonathan Cameron @ 2017-10-08 14:38 UTC (permalink / raw)
  To: William Breathitt Gray
  Cc: benjamin.gaignard, knaack.h, lars, pmeerw, linux-iio, linux-kernel

On Thu,  5 Oct 2017 14:13:14 -0400
William Breathitt Gray <vilhelm.gray@gmail.com> wrote:

> There have been some significant implementation changes for this version
> of the patchset. Here's a brief summary of the updates:
> 
>  - Inline comments throughout the industrial-counter.c file; this
>    should help clarify some of the code and aid in the review of the
>    system architecture
> 
>  - Simplification of the driver API; primarily, dynamic updates to the
>    Counter Values, Triggers, and Signals lists are no longer allowed --
>    all relationships must be defined before the Counter is registered to
>    the system
> 
>  - Dynamic component registration functions
>    (iio_counter_value_register, et al.) have been removed, and now the
>    signals, values, and triggers array members serve to register the
>    components; signals, values, and triggers were previously named
>    init_signals, init_values, and init_triggers respectively -- the
>    "init_" prefix was removed now that dynamic registration is no longer
>    allowed
> 
>  - Requiring static component relationships means linked lists are no
>    longer necessary so all the list related code has been removed;
>    Signals, Triggers, and Values are now accessed directly from the
>    arrays supplied in the iio_counter structure
> 
>  - No dynamic additions/substraction of components eliminates the need
>    for mutex locks, so those have been removed as well
> 
>  - Previously, the entire iio_counter structure was copied and stored as
>    the iio_dev private data; now, only a pointer to the supplied
>    iio_counter structure is stored
> 
>  - devm_iio_counter_register and devm_iio_counter_unregister functions
>    were incorporated from Benjamin Gaignard's suggestions
> 
>  - Dummy counter driver provided as a reference for a simple counter
>    implementation utilizing the Generic Counter interface
> 
> In a previous discussion, Benjamin noted some conflicts between the
> Generic Counter and the underlying IIO core code: mapping does not
> always work out seemlessly and sometimes the IIO core functionality
> exposure provided via iio_counter is not quite flexible enough. I intend
> to address these shortcomings in the next version of this patchset
> hopefully.
> 
> In particular, I'm leaning towards separating the Generic Counter
> interface from the IIO core dependency entirely. Since the Generic
> Counter inteface focuses on a more abstract representation of a counter
> device, I don't think it provides a suitable interface to map onto IIO
> core, which appears to focus more on a representation of the physical
> hardware itself.
> 
> Separating Generic Counter from IIO core should allow a driver to use
> the IIO core functions (iio_device_register, et al.) to represent the
> physical nature of their hardware (voltages, currents, etc.), while also
> utilize the Generic Counter interface to represent the abstract
> relationships between those Signals and their ultimate counter Values.
> 
> In particular, I'm hoping for the Generic Counter system implementation
> itself to not require all this hoop-jumping (mapping to IIO core
> functions, jumping back to the parent iio_counter, handling non-matching
> parameter lists between iio_counter and iio_dev, etc.); that should make
> the code simpler to debug, more efficient, and more stable in the
> long-run. I will consider these advantages and disadvantages before
> committing to a separation however.

Key things here:
1) userspace ABI - this bit we need to get right
2) driver ABI - right now we'll irritate a few rather nice and hopefully calm
   people - so it doesn't matter much :)
3) Maintainability.  The key thing is to end up with something that is
   easy to look after.  Particularly when the inevitable happens and you 
   can no longer remember how some corner of your own code works.
> 
> Regarding this version of the patchset in particular, I decided to
> remove the dynamic component registration functionality since I doubt
> that many devices would require such; almost all, if not all, hardware
> counters I encountered have static relationships between input lines and
> the count value (i.e. specific input lines correlate to specific count
> registers).

Where this is not true of the bit we are writing a driver for the
wider system should impose this relationship in most cases.
The only exception would be where the counter resources are more
constrained than the hardware it is interfaced to.  So say we have
2 hardware counters on a system that has 4 encoders but there is
some underlying reason why we don't need to use all the encoders
at the same time...  We can probably figure out how to represent
that though even with the initialization time mappings etc.

Something to fix when we care - not from the outset!

> 
> The requirement that Counter component relationships are
> static has opened the possibility of some optimizations in the Generic
> Counter interface code:
> 
>  - Signals, Triggers, and Values are provided as arrays; these arrays
>    could benefit from some amount of sorting based on component IDs
> 
>  - Searching for a component can be more efficient in a sorted array;
>    currently, the code just walks down the array elements and compare
>    the IDs

I raise the point that mostly (I think) the drivers know exactly where
to look in these arrays from the index.  My inclination is to give
them the ability to provide that knowledge to the core.  I wouldn't
worry too much for now. There are lots of ways of dealing with the
small performance enhancements once the rest is sorted - they don't
effect the ABI so we can try what we like ;)

> 
>  - Similar to general searching, the arrays are initially verified to
>    gurantee unique IDs; currently, the code walks down the array
>    elements and compares the current element ID with all the elements
>    before and after -- this could be made more efficient with some
>    sorting

It's not a fast path - don't bother. You'll add code complexity to little
practical gain - 0.1 seconds on boot up or something less than that...

> 
> There are likely other areas of improvement, so let me know and I'll see
> what I can do.

It comes up in the individual patch reviews but my main takeaways would be:

1) Naming - we need to deal with trigger (and I think also value -> counter)
   to limit confusion.
2) Fuller definitions in documentation.  Good start on the docs, but you need
   to be specifying allowable values for parameters as that's what a userspace 
   interface will need to know.

Anyhow, sorry it took me so long to get to it and looking forward to the next
revision!

Jonathan
> 
> William Breathitt Gray (6):
>   iio: Implement counter channel specification and IIO_SIGNAL constant
>   iio: Introduce the generic counter interface
>   iio: Documentation: Add IIO Generic Counter sysfs documentation
>   docs: Add IIO Generic Counter Interface documentation
>   iio: Add dummy counter driver
>   iio: 104-quad-8: Add IIO generic counter interface support
> 
>  .../testing/sysfs-bus-iio-generic-counter-sysfs    |  63 ++
>  Documentation/driver-api/iio/generic-counter.txt   | 526 ++++++++++++
>  MAINTAINERS                                        |   7 +
>  drivers/iio/Kconfig                                |   8 +
>  drivers/iio/Makefile                               |   1 +
>  drivers/iio/counter/104-quad-8.c                   | 294 ++++++-
>  drivers/iio/counter/Kconfig                        |  16 +
>  drivers/iio/counter/Makefile                       |   1 +
>  drivers/iio/counter/dummy-counter.c                | 293 +++++++
>  drivers/iio/industrialio-core.c                    |  14 +-
>  drivers/iio/industrialio-counter.c                 | 900 +++++++++++++++++++++
>  include/linux/iio/counter.h                        | 166 ++++
>  include/linux/iio/iio.h                            |   2 +
>  include/uapi/linux/iio/types.h                     |   1 +
>  14 files changed, 2274 insertions(+), 18 deletions(-)
>  create mode 100644 Documentation/ABI/testing/sysfs-bus-iio-generic-counter-sysfs
>  create mode 100644 Documentation/driver-api/iio/generic-counter.txt
>  create mode 100644 drivers/iio/counter/dummy-counter.c
>  create mode 100644 drivers/iio/industrialio-counter.c
>  create mode 100644 include/linux/iio/counter.h
> 

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

* Re: [PATCH v3 5/6] iio: Add dummy counter driver
  2017-10-08 13:41   ` Jonathan Cameron
@ 2017-10-09 12:35     ` Benjamin Gaignard
  0 siblings, 0 replies; 17+ messages in thread
From: Benjamin Gaignard @ 2017-10-09 12:35 UTC (permalink / raw)
  To: Jonathan Cameron
  Cc: William Breathitt Gray, Hartmut Knaack, Lars-Peter Clausen,
	Peter Meerwald-Stadler, linux-iio, Linux Kernel Mailing List

2017-10-08 15:41 GMT+02:00 Jonathan Cameron <jic23@kernel.org>:
> On Thu,  5 Oct 2017 14:14:38 -0400
> William Breathitt Gray <vilhelm.gray@gmail.com> wrote:
>
>> This patch introduces the dummy counter driver. The dummy counter driver
>> serves as a reference implementation of a driver that utilizes the
>> Generic Counter interface.
>
> This is great - I was planning to write one of these to try out the
> interface and you've already done it :)

May I suggest you to write a gpio-counter driver instead ? (that was
in my todo list)
It is more or less the same than this driver but instead of simulate the signal
it could use gpio to them provide a software quadratic counter.

Benjamin

>>
>> Writing individual '1' and '0' characters to the Signal attributes
>> allows a user to simulate a typical Counter Signal input stream for
>> evaluation; the Counter will evaluate the Signal data based on the
>> respective trigger mode for the associated Signal, and trigger the
>> associated counter function specified by the respective function mode.
>> The current Value value may be read, and the Value value preset by a
>> write.
>>
>> Signed-off-by: William Breathitt Gray <vilhelm.gray@gmail.com>
>
> Comments are more generic suggestions for improving the example than
> general comments on the ABI - that all seems to make reasonable sense
> (other than when the documents contain the wonderful
> Value value - not confusing at all ;)
>
>> ---
>>  drivers/iio/counter/Kconfig         |  15 ++
>>  drivers/iio/counter/Makefile        |   1 +
>>  drivers/iio/counter/dummy-counter.c | 293 ++++++++++++++++++++++++++++++++++++
>>  3 files changed, 309 insertions(+)
>>  create mode 100644 drivers/iio/counter/dummy-counter.c
>>
>> diff --git a/drivers/iio/counter/Kconfig b/drivers/iio/counter/Kconfig
>> index c8becfe78e28..494aed40e9c9 100644
>> --- a/drivers/iio/counter/Kconfig
>> +++ b/drivers/iio/counter/Kconfig
>> @@ -22,6 +22,21 @@ config 104_QUAD_8
>>         The base port addresses for the devices may be configured via the base
>>         array module parameter.
>>
>> +config DUMMY_COUNTER
>> +     tristate "Dummy counter driver"
>> +     help
>> +       Select this option to enable the dummy counter driver. The dummy
>> +       counter driver serves as a reference implementation of a driver that
>> +       utilizes the Generic Counter interface.
>> +
>> +       Writing individual '1' and '0' characters to the Signal attributes
>> +       allows a user to simulate a typical Counter Signal input stream for
>> +       evaluation; the Counter will evaluate the Signal data based on the
>> +       respective trigger mode for the associated Signal, and trigger the
>> +       associated counter function specified by the respective function mode.
>> +       The current Value value may be read, and the Value value preset by a
>> +       write.
>> +
>>  config STM32_LPTIMER_CNT
>>       tristate "STM32 LP Timer encoder counter driver"
>>       depends on MFD_STM32_LPTIMER || COMPILE_TEST
>> diff --git a/drivers/iio/counter/Makefile b/drivers/iio/counter/Makefile
>> index 1b9a896eb488..8c2ef0115426 100644
>> --- a/drivers/iio/counter/Makefile
>> +++ b/drivers/iio/counter/Makefile
>> @@ -5,4 +5,5 @@
>>  # When adding new entries keep the list in alphabetical order
>>
>>  obj-$(CONFIG_104_QUAD_8)     += 104-quad-8.o
>> +obj-$(CONFIG_DUMMY_COUNTER)  += dummy-counter.o
>>  obj-$(CONFIG_STM32_LPTIMER_CNT)      += stm32-lptimer-cnt.o
>> diff --git a/drivers/iio/counter/dummy-counter.c b/drivers/iio/counter/dummy-counter.c
>> new file mode 100644
>> index 000000000000..6ecc9854894f
>> --- /dev/null
>> +++ b/drivers/iio/counter/dummy-counter.c
>> @@ -0,0 +1,293 @@
>> +/*
>> + * Dummy counter driver
>> + * Copyright (C) 2017 William Breathitt Gray
>> + *
>> + * This program is free software; you can redistribute it and/or modify
>> + * it under the terms of the GNU General Public License, version 2, as
>> + * published by the Free Software Foundation.
>> + *
>> + * This program is distributed in the hope that it will be useful, but
>> + * WITHOUT ANY WARRANTY; without even the implied warranty of
>> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
>> + * General Public License for more details.
>> + */
>> +#include <linux/device.h>
>> +#include <linux/errno.h>
>> +#include <linux/iio/counter.h>
>> +#include <linux/kernel.h>
>> +#include <linux/module.h>
>> +#include <linux/platform_device.h>
>> +#include <linux/types.h>
>> +
>> +#define DUMCNT_NUM_COUNTERS 2
>> +/**
>> + * struct dumcnt - private data structure
>> + * @counter: instance of the iio_counter
>> + * @counts:  array of accumulation values
>> + * @states:  array of input line states
>> + */
>> +struct dumcnt {
>> +     struct iio_counter counter;
>> +     unsigned int counts[DUMCNT_NUM_COUNTERS];
>> +     unsigned int states[DUMCNT_NUM_COUNTERS];
>> +};
>> +
>> +static int dumcnt_signal_read(struct iio_counter *counter,
>> +     struct iio_counter_signal *signal, int *val, int *val2)
>> +{
>> +     struct dumcnt *const priv = counter->driver_data;
>> +     *val = priv->states[signal->id];
>> +
>> +     return IIO_VAL_INT;
>> +}
>> +
>> +static int dumcnt_signal_write(struct iio_counter *counter,
>> +     struct iio_counter_signal *signal, int val, int val2)
>> +{
>
> This is an odd one to have in the generic interface.
> What real hardware does writing the state make sense for?
> If it is only fake drivers - figure out a way to do it without
> having to add to the generic interfaces.
>
>> +     struct dumcnt *const priv = counter->driver_data;
>> +     const unsigned int id = signal->id;
>> +     const unsigned int prev_state = priv->states[id];
>> +     struct iio_counter_value *const value = counter->values + id;
>> +     const unsigned int function_mode = value->mode;
>> +     const unsigned int trigger_mode = value->triggers[0].mode;
>> +     unsigned int triggered = 0;
>> +
>> +     if (val && val != 1)
>> +             return -EINVAL;
>> +
>> +     /* If no state change then just exit */
>> +     if (prev_state == val)
>> +             return 0;
>> +
>> +     priv->states[id] = val;
>> +
>> +     switch (trigger_mode) {
>> +     /* "none" case */
>> +     case 0:
>> +             return 0;
>> +     /* "rising edge" case */
>> +     case 1:
>> +             if (!prev_state)
>> +                     triggered = 1;
>> +             break;
>> +     /* "falling edge" case */
>> +     case 2:
>> +             if (prev_state)
>> +                     triggered = 1;
>> +             break;
>> +     /* "both edges" case */
>> +     case 3:
>> +             triggered = 1;
>> +             break;
>> +     }
>> +
>> +     /* If counter function triggered */
>> +     if (triggered)
>> +             /* "increase" case */
>> +             if (function_mode)
>> +                     priv->counts[id]++;
>> +             /* "decrease" case */
>> +             else
>> +                     priv->counts[id]--;
>> +
>> +     return 0;
>> +}
>> +
>> +static int dumcnt_trigger_mode_set(struct iio_counter *counter,
>> +     struct iio_counter_value *value, struct iio_counter_trigger *trigger,
>> +     unsigned int mode)
>> +{
>> +     if (mode >= trigger->num_trigger_modes)
>> +             return -EINVAL;
>> +
>> +     trigger->mode = mode;
>> +
>> +     return 0;
>> +}
>> +
>> +static int dumcnt_trigger_mode_get(struct iio_counter *counter,
>> +     struct iio_counter_value *value, struct iio_counter_trigger *trigger)
>> +{
>> +     return trigger->mode;
>> +}
>> +
>> +static int dumcnt_value_read(struct iio_counter *counter,
>> +     struct iio_counter_value *value, int *val, int *val2)
>> +{
>> +     struct dumcnt *const priv = counter->driver_data;
>> +
>> +     *val = priv->counts[value->id];
>> +
>> +     return IIO_VAL_INT;
>> +}
>> +
>> +static int dumcnt_value_write(struct iio_counter *counter,
>> +     struct iio_counter_value *value, int val, int val2)
>> +{
>> +     struct dumcnt *const priv = counter->driver_data;
>> +
>> +     priv->counts[value->id] = val;
>> +
>> +     return 0;
>> +}
>> +
>> +static int dumcnt_value_function_set(struct iio_counter *counter,
>> +     struct iio_counter_value *value, unsigned int mode)
>> +{
>> +     if (mode >= value->num_function_modes)
>> +             return -EINVAL;
>> +
>> +     value->mode = mode;
>> +
>> +     return 0;
>> +}
>> +
>> +static int dumcnt_value_function_get(struct iio_counter *counter,
>> +     struct iio_counter_value *value)
>> +{
>> +     return value->mode;
>
> If it's called function in the function name, call it function in
> the structure as well rather than mode.
>
>> +}
>> +
>> +static const struct iio_counter_ops dumcnt_ops = {
>> +     .signal_read = dumcnt_signal_read,
>> +     .signal_write = dumcnt_signal_write,
>> +     .trigger_mode_get = dumcnt_trigger_mode_get,
>> +     .trigger_mode_set = dumcnt_trigger_mode_set,
>> +     .value_read = dumcnt_value_read,
>> +     .value_write = dumcnt_value_write,
>> +     .value_function_set = dumcnt_value_function_set,
>> +     .value_function_get = dumcnt_value_function_get
>> +};
>> +
>> +static const char *const dumcnt_function_modes[] = {
>> +     "decrease",
>
> I think increment was used somewhere in the docs... It's
> clearer, but you need to document this ABI to stop having
> subtle variations of it like this (even if I imagined it ;)
>
>> +     "increase"
>> +};
>> +
>> +#define DUMCNT_SIGNAL(_id, _name) {  \
>> +     .id = _id,                      \
>> +     .name = _name                   \
>> +}
>> +
>> +static const struct iio_counter_signal dumcnt_signals[] = {
>> +     DUMCNT_SIGNAL(0, "Signal A"), DUMCNT_SIGNAL(1, "Signal B")
>> +};
>> +
>> +#define DUMCNT_VALUE(_id, _name) {                                   \
>> +     .id = _id,                                                      \
>> +     .name = _name,                                                  \
>> +     .mode = 0,                                                      \
>> +     .function_modes = dumcnt_function_modes,                        \
>> +     .num_function_modes = ARRAY_SIZE(dumcnt_function_modes)         \
>> +}
>> +
>> +static const struct iio_counter_value dumcnt_values[] = {
>> +     DUMCNT_VALUE(0, "Count A"), DUMCNT_VALUE(1, "Count B")
>> +};
>> +
>> +static const char *const dumcnt_trigger_modes[] = {
>
> As mentioned below, use an enum for the index as then you can make it obvious
> what 0 means when you set the mode to it later.
>
>> +     "none",
>> +     "rising edge",
>> +     "falling edge",
>> +     "both edges"
>> +};
>> +
>> +static int dumcnt_probe(struct platform_device *pdev)
>> +{
>> +     struct device *dev = &pdev->dev;
>> +     struct iio_counter_signal *signals;
>> +     const size_t num_signals = ARRAY_SIZE(dumcnt_signals);
>
> Don't bother with local variable - makes it less obvious what
> is going on.
>
>> +     struct iio_counter_value *values;
>> +     const size_t num_values = ARRAY_SIZE(dumcnt_values);
>
> Local variable doesn't add anything and if anything makes
> it slightly harder to check what is going on.
>
>> +     struct iio_counter_trigger *triggers;
>> +     int i;
>> +     struct dumcnt *dumcnt;
>> +
>> +     signals = devm_kmalloc(dev, sizeof(dumcnt_signals), GFP_KERNEL);
>> +     if (!signals)
>> +             return -ENOMEM;
>> +
>> +     memcpy(signals, dumcnt_signals, sizeof(dumcnt_signals));
>
> devm_kmemdup?
>> +
>> +     values = devm_kmalloc(dev, sizeof(dumcnt_values), GFP_KERNEL);
>> +     if (!values)
>> +             return -ENOMEM;
>> +
>> +     memcpy(values, dumcnt_values, sizeof(dumcnt_values));
>
> devm_kmemdup?
>
>> +
>> +     /* Associate values with their respective signals */
>> +     for (i = 0; i < num_values; i++) {
>> +             triggers = devm_kmalloc(dev, sizeof(*triggers), GFP_KERNEL);
>> +             if (!triggers)
>> +                     return -ENOMEM;
>> +
>> +             triggers->mode = 0;
>
> Use an enum for the dumcn_trigger_modes array index then specify by enum value
> here.  Will make it more readable.
>
>> +             triggers->trigger_modes = dumcnt_trigger_modes;
>> +             triggers->num_trigger_modes = ARRAY_SIZE(dumcnt_trigger_modes);
>> +             triggers->signal = &signals[i];
>> +
>> +             values[i].triggers = triggers;
>> +             values[i].num_triggers = 1;
>> +     }
>> +
>> +     dumcnt = devm_kzalloc(dev, sizeof(*dumcnt), GFP_KERNEL);
>> +     if (!dumcnt)
>> +             return -ENOMEM;
>> +
>> +     dumcnt->counter.name = dev_name(dev);
>> +     dumcnt->counter.dev = dev;
>> +     dumcnt->counter.ops = &dumcnt_ops;
>> +     dumcnt->counter.signals = signals;
>> +     dumcnt->counter.num_signals = num_signals;
>> +     dumcnt->counter.values = values;
>> +     dumcnt->counter.num_values = num_values;
>> +     dumcnt->counter.driver_data = dumcnt;
>> +
>> +     return devm_iio_counter_register(dev, &dumcnt->counter);
>> +}
>> +
>> +static struct platform_device *dumcnt_device;
>
> Support multiple instances - nick this stuff from the
> IIO dummy driver or more specifically the
> industrialio-sw-device.c
>
>> +
>> +static struct platform_driver dumcnt_driver = {
>> +     .driver = {
>> +             .name = "104-quad-8"
>
> Don't do that!  Give it it's own name.
>
>> +     }
>> +};
>> +
>> +static void __exit dumcnt_exit(void)
>> +{
>> +     platform_device_unregister(dumcnt_device);
>> +     platform_driver_unregister(&dumcnt_driver);
>> +}
>> +
>> +static int __init dumcnt_init(void)
>> +{
>> +     int err;
>> +
>
> General thing, but if we are going to upstream this with the subsystem,
> make device instantiation happen via configfs.
>
>> +     dumcnt_device = platform_device_alloc(dumcnt_driver.driver.name, -1);
>> +     if (!dumcnt_device)
>> +             return -ENOMEM;
>> +
>> +     err = platform_device_add(dumcnt_device);
>> +     if (err)
>> +             goto err_platform_device;
>> +
>> +     err = platform_driver_probe(&dumcnt_driver, dumcnt_probe);
>> +     if (err)
>> +             goto err_platform_driver;
>> +
>> +     return 0;
>> +
>> +err_platform_driver:
>> +     platform_device_del(dumcnt_device);
>> +err_platform_device:
>> +     platform_device_put(dumcnt_device);
>> +     return err;
>> +}
>> +
>> +module_init(dumcnt_init);
>> +module_exit(dumcnt_exit);
>> +
>> +MODULE_AUTHOR("William Breathitt Gray <vilhelm.gray@gmail.com>");
>> +MODULE_DESCRIPTION("Dummy counter driver");
>> +MODULE_LICENSE("GPL v2");

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

* Re: [PATCH v3 2/6] iio: Introduce the generic counter interface
  2017-10-08 14:30   ` Jonathan Cameron
@ 2017-10-09 12:56       ` Benjamin Gaignard
  0 siblings, 0 replies; 17+ messages in thread
From: Benjamin Gaignard @ 2017-10-09 12:56 UTC (permalink / raw)
  To: Jonathan Cameron
  Cc: William Breathitt Gray, Hartmut Knaack, Lars-Peter Clausen,
	Peter Meerwald-Stadler, linux-iio, Linux Kernel Mailing List

2017-10-08 16:30 GMT+02:00 Jonathan Cameron <jic23@kernel.org>:
> On Thu,  5 Oct 2017 14:13:44 -0400
> William Breathitt Gray <vilhelm.gray@gmail.com> wrote:
>
>> This patch introduces the IIO generic counter interface for supporting
>> counter devices. The generic counter interface serves as a catch-all to
>> enable rudimentary support for devices that qualify as counters. More
>> specific and apt counter interfaces may be developed on top of the
>> generic counter interface, and those interfaces should be used by
>> drivers when possible rather than the generic counter interface.
>
> I think you probably want to avoid any additional layering and try to
> push things down here (maybe with a few utility functions to simplify
> common cases), but that's a question for another day!
>>
>> In the context of the IIO generic counter interface, a counter is
>> defined as a device that reports one or more "counter values" based on
>> the state changes of one or more "counter signals" as evaluated by a
>> defined "counter function."
>>
>> The IIO generic counter interface piggybacks off of the IIO core. This
>> is primarily used to leverage the existing sysfs setup: the IIO_COUNT
>> channel attributes represent the Counter Value, while the IIO_SIGNAL
>> channel attributes represent the Counter Signal; auxilary IIO_COUNT
>> attributes represent the Counter Signal connections (Triggers) and their
>> respective state change configurations which trigger an associated
>> "counter function" evaluation.
>>
>> The iio_counter_ops structure serves as a container for driver callbacks
>> to communicate with the device; function callbacks are provided to read
>> and write various Signals and Values, and set and get the "trigger mode"
>> and "function mode" for various Triggers and Values respectively.
>>
>> To support a counter device, a driver must first allocate the available
>> Counter Signals via iio_counter_signal structures. These Signals should
>> be stored as an array and set to the signals array member of an
>> allocated iio_counter structure before the Counter is registered to the
>> system.
>>
>> Counter Values may be allocated via iio_counter_value structures, and
>> respective Counter Signal associations (Triggers) made via
>> iio_counter_trigger structures. Associated iio_counter_trigger
>> structures are stored as an array and set to the the triggers array
>> member of the respective iio_counter_value structure. These
>> iio_counter_value structures are set to the values array member of an
>> allocated iio_counter structure before the Counter is registered to the
>> system.
>>
>> A counter device is registered to the system by passing the respective
>> initialized iio_counter structure to the iio_counter_register function;
>> similarly, the iio_counter_unregister function unregisters the
>> respective counter. The devm_iio_counter_register and
>> iio_devm_iio_counter_unregister functions serve as device memory-managed
>> versions of the iio_counter_register and iio_counter unregister
>> functions respectively.
>
> All the find by ids are the main complexity introduced by layering this
> on IIO that you probably wouldn't have if it wasn't so layered.  Hmm.
> I suggest inline that you could allow drivers to provide fast
> versions of that search given they can probably infer it directly from
> the index.  Might be something to ignore for now though in the interests
> of initial simplicity.
>
> Otherwise a few comments inline, but mostly this is coming together
> pretty well.  I'm actually surprised the layering on top of IIO didn't
> end up more painful than it did. It's not horrendous - which is not
> to say you wouldn't be better breaking free of that entirely...
> (I'm not sure either way).
>
> I think the big remaining questions are around naming more than anything
> else.  I don't like the name value when a user will think they are looking
> for a counter.  If you use counter though the current use of counter needs
> to change.
>
> Trigger is also, as has been raised, a rather overloaded term. Not totally
> obvious what a better term is but we shouldn't reuse that one...
>
> I'll try and think of something if you have no luck!
>
> Jonathan
>>
>> Signed-off-by: William Breathitt Gray <vilhelm.gray@gmail.com>
>> ---
>>  MAINTAINERS                        |   7 +
>>  drivers/iio/Kconfig                |   8 +
>>  drivers/iio/Makefile               |   1 +
>>  drivers/iio/counter/Kconfig        |   1 +
>>  drivers/iio/industrialio-counter.c | 900 +++++++++++++++++++++++++++++++++++++
>>  include/linux/iio/counter.h        | 166 +++++++
>>  6 files changed, 1083 insertions(+)
>>  create mode 100644 drivers/iio/industrialio-counter.c
>>  create mode 100644 include/linux/iio/counter.h
>>
>> diff --git a/MAINTAINERS b/MAINTAINERS
>> index 2281af4b41b6..8b7c37bed252 100644
>> --- a/MAINTAINERS
>> +++ b/MAINTAINERS
>> @@ -6693,6 +6693,13 @@ F:     Documentation/ABI/testing/sysfs-bus-iio-adc-envelope-detector
>>  F:   Documentation/devicetree/bindings/iio/adc/envelope-detector.txt
>>  F:   drivers/iio/adc/envelope-detector.c
>>
>> +IIO GENERIC COUNTER INTERFACE
>> +M:   William Breathitt Gray <vilhelm.gray@gmail.com>
>> +L:   linux-iio@vger.kernel.org
>> +S:   Maintained
>> +F:   drivers/iio/industrialio-counter.c
>> +F:   include/linux/iio/counter.h
>
> Don't forget the directory the drivers are going in ;)
> *laughs manically*
>> +
>>  IIO MULTIPLEXER
>>  M:   Peter Rosin <peda@axentia.se>
>>  L:   linux-iio@vger.kernel.org
>> diff --git a/drivers/iio/Kconfig b/drivers/iio/Kconfig
>> index b3c8c6ef0dff..78e01f4f5937 100644
>> --- a/drivers/iio/Kconfig
>> +++ b/drivers/iio/Kconfig
>> @@ -30,6 +30,14 @@ config IIO_CONFIGFS
>>         (e.g. software triggers). For more info see
>>         Documentation/iio/iio_configfs.txt.
>>
>> +config IIO_COUNTER
>> +     bool "Enable IIO counter support"
>> +     help
>> +       Provides IIO core support for counters. This API provides
>> +       a generic interface that serves as the building blocks to
>> +       create more complex counter interfaces. Rudimentary support
>> +       for counters is enabled.
>> +
>>  config IIO_TRIGGER
>>       bool "Enable triggered sampling support"
>>       help
>> diff --git a/drivers/iio/Makefile b/drivers/iio/Makefile
>> index 93c769cd99bf..6427ff38f964 100644
>> --- a/drivers/iio/Makefile
>> +++ b/drivers/iio/Makefile
>> @@ -5,6 +5,7 @@
>>  obj-$(CONFIG_IIO) += industrialio.o
>>  industrialio-y := industrialio-core.o industrialio-event.o inkern.o
>>  industrialio-$(CONFIG_IIO_BUFFER) += industrialio-buffer.o
>> +industrialio-$(CONFIG_IIO_COUNTER) += industrialio-counter.o
>>  industrialio-$(CONFIG_IIO_TRIGGER) += industrialio-trigger.o
>>
>>  obj-$(CONFIG_IIO_CONFIGFS) += industrialio-configfs.o
>> diff --git a/drivers/iio/counter/Kconfig b/drivers/iio/counter/Kconfig
>> index 474e1ac4e7c0..c8becfe78e28 100644
>> --- a/drivers/iio/counter/Kconfig
>> +++ b/drivers/iio/counter/Kconfig
>> @@ -4,6 +4,7 @@
>>  # When adding new entries keep the list in alphabetical order
>>
>>  menu "Counters"
>> +     depends on IIO_COUNTER
>>
>>  config 104_QUAD_8
>>       tristate "ACCES 104-QUAD-8 driver"
>> diff --git a/drivers/iio/industrialio-counter.c b/drivers/iio/industrialio-counter.c
>> new file mode 100644
>> index 000000000000..dfb982dae3a8
>> --- /dev/null
>> +++ b/drivers/iio/industrialio-counter.c
>> @@ -0,0 +1,900 @@
>> +/*
>> + * Industrial I/O counter interface functions
>> + * Copyright (C) 2017 William Breathitt Gray
>> + *
>> + * This program is free software; you can redistribute it and/or modify
>> + * it under the terms of the GNU General Public License, version 2, as
>> + * published by the Free Software Foundation.
>> + *
>> + * This program is distributed in the hope that it will be useful, but
>> + * WITHOUT ANY WARRANTY; without even the implied warranty of
>> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
>> + * General Public License for more details.
>> + */
>> +#include <linux/device.h>
>> +#include <linux/err.h>
>> +#include <linux/export.h>
>> +#include <linux/gfp.h>
>> +#include <linux/kernel.h>
>> +#include <linux/printk.h>
>> +#include <linux/slab.h>
>> +#include <linux/string.h>
>> +
>> +#include <linux/iio/iio.h>
>> +#include <linux/iio/types.h>
>> +
>> +#include <linux/iio/counter.h>
>> +
>> +#define iio_priv_get_counter(_indio_dev) \
>> +     (*(struct iio_counter **)iio_priv(_indio_dev))
>> +
>> +static struct iio_counter_signal *iio_counter_signal_find_by_id(
>> +     const struct iio_counter *const counter, const int id)
>> +{
>> +     size_t i;
>> +     struct iio_counter_signal *signal;
>> +
>> +     for (i = 0; i < counter->num_signals; i++) {
>> +             signal = counter->signals + i;
>> +             if (signal->id == id)
>> +                     return signal;
>> +     }
>> +
>> +     return NULL;
>> +}
>> +
>> +static struct iio_counter_trigger *iio_counter_trigger_find_by_id(
>> +     const struct iio_counter_value *const value, const int id)
>> +{
>> +     size_t i;
>> +     struct iio_counter_trigger *trigger;
>> +
>> +     for (i = 0; i < value->num_triggers; i++) {
>> +             trigger = value->triggers + i;
>> +             if (trigger->signal->id == id)
>> +                     return trigger;
>> +     }
>> +
>> +     return NULL;
>> +}
>> +
>> +static struct iio_counter_value *iio_counter_value_find_by_id(
>> +     const struct iio_counter *const counter, const int id)
>> +{
>> +     size_t i;
>> +     struct iio_counter_value *value;
>
> In most cases this mapping will be entirely obvious to the
> driver - perhaps provide an optional callback to let it
> provide it directly without any searching?
>
> We could always add this later though if we start getting
> drivers with lots of instances of the various parts...
>
>> +
>> +     for (i = 0; i < counter->num_values; i++) {
>> +             value = counter->values + i;
>> +             if (value->id == id)
>> +                     return value;
>> +     }
>> +
>> +     return NULL;
>> +}
>> +
>> +static ssize_t iio_counter_signal_name_read(struct iio_dev *indio_dev,
>> +     uintptr_t priv, const struct iio_chan_spec *chan, char *buf)
>> +{
>> +     struct iio_counter *const counter = iio_priv_get_counter(indio_dev);
>> +     const struct iio_counter_signal *signal;
>> +
>> +     signal = iio_counter_signal_find_by_id(counter, chan->channel2);
>> +     if (!signal)
>> +             return -EINVAL;
>> +
>> +     return scnprintf(buf, PAGE_SIZE, "%s\n", signal->name);
>> +}
>> +
>> +static ssize_t iio_counter_value_name_read(struct iio_dev *indio_dev,
>> +     uintptr_t priv, const struct iio_chan_spec *chan, char *buf)
>> +{
>> +     struct iio_counter *const counter = iio_priv_get_counter(indio_dev);
>> +     const struct iio_counter_value *value;
>> +
>> +     value = iio_counter_value_find_by_id(counter, chan->channel2);
>> +     if (!value)
>> +             return -EINVAL;
>> +
>> +     return scnprintf(buf, PAGE_SIZE, "%s\n", value->name);
>
> I mentioned before but I wonder if we can just push name down into
> the chan spec itself in some fashion.  Whether to associate it with
> existing datasheet_name or not is an open question however.
>
>> +}
>> +
>> +static ssize_t iio_counter_value_triggers_read(struct iio_dev *indio_dev,
>> +     uintptr_t priv, const struct iio_chan_spec *chan, char *buf)
>> +{
>> +     struct iio_counter *const counter = iio_priv_get_counter(indio_dev);
>> +     struct iio_counter_value *value;
>> +     size_t i;
>> +     const struct iio_counter_trigger *trigger;
>> +     ssize_t len = 0;
>> +
>> +     value = iio_counter_value_find_by_id(counter, chan->channel2);
>> +     if (!value)
>> +             return -EINVAL;
>> +
>> +     /* Print out a list of every Signal association to Value */
>> +     for (i = 0; i < value->num_triggers; i++) {
>> +             trigger = value->triggers + i;
>> +             len += snprintf(buf + len, PAGE_SIZE - len, "%d\t%s\t%s\n",
>> +                     trigger->signal->id, trigger->signal->name,
>> +                     trigger->trigger_modes[trigger->mode]);
>> +             if (len >= PAGE_SIZE)
>> +                     return -ENOMEM;
>> +     }
>> +
>> +     return len;
>> +}
>> +
>> +static ssize_t iio_counter_trigger_mode_read(struct iio_dev *indio_dev,
>> +     uintptr_t priv, const struct iio_chan_spec *chan, char *buf)
>> +{
>> +     struct iio_counter *const counter = iio_priv_get_counter(indio_dev);
>> +     struct iio_counter_value *value;
>> +     struct iio_counter_trigger *trigger;
>> +     const int signal_id = *(int *)((void *)priv);
>> +     int mode;
>> +
>> +     if (!counter->ops->trigger_mode_get)
>> +             return -EINVAL;
>> +
>> +     value = iio_counter_value_find_by_id(counter, chan->channel2);
>> +     if (!value)
>> +             return -EINVAL;
>> +
>> +     trigger = iio_counter_trigger_find_by_id(value, signal_id);
>> +     if (!trigger)
>> +             return -EINVAL;
>> +
>> +     mode = counter->ops->trigger_mode_get(counter, value, trigger);
>> +
>> +     if (mode < 0)
>> +             return mode;
>> +     else if (mode >= trigger->num_trigger_modes)
>> +             return -EINVAL;
>> +
>> +     trigger->mode = mode;
>> +
>> +     return scnprintf(buf, PAGE_SIZE, "%s\n", trigger->trigger_modes[mode]);
>> +}
>> +
>> +static ssize_t iio_counter_trigger_mode_write(struct iio_dev *indio_dev,
>> +     uintptr_t priv, const struct iio_chan_spec *chan, const char *buf,
>> +     size_t len)
>> +{
>> +     struct iio_counter *const counter = iio_priv_get_counter(indio_dev);
>> +     struct iio_counter_value *value;
>> +     ssize_t err;
>> +     struct iio_counter_trigger *trigger;
>> +     const int signal_id = *(int *)((void *)priv);
>> +     unsigned int mode;
>> +
>> +     if (!counter->ops->trigger_mode_set)
>> +             return -EINVAL;
>> +
>> +     value = iio_counter_value_find_by_id(counter, chan->channel2);
>> +     if (!value)
>> +             return -EINVAL;
>> +
>> +     trigger = iio_counter_trigger_find_by_id(value, signal_id);
>> +     if (!trigger)
>> +             return -EINVAL;
>> +
>> +     for (mode = 0; mode < trigger->num_trigger_modes; mode++)
>> +             if (sysfs_streq(buf, trigger->trigger_modes[mode]))
>> +                     break;
>> +
>> +     if (mode >= trigger->num_trigger_modes)
>> +             return -EINVAL;
>> +
>> +     err = counter->ops->trigger_mode_set(counter, value, trigger, mode);
>> +     if (err)
>> +             return err;
>> +
>> +     trigger->mode = mode;
>> +
>> +     return len;
>> +}
>> +
>> +static ssize_t iio_counter_trigger_mode_available_read(
>> +     struct iio_dev *indio_dev, uintptr_t priv,
>> +     const struct iio_chan_spec *chan, char *buf)
>> +{
>> +     struct iio_counter *const counter = iio_priv_get_counter(indio_dev);
>> +     struct iio_counter_value *value;
>> +     ssize_t len = 0;
>> +     struct iio_counter_trigger *trigger;
>> +     const int signal_id = *(int *)((void *)priv);
>> +     unsigned int i;
>> +
>> +     value = iio_counter_value_find_by_id(counter, chan->channel2);
>> +     if (!value)
>> +             return -EINVAL;
>> +
>> +     trigger = iio_counter_trigger_find_by_id(value, signal_id);
>> +     if (!trigger)
>> +             return -EINVAL;
>> +
>> +     for (i = 0; i < trigger->num_trigger_modes; i++)
>> +             len += scnprintf(buf + len, PAGE_SIZE - len, "%s ",
>> +                     trigger->trigger_modes[i]);
>> +
>> +     buf[len - 1] = '\n';
>> +
>> +     return len;
>> +}
>> +
>> +static int iio_counter_value_function_set(struct iio_dev *indio_dev,
>> +     const struct iio_chan_spec *chan, unsigned int mode)
>> +{
>> +     struct iio_counter *const counter = iio_priv_get_counter(indio_dev);
>> +     struct iio_counter_value *value;
>> +     int err;
>> +
>> +     if (!counter->ops->value_function_set)
>> +             return -EINVAL;
>> +
>> +     /* Find relevant Value */
>> +     value = iio_counter_value_find_by_id(counter, chan->channel2);
>> +     if (!value)
>> +             return -EINVAL;
>> +
>> +     /* Map IIO core function_set to Generic Counter value_function_set */
>> +     err = counter->ops->value_function_set(counter, value, mode);
>> +     if (err)
>> +             return err;
>> +
>> +     value->mode = mode;
>> +
>> +     return 0;
>> +}
>> +
>> +static int iio_counter_value_function_get(struct iio_dev *indio_dev,
>> +     const struct iio_chan_spec *chan)
>> +{
>> +     struct iio_counter *const counter = iio_priv_get_counter(indio_dev);
>> +     struct iio_counter_value *value;
>> +     int retval;
>> +
>> +     if (!counter->ops->value_function_get)
>> +             return -EINVAL;
>> +
>> +     /* Find relevant Value */
>> +     value = iio_counter_value_find_by_id(counter, chan->channel2);
>
> Same argument as below - would the driver not be able to do this better?
> Often it would know a simple transform to get the right one...
>
>
>> +     if (!value)
>> +             return -EINVAL;
>> +
>> +     /* Map IIO core function_get to Generic Counter value_function_get */
>> +     retval = counter->ops->value_function_get(counter, value);
>> +     if (retval < 0)
>> +             return retval;
>> +     else if (retval >= value->num_function_modes)
>> +             return -EINVAL;
>> +
>> +     value->mode = retval;
>> +
>> +     return retval;
>> +}
>> +
>> +static int iio_counter_value_ext_info_alloc(struct iio_chan_spec *const chan,
>> +     struct iio_counter_value *const value)
>> +{
>> +     const struct iio_chan_spec_ext_info ext_info_default[] = {
>> +             {
>> +                     .name = "name",
>> +                     .shared = IIO_SEPARATE,
>> +                     .read = iio_counter_value_name_read
>> +             },
>> +             IIO_ENUM("function", IIO_SEPARATE, &value->function_enum),
>> +             {
>> +                     .name = "function_available",
>> +                     .shared = IIO_SEPARATE,
>> +                     .read = iio_enum_available_read,
>> +                     .private = (void *)&value->function_enum
>> +             },
>> +             {
>> +                     .name = "triggers",
>> +                     .shared = IIO_SEPARATE,
>> +                     .read = iio_counter_value_triggers_read
>> +             }
>> +     };
>> +     const size_t num_default = ARRAY_SIZE(ext_info_default);
>> +     const struct iio_chan_spec_ext_info ext_info_trigger[] = {
>> +             {
>> +                     .shared = IIO_SEPARATE,
>> +                     .read = iio_counter_trigger_mode_read,
>> +                     .write = iio_counter_trigger_mode_write
>> +             },
>> +             {
>> +                     .shared = IIO_SEPARATE,
>> +                     .read = iio_counter_trigger_mode_available_read
>> +             }
>> +     };
>> +     const size_t num_ext_info_trigger = ARRAY_SIZE(ext_info_trigger);
>> +     const size_t num_triggers_ext_info = num_ext_info_trigger *
>> +             value->num_triggers;
>> +     const size_t num_ext_info = num_default + num_triggers_ext_info + 1;
>> +     int err;
>> +     struct iio_chan_spec_ext_info *ext_info;
>> +     const struct iio_counter_trigger *trigger;
>> +     size_t i;
>> +     size_t j;
>> +
>> +     /* Construct function_enum for Value */
>> +     value->function_enum.items = value->function_modes;
>> +     value->function_enum.num_items = value->num_function_modes;
>> +     value->function_enum.set = iio_counter_value_function_set;
>> +     value->function_enum.get = iio_counter_value_function_get;
>> +
>> +     ext_info = kmalloc_array(num_ext_info, sizeof(*ext_info), GFP_KERNEL);
>> +     if (!ext_info)
>> +             return -ENOMEM;
>> +     ext_info[num_ext_info - 1].name = NULL;
>> +
>> +     /* Add ext_info for the name, function, function_available, and triggers
>> +      * attributes
>> +      */
>> +     memcpy(ext_info, ext_info_default, sizeof(ext_info_default));
>> +     /* Add ext_info for the trigger_signalX-Z and
>> +      * trigger_signalX-Z_available attributes for each Trigger
>> +      */
>> +     for (i = 0; i < num_triggers_ext_info; i += num_ext_info_trigger)
>> +             memcpy(ext_info + num_default + i, ext_info_trigger,
>> +                     sizeof(ext_info_trigger));
>> +
>> +     /* Set name for each trigger_signalX-Z and trigger_signalX-Z_available
>> +      * attribute; store the respective Signal ID address in each ext_info
>> +      * private member
>> +      */
>> +     for (i = num_default, j = 0; j < value->num_triggers; i++, j++) {
>> +             trigger = value->triggers + j;
>> +
>> +             ext_info[i].name = kasprintf(GFP_KERNEL, "trigger_signal%d-%d",
>> +                     chan->channel, trigger->signal->id);
>> +             if (!ext_info[i].name) {
>> +                     err = -ENOMEM;
>> +                     goto err_name_alloc;
>> +             }
>> +             ext_info[i].private = (void *)&trigger->signal->id;
>> +             i++;
>> +
>> +             ext_info[i].name = kasprintf(GFP_KERNEL,
>> +                     "trigger_signal%d-%d_available",
>> +                     chan->channel, trigger->signal->id);
>> +             if (!ext_info[i].name) {
>> +                     err = -ENOMEM;
>> +                     goto err_name_alloc;
>> +             }
>> +             ext_info[i].private = (void *)&trigger->signal->id;
>> +     }
>> +
>> +     chan->ext_info = ext_info;
>> +
>> +     return 0;
>> +
>> +err_name_alloc:
>> +     while (i-- > num_default)
>> +             kfree(ext_info[i].name);
>> +     kfree(ext_info);
>> +     return err;
>> +}
>> +
>> +static void iio_counter_value_ext_info_free(
>> +     const struct iio_chan_spec *const channel)
>> +{
>> +     size_t i;
>> +     const char *const prefix = "trigger_signal";
>> +     const size_t prefix_len = strlen(prefix);
>> +
>> +     for (i = 0; channel->ext_info[i].name; i++)
>> +             if (!strncmp(channel->ext_info[i].name, prefix, prefix_len))
>> +                     kfree(channel->ext_info[i].name);
>> +     kfree(channel->ext_info);
>> +}
>> +
>> +static const struct iio_chan_spec_ext_info iio_counter_signal_ext_info[] = {
>> +     {
>> +             .name = "name",
>> +             .shared = IIO_SEPARATE,
>> +             .read = iio_counter_signal_name_read
>> +     },
>> +     {}
>> +};
>> +
>> +static int iio_counter_channels_alloc(struct iio_counter *const counter)
>> +{
>> +     const size_t num_channels = counter->num_signals + counter->num_values +
>> +             counter->num_channels;
>> +     int err;
>> +     struct iio_chan_spec *channels;
>> +     size_t j;
>> +     struct iio_counter_value *value;
>> +     size_t i = counter->num_channels;
>> +     const struct iio_counter_signal *signal;
>> +
>> +     channels = kcalloc(num_channels, sizeof(*channels), GFP_KERNEL);
>
> Bring the calculation of num_channels down here - it will make it
> more obvious that this allows space for both sources of channel.
>
>> +     if (!channels)
>> +             return -ENOMEM;
>> +
>> +     /* If any channels were supplied on Counter registration,
>> +      * we add them here to the front of the array
>> +      */
>> +     memcpy(channels, counter->channels,
>> +             counter->num_channels * sizeof(*counter->channels));
>> +
>> +     /* Add channel for each Value */
>> +     for (j = 0; j < counter->num_values; j++) {
>> +             value = counter->values + j;
>> +
>> +             channels[i].type = IIO_COUNT;
>> +             channels[i].channel = counter->id;
>> +             channels[i].channel2 = value->id;
>> +             channels[i].info_mask_separate = BIT(IIO_CHAN_INFO_RAW);
>> +             channels[i].indexed = 1;
>> +             channels[i].counter = 1;
>> +
>> +             /* Add channels for Triggers associated with Value */
> Add channels?  'i' would need incrementing.   Also that's not what this function
> is doing that I can see...
>> +             err = iio_counter_value_ext_info_alloc(channels + i, value);
>> +             if (err)
>> +                     goto err_value_ext_info_alloc;
>> +
>> +             i++;
>> +     }
>> +
>> +     /* Add channel for each Signal */
>> +     for (j = 0; j < counter->num_signals; j++) {
>> +             signal = counter->signals + j;
>> +
>> +             channels[i].type = IIO_SIGNAL;
>> +             channels[i].channel = counter->id;
>> +             channels[i].channel2 = signal->id;
>> +             channels[i].info_mask_separate = BIT(IIO_CHAN_INFO_RAW);
>> +             channels[i].indexed = 1;
>> +             channels[i].counter = 1;
>> +             channels[i].ext_info = iio_counter_signal_ext_info;
>> +
>> +             i++;
>> +     }
>> +
>> +     counter->indio_dev->num_channels = num_channels;
>> +     counter->indio_dev->channels = channels;
>> +
>> +     return 0;
>> +
>> +err_value_ext_info_alloc:
>> +     while (i-- > counter->num_channels)
>> +             iio_counter_value_ext_info_free(channels + i);
>> +     kfree(channels);
>> +     return err;
>> +}
>> +
>> +static void iio_counter_channels_free(const struct iio_counter *const counter)
>> +{
>> +     size_t i = counter->num_channels + counter->indio_dev->num_channels;
>> +     const struct iio_chan_spec *const chans = counter->indio_dev->channels;
>> +
>> +     while (i-- > counter->num_channels)
>> +             /* Only IIO_COUNT channels need to be freed here */
>> +             if (chans[i].type == IIO_COUNT)
>> +                     iio_counter_value_ext_info_free(chans + i);
>> +
>> +     kfree(chans);
>> +}
>> +
>> +static int iio_counter_read_raw(struct iio_dev *indio_dev,
>> +     struct iio_chan_spec const *chan, int *val, int *val2, long mask)
>> +{
>> +     struct iio_counter *const counter = iio_priv_get_counter(indio_dev);
>> +     struct iio_counter_signal *signal;
>> +     struct iio_counter_value *value;
>> +
>> +     if (mask != IIO_CHAN_INFO_RAW)
>> +             return -EINVAL;
>> +
>> +     switch (chan->type) {
>> +     /* Map read_raw to signal_read for Signal */
>> +     case IIO_SIGNAL:
>> +             if (!counter->ops->signal_read)
>> +                     return -EINVAL;
>> +
>> +             signal = iio_counter_signal_find_by_id(counter, chan->channel2);
>> +             if (!signal)
>> +                     return -EINVAL;
>> +
>> +             return counter->ops->signal_read(counter, signal, val, val2);
>> +     /* Map read_raw to value_read for Value */
>> +     case IIO_COUNT:
>> +             if (!counter->ops->value_read)
>> +                     return -EINVAL;
>> +
>> +             value = iio_counter_value_find_by_id(counter, chan->channel2);
>> +             if (!value)
>> +                     return -EINVAL;
>> +
>> +             return counter->ops->value_read(counter, value, val, val2);
>> +     /* Map read_raw to read_raw for non-counter channel */
>> +     default:
>> +             if (counter->info && counter->info->read_raw)
>> +                     return counter->info->read_raw(indio_dev, chan, val,
>> +                             val2, mask);
>> +     }
>> +
>> +     return -EINVAL;
>> +}
>> +
>> +static int iio_counter_write_raw(struct iio_dev *indio_dev,
>> +     struct iio_chan_spec const *chan, int val, int val2, long mask)
>> +{
>> +     struct iio_counter *const counter = iio_priv_get_counter(indio_dev);
>> +     struct iio_counter_signal *signal;
>> +     struct iio_counter_value *value;
>> +
>> +     if (mask != IIO_CHAN_INFO_RAW)
>> +             return -EINVAL;
>> +
>> +     switch (chan->type) {
>> +     /* Map write_raw to signal_write for Signal */
>> +     case IIO_SIGNAL:
>> +             if (!counter->ops->signal_write)
>> +                     return -EINVAL;
>> +
>> +             signal = iio_counter_signal_find_by_id(counter, chan->channel2);
>> +             if (!signal)
>> +                     return -EINVAL;
>
>         Hmm. have to search is certainly a little ugly.  Particularly as the driver
>         would be able to maintain this as a lookup.  I'd be tempted to pass the
>         signal id down into the callback rather than finding the signal in the core.
>         Might turn out messier though... :)
>
>> +
>> +             return counter->ops->signal_write(counter, signal, val, val2);
>> +     /* Map write_raw to value_write for Value */
>> +     case IIO_COUNT:
>> +             if (!counter->ops->value_write)
>> +                     return -EINVAL;
>> +
>> +             value = iio_counter_value_find_by_id(counter, chan->channel2);
>
> Again, this mapping is a simple lookup in the driver I think, perhaps push
> it down there?
>
>> +             if (!value)
>> +                     return -EINVAL;
>> +
>> +             return counter->ops->value_write(counter, value, val, val2);
>> +     /* Map write_raw to write_raw for non-counter channel */
>> +     default:
>> +             if (counter->info && counter->info->write_raw)
>> +                     return counter->info->write_raw(indio_dev, chan, val,
>> +                             val2, mask);
>> +     }
>> +
>> +     return -EINVAL;
>> +}
>> +
>> +static int iio_counter_signals_validate(const struct iio_counter *const counter)
>> +{
>> +     size_t i;
>> +     const struct iio_counter_signal *signal;
>> +     size_t j;
>> +     int curr_id;
>> +
>> +     /* At least one Signal must be defined */
>> +     if (!counter->num_signals || !counter->signals) {
>> +             pr_err("Counter '%d' Signals undefined\n", counter->id);
>> +             return -EINVAL;
>> +     }
>> +
>> +     for (i = 0; i < counter->num_signals; i++) {
>> +             signal = counter->signals + i;
>> +             /* No two Signals may share the same ID */
>> +             for (j = 0; j < i; j++) {
>> +                     curr_id = counter->signals[j].id;
>> +                     if (curr_id == signal->id) {
>> +                             pr_err("Duplicate Counter '%d' Signal '%d'\n",
>> +                                             counter->id, signal->id);
>> +                             return -EEXIST;
>> +                     }
>> +             }
>> +             for (j = i + 1; j < counter->num_signals; j++) {
>> +                     curr_id = counter->signals[j].id;
>> +                     if (curr_id == signal->id) {
>> +                             pr_err("Duplicate Counter '%d' Signal '%d'\n",
>> +                                             counter->id, signal->id);
>> +                             return -EEXIST;
>> +                     }
>> +             }
>
> Same as below.
>
>> +     }
>> +
>> +     return 0;
>> +}
>> +
>> +static int iio_counter_triggers_validate(const int counter_id,
>> +     const struct iio_counter_value *const value)
>> +{
>> +     size_t i;
>> +     const struct iio_counter_trigger *trigger;
>> +     size_t j;
>> +     int curr_id;
>> +
>> +     /* At least one Trigger must be defined */
>> +     if (!value->num_triggers || !value->triggers) {
>> +             pr_err("Counter '%d' Value '%d' Triggers undefined\n",
>> +                     counter_id, value->id);
>> +             return -EINVAL;
>> +     }
>> +
>> +     /* Ensure all Triggers have a defined Signal; this prevents a possible
>> +      * NULL pointer dereference when later validating Trigger Signal IDs
>
> Fix comment syntax throughout...
>
>> +      */
>> +     for (i = 0; i < value->num_triggers; i++)
>> +             if (!value->triggers[i].signal) {
>> +                     pr_err("Counter '%d' Trigger '%zu' Signal undefined\n",
>> +                             counter_id, i);
>> +                     return -EINVAL;
>> +             }
>> +
>> +     /* Verify validity of each Trigger */
>> +     for (i = 0; i < value->num_triggers; i++) {
>> +             trigger = value->triggers + i;
>> +             /* No two Trigger Signals may share the same ID */
>> +             for (j = 0; j < i; j++) {
>> +                     curr_id = value->triggers[j].signal->id;
>> +                     if (curr_id == trigger->signal->id) {
>> +                             pr_err("Signal '%d' is already linked to Counter '%d' Value '%d'\n",
>> +                                     trigger->signal->id, counter_id,
>> +                                     value->id);
>> +                             return -EEXIST;
>> +                     }
>> +             }
>> +             for (j = i + 1; j < value->num_triggers; j++) {
>> +                     curr_id = value->triggers[j].signal->id;
>> +                     if (curr_id == trigger->signal->id) {
>> +                             pr_err("Signal '%d' is already linked to Counter '%d' Value '%d'\n",
>> +                                     trigger->signal->id, counter_id,
>> +                                     value->id);
>> +                             return -EEXIST;
>> +                     }
>> +             }
> Again, one loop with the condition changed so it doesn't fault on the i = j case -- see below and
> note I'm reviewing backwards...
>
>
>> +
>> +             /* At least one trigger mode must be defined for each Trigger */
>> +             if (!trigger->num_trigger_modes || !trigger->trigger_modes) {
>> +                     pr_err("Counter '%d' Signal '%d' trigger modes undefined\n",
>> +                             counter_id, trigger->signal->id);
>> +                     return -EINVAL;
>> +             }
>> +     }
>> +
>> +     return 0;
>> +}
>> +
>> +static int iio_counter_values_validate(const struct iio_counter *const counter)
>> +{
>
> Hmm. Pretty heavy weight checking.  Ah well, up to you.
>
>> +     size_t i;
>> +     const struct iio_counter_value *value;
>> +     size_t j;
>> +     int curr_id;
>> +     int err;
>> +
>> +     /* At least one Value must be defined */
>> +     if (!counter->num_values || !counter->values) {
>> +             pr_err("Counter '%d' Values undefined\n", counter->id);
>> +             return -EINVAL;
>> +     }
>> +
>> +     /* Verify validity of each Value */
>> +     for (i = 0; i < counter->num_values; i++) {
>> +             value = counter->values + i;
>> +             /* No two Values may share the same ID */
>> +             for (j = 0; j < i; j++) {
>
> Single loop with a slightly change to condition
> if ((i != j) and (curr_id == value->id))
>
>> +                     curr_id = counter->values[j].id;
>> +                     if (curr_id == value->id) {
>> +                             pr_err("Duplicate Counter '%d' Value '%d'\n",
>> +                                     counter->id, value->id);
>> +                             return -EEXIST;
>> +                     }
>> +             }
>> +             for (j = i + 1; j < counter->num_values; j++) {
>> +                     curr_id = counter->values[j].id;
>> +                     if (curr_id == value->id) {
>> +                             pr_err("Duplicate Counter '%d' Value '%d'\n",
>> +                                     counter->id, value->id);
>> +                             return -EEXIST;
>> +                     }
>> +             }
>> +
>> +             /* At least one function mode must be defined for each Value */
>> +             if (!value->num_function_modes || !value->function_modes) {
>> +                     pr_err("Counter '%d' Value '%d' function modes undefined\n",
>> +                             counter->id, value->id);
>> +                     return -EINVAL;
>> +             }
>> +
>> +             /* Verify the Triggers associated with each Value */
>> +             err = iio_counter_triggers_validate(counter->id, value);
>> +             if (err)
>> +                     return err;
>> +     }
>> +
>> +     return 0;
>> +}
>> +
>> +/**
>> + * iio_counter_register - register Counter to the system
>> + * @counter: pointer to IIO Counter to register
>> + *
>> + * This function piggybacks off of iio_device_register. First, the relevant
>> + * Counter members are validated; the signals and values members must be defined
>> + * and populated by valid Signal and Value structures respectively. Next, a
>> + * struct iio_dev is allocated by a call to iio_device_alloc and initialized for
>> + * the Counter, IIO channels are allocated, the Counter address is stored as the
>> + * private data, and finally iio_device_register is called.
>> + */
>> +int iio_counter_register(struct iio_counter *const counter)
>> +{
>> +     const struct iio_info info_default = {
>> +             .read_raw = iio_counter_read_raw,
>> +             .write_raw = iio_counter_write_raw
>> +     };
>> +     int err;
>> +     struct iio_info *info;
>> +     struct iio_counter **priv;
>> +
>> +     if (!counter)
>> +             return -EINVAL;
>> +
>> +     /* Verify that Signals are valid and IDs do not conflict */
>> +     err = iio_counter_signals_validate(counter);
>> +     if (err)
>> +             return err;
>> +
>> +     /* Verify that Values are valid and IDs do not conflict;
> Fix multiline comment syntax to standard kernel syntax.
> /*
>  * Verify
>  */
>> +      * Triggers for each Value are also verified for validity
>> +      */
>> +     err = iio_counter_values_validate(counter);
>> +     if (err)
>> +             return err;
>> +
>> +     counter->indio_dev = iio_device_alloc(sizeof(counter));
>> +     if (!counter->indio_dev)
>> +             return -ENOMEM;
>> +
>> +     info = kmalloc(sizeof(*info), GFP_KERNEL);
>> +     if (!info) {
>> +             err = -ENOMEM;
>> +             goto err_info_alloc;
>> +     }
>> +     /* If an iio_info has been supplied than we use that,
>> +      * otherwise we set all callbacks to NULL; iio_counter_read_raw
>> +      * and iio_counter_write_raw is used for read_raw and write_raw
>> +      * for either case in order to support counter functionality
>> +      * (supplied read_raw/write_raw will be called from within
>> +      * iio_counter_read_raw/iio_counter_write_raw for non-counter
>> +      * channels)
>> +      */
>
>> +     if (counter->info) {
>> +             memcpy(info, counter->info, sizeof(*counter->info));
>> +             info->read_raw = iio_counter_read_raw;
>> +             info->write_raw = iio_counter_write_raw;
>> +     } else {
>> +             memcpy(info, &info_default, sizeof(info_default));
>> +     }
>> +
>> +     counter->indio_dev->info = info;
>> +     counter->indio_dev->modes = INDIO_DIRECT_MODE;

As I have said in the previous version, stm32 driver will not work with
this mode, so I need way to change it.
One solution could be to use iio_priv to store an iio_counter structure with
ops, arrays etc... Maybe even iio_counter structure could be hiden from drivers.
It could require to split iio_counter allocation and registration to functions.

>> +     counter->indio_dev->name = counter->name;
>> +     counter->indio_dev->dev.parent = counter->dev;
>> +
>> +     /* IIO channels are allocated and set for Signals, Values, and Triggers;
>> +      * any auxiliary IIO channels provided in iio_counter are also set
>> +      */
>> +     err = iio_counter_channels_alloc(counter);
>> +     if (err)
>> +             goto err_channels_alloc;
>> +
>> +     /* Pointer to the counter is stored in indio_dev as a way to refer
>> +      * back to the counter from within various IIO callbacks
>> +      */
>> +     priv = iio_priv(counter->indio_dev);
>> +     memcpy(priv, &counter, sizeof(*priv));
>> +
>> +     err = iio_device_register(counter->indio_dev);
>> +     if (err)
>> +             goto err_iio_device_register;
>> +
>> +     return 0;
>> +
>> +err_iio_device_register:
>> +     iio_counter_channels_free(counter);
>> +err_channels_alloc:
>> +     kfree(info);
>> +err_info_alloc:
>> +     iio_device_free(counter->indio_dev);
>> +     return err;
>> +}
>> +EXPORT_SYMBOL(iio_counter_register);
>> +
>> +/**
>> + * iio_counter_unregister - unregister Counter from the system
>> + * @counter: pointer to IIO Counter to unregister
>> + *
>> + * The Counter is unregistered from the system. The indio_dev is unregistered
>> + * and all allocated memory is freed.
>> + */
>> +void iio_counter_unregister(struct iio_counter *const counter)
>> +{
>> +     const struct iio_info *const info = counter->indio_dev->info;
>> +
>> +     if (!counter)
>> +             return;
>> +
>> +     iio_device_unregister(counter->indio_dev);
>> +
>> +     iio_counter_channels_free(counter);
>> +
>> +     kfree(info);
>> +     iio_device_free(counter->indio_dev);
>> +}
>> +EXPORT_SYMBOL(iio_counter_unregister);
>> +
>> +static void devm_iio_counter_unreg(struct device *dev, void *res)
>> +{
>> +     iio_counter_unregister(*(struct iio_counter **)res);
>> +}
>> +
>> +/**
>> + * devm_iio_counter_register - Resource-managed iio_counter_register
>> + * @dev: Device to allocate iio_counter for
>> + * @counter: pointer to IIO Counter to register
>> + *
>> + * Managed iio_counter_register. The IIO counter registered with this
>> + * function is automatically unregistered on driver detach. This function
>> + * calls iio_counter_register internally. Refer to that function for more
>> + * information.
>> + *
>> + * If an iio counter registered with this function needs to be unregistered
>> + * separately, devm_iio_counter_unregister must be used.
>> + *
>> + * RETURNS:
>> + * 0 on success, negative error number on failure.
>> + */
>> +int devm_iio_counter_register(struct device *dev,
>> +     struct iio_counter *const counter)
>> +{
>> +     struct iio_counter **ptr;
>> +     int ret;
>> +
>> +     ptr = devres_alloc(devm_iio_counter_unreg, sizeof(*ptr), GFP_KERNEL);
>> +     if (!ptr)
>> +             return -ENOMEM;
>> +
>> +     *ptr = counter;
>> +     ret = iio_counter_register(counter);
>> +     if (!ret)
>> +             devres_add(dev, ptr);
>> +     else
>> +             devres_free(ptr);
>> +
>> +     return ret;
>> +}
>> +EXPORT_SYMBOL(devm_iio_counter_register);
>> +
>> +static int devm_iio_counter_match(struct device *dev, void *res, void *data)
>> +{
>> +     struct iio_counter **r = res;
>> +
>> +     if (!r || !*r) {
>> +             WARN_ON(!r || !*r);
>> +             return 0;
>> +     }
>> +
>> +     return *r == data;
>> +}
>> +
>> +/**
>> + * devm_iio_counter_unregister - Resource-managed iio_counter_unregister
>> + * @dev: Device this iio_counter belongs to
>> + * @counter: the iio counter associated with the device
>> + *
>> + * Unregister iio counter registered with devm_iio_counter_register.
>> + */
>> +void devm_iio_counter_unregister(struct device *dev,
>> +     struct iio_counter *const counter)
>> +{
>> +     int rc;
>> +
>> +     rc = devres_release(dev, devm_iio_counter_unreg,
>> +             devm_iio_counter_match, counter);
>> +     WARN_ON(rc);
>> +}
>> +EXPORT_SYMBOL(devm_iio_counter_unregister);
>> diff --git a/include/linux/iio/counter.h b/include/linux/iio/counter.h
>> new file mode 100644
>> index 000000000000..35645406711a
>> --- /dev/null
>> +++ b/include/linux/iio/counter.h
>> @@ -0,0 +1,166 @@
>> +/*
>> + * Industrial I/O counter interface
>> + * Copyright (C) 2017 William Breathitt Gray
>> + *
>> + * This program is free software; you can redistribute it and/or modify
>> + * it under the terms of the GNU General Public License, version 2, as
>> + * published by the Free Software Foundation.
>> + *
>> + * This program is distributed in the hope that it will be useful, but
>> + * WITHOUT ANY WARRANTY; without even the implied warranty of
>> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
>> + * General Public License for more details.
>> + */
>> +#ifndef _IIO_COUNTER_H_
>> +#define _IIO_COUNTER_H_
>> +
>> +#ifdef CONFIG_IIO_COUNTER
>> +
>> +#include <linux/device.h>
>> +#include <linux/types.h>
>> +
>> +#include <linux/iio/iio.h>
>> +
>> +/**
>> + * struct iio_counter_signal - IIO Counter Signal node
>> + * @id:              [DRIVER] unique ID used to identify signal
>> + * @name:    [DRIVER] device-specific signal name
>> + */
>> +struct iio_counter_signal {
>> +     int             id;
>> +     const char      *name;
>> +};
>> +
>> +/**
>> + * struct iio_counter_trigger - IIO Counter Trigger node
>> + * @mode:            [DRIVER] current trigger mode state
>> + * @trigger_modes:   [DRIVER] available trigger modes
>> + * @num_trigger_modes:       [DRIVER] number of modes specified in @trigger_modes
>> + * @signal:          [DRIVER] pointer to associated signal
>> + */
>> +struct iio_counter_trigger {
>> +     unsigned int                    mode;
>> +     const char *const               *trigger_modes;
>> +     unsigned int                    num_trigger_modes;
>> +     struct iio_counter_signal       *signal;
>
> Are signals actually changeable? - Or is that pointer constant?
>
>> +};
>> +
>> +/**
>> + * struct iio_counter_value - IIO Counter Value node
>> + * @id:                      [DRIVER] unique ID used to identify value
>> + * @name:            [DRIVER] device-specific value name
>> + * @mode:            [DRIVER] current function mode state
>> + * @function_modes:  [DRIVER] available function modes
>> + * @num_function_modes:      [DRIVER] number of modes specified in @function_modes
>> + * @triggers:                [DRIVER] array of triggers for initialization
>> + * @num_triggers:    [DRIVER] number of triggers specified in @triggers
>> + * @function_enum:   [INTERN] used internally to generate function attributes
>> + */
>> +struct iio_counter_value {
>> +     int                     id;
>> +     const char              *name;
>> +     unsigned int            mode;
>> +     const char *const       *function_modes;
>> +     unsigned int            num_function_modes;
>> +
>> +     struct iio_counter_trigger      *triggers;
>> +     size_t                          num_triggers;
>> +
>> +     struct iio_enum         function_enum;
>> +};
>> +
>> +struct iio_counter;
>> +
>> +/**
>> + * struct iio_counter_ops - IIO Counter related callbacks
>> + * @signal_read:     function to request a signal value from the device.
>> + *                   Return value will specify the type of value returned by
>> + *                   the device. val and val2 will contain the elements
>> + *                   making up the returned value.
>
> Add some detail on the importance of the return value.
>
>> + * @signal_write:    function to write a signal value to the device.
>> + *                      Parameters are interpreted the same as signal_read.
>
> You need a means of establishing the correct format for write (we needed it
> in IIO fairly early on too)
>
>> + * @trigger_mode_set:        function to set the trigger mode. mode is the index of
>> + *                   the requested mode from the value trigger_modes array.
>> + * @trigger_mode_get:        function to get the current trigger mode. Return value
>> + *                   will specify the index of the current mode from the
>> + *                   value trigger_modes array.
>> + * @value_read:              function to request a value value from the device.
>> + *                   Return value will specify the type of value returned by
>> + *                   the device. val and val2 will contain the elements
>> + *                   making up the returned value.
>> + * @value_write:     function to write a value value to the device.
>> + *                      Parameters are interpreted the same as value_read.
>> + * @value_function_set: function to set the value function mode. mode is the
>> + *                   index of the requested mode from the value
>> + *                   function_modes array.
>> + * @value_function_get: function to get the current value function mode. Return
>> + *                   value will specify the index of the current mode from
>> + *                   the value function_modes array.
>> + */
>> +struct iio_counter_ops {
>> +     int (*signal_read)(struct iio_counter *counter,
>> +             struct iio_counter_signal *signal, int *val, int *val2);
>> +     int (*signal_write)(struct iio_counter *counter,
>> +             struct iio_counter_signal *signal, int val, int val2);
>> +     int (*trigger_mode_set)(struct iio_counter *counter,
>> +             struct iio_counter_value *value,
>> +             struct iio_counter_trigger *trigger, unsigned int mode);
>> +     int (*trigger_mode_get)(struct iio_counter *counter,
>> +             struct iio_counter_value *value,
>> +             struct iio_counter_trigger *trigger);
>> +     int (*value_read)(struct iio_counter *counter,
>> +             struct iio_counter_value *value, int *val, int *val2);
>> +     int (*value_write)(struct iio_counter *counter,
>> +             struct iio_counter_value *value, int val, int val2);
>> +     int (*value_function_set)(struct iio_counter *counter,
>> +             struct iio_counter_value *value, unsigned int mode);
>> +     int (*value_function_get)(struct iio_counter *counter,
>> +             struct iio_counter_value *value);
>> +};
>> +
>> +/**
>> + * struct iio_counter - IIO Counter data structure
>> + * @id:                      [DRIVER] unique ID used to identify counter
>> + * @name:            [DRIVER] name of the device
>> + * @dev:             [DRIVER] device structure, should be assigned a parent
>> + *                   and owner
>> + * @ops:             [DRIVER] callbacks from driver for counter components
>> + * @signals:         [DRIVER] array of signals for initialization
>> + * @num_signals:     [DRIVER] number of signals specified in @signals
>> + * @values:          [DRIVER] array of values for initialization
>> + * @num_values:              [DRIVER] number of values specified in @values
>> + * @channels:                [DRIVER] channel specification structure table
>> + * @num_channels:    [DRIVER] number of channels specified in @channels
>> + * @info:            [DRIVER] callbacks and constant info from driver
>> + * @indio_dev:               [INTERN] industrial I/O device structure
>> + * @driver_data:     [DRIVER] driver data
>> + */
>> +struct iio_counter {
>
> Mentioned earlier - I think naming the device counter, is confusing.
> The counters should be named counter - call it iio_counter_device
> or iio_counter_group or something and keep counter for the things
> currently called value.
>
>> +     int                             id;
>> +     const char                      *name;
>> +     struct device                   *dev;
>> +     const struct iio_counter_ops    *ops;
>> +
>> +     struct iio_counter_signal       *signals;
>> +     size_t                          num_signals;
>> +     struct iio_counter_value        *values;
>> +     size_t                          num_values;
>> +
>> +     const struct iio_chan_spec      *channels;
>> +     size_t                          num_channels;
>> +     const struct iio_info           *info;
>> +
>> +     struct iio_dev  *indio_dev;
>> +     void            *driver_data;
>> +};
>> +
>> +int iio_counter_register(struct iio_counter *const counter);
>> +void iio_counter_unregister(struct iio_counter *const counter);
>> +int devm_iio_counter_register(struct device *dev,
>> +     struct iio_counter *const counter);
>> +void devm_iio_counter_unregister(struct device *dev,
>> +     struct iio_counter *const counter);
>> +
>> +#endif /* CONFIG_IIO_COUNTER */
>> +
>> +#endif /* _IIO_COUNTER_H_ */
>



-- 
Benjamin Gaignard

Graphic Study Group

Linaro.org │ Open source software for ARM SoCs

Follow Linaro: Facebook | Twitter | Blog

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

* Re: [PATCH v3 2/6] iio: Introduce the generic counter interface
@ 2017-10-09 12:56       ` Benjamin Gaignard
  0 siblings, 0 replies; 17+ messages in thread
From: Benjamin Gaignard @ 2017-10-09 12:56 UTC (permalink / raw)
  To: Jonathan Cameron
  Cc: William Breathitt Gray, Hartmut Knaack, Lars-Peter Clausen,
	Peter Meerwald-Stadler, linux-iio, Linux Kernel Mailing List

2017-10-08 16:30 GMT+02:00 Jonathan Cameron <jic23@kernel.org>:
> On Thu,  5 Oct 2017 14:13:44 -0400
> William Breathitt Gray <vilhelm.gray@gmail.com> wrote:
>
>> This patch introduces the IIO generic counter interface for supporting
>> counter devices. The generic counter interface serves as a catch-all to
>> enable rudimentary support for devices that qualify as counters. More
>> specific and apt counter interfaces may be developed on top of the
>> generic counter interface, and those interfaces should be used by
>> drivers when possible rather than the generic counter interface.
>
> I think you probably want to avoid any additional layering and try to
> push things down here (maybe with a few utility functions to simplify
> common cases), but that's a question for another day!
>>
>> In the context of the IIO generic counter interface, a counter is
>> defined as a device that reports one or more "counter values" based on
>> the state changes of one or more "counter signals" as evaluated by a
>> defined "counter function."
>>
>> The IIO generic counter interface piggybacks off of the IIO core. This
>> is primarily used to leverage the existing sysfs setup: the IIO_COUNT
>> channel attributes represent the Counter Value, while the IIO_SIGNAL
>> channel attributes represent the Counter Signal; auxilary IIO_COUNT
>> attributes represent the Counter Signal connections (Triggers) and their
>> respective state change configurations which trigger an associated
>> "counter function" evaluation.
>>
>> The iio_counter_ops structure serves as a container for driver callbacks
>> to communicate with the device; function callbacks are provided to read
>> and write various Signals and Values, and set and get the "trigger mode"
>> and "function mode" for various Triggers and Values respectively.
>>
>> To support a counter device, a driver must first allocate the available
>> Counter Signals via iio_counter_signal structures. These Signals should
>> be stored as an array and set to the signals array member of an
>> allocated iio_counter structure before the Counter is registered to the
>> system.
>>
>> Counter Values may be allocated via iio_counter_value structures, and
>> respective Counter Signal associations (Triggers) made via
>> iio_counter_trigger structures. Associated iio_counter_trigger
>> structures are stored as an array and set to the the triggers array
>> member of the respective iio_counter_value structure. These
>> iio_counter_value structures are set to the values array member of an
>> allocated iio_counter structure before the Counter is registered to the
>> system.
>>
>> A counter device is registered to the system by passing the respective
>> initialized iio_counter structure to the iio_counter_register function;
>> similarly, the iio_counter_unregister function unregisters the
>> respective counter. The devm_iio_counter_register and
>> iio_devm_iio_counter_unregister functions serve as device memory-managed
>> versions of the iio_counter_register and iio_counter unregister
>> functions respectively.
>
> All the find by ids are the main complexity introduced by layering this
> on IIO that you probably wouldn't have if it wasn't so layered.  Hmm.
> I suggest inline that you could allow drivers to provide fast
> versions of that search given they can probably infer it directly from
> the index.  Might be something to ignore for now though in the interests
> of initial simplicity.
>
> Otherwise a few comments inline, but mostly this is coming together
> pretty well.  I'm actually surprised the layering on top of IIO didn't
> end up more painful than it did. It's not horrendous - which is not
> to say you wouldn't be better breaking free of that entirely...
> (I'm not sure either way).
>
> I think the big remaining questions are around naming more than anything
> else.  I don't like the name value when a user will think they are lookin=
g
> for a counter.  If you use counter though the current use of counter need=
s
> to change.
>
> Trigger is also, as has been raised, a rather overloaded term. Not totall=
y
> obvious what a better term is but we shouldn't reuse that one...
>
> I'll try and think of something if you have no luck!
>
> Jonathan
>>
>> Signed-off-by: William Breathitt Gray <vilhelm.gray@gmail.com>
>> ---
>>  MAINTAINERS                        |   7 +
>>  drivers/iio/Kconfig                |   8 +
>>  drivers/iio/Makefile               |   1 +
>>  drivers/iio/counter/Kconfig        |   1 +
>>  drivers/iio/industrialio-counter.c | 900 ++++++++++++++++++++++++++++++=
+++++++
>>  include/linux/iio/counter.h        | 166 +++++++
>>  6 files changed, 1083 insertions(+)
>>  create mode 100644 drivers/iio/industrialio-counter.c
>>  create mode 100644 include/linux/iio/counter.h
>>
>> diff --git a/MAINTAINERS b/MAINTAINERS
>> index 2281af4b41b6..8b7c37bed252 100644
>> --- a/MAINTAINERS
>> +++ b/MAINTAINERS
>> @@ -6693,6 +6693,13 @@ F:     Documentation/ABI/testing/sysfs-bus-iio-ad=
c-envelope-detector
>>  F:   Documentation/devicetree/bindings/iio/adc/envelope-detector.txt
>>  F:   drivers/iio/adc/envelope-detector.c
>>
>> +IIO GENERIC COUNTER INTERFACE
>> +M:   William Breathitt Gray <vilhelm.gray@gmail.com>
>> +L:   linux-iio@vger.kernel.org
>> +S:   Maintained
>> +F:   drivers/iio/industrialio-counter.c
>> +F:   include/linux/iio/counter.h
>
> Don't forget the directory the drivers are going in ;)
> *laughs manically*
>> +
>>  IIO MULTIPLEXER
>>  M:   Peter Rosin <peda@axentia.se>
>>  L:   linux-iio@vger.kernel.org
>> diff --git a/drivers/iio/Kconfig b/drivers/iio/Kconfig
>> index b3c8c6ef0dff..78e01f4f5937 100644
>> --- a/drivers/iio/Kconfig
>> +++ b/drivers/iio/Kconfig
>> @@ -30,6 +30,14 @@ config IIO_CONFIGFS
>>         (e.g. software triggers). For more info see
>>         Documentation/iio/iio_configfs.txt.
>>
>> +config IIO_COUNTER
>> +     bool "Enable IIO counter support"
>> +     help
>> +       Provides IIO core support for counters. This API provides
>> +       a generic interface that serves as the building blocks to
>> +       create more complex counter interfaces. Rudimentary support
>> +       for counters is enabled.
>> +
>>  config IIO_TRIGGER
>>       bool "Enable triggered sampling support"
>>       help
>> diff --git a/drivers/iio/Makefile b/drivers/iio/Makefile
>> index 93c769cd99bf..6427ff38f964 100644
>> --- a/drivers/iio/Makefile
>> +++ b/drivers/iio/Makefile
>> @@ -5,6 +5,7 @@
>>  obj-$(CONFIG_IIO) +=3D industrialio.o
>>  industrialio-y :=3D industrialio-core.o industrialio-event.o inkern.o
>>  industrialio-$(CONFIG_IIO_BUFFER) +=3D industrialio-buffer.o
>> +industrialio-$(CONFIG_IIO_COUNTER) +=3D industrialio-counter.o
>>  industrialio-$(CONFIG_IIO_TRIGGER) +=3D industrialio-trigger.o
>>
>>  obj-$(CONFIG_IIO_CONFIGFS) +=3D industrialio-configfs.o
>> diff --git a/drivers/iio/counter/Kconfig b/drivers/iio/counter/Kconfig
>> index 474e1ac4e7c0..c8becfe78e28 100644
>> --- a/drivers/iio/counter/Kconfig
>> +++ b/drivers/iio/counter/Kconfig
>> @@ -4,6 +4,7 @@
>>  # When adding new entries keep the list in alphabetical order
>>
>>  menu "Counters"
>> +     depends on IIO_COUNTER
>>
>>  config 104_QUAD_8
>>       tristate "ACCES 104-QUAD-8 driver"
>> diff --git a/drivers/iio/industrialio-counter.c b/drivers/iio/industrial=
io-counter.c
>> new file mode 100644
>> index 000000000000..dfb982dae3a8
>> --- /dev/null
>> +++ b/drivers/iio/industrialio-counter.c
>> @@ -0,0 +1,900 @@
>> +/*
>> + * Industrial I/O counter interface functions
>> + * Copyright (C) 2017 William Breathitt Gray
>> + *
>> + * This program is free software; you can redistribute it and/or modify
>> + * it under the terms of the GNU General Public License, version 2, as
>> + * published by the Free Software Foundation.
>> + *
>> + * This program is distributed in the hope that it will be useful, but
>> + * WITHOUT ANY WARRANTY; without even the implied warranty of
>> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
>> + * General Public License for more details.
>> + */
>> +#include <linux/device.h>
>> +#include <linux/err.h>
>> +#include <linux/export.h>
>> +#include <linux/gfp.h>
>> +#include <linux/kernel.h>
>> +#include <linux/printk.h>
>> +#include <linux/slab.h>
>> +#include <linux/string.h>
>> +
>> +#include <linux/iio/iio.h>
>> +#include <linux/iio/types.h>
>> +
>> +#include <linux/iio/counter.h>
>> +
>> +#define iio_priv_get_counter(_indio_dev) \
>> +     (*(struct iio_counter **)iio_priv(_indio_dev))
>> +
>> +static struct iio_counter_signal *iio_counter_signal_find_by_id(
>> +     const struct iio_counter *const counter, const int id)
>> +{
>> +     size_t i;
>> +     struct iio_counter_signal *signal;
>> +
>> +     for (i =3D 0; i < counter->num_signals; i++) {
>> +             signal =3D counter->signals + i;
>> +             if (signal->id =3D=3D id)
>> +                     return signal;
>> +     }
>> +
>> +     return NULL;
>> +}
>> +
>> +static struct iio_counter_trigger *iio_counter_trigger_find_by_id(
>> +     const struct iio_counter_value *const value, const int id)
>> +{
>> +     size_t i;
>> +     struct iio_counter_trigger *trigger;
>> +
>> +     for (i =3D 0; i < value->num_triggers; i++) {
>> +             trigger =3D value->triggers + i;
>> +             if (trigger->signal->id =3D=3D id)
>> +                     return trigger;
>> +     }
>> +
>> +     return NULL;
>> +}
>> +
>> +static struct iio_counter_value *iio_counter_value_find_by_id(
>> +     const struct iio_counter *const counter, const int id)
>> +{
>> +     size_t i;
>> +     struct iio_counter_value *value;
>
> In most cases this mapping will be entirely obvious to the
> driver - perhaps provide an optional callback to let it
> provide it directly without any searching?
>
> We could always add this later though if we start getting
> drivers with lots of instances of the various parts...
>
>> +
>> +     for (i =3D 0; i < counter->num_values; i++) {
>> +             value =3D counter->values + i;
>> +             if (value->id =3D=3D id)
>> +                     return value;
>> +     }
>> +
>> +     return NULL;
>> +}
>> +
>> +static ssize_t iio_counter_signal_name_read(struct iio_dev *indio_dev,
>> +     uintptr_t priv, const struct iio_chan_spec *chan, char *buf)
>> +{
>> +     struct iio_counter *const counter =3D iio_priv_get_counter(indio_d=
ev);
>> +     const struct iio_counter_signal *signal;
>> +
>> +     signal =3D iio_counter_signal_find_by_id(counter, chan->channel2);
>> +     if (!signal)
>> +             return -EINVAL;
>> +
>> +     return scnprintf(buf, PAGE_SIZE, "%s\n", signal->name);
>> +}
>> +
>> +static ssize_t iio_counter_value_name_read(struct iio_dev *indio_dev,
>> +     uintptr_t priv, const struct iio_chan_spec *chan, char *buf)
>> +{
>> +     struct iio_counter *const counter =3D iio_priv_get_counter(indio_d=
ev);
>> +     const struct iio_counter_value *value;
>> +
>> +     value =3D iio_counter_value_find_by_id(counter, chan->channel2);
>> +     if (!value)
>> +             return -EINVAL;
>> +
>> +     return scnprintf(buf, PAGE_SIZE, "%s\n", value->name);
>
> I mentioned before but I wonder if we can just push name down into
> the chan spec itself in some fashion.  Whether to associate it with
> existing datasheet_name or not is an open question however.
>
>> +}
>> +
>> +static ssize_t iio_counter_value_triggers_read(struct iio_dev *indio_de=
v,
>> +     uintptr_t priv, const struct iio_chan_spec *chan, char *buf)
>> +{
>> +     struct iio_counter *const counter =3D iio_priv_get_counter(indio_d=
ev);
>> +     struct iio_counter_value *value;
>> +     size_t i;
>> +     const struct iio_counter_trigger *trigger;
>> +     ssize_t len =3D 0;
>> +
>> +     value =3D iio_counter_value_find_by_id(counter, chan->channel2);
>> +     if (!value)
>> +             return -EINVAL;
>> +
>> +     /* Print out a list of every Signal association to Value */
>> +     for (i =3D 0; i < value->num_triggers; i++) {
>> +             trigger =3D value->triggers + i;
>> +             len +=3D snprintf(buf + len, PAGE_SIZE - len, "%d\t%s\t%s\=
n",
>> +                     trigger->signal->id, trigger->signal->name,
>> +                     trigger->trigger_modes[trigger->mode]);
>> +             if (len >=3D PAGE_SIZE)
>> +                     return -ENOMEM;
>> +     }
>> +
>> +     return len;
>> +}
>> +
>> +static ssize_t iio_counter_trigger_mode_read(struct iio_dev *indio_dev,
>> +     uintptr_t priv, const struct iio_chan_spec *chan, char *buf)
>> +{
>> +     struct iio_counter *const counter =3D iio_priv_get_counter(indio_d=
ev);
>> +     struct iio_counter_value *value;
>> +     struct iio_counter_trigger *trigger;
>> +     const int signal_id =3D *(int *)((void *)priv);
>> +     int mode;
>> +
>> +     if (!counter->ops->trigger_mode_get)
>> +             return -EINVAL;
>> +
>> +     value =3D iio_counter_value_find_by_id(counter, chan->channel2);
>> +     if (!value)
>> +             return -EINVAL;
>> +
>> +     trigger =3D iio_counter_trigger_find_by_id(value, signal_id);
>> +     if (!trigger)
>> +             return -EINVAL;
>> +
>> +     mode =3D counter->ops->trigger_mode_get(counter, value, trigger);
>> +
>> +     if (mode < 0)
>> +             return mode;
>> +     else if (mode >=3D trigger->num_trigger_modes)
>> +             return -EINVAL;
>> +
>> +     trigger->mode =3D mode;
>> +
>> +     return scnprintf(buf, PAGE_SIZE, "%s\n", trigger->trigger_modes[mo=
de]);
>> +}
>> +
>> +static ssize_t iio_counter_trigger_mode_write(struct iio_dev *indio_dev=
,
>> +     uintptr_t priv, const struct iio_chan_spec *chan, const char *buf,
>> +     size_t len)
>> +{
>> +     struct iio_counter *const counter =3D iio_priv_get_counter(indio_d=
ev);
>> +     struct iio_counter_value *value;
>> +     ssize_t err;
>> +     struct iio_counter_trigger *trigger;
>> +     const int signal_id =3D *(int *)((void *)priv);
>> +     unsigned int mode;
>> +
>> +     if (!counter->ops->trigger_mode_set)
>> +             return -EINVAL;
>> +
>> +     value =3D iio_counter_value_find_by_id(counter, chan->channel2);
>> +     if (!value)
>> +             return -EINVAL;
>> +
>> +     trigger =3D iio_counter_trigger_find_by_id(value, signal_id);
>> +     if (!trigger)
>> +             return -EINVAL;
>> +
>> +     for (mode =3D 0; mode < trigger->num_trigger_modes; mode++)
>> +             if (sysfs_streq(buf, trigger->trigger_modes[mode]))
>> +                     break;
>> +
>> +     if (mode >=3D trigger->num_trigger_modes)
>> +             return -EINVAL;
>> +
>> +     err =3D counter->ops->trigger_mode_set(counter, value, trigger, mo=
de);
>> +     if (err)
>> +             return err;
>> +
>> +     trigger->mode =3D mode;
>> +
>> +     return len;
>> +}
>> +
>> +static ssize_t iio_counter_trigger_mode_available_read(
>> +     struct iio_dev *indio_dev, uintptr_t priv,
>> +     const struct iio_chan_spec *chan, char *buf)
>> +{
>> +     struct iio_counter *const counter =3D iio_priv_get_counter(indio_d=
ev);
>> +     struct iio_counter_value *value;
>> +     ssize_t len =3D 0;
>> +     struct iio_counter_trigger *trigger;
>> +     const int signal_id =3D *(int *)((void *)priv);
>> +     unsigned int i;
>> +
>> +     value =3D iio_counter_value_find_by_id(counter, chan->channel2);
>> +     if (!value)
>> +             return -EINVAL;
>> +
>> +     trigger =3D iio_counter_trigger_find_by_id(value, signal_id);
>> +     if (!trigger)
>> +             return -EINVAL;
>> +
>> +     for (i =3D 0; i < trigger->num_trigger_modes; i++)
>> +             len +=3D scnprintf(buf + len, PAGE_SIZE - len, "%s ",
>> +                     trigger->trigger_modes[i]);
>> +
>> +     buf[len - 1] =3D '\n';
>> +
>> +     return len;
>> +}
>> +
>> +static int iio_counter_value_function_set(struct iio_dev *indio_dev,
>> +     const struct iio_chan_spec *chan, unsigned int mode)
>> +{
>> +     struct iio_counter *const counter =3D iio_priv_get_counter(indio_d=
ev);
>> +     struct iio_counter_value *value;
>> +     int err;
>> +
>> +     if (!counter->ops->value_function_set)
>> +             return -EINVAL;
>> +
>> +     /* Find relevant Value */
>> +     value =3D iio_counter_value_find_by_id(counter, chan->channel2);
>> +     if (!value)
>> +             return -EINVAL;
>> +
>> +     /* Map IIO core function_set to Generic Counter value_function_set=
 */
>> +     err =3D counter->ops->value_function_set(counter, value, mode);
>> +     if (err)
>> +             return err;
>> +
>> +     value->mode =3D mode;
>> +
>> +     return 0;
>> +}
>> +
>> +static int iio_counter_value_function_get(struct iio_dev *indio_dev,
>> +     const struct iio_chan_spec *chan)
>> +{
>> +     struct iio_counter *const counter =3D iio_priv_get_counter(indio_d=
ev);
>> +     struct iio_counter_value *value;
>> +     int retval;
>> +
>> +     if (!counter->ops->value_function_get)
>> +             return -EINVAL;
>> +
>> +     /* Find relevant Value */
>> +     value =3D iio_counter_value_find_by_id(counter, chan->channel2);
>
> Same argument as below - would the driver not be able to do this better?
> Often it would know a simple transform to get the right one...
>
>
>> +     if (!value)
>> +             return -EINVAL;
>> +
>> +     /* Map IIO core function_get to Generic Counter value_function_get=
 */
>> +     retval =3D counter->ops->value_function_get(counter, value);
>> +     if (retval < 0)
>> +             return retval;
>> +     else if (retval >=3D value->num_function_modes)
>> +             return -EINVAL;
>> +
>> +     value->mode =3D retval;
>> +
>> +     return retval;
>> +}
>> +
>> +static int iio_counter_value_ext_info_alloc(struct iio_chan_spec *const=
 chan,
>> +     struct iio_counter_value *const value)
>> +{
>> +     const struct iio_chan_spec_ext_info ext_info_default[] =3D {
>> +             {
>> +                     .name =3D "name",
>> +                     .shared =3D IIO_SEPARATE,
>> +                     .read =3D iio_counter_value_name_read
>> +             },
>> +             IIO_ENUM("function", IIO_SEPARATE, &value->function_enum),
>> +             {
>> +                     .name =3D "function_available",
>> +                     .shared =3D IIO_SEPARATE,
>> +                     .read =3D iio_enum_available_read,
>> +                     .private =3D (void *)&value->function_enum
>> +             },
>> +             {
>> +                     .name =3D "triggers",
>> +                     .shared =3D IIO_SEPARATE,
>> +                     .read =3D iio_counter_value_triggers_read
>> +             }
>> +     };
>> +     const size_t num_default =3D ARRAY_SIZE(ext_info_default);
>> +     const struct iio_chan_spec_ext_info ext_info_trigger[] =3D {
>> +             {
>> +                     .shared =3D IIO_SEPARATE,
>> +                     .read =3D iio_counter_trigger_mode_read,
>> +                     .write =3D iio_counter_trigger_mode_write
>> +             },
>> +             {
>> +                     .shared =3D IIO_SEPARATE,
>> +                     .read =3D iio_counter_trigger_mode_available_read
>> +             }
>> +     };
>> +     const size_t num_ext_info_trigger =3D ARRAY_SIZE(ext_info_trigger)=
;
>> +     const size_t num_triggers_ext_info =3D num_ext_info_trigger *
>> +             value->num_triggers;
>> +     const size_t num_ext_info =3D num_default + num_triggers_ext_info =
+ 1;
>> +     int err;
>> +     struct iio_chan_spec_ext_info *ext_info;
>> +     const struct iio_counter_trigger *trigger;
>> +     size_t i;
>> +     size_t j;
>> +
>> +     /* Construct function_enum for Value */
>> +     value->function_enum.items =3D value->function_modes;
>> +     value->function_enum.num_items =3D value->num_function_modes;
>> +     value->function_enum.set =3D iio_counter_value_function_set;
>> +     value->function_enum.get =3D iio_counter_value_function_get;
>> +
>> +     ext_info =3D kmalloc_array(num_ext_info, sizeof(*ext_info), GFP_KE=
RNEL);
>> +     if (!ext_info)
>> +             return -ENOMEM;
>> +     ext_info[num_ext_info - 1].name =3D NULL;
>> +
>> +     /* Add ext_info for the name, function, function_available, and tr=
iggers
>> +      * attributes
>> +      */
>> +     memcpy(ext_info, ext_info_default, sizeof(ext_info_default));
>> +     /* Add ext_info for the trigger_signalX-Z and
>> +      * trigger_signalX-Z_available attributes for each Trigger
>> +      */
>> +     for (i =3D 0; i < num_triggers_ext_info; i +=3D num_ext_info_trigg=
er)
>> +             memcpy(ext_info + num_default + i, ext_info_trigger,
>> +                     sizeof(ext_info_trigger));
>> +
>> +     /* Set name for each trigger_signalX-Z and trigger_signalX-Z_avail=
able
>> +      * attribute; store the respective Signal ID address in each ext_i=
nfo
>> +      * private member
>> +      */
>> +     for (i =3D num_default, j =3D 0; j < value->num_triggers; i++, j++=
) {
>> +             trigger =3D value->triggers + j;
>> +
>> +             ext_info[i].name =3D kasprintf(GFP_KERNEL, "trigger_signal=
%d-%d",
>> +                     chan->channel, trigger->signal->id);
>> +             if (!ext_info[i].name) {
>> +                     err =3D -ENOMEM;
>> +                     goto err_name_alloc;
>> +             }
>> +             ext_info[i].private =3D (void *)&trigger->signal->id;
>> +             i++;
>> +
>> +             ext_info[i].name =3D kasprintf(GFP_KERNEL,
>> +                     "trigger_signal%d-%d_available",
>> +                     chan->channel, trigger->signal->id);
>> +             if (!ext_info[i].name) {
>> +                     err =3D -ENOMEM;
>> +                     goto err_name_alloc;
>> +             }
>> +             ext_info[i].private =3D (void *)&trigger->signal->id;
>> +     }
>> +
>> +     chan->ext_info =3D ext_info;
>> +
>> +     return 0;
>> +
>> +err_name_alloc:
>> +     while (i-- > num_default)
>> +             kfree(ext_info[i].name);
>> +     kfree(ext_info);
>> +     return err;
>> +}
>> +
>> +static void iio_counter_value_ext_info_free(
>> +     const struct iio_chan_spec *const channel)
>> +{
>> +     size_t i;
>> +     const char *const prefix =3D "trigger_signal";
>> +     const size_t prefix_len =3D strlen(prefix);
>> +
>> +     for (i =3D 0; channel->ext_info[i].name; i++)
>> +             if (!strncmp(channel->ext_info[i].name, prefix, prefix_len=
))
>> +                     kfree(channel->ext_info[i].name);
>> +     kfree(channel->ext_info);
>> +}
>> +
>> +static const struct iio_chan_spec_ext_info iio_counter_signal_ext_info[=
] =3D {
>> +     {
>> +             .name =3D "name",
>> +             .shared =3D IIO_SEPARATE,
>> +             .read =3D iio_counter_signal_name_read
>> +     },
>> +     {}
>> +};
>> +
>> +static int iio_counter_channels_alloc(struct iio_counter *const counter=
)
>> +{
>> +     const size_t num_channels =3D counter->num_signals + counter->num_=
values +
>> +             counter->num_channels;
>> +     int err;
>> +     struct iio_chan_spec *channels;
>> +     size_t j;
>> +     struct iio_counter_value *value;
>> +     size_t i =3D counter->num_channels;
>> +     const struct iio_counter_signal *signal;
>> +
>> +     channels =3D kcalloc(num_channels, sizeof(*channels), GFP_KERNEL);
>
> Bring the calculation of num_channels down here - it will make it
> more obvious that this allows space for both sources of channel.
>
>> +     if (!channels)
>> +             return -ENOMEM;
>> +
>> +     /* If any channels were supplied on Counter registration,
>> +      * we add them here to the front of the array
>> +      */
>> +     memcpy(channels, counter->channels,
>> +             counter->num_channels * sizeof(*counter->channels));
>> +
>> +     /* Add channel for each Value */
>> +     for (j =3D 0; j < counter->num_values; j++) {
>> +             value =3D counter->values + j;
>> +
>> +             channels[i].type =3D IIO_COUNT;
>> +             channels[i].channel =3D counter->id;
>> +             channels[i].channel2 =3D value->id;
>> +             channels[i].info_mask_separate =3D BIT(IIO_CHAN_INFO_RAW);
>> +             channels[i].indexed =3D 1;
>> +             channels[i].counter =3D 1;
>> +
>> +             /* Add channels for Triggers associated with Value */
> Add channels?  'i' would need incrementing.   Also that's not what this f=
unction
> is doing that I can see...
>> +             err =3D iio_counter_value_ext_info_alloc(channels + i, val=
ue);
>> +             if (err)
>> +                     goto err_value_ext_info_alloc;
>> +
>> +             i++;
>> +     }
>> +
>> +     /* Add channel for each Signal */
>> +     for (j =3D 0; j < counter->num_signals; j++) {
>> +             signal =3D counter->signals + j;
>> +
>> +             channels[i].type =3D IIO_SIGNAL;
>> +             channels[i].channel =3D counter->id;
>> +             channels[i].channel2 =3D signal->id;
>> +             channels[i].info_mask_separate =3D BIT(IIO_CHAN_INFO_RAW);
>> +             channels[i].indexed =3D 1;
>> +             channels[i].counter =3D 1;
>> +             channels[i].ext_info =3D iio_counter_signal_ext_info;
>> +
>> +             i++;
>> +     }
>> +
>> +     counter->indio_dev->num_channels =3D num_channels;
>> +     counter->indio_dev->channels =3D channels;
>> +
>> +     return 0;
>> +
>> +err_value_ext_info_alloc:
>> +     while (i-- > counter->num_channels)
>> +             iio_counter_value_ext_info_free(channels + i);
>> +     kfree(channels);
>> +     return err;
>> +}
>> +
>> +static void iio_counter_channels_free(const struct iio_counter *const c=
ounter)
>> +{
>> +     size_t i =3D counter->num_channels + counter->indio_dev->num_chann=
els;
>> +     const struct iio_chan_spec *const chans =3D counter->indio_dev->ch=
annels;
>> +
>> +     while (i-- > counter->num_channels)
>> +             /* Only IIO_COUNT channels need to be freed here */
>> +             if (chans[i].type =3D=3D IIO_COUNT)
>> +                     iio_counter_value_ext_info_free(chans + i);
>> +
>> +     kfree(chans);
>> +}
>> +
>> +static int iio_counter_read_raw(struct iio_dev *indio_dev,
>> +     struct iio_chan_spec const *chan, int *val, int *val2, long mask)
>> +{
>> +     struct iio_counter *const counter =3D iio_priv_get_counter(indio_d=
ev);
>> +     struct iio_counter_signal *signal;
>> +     struct iio_counter_value *value;
>> +
>> +     if (mask !=3D IIO_CHAN_INFO_RAW)
>> +             return -EINVAL;
>> +
>> +     switch (chan->type) {
>> +     /* Map read_raw to signal_read for Signal */
>> +     case IIO_SIGNAL:
>> +             if (!counter->ops->signal_read)
>> +                     return -EINVAL;
>> +
>> +             signal =3D iio_counter_signal_find_by_id(counter, chan->ch=
annel2);
>> +             if (!signal)
>> +                     return -EINVAL;
>> +
>> +             return counter->ops->signal_read(counter, signal, val, val=
2);
>> +     /* Map read_raw to value_read for Value */
>> +     case IIO_COUNT:
>> +             if (!counter->ops->value_read)
>> +                     return -EINVAL;
>> +
>> +             value =3D iio_counter_value_find_by_id(counter, chan->chan=
nel2);
>> +             if (!value)
>> +                     return -EINVAL;
>> +
>> +             return counter->ops->value_read(counter, value, val, val2)=
;
>> +     /* Map read_raw to read_raw for non-counter channel */
>> +     default:
>> +             if (counter->info && counter->info->read_raw)
>> +                     return counter->info->read_raw(indio_dev, chan, va=
l,
>> +                             val2, mask);
>> +     }
>> +
>> +     return -EINVAL;
>> +}
>> +
>> +static int iio_counter_write_raw(struct iio_dev *indio_dev,
>> +     struct iio_chan_spec const *chan, int val, int val2, long mask)
>> +{
>> +     struct iio_counter *const counter =3D iio_priv_get_counter(indio_d=
ev);
>> +     struct iio_counter_signal *signal;
>> +     struct iio_counter_value *value;
>> +
>> +     if (mask !=3D IIO_CHAN_INFO_RAW)
>> +             return -EINVAL;
>> +
>> +     switch (chan->type) {
>> +     /* Map write_raw to signal_write for Signal */
>> +     case IIO_SIGNAL:
>> +             if (!counter->ops->signal_write)
>> +                     return -EINVAL;
>> +
>> +             signal =3D iio_counter_signal_find_by_id(counter, chan->ch=
annel2);
>> +             if (!signal)
>> +                     return -EINVAL;
>
>         Hmm. have to search is certainly a little ugly.  Particularly as =
the driver
>         would be able to maintain this as a lookup.  I'd be tempted to pa=
ss the
>         signal id down into the callback rather than finding the signal i=
n the core.
>         Might turn out messier though... :)
>
>> +
>> +             return counter->ops->signal_write(counter, signal, val, va=
l2);
>> +     /* Map write_raw to value_write for Value */
>> +     case IIO_COUNT:
>> +             if (!counter->ops->value_write)
>> +                     return -EINVAL;
>> +
>> +             value =3D iio_counter_value_find_by_id(counter, chan->chan=
nel2);
>
> Again, this mapping is a simple lookup in the driver I think, perhaps pus=
h
> it down there?
>
>> +             if (!value)
>> +                     return -EINVAL;
>> +
>> +             return counter->ops->value_write(counter, value, val, val2=
);
>> +     /* Map write_raw to write_raw for non-counter channel */
>> +     default:
>> +             if (counter->info && counter->info->write_raw)
>> +                     return counter->info->write_raw(indio_dev, chan, v=
al,
>> +                             val2, mask);
>> +     }
>> +
>> +     return -EINVAL;
>> +}
>> +
>> +static int iio_counter_signals_validate(const struct iio_counter *const=
 counter)
>> +{
>> +     size_t i;
>> +     const struct iio_counter_signal *signal;
>> +     size_t j;
>> +     int curr_id;
>> +
>> +     /* At least one Signal must be defined */
>> +     if (!counter->num_signals || !counter->signals) {
>> +             pr_err("Counter '%d' Signals undefined\n", counter->id);
>> +             return -EINVAL;
>> +     }
>> +
>> +     for (i =3D 0; i < counter->num_signals; i++) {
>> +             signal =3D counter->signals + i;
>> +             /* No two Signals may share the same ID */
>> +             for (j =3D 0; j < i; j++) {
>> +                     curr_id =3D counter->signals[j].id;
>> +                     if (curr_id =3D=3D signal->id) {
>> +                             pr_err("Duplicate Counter '%d' Signal '%d'=
\n",
>> +                                             counter->id, signal->id);
>> +                             return -EEXIST;
>> +                     }
>> +             }
>> +             for (j =3D i + 1; j < counter->num_signals; j++) {
>> +                     curr_id =3D counter->signals[j].id;
>> +                     if (curr_id =3D=3D signal->id) {
>> +                             pr_err("Duplicate Counter '%d' Signal '%d'=
\n",
>> +                                             counter->id, signal->id);
>> +                             return -EEXIST;
>> +                     }
>> +             }
>
> Same as below.
>
>> +     }
>> +
>> +     return 0;
>> +}
>> +
>> +static int iio_counter_triggers_validate(const int counter_id,
>> +     const struct iio_counter_value *const value)
>> +{
>> +     size_t i;
>> +     const struct iio_counter_trigger *trigger;
>> +     size_t j;
>> +     int curr_id;
>> +
>> +     /* At least one Trigger must be defined */
>> +     if (!value->num_triggers || !value->triggers) {
>> +             pr_err("Counter '%d' Value '%d' Triggers undefined\n",
>> +                     counter_id, value->id);
>> +             return -EINVAL;
>> +     }
>> +
>> +     /* Ensure all Triggers have a defined Signal; this prevents a poss=
ible
>> +      * NULL pointer dereference when later validating Trigger Signal I=
Ds
>
> Fix comment syntax throughout...
>
>> +      */
>> +     for (i =3D 0; i < value->num_triggers; i++)
>> +             if (!value->triggers[i].signal) {
>> +                     pr_err("Counter '%d' Trigger '%zu' Signal undefine=
d\n",
>> +                             counter_id, i);
>> +                     return -EINVAL;
>> +             }
>> +
>> +     /* Verify validity of each Trigger */
>> +     for (i =3D 0; i < value->num_triggers; i++) {
>> +             trigger =3D value->triggers + i;
>> +             /* No two Trigger Signals may share the same ID */
>> +             for (j =3D 0; j < i; j++) {
>> +                     curr_id =3D value->triggers[j].signal->id;
>> +                     if (curr_id =3D=3D trigger->signal->id) {
>> +                             pr_err("Signal '%d' is already linked to C=
ounter '%d' Value '%d'\n",
>> +                                     trigger->signal->id, counter_id,
>> +                                     value->id);
>> +                             return -EEXIST;
>> +                     }
>> +             }
>> +             for (j =3D i + 1; j < value->num_triggers; j++) {
>> +                     curr_id =3D value->triggers[j].signal->id;
>> +                     if (curr_id =3D=3D trigger->signal->id) {
>> +                             pr_err("Signal '%d' is already linked to C=
ounter '%d' Value '%d'\n",
>> +                                     trigger->signal->id, counter_id,
>> +                                     value->id);
>> +                             return -EEXIST;
>> +                     }
>> +             }
> Again, one loop with the condition changed so it doesn't fault on the i =
=3D j case -- see below and
> note I'm reviewing backwards...
>
>
>> +
>> +             /* At least one trigger mode must be defined for each Trig=
ger */
>> +             if (!trigger->num_trigger_modes || !trigger->trigger_modes=
) {
>> +                     pr_err("Counter '%d' Signal '%d' trigger modes und=
efined\n",
>> +                             counter_id, trigger->signal->id);
>> +                     return -EINVAL;
>> +             }
>> +     }
>> +
>> +     return 0;
>> +}
>> +
>> +static int iio_counter_values_validate(const struct iio_counter *const =
counter)
>> +{
>
> Hmm. Pretty heavy weight checking.  Ah well, up to you.
>
>> +     size_t i;
>> +     const struct iio_counter_value *value;
>> +     size_t j;
>> +     int curr_id;
>> +     int err;
>> +
>> +     /* At least one Value must be defined */
>> +     if (!counter->num_values || !counter->values) {
>> +             pr_err("Counter '%d' Values undefined\n", counter->id);
>> +             return -EINVAL;
>> +     }
>> +
>> +     /* Verify validity of each Value */
>> +     for (i =3D 0; i < counter->num_values; i++) {
>> +             value =3D counter->values + i;
>> +             /* No two Values may share the same ID */
>> +             for (j =3D 0; j < i; j++) {
>
> Single loop with a slightly change to condition
> if ((i !=3D j) and (curr_id =3D=3D value->id))
>
>> +                     curr_id =3D counter->values[j].id;
>> +                     if (curr_id =3D=3D value->id) {
>> +                             pr_err("Duplicate Counter '%d' Value '%d'\=
n",
>> +                                     counter->id, value->id);
>> +                             return -EEXIST;
>> +                     }
>> +             }
>> +             for (j =3D i + 1; j < counter->num_values; j++) {
>> +                     curr_id =3D counter->values[j].id;
>> +                     if (curr_id =3D=3D value->id) {
>> +                             pr_err("Duplicate Counter '%d' Value '%d'\=
n",
>> +                                     counter->id, value->id);
>> +                             return -EEXIST;
>> +                     }
>> +             }
>> +
>> +             /* At least one function mode must be defined for each Val=
ue */
>> +             if (!value->num_function_modes || !value->function_modes) =
{
>> +                     pr_err("Counter '%d' Value '%d' function modes und=
efined\n",
>> +                             counter->id, value->id);
>> +                     return -EINVAL;
>> +             }
>> +
>> +             /* Verify the Triggers associated with each Value */
>> +             err =3D iio_counter_triggers_validate(counter->id, value);
>> +             if (err)
>> +                     return err;
>> +     }
>> +
>> +     return 0;
>> +}
>> +
>> +/**
>> + * iio_counter_register - register Counter to the system
>> + * @counter: pointer to IIO Counter to register
>> + *
>> + * This function piggybacks off of iio_device_register. First, the rele=
vant
>> + * Counter members are validated; the signals and values members must b=
e defined
>> + * and populated by valid Signal and Value structures respectively. Nex=
t, a
>> + * struct iio_dev is allocated by a call to iio_device_alloc and initia=
lized for
>> + * the Counter, IIO channels are allocated, the Counter address is stor=
ed as the
>> + * private data, and finally iio_device_register is called.
>> + */
>> +int iio_counter_register(struct iio_counter *const counter)
>> +{
>> +     const struct iio_info info_default =3D {
>> +             .read_raw =3D iio_counter_read_raw,
>> +             .write_raw =3D iio_counter_write_raw
>> +     };
>> +     int err;
>> +     struct iio_info *info;
>> +     struct iio_counter **priv;
>> +
>> +     if (!counter)
>> +             return -EINVAL;
>> +
>> +     /* Verify that Signals are valid and IDs do not conflict */
>> +     err =3D iio_counter_signals_validate(counter);
>> +     if (err)
>> +             return err;
>> +
>> +     /* Verify that Values are valid and IDs do not conflict;
> Fix multiline comment syntax to standard kernel syntax.
> /*
>  * Verify
>  */
>> +      * Triggers for each Value are also verified for validity
>> +      */
>> +     err =3D iio_counter_values_validate(counter);
>> +     if (err)
>> +             return err;
>> +
>> +     counter->indio_dev =3D iio_device_alloc(sizeof(counter));
>> +     if (!counter->indio_dev)
>> +             return -ENOMEM;
>> +
>> +     info =3D kmalloc(sizeof(*info), GFP_KERNEL);
>> +     if (!info) {
>> +             err =3D -ENOMEM;
>> +             goto err_info_alloc;
>> +     }
>> +     /* If an iio_info has been supplied than we use that,
>> +      * otherwise we set all callbacks to NULL; iio_counter_read_raw
>> +      * and iio_counter_write_raw is used for read_raw and write_raw
>> +      * for either case in order to support counter functionality
>> +      * (supplied read_raw/write_raw will be called from within
>> +      * iio_counter_read_raw/iio_counter_write_raw for non-counter
>> +      * channels)
>> +      */
>
>> +     if (counter->info) {
>> +             memcpy(info, counter->info, sizeof(*counter->info));
>> +             info->read_raw =3D iio_counter_read_raw;
>> +             info->write_raw =3D iio_counter_write_raw;
>> +     } else {
>> +             memcpy(info, &info_default, sizeof(info_default));
>> +     }
>> +
>> +     counter->indio_dev->info =3D info;
>> +     counter->indio_dev->modes =3D INDIO_DIRECT_MODE;

As I have said in the previous version, stm32 driver will not work with
this mode, so I need way to change it.
One solution could be to use iio_priv to store an iio_counter structure wit=
h
ops, arrays etc... Maybe even iio_counter structure could be hiden from dri=
vers.
It could require to split iio_counter allocation and registration to functi=
ons.

>> +     counter->indio_dev->name =3D counter->name;
>> +     counter->indio_dev->dev.parent =3D counter->dev;
>> +
>> +     /* IIO channels are allocated and set for Signals, Values, and Tri=
ggers;
>> +      * any auxiliary IIO channels provided in iio_counter are also set
>> +      */
>> +     err =3D iio_counter_channels_alloc(counter);
>> +     if (err)
>> +             goto err_channels_alloc;
>> +
>> +     /* Pointer to the counter is stored in indio_dev as a way to refer
>> +      * back to the counter from within various IIO callbacks
>> +      */
>> +     priv =3D iio_priv(counter->indio_dev);
>> +     memcpy(priv, &counter, sizeof(*priv));
>> +
>> +     err =3D iio_device_register(counter->indio_dev);
>> +     if (err)
>> +             goto err_iio_device_register;
>> +
>> +     return 0;
>> +
>> +err_iio_device_register:
>> +     iio_counter_channels_free(counter);
>> +err_channels_alloc:
>> +     kfree(info);
>> +err_info_alloc:
>> +     iio_device_free(counter->indio_dev);
>> +     return err;
>> +}
>> +EXPORT_SYMBOL(iio_counter_register);
>> +
>> +/**
>> + * iio_counter_unregister - unregister Counter from the system
>> + * @counter: pointer to IIO Counter to unregister
>> + *
>> + * The Counter is unregistered from the system. The indio_dev is unregi=
stered
>> + * and all allocated memory is freed.
>> + */
>> +void iio_counter_unregister(struct iio_counter *const counter)
>> +{
>> +     const struct iio_info *const info =3D counter->indio_dev->info;
>> +
>> +     if (!counter)
>> +             return;
>> +
>> +     iio_device_unregister(counter->indio_dev);
>> +
>> +     iio_counter_channels_free(counter);
>> +
>> +     kfree(info);
>> +     iio_device_free(counter->indio_dev);
>> +}
>> +EXPORT_SYMBOL(iio_counter_unregister);
>> +
>> +static void devm_iio_counter_unreg(struct device *dev, void *res)
>> +{
>> +     iio_counter_unregister(*(struct iio_counter **)res);
>> +}
>> +
>> +/**
>> + * devm_iio_counter_register - Resource-managed iio_counter_register
>> + * @dev: Device to allocate iio_counter for
>> + * @counter: pointer to IIO Counter to register
>> + *
>> + * Managed iio_counter_register. The IIO counter registered with this
>> + * function is automatically unregistered on driver detach. This functi=
on
>> + * calls iio_counter_register internally. Refer to that function for mo=
re
>> + * information.
>> + *
>> + * If an iio counter registered with this function needs to be unregist=
ered
>> + * separately, devm_iio_counter_unregister must be used.
>> + *
>> + * RETURNS:
>> + * 0 on success, negative error number on failure.
>> + */
>> +int devm_iio_counter_register(struct device *dev,
>> +     struct iio_counter *const counter)
>> +{
>> +     struct iio_counter **ptr;
>> +     int ret;
>> +
>> +     ptr =3D devres_alloc(devm_iio_counter_unreg, sizeof(*ptr), GFP_KER=
NEL);
>> +     if (!ptr)
>> +             return -ENOMEM;
>> +
>> +     *ptr =3D counter;
>> +     ret =3D iio_counter_register(counter);
>> +     if (!ret)
>> +             devres_add(dev, ptr);
>> +     else
>> +             devres_free(ptr);
>> +
>> +     return ret;
>> +}
>> +EXPORT_SYMBOL(devm_iio_counter_register);
>> +
>> +static int devm_iio_counter_match(struct device *dev, void *res, void *=
data)
>> +{
>> +     struct iio_counter **r =3D res;
>> +
>> +     if (!r || !*r) {
>> +             WARN_ON(!r || !*r);
>> +             return 0;
>> +     }
>> +
>> +     return *r =3D=3D data;
>> +}
>> +
>> +/**
>> + * devm_iio_counter_unregister - Resource-managed iio_counter_unregiste=
r
>> + * @dev: Device this iio_counter belongs to
>> + * @counter: the iio counter associated with the device
>> + *
>> + * Unregister iio counter registered with devm_iio_counter_register.
>> + */
>> +void devm_iio_counter_unregister(struct device *dev,
>> +     struct iio_counter *const counter)
>> +{
>> +     int rc;
>> +
>> +     rc =3D devres_release(dev, devm_iio_counter_unreg,
>> +             devm_iio_counter_match, counter);
>> +     WARN_ON(rc);
>> +}
>> +EXPORT_SYMBOL(devm_iio_counter_unregister);
>> diff --git a/include/linux/iio/counter.h b/include/linux/iio/counter.h
>> new file mode 100644
>> index 000000000000..35645406711a
>> --- /dev/null
>> +++ b/include/linux/iio/counter.h
>> @@ -0,0 +1,166 @@
>> +/*
>> + * Industrial I/O counter interface
>> + * Copyright (C) 2017 William Breathitt Gray
>> + *
>> + * This program is free software; you can redistribute it and/or modify
>> + * it under the terms of the GNU General Public License, version 2, as
>> + * published by the Free Software Foundation.
>> + *
>> + * This program is distributed in the hope that it will be useful, but
>> + * WITHOUT ANY WARRANTY; without even the implied warranty of
>> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
>> + * General Public License for more details.
>> + */
>> +#ifndef _IIO_COUNTER_H_
>> +#define _IIO_COUNTER_H_
>> +
>> +#ifdef CONFIG_IIO_COUNTER
>> +
>> +#include <linux/device.h>
>> +#include <linux/types.h>
>> +
>> +#include <linux/iio/iio.h>
>> +
>> +/**
>> + * struct iio_counter_signal - IIO Counter Signal node
>> + * @id:              [DRIVER] unique ID used to identify signal
>> + * @name:    [DRIVER] device-specific signal name
>> + */
>> +struct iio_counter_signal {
>> +     int             id;
>> +     const char      *name;
>> +};
>> +
>> +/**
>> + * struct iio_counter_trigger - IIO Counter Trigger node
>> + * @mode:            [DRIVER] current trigger mode state
>> + * @trigger_modes:   [DRIVER] available trigger modes
>> + * @num_trigger_modes:       [DRIVER] number of modes specified in @tri=
gger_modes
>> + * @signal:          [DRIVER] pointer to associated signal
>> + */
>> +struct iio_counter_trigger {
>> +     unsigned int                    mode;
>> +     const char *const               *trigger_modes;
>> +     unsigned int                    num_trigger_modes;
>> +     struct iio_counter_signal       *signal;
>
> Are signals actually changeable? - Or is that pointer constant?
>
>> +};
>> +
>> +/**
>> + * struct iio_counter_value - IIO Counter Value node
>> + * @id:                      [DRIVER] unique ID used to identify value
>> + * @name:            [DRIVER] device-specific value name
>> + * @mode:            [DRIVER] current function mode state
>> + * @function_modes:  [DRIVER] available function modes
>> + * @num_function_modes:      [DRIVER] number of modes specified in @fun=
ction_modes
>> + * @triggers:                [DRIVER] array of triggers for initializat=
ion
>> + * @num_triggers:    [DRIVER] number of triggers specified in @triggers
>> + * @function_enum:   [INTERN] used internally to generate function attr=
ibutes
>> + */
>> +struct iio_counter_value {
>> +     int                     id;
>> +     const char              *name;
>> +     unsigned int            mode;
>> +     const char *const       *function_modes;
>> +     unsigned int            num_function_modes;
>> +
>> +     struct iio_counter_trigger      *triggers;
>> +     size_t                          num_triggers;
>> +
>> +     struct iio_enum         function_enum;
>> +};
>> +
>> +struct iio_counter;
>> +
>> +/**
>> + * struct iio_counter_ops - IIO Counter related callbacks
>> + * @signal_read:     function to request a signal value from the device=
.
>> + *                   Return value will specify the type of value return=
ed by
>> + *                   the device. val and val2 will contain the elements
>> + *                   making up the returned value.
>
> Add some detail on the importance of the return value.
>
>> + * @signal_write:    function to write a signal value to the device.
>> + *                      Parameters are interpreted the same as signal_r=
ead.
>
> You need a means of establishing the correct format for write (we needed =
it
> in IIO fairly early on too)
>
>> + * @trigger_mode_set:        function to set the trigger mode. mode is =
the index of
>> + *                   the requested mode from the value trigger_modes ar=
ray.
>> + * @trigger_mode_get:        function to get the current trigger mode. =
Return value
>> + *                   will specify the index of the current mode from th=
e
>> + *                   value trigger_modes array.
>> + * @value_read:              function to request a value value from the=
 device.
>> + *                   Return value will specify the type of value return=
ed by
>> + *                   the device. val and val2 will contain the elements
>> + *                   making up the returned value.
>> + * @value_write:     function to write a value value to the device.
>> + *                      Parameters are interpreted the same as value_re=
ad.
>> + * @value_function_set: function to set the value function mode. mode i=
s the
>> + *                   index of the requested mode from the value
>> + *                   function_modes array.
>> + * @value_function_get: function to get the current value function mode=
. Return
>> + *                   value will specify the index of the current mode f=
rom
>> + *                   the value function_modes array.
>> + */
>> +struct iio_counter_ops {
>> +     int (*signal_read)(struct iio_counter *counter,
>> +             struct iio_counter_signal *signal, int *val, int *val2);
>> +     int (*signal_write)(struct iio_counter *counter,
>> +             struct iio_counter_signal *signal, int val, int val2);
>> +     int (*trigger_mode_set)(struct iio_counter *counter,
>> +             struct iio_counter_value *value,
>> +             struct iio_counter_trigger *trigger, unsigned int mode);
>> +     int (*trigger_mode_get)(struct iio_counter *counter,
>> +             struct iio_counter_value *value,
>> +             struct iio_counter_trigger *trigger);
>> +     int (*value_read)(struct iio_counter *counter,
>> +             struct iio_counter_value *value, int *val, int *val2);
>> +     int (*value_write)(struct iio_counter *counter,
>> +             struct iio_counter_value *value, int val, int val2);
>> +     int (*value_function_set)(struct iio_counter *counter,
>> +             struct iio_counter_value *value, unsigned int mode);
>> +     int (*value_function_get)(struct iio_counter *counter,
>> +             struct iio_counter_value *value);
>> +};
>> +
>> +/**
>> + * struct iio_counter - IIO Counter data structure
>> + * @id:                      [DRIVER] unique ID used to identify counte=
r
>> + * @name:            [DRIVER] name of the device
>> + * @dev:             [DRIVER] device structure, should be assigned a pa=
rent
>> + *                   and owner
>> + * @ops:             [DRIVER] callbacks from driver for counter compone=
nts
>> + * @signals:         [DRIVER] array of signals for initialization
>> + * @num_signals:     [DRIVER] number of signals specified in @signals
>> + * @values:          [DRIVER] array of values for initialization
>> + * @num_values:              [DRIVER] number of values specified in @va=
lues
>> + * @channels:                [DRIVER] channel specification structure t=
able
>> + * @num_channels:    [DRIVER] number of channels specified in @channels
>> + * @info:            [DRIVER] callbacks and constant info from driver
>> + * @indio_dev:               [INTERN] industrial I/O device structure
>> + * @driver_data:     [DRIVER] driver data
>> + */
>> +struct iio_counter {
>
> Mentioned earlier - I think naming the device counter, is confusing.
> The counters should be named counter - call it iio_counter_device
> or iio_counter_group or something and keep counter for the things
> currently called value.
>
>> +     int                             id;
>> +     const char                      *name;
>> +     struct device                   *dev;
>> +     const struct iio_counter_ops    *ops;
>> +
>> +     struct iio_counter_signal       *signals;
>> +     size_t                          num_signals;
>> +     struct iio_counter_value        *values;
>> +     size_t                          num_values;
>> +
>> +     const struct iio_chan_spec      *channels;
>> +     size_t                          num_channels;
>> +     const struct iio_info           *info;
>> +
>> +     struct iio_dev  *indio_dev;
>> +     void            *driver_data;
>> +};
>> +
>> +int iio_counter_register(struct iio_counter *const counter);
>> +void iio_counter_unregister(struct iio_counter *const counter);
>> +int devm_iio_counter_register(struct device *dev,
>> +     struct iio_counter *const counter);
>> +void devm_iio_counter_unregister(struct device *dev,
>> +     struct iio_counter *const counter);
>> +
>> +#endif /* CONFIG_IIO_COUNTER */
>> +
>> +#endif /* _IIO_COUNTER_H_ */
>



--=20
Benjamin Gaignard

Graphic Study Group

Linaro.org =E2=94=82 Open source software for ARM SoCs

Follow Linaro: Facebook | Twitter | Blog

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

end of thread, other threads:[~2017-10-09 12:56 UTC | newest]

Thread overview: 17+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2017-10-05 18:13 [PATCH v3 0/6] iio: Introduce the generic counter interface William Breathitt Gray
2017-10-05 18:13 ` [PATCH v3 1/6] iio: Implement counter channel specification and IIO_SIGNAL constant William Breathitt Gray
2017-10-08 11:57   ` Jonathan Cameron
2017-10-05 18:13 ` [PATCH v3 2/6] iio: Introduce the generic counter interface William Breathitt Gray
2017-10-08 14:30   ` Jonathan Cameron
2017-10-09 12:56     ` Benjamin Gaignard
2017-10-09 12:56       ` Benjamin Gaignard
2017-10-05 18:13 ` [PATCH v3 3/6] iio: Documentation: Add IIO Generic Counter sysfs documentation William Breathitt Gray
2017-10-08 12:10   ` Jonathan Cameron
2017-10-05 18:14 ` [PATCH v3 4/6] docs: Add IIO Generic Counter Interface documentation William Breathitt Gray
2017-10-08 13:19   ` Jonathan Cameron
2017-10-05 18:14 ` [PATCH v3 5/6] iio: Add dummy counter driver William Breathitt Gray
2017-10-08 13:41   ` Jonathan Cameron
2017-10-09 12:35     ` Benjamin Gaignard
2017-10-05 18:14 ` [PATCH v3 6/6] iio: 104-quad-8: Add IIO generic counter interface support William Breathitt Gray
2017-10-08 13:44   ` Jonathan Cameron
2017-10-08 14:38 ` [PATCH v3 0/6] iio: Introduce the generic counter interface 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.