Re: [PATCH v4 08/11] counter: Introduce the Quadrature Counter interface

From: Jonathan Cameron
Date: Mon Jan 01 2018 - 07:48:30 EST


On Thu, 14 Dec 2017 15:52:20 -0500
William Breathitt Gray <vilhelm.gray@xxxxxxxxx> wrote:

> This patch introduces the Quadrature Counter interface. The Quadrature
> Counter interface serves as an API to provide support for quadrature
> encoder counter devices. The Quadrature Counter interface is built on
> top of the Generic Counter interface.
>
> A quadrature encoder counter device is a counter device that has a
> quadrature pair of signals associated with each count value. Signals may
> have a value of "low" or "high."
>
> Signals may be represented by two possible states:
>
> QUAD_COUNTER_SIGNAL_LOW: "low"
> QUAD_COUNTER_SIGNAL_HIGH: "high"
>
> With quadrature encoders, there types of encoding are typically used:
> X1, X2, and X4; some quadrature encoders also offer a non-quadrature
> mode (typically pulse-direction encoding).
>
> The Quadrature Counter interface provides four count function modes:
>
> QUAD_COUNTER_FUNCTION_PULSE_DIRECTION: "pulse-direction"
> QUAD_COUNTER_FUNCTION_QUADRATURE_X1: "quadrature x1"
> QUAD_COUNTER_FUNCTION_QUADRATURE_X2: "quadrature x2"
> QUAD_COUNTER_FUNCTION_QUADRATURE_X4: "quadrature x4"
>
> Since the Quadrature Counter interface utilizes the Generic Counter
> interface underneath, all the expected functionality of the Generic
> Counter interface such as sysfs attributes is exposed to userspace for
> end user consumption. The Quadrature Counter interface serves as a
> convenience API for supporting a common class of counter devices without
> the need to manually configure the more cumbersome Generic Counter
> interface for use.
>
> In addition to the typical sysfs attributes of the Generic Counter
> interface, the Quadrature Counter interface provides "direction"
> attributes for each count value. These read-only attributes provide the
> current direction of their respective quadrature encoding stream.
>
> To use the Quadrature Counter interface, first create an array of
> quad_counter_count structures to represent the desired counts and
> signals of the counter device; the signal_a member of a
> quad_counter_count structure should define the Channel A signal of the
> respective quadrature pair, and similarly the signal_b member should
> define Channel B. Next, allocate a quad_counter_device structure and
> populate it with the desired driver callbacks and the quad_counter_count
> array created earlier. Finally, register the counter by calling the
> quad_counter_register function. The quad_counter_unregister function may
> be used to unregistered a previously registered counter.
>
> Memory-managed versions of quad_counter_register and
> quad_counter_unregister functions are provided by the
> devm_quad_counter_register and devm_quad_counter_unregister functions
> respectively.
>
> Signed-off-by: William Breathitt Gray <vilhelm.gray@xxxxxxxxx>

Pretty much same few comments as for the simple counter so I haven't repeated
here.

Jonathan
> ---
> drivers/iio/counter/Kconfig | 4 +-
> drivers/iio/counter/Makefile | 1 +
> drivers/iio/counter/quad-counter.c | 774 +++++++++++++++++++++++++++++++++++++
> include/linux/iio/counter.h | 191 +++++++++
> 4 files changed, 969 insertions(+), 1 deletion(-)
> create mode 100644 drivers/iio/counter/quad-counter.c
>
> diff --git a/drivers/iio/counter/Kconfig b/drivers/iio/counter/Kconfig
> index 9d7dae137f9c..33fde25e5018 100644
> --- a/drivers/iio/counter/Kconfig
> +++ b/drivers/iio/counter/Kconfig
> @@ -10,7 +10,9 @@ menuconfig COUNTER
> rudimentary support for counters and serves as building blocks to
> create more complex counter interfaces. The Simple Counter API
> provides support for simple hardware counter devices that have a
> - one-to-one mapping between their Signals and Counts.
> + one-to-one mapping between their Signals and Counts. The Quadrature
> + Counter API provides support for quadrature counter devices that have
> + Signals arranged as quadrature pairs associated to Counts.
>
> if COUNTER
>
> diff --git a/drivers/iio/counter/Makefile b/drivers/iio/counter/Makefile
> index febd2884b474..55f59e566d72 100644
> --- a/drivers/iio/counter/Makefile
> +++ b/drivers/iio/counter/Makefile
> @@ -6,6 +6,7 @@
>
> obj-$(CONFIG_COUNTER) += counter.o
> counter-$(CONFIG_COUNTER) += generic-counter.o
> +counter-$(CONFIG_COUNTER) += quad-counter.o
> counter-$(CONFIG_COUNTER) += simple-counter.o
>
> obj-$(CONFIG_104_QUAD_8) += 104-quad-8.o
> diff --git a/drivers/iio/counter/quad-counter.c b/drivers/iio/counter/quad-counter.c
> new file mode 100644
> index 000000000000..74a738e4b515
> --- /dev/null
> +++ b/drivers/iio/counter/quad-counter.c
> @@ -0,0 +1,774 @@
> +/*
> + * Quadrature 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.
> + */
> +#include <linux/device.h>
> +#include <linux/err.h>
> +#include <linux/export.h>
> +#include <linux/gfp.h>
> +#include <linux/kernel.h>
> +#include <linux/module.h>
> +#include <linux/slab.h>
> +#include <linux/string.h>
> +#include <linux/types.h>
> +
> +#include <linux/iio/counter.h>
> +
> +static const char *const quad_counter_signal_level_names[] = {
> + [QUAD_COUNTER_SIGNAL_LOW] = "low",
> + [QUAD_COUNTER_SIGNAL_HIGH] = "high"
> +};
> +
> +static ssize_t quad_counter_signal_read(struct counter_device *counter_dev,
> + struct counter_signal *counter_sig, char *buf)
> +{
> + struct quad_counter_device *const counter = counter_dev->priv;
> + struct quad_counter_signal *const signal = counter_sig->priv;
> + int err;
> + enum quad_counter_signal_level level;
> +
> + err = counter->signal_read(counter, signal, &level);
> + if (err)
> + return err;
> +
> + return scnprintf(buf, PAGE_SIZE, "%s\n",
> + quad_counter_signal_level_names[level]);
> +}
> +
> +static ssize_t quad_counter_count_read(struct counter_device *counter_dev,
> + struct counter_count *counter_cnt, char *buf)
> +{
> + struct quad_counter_device *const counter = counter_dev->priv;
> + struct quad_counter_count *const count = counter_cnt->priv;
> + int err;
> + long val;
> +
> + err = counter->count_read(counter, count, &val);
> + if (err)
> + return err;
> +
> + return scnprintf(buf, PAGE_SIZE, "%ld\n", val);
> +}
> +
> +static ssize_t quad_counter_count_write(struct counter_device *counter_dev,
> + struct counter_count *counter_cnt, const char *buf, size_t len)
> +{
> + struct quad_counter_device *const counter = counter_dev->priv;
> + struct quad_counter_count *const count = counter_cnt->priv;
> + int err;
> + long val;
> +
> + err = kstrtol(buf, 0, &val);
> + if (err)
> + return err;
> +
> + err = counter->count_write(counter, count, val);
> + if (err)
> + return err;
> +
> + return len;
> +}
> +
> +static int quad_counter_function_get(struct counter_device *counter_dev,
> + struct counter_count *counter_cnt, size_t *counter_func)
> +{
> + int err;
> + struct quad_counter_device *const counter = counter_dev->priv;
> + struct quad_counter_count *const count = counter_cnt->priv;
> + enum quad_counter_function function;
> +
> + err = counter->function_get(counter, count, &function);
> + if (err)
> + return err;
> +
> + count->function = function;
> +
> + *counter_func = function;
> +
> + return 0;
> +}
> +
> +static int quad_counter_function_set(struct counter_device *counter_dev,
> + struct counter_count *counter_cnt, size_t function)
> +{
> + struct quad_counter_device *const counter = counter_dev->priv;
> + struct quad_counter_count *const count = counter_cnt->priv;
> + int err;
> +
> + err = counter->function_set(counter, count, function);
> + if (err)
> + return err;
> +
> + count->function = function;
> +
> + return 0;
> +}
> +
> +enum quad_counter_action {
> + QUAD_COUNTER_ACTION_NONE = 0,
> + QUAD_COUNTER_ACTION_RISING_EDGE,
> + QUAD_COUNTER_ACTION_FALLING_EDGE,
> + QUAD_COUNTER_ACTION_BOTH_EDGES
> +};
> +
> +static int quad_counter_action_get(struct counter_device *counter_dev,
> + struct counter_count *counter_cnt, struct counter_synapse *counter_syn,
> + size_t *counter_act)
> +{
> + int err;
> + struct quad_counter_device *const counter = counter_dev->priv;
> + struct quad_counter_count *const count = counter_cnt->priv;
> + enum quad_counter_function function;
> + enum quad_counter_direction dir;
> +
> + err = counter->function_get(counter, count, &function);
> + if (err)
> + return err;
> +
> + /* Default action mode */
> + *counter_act = QUAD_COUNTER_ACTION_NONE;
> +
> + /* Determine action mode based on current count function mode */
> + switch (function) {
> + case QUAD_COUNTER_FUNCTION_PULSE_DIRECTION:
> + if (count->signal_a.id == counter_syn->signal->id)
> + *counter_act = QUAD_COUNTER_ACTION_RISING_EDGE;
> + break;
> + case QUAD_COUNTER_FUNCTION_QUADRATURE_X1:
> + if (count->signal_a.id == counter_syn->signal->id) {
> + err = counter->direction_get(counter, count, &dir);
> + if (err)
> + return err;
> +
> + if (dir == QUAD_COUNTER_DIRECTION_FORWARD)
> + *counter_act = QUAD_COUNTER_ACTION_RISING_EDGE;
> + else
> + *counter_act = QUAD_COUNTER_ACTION_FALLING_EDGE;
> + }
> + break;
> + case QUAD_COUNTER_FUNCTION_QUADRATURE_X2:
> + if (count->signal_a.id == counter_syn->signal->id)
> + *counter_act = QUAD_COUNTER_ACTION_BOTH_EDGES;
> + break;
> + case QUAD_COUNTER_FUNCTION_QUADRATURE_X4:
> + *counter_act = QUAD_COUNTER_ACTION_BOTH_EDGES;
> + break;
> + }
> +
> + return 0;
> +}
> +
> +static ssize_t quad_counter_signal_ext_read(struct counter_device *dev,
> + struct counter_signal *signal, void *priv, char *buf)
> +{
> + const struct quad_counter_signal_ext *const ext = priv;
> + struct quad_counter_device *const counter = dev->priv;
> + struct quad_counter_signal *const quad_signal = signal->priv;
> +
> + return ext->read(counter, quad_signal, ext->priv, buf);
> +}
> +
> +static ssize_t quad_counter_signal_ext_write(struct counter_device *dev,
> + struct counter_signal *signal, void *priv, const char *buf, size_t len)
> +{
> + const struct quad_counter_signal_ext *const ext = priv;
> + struct quad_counter_device *const counter = dev->priv;
> + struct quad_counter_signal *const quad_signal = signal->priv;
> +
> + return ext->write(counter, quad_signal, ext->priv, buf, len);
> +}
> +
> +static int quad_counter_counter_signal_ext_register(
> + const struct quad_counter_signal *const quad_signal,
> + struct counter_signal *const signal)
> +{
> + const struct quad_counter_signal_ext *const quad_ext = quad_signal->ext;
> + const size_t num_ext = quad_signal->num_ext;
> + struct counter_signal_ext *ext;
> + size_t i;
> +
> + /* Exit early if no extensions */
> + if (!quad_ext || !num_ext)
> + return 0;
> +
> + /* Allocate space for counter_signal_ext array */
> + ext = kmalloc_array(num_ext, sizeof(*ext), GFP_KERNEL);
> + if (!ext)
> + return -ENOMEM;
> +
> + /* Register quad_counter_signal_ext via counter_signal_ext */
> + for (i = 0; i < num_ext; i++) {
> + ext[i].name = quad_ext[i].name;
> + ext[i].read = (quad_ext[i].read) ?
> + quad_counter_signal_ext_read : NULL;
> + ext[i].write = (quad_ext[i].write) ?
> + quad_counter_signal_ext_write : NULL;
> + ext[i].priv = quad_ext + i;
> + }
> +
> + /* Register Counter Signal extensions */
> + signal->ext = ext;
> + signal->num_ext = num_ext;
> +
> + return 0;
> +}
> +
> +static int quad_counter_counter_signals_register(
> + const struct quad_counter_device *const counter)
> +{
> + struct counter_signal *signals;
> + const size_t num_counts = counter->num_counts;
> + const size_t num_signals = 2 * num_counts;
> + size_t i;
> + struct counter_signal *signal;
> + struct quad_counter_signal *quad_signal;
> + struct quad_counter_count *const counts = counter->counts;
> + int err;
> + struct counter_device *const counter_dev = counter->counter_dev;
> +
> + /* Allocate space for signals array */
> + signals = kcalloc(num_signals, sizeof(*signals), GFP_KERNEL);
> + if (!signals)
> + return -ENOMEM;
> +
> + /* Configure Signals */
> + for (i = 0; i < num_signals; i++) {
> + signal = signals + i;
> + if (i % 2)
> + quad_signal = &counts[i / 2].signal_b;
> + else
> + quad_signal = &counts[i / 2].signal_a;
> +
> + signal->id = quad_signal->id;
> + signal->name = quad_signal->name;
> + signal->priv = quad_signal;
> +
> + /* Register Counter Signal extensions */
> + err = quad_counter_counter_signal_ext_register(quad_signal,
> + signal);
> + if (err)
> + goto err_free_signals;
> + }
> +
> + /* Register Signals to Counter device container */
> + counter_dev->signals = signals;
> + counter_dev->num_signals = num_signals;
> +
> + return 0;
> +
> +err_free_signals:
> + while (i--)
> + kfree(signals[i].ext);
> + kfree(signals);
> + return err;
> +}
> +
> +static const char *const quad_counter_function_names[] = {
> + [QUAD_COUNTER_FUNCTION_PULSE_DIRECTION] = "pulse-direction",
> + [QUAD_COUNTER_FUNCTION_QUADRATURE_X1] = "quadrature x1",
> + [QUAD_COUNTER_FUNCTION_QUADRATURE_X2] = "quadrature x2",
> + [QUAD_COUNTER_FUNCTION_QUADRATURE_X4] = "quadrature x4"
> +};
> +
> +static const char *const quad_counter_action_names[] = {
> + [QUAD_COUNTER_ACTION_NONE] = "none",
> + [QUAD_COUNTER_ACTION_RISING_EDGE] = "rising edge",
> + [QUAD_COUNTER_ACTION_FALLING_EDGE] = "falling edge",
> + [QUAD_COUNTER_ACTION_BOTH_EDGES] = "both edges"
> +};
> +
> +static int quad_counter_counter_synapses_register(
> + struct counter_signal *const signals, struct counter_count *const count)
> +{
> + struct counter_synapse *synapses;
> + const size_t num_synapses = 2;
> + size_t i;
> +
> + /* Allocate space for Counter Synapses */
> + synapses = kcalloc(num_synapses, sizeof(*synapses), GFP_KERNEL);
> + if (!synapses)
> + return -ENOMEM;
> +
> + /* Configure Synapses */
> + for (i = 0; i < num_synapses; i++) {
> + synapses[i].signal = signals + i;
> + synapses[i].actions = quad_counter_action_names;
> + synapses[i].num_actions = ARRAY_SIZE(quad_counter_action_names);
> + }
> +
> + /* Register Counter Synapses */
> + count->synapses = synapses;
> + count->num_synapses = num_synapses;
> +
> + return 0;
> +}
> +
> +static const char *const quad_counter_direction_names[] = {
> + [QUAD_COUNTER_DIRECTION_FORWARD] = "forward",
> + [QUAD_COUNTER_DIRECTION_BACKWARD] = "backward"
> +};
> +
> +static ssize_t quad_counter_direction_read(struct counter_device *dev,
> + struct counter_count *count, void *priv, char *buf)
> +{
> + struct quad_counter_device *const counter = dev->priv;
> + struct quad_counter_count *const quad_count = count->priv;
> + int err;
> + enum quad_counter_direction direction;
> +
> + err = counter->direction_get(counter, quad_count, &direction);
> + if (err)
> + return err;
> +
> + return scnprintf(buf, PAGE_SIZE, "%s\n",
> + quad_counter_direction_names[direction]);
> +}
> +
> +static ssize_t quad_counter_count_ext_read(struct counter_device *dev,
> + struct counter_count *count, void *priv, char *buf)
> +{
> + const struct quad_counter_count_ext *const ext = priv;
> + struct quad_counter_device *const counter = dev->priv;
> + struct quad_counter_count *const quad_count = count->priv;
> +
> + return ext->read(counter, quad_count, ext->priv, buf);
> +}
> +
> +static ssize_t quad_counter_count_ext_write(struct counter_device *dev,
> + struct counter_count *count, void *priv, const char *buf, size_t len)
> +{
> + const struct quad_counter_count_ext *const ext = priv;
> + struct quad_counter_device *const counter = dev->priv;
> + struct quad_counter_count *const quad_count = count->priv;
> +
> + return ext->write(counter, quad_count, ext->priv, buf, len);
> +}
> +
> +static int quad_counter_counter_count_ext_register(
> + const struct quad_counter_device *const counter,
> + const struct quad_counter_count *const quad_count,
> + struct counter_count *const count)
> +{
> + size_t num_ext = 0;
> + const struct quad_counter_count_ext *const quad_ext = quad_count->ext;
> + const size_t quad_num_ext = quad_count->num_ext;
> + struct counter_count_ext *ext;
> + size_t ext_i = 0;
> + size_t i;
> +
> + /* Count number of extensions */
> + if (counter->direction_get)
> + num_ext++;
> + if (quad_ext)
> + num_ext += quad_num_ext;
> +
> + /* Return early if no extensions */
> + if (!num_ext)
> + return 0;
> +
> + /* Allocate space for Counter Count extensions array */
> + ext = kcalloc(num_ext, sizeof(*ext), GFP_KERNEL);
> + if (!ext)
> + return -ENOMEM;
> +
> + /* Register direction extension */
> + if (counter->direction_get) {
> + ext[ext_i].name = "direction";
> + ext[ext_i].read = quad_counter_direction_read;
> +
> + ext_i++;
> + }
> +
> + /* Register driver Quadrature Counter Count extensions */
> + for (i = 0; i < quad_num_ext; i++) {
> + ext[ext_i + i].name = quad_ext[i].name;
> + ext[ext_i + i].read = (quad_ext[i].read) ?
> + quad_counter_count_ext_read : NULL;
> + ext[ext_i + i].write = (quad_ext[i].write) ?
> + quad_counter_count_ext_write : NULL;
> + ext[ext_i + i].priv = quad_ext + i;
> + }
> + ext_i += quad_num_ext;
> +
> + /* Register Counter Count extensions */
> + count->ext = ext;
> + count->num_ext = num_ext;
> +
> + return 0;
> +}
> +
> +static void quad_counter_counter_synapses_unregister(
> + const struct counter_count *const count)
> +{
> + kfree(count->synapses);
> +}
> +
> +static int quad_counter_counter_count_init(struct counter_count *const count,
> + struct quad_counter_count *const quad_count,
> + struct counter_signal *const signals,
> + const struct quad_counter_device *const counter)
> +{
> + int err;
> +
> + count->id = quad_count->id;
> + count->name = quad_count->name;
> + count->functions = quad_counter_function_names;
> + count->num_functions = ARRAY_SIZE(quad_counter_function_names);
> + count->priv = quad_count;
> +
> + /* Register Counter Synapses */
> + err = quad_counter_counter_synapses_register(signals, count);
> + if (err)
> + return -ENOMEM;
> +
> + /* Register Quadrature Counter Count extensions */
> + err = quad_counter_counter_count_ext_register(counter, quad_count,
> + count);
> + if (err)
> + goto err_unregister_synapses;
> +
> + return 0;
> +
> +err_unregister_synapses:
> + quad_counter_counter_synapses_unregister(count);
> + return err;
> +}
> +
> +static void quad_counter_counter_count_ext_unregister(
> + const struct counter_count *const count)
> +{
> + kfree(count->ext);
> +}
> +
> +static void quad_counter_counter_count_free(
> + const struct counter_count *const count)
> +{
> + quad_counter_counter_count_ext_unregister(count);
> + quad_counter_counter_synapses_unregister(count);
> +}
> +
> +static int quad_counter_counter_counts_register(
> + const struct quad_counter_device *const counter)
> +{
> + struct counter_device *const counter_dev = counter->counter_dev;
> + struct counter_count *counts;
> + const size_t num_counts = counter->num_counts;
> + size_t i;
> + struct quad_counter_count *const quad_counts = counter->counts;
> + struct counter_signal *const signals = counter_dev->signals;
> + int err;
> +
> + /* Allocate space for counts array */
> + counts = kcalloc(num_counts, sizeof(*counts), GFP_KERNEL);
> + if (!counts)
> + return -ENOMEM;
> +
> + /* Initialize Counts */
> + for (i = 0; i < num_counts; i++) {
> + err = quad_counter_counter_count_init(counts + i,
> + quad_counts + i, signals + 2 * i, counter);
> + if (err)
> + goto err_free_counts;
> + }
> +
> + /* Register Counts to Counter device container */
> + counter_dev->counts = counts;
> + counter_dev->num_counts = num_counts;
> +
> + return 0;
> +
> +err_free_counts:
> + while (i--)
> + quad_counter_counter_count_free(counts + i);
> + kfree(counts);
> + return err;
> +}
> +
> +static void quad_counter_counter_signals_unregister(
> + const struct counter_device *const counter_dev)
> +{
> + const struct counter_signal *const signals = counter_dev->signals;
> + size_t num_signals = counter_dev->num_signals;
> +
> + while (num_signals--)
> + kfree(signals[num_signals].ext);
> + kfree(signals);
> +}
> +
> +static int quad_counter_counts_register(
> + struct quad_counter_device *const counter)
> +{
> + const struct quad_counter_count *const quad_counts = counter->counts;
> + const size_t num_counts = counter->num_counts;
> + int err;
> +
> + /* At least one Count must be defined */
> + if (!quad_counts || !num_counts) {
> + pr_err("quad-counter: Quadrature Counter Counts undefined\n");
> + return -EINVAL;
> + }
> +
> + /* Allocate Counter Signals */
> + err = quad_counter_counter_signals_register(counter);
> + if (err)
> + return err;
> +
> + /* Allocate Counter Counts */
> + err = quad_counter_counter_counts_register(counter);
> + if (err)
> + goto err_unregister_signals;
> +
> + return 0;
> +
> +err_unregister_signals:
> + quad_counter_counter_signals_unregister(counter->counter_dev);
> + return err;
> +}
> +
> +static ssize_t quad_counter_device_ext_read(struct counter_device *dev,
> + void *priv, char *buf)
> +{
> + const struct quad_counter_device_ext *const ext = priv;
> + struct quad_counter_device *const counter = dev->priv;
> +
> + return ext->read(counter, ext->priv, buf);
> +}
> +
> +static ssize_t quad_counter_device_ext_write(struct counter_device *dev,
> + void *priv, const char *buf, size_t len)
> +{
> + const struct quad_counter_device_ext *const ext = priv;
> + struct quad_counter_device *const counter = dev->priv;
> +
> + return ext->write(counter, ext->priv, buf, len);
> +}
> +
> +static int quad_counter_device_ext_register(
> + struct quad_counter_device *const counter)
> +{
> + const struct quad_counter_device_ext *const quad_ext = counter->ext;
> + const size_t num_ext = counter->num_ext;
> + struct counter_device_ext *ext;
> + size_t i;
> + struct counter_device *const counter_dev = counter->counter_dev;
> +
> + /* Return early if no extensions */
> + if (!quad_ext || !num_ext)
> + return 0;
> +
> + /* Allocate space for counter_device_ext array */
> + ext = kmalloc_array(num_ext, sizeof(*ext), GFP_KERNEL);
> + if (!ext)
> + return -ENOMEM;
> +
> + /* Register quad_counter_device_ext via counter_device_ext */
> + for (i = 0; i < num_ext; i++) {
> + ext[i].name = quad_ext[i].name;
> + ext[i].read = (quad_ext[i].read) ?
> + quad_counter_device_ext_read : NULL;
> + ext[i].write = (quad_ext[i].write) ?
> + quad_counter_device_ext_write : NULL;
> + ext[i].priv = quad_ext + i;
> + }
> +
> + /* Register Counter device extensions */
> + counter_dev->ext = ext;
> + counter_dev->num_ext = num_ext;
> +
> + return 0;
> +}
> +
> +static void quad_counter_counter_counts_unregister(
> + const struct counter_device *const counter_dev)
> +{
> + const struct counter_count *const counts = counter_dev->counts;
> + size_t num_counts = counter_dev->num_counts;
> +
> + while (num_counts--)
> + quad_counter_counter_count_free(counts + num_counts);
> + kfree(counts);
> +}
> +
> +static void quad_counter_counts_unregister(
> + const struct quad_counter_device *const counter)
> +{
> + const struct counter_device *const counter_dev = counter->counter_dev;
> +
> + quad_counter_counter_counts_unregister(counter_dev);
> + quad_counter_counter_signals_unregister(counter_dev);
> +}
> +
> +/**
> + * quad_counter_register - register Quadrature Counter to the system
> + * @counter: pointer to Quadrature Counter to register
> + *
> + * This function registers a Quadrature Counter to the system. A sysfs "counter"
> + * directory will be created and populated with sysfs attributes correlating
> + * with the Quadrature Counter Signals, Synapses, and Counts respectively.
> + */
> +int quad_counter_register(struct quad_counter_device *const counter)
> +{
> + struct counter_device *counter_dev;
> + int err;
> +
> + if (!counter)
> + return -EINVAL;
> +
> + /* Allocate internal Counter container */
> + counter_dev = kzalloc(sizeof(*counter_dev), GFP_KERNEL);
> + if (!counter)
> + return -ENOMEM;
> + counter->counter_dev = counter_dev;
> +
> + /* Configure internal Counter */
> + counter_dev->name = counter->name;
> + counter_dev->parent = counter->parent;
> + counter_dev->signal_read = (counter->signal_read) ?
> + quad_counter_signal_read : NULL;
> + counter_dev->count_read = (counter->count_read) ?
> + quad_counter_count_read : NULL;
> + counter_dev->count_write = (counter->count_write) ?
> + quad_counter_count_write : NULL;
> + counter_dev->function_get = (counter->function_get) ?
> + quad_counter_function_get : NULL;
> + counter_dev->function_set = (counter->function_set) ?
> + quad_counter_function_set : NULL;
> + counter_dev->action_get = (counter->function_get &&
> + counter->direction_get) ? quad_counter_action_get : NULL;
> + counter_dev->priv = counter;
> +
> + /* Register Quadrature Counter Counts */
> + err = quad_counter_counts_register(counter);
> + if (err)
> + goto err_free_counter_dev;
> +
> + /* Register Quadrature Counter device extension attributes */
> + err = quad_counter_device_ext_register(counter);
> + if (err)
> + goto err_unregister_counts;
> +
> + /* Register internal Counter to the system */
> + err = counter_register(counter_dev);
> + if (err)
> + goto err_free_ext;
> +
> + return 0;
> +
> +err_free_ext:
> + kfree(counter_dev->ext);
> +err_unregister_counts:
> + quad_counter_counts_unregister(counter);
> +err_free_counter_dev:
> + kfree(counter_dev);
> + return err;
> +}
> +EXPORT_SYMBOL(quad_counter_register);
> +
> +/**
> + * quad_counter_unregister - unregister Quadrature Counter from the system
> + * @counter: pointer to Quadrature Counter to unregister
> + *
> + * The Quadrature Counter is unregistered from the system; all allocated memory
> + * is freed.
> + */
> +void quad_counter_unregister(struct quad_counter_device *const counter)
> +{
> + struct counter_device *counter_dev;
> +
> + if (!counter)
> + return;
> +
> + counter_dev = counter->counter_dev;
> +
> + counter_unregister(counter_dev);
> +
> + kfree(counter_dev->ext);
> + quad_counter_counts_unregister(counter);
> + kfree(counter_dev);
> +}
> +EXPORT_SYMBOL(quad_counter_unregister);
> +
> +static void devm_quad_counter_unreg(struct device *dev, void *res)
> +{
> + quad_counter_unregister(*(struct quad_counter_device **)res);
> +}
> +
> +/**
> + * devm_quad_counter_register - Resource-managed quad_counter_register
> + * @dev: device to allocate quad_counter_device for
> + * @counter: pointer to Quadrature Counter to register
> + *
> + * Managed quad_counter_register. The Quadrature Counter registered with this
> + * function is automatically unregistered on driver detach. This function calls
> + * quad_counter_register internally. Refer to that function for more
> + * information.
> + *
> + * If an Quadrature Counter registered with this function needs to be
> + * unregistered separately, devm_quad_counter_unregister must be used.
> + *
> + * RETURNS:
> + * 0 on success, negative error number on failure.
> + */
> +int devm_quad_counter_register(struct device *dev,
> + struct quad_counter_device *const counter)
> +{
> + struct quad_counter_device **ptr;
> + int ret;
> +
> + ptr = devres_alloc(devm_quad_counter_unreg, sizeof(*ptr), GFP_KERNEL);
> + if (!ptr)
> + return -ENOMEM;
> +
> + ret = quad_counter_register(counter);
> + if (!ret) {
> + *ptr = counter;
> + devres_add(dev, ptr);
> + } else
> + devres_free(ptr);
> +
> + return ret;
> +}
> +EXPORT_SYMBOL(devm_quad_counter_register);
> +
> +static int devm_quad_counter_match(struct device *dev, void *res, void *data)
> +{
> + struct quad_counter_device **r = res;
> +
> + if (!r || !*r) {
> + WARN_ON(!r || !*r);
> + return 0;
> + }
> +
> + return *r == data;
> +}
> +
> +/**
> + * devm_quad_counter_unregister - Resource-managed quad_counter_unregister
> + * @dev: device this quad_counter_device belongs to
> + * @counter: the Quadrature Counter associated with the device
> + *
> + * Unregister Quadrature Counter registered with devm_quad_counter_register.
> + */
> +void devm_quad_counter_unregister(struct device *dev,
> + struct quad_counter_device *const counter)
> +{
> + int rc;
> +
> + rc = devres_release(dev, devm_quad_counter_unreg,
> + devm_quad_counter_match, counter);
> + WARN_ON(rc);
> +}
> +EXPORT_SYMBOL(devm_quad_counter_unregister);
> +
> +MODULE_AUTHOR("William Breathitt Gray <vilhelm.gray@xxxxxxxxx>");
> +MODULE_DESCRIPTION("Quadrature Counter interface");
> +MODULE_LICENSE("GPL v2");
> diff --git a/include/linux/iio/counter.h b/include/linux/iio/counter.h
> index 0967ea2a9bef..a6f0f9130377 100644
> --- a/include/linux/iio/counter.h
> +++ b/include/linux/iio/counter.h
> @@ -435,4 +435,195 @@ extern int devm_simple_counter_register(struct device *dev,
> extern void devm_simple_counter_unregister(struct device *dev,
> struct simple_counter_device *const counter);
>
> +struct quad_counter_device;
> +struct quad_counter_signal;
> +
> +/**
> + * struct quad_counter_signal_ext - Quadrature Counter Signal extension
> + * @name: [DRIVER] attribute name
> + * @read: [DRIVER] read callback for this attribute; may be NULL
> + * @write: [DRIVER] write callback for this attribute; may be NULL
> + * @priv: [DRIVER] data private to the driver
> + */
> +struct quad_counter_signal_ext {
> + const char *name;
> + ssize_t (*read)(struct quad_counter_device *counter,
> + struct quad_counter_signal *signal, void *priv,
> + char *buf);
> + ssize_t (*write)(struct quad_counter_device *counter,
> + struct quad_counter_signal *signal, void *priv,
> + const char *buf, size_t len);
> + void *priv;
> +};
> +
> +/**
> + * struct quad_counter_signal - Quadrature Counter Signal node
> + * @id: [DRIVER] unique ID used to identify signal
> + * @name: [DRIVER] device-specific signal name
> + * @ext: [DRIVER] optional array of Quadrature Counter Signal extensions
> + * @num_ext: [DRIVER] number of Quadrature Counter Signal extensions
> + * specified in @ext
> + * @priv: [DRIVER] optional private data supplied by driver
> + */
> +struct quad_counter_signal {
> + int id;
> + const char *name;
> +
> + const struct quad_counter_signal_ext *ext;
> + size_t num_ext;
> +
> + void *priv;
> +};
> +
> +enum quad_counter_signal_level {
> + QUAD_COUNTER_SIGNAL_LOW = 0,
> + QUAD_COUNTER_SIGNAL_HIGH
> +};
> +
> +struct quad_counter_count;
> +
> +enum quad_counter_function {
> + QUAD_COUNTER_FUNCTION_PULSE_DIRECTION = 0,
> + QUAD_COUNTER_FUNCTION_QUADRATURE_X1,
> + QUAD_COUNTER_FUNCTION_QUADRATURE_X2,
> + QUAD_COUNTER_FUNCTION_QUADRATURE_X4
> +};
> +
> +enum quad_counter_direction {
> + QUAD_COUNTER_DIRECTION_FORWARD = 0,
> + QUAD_COUNTER_DIRECTION_BACKWARD
> +};
> +
> +/**
> + * struct quad_counter_count_ext - Quadrature Counter Count extension
> + * @name: [DRIVER] attribute name
> + * @read: [DRIVER] read callback for this attribute; may be NULL
> + * @write: [DRIVER] write callback for this attribute; may be NULL
> + * @priv: [DRIVER] data private to the driver
> + */
> +struct quad_counter_count_ext {
> + const char *name;
> + ssize_t (*read)(struct quad_counter_device *counter,
> + struct quad_counter_count *count, void *priv,
> + char *buf);
> + ssize_t (*write)(struct quad_counter_device *counter,
> + struct quad_counter_count *count, void *priv,
> + const char *buf, size_t len);
> + void *priv;
> +};
> +
> +/**
> + * struct quad_counter_count - Quadrature Counter Count node
> + * @id: [DRIVER] unique ID used to identify Count
> + * @name: [DRIVER] device-specific Count name
> + * @function: [DRIVER] current function mode
> + * @direction: [DRIVER] current direction state
> + * @signal_a: [DRIVER] associated quadrature A signal
> + * @signal_b: [DRIVER] associated quadrature B signal
> + * @ext: [DRIVER] optional array of Quadrature Counter Count extensions
> + * @num_ext: [DRIVER] number of Quadrature Counter Count extensions specified
> + * in @ext
> + * @priv: [DRIVER] optional private data supplied by driver
> + */
> +struct quad_counter_count {
> + int id;
> + const char *name;
> + enum quad_counter_function function;
> + enum quad_counter_direction direction;
> +
> + struct quad_counter_signal signal_a;
> + struct quad_counter_signal signal_b;
> +
> + const struct quad_counter_count_ext *ext;
> + size_t num_ext;
> +
> + void *priv;
> +};
> +
> +/**
> + * struct quad_counter_device_ext - Quadrature Counter device extension
> + * @name: [DRIVER] attribute name
> + * @read: [DRIVER] read callback for this attribute; may be NULL
> + * @write: [DRIVER] write callback for this attribute; may be NULL
> + * @priv: [DRIVER] data private to the driver
> + */
> +struct quad_counter_device_ext {
> + const char *name;
> + ssize_t (*read)(struct quad_counter_device *counter, void *priv,
> + char *buf);
> + ssize_t (*write)(struct quad_counter_device *counter,
> + void *priv, const char *buf, size_t len);
> + void *priv;
> +};
> +
> +/**
> + * struct quad_counter_device - Quadrature Counter data structure
> + * @name: [DRIVER] name of the device
> + * @parent: [DRIVER] optional parent device providing the counters
> + * @counter_dev: [INTERN] internal Counter container
> + * @signal_read: [DRIVER] read callback for Signal attribute; may be
> + * NULL. Returns 0 on success and negative error code on
> + * error. The respective Signal's returned level should be
> + * passed back via the level parameter.
> + * @count_read: [DRIVER] read callback for Count attribute; may be NULL.
> + * Returns 0 on success and negative error code on error.
> + * The respective Count's returned value should be passed
> + * back via the val parameter.
> + * @count_write: [DRIVER] write callback for Count attribute; may be NULL
> + * @function_get: [DRIVER] function to get the current count function
> + * mode. Returns 0 on success and negative error code on
> + * error. The respective Count's returned function mode
> + * should be passed back via the function parameter.
> + * @function_set: [DRIVER] function to set the count function mode
> + * @direction_get: [DRIVER] function to get the current direction. Returns
> + * 0 on success and negative error code on error. The
> + * respective Count's returned direction should be passed
> + * back via the direction parameter.
> + * @counts: [DRIVER] array of Quadrature Counter Counts
> + * @num_counts: [DRIVER] number of Quadrature Counter Counts specified
> + * in @counts
> + * @ext: [DRIVER] optional array of Quadrature Counter device
> + * extensions
> + * @num_ext: [DRIVER] number of Quadrature Counter device extensions
> + * specified in @ext
> + * @priv: [DRIVER] optional private data supplied by driver
> + */
> +struct quad_counter_device {
> + const char *name;
> + struct device *parent;
> + struct counter_device *counter_dev;
> +
> + int (*signal_read)(struct quad_counter_device *counter,
> + struct quad_counter_signal *signal,
> + enum quad_counter_signal_level *level);
> + int (*count_read)(struct quad_counter_device *counter,
> + struct quad_counter_count *count, long *val);
> + int (*count_write)(struct quad_counter_device *counter,
> + struct quad_counter_count *count, long val);
> + int (*function_get)(struct quad_counter_device *counter,
> + struct quad_counter_count *count,
> + enum quad_counter_function *function);
> + int (*function_set)(struct quad_counter_device *counter,
> + struct quad_counter_count *count,
> + enum quad_counter_function function);
> + int (*direction_get)(struct quad_counter_device *counter,
> + struct quad_counter_count *count,
> + enum quad_counter_direction *direction);
> +
> + struct quad_counter_count *counts;
> + size_t num_counts;
> +
> + const struct quad_counter_device_ext *ext;
> + size_t num_ext;
> +
> + void *priv;
> +};
> +
> +extern int quad_counter_register(struct quad_counter_device *const counter);
> +extern void quad_counter_unregister(struct quad_counter_device *const counter);
> +extern int devm_quad_counter_register(struct device *dev,
> + struct quad_counter_device *const counter);
> +extern void devm_quad_counter_unregister(struct device *dev,
> + struct quad_counter_device *const counter);
> +
> #endif /* _COUNTER_H_ */