Linux-IIO Archive on lore.kernel.org
 help / color / Atom feed
* [PATCH v4 0/5] Introduce the Counter character device interface
@ 2020-07-21 19:35 William Breathitt Gray
  2020-07-21 19:35 ` [PATCH v4 2/5] docs: counter: Update to reflect sysfs internalization William Breathitt Gray
                   ` (5 more replies)
  0 siblings, 6 replies; 18+ messages in thread
From: William Breathitt Gray @ 2020-07-21 19:35 UTC (permalink / raw)
  To: jic23
  Cc: kamel.bouhara, gwendal, alexandre.belloni, david, linux-iio,
	linux-kernel, linux-stm32, linux-arm-kernel, syednwaris,
	patrick.havelange, fabrice.gasnier, mcoquelin.stm32,
	alexandre.torgue, William Breathitt Gray

Changes in v4:
 - Reimplement character device interface to report Counter events
 - Implement Counter timestamps
 - Implement poll() support
 - Convert microchip-tcb-capture.c to new driver interface
 - Add IRQ support for the 104-quad-8 Counter driver

Over the past couple years we have noticed some shortcomings with the
Counter sysfs interface. Although useful in the majority of situations,
there are certain use-cases where interacting through sysfs attributes
can become cumbersome and inefficient. A desire to support more advanced
functionality such as timestamps, multi-axes positioning tables, and
other such latency-sensitive applications, has motivated a reevaluation
of the Counter subsystem. I believe a character device interface will be
helpful for this more niche area of counter device use.

To quell any concerns from the offset: this patchset makes no changes to
the existing Counter sysfs userspace interface -- existing userspace
applications will continue to work with no modifications necessary. I
request that driver maintainers please test their applications to verify
that this is true, and report any discrepancies if they arise.

However, this patchset does contain a major reimplementation of the
Counter subsystem core and driver API. A reimplementation was necessary
in order to separate the sysfs code from the counter device drivers and
internalize it as a dedicated component of the core Counter subsystem
module. A minor benefit from all of this is that the sysfs interface is
now ensured a certain amount of consistency because the translation is
performed outside of individual counter device drivers.

Essentially, the reimplementation has enabled counter device drivers to
pass and handle data as native C datatypes now rather than the sysfs
strings from before. A high-level view of how a count value is passed
down from a counter device driver can be exemplified by the following:

                 ----------------------
                / Counter device       \
                +----------------------+
                | Count register: 0x28 |
                +----------------------+
                        |
                 -----------------
                / raw count data /
                -----------------
                        |
                        V
                +----------------------------+
                | Counter device driver      |----------+
                +----------------------------+          |
                | Processes data from device |   -------------------
                |----------------------------|  / driver callbacks /
                | Type: u64                  |  -------------------
                | Value: 42                  |          |
                +----------------------------+          |
                        |                               |
                 ----------                             |
                / u64     /                             |
                ----------                              |
                        |                               |
                        |                               V
                        |               +----------------------+
                        |               | Counter core         |
                        |               +----------------------+
                        |               | Routes device driver |
                        |               | callbacks to the     |
                        |               | userspace interfaces |
                        |               +----------------------+
                        |                       |
                        |                -------------------
                        |               / driver callbacks /
                        |               -------------------
                        |                       |
                +-------+---------------+       |
                |                       |       |
                |               +-------|-------+
                |               |       |
                V               |       V
        +--------------------+  |  +---------------------+
        | Counter sysfs      |<-+->| Counter chrdev      |
        +--------------------+     +---------------------+
        | Translates to the  |     | Translates to the   |
        | standard Counter   |     | standard Counter    |
        | sysfs output       |     | character device    |
        |--------------------|     |---------------------+
        | Type: const char * |     | Type: u64           |
        | Value: "42"        |     | Value: 42           |
        +--------------------+     +---------------------+
                |                               |
         ---------------                 -----------------------
        / const char * /                / struct counter_event /
        ---------------                 -----------------------
                |                               |
                |                               V
                |                       +-----------+
                |                       | read      |
                |                       +-----------+
                |                       \ Count: 42 /
                |                        -----------
                |
                V
        +--------------------------------------------------+
        | `/sys/bus/counter/devices/counterX/countY/count` |
        +--------------------------------------------------+
        \ Count: "42"                                      /
         --------------------------------------------------

Counter device data is exposed through standard character device read
operations. Device data is gathered when a Counter event is pushed by
the respective Counter device driver. Configuration is handled via ioctl
operations on the respective Counter character device node.

The following are some questions I have about this patchset:

1. Should I support multiple file descriptors for the character device
   in this introduction patchset?

   I intend to add support for multiple file descriptors to the Counter
   character device, but I restricted this patchset to a single file
   descriptor to simplify the code logic for the sake of review. If
   there is enough interest, I can add support for multiple file
   descriptors in the next revision; I anticipate that this should be
   simple to implement through the allocation of a kfifo for each file
   descriptor during the open callback.

2. Should struct counter_event have a union for different value types,
   or just a value u8 array?

   Currently I expose the event data value via a union containing the
   various possible Counter data types (value_u8 and value_u64). It is
   up to the user to select the right union member for the data they
   received. Would it make sense to return this data in a u8 array
   instead, with the expectation that the user will cast to the
   necessary data type?

3. How should errors be returned for Counter data reads performed by
   Counter events?

   Counter events are configured with a list of Counter data read
   operations to perform for the user. Any one of those data reads can
   return an error code, but not necessarily all of them. Currently, the
   code exits early when an error code is returned. Should the code
   instead continue on, saving the error code to the struct
   counter_event for userspace to handle?

William Breathitt Gray (5):
  counter: Internalize sysfs interface code
  docs: counter: Update to reflect sysfs internalization
  counter: Add character device interface
  docs: counter: Document character device interface
  counter: 104-quad-8: Add IRQ support for the ACCES 104-QUAD-8

 .../ABI/testing/sysfs-bus-counter-104-quad-8  |   32 +
 Documentation/driver-api/generic-counter.rst  |  363 +++-
 .../userspace-api/ioctl/ioctl-number.rst      |    1 +
 MAINTAINERS                                   |    2 +-
 drivers/counter/104-quad-8.c                  |  753 +++++----
 drivers/counter/Kconfig                       |    6 +-
 drivers/counter/Makefile                      |    1 +
 drivers/counter/counter-chrdev.c              |  441 +++++
 drivers/counter/counter-chrdev.h              |   16 +
 drivers/counter/counter-core.c                |  188 +++
 drivers/counter/counter-sysfs.c               |  849 ++++++++++
 drivers/counter/counter-sysfs.h               |   14 +
 drivers/counter/counter.c                     | 1496 -----------------
 drivers/counter/ftm-quaddec.c                 |   59 +-
 drivers/counter/microchip-tcb-capture.c       |  104 +-
 drivers/counter/stm32-lptimer-cnt.c           |  161 +-
 drivers/counter/stm32-timer-cnt.c             |  139 +-
 drivers/counter/ti-eqep.c                     |  211 +--
 include/linux/counter.h                       |  633 +++----
 include/linux/counter_enum.h                  |   45 -
 include/uapi/linux/counter.h                  |   90 +
 21 files changed, 2919 insertions(+), 2685 deletions(-)
 create mode 100644 drivers/counter/counter-chrdev.c
 create mode 100644 drivers/counter/counter-chrdev.h
 create mode 100644 drivers/counter/counter-core.c
 create mode 100644 drivers/counter/counter-sysfs.c
 create mode 100644 drivers/counter/counter-sysfs.h
 delete mode 100644 drivers/counter/counter.c
 delete mode 100644 include/linux/counter_enum.h
 create mode 100644 include/uapi/linux/counter.h

-- 
2.27.0


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

* [PATCH v4 2/5] docs: counter: Update to reflect sysfs internalization
  2020-07-21 19:35 [PATCH v4 0/5] Introduce the Counter character device interface William Breathitt Gray
@ 2020-07-21 19:35 ` William Breathitt Gray
  2020-07-21 19:35 ` [PATCH v4 3/5] counter: Add character device interface William Breathitt Gray
                   ` (4 subsequent siblings)
  5 siblings, 0 replies; 18+ messages in thread
From: William Breathitt Gray @ 2020-07-21 19:35 UTC (permalink / raw)
  To: jic23
  Cc: kamel.bouhara, gwendal, alexandre.belloni, david, linux-iio,
	linux-kernel, linux-stm32, linux-arm-kernel, syednwaris,
	patrick.havelange, fabrice.gasnier, mcoquelin.stm32,
	alexandre.torgue, William Breathitt Gray

The Counter subsystem architecture and driver implementations have
changed in order to handle Counter sysfs interactions in a more
consistent way. This patch updates the Generic Counter interface
documentation to reflect the changes.

Signed-off-by: William Breathitt Gray <vilhelm.gray@gmail.com>
---
 Documentation/driver-api/generic-counter.rst | 216 +++++++++++++------
 1 file changed, 150 insertions(+), 66 deletions(-)

diff --git a/Documentation/driver-api/generic-counter.rst b/Documentation/driver-api/generic-counter.rst
index b02c52cd69d6..fa2d699d44a5 100644
--- a/Documentation/driver-api/generic-counter.rst
+++ b/Documentation/driver-api/generic-counter.rst
@@ -250,8 +250,8 @@ for defining a counter device.
 .. kernel-doc:: drivers/counter/counter.c
    :export:
 
-Implementation
-==============
+Driver Implementation
+=====================
 
 To support a counter device, a driver must first allocate the available
 Counter Signals via counter_signal structures. These Signals should
@@ -267,25 +267,59 @@ respective counter_count structure. These counter_count structures are
 set to the counts array member of an allocated counter_device structure
 before the Counter is registered to the system.
 
-Driver callbacks should be provided to the counter_device structure via
-a constant counter_ops structure in order to communicate with the
-device: to read and write various Signals and Counts, and to set and get
-the "action mode" and "function mode" for various Synapses and Counts
-respectively.
+Driver callbacks must be provided to the counter_device structure in
+order to communicate with the device: to read and write various Signals
+and Counts, and to set and get the "action mode" and "function mode" for
+various Synapses and Counts respectively.
 
 A defined counter_device structure may be registered to the system by
 passing it to the counter_register function, and unregistered by passing
 it to the counter_unregister function. Similarly, the
-devm_counter_register and devm_counter_unregister functions may be used
-if device memory-managed registration is desired.
-
-Extension sysfs attributes can be created for auxiliary functionality
-and data by passing in defined counter_device_ext, counter_count_ext,
-and counter_signal_ext structures. In these cases, the
-counter_device_ext structure is used for global/miscellaneous exposure
-and configuration of the respective Counter device, while the
-counter_count_ext and counter_signal_ext structures allow for auxiliary
-exposure and configuration of a specific Count or Signal respectively.
+devm_counter_register function may be used if device memory-managed
+registration is desired.
+
+The struct counter_data structure is used to define counter extensions
+for Signals, Synapses, and Counts.
+
+The "type" member specifies the type of high-level data (e.g. BOOL,
+COUNT_DIRECTION, etc.) handled by this extension. The "`*_read`" and
+"`*_write`" members can then be set by the counter device driver with
+callbacks to handle that data using native C data types (i.e. u8, u64,
+etc.).
+
+Convenience macros such as `COUNTER_DATA_COUNT_U64` are provided for use
+by driver authors. In particular, driver authors are expected to use
+the provided macros for standard Counter subsystem attributes in order
+to maintain a consistent interface for userspace. For example, a counter
+device driver may define several standard attributes like so::
+
+        struct counter_data count_ext[] = {
+                COUNTER_DATA_DIRECTION(count_direction_read),
+                COUNTER_DATA_ENABLE(count_enable_read, count_enable_write),
+                COUNTER_DATA_CEILING(count_ceiling_read, count_ceiling_write),
+        };
+
+This makes it simple to see, add, and modify the attributes that are
+supported by this driver ("direction", "enable", and "ceiling") and to
+maintain this code without getting lost in a web of struct braces.
+
+Callbacks must match the function type expected for the respective
+component or extension. These function types are defined in the struct
+counter_data structure as the "`*_read`" and "`*_write`" union members.
+
+The corresponding callback prototypes for the extensions mentioned in
+the previous example above would be::
+
+        int count_direction_read(struct counter_device *counter,
+                                 struct counter_count *count, u8 *direction);
+        int count_enable_read(struct counter_device *counter,
+                              struct counter_count *count, u8 *enable);
+        int count_enable_write(struct counter_device *counter,
+                               struct counter_count *count, u8 enable);
+        int count_ceiling_read(struct counter_device *counter,
+                               struct counter_count *count, u64 *ceiling);
+        int count_ceiling_write(struct counter_device *counter,
+                                struct counter_count *count, u64 ceiling);
 
 Determining the type of extension to create is a matter of scope.
 
@@ -313,52 +347,102 @@ Determining the type of extension to create is a matter of scope.
   chip overheated via a device extension called "error_overtemp":
   /sys/bus/counter/devices/counterX/error_overtemp
 
-Architecture
-============
-
-When the Generic Counter interface counter module is loaded, the
-counter_init function is called which registers a bus_type named
-"counter" to the system. Subsequently, when the module is unloaded, the
-counter_exit function is called which unregisters the bus_type named
-"counter" from the system.
-
-Counter devices are registered to the system via the counter_register
-function, and later removed via the counter_unregister function. The
-counter_register function establishes a unique ID for the Counter
-device and creates a respective sysfs directory, where X is the
-mentioned unique ID:
-
-    /sys/bus/counter/devices/counterX
-
-Sysfs attributes are created within the counterX directory to expose
-functionality, configurations, and data relating to the Counts, Signals,
-and Synapses of the Counter device, as well as options and information
-for the Counter device itself.
-
-Each Signal has a directory created to house its relevant sysfs
-attributes, where Y is the unique ID of the respective Signal:
-
-    /sys/bus/counter/devices/counterX/signalY
-
-Similarly, each Count has a directory created to house its relevant
-sysfs attributes, where Y is the unique ID of the respective Count:
-
-    /sys/bus/counter/devices/counterX/countY
-
-For a more detailed breakdown of the available Generic Counter interface
-sysfs attributes, please refer to the
-Documentation/ABI/testing/sysfs-bus-counter file.
-
-The Signals and Counts associated with the Counter device are registered
-to the system as well by the counter_register function. The
-signal_read/signal_write driver callbacks are associated with their
-respective Signal attributes, while the count_read/count_write and
-function_get/function_set driver callbacks are associated with their
-respective Count attributes; similarly, the same is true for the
-action_get/action_set driver callbacks and their respective Synapse
-attributes. If a driver callback is left undefined, then the respective
-read/write permission is left disabled for the relevant attributes.
-
-Similarly, extension sysfs attributes are created for the defined
-counter_device_ext, counter_count_ext, and counter_signal_ext
-structures that are passed in.
+Subsystem Architecture
+======================
+
+Counter drivers pass and take data natively (i.e. `u8`, `u64`, etc.) and
+the shared counter module handles the translation between the sysfs
+interface. This gurantees a standard userspace interface for all counter
+drivers, and helps generalize the Generic Counter driver ABI in order to
+support the Generic Counter chrdev interface without significant changes
+to the existing counter drivers.
+
+A high-level view of how a count value is passed down from a counter
+driver is exemplified by the following::
+
+       Count data request:
+       ~~~~~~~~~~~~~~~~~~~
+                 ----------------------
+                / Counter device       \
+                +----------------------+
+                | Count register: 0x28 |
+                +----------------------+
+                        |
+                 -----------------
+                / raw count data /
+                -----------------
+                        |
+                        V
+                +----------------------------+
+                | Counter device driver      |----------+
+                +----------------------------+          |
+                | Processes data from device |   -------------------
+                |----------------------------|  / driver callbacks /
+                | Type: u64                  |  -------------------
+                | Value: 42                  |          |
+                +----------------------------+          |
+                        |                               |
+                 ----------                             |
+                / u64     /                             |
+                ----------                              |
+                        |                               |
+                        |                               V
+                        |               +----------------------+
+                        |               | Counter core         |
+                        |               +----------------------+
+                        |               | Routes device driver |
+                        |               | callbacks to the     |
+                        |               | userspace interfaces |
+                        |               +----------------------+
+                        |                       |
+                        |                -------------------
+                        |               / driver callbacks /
+                        |               -------------------
+                        |                       |
+                +-------+                       |
+                |                               |
+                |               +---------------+
+                |               |
+                V               |
+        +--------------------+  |
+        | Counter sysfs      |<-+
+        +--------------------+
+        | Translates to the  |
+        | standard Counter   |
+        | sysfs output       |
+        |--------------------|
+        | Type: const char * |
+        | Value: "42"        |
+        +--------------------+
+                |
+         ---------------
+        / const char * /
+        ---------------
+                |
+                V
+        +--------------------------------------------------+
+        | `/sys/bus/counter/devices/counterX/countY/count` |
+        +--------------------------------------------------+
+        \ Count: "42"                                      /
+         --------------------------------------------------
+
+There are three primary components involved:
+
+Counter device driver
+---------------------
+Communicates with the hardware device to read/write data; e.g. counter
+drivers for quadrature encoders, timers, etc.
+
+Counter core
+------------
+Registers the counter device driver to the system so that the respective
+callbacks are called during userspace interaction.
+
+Counter sysfs
+-------------
+Translates counter data to the standard Counter sysfs interface format
+and vice versa.
+
+Please refer to the `Documentation/ABI/testing/sysfs-bus-counter` file
+for a detailed breakdown of the available Generic Counter interface
+sysfs attributes.
-- 
2.27.0


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

* [PATCH v4 3/5] counter: Add character device interface
  2020-07-21 19:35 [PATCH v4 0/5] Introduce the Counter character device interface William Breathitt Gray
  2020-07-21 19:35 ` [PATCH v4 2/5] docs: counter: Update to reflect sysfs internalization William Breathitt Gray
@ 2020-07-21 19:35 ` William Breathitt Gray
  2020-07-29  0:20   ` David Lechner
  2020-07-21 19:35 ` [PATCH v4 4/5] docs: counter: Document " William Breathitt Gray
                   ` (3 subsequent siblings)
  5 siblings, 1 reply; 18+ messages in thread
From: William Breathitt Gray @ 2020-07-21 19:35 UTC (permalink / raw)
  To: jic23
  Cc: kamel.bouhara, gwendal, alexandre.belloni, david, linux-iio,
	linux-kernel, linux-stm32, linux-arm-kernel, syednwaris,
	patrick.havelange, fabrice.gasnier, mcoquelin.stm32,
	alexandre.torgue, William Breathitt Gray

This patch introduces a character device interface for the Counter
subsystem. Device data is exposed through standard character device read
operations. Device data is gathered when a Counter event is pushed by
the respective Counter device driver. Configuration is handled via ioctl
operations on the respective Counter character device node.

A high-level view of how a count value is passed down from a counter
driver is exemplified by the following:

                 ----------------------
                / Counter device       \
                +----------------------+
                | Count register: 0x28 |
                +----------------------+
                        |
                 -----------------
                / raw count data /
                -----------------
                        |
                        V
                +----------------------------+
                | Counter device driver      |----------+
                +----------------------------+          |
                | Processes data from device |   -------------------
                |----------------------------|  / driver callbacks /
                | Type: u64                  |  -------------------
                | Value: 42                  |          |
                +----------------------------+          |
                        |                               |
                 ----------                             |
                / u64     /                             |
                ----------                              |
                        |                               |
                        |                               V
                        |               +----------------------+
                        |               | Counter core         |
                        |               +----------------------+
                        |               | Routes device driver |
                        |               | callbacks to the     |
                        |               | userspace interfaces |
                        |               +----------------------+
                        |                       |
                        |                -------------------
                        |               / driver callbacks /
                        |               -------------------
                        |                       |
                +-------+---------------+       |
                |                       |       |
                |               +-------|-------+
                |               |       |
                V               |       V
        +--------------------+  |  +---------------------+
        | Counter sysfs      |<-+->| Counter chrdev      |
        +--------------------+     +---------------------+
        | Translates to the  |     | Translates to the   |
        | standard Counter   |     | standard Counter    |
        | sysfs output       |     | character device    |
        |--------------------|     |---------------------+
        | Type: const char * |     | Type: u64           |
        | Value: "42"        |     | Value: 42           |
        +--------------------+     +---------------------+
                |                               |
         ---------------                 -----------------------
        / const char * /                / struct counter_event /
        ---------------                 -----------------------
                |                               |
                |                               V
                |                       +-----------+
                |                       | read      |
                |                       +-----------+
                |                       \ Count: 42 /
                |                        -----------
                |
                V
        +--------------------------------------------------+
        | `/sys/bus/counter/devices/counterX/countY/count` |
        +--------------------------------------------------+
        \ Count: "42"                                      /
         --------------------------------------------------

Counter character device nodes are created under the `/dev` directory as
`counterX`, where `X` is the respective counter device id. Defines for
the standard Counter data types are exposed via the userspace
`include/uapi/linux/counter.h` file.

Counter events
--------------
Counter device drivers can support Counter events by utilizing the
`counter_push_event` function:

    int counter_push_event(struct counter_device *const counter,
                           const u8 event);

The event id is specified by the `event` parameter. When this function
is called, the Counter data associated with the respective event is
gathered, and a `struct counter_event` is generated for each datum and
pushed to userspace.

Counter events can be configured by users to report various Counter
data of interest. This can be conceptualized as a list of Counter
component read calls to perform. For example:

    +------------------------+------------------------+
    | Event 0                | Event 1                |
    +------------------------+------------------------+
    | * Count 0              | * Signal 0             |
    | * Count 1              | * Signal 0 Extension 0 |
    | * Signal 3             | * Extension 4          |
    | * Count 4 Extension 2  |                        |
    | * Signal 5 Extension 0 |                        |
    +------------------------+------------------------+

When `counter_push_event(counter, 1)` is called for example, it will go
down the list for Event 1 and execute the read callbacks for Signal 0,
Signal 0 Extension 0, and Extension 4 -- the data returned for each is
pushed to a kfifo as a `struct counter_event`, which userspace can
retrieve via a standard read operation on the respective character
device node.

Userspace
---------
Userspace applications can configure Counter events via ioctl operations
on the Counter character device node. There following ioctl codes are
supported and provided by the `linux/counter.h` userspace header file:

* COUNTER_CLEAR_WATCHES_IOCTL:
  Clear all Counter watches from all events

* COUNTER_SET_WATCH_IOCTL:
  Set a Counter watch on the specified event

To configure events to gather Counter data, users first populate a
`struct counter_watch` with the relevant event id and the information
for the desired Counter component from which to read, and then pass it
via the `COUNTER_SET_WATCH_IOCTL` ioctl command.

Userspace applications can then execute a `read` operation (optionally
calling `poll` first) on the Counter character device node to retrieve
`struct counter_event` elements with the desired data.

For example, the following userspace code opens `/dev/counter0`,
configures Event 0 to gather Count 0 and Count 1, and prints out the
data as it becomes available on the character device node:

    #include <fcntl.h>
    #include <linux/counter.h>
    #include <poll.h>
    #include <stdio.h>
    #include <sys/ioctl.h>
    #include <unistd.h>

    struct counter_watch watches[2] = {
            {
                    .event = 0,
                    .component.owner_type = COUNTER_OWNER_TYPE_COUNT,
                    .component.owner_id = 0,
                    .component.type = COUNTER_COMPONENT_TYPE_COUNT,
            },
            {
                    .event = 0,
                    .component.owner_type = COUNTER_OWNER_TYPE_COUNT,
                    .component.owner_id = 1,
                    .component.type = COUNTER_COMPONENT_TYPE_COUNT,
            },
    };

    int main(void)
    {
            struct pollfd pfd = { .events = POLLIN };
            struct counter_event event_data[2];

            pfd.fd = open("/dev/counter0", O_RDWR);

            ioctl(pfd.fd, COUNTER_SET_WATCH_IOCTL, watches);
            ioctl(pfd.fd, COUNTER_SET_WATCH_IOCTL, watches + 1);

            for (;;) {
                    poll(&pfd, 1, -1);

                    read(pfd.fd, event_data, sizeof(event_data));

                    printf("Timestamp 0: %llu\nCount 0: %llu\n"
                           "Timestamp 1: %llu\nCount 1: %llu\n",
                           (unsigned long long)event_data[0].timestamp,
                           (unsigned long long)event_data[0].value_u64,
                           (unsigned long long)event_data[1].timestamp,
                           (unsigned long long)event_data[1].value_u64);
            }

            return 0;
    }

Cc: David Lechner <david@lechnology.com>
Cc: Gwendal Grignou <gwendal@chromium.org>
Signed-off-by: William Breathitt Gray <vilhelm.gray@gmail.com>
---
 drivers/counter/Makefile         |   2 +-
 drivers/counter/counter-chrdev.c | 441 +++++++++++++++++++++++++++++++
 drivers/counter/counter-chrdev.h |  16 ++
 drivers/counter/counter-core.c   |  35 ++-
 include/linux/counter.h          |  15 ++
 include/uapi/linux/counter.h     |  52 ++++
 6 files changed, 558 insertions(+), 3 deletions(-)
 create mode 100644 drivers/counter/counter-chrdev.c
 create mode 100644 drivers/counter/counter-chrdev.h

diff --git a/drivers/counter/Makefile b/drivers/counter/Makefile
index cbe1d06af6a9..c4870eb5b1dd 100644
--- a/drivers/counter/Makefile
+++ b/drivers/counter/Makefile
@@ -4,7 +4,7 @@
 #
 
 obj-$(CONFIG_COUNTER) += counter.o
-counter-y := counter-core.o counter-sysfs.o
+counter-y := counter-core.o counter-sysfs.o counter-chrdev.o
 
 obj-$(CONFIG_104_QUAD_8)	+= 104-quad-8.o
 obj-$(CONFIG_STM32_TIMER_CNT)	+= stm32-timer-cnt.o
diff --git a/drivers/counter/counter-chrdev.c b/drivers/counter/counter-chrdev.c
new file mode 100644
index 000000000000..9aa2d32e7bc9
--- /dev/null
+++ b/drivers/counter/counter-chrdev.c
@@ -0,0 +1,441 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Generic Counter character device interface
+ * Copyright (C) 2020 William Breathitt Gray
+ */
+
+#include <linux/cdev.h>
+#include <linux/counter.h>
+#include <linux/device.h>
+#include <linux/err.h>
+#include <linux/errno.h>
+#include <linux/export.h>
+#include <linux/fs.h>
+#include <linux/list.h>
+#include <linux/poll.h>
+#include <linux/kdev_t.h>
+#include <linux/kfifo.h>
+#include <linux/slab.h>
+#include <linux/spinlock.h>
+#include <linux/timekeeping.h>
+#include <linux/types.h>
+#include <linux/wait.h>
+#include <linux/uaccess.h>
+
+#include "counter-chrdev.h"
+
+struct counter_data_item {
+	struct list_head l;
+	struct counter_component component;
+	struct counter_data data;
+	void *owner;
+};
+
+struct counter_event_item {
+	struct list_head l;
+	u8 event;
+	struct list_head data_list;
+};
+
+static ssize_t counter_chrdev_read(struct file *filp, char __user *buf,
+				   size_t len, loff_t *f_ps)
+{
+	struct counter_device *const counter = filp->private_data;
+	int err;
+	unsigned long flags;
+	unsigned int copied;
+
+	if (len < sizeof(struct counter_event))
+		return -EINVAL;
+
+	do {
+		if (kfifo_is_empty(&counter->events)) {
+			if (filp->f_flags & O_NONBLOCK)
+				return -EAGAIN;
+
+			err = wait_event_interruptible(counter->events_wait,
+					!kfifo_is_empty(&counter->events));
+			if (err)
+				return err;
+		}
+
+		raw_spin_lock_irqsave(&counter->events_lock, flags);
+		err = kfifo_to_user(&counter->events, buf, len, &copied);
+		raw_spin_unlock_irqrestore(&counter->events_lock, flags);
+		if (err)
+			return err;
+	} while (!copied);
+
+	return copied;
+}
+
+static __poll_t counter_chrdev_poll(struct file *filp,
+				    struct poll_table_struct *pollt)
+{
+	struct counter_device *const counter = filp->private_data;
+	__poll_t events = 0;
+
+	poll_wait(filp, &counter->events_wait, pollt);
+
+	if (!kfifo_is_empty(&counter->events))
+		events = EPOLLIN | EPOLLRDNORM;
+
+	return events;
+}
+
+static void counter_events_list_free(struct counter_device *const counter)
+{
+	unsigned long flags;
+	struct counter_event_item *p, *n;
+	struct counter_data_item *q, *o;
+
+	raw_spin_lock_irqsave(&counter->events_lock, flags);
+
+	list_for_each_entry_safe(p, n, &counter->events_list, l) {
+		/* Free associated data items */
+		list_for_each_entry_safe(q, o, &p->data_list, l) {
+			list_del(&q->l);
+			kfree(q);
+		}
+
+		/* Free event item */
+		list_del(&p->l);
+		kfree(p);
+	}
+
+	raw_spin_unlock_irqrestore(&counter->events_lock, flags);
+}
+
+static int counter_set_event_item(struct counter_device *const counter,
+				  const u8 event,
+				  const struct counter_data_item *const cfg)
+{
+	unsigned long flags;
+	struct counter_event_item *event_item;
+	int err;
+	struct counter_data_item *data_item;
+
+	raw_spin_lock_irqsave(&counter->events_lock, flags);
+
+	/* Search for event in the list */
+	list_for_each_entry(event_item, &counter->events_list, l)
+		if (event_item->event == event)
+			break;
+
+	/* If event is not already in the list */
+	if (&event_item->l == &counter->events_list) {
+		/* Allocate new event item */
+		event_item = kmalloc(sizeof(*event_item), GFP_ATOMIC);
+		if (!event_item) {
+			err = -ENOMEM;
+			goto err_event_item;
+		}
+
+		/* Configure event item and add to the list */
+		event_item->event = event;
+		INIT_LIST_HEAD(&event_item->data_list);
+		list_add(&event_item->l, &counter->events_list);
+	}
+
+	/* Search for data item in the list */
+	list_for_each_entry(data_item, &event_item->data_list, l)
+		if (data_item->owner == cfg->owner &&
+		    data_item->data.count_u8_read == cfg->data.count_u8_read) {
+			err = -EINVAL;
+			goto err_data_item;
+		}
+
+	/* Allocate data item */
+	data_item = kmalloc(sizeof(*data_item), GFP_ATOMIC);
+	if (!data_item) {
+		err = -ENOMEM;
+		goto err_data_item;
+	}
+	*data_item = *cfg;
+
+	/* Add data item to event item */
+	list_add_tail(&data_item->l, &event_item->data_list);
+
+	raw_spin_unlock_irqrestore(&counter->events_lock, flags);
+
+	return 0;
+
+err_data_item:
+	if (list_empty(&event_item->data_list)) {
+		list_del(&event_item->l);
+		kfree(event_item);
+	}
+err_event_item:
+	raw_spin_unlock_irqrestore(&counter->events_lock, flags);
+	return err;
+}
+
+static int counter_set_watch(struct counter_device *const counter,
+			     const unsigned long arg)
+{
+	void __user *const uwatch = (void __user *)arg;
+	struct counter_watch watch;
+	struct counter_data_item data_item;
+	size_t owner_id, id;
+	struct counter_data *ext;
+	size_t num_ext;
+
+	if (copy_from_user(&watch, uwatch, sizeof(watch)))
+		return -EFAULT;
+	owner_id = watch.component.owner_id;
+	id = watch.component.id;
+
+	/* Configure owner info for data item */
+	switch (watch.component.owner_type) {
+	case COUNTER_OWNER_TYPE_DEVICE:
+		data_item.owner = NULL;
+
+		ext = counter->ext;
+		num_ext = counter->num_ext;
+		break;
+	case COUNTER_OWNER_TYPE_SIGNAL:
+		if (counter->num_signals < owner_id + 1)
+			return -EINVAL;
+
+		data_item.owner = counter->signals + owner_id;
+
+		ext = counter->signals[owner_id].ext;
+		num_ext = counter->signals[owner_id].num_ext;
+		break;
+	case COUNTER_OWNER_TYPE_COUNT:
+		if (counter->num_counts < owner_id + 1)
+			return -EINVAL;
+
+		data_item.owner = counter->counts + owner_id;
+
+		ext = counter->counts[owner_id].ext;
+		num_ext = counter->counts[owner_id].num_ext;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	/* Configure component info for data item */
+	switch (watch.component.type) {
+	case COUNTER_COMPONENT_TYPE_SIGNAL:
+		if (watch.component.owner_type != COUNTER_OWNER_TYPE_SIGNAL)
+			return -EINVAL;
+
+		data_item.data.type = COUNTER_DATA_TYPE_SIGNAL;
+		data_item.data.signal_u8_read = counter->signal_read;
+		break;
+	case COUNTER_COMPONENT_TYPE_COUNT:
+		if (watch.component.owner_type != COUNTER_OWNER_TYPE_COUNT)
+			return -EINVAL;
+
+		data_item.data.type = COUNTER_DATA_TYPE_U64;
+		data_item.data.count_u64_read = counter->count_read;
+		break;
+	case COUNTER_COMPONENT_TYPE_COUNT_FUNCTION:
+		if (watch.component.owner_type != COUNTER_OWNER_TYPE_COUNT)
+			return -EINVAL;
+
+		data_item.data.type = COUNTER_DATA_TYPE_COUNT_FUNCTION;
+		data_item.data.count_u8_read = counter->function_read;
+		break;
+	case COUNTER_COMPONENT_TYPE_SYNAPSE_ACTION:
+		if (watch.component.owner_type != COUNTER_OWNER_TYPE_COUNT)
+			return -EINVAL;
+		if (counter->counts[owner_id].num_synapses < id + 1)
+			return -EINVAL;
+
+		data_item.data.type = COUNTER_DATA_TYPE_SYNAPSE_ACTION;
+		data_item.data.action_read = counter->action_read;
+		data_item.data.priv = counter->counts[owner_id].synapses + id;
+		break;
+	case COUNTER_COMPONENT_TYPE_EXTENSION:
+		if (num_ext < id + 1)
+			return -EINVAL;
+
+		data_item.data = ext[id];
+		break;
+	default:
+		return -EINVAL;
+	}
+	if (!data_item.data.count_u8_read)
+		return -EFAULT;
+	data_item.component = watch.component;
+
+	return counter_set_event_item(counter, watch.event, &data_item);
+}
+
+static long counter_chrdev_ioctl(struct file *filp, unsigned int cmd,
+				 unsigned long arg)
+{
+	struct counter_device *const counter = filp->private_data;
+
+	switch (cmd) {
+	case COUNTER_CLEAR_WATCHES_IOCTL:
+		counter_events_list_free(counter);
+		break;
+	case COUNTER_SET_WATCH_IOCTL:
+		return counter_set_watch(counter, arg);
+	default:
+		return -ENOIOCTLCMD;
+	}
+
+	return 0;
+}
+
+static int counter_chrdev_open(struct inode *inode, struct file *filp)
+{
+	struct counter_device *const counter = container_of(inode->i_cdev,
+							    typeof(*counter),
+							    chrdev);
+
+	get_device(&counter->dev);
+	filp->private_data = counter;
+
+	return nonseekable_open(inode, filp);
+}
+
+static int counter_chrdev_release(struct inode *inode, struct file *filp)
+{
+	struct counter_device *const counter = filp->private_data;
+
+	put_device(&counter->dev);
+
+	counter_events_list_free(counter);
+
+	return 0;
+}
+
+static const struct file_operations counter_fops = {
+	.llseek = no_llseek,
+	.read = counter_chrdev_read,
+	.poll = counter_chrdev_poll,
+	.unlocked_ioctl = counter_chrdev_ioctl,
+	.open = counter_chrdev_open,
+	.release = counter_chrdev_release,
+};
+
+int counter_chrdev_add(struct counter_device *const counter,
+		       const dev_t counter_devt)
+{
+	struct device *const dev = &counter->dev;
+	struct cdev *const chrdev = &counter->chrdev;
+
+	/* Initialize Counter events list */
+	INIT_LIST_HEAD(&counter->events_list);
+	raw_spin_lock_init(&counter->events_lock);
+
+	/* Initialize Counter events queue */
+	INIT_KFIFO(counter->events);
+	init_waitqueue_head(&counter->events_wait);
+
+	/* Initialize character device */
+	cdev_init(chrdev, &counter_fops);
+	dev->devt = MKDEV(MAJOR(counter_devt), counter->id);
+	cdev_set_parent(chrdev, &dev->kobj);
+
+	return cdev_add(chrdev, dev->devt, 1);
+}
+
+void counter_chrdev_free(struct counter_device *const counter)
+{
+	cdev_del(&counter->chrdev);
+}
+
+static int counter_get_data(struct counter_device *const counter,
+			    const struct counter_data_item *const data_item,
+			    void *const value)
+{
+	const struct counter_data *const data = &data_item->data;
+	void *const owner = data_item->owner;
+
+	switch (data->type) {
+	case COUNTER_DATA_TYPE_U8:
+	case COUNTER_DATA_TYPE_BOOL:
+	case COUNTER_DATA_TYPE_SIGNAL:
+	case COUNTER_DATA_TYPE_COUNT_FUNCTION:
+	case COUNTER_DATA_TYPE_ENUM:
+	case COUNTER_DATA_TYPE_COUNT_DIRECTION:
+	case COUNTER_DATA_TYPE_COUNT_MODE:
+		switch (data_item->component.owner_type) {
+		case COUNTER_OWNER_TYPE_DEVICE:
+			return data->device_u8_read(counter, value);
+		case COUNTER_OWNER_TYPE_SIGNAL:
+			return data->signal_u8_read(counter, owner, value);
+		case COUNTER_OWNER_TYPE_COUNT:
+			return data->count_u8_read(counter, owner, value);
+		}
+		break;
+	case COUNTER_DATA_TYPE_U64:
+		switch (data_item->component.owner_type) {
+		case COUNTER_OWNER_TYPE_DEVICE:
+			return data->device_u64_read(counter, value);
+		case COUNTER_OWNER_TYPE_SIGNAL:
+			return data->signal_u64_read(counter, owner, value);
+		case COUNTER_OWNER_TYPE_COUNT:
+			return data->count_u64_read(counter, owner, value);
+		}
+		break;
+	case COUNTER_DATA_TYPE_SYNAPSE_ACTION:
+		return data->action_read(counter, owner, data->priv, value);
+	}
+
+	return 0;
+}
+
+/**
+ * counter_push_event - queue event for userspace reading
+ * @counter:	pointer to Counter structure
+ * @event:	triggered event
+ *
+ * Note: If no one is watching for the respective event, it is silently
+ * discarded.
+ *
+ * RETURNS:
+ * 0 on success, negative error number on failure.
+ */
+int counter_push_event(struct counter_device *const counter, const u8 event)
+{
+	struct counter_event ev = { .watch.event = event };
+	unsigned int copied = 0;
+	unsigned long flags;
+	struct counter_event_item *event_item;
+	struct counter_data_item *data_item;
+	int err;
+
+	ev.timestamp = ktime_get_ns();
+
+	raw_spin_lock_irqsave(&counter->events_lock, flags);
+
+	/* Search for event in the list */
+	list_for_each_entry(event_item, &counter->events_list, l)
+		if (event_item->event == event)
+			break;
+
+	/* If event is not in the list */
+	if (&event_item->l == &counter->events_list)
+		goto exit_early;
+
+	/* Read and queue relevant data for userspace */
+	list_for_each_entry(data_item, &event_item->data_list, l) {
+		err = counter_get_data(counter, data_item, &ev.value_u8);
+		if (err)
+			goto err_counter_get_data;
+
+		ev.watch.component = data_item->component;
+
+		copied += kfifo_put(&counter->events, ev);
+	}
+
+	if (copied)
+		wake_up_poll(&counter->events_wait, EPOLLIN);
+
+exit_early:
+	raw_spin_unlock_irqrestore(&counter->events_lock, flags);
+
+	return 0;
+
+err_counter_get_data:
+	raw_spin_unlock_irqrestore(&counter->events_lock, flags);
+	return err;
+}
+EXPORT_SYMBOL_GPL(counter_push_event);
diff --git a/drivers/counter/counter-chrdev.h b/drivers/counter/counter-chrdev.h
new file mode 100644
index 000000000000..7ab0797d3857
--- /dev/null
+++ b/drivers/counter/counter-chrdev.h
@@ -0,0 +1,16 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Counter character device interface
+ * Copyright (C) 2020 William Breathitt Gray
+ */
+#ifndef _COUNTER_CHRDEV_H_
+#define _COUNTER_CHRDEV_H_
+
+#include <linux/counter.h>
+#include <linux/types.h>
+
+int counter_chrdev_add(struct counter_device *const counter,
+		       const dev_t counter_devt);
+void counter_chrdev_free(struct counter_device *const counter);
+
+#endif /* _COUNTER_CHRDEV_H_ */
diff --git a/drivers/counter/counter-core.c b/drivers/counter/counter-core.c
index 499664809c75..d9ae889a0a8c 100644
--- a/drivers/counter/counter-core.c
+++ b/drivers/counter/counter-core.c
@@ -5,12 +5,16 @@
  */
 #include <linux/counter.h>
 #include <linux/device.h>
+#include <linux/device/bus.h>
 #include <linux/export.h>
+#include <linux/fs.h>
 #include <linux/gfp.h>
 #include <linux/idr.h>
 #include <linux/init.h>
 #include <linux/module.h>
+#include <linux/types.h>
 
+#include "counter-chrdev.h"
 #include "counter-sysfs.h"
 
 /* Provides a unique ID for each counter device */
@@ -33,6 +37,8 @@ static struct bus_type counter_bus_type = {
 	.name = "counter"
 };
 
+static dev_t counter_devt;
+
 /**
  * counter_register - register Counter to the system
  * @counter:	pointer to Counter to register
@@ -62,10 +68,15 @@ int counter_register(struct counter_device *const counter)
 	device_initialize(dev);
 	dev_set_drvdata(dev, counter);
 
+	/* Add Counter character device */
+	err = counter_chrdev_add(counter, counter_devt);
+	if (err)
+		goto err_free_id;
+
 	/* Add Counter sysfs attributes */
 	err = counter_sysfs_add(counter);
 	if (err)
-		goto err_free_id;
+		goto err_free_chrdev;
 
 	/* Add device to system */
 	err = device_add(dev);
@@ -76,6 +87,8 @@ int counter_register(struct counter_device *const counter)
 
 err_free_sysfs:
 	counter_sysfs_free(counter);
+err_free_chrdev:
+	counter_chrdev_free(counter);
 err_free_id:
 	ida_simple_remove(&counter_ida, counter->id);
 	return err;
@@ -93,6 +106,7 @@ void counter_unregister(struct counter_device *const counter)
 	if (counter) {
 		device_del(&counter->dev);
 		counter_sysfs_free(counter);
+		counter_chrdev_free(counter);
 	}
 }
 EXPORT_SYMBOL_GPL(counter_unregister);
@@ -139,13 +153,30 @@ int devm_counter_register(struct device *dev,
 }
 EXPORT_SYMBOL_GPL(devm_counter_register);
 
+#define COUNTER_DEV_MAX 256
+
 static int __init counter_init(void)
 {
-	return bus_register(&counter_bus_type);
+	int err;
+
+	err = bus_register(&counter_bus_type);
+	if (err < 0)
+		return err;
+
+	err = alloc_chrdev_region(&counter_devt, 0, COUNTER_DEV_MAX, "counter");
+	if (err < 0)
+		goto err_unregister_bus;
+
+	return 0;
+
+err_unregister_bus:
+	bus_unregister(&counter_bus_type);
+	return err;
 }
 
 static void __exit counter_exit(void)
 {
+	unregister_chrdev_region(counter_devt, COUNTER_DEV_MAX);
 	bus_unregister(&counter_bus_type);
 }
 
diff --git a/include/linux/counter.h b/include/linux/counter.h
index 76657d203a26..d4f2f2463ea3 100644
--- a/include/linux/counter.h
+++ b/include/linux/counter.h
@@ -6,10 +6,14 @@
 #ifndef _COUNTER_H_
 #define _COUNTER_H_
 
+#include <linux/cdev.h>
 #include <linux/device.h>
 #include <linux/kernel.h>
+#include <linux/kfifo.h>
 #include <linux/list.h>
+#include <linux/spinlock_types.h>
 #include <linux/types.h>
+#include <linux/wait.h>
 #include <uapi/linux/counter.h>
 
 struct counter_device;
@@ -166,6 +170,11 @@ struct counter_attribute_group {
  * @priv:		optional private data supplied by driver
  * @id:			unique ID used to identify the Counter
  * @dev:		internal device structure
+ * @chrdev:		internal character device structure
+ * @events_list:	list for Counter events
+ * @events_lock:	synchronization lock for Counter events
+ * @events_wait:	wait queue to allow blocking reads of Counter events
+ * @events:		queue of detected Counter events
  * @dynamic_names_list:	list for dynamically allocated names
  * @groups_list:	attribute groups list (for Signals, Counts, and ext)
  * @num_groups:		number of attribute groups containers
@@ -204,6 +213,11 @@ struct counter_device {
 
 	int id;
 	struct device dev;
+	struct cdev chrdev;
+	struct list_head events_list;
+	raw_spinlock_t events_lock;
+	wait_queue_head_t events_wait;
+	DECLARE_KFIFO(events, struct counter_event, 64);
 	struct list_head dynamic_names_list;
 	struct counter_attribute_group *groups_list;
 	size_t num_groups;
@@ -216,6 +230,7 @@ int devm_counter_register(struct device *dev,
 			  struct counter_device *const counter);
 void devm_counter_unregister(struct device *dev,
 			     struct counter_device *const counter);
+int counter_push_event(struct counter_device *const counter, const u8 event);
 
 #define COUNTER_DATA_DEVICE_U8(_name, _read, _write) \
 { \
diff --git a/include/uapi/linux/counter.h b/include/uapi/linux/counter.h
index 2ddee9fc93e0..b903d2ad9a94 100644
--- a/include/uapi/linux/counter.h
+++ b/include/uapi/linux/counter.h
@@ -6,10 +6,62 @@
 #ifndef _UAPI_COUNTER_H_
 #define _UAPI_COUNTER_H_
 
+#include <linux/ioctl.h>
+#include <linux/types.h>
+
 #define COUNTER_OWNER_TYPE_DEVICE 0
 #define COUNTER_OWNER_TYPE_SIGNAL 1
 #define COUNTER_OWNER_TYPE_COUNT 2
 
+#define COUNTER_COMPONENT_TYPE_SIGNAL 0
+#define COUNTER_COMPONENT_TYPE_COUNT 1
+#define COUNTER_COMPONENT_TYPE_COUNT_FUNCTION 2
+#define COUNTER_COMPONENT_TYPE_SYNAPSE_ACTION 3
+#define COUNTER_COMPONENT_TYPE_EXTENSION 4
+
+/**
+ * struct counter_component - Counter component identification
+ * @owner_type: owner type (Device, Count, or Signal)
+ * @owner_id: owner identification number
+ * @type: component type (Count, extension, etc.)
+ * @id: component identification number
+ */
+struct counter_component {
+	__u8 owner_type;
+	__u64 owner_id;
+	__u8 type;
+	__u64 id;
+};
+
+/**
+ * struct counter_watch - Counter component watch configuration
+ * @event: event that triggers
+ * @component: component to watch when event triggers
+ */
+struct counter_watch {
+	__u8 event;
+	struct counter_component component;
+};
+
+#define COUNTER_CLEAR_WATCHES_IOCTL _IO(0x3E, 0x00)
+#define COUNTER_SET_WATCH_IOCTL _IOW(0x3E, 0x01, struct counter_watch)
+
+/**
+ * struct counter_event - Counter event data
+ * @timestamp: best estimate of time of event occurrence, in nanoseconds
+ * @watch: component watch configuration
+ * @value_u8: component value as __u8 data type
+ * @value_u64: component value as __u64 data type
+ */
+struct counter_event {
+	__u64 timestamp;
+	struct counter_watch watch;
+	union {
+		__u8 value_u8;
+		__u64 value_u64;
+	};
+};
+
 #define COUNTER_COUNT_DIRECTION_FORWARD 0
 #define COUNTER_COUNT_DIRECTION_BACKWARD 1
 
-- 
2.27.0


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

* [PATCH v4 4/5] docs: counter: Document character device interface
  2020-07-21 19:35 [PATCH v4 0/5] Introduce the Counter character device interface William Breathitt Gray
  2020-07-21 19:35 ` [PATCH v4 2/5] docs: counter: Update to reflect sysfs internalization William Breathitt Gray
  2020-07-21 19:35 ` [PATCH v4 3/5] counter: Add character device interface William Breathitt Gray
@ 2020-07-21 19:35 ` William Breathitt Gray
  2020-07-21 19:35 ` [PATCH v4 5/5] counter: 104-quad-8: Add IRQ support for the ACCES 104-QUAD-8 William Breathitt Gray
                   ` (2 subsequent siblings)
  5 siblings, 0 replies; 18+ messages in thread
From: William Breathitt Gray @ 2020-07-21 19:35 UTC (permalink / raw)
  To: jic23
  Cc: kamel.bouhara, gwendal, alexandre.belloni, david, linux-iio,
	linux-kernel, linux-stm32, linux-arm-kernel, syednwaris,
	patrick.havelange, fabrice.gasnier, mcoquelin.stm32,
	alexandre.torgue, William Breathitt Gray

This patch adds high-level documentation about the Counter subsystem
character device interface.

Signed-off-by: William Breathitt Gray <vilhelm.gray@gmail.com>
---
 Documentation/driver-api/generic-counter.rst  | 191 +++++++++++++++---
 .../userspace-api/ioctl/ioctl-number.rst      |   1 +
 2 files changed, 160 insertions(+), 32 deletions(-)

diff --git a/Documentation/driver-api/generic-counter.rst b/Documentation/driver-api/generic-counter.rst
index fa2d699d44a5..a5f2e8dc430c 100644
--- a/Documentation/driver-api/generic-counter.rst
+++ b/Documentation/driver-api/generic-counter.rst
@@ -223,19 +223,6 @@ whether an input line is differential or single-ended) and instead focus
 on the core idea of what the data and process represent (e.g. position
 as interpreted from quadrature encoding data).
 
-Userspace Interface
-===================
-
-Several sysfs attributes are generated by the Generic Counter interface,
-and reside under the /sys/bus/counter/devices/counterX directory, where
-counterX refers to the respective counter device. Please see
-Documentation/ABI/testing/sysfs-bus-counter for detailed
-information on each Generic Counter interface sysfs attribute.
-
-Through these sysfs attributes, programs and scripts may interact with
-the Generic Counter paradigm Counts, Signals, and Synapses of respective
-counter devices.
-
 Driver API
 ==========
 
@@ -399,25 +386,32 @@ driver is exemplified by the following::
                         |               / driver callbacks /
                         |               -------------------
                         |                       |
-                +-------+                       |
+                +-------+---------------+       |
+                |                       |       |
+                |               +-------|-------+
+                |               |       |
+                V               |       V
+        +--------------------+  |  +---------------------+
+        | Counter sysfs      |<-+->| Counter chrdev      |
+        +--------------------+     +---------------------+
+        | Translates to the  |     | Translates to the   |
+        | standard Counter   |     | standard Counter    |
+        | sysfs output       |     | character device    |
+        |--------------------|     |---------------------+
+        | Type: const char * |     | Type: u64           |
+        | Value: "42"        |     | Value: 42           |
+        +--------------------+     +---------------------+
                 |                               |
-                |               +---------------+
-                |               |
-                V               |
-        +--------------------+  |
-        | Counter sysfs      |<-+
-        +--------------------+
-        | Translates to the  |
-        | standard Counter   |
-        | sysfs output       |
-        |--------------------|
-        | Type: const char * |
-        | Value: "42"        |
-        +--------------------+
-                |
-         ---------------
-        / const char * /
-        ---------------
+         ---------------                 -----------------------
+        / const char * /                / struct counter_event /
+        ---------------                 -----------------------
+                |                               |
+                |                               V
+                |                       +-----------+
+                |                       | read      |
+                |                       +-----------+
+                |                       \ Count: 42 /
+                |                        -----------
                 |
                 V
         +--------------------------------------------------+
@@ -426,7 +420,7 @@ driver is exemplified by the following::
         \ Count: "42"                                      /
          --------------------------------------------------
 
-There are three primary components involved:
+There are four primary components involved:
 
 Counter device driver
 ---------------------
@@ -446,3 +440,136 @@ and vice versa.
 Please refer to the `Documentation/ABI/testing/sysfs-bus-counter` file
 for a detailed breakdown of the available Generic Counter interface
 sysfs attributes.
+
+Counter chrdev
+--------------
+Translates counter data to the standard Counter character device; data
+is transferred via standard character device read calls, while Counter
+events are configured via ioctl calls.
+
+Sysfs Interface
+===============
+
+Several sysfs attributes are generated by the Generic Counter interface,
+and reside under the `/sys/bus/counter/devices/counterX` directory,
+where `X` is to the respective counter device id. Please see
+`Documentation/ABI/testing/sysfs-bus-counter` for detailed information
+on each Generic Counter interface sysfs attribute.
+
+Through these sysfs attributes, programs and scripts may interact with
+the Generic Counter paradigm Counts, Signals, and Synapses of respective
+counter devices.
+
+Counter Character Device
+========================
+
+Counter character device nodes are created under the `/dev` directory as
+`counterX`, where `X` is the respective counter device id. Defines for
+the standard Counter data types are exposed via the userspace
+`include/uapi/linux/counter.h` file.
+
+Counter events
+--------------
+Counter device drivers can support Counter events by utilizing the
+`counter_push_event` function::
+
+        int counter_push_event(struct counter_device *const counter, const u8 event);
+
+The event id is specified by the `event` parameter. When this function
+is called, the Counter data associated with the respective event is
+gathered, and a `struct counter_event` is generated for each datum and
+pushed to userspace.
+
+Counter events can be configured by users to report various Counter
+data of interest. This can be conceptualized as a list of Counter
+component read calls to perform. For example::
+
+        +------------------------+------------------------+
+        | Event 0                | Event 1                |
+        +------------------------+------------------------+
+        | * Count 0              | * Signal 0             |
+        | * Count 1              | * Signal 0 Extension 0 |
+        | * Signal 3             | * Extension 4          |
+        | * Count 4 Extension 2  |                        |
+        | * Signal 5 Extension 0 |                        |
+        +------------------------+------------------------+
+
+When `counter_push_event(counter, 1)` is called for example, it will go
+down the list for Event 1 and execute the read callbacks for Signal 0,
+Signal 0 Extension 0, and Extension 4 -- the data returned for each is
+pushed to a kfifo as a `struct counter_event`, which userspace can
+retrieve via a standard read operation on the respective character
+device node.
+
+Userspace
+---------
+Userspace applications can configure Counter events via ioctl operations
+on the Counter character device node. There following ioctl codes are
+supported and provided by the `linux/counter.h` userspace header file:
+
+* COUNTER_CLEAR_WATCHES_IOCTL:
+  Clear all Counter watches from all events
+
+* COUNTER_SET_WATCH_IOCTL:
+  Set a Counter watch on the specified event
+
+To configure events to gather Counter data, users first populate a
+`struct counter_watch` with the relevant event id and the information
+for the desired Counter component from which to read, and then pass it
+via the `COUNTER_SET_WATCH_IOCTL` ioctl command.
+
+Userspace applications can then execute a `read` operation (optionally
+calling `poll` first) on the Counter character device node to retrieve
+`struct counter_event` elements with the desired data.
+
+For example, the following userspace code opens `/dev/counter0`,
+configures Event 0 to gather Count 0 and Count 1, and prints out the
+data as it becomes available on the character device node::
+
+        #include <fcntl.h>
+        #include <linux/counter.h>
+        #include <poll.h>
+        #include <stdio.h>
+        #include <sys/ioctl.h>
+        #include <unistd.h>
+
+        struct counter_watch watches[2] = {
+                {
+                        .event = 0,
+                        .component.owner_type = COUNTER_OWNER_TYPE_COUNT,
+                        .component.owner_id = 0,
+                        .component.type = COUNTER_COMPONENT_TYPE_COUNT,
+                },
+                {
+                        .event = 0,
+                        .component.owner_type = COUNTER_OWNER_TYPE_COUNT,
+                        .component.owner_id = 1,
+                        .component.type = COUNTER_COMPONENT_TYPE_COUNT,
+                },
+        };
+
+        int main(void)
+        {
+                struct pollfd pfd = { .events = POLLIN };
+                struct counter_event event_data[2];
+
+                pfd.fd = open("/dev/counter0", O_RDWR);
+
+                ioctl(pfd.fd, COUNTER_SET_WATCH_IOCTL, watches);
+                ioctl(pfd.fd, COUNTER_SET_WATCH_IOCTL, watches + 1);
+
+                for (;;) {
+                        poll(&pfd, 1, -1);
+
+                        read(pfd.fd, event_data, sizeof(event_data));
+
+                        printf("Timestamp 0: %llu\nCount 0: %llu\n"
+                               "Timestamp 1: %llu\nCount 1: %llu\n",
+                               (unsigned long long)event_data[0].timestamp,
+                               (unsigned long long)event_data[0].value_u64,
+                               (unsigned long long)event_data[1].timestamp,
+                               (unsigned long long)event_data[1].value_u64);
+                }
+
+                return 0;
+        }
diff --git a/Documentation/userspace-api/ioctl/ioctl-number.rst b/Documentation/userspace-api/ioctl/ioctl-number.rst
index 59472cd6a11d..63ff377561fd 100644
--- a/Documentation/userspace-api/ioctl/ioctl-number.rst
+++ b/Documentation/userspace-api/ioctl/ioctl-number.rst
@@ -88,6 +88,7 @@ Code  Seq#    Include File                                           Comments
                                                                      <http://infiniband.sourceforge.net/>
 0x20  all    drivers/cdrom/cm206.h
 0x22  all    scsi/sg.h
+0x3E  00-0F  linux/counter.h                                         <mailto:linux-iio@vger.kernel.org>
 '!'   00-1F  uapi/linux/seccomp.h
 '#'   00-3F                                                          IEEE 1394 Subsystem
                                                                      Block for the entire subsystem
-- 
2.27.0


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

* [PATCH v4 5/5] counter: 104-quad-8: Add IRQ support for the ACCES 104-QUAD-8
  2020-07-21 19:35 [PATCH v4 0/5] Introduce the Counter character device interface William Breathitt Gray
                   ` (2 preceding siblings ...)
  2020-07-21 19:35 ` [PATCH v4 4/5] docs: counter: Document " William Breathitt Gray
@ 2020-07-21 19:35 ` William Breathitt Gray
  2020-07-30 17:07   ` Syed Nayyar Waris
       [not found] ` <e13d43849f68af8227c6aaa0ef672b459d47e9ab.1595358237.git.vilhelm.gray@gmail.com>
  2020-08-09 13:48 ` [PATCH v4 0/5] Introduce the Counter character device interface Jonathan Cameron
  5 siblings, 1 reply; 18+ messages in thread
From: William Breathitt Gray @ 2020-07-21 19:35 UTC (permalink / raw)
  To: jic23
  Cc: kamel.bouhara, gwendal, alexandre.belloni, david, linux-iio,
	linux-kernel, linux-stm32, linux-arm-kernel, syednwaris,
	patrick.havelange, fabrice.gasnier, mcoquelin.stm32,
	alexandre.torgue, William Breathitt Gray

The LSI/CSI LS7266R1 chip provides programmable output via the FLG pins.
When interrupts are enabled on the ACCES 104-QUAD-8, they occur whenever
FLG1 is active. Four functions are available for the FLG1 signal: Carry,
Compare, Carry-Borrow, and Index.

	Carry:
		Interrupt generated on active low Carry signal. Carry
		signal toggles every time the respective channel's
		counter overflows.

	Compare:
		Interrupt generated on active low Compare signal.
		Compare signal toggles every time respective channel's
		preset register is equal to the respective channel's
		counter.

	Carry-Borrow:
		Interrupt generated on active low Carry signal and
		active low Borrow signal. Carry signal toggles every
		time the respective channel's counter overflows. Borrow
		signal toggles every time the respective channel's
		counter underflows.

	Index:
		Interrupt generated on active high Index signal.

The irq_trigger Count extension is introduced to allow the selection of
the desired IRQ trigger function per channel. The irq_trigger_enable
Count extension is introduced to allow the enablement of interrupts for
a respective channel. Interrupts push Counter events as Event X, where
'X' is the respective channel whose FLG1 activated.

This patch adds IRQ support for the ACCES 104-QUAD-8. The interrupt line
numbers for the devices may be configured via the irq array module
parameter.

Cc: Syed Nayyar Waris <syednwaris@gmail.com>
Signed-off-by: William Breathitt Gray <vilhelm.gray@gmail.com>
---
 .../ABI/testing/sysfs-bus-counter-104-quad-8  |  32 ++
 drivers/counter/104-quad-8.c                  | 283 +++++++++++++-----
 drivers/counter/Kconfig                       |   6 +-
 3 files changed, 249 insertions(+), 72 deletions(-)

diff --git a/Documentation/ABI/testing/sysfs-bus-counter-104-quad-8 b/Documentation/ABI/testing/sysfs-bus-counter-104-quad-8
index eac32180c40d..718f6199c71e 100644
--- a/Documentation/ABI/testing/sysfs-bus-counter-104-quad-8
+++ b/Documentation/ABI/testing/sysfs-bus-counter-104-quad-8
@@ -1,3 +1,35 @@
+What:		/sys/bus/counter/devices/counterX/countY/irq_trigger
+KernelVersion:	5.9
+Contact:	linux-iio@vger.kernel.org
+Description:
+		IRQ trigger function for channel Y. Four trigger functions are
+		available: carry, compare, carry-borrow, and index.
+
+		carry:
+			Interrupt generated on active low Carry signal. Carry
+			signal toggles every time channel Y counter overflows.
+
+		compare:
+			Interrupt generated on active low Compare signal.
+			Compare signal toggles every time channel Y preset
+			register is equal to channel Y counter.
+
+		carry-borrow:
+			Interrupt generated on active low Carry signal and
+			active low Borrow signal. Carry signal toggles every
+			time channel Y counter overflows. Borrow signal toggles
+			every time channel Y counter underflows.
+
+		index:
+			Interrupt generated on active high Index signal.
+
+What:		/sys/bus/counter/devices/counterX/countY/irq_trigger_enable
+KernelVersion:	5.9
+Contact:	linux-iio@vger.kernel.org
+Description:
+		Whether generation of interrupts is enabled for channel Y. Valid
+		attribute values are boolean.
+
 What:		/sys/bus/counter/devices/counterX/signalY/cable_fault
 KernelVersion:	5.7
 Contact:	linux-iio@vger.kernel.org
diff --git a/drivers/counter/104-quad-8.c b/drivers/counter/104-quad-8.c
index 0f20920073d6..b43be2d5464d 100644
--- a/drivers/counter/104-quad-8.c
+++ b/drivers/counter/104-quad-8.c
@@ -13,23 +13,30 @@
 #include <linux/iio/types.h>
 #include <linux/io.h>
 #include <linux/ioport.h>
+#include <linux/interrupt.h>
 #include <linux/isa.h>
 #include <linux/kernel.h>
 #include <linux/module.h>
 #include <linux/moduleparam.h>
 #include <linux/types.h>
+#include <linux/spinlock.h>
 
 #define QUAD8_EXTENT 32
 
 static unsigned int base[max_num_isa_dev(QUAD8_EXTENT)];
 static unsigned int num_quad8;
-module_param_array(base, uint, &num_quad8, 0);
+module_param_hw_array(base, uint, ioport, &num_quad8, 0);
 MODULE_PARM_DESC(base, "ACCES 104-QUAD-8 base addresses");
 
+static unsigned int irq[max_num_isa_dev(QUAD8_EXTENT)];
+module_param_hw_array(irq, uint, irq, NULL, 0);
+MODULE_PARM_DESC(irq, "ACCES 104-QUAD-8 interrupt line numbers");
+
 #define QUAD8_NUM_COUNTERS 8
 
 /**
  * struct quad8_iio - IIO device private data structure
+ * @lock:		synchronization lock to prevent I/O race conditions
  * @counter:		instance of the counter_device
  * @fck_prescaler:	array of filter clock prescaler configurations
  * @preset:		array of preset values
@@ -38,13 +45,14 @@ MODULE_PARM_DESC(base, "ACCES 104-QUAD-8 base addresses");
  * @quadrature_scale:	array of quadrature mode scale configurations
  * @ab_enable:		array of A and B inputs enable configurations
  * @preset_enable:	array of set_to_preset_on_index attribute configurations
+ * @irq_trigger:	array of interrupt trigger function configurations
  * @synchronous_mode:	array of index function synchronous mode configurations
  * @index_polarity:	array of index function polarity configurations
  * @cable_fault_enable:	differential encoder cable status enable configurations
  * @base:		base port address of the IIO device
  */
 struct quad8_iio {
-	struct mutex lock;
+	raw_spinlock_t lock;
 	struct counter_device counter;
 	unsigned int fck_prescaler[QUAD8_NUM_COUNTERS];
 	unsigned int preset[QUAD8_NUM_COUNTERS];
@@ -53,13 +61,16 @@ struct quad8_iio {
 	unsigned int quadrature_scale[QUAD8_NUM_COUNTERS];
 	unsigned int ab_enable[QUAD8_NUM_COUNTERS];
 	unsigned int preset_enable[QUAD8_NUM_COUNTERS];
+	unsigned int irq_trigger[QUAD8_NUM_COUNTERS];
 	unsigned int synchronous_mode[QUAD8_NUM_COUNTERS];
 	unsigned int index_polarity[QUAD8_NUM_COUNTERS];
 	unsigned int cable_fault_enable;
 	unsigned int base;
 };
 
+#define QUAD8_REG_INTERRUPT_STATUS 0x10
 #define QUAD8_REG_CHAN_OP 0x11
+#define QUAD8_REG_INDEX_INTERRUPT 0x12
 #define QUAD8_REG_INDEX_INPUT_LEVELS 0x16
 #define QUAD8_DIFF_ENCODER_CABLE_STATUS 0x17
 /* Borrow Toggle flip-flop */
@@ -92,8 +103,8 @@ struct quad8_iio {
 #define QUAD8_RLD_CNTR_OUT 0x10
 /* Transfer Preset Register LSB to FCK Prescaler */
 #define QUAD8_RLD_PRESET_PSC 0x18
-#define QUAD8_CHAN_OP_ENABLE_COUNTERS 0x00
 #define QUAD8_CHAN_OP_RESET_COUNTERS 0x01
+#define QUAD8_CHAN_OP_ENABLE_INTERRUPT_FUNC 0x04
 #define QUAD8_CMR_QUADRATURE_X1 0x08
 #define QUAD8_CMR_QUADRATURE_X2 0x10
 #define QUAD8_CMR_QUADRATURE_X4 0x18
@@ -107,6 +118,7 @@ static int quad8_read_raw(struct iio_dev *indio_dev,
 	unsigned int flags;
 	unsigned int borrow;
 	unsigned int carry;
+	unsigned long irqflags;
 	int i;
 
 	switch (mask) {
@@ -124,7 +136,7 @@ static int quad8_read_raw(struct iio_dev *indio_dev,
 		/* Borrow XOR Carry effectively doubles count range */
 		*val = (borrow ^ carry) << 24;
 
-		mutex_lock(&priv->lock);
+		raw_spin_lock_irqsave(&priv->lock, irqflags);
 
 		/* Reset Byte Pointer; transfer Counter to Output Latch */
 		outb(QUAD8_CTR_RLD | QUAD8_RLD_RESET_BP | QUAD8_RLD_CNTR_OUT,
@@ -133,7 +145,7 @@ static int quad8_read_raw(struct iio_dev *indio_dev,
 		for (i = 0; i < 3; i++)
 			*val |= (unsigned int)inb(base_offset) << (8 * i);
 
-		mutex_unlock(&priv->lock);
+		raw_spin_unlock_irqrestore(&priv->lock, irqflags);
 
 		return IIO_VAL_INT;
 	case IIO_CHAN_INFO_ENABLE:
@@ -153,6 +165,7 @@ static int quad8_write_raw(struct iio_dev *indio_dev,
 {
 	struct quad8_iio *const priv = iio_priv(indio_dev);
 	const int base_offset = priv->base + 2 * chan->channel;
+	unsigned long flags;
 	int i;
 	unsigned int ior_cfg;
 
@@ -165,7 +178,7 @@ static int quad8_write_raw(struct iio_dev *indio_dev,
 		if ((unsigned int)val > 0xFFFFFF)
 			return -EINVAL;
 
-		mutex_lock(&priv->lock);
+		raw_spin_lock_irqsave(&priv->lock, flags);
 
 		/* Reset Byte Pointer */
 		outb(QUAD8_CTR_RLD | QUAD8_RLD_RESET_BP, base_offset + 1);
@@ -190,7 +203,7 @@ static int quad8_write_raw(struct iio_dev *indio_dev,
 		/* Reset Error flag */
 		outb(QUAD8_CTR_RLD | QUAD8_RLD_RESET_E, base_offset + 1);
 
-		mutex_unlock(&priv->lock);
+		raw_spin_unlock_irqrestore(&priv->lock, flags);
 
 		return 0;
 	case IIO_CHAN_INFO_ENABLE:
@@ -198,25 +211,26 @@ static int quad8_write_raw(struct iio_dev *indio_dev,
 		if (val < 0 || val > 1)
 			return -EINVAL;
 
-		mutex_lock(&priv->lock);
+		raw_spin_lock_irqsave(&priv->lock, flags);
 
 		priv->ab_enable[chan->channel] = val;
 
-		ior_cfg = val | priv->preset_enable[chan->channel] << 1;
+		ior_cfg = val | priv->preset_enable[chan->channel] << 1 |
+			  priv->irq_trigger[chan->channel] << 3;
 
 		/* Load I/O control configuration */
 		outb(QUAD8_CTR_IOR | ior_cfg, base_offset + 1);
 
-		mutex_unlock(&priv->lock);
+		raw_spin_unlock_irqrestore(&priv->lock, flags);
 
 		return 0;
 	case IIO_CHAN_INFO_SCALE:
-		mutex_lock(&priv->lock);
+		raw_spin_lock_irqsave(&priv->lock, flags);
 
 		/* Quadrature scaling only available in quadrature mode */
 		if (!priv->quadrature_mode[chan->channel] &&
 				(val2 || val != 1)) {
-			mutex_unlock(&priv->lock);
+			raw_spin_unlock_irqrestore(&priv->lock, flags);
 			return -EINVAL;
 		}
 
@@ -232,15 +246,15 @@ static int quad8_write_raw(struct iio_dev *indio_dev,
 				priv->quadrature_scale[chan->channel] = 2;
 				break;
 			default:
-				mutex_unlock(&priv->lock);
+				raw_spin_unlock_irqrestore(&priv->lock, flags);
 				return -EINVAL;
 			}
 		else {
-			mutex_unlock(&priv->lock);
+			raw_spin_unlock_irqrestore(&priv->lock, flags);
 			return -EINVAL;
 		}
 
-		mutex_unlock(&priv->lock);
+		raw_spin_unlock_irqrestore(&priv->lock, flags);
 		return 0;
 	}
 
@@ -266,6 +280,7 @@ static ssize_t quad8_write_preset(struct iio_dev *indio_dev, uintptr_t private,
 	struct quad8_iio *const priv = iio_priv(indio_dev);
 	const int base_offset = priv->base + 2 * chan->channel;
 	unsigned int preset;
+	unsigned long irqflags;
 	int ret;
 	int i;
 
@@ -277,7 +292,7 @@ static ssize_t quad8_write_preset(struct iio_dev *indio_dev, uintptr_t private,
 	if (preset > 0xFFFFFF)
 		return -EINVAL;
 
-	mutex_lock(&priv->lock);
+	raw_spin_lock_irqsave(&priv->lock, irqflags);
 
 	priv->preset[chan->channel] = preset;
 
@@ -288,7 +303,7 @@ static ssize_t quad8_write_preset(struct iio_dev *indio_dev, uintptr_t private,
 	for (i = 0; i < 3; i++)
 		outb(preset >> (8 * i), base_offset);
 
-	mutex_unlock(&priv->lock);
+	raw_spin_unlock_irqrestore(&priv->lock, irqflags);
 
 	return len;
 }
@@ -309,6 +324,7 @@ static ssize_t quad8_write_set_to_preset_on_index(struct iio_dev *indio_dev,
 	struct quad8_iio *const priv = iio_priv(indio_dev);
 	const int base_offset = priv->base + 2 * chan->channel + 1;
 	bool preset_enable;
+	unsigned long irqflags;
 	int ret;
 	unsigned int ior_cfg;
 
@@ -319,17 +335,18 @@ static ssize_t quad8_write_set_to_preset_on_index(struct iio_dev *indio_dev,
 	/* Preset enable is active low in Input/Output Control register */
 	preset_enable = !preset_enable;
 
-	mutex_lock(&priv->lock);
+	raw_spin_lock_irqsave(&priv->lock, irqflags);
 
 	priv->preset_enable[chan->channel] = preset_enable;
 
 	ior_cfg = priv->ab_enable[chan->channel] |
-		(unsigned int)preset_enable << 1;
+		  (unsigned int)preset_enable << 1 |
+		  priv->irq_trigger[chan->channel] << 3;
 
 	/* Load I/O control configuration to Input / Output Control Register */
 	outb(QUAD8_CTR_IOR | ior_cfg, base_offset);
 
-	mutex_unlock(&priv->lock);
+	raw_spin_unlock_irqrestore(&priv->lock, irqflags);
 
 	return len;
 }
@@ -387,8 +404,9 @@ static int quad8_set_count_mode(struct iio_dev *indio_dev,
 	struct quad8_iio *const priv = iio_priv(indio_dev);
 	unsigned int mode_cfg = cnt_mode << 1;
 	const int base_offset = priv->base + 2 * chan->channel + 1;
+	unsigned long irqflags;
 
-	mutex_lock(&priv->lock);
+	raw_spin_lock_irqsave(&priv->lock, irqflags);
 
 	priv->count_mode[chan->channel] = cnt_mode;
 
@@ -399,7 +417,7 @@ static int quad8_set_count_mode(struct iio_dev *indio_dev,
 	/* Load mode configuration to Counter Mode Register */
 	outb(QUAD8_CTR_CMR | mode_cfg, base_offset);
 
-	mutex_unlock(&priv->lock);
+	raw_spin_unlock_irqrestore(&priv->lock, irqflags);
 
 	return 0;
 }
@@ -430,14 +448,15 @@ static int quad8_set_synchronous_mode(struct iio_dev *indio_dev,
 	struct quad8_iio *const priv = iio_priv(indio_dev);
 	const int base_offset = priv->base + 2 * chan->channel + 1;
 	unsigned int idr_cfg = synchronous_mode;
+	unsigned long irqflags;
 
-	mutex_lock(&priv->lock);
+	raw_spin_lock_irqsave(&priv->lock, irqflags);
 
 	idr_cfg |= priv->index_polarity[chan->channel] << 1;
 
 	/* Index function must be non-synchronous in non-quadrature mode */
 	if (synchronous_mode && !priv->quadrature_mode[chan->channel]) {
-		mutex_unlock(&priv->lock);
+		raw_spin_unlock_irqrestore(&priv->lock, irqflags);
 		return -EINVAL;
 	}
 
@@ -446,7 +465,7 @@ static int quad8_set_synchronous_mode(struct iio_dev *indio_dev,
 	/* Load Index Control configuration to Index Control Register */
 	outb(QUAD8_CTR_IDR | idr_cfg, base_offset);
 
-	mutex_unlock(&priv->lock);
+	raw_spin_unlock_irqrestore(&priv->lock, irqflags);
 
 	return 0;
 }
@@ -476,9 +495,10 @@ static int quad8_set_quadrature_mode(struct iio_dev *indio_dev,
 {
 	struct quad8_iio *const priv = iio_priv(indio_dev);
 	const int base_offset = priv->base + 2 * chan->channel + 1;
+	unsigned long irqflags;
 	unsigned int mode_cfg;
 
-	mutex_lock(&priv->lock);
+	raw_spin_lock_irqsave(&priv->lock, irqflags);
 
 	mode_cfg = priv->count_mode[chan->channel] << 1;
 
@@ -498,7 +518,7 @@ static int quad8_set_quadrature_mode(struct iio_dev *indio_dev,
 	/* Load mode configuration to Counter Mode Register */
 	outb(QUAD8_CTR_CMR | mode_cfg, base_offset);
 
-	mutex_unlock(&priv->lock);
+	raw_spin_unlock_irqrestore(&priv->lock, irqflags);
 
 	return 0;
 }
@@ -529,8 +549,9 @@ static int quad8_set_index_polarity(struct iio_dev *indio_dev,
 	struct quad8_iio *const priv = iio_priv(indio_dev);
 	const int base_offset = priv->base + 2 * chan->channel + 1;
 	unsigned int idr_cfg = index_polarity << 1;
+	unsigned long irqflags;
 
-	mutex_lock(&priv->lock);
+	raw_spin_lock_irqsave(&priv->lock, irqflags);
 
 	idr_cfg |= priv->synchronous_mode[chan->channel];
 
@@ -539,7 +560,7 @@ static int quad8_set_index_polarity(struct iio_dev *indio_dev,
 	/* Load Index Control configuration to Index Control Register */
 	outb(QUAD8_CTR_IDR | idr_cfg, base_offset);
 
-	mutex_unlock(&priv->lock);
+	raw_spin_unlock_irqrestore(&priv->lock, irqflags);
 
 	return 0;
 }
@@ -646,6 +667,7 @@ static int quad8_count_read(struct counter_device *counter,
 	unsigned int flags;
 	unsigned int borrow;
 	unsigned int carry;
+	unsigned long irqflags;
 	int i;
 
 	flags = inb(base_offset + 1);
@@ -655,7 +677,7 @@ static int quad8_count_read(struct counter_device *counter,
 	/* Borrow XOR Carry effectively doubles count range */
 	*val = (unsigned long)(borrow ^ carry) << 24;
 
-	mutex_lock(&priv->lock);
+	raw_spin_lock_irqsave(&priv->lock, irqflags);
 
 	/* Reset Byte Pointer; transfer Counter to Output Latch */
 	outb(QUAD8_CTR_RLD | QUAD8_RLD_RESET_BP | QUAD8_RLD_CNTR_OUT,
@@ -664,7 +686,7 @@ static int quad8_count_read(struct counter_device *counter,
 	for (i = 0; i < 3; i++)
 		*val |= (unsigned long)inb(base_offset) << (8 * i);
 
-	mutex_unlock(&priv->lock);
+	raw_spin_unlock_irqrestore(&priv->lock, irqflags);
 
 	return 0;
 }
@@ -674,13 +696,14 @@ static int quad8_count_write(struct counter_device *counter,
 {
 	struct quad8_iio *const priv = counter->priv;
 	const int base_offset = priv->base + 2 * count->id;
+	unsigned long irqflags;
 	int i;
 
 	/* Only 24-bit values are supported */
 	if (val > 0xFFFFFF)
 		return -EINVAL;
 
-	mutex_lock(&priv->lock);
+	raw_spin_lock_irqsave(&priv->lock, irqflags);
 
 	/* Reset Byte Pointer */
 	outb(QUAD8_CTR_RLD | QUAD8_RLD_RESET_BP, base_offset + 1);
@@ -705,7 +728,7 @@ static int quad8_count_write(struct counter_device *counter,
 	/* Reset Error flag */
 	outb(QUAD8_CTR_RLD | QUAD8_RLD_RESET_E, base_offset + 1);
 
-	mutex_unlock(&priv->lock);
+	raw_spin_unlock_irqrestore(&priv->lock, irqflags);
 
 	return 0;
 }
@@ -723,8 +746,9 @@ static int quad8_function_read(struct counter_device *counter,
 {
 	struct quad8_iio *const priv = counter->priv;
 	const int id = count->id;
+	unsigned long irqflags;
 
-	mutex_lock(&priv->lock);
+	raw_spin_lock_irqsave(&priv->lock, irqflags);
 
 	if (priv->quadrature_mode[id])
 		switch (priv->quadrature_scale[id]) {
@@ -741,7 +765,7 @@ static int quad8_function_read(struct counter_device *counter,
 	else
 		*function = COUNTER_COUNT_FUNCTION_PULSE_DIRECTION;
 
-	mutex_unlock(&priv->lock);
+	raw_spin_unlock_irqrestore(&priv->lock, irqflags);
 
 	return 0;
 }
@@ -755,10 +779,11 @@ static int quad8_function_write(struct counter_device *counter,
 	unsigned int *const scale = priv->quadrature_scale + id;
 	unsigned int *const synchronous_mode = priv->synchronous_mode + id;
 	const int base_offset = priv->base + 2 * id + 1;
+	unsigned long irqflags;
 	unsigned int mode_cfg;
 	unsigned int idr_cfg;
 
-	mutex_lock(&priv->lock);
+	raw_spin_lock_irqsave(&priv->lock, irqflags);
 
 	mode_cfg = priv->count_mode[id] << 1;
 	idr_cfg = priv->index_polarity[id] << 1;
@@ -797,7 +822,7 @@ static int quad8_function_write(struct counter_device *counter,
 	/* Load mode configuration to Counter Mode Register */
 	outb(QUAD8_CTR_CMR | mode_cfg, base_offset);
 
-	mutex_unlock(&priv->lock);
+	raw_spin_unlock_irqrestore(&priv->lock, irqflags);
 
 	return 0;
 }
@@ -906,9 +931,10 @@ static int quad8_index_polarity_set(struct counter_device *counter,
 	struct quad8_iio *const priv = counter->priv;
 	const size_t channel_id = signal->id - 16;
 	const int base_offset = priv->base + 2 * channel_id + 1;
+	unsigned long irqflags;
 	unsigned int idr_cfg;
 
-	mutex_lock(&priv->lock);
+	raw_spin_lock_irqsave(&priv->lock, irqflags);
 
 	priv->index_polarity[channel_id] = index_polarity;
 
@@ -916,7 +942,7 @@ static int quad8_index_polarity_set(struct counter_device *counter,
 	idr_cfg = priv->synchronous_mode[channel_id] | index_polarity << 1;
 	outb(QUAD8_CTR_IDR | idr_cfg, base_offset);
 
-	mutex_unlock(&priv->lock);
+	raw_spin_unlock_irqrestore(&priv->lock, irqflags);
 
 	return 0;
 }
@@ -940,13 +966,14 @@ static int quad8_synchronous_mode_set(struct counter_device *counter,
 	struct quad8_iio *const priv = counter->priv;
 	const size_t channel_id = signal->id - 16;
 	const int base_offset = priv->base + 2 * channel_id + 1;
+	unsigned long irqflags;
 	unsigned int idr_cfg;
 
-	mutex_lock(&priv->lock);
+	raw_spin_lock_irqsave(&priv->lock, irqflags);
 
 	/* Index function must be non-synchronous in non-quadrature mode */
 	if (synchronous_mode && !priv->quadrature_mode[channel_id]) {
-		mutex_unlock(&priv->lock);
+		raw_spin_unlock_irqrestore(&priv->lock, irqflags);
 		return -EINVAL;
 	}
 
@@ -956,7 +983,7 @@ static int quad8_synchronous_mode_set(struct counter_device *counter,
 	idr_cfg = synchronous_mode | priv->index_polarity[channel_id] << 1;
 	outb(QUAD8_CTR_IDR | idr_cfg, base_offset);
 
-	mutex_unlock(&priv->lock);
+	raw_spin_unlock_irqrestore(&priv->lock, irqflags);
 
 	return 0;
 }
@@ -1001,6 +1028,7 @@ static int quad8_count_mode_write(struct counter_device *counter,
 	unsigned int count_mode;
 	unsigned int mode_cfg;
 	const int base_offset = priv->base + 2 * count->id + 1;
+	unsigned long irqflags;
 
 	/* Map Generic Counter count mode to 104-QUAD-8 count mode */
 	switch (cnt_mode) {
@@ -1018,7 +1046,7 @@ static int quad8_count_mode_write(struct counter_device *counter,
 		break;
 	}
 
-	mutex_lock(&priv->lock);
+	raw_spin_lock_irqsave(&priv->lock, irqflags);
 
 	priv->count_mode[count->id] = count_mode;
 
@@ -1032,7 +1060,7 @@ static int quad8_count_mode_write(struct counter_device *counter,
 	/* Load mode configuration to Counter Mode Register */
 	outb(QUAD8_CTR_CMR | mode_cfg, base_offset);
 
-	mutex_unlock(&priv->lock);
+	raw_spin_unlock_irqrestore(&priv->lock, irqflags);
 
 	return 0;
 }
@@ -1052,18 +1080,20 @@ static int quad8_count_enable_write(struct counter_device *counter,
 {
 	struct quad8_iio *const priv = counter->priv;
 	const int base_offset = priv->base + 2 * count->id;
+	unsigned long irqflags;
 	unsigned int ior_cfg;
 
-	mutex_lock(&priv->lock);
+	raw_spin_lock_irqsave(&priv->lock, irqflags);
 
 	priv->ab_enable[count->id] = enable;
 
-	ior_cfg = enable | priv->preset_enable[count->id] << 1;
+	ior_cfg = enable | priv->preset_enable[count->id] << 1 |
+		  priv->irq_trigger[count->id] << 3;
 
 	/* Load I/O control configuration */
 	outb(QUAD8_CTR_IOR | ior_cfg, base_offset + 1);
 
-	mutex_unlock(&priv->lock);
+	raw_spin_unlock_irqrestore(&priv->lock, irqflags);
 
 	return 0;
 }
@@ -1109,16 +1139,17 @@ static int quad8_count_preset_write(struct counter_device *counter,
 				    struct counter_count *count, u64 preset)
 {
 	struct quad8_iio *const priv = counter->priv;
+	unsigned long irqflags;
 
 	/* Only 24-bit values are supported */
 	if (preset > 0xFFFFFF)
 		return -EINVAL;
 
-	mutex_lock(&priv->lock);
+	raw_spin_lock_irqsave(&priv->lock, irqflags);
 
 	quad8_preset_register_set(priv, count->id, preset);
 
-	mutex_unlock(&priv->lock);
+	raw_spin_unlock_irqrestore(&priv->lock, irqflags);
 
 	return 0;
 }
@@ -1127,18 +1158,19 @@ static int quad8_count_ceiling_read(struct counter_device *counter,
 				    struct counter_count *count, u64 *ceiling)
 {
 	struct quad8_iio *const priv = counter->priv;
+	unsigned long irqflags;
 
-	mutex_lock(&priv->lock);
+	raw_spin_lock_irqsave(&priv->lock, irqflags);
 
 	/* Range Limit and Modulo-N count modes use preset value as ceiling */
 	switch (priv->count_mode[count->id]) {
 	case 1:
 	case 3:
-		mutex_unlock(&priv->lock);
+		raw_spin_unlock_irqrestore(&priv->lock, irqflags);
 		return quad8_count_preset_read(counter, count, ceiling);
 	}
 
-	mutex_unlock(&priv->lock);
+	raw_spin_unlock_irqrestore(&priv->lock, irqflags);
 
 	/* By default 0x1FFFFFF (25 bits unsigned) is maximum count */
 	*ceiling = 0x1FFFFFF;
@@ -1150,12 +1182,13 @@ static int quad8_count_ceiling_write(struct counter_device *counter,
 				     struct counter_count *count, u64 ceiling)
 {
 	struct quad8_iio *const priv = counter->priv;
+	unsigned long irqflags;
 
 	/* Only 24-bit values are supported */
 	if (ceiling > 0xFFFFFF)
 		return -EINVAL;
 
-	mutex_lock(&priv->lock);
+	raw_spin_lock_irqsave(&priv->lock, irqflags);
 
 	/* Range Limit and Modulo-N count modes use preset value as ceiling */
 	switch (priv->count_mode[count->id]) {
@@ -1164,7 +1197,7 @@ static int quad8_count_ceiling_write(struct counter_device *counter,
 		return quad8_count_preset_write(counter, count, ceiling);
 	}
 
-	mutex_unlock(&priv->lock);
+	raw_spin_unlock_irqrestore(&priv->lock, irqflags);
 
 	return -EINVAL;
 }
@@ -1186,21 +1219,91 @@ static int quad8_count_preset_enable_write(struct counter_device *counter,
 {
 	struct quad8_iio *const priv = counter->priv;
 	const int base_offset = priv->base + 2 * count->id + 1;
+	unsigned long irqflags;
 	unsigned int ior_cfg;
 
 	/* Preset enable is active low in Input/Output Control register */
 	preset_enable = !preset_enable;
 
-	mutex_lock(&priv->lock);
+	raw_spin_lock_irqsave(&priv->lock, irqflags);
 
 	priv->preset_enable[count->id] = preset_enable;
 
-	ior_cfg = priv->ab_enable[count->id] | preset_enable << 1;
+	ior_cfg = priv->ab_enable[count->id] | preset_enable << 1 |
+		  priv->irq_trigger[count->id] << 3;
+
+	/* Load I/O control configuration to Input / Output Control Register */
+	outb(QUAD8_CTR_IOR | ior_cfg, base_offset);
+
+	raw_spin_unlock_irqrestore(&priv->lock, irqflags);
+
+	return 0;
+}
+
+static int quad8_irq_trigger_get(struct counter_device *counter,
+				 struct counter_count *count, u8 *irq_trigger)
+{
+	const struct quad8_iio *const priv = counter->priv;
+
+	*irq_trigger = priv->irq_trigger[count->id];
+
+	return 0;
+}
+
+static int quad8_irq_trigger_set(struct counter_device *counter,
+				 struct counter_count *count, u8 irq_trigger)
+{
+	struct quad8_iio *const priv = counter->priv;
+	const unsigned long base_offset = priv->base + 2 * count->id + 1;
+	unsigned long irqflags;
+	unsigned long ior_cfg;
+
+	raw_spin_lock_irqsave(&priv->lock, irqflags);
+
+	priv->irq_trigger[count->id] = irq_trigger;
+
+	ior_cfg = priv->ab_enable[count->id] |
+		  priv->preset_enable[count->id] << 1 | irq_trigger << 3;
 
 	/* Load I/O control configuration to Input / Output Control Register */
 	outb(QUAD8_CTR_IOR | ior_cfg, base_offset);
 
-	mutex_unlock(&priv->lock);
+	raw_spin_unlock_irqrestore(&priv->lock, irqflags);
+
+	return 0;
+}
+
+static int quad8_irq_trigger_enable_read(struct counter_device *counter,
+					 struct counter_count *count, u8 *state)
+{
+	const struct quad8_iio *const priv = counter->priv;
+	unsigned long irq_enabled;
+
+	irq_enabled = inb(priv->base + QUAD8_REG_INDEX_INTERRUPT);
+	*state = !!(irq_enabled & BIT(count->id));
+
+	return 0;
+}
+
+static int quad8_irq_trigger_enable_write(struct counter_device *counter,
+				    struct counter_count *count, u8 state)
+{
+	struct quad8_iio *const priv = counter->priv;
+	unsigned long irqflags;
+	unsigned long irq_enabled;
+
+	raw_spin_lock_irqsave(&priv->lock, irqflags);
+
+	irq_enabled = inb(priv->base + QUAD8_REG_INDEX_INTERRUPT);
+
+	if (state)
+		irq_enabled |= BIT(count->id);
+	else
+		irq_enabled &= ~BIT(count->id);
+
+	outb(irq_enabled, priv->base + QUAD8_REG_INDEX_INTERRUPT);
+
+	raw_spin_unlock_irqrestore(&priv->lock, irqflags);
 
 	return 0;
 }
@@ -1211,22 +1314,23 @@ static int quad8_signal_cable_fault_read(struct counter_device *counter,
 {
 	struct quad8_iio *const priv = counter->priv;
 	const size_t channel_id = signal->id / 2;
+	unsigned long irqflags;
 	bool disabled;
 	unsigned int status;
 
-	mutex_lock(&priv->lock);
+	raw_spin_lock_irqsave(&priv->lock, irqflags);
 
 	disabled = !(priv->cable_fault_enable & BIT(channel_id));
 
 	if (disabled) {
-		mutex_unlock(&priv->lock);
+		raw_spin_unlock_irqrestore(&priv->lock, irqflags);
 		return -EINVAL;
 	}
 
 	/* Logic 0 = cable fault */
 	status = inb(priv->base + QUAD8_DIFF_ENCODER_CABLE_STATUS);
 
-	mutex_unlock(&priv->lock);
+	raw_spin_unlock_irqrestore(&priv->lock, irqflags);
 
 	/* Mask respective channel and invert logic */
 	*cable_fault = !(status & BIT(channel_id));
@@ -1252,9 +1356,10 @@ static int quad8_signal_cable_fault_enable_write(struct counter_device *counter,
 {
 	struct quad8_iio *const priv = counter->priv;
 	const size_t channel_id = signal->id / 2;
+	unsigned long irqflags;
 	unsigned int cable_fault_enable;
 
-	mutex_lock(&priv->lock);
+	raw_spin_lock_irqsave(&priv->lock, irqflags);
 
 	if (enable)
 		priv->cable_fault_enable |= BIT(channel_id);
@@ -1266,7 +1371,7 @@ static int quad8_signal_cable_fault_enable_write(struct counter_device *counter,
 
 	outb(cable_fault_enable, priv->base + QUAD8_DIFF_ENCODER_CABLE_STATUS);
 
-	mutex_unlock(&priv->lock);
+	raw_spin_unlock_irqrestore(&priv->lock, irqflags);
 
 	return 0;
 }
@@ -1289,8 +1394,9 @@ static int quad8_signal_fck_prescaler_write(struct counter_device *counter,
 	struct quad8_iio *const priv = counter->priv;
 	const size_t channel_id = signal->id / 2;
 	const int base_offset = priv->base + 2 * channel_id;
+	unsigned long irqflags;
 
-	mutex_lock(&priv->lock);
+	raw_spin_lock_irqsave(&priv->lock, irqflags);
 
 	priv->fck_prescaler[channel_id] = prescaler;
 
@@ -1302,7 +1408,7 @@ static int quad8_signal_fck_prescaler_write(struct counter_device *counter,
 	outb(QUAD8_CTR_RLD | QUAD8_RLD_RESET_BP | QUAD8_RLD_PRESET_PSC,
 	     base_offset + 1);
 
-	mutex_unlock(&priv->lock);
+	raw_spin_unlock_irqrestore(&priv->lock, irqflags);
 
 	return 0;
 }
@@ -1405,7 +1511,15 @@ static const u8 quad8_cnt_modes[] = {
 
 static DEFINE_COUNTER_AVAILABLE(quad8_count_mode_available, quad8_cnt_modes);
 
+static const char *const quad8_irq_trigger_states[] = {
+	"carry",
+	"compare",
+	"carry-borrow",
+	"index",
+};
+
 static DEFINE_COUNTER_ENUM(quad8_error_noise_enum, quad8_noise_error_states);
+static DEFINE_COUNTER_ENUM(quad8_irq_trigger_enum, quad8_irq_trigger_states);
 
 static struct counter_data quad8_count_ext[] = {
 	COUNTER_DATA_CEILING(quad8_count_ceiling_read,
@@ -1420,6 +1534,11 @@ static struct counter_data quad8_count_ext[] = {
 	COUNTER_DATA_PRESET(quad8_count_preset_read, quad8_count_preset_write),
 	COUNTER_DATA_PRESET_ENABLE(quad8_count_preset_enable_read,
 				   quad8_count_preset_enable_write),
+	COUNTER_DATA_COUNT_ENUM("irq_trigger", quad8_irq_trigger_get,
+				quad8_irq_trigger_set, quad8_irq_trigger_enum),
+	COUNTER_DATA_COUNT_BOOL("irq_trigger_enable",
+				quad8_irq_trigger_enable_read,
+				quad8_irq_trigger_enable_write),
 };
 
 #define QUAD8_COUNT(_id, _cntname) {					\
@@ -1444,6 +1563,26 @@ static struct counter_count quad8_counts[] = {
 	QUAD8_COUNT(7, "Channel 8 Count")
 };
 
+static irqreturn_t quad8_irq_handler(int irq, void *quad8iio)
+{
+	struct quad8_iio *const priv = quad8iio;
+	const unsigned long base = priv->base;
+	unsigned long irq_status;
+	unsigned long channel;
+
+	irq_status = inb(base + QUAD8_REG_INTERRUPT_STATUS);
+	if (!irq_status)
+		return IRQ_NONE;
+
+	for_each_set_bit(channel, &irq_status, QUAD8_NUM_COUNTERS)
+		counter_push_event(&priv->counter, channel);
+
+	/* Clear pending interrupts on device */
+	outb(QUAD8_CHAN_OP_ENABLE_INTERRUPT_FUNC, base + QUAD8_REG_CHAN_OP);
+
+	return IRQ_HANDLED;
+}
+
 static int quad8_probe(struct device *dev, unsigned int id)
 {
 	struct iio_dev *indio_dev;
@@ -1487,9 +1626,10 @@ static int quad8_probe(struct device *dev, unsigned int id)
 	quad8iio->counter.priv = quad8iio;
 	quad8iio->base = base[id];
 
-	/* Initialize mutex */
-	mutex_init(&quad8iio->lock);
+	raw_spin_lock_init(&quad8iio->lock);
 
+	/* Reset Index/Interrupt Register */
+	outb(0x00, base[id] + QUAD8_REG_INDEX_INTERRUPT);
 	/* Reset all counters and disable interrupt function */
 	outb(QUAD8_CHAN_OP_RESET_COUNTERS, base[id] + QUAD8_REG_CHAN_OP);
 	/* Set initial configuration for all counters */
@@ -1519,8 +1659,8 @@ static int quad8_probe(struct device *dev, unsigned int id)
 	}
 	/* Disable Differential Encoder Cable Status for all channels */
 	outb(0xFF, base[id] + QUAD8_DIFF_ENCODER_CABLE_STATUS);
-	/* Enable all counters */
-	outb(QUAD8_CHAN_OP_ENABLE_COUNTERS, base[id] + QUAD8_REG_CHAN_OP);
+	/* Enable all counters and enable interrupt function */
+	outb(QUAD8_CHAN_OP_ENABLE_INTERRUPT_FUNC, base[id] + QUAD8_REG_CHAN_OP);
 
 	/* Register IIO device */
 	err = devm_iio_device_register(dev, indio_dev);
@@ -1528,7 +1668,12 @@ static int quad8_probe(struct device *dev, unsigned int id)
 		return err;
 
 	/* Register Counter device */
-	return devm_counter_register(dev, &quad8iio->counter);
+	err = devm_counter_register(dev, &quad8iio->counter);
+	if (err)
+		return err;
+
+	return devm_request_irq(dev, irq[id], quad8_irq_handler, IRQF_SHARED,
+				quad8iio->counter.name, quad8iio);
 }
 
 static struct isa_driver quad8_driver = {
diff --git a/drivers/counter/Kconfig b/drivers/counter/Kconfig
index 2de53ab0dd25..bd42df98f522 100644
--- a/drivers/counter/Kconfig
+++ b/drivers/counter/Kconfig
@@ -23,11 +23,11 @@ config 104_QUAD_8
 	  A counter's respective error flag may be cleared by performing a write
 	  operation on the respective count value attribute. Although the
 	  104-QUAD-8 counters have a 25-bit range, only the lower 24 bits may be
-	  set, either directly or via the counter's preset attribute. Interrupts
-	  are not supported by this driver.
+	  set, either directly or via the counter's preset attribute.
 
 	  The base port addresses for the devices may be configured via the base
-	  array module parameter.
+	  array module parameter. The interrupt line numbers for the devices may
+	  be configured via the irq array module parameter.
 
 config STM32_TIMER_CNT
 	tristate "STM32 Timer encoder counter driver"
-- 
2.27.0


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

* Re: [PATCH v4 3/5] counter: Add character device interface
  2020-07-21 19:35 ` [PATCH v4 3/5] counter: Add character device interface William Breathitt Gray
@ 2020-07-29  0:20   ` David Lechner
  2020-07-30 22:49     ` David Lechner
  2020-08-09 14:51     ` William Breathitt Gray
  0 siblings, 2 replies; 18+ messages in thread
From: David Lechner @ 2020-07-29  0:20 UTC (permalink / raw)
  To: William Breathitt Gray, jic23
  Cc: kamel.bouhara, gwendal, alexandre.belloni, linux-iio,
	linux-kernel, linux-stm32, linux-arm-kernel, syednwaris,
	patrick.havelange, fabrice.gasnier, mcoquelin.stm32,
	alexandre.torgue

On 7/21/20 2:35 PM, William Breathitt Gray wrote:
> This patch introduces a character device interface for the Counter
> subsystem. Device data is exposed through standard character device read
> operations. Device data is gathered when a Counter event is pushed by
> the respective Counter device driver. Configuration is handled via ioctl
> operations on the respective Counter character device node.

This sounds similar to triggers and buffers in the iio subsystem. And
I can see how it might be useful in some cases. But I think it would not
give the desired results when performance is important.

Thinking through a few cases here...

Suppose there was a new counter device that used the I2C bus. This would
either have to be periodically polled for events or it might have a
separate GPIO line to notify the MCU. In any case, with the proposed
implementation, there would be a separate I2C transaction for each data
point for that event. So none of the data for that event would actually
be from the same point in time. And with I2C, this time difference could
be significant.

With the TI eQEP I have been working with, there are special latched
registers for some events. To make use of these with events, we would have
add extensions for each one we want to use (and expose it in sysfs). But
really, the fact that we are using a latched register should be an
implementation detail in the driver and not something userspace should have
to know about.

So, I'm wondering if it would make sense to keep things simpler and have
events like the input subsystem where the event value is directly tied
to the event. It would probably be rare for an event to have more than
one or two values. And error events probably would not have a value at
all.

For example, with the TI eQEP, there is a unit timer time out event.
This latches the position count, the timer count and the timer period.
To translate this to an event data structure, the latched time would
be the event timestamp and the position count would be the event value.
The timer period should already be known since we would have configured
the timer ourselves. There is also a count event that works similarly.
In this case, the latched time would be the event timestamp and the
latched timer period would be the event value. We would know the count
already since we get an event for each count (and a separate direction
change event if the direction changes).


> 
> A high-level view of how a count value is passed down from a counter
> driver is exemplified by the following:
> 
>                   ----------------------
>                  / Counter device       \
>                  +----------------------+
>                  | Count register: 0x28 |
>                  +----------------------+
>                          |
>                   -----------------
>                  / raw count data /
>                  -----------------
>                          |
>                          V
>                  +----------------------------+
>                  | Counter device driver      |----------+
>                  +----------------------------+          |
>                  | Processes data from device |   -------------------
>                  |----------------------------|  / driver callbacks /
>                  | Type: u64                  |  -------------------
>                  | Value: 42                  |          |
>                  +----------------------------+          |
>                          |                               |
>                   ----------                             |
>                  / u64     /                             |
>                  ----------                              |
>                          |                               |
>                          |                               V
>                          |               +----------------------+
>                          |               | Counter core         |
>                          |               +----------------------+
>                          |               | Routes device driver |
>                          |               | callbacks to the     |
>                          |               | userspace interfaces |
>                          |               +----------------------+
>                          |                       |
>                          |                -------------------
>                          |               / driver callbacks /
>                          |               -------------------
>                          |                       |
>                  +-------+---------------+       |
>                  |                       |       |
>                  |               +-------|-------+
>                  |               |       |
>                  V               |       V
>          +--------------------+  |  +---------------------+
>          | Counter sysfs      |<-+->| Counter chrdev      |
>          +--------------------+     +---------------------+
>          | Translates to the  |     | Translates to the   |
>          | standard Counter   |     | standard Counter    |
>          | sysfs output       |     | character device    |
>          |--------------------|     |---------------------+
>          | Type: const char * |     | Type: u64           |
>          | Value: "42"        |     | Value: 42           |
>          +--------------------+     +---------------------+
>                  |                               |
>           ---------------                 -----------------------
>          / const char * /                / struct counter_event /
>          ---------------                 -----------------------
>                  |                               |
>                  |                               V
>                  |                       +-----------+
>                  |                       | read      |
>                  |                       +-----------+
>                  |                       \ Count: 42 /
>                  |                        -----------
>                  |
>                  V
>          +--------------------------------------------------+
>          | `/sys/bus/counter/devices/counterX/countY/count` |
>          +--------------------------------------------------+
>          \ Count: "42"                                      /
>           --------------------------------------------------
> 
> Counter character device nodes are created under the `/dev` directory as
> `counterX`, where `X` is the respective counter device id. Defines for
> the standard Counter data types are exposed via the userspace
> `include/uapi/linux/counter.h` file.
> 
> Counter events
> --------------
> Counter device drivers can support Counter events by utilizing the
> `counter_push_event` function:
> 
>      int counter_push_event(struct counter_device *const counter,
>                             const u8 event);
> 
> The event id is specified by the `event` parameter. When this function
> is called, the Counter data associated with the respective event is
> gathered, and a `struct counter_event` is generated for each datum and
> pushed to userspace.
> 
> Counter events can be configured by users to report various Counter
> data of interest. This can be conceptualized as a list of Counter
> component read calls to perform. For example:
> 
>      +------------------------+------------------------+
>      | Event 0                | Event 1                |
>      +------------------------+------------------------+
>      | * Count 0              | * Signal 0             |
>      | * Count 1              | * Signal 0 Extension 0 |
>      | * Signal 3             | * Extension 4          |
>      | * Count 4 Extension 2  |                        |
>      | * Signal 5 Extension 0 |                        |
>      +------------------------+------------------------+

In the current implementation, I can't tell if the event number corresponds
to the individual counter or some device-specific interrupt bits. In either
case, it seems like it would be better to have a generic enum of possible
counter events like overflow, underflow, direction change, etc.

> 
> When `counter_push_event(counter, 1)` is called for example, it will go
> down the list for Event 1 and execute the read callbacks for Signal 0,
> Signal 0 Extension 0, and Extension 4 -- the data returned for each is
> pushed to a kfifo as a `struct counter_event`, which userspace can
> retrieve via a standard read operation on the respective character
> device node.
> 
> Userspace
> ---------
> Userspace applications can configure Counter events via ioctl operations
> on the Counter character device node. There following ioctl codes are
> supported and provided by the `linux/counter.h` userspace header file:
> 
> * COUNTER_CLEAR_WATCHES_IOCTL:
>    Clear all Counter watches from all events
> 
> * COUNTER_SET_WATCH_IOCTL:
>    Set a Counter watch on the specified event
> 
> To configure events to gather Counter data, users first populate a
> `struct counter_watch` with the relevant event id and the information
> for the desired Counter component from which to read, and then pass it
> via the `COUNTER_SET_WATCH_IOCTL` ioctl command.
> 
> Userspace applications can then execute a `read` operation (optionally
> calling `poll` first) on the Counter character device node to retrieve
> `struct counter_event` elements with the desired data.
> 
> For example, the following userspace code opens `/dev/counter0`,
> configures Event 0 to gather Count 0 and Count 1, and prints out the
> data as it becomes available on the character device node:
> 
>      #include <fcntl.h>
>      #include <linux/counter.h>
>      #include <poll.h>
>      #include <stdio.h>
>      #include <sys/ioctl.h>
>      #include <unistd.h>
> 
>      struct counter_watch watches[2] = {
>              {
>                      .event = 0,
>                      .component.owner_type = COUNTER_OWNER_TYPE_COUNT,
>                      .component.owner_id = 0,
>                      .component.type = COUNTER_COMPONENT_TYPE_COUNT,
>              },
>              {
>                      .event = 0,
>                      .component.owner_type = COUNTER_OWNER_TYPE_COUNT,
>                      .component.owner_id = 1,
>                      .component.type = COUNTER_COMPONENT_TYPE_COUNT,
>              },
>      };
> 
>      int main(void)
>      {
>              struct pollfd pfd = { .events = POLLIN };
>              struct counter_event event_data[2];
> 
>              pfd.fd = open("/dev/counter0", O_RDWR);
> 
>              ioctl(pfd.fd, COUNTER_SET_WATCH_IOCTL, watches);
>              ioctl(pfd.fd, COUNTER_SET_WATCH_IOCTL, watches + 1);

What enables events? If an event is enabled for each of these ioctls,
then we have a race condition where events events from the first watch
can start to be queued before the second watch is added. So we would
have to flush the chardev first before polling, otherwise the assumption
that event_data[0] is owner_id=0 and event_data[1] is owner_id=1 is
not true.

This is also racy if we want to clear watches and set up new watches
at runtime. There would be a period of time where there were no watches
and we could miss events.

With my suggested changes of having fixed values per event and generic
events, we could just have a single ioctl to enable and disable events.
This would probably need to take an array of event descriptors as an
argument where event descriptors contain the component type/id and the
event to enable.

> 
>              for (;;) {
>                      poll(&pfd, 1, -1);
> 
>                      read(pfd.fd, event_data, sizeof(event_data));
> 
>                      printf("Timestamp 0: %llu\nCount 0: %llu\n"
>                             "Timestamp 1: %llu\nCount 1: %llu\n",
>                             (unsigned long long)event_data[0].timestamp,
>                             (unsigned long long)event_data[0].value_u64,
>                             (unsigned long long)event_data[1].timestamp,
>                             (unsigned long long)event_data[1].value_u64);
>              }
> 
>              return 0;
>      }
> 
> Cc: David Lechner <david@lechnology.com>
> Cc: Gwendal Grignou <gwendal@chromium.org>
> Signed-off-by: William Breathitt Gray <vilhelm.gray@gmail.com>
> ---


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

* Re: [PATCH v4 5/5] counter: 104-quad-8: Add IRQ support for the ACCES 104-QUAD-8
  2020-07-21 19:35 ` [PATCH v4 5/5] counter: 104-quad-8: Add IRQ support for the ACCES 104-QUAD-8 William Breathitt Gray
@ 2020-07-30 17:07   ` Syed Nayyar Waris
  0 siblings, 0 replies; 18+ messages in thread
From: Syed Nayyar Waris @ 2020-07-30 17:07 UTC (permalink / raw)
  To: William Breathitt Gray
  Cc: Jonathan Cameron, kamel.bouhara, gwendal, alexandre.belloni,
	david, linux-iio, Linux Kernel Mailing List, linux-stm32,
	linux-arm Mailing List, patrick.havelange, fabrice.gasnier,
	mcoquelin.stm32, alexandre.torgue

On Wed, Jul 22, 2020 at 1:06 AM William Breathitt Gray
<vilhelm.gray@gmail.com> wrote:
>
> The LSI/CSI LS7266R1 chip provides programmable output via the FLG pins.
> When interrupts are enabled on the ACCES 104-QUAD-8, they occur whenever
> FLG1 is active. Four functions are available for the FLG1 signal: Carry,
> Compare, Carry-Borrow, and Index.
>
>         Carry:
>                 Interrupt generated on active low Carry signal. Carry
>                 signal toggles every time the respective channel's
>                 counter overflows.
>
>         Compare:
>                 Interrupt generated on active low Compare signal.
>                 Compare signal toggles every time respective channel's
>                 preset register is equal to the respective channel's
>                 counter.
>
>         Carry-Borrow:
>                 Interrupt generated on active low Carry signal and
>                 active low Borrow signal. Carry signal toggles every
>                 time the respective channel's counter overflows. Borrow
>                 signal toggles every time the respective channel's
>                 counter underflows.
>
>         Index:
>                 Interrupt generated on active high Index signal.
>
> The irq_trigger Count extension is introduced to allow the selection of
> the desired IRQ trigger function per channel. The irq_trigger_enable
> Count extension is introduced to allow the enablement of interrupts for
> a respective channel. Interrupts push Counter events as Event X, where
> 'X' is the respective channel whose FLG1 activated.
>
> This patch adds IRQ support for the ACCES 104-QUAD-8. The interrupt line
> numbers for the devices may be configured via the irq array module
> parameter.
>
> Cc: Syed Nayyar Waris <syednwaris@gmail.com>
> Signed-off-by: William Breathitt Gray <vilhelm.gray@gmail.com>
> ---
>  .../ABI/testing/sysfs-bus-counter-104-quad-8  |  32 ++
>  drivers/counter/104-quad-8.c                  | 283 +++++++++++++-----
>  drivers/counter/Kconfig                       |   6 +-
>  3 files changed, 249 insertions(+), 72 deletions(-)
>
> diff --git a/Documentation/ABI/testing/sysfs-bus-counter-104-quad-8 b/Documentation/ABI/testing/sysfs-bus-counter-104-quad-8
> index eac32180c40d..718f6199c71e 100644
> --- a/Documentation/ABI/testing/sysfs-bus-counter-104-quad-8
> +++ b/Documentation/ABI/testing/sysfs-bus-counter-104-quad-8
> @@ -1,3 +1,35 @@
> +What:          /sys/bus/counter/devices/counterX/countY/irq_trigger
> +KernelVersion: 5.9
> +Contact:       linux-iio@vger.kernel.org
> +Description:
> +               IRQ trigger function for channel Y. Four trigger functions are
> +               available: carry, compare, carry-borrow, and index.
> +
> +               carry:
> +                       Interrupt generated on active low Carry signal. Carry
> +                       signal toggles every time channel Y counter overflows.
> +
> +               compare:
> +                       Interrupt generated on active low Compare signal.
> +                       Compare signal toggles every time channel Y preset
> +                       register is equal to channel Y counter.
> +
> +               carry-borrow:
> +                       Interrupt generated on active low Carry signal and
> +                       active low Borrow signal. Carry signal toggles every
> +                       time channel Y counter overflows. Borrow signal toggles
> +                       every time channel Y counter underflows.
> +
> +               index:
> +                       Interrupt generated on active high Index signal.
> +
> +What:          /sys/bus/counter/devices/counterX/countY/irq_trigger_enable
> +KernelVersion: 5.9
> +Contact:       linux-iio@vger.kernel.org
> +Description:
> +               Whether generation of interrupts is enabled for channel Y. Valid
> +               attribute values are boolean.
> +
>  What:          /sys/bus/counter/devices/counterX/signalY/cable_fault
>  KernelVersion: 5.7
>  Contact:       linux-iio@vger.kernel.org
> diff --git a/drivers/counter/104-quad-8.c b/drivers/counter/104-quad-8.c
> index 0f20920073d6..b43be2d5464d 100644
> --- a/drivers/counter/104-quad-8.c
> +++ b/drivers/counter/104-quad-8.c
> @@ -13,23 +13,30 @@
>  #include <linux/iio/types.h>
>  #include <linux/io.h>
>  #include <linux/ioport.h>
> +#include <linux/interrupt.h>
>  #include <linux/isa.h>
>  #include <linux/kernel.h>
>  #include <linux/module.h>
>  #include <linux/moduleparam.h>
>  #include <linux/types.h>
> +#include <linux/spinlock.h>
>
>  #define QUAD8_EXTENT 32
>
>  static unsigned int base[max_num_isa_dev(QUAD8_EXTENT)];
>  static unsigned int num_quad8;
> -module_param_array(base, uint, &num_quad8, 0);
> +module_param_hw_array(base, uint, ioport, &num_quad8, 0);
>  MODULE_PARM_DESC(base, "ACCES 104-QUAD-8 base addresses");
>
> +static unsigned int irq[max_num_isa_dev(QUAD8_EXTENT)];
> +module_param_hw_array(irq, uint, irq, NULL, 0);
> +MODULE_PARM_DESC(irq, "ACCES 104-QUAD-8 interrupt line numbers");
> +
>  #define QUAD8_NUM_COUNTERS 8
>
>  /**
>   * struct quad8_iio - IIO device private data structure
> + * @lock:              synchronization lock to prevent I/O race conditions
>   * @counter:           instance of the counter_device
>   * @fck_prescaler:     array of filter clock prescaler configurations
>   * @preset:            array of preset values
> @@ -38,13 +45,14 @@ MODULE_PARM_DESC(base, "ACCES 104-QUAD-8 base addresses");
>   * @quadrature_scale:  array of quadrature mode scale configurations
>   * @ab_enable:         array of A and B inputs enable configurations
>   * @preset_enable:     array of set_to_preset_on_index attribute configurations
> + * @irq_trigger:       array of interrupt trigger function configurations
>   * @synchronous_mode:  array of index function synchronous mode configurations
>   * @index_polarity:    array of index function polarity configurations
>   * @cable_fault_enable:        differential encoder cable status enable configurations
>   * @base:              base port address of the IIO device
>   */
>  struct quad8_iio {
> -       struct mutex lock;
> +       raw_spinlock_t lock;
>         struct counter_device counter;
>         unsigned int fck_prescaler[QUAD8_NUM_COUNTERS];
>         unsigned int preset[QUAD8_NUM_COUNTERS];
> @@ -53,13 +61,16 @@ struct quad8_iio {
>         unsigned int quadrature_scale[QUAD8_NUM_COUNTERS];
>         unsigned int ab_enable[QUAD8_NUM_COUNTERS];
>         unsigned int preset_enable[QUAD8_NUM_COUNTERS];
> +       unsigned int irq_trigger[QUAD8_NUM_COUNTERS];
>         unsigned int synchronous_mode[QUAD8_NUM_COUNTERS];
>         unsigned int index_polarity[QUAD8_NUM_COUNTERS];
>         unsigned int cable_fault_enable;
>         unsigned int base;
>  };
>
> +#define QUAD8_REG_INTERRUPT_STATUS 0x10
>  #define QUAD8_REG_CHAN_OP 0x11
> +#define QUAD8_REG_INDEX_INTERRUPT 0x12
>  #define QUAD8_REG_INDEX_INPUT_LEVELS 0x16
>  #define QUAD8_DIFF_ENCODER_CABLE_STATUS 0x17
>  /* Borrow Toggle flip-flop */
> @@ -92,8 +103,8 @@ struct quad8_iio {
>  #define QUAD8_RLD_CNTR_OUT 0x10
>  /* Transfer Preset Register LSB to FCK Prescaler */
>  #define QUAD8_RLD_PRESET_PSC 0x18
> -#define QUAD8_CHAN_OP_ENABLE_COUNTERS 0x00
>  #define QUAD8_CHAN_OP_RESET_COUNTERS 0x01
> +#define QUAD8_CHAN_OP_ENABLE_INTERRUPT_FUNC 0x04
>  #define QUAD8_CMR_QUADRATURE_X1 0x08
>  #define QUAD8_CMR_QUADRATURE_X2 0x10
>  #define QUAD8_CMR_QUADRATURE_X4 0x18
> @@ -107,6 +118,7 @@ static int quad8_read_raw(struct iio_dev *indio_dev,
>         unsigned int flags;
>         unsigned int borrow;
>         unsigned int carry;
> +       unsigned long irqflags;
>         int i;
>
>         switch (mask) {
> @@ -124,7 +136,7 @@ static int quad8_read_raw(struct iio_dev *indio_dev,
>                 /* Borrow XOR Carry effectively doubles count range */
>                 *val = (borrow ^ carry) << 24;
>
> -               mutex_lock(&priv->lock);
> +               raw_spin_lock_irqsave(&priv->lock, irqflags);
>
>                 /* Reset Byte Pointer; transfer Counter to Output Latch */
>                 outb(QUAD8_CTR_RLD | QUAD8_RLD_RESET_BP | QUAD8_RLD_CNTR_OUT,
> @@ -133,7 +145,7 @@ static int quad8_read_raw(struct iio_dev *indio_dev,
>                 for (i = 0; i < 3; i++)
>                         *val |= (unsigned int)inb(base_offset) << (8 * i);
>
> -               mutex_unlock(&priv->lock);
> +               raw_spin_unlock_irqrestore(&priv->lock, irqflags);
>
>                 return IIO_VAL_INT;
>         case IIO_CHAN_INFO_ENABLE:
> @@ -153,6 +165,7 @@ static int quad8_write_raw(struct iio_dev *indio_dev,
>  {
>         struct quad8_iio *const priv = iio_priv(indio_dev);
>         const int base_offset = priv->base + 2 * chan->channel;
> +       unsigned long flags;
>         int i;
>         unsigned int ior_cfg;
>
> @@ -165,7 +178,7 @@ static int quad8_write_raw(struct iio_dev *indio_dev,
>                 if ((unsigned int)val > 0xFFFFFF)
>                         return -EINVAL;
>
> -               mutex_lock(&priv->lock);
> +               raw_spin_lock_irqsave(&priv->lock, flags);
>
>                 /* Reset Byte Pointer */
>                 outb(QUAD8_CTR_RLD | QUAD8_RLD_RESET_BP, base_offset + 1);
> @@ -190,7 +203,7 @@ static int quad8_write_raw(struct iio_dev *indio_dev,
>                 /* Reset Error flag */
>                 outb(QUAD8_CTR_RLD | QUAD8_RLD_RESET_E, base_offset + 1);
>
> -               mutex_unlock(&priv->lock);
> +               raw_spin_unlock_irqrestore(&priv->lock, flags);
>
>                 return 0;
>         case IIO_CHAN_INFO_ENABLE:
> @@ -198,25 +211,26 @@ static int quad8_write_raw(struct iio_dev *indio_dev,
>                 if (val < 0 || val > 1)
>                         return -EINVAL;
>
> -               mutex_lock(&priv->lock);
> +               raw_spin_lock_irqsave(&priv->lock, flags);
>
>                 priv->ab_enable[chan->channel] = val;
>
> -               ior_cfg = val | priv->preset_enable[chan->channel] << 1;
> +               ior_cfg = val | priv->preset_enable[chan->channel] << 1 |
> +                         priv->irq_trigger[chan->channel] << 3;
>
>                 /* Load I/O control configuration */
>                 outb(QUAD8_CTR_IOR | ior_cfg, base_offset + 1);
>
> -               mutex_unlock(&priv->lock);
> +               raw_spin_unlock_irqrestore(&priv->lock, flags);
>
>                 return 0;
>         case IIO_CHAN_INFO_SCALE:
> -               mutex_lock(&priv->lock);
> +               raw_spin_lock_irqsave(&priv->lock, flags);
>
>                 /* Quadrature scaling only available in quadrature mode */
>                 if (!priv->quadrature_mode[chan->channel] &&
>                                 (val2 || val != 1)) {
> -                       mutex_unlock(&priv->lock);
> +                       raw_spin_unlock_irqrestore(&priv->lock, flags);
>                         return -EINVAL;
>                 }
>
> @@ -232,15 +246,15 @@ static int quad8_write_raw(struct iio_dev *indio_dev,
>                                 priv->quadrature_scale[chan->channel] = 2;
>                                 break;
>                         default:
> -                               mutex_unlock(&priv->lock);
> +                               raw_spin_unlock_irqrestore(&priv->lock, flags);
>                                 return -EINVAL;
>                         }
>                 else {
> -                       mutex_unlock(&priv->lock);
> +                       raw_spin_unlock_irqrestore(&priv->lock, flags);
>                         return -EINVAL;
>                 }
>
> -               mutex_unlock(&priv->lock);
> +               raw_spin_unlock_irqrestore(&priv->lock, flags);
>                 return 0;
>         }
>
> @@ -266,6 +280,7 @@ static ssize_t quad8_write_preset(struct iio_dev *indio_dev, uintptr_t private,
>         struct quad8_iio *const priv = iio_priv(indio_dev);
>         const int base_offset = priv->base + 2 * chan->channel;
>         unsigned int preset;
> +       unsigned long irqflags;
>         int ret;
>         int i;
>
> @@ -277,7 +292,7 @@ static ssize_t quad8_write_preset(struct iio_dev *indio_dev, uintptr_t private,
>         if (preset > 0xFFFFFF)
>                 return -EINVAL;
>
> -       mutex_lock(&priv->lock);
> +       raw_spin_lock_irqsave(&priv->lock, irqflags);
>
>         priv->preset[chan->channel] = preset;
>
> @@ -288,7 +303,7 @@ static ssize_t quad8_write_preset(struct iio_dev *indio_dev, uintptr_t private,
>         for (i = 0; i < 3; i++)
>                 outb(preset >> (8 * i), base_offset);
>
> -       mutex_unlock(&priv->lock);
> +       raw_spin_unlock_irqrestore(&priv->lock, irqflags);
>
>         return len;
>  }
> @@ -309,6 +324,7 @@ static ssize_t quad8_write_set_to_preset_on_index(struct iio_dev *indio_dev,
>         struct quad8_iio *const priv = iio_priv(indio_dev);
>         const int base_offset = priv->base + 2 * chan->channel + 1;
>         bool preset_enable;
> +       unsigned long irqflags;
>         int ret;
>         unsigned int ior_cfg;
>
> @@ -319,17 +335,18 @@ static ssize_t quad8_write_set_to_preset_on_index(struct iio_dev *indio_dev,
>         /* Preset enable is active low in Input/Output Control register */
>         preset_enable = !preset_enable;
>
> -       mutex_lock(&priv->lock);
> +       raw_spin_lock_irqsave(&priv->lock, irqflags);
>
>         priv->preset_enable[chan->channel] = preset_enable;
>
>         ior_cfg = priv->ab_enable[chan->channel] |
> -               (unsigned int)preset_enable << 1;
> +                 (unsigned int)preset_enable << 1 |
> +                 priv->irq_trigger[chan->channel] << 3;
>
>         /* Load I/O control configuration to Input / Output Control Register */
>         outb(QUAD8_CTR_IOR | ior_cfg, base_offset);
>
> -       mutex_unlock(&priv->lock);
> +       raw_spin_unlock_irqrestore(&priv->lock, irqflags);
>
>         return len;
>  }
> @@ -387,8 +404,9 @@ static int quad8_set_count_mode(struct iio_dev *indio_dev,
>         struct quad8_iio *const priv = iio_priv(indio_dev);
>         unsigned int mode_cfg = cnt_mode << 1;
>         const int base_offset = priv->base + 2 * chan->channel + 1;
> +       unsigned long irqflags;
>
> -       mutex_lock(&priv->lock);
> +       raw_spin_lock_irqsave(&priv->lock, irqflags);
>
>         priv->count_mode[chan->channel] = cnt_mode;
>
> @@ -399,7 +417,7 @@ static int quad8_set_count_mode(struct iio_dev *indio_dev,
>         /* Load mode configuration to Counter Mode Register */
>         outb(QUAD8_CTR_CMR | mode_cfg, base_offset);
>
> -       mutex_unlock(&priv->lock);
> +       raw_spin_unlock_irqrestore(&priv->lock, irqflags);
>
>         return 0;
>  }
> @@ -430,14 +448,15 @@ static int quad8_set_synchronous_mode(struct iio_dev *indio_dev,
>         struct quad8_iio *const priv = iio_priv(indio_dev);
>         const int base_offset = priv->base + 2 * chan->channel + 1;
>         unsigned int idr_cfg = synchronous_mode;
> +       unsigned long irqflags;
>
> -       mutex_lock(&priv->lock);
> +       raw_spin_lock_irqsave(&priv->lock, irqflags);
>
>         idr_cfg |= priv->index_polarity[chan->channel] << 1;
>
>         /* Index function must be non-synchronous in non-quadrature mode */
>         if (synchronous_mode && !priv->quadrature_mode[chan->channel]) {
> -               mutex_unlock(&priv->lock);
> +               raw_spin_unlock_irqrestore(&priv->lock, irqflags);
>                 return -EINVAL;
>         }
>
> @@ -446,7 +465,7 @@ static int quad8_set_synchronous_mode(struct iio_dev *indio_dev,
>         /* Load Index Control configuration to Index Control Register */
>         outb(QUAD8_CTR_IDR | idr_cfg, base_offset);
>
> -       mutex_unlock(&priv->lock);
> +       raw_spin_unlock_irqrestore(&priv->lock, irqflags);
>
>         return 0;
>  }
> @@ -476,9 +495,10 @@ static int quad8_set_quadrature_mode(struct iio_dev *indio_dev,
>  {
>         struct quad8_iio *const priv = iio_priv(indio_dev);
>         const int base_offset = priv->base + 2 * chan->channel + 1;
> +       unsigned long irqflags;
>         unsigned int mode_cfg;
>
> -       mutex_lock(&priv->lock);
> +       raw_spin_lock_irqsave(&priv->lock, irqflags);
>
>         mode_cfg = priv->count_mode[chan->channel] << 1;
>
> @@ -498,7 +518,7 @@ static int quad8_set_quadrature_mode(struct iio_dev *indio_dev,
>         /* Load mode configuration to Counter Mode Register */
>         outb(QUAD8_CTR_CMR | mode_cfg, base_offset);
>
> -       mutex_unlock(&priv->lock);
> +       raw_spin_unlock_irqrestore(&priv->lock, irqflags);
>
>         return 0;
>  }
> @@ -529,8 +549,9 @@ static int quad8_set_index_polarity(struct iio_dev *indio_dev,
>         struct quad8_iio *const priv = iio_priv(indio_dev);
>         const int base_offset = priv->base + 2 * chan->channel + 1;
>         unsigned int idr_cfg = index_polarity << 1;
> +       unsigned long irqflags;
>
> -       mutex_lock(&priv->lock);
> +       raw_spin_lock_irqsave(&priv->lock, irqflags);
>
>         idr_cfg |= priv->synchronous_mode[chan->channel];
>
> @@ -539,7 +560,7 @@ static int quad8_set_index_polarity(struct iio_dev *indio_dev,
>         /* Load Index Control configuration to Index Control Register */
>         outb(QUAD8_CTR_IDR | idr_cfg, base_offset);
>
> -       mutex_unlock(&priv->lock);
> +       raw_spin_unlock_irqrestore(&priv->lock, irqflags);
>
>         return 0;
>  }
> @@ -646,6 +667,7 @@ static int quad8_count_read(struct counter_device *counter,
>         unsigned int flags;
>         unsigned int borrow;
>         unsigned int carry;
> +       unsigned long irqflags;
>         int i;
>
>         flags = inb(base_offset + 1);
> @@ -655,7 +677,7 @@ static int quad8_count_read(struct counter_device *counter,
>         /* Borrow XOR Carry effectively doubles count range */
>         *val = (unsigned long)(borrow ^ carry) << 24;
>
> -       mutex_lock(&priv->lock);
> +       raw_spin_lock_irqsave(&priv->lock, irqflags);
>
>         /* Reset Byte Pointer; transfer Counter to Output Latch */
>         outb(QUAD8_CTR_RLD | QUAD8_RLD_RESET_BP | QUAD8_RLD_CNTR_OUT,
> @@ -664,7 +686,7 @@ static int quad8_count_read(struct counter_device *counter,
>         for (i = 0; i < 3; i++)
>                 *val |= (unsigned long)inb(base_offset) << (8 * i);
>
> -       mutex_unlock(&priv->lock);
> +       raw_spin_unlock_irqrestore(&priv->lock, irqflags);
>
>         return 0;
>  }
> @@ -674,13 +696,14 @@ static int quad8_count_write(struct counter_device *counter,
>  {
>         struct quad8_iio *const priv = counter->priv;
>         const int base_offset = priv->base + 2 * count->id;
> +       unsigned long irqflags;
>         int i;
>
>         /* Only 24-bit values are supported */
>         if (val > 0xFFFFFF)
>                 return -EINVAL;
>
> -       mutex_lock(&priv->lock);
> +       raw_spin_lock_irqsave(&priv->lock, irqflags);
>
>         /* Reset Byte Pointer */
>         outb(QUAD8_CTR_RLD | QUAD8_RLD_RESET_BP, base_offset + 1);
> @@ -705,7 +728,7 @@ static int quad8_count_write(struct counter_device *counter,
>         /* Reset Error flag */
>         outb(QUAD8_CTR_RLD | QUAD8_RLD_RESET_E, base_offset + 1);
>
> -       mutex_unlock(&priv->lock);
> +       raw_spin_unlock_irqrestore(&priv->lock, irqflags);
>
>         return 0;
>  }
> @@ -723,8 +746,9 @@ static int quad8_function_read(struct counter_device *counter,
>  {
>         struct quad8_iio *const priv = counter->priv;
>         const int id = count->id;
> +       unsigned long irqflags;
>
> -       mutex_lock(&priv->lock);
> +       raw_spin_lock_irqsave(&priv->lock, irqflags);
>
>         if (priv->quadrature_mode[id])
>                 switch (priv->quadrature_scale[id]) {
> @@ -741,7 +765,7 @@ static int quad8_function_read(struct counter_device *counter,
>         else
>                 *function = COUNTER_COUNT_FUNCTION_PULSE_DIRECTION;
>
> -       mutex_unlock(&priv->lock);
> +       raw_spin_unlock_irqrestore(&priv->lock, irqflags);
>
>         return 0;
>  }
> @@ -755,10 +779,11 @@ static int quad8_function_write(struct counter_device *counter,
>         unsigned int *const scale = priv->quadrature_scale + id;
>         unsigned int *const synchronous_mode = priv->synchronous_mode + id;
>         const int base_offset = priv->base + 2 * id + 1;
> +       unsigned long irqflags;
>         unsigned int mode_cfg;
>         unsigned int idr_cfg;
>
> -       mutex_lock(&priv->lock);
> +       raw_spin_lock_irqsave(&priv->lock, irqflags);
>
>         mode_cfg = priv->count_mode[id] << 1;
>         idr_cfg = priv->index_polarity[id] << 1;
> @@ -797,7 +822,7 @@ static int quad8_function_write(struct counter_device *counter,
>         /* Load mode configuration to Counter Mode Register */
>         outb(QUAD8_CTR_CMR | mode_cfg, base_offset);
>
> -       mutex_unlock(&priv->lock);
> +       raw_spin_unlock_irqrestore(&priv->lock, irqflags);
>
>         return 0;
>  }
> @@ -906,9 +931,10 @@ static int quad8_index_polarity_set(struct counter_device *counter,
>         struct quad8_iio *const priv = counter->priv;
>         const size_t channel_id = signal->id - 16;
>         const int base_offset = priv->base + 2 * channel_id + 1;
> +       unsigned long irqflags;
>         unsigned int idr_cfg;
>
> -       mutex_lock(&priv->lock);
> +       raw_spin_lock_irqsave(&priv->lock, irqflags);
>
>         priv->index_polarity[channel_id] = index_polarity;
>
> @@ -916,7 +942,7 @@ static int quad8_index_polarity_set(struct counter_device *counter,
>         idr_cfg = priv->synchronous_mode[channel_id] | index_polarity << 1;
>         outb(QUAD8_CTR_IDR | idr_cfg, base_offset);
>
> -       mutex_unlock(&priv->lock);
> +       raw_spin_unlock_irqrestore(&priv->lock, irqflags);
>
>         return 0;
>  }
> @@ -940,13 +966,14 @@ static int quad8_synchronous_mode_set(struct counter_device *counter,
>         struct quad8_iio *const priv = counter->priv;
>         const size_t channel_id = signal->id - 16;
>         const int base_offset = priv->base + 2 * channel_id + 1;
> +       unsigned long irqflags;
>         unsigned int idr_cfg;
>
> -       mutex_lock(&priv->lock);
> +       raw_spin_lock_irqsave(&priv->lock, irqflags);
>
>         /* Index function must be non-synchronous in non-quadrature mode */
>         if (synchronous_mode && !priv->quadrature_mode[channel_id]) {
> -               mutex_unlock(&priv->lock);
> +               raw_spin_unlock_irqrestore(&priv->lock, irqflags);
>                 return -EINVAL;
>         }
>
> @@ -956,7 +983,7 @@ static int quad8_synchronous_mode_set(struct counter_device *counter,
>         idr_cfg = synchronous_mode | priv->index_polarity[channel_id] << 1;
>         outb(QUAD8_CTR_IDR | idr_cfg, base_offset);
>
> -       mutex_unlock(&priv->lock);
> +       raw_spin_unlock_irqrestore(&priv->lock, irqflags);
>
>         return 0;
>  }
> @@ -1001,6 +1028,7 @@ static int quad8_count_mode_write(struct counter_device *counter,
>         unsigned int count_mode;
>         unsigned int mode_cfg;
>         const int base_offset = priv->base + 2 * count->id + 1;
> +       unsigned long irqflags;
>
>         /* Map Generic Counter count mode to 104-QUAD-8 count mode */
>         switch (cnt_mode) {
> @@ -1018,7 +1046,7 @@ static int quad8_count_mode_write(struct counter_device *counter,
>                 break;
>         }
>
> -       mutex_lock(&priv->lock);
> +       raw_spin_lock_irqsave(&priv->lock, irqflags);
>
>         priv->count_mode[count->id] = count_mode;
>
> @@ -1032,7 +1060,7 @@ static int quad8_count_mode_write(struct counter_device *counter,
>         /* Load mode configuration to Counter Mode Register */
>         outb(QUAD8_CTR_CMR | mode_cfg, base_offset);
>
> -       mutex_unlock(&priv->lock);
> +       raw_spin_unlock_irqrestore(&priv->lock, irqflags);
>
>         return 0;
>  }
> @@ -1052,18 +1080,20 @@ static int quad8_count_enable_write(struct counter_device *counter,
>  {
>         struct quad8_iio *const priv = counter->priv;
>         const int base_offset = priv->base + 2 * count->id;
> +       unsigned long irqflags;
>         unsigned int ior_cfg;
>
> -       mutex_lock(&priv->lock);
> +       raw_spin_lock_irqsave(&priv->lock, irqflags);
>
>         priv->ab_enable[count->id] = enable;
>
> -       ior_cfg = enable | priv->preset_enable[count->id] << 1;
> +       ior_cfg = enable | priv->preset_enable[count->id] << 1 |
> +                 priv->irq_trigger[count->id] << 3;
>
>         /* Load I/O control configuration */
>         outb(QUAD8_CTR_IOR | ior_cfg, base_offset + 1);
>
> -       mutex_unlock(&priv->lock);
> +       raw_spin_unlock_irqrestore(&priv->lock, irqflags);
>
>         return 0;
>  }
> @@ -1109,16 +1139,17 @@ static int quad8_count_preset_write(struct counter_device *counter,
>                                     struct counter_count *count, u64 preset)
>  {
>         struct quad8_iio *const priv = counter->priv;
> +       unsigned long irqflags;
>
>         /* Only 24-bit values are supported */
>         if (preset > 0xFFFFFF)
>                 return -EINVAL;
>
> -       mutex_lock(&priv->lock);
> +       raw_spin_lock_irqsave(&priv->lock, irqflags);
>
>         quad8_preset_register_set(priv, count->id, preset);
>
> -       mutex_unlock(&priv->lock);
> +       raw_spin_unlock_irqrestore(&priv->lock, irqflags);
>
>         return 0;
>  }
> @@ -1127,18 +1158,19 @@ static int quad8_count_ceiling_read(struct counter_device *counter,
>                                     struct counter_count *count, u64 *ceiling)
>  {
>         struct quad8_iio *const priv = counter->priv;
> +       unsigned long irqflags;
>
> -       mutex_lock(&priv->lock);
> +       raw_spin_lock_irqsave(&priv->lock, irqflags);
>
>         /* Range Limit and Modulo-N count modes use preset value as ceiling */
>         switch (priv->count_mode[count->id]) {
>         case 1:
>         case 3:
> -               mutex_unlock(&priv->lock);
> +               raw_spin_unlock_irqrestore(&priv->lock, irqflags);
>                 return quad8_count_preset_read(counter, count, ceiling);
>         }
>
> -       mutex_unlock(&priv->lock);
> +       raw_spin_unlock_irqrestore(&priv->lock, irqflags);
>
>         /* By default 0x1FFFFFF (25 bits unsigned) is maximum count */
>         *ceiling = 0x1FFFFFF;
> @@ -1150,12 +1182,13 @@ static int quad8_count_ceiling_write(struct counter_device *counter,
>                                      struct counter_count *count, u64 ceiling)
>  {
>         struct quad8_iio *const priv = counter->priv;
> +       unsigned long irqflags;
>
>         /* Only 24-bit values are supported */
>         if (ceiling > 0xFFFFFF)
>                 return -EINVAL;
>
> -       mutex_lock(&priv->lock);
> +       raw_spin_lock_irqsave(&priv->lock, irqflags);
>
>         /* Range Limit and Modulo-N count modes use preset value as ceiling */
>         switch (priv->count_mode[count->id]) {
> @@ -1164,7 +1197,7 @@ static int quad8_count_ceiling_write(struct counter_device *counter,
>                 return quad8_count_preset_write(counter, count, ceiling);
>         }
>
> -       mutex_unlock(&priv->lock);
> +       raw_spin_unlock_irqrestore(&priv->lock, irqflags);
>
>         return -EINVAL;
>  }
> @@ -1186,21 +1219,91 @@ static int quad8_count_preset_enable_write(struct counter_device *counter,
>  {
>         struct quad8_iio *const priv = counter->priv;
>         const int base_offset = priv->base + 2 * count->id + 1;
> +       unsigned long irqflags;
>         unsigned int ior_cfg;
>
>         /* Preset enable is active low in Input/Output Control register */
>         preset_enable = !preset_enable;
>
> -       mutex_lock(&priv->lock);
> +       raw_spin_lock_irqsave(&priv->lock, irqflags);
>
>         priv->preset_enable[count->id] = preset_enable;
>
> -       ior_cfg = priv->ab_enable[count->id] | preset_enable << 1;
> +       ior_cfg = priv->ab_enable[count->id] | preset_enable << 1 |
> +                 priv->irq_trigger[count->id] << 3;
> +
> +       /* Load I/O control configuration to Input / Output Control Register */
> +       outb(QUAD8_CTR_IOR | ior_cfg, base_offset);
> +
> +       raw_spin_unlock_irqrestore(&priv->lock, irqflags);
> +
> +       return 0;
> +}
> +
> +static int quad8_irq_trigger_get(struct counter_device *counter,
> +                                struct counter_count *count, u8 *irq_trigger)
> +{
> +       const struct quad8_iio *const priv = counter->priv;
> +
> +       *irq_trigger = priv->irq_trigger[count->id];
> +
> +       return 0;
> +}
> +
> +static int quad8_irq_trigger_set(struct counter_device *counter,
> +                                struct counter_count *count, u8 irq_trigger)
> +{
> +       struct quad8_iio *const priv = counter->priv;
> +       const unsigned long base_offset = priv->base + 2 * count->id + 1;
> +       unsigned long irqflags;
> +       unsigned long ior_cfg;
> +
> +       raw_spin_lock_irqsave(&priv->lock, irqflags);
> +
> +       priv->irq_trigger[count->id] = irq_trigger;
> +
> +       ior_cfg = priv->ab_enable[count->id] |
> +                 priv->preset_enable[count->id] << 1 | irq_trigger << 3;
>
>         /* Load I/O control configuration to Input / Output Control Register */
>         outb(QUAD8_CTR_IOR | ior_cfg, base_offset);
>
> -       mutex_unlock(&priv->lock);
> +       raw_spin_unlock_irqrestore(&priv->lock, irqflags);
> +
> +       return 0;
> +}
> +
> +static int quad8_irq_trigger_enable_read(struct counter_device *counter,
> +                                        struct counter_count *count, u8 *state)
> +{
> +       const struct quad8_iio *const priv = counter->priv;
> +       unsigned long irq_enabled;
> +
> +       irq_enabled = inb(priv->base + QUAD8_REG_INDEX_INTERRUPT);
> +       *state = !!(irq_enabled & BIT(count->id));
> +
> +       return 0;
> +}
> +
> +static int quad8_irq_trigger_enable_write(struct counter_device *counter,
> +                                   struct counter_count *count, u8 state)
> +{
> +       struct quad8_iio *const priv = counter->priv;
> +       unsigned long irqflags;
> +       unsigned long irq_enabled;
> +
> +       raw_spin_lock_irqsave(&priv->lock, irqflags);
> +
> +       irq_enabled = inb(priv->base + QUAD8_REG_INDEX_INTERRUPT);
> +
> +       if (state)
> +               irq_enabled |= BIT(count->id);
> +       else
> +               irq_enabled &= ~BIT(count->id);
> +
> +       outb(irq_enabled, priv->base + QUAD8_REG_INDEX_INTERRUPT);
> +
> +       raw_spin_unlock_irqrestore(&priv->lock, irqflags);
>
>         return 0;
>  }
> @@ -1211,22 +1314,23 @@ static int quad8_signal_cable_fault_read(struct counter_device *counter,
>  {
>         struct quad8_iio *const priv = counter->priv;
>         const size_t channel_id = signal->id / 2;
> +       unsigned long irqflags;
>         bool disabled;
>         unsigned int status;
>
> -       mutex_lock(&priv->lock);
> +       raw_spin_lock_irqsave(&priv->lock, irqflags);
>
>         disabled = !(priv->cable_fault_enable & BIT(channel_id));
>
>         if (disabled) {
> -               mutex_unlock(&priv->lock);
> +               raw_spin_unlock_irqrestore(&priv->lock, irqflags);
>                 return -EINVAL;
>         }
>
>         /* Logic 0 = cable fault */
>         status = inb(priv->base + QUAD8_DIFF_ENCODER_CABLE_STATUS);
>
> -       mutex_unlock(&priv->lock);
> +       raw_spin_unlock_irqrestore(&priv->lock, irqflags);
>
>         /* Mask respective channel and invert logic */
>         *cable_fault = !(status & BIT(channel_id));
> @@ -1252,9 +1356,10 @@ static int quad8_signal_cable_fault_enable_write(struct counter_device *counter,
>  {
>         struct quad8_iio *const priv = counter->priv;
>         const size_t channel_id = signal->id / 2;
> +       unsigned long irqflags;
>         unsigned int cable_fault_enable;
>
> -       mutex_lock(&priv->lock);
> +       raw_spin_lock_irqsave(&priv->lock, irqflags);
>
>         if (enable)
>                 priv->cable_fault_enable |= BIT(channel_id);
> @@ -1266,7 +1371,7 @@ static int quad8_signal_cable_fault_enable_write(struct counter_device *counter,
>
>         outb(cable_fault_enable, priv->base + QUAD8_DIFF_ENCODER_CABLE_STATUS);
>
> -       mutex_unlock(&priv->lock);
> +       raw_spin_unlock_irqrestore(&priv->lock, irqflags);
>
>         return 0;
>  }
> @@ -1289,8 +1394,9 @@ static int quad8_signal_fck_prescaler_write(struct counter_device *counter,
>         struct quad8_iio *const priv = counter->priv;
>         const size_t channel_id = signal->id / 2;
>         const int base_offset = priv->base + 2 * channel_id;
> +       unsigned long irqflags;
>
> -       mutex_lock(&priv->lock);
> +       raw_spin_lock_irqsave(&priv->lock, irqflags);
>
>         priv->fck_prescaler[channel_id] = prescaler;
>
> @@ -1302,7 +1408,7 @@ static int quad8_signal_fck_prescaler_write(struct counter_device *counter,
>         outb(QUAD8_CTR_RLD | QUAD8_RLD_RESET_BP | QUAD8_RLD_PRESET_PSC,
>              base_offset + 1);
>
> -       mutex_unlock(&priv->lock);
> +       raw_spin_unlock_irqrestore(&priv->lock, irqflags);
>
>         return 0;
>  }
> @@ -1405,7 +1511,15 @@ static const u8 quad8_cnt_modes[] = {
>
>  static DEFINE_COUNTER_AVAILABLE(quad8_count_mode_available, quad8_cnt_modes);
>
> +static const char *const quad8_irq_trigger_states[] = {
> +       "carry",
> +       "compare",
> +       "carry-borrow",
> +       "index",
> +};
> +
>  static DEFINE_COUNTER_ENUM(quad8_error_noise_enum, quad8_noise_error_states);
> +static DEFINE_COUNTER_ENUM(quad8_irq_trigger_enum, quad8_irq_trigger_states);
>
>  static struct counter_data quad8_count_ext[] = {
>         COUNTER_DATA_CEILING(quad8_count_ceiling_read,
> @@ -1420,6 +1534,11 @@ static struct counter_data quad8_count_ext[] = {
>         COUNTER_DATA_PRESET(quad8_count_preset_read, quad8_count_preset_write),
>         COUNTER_DATA_PRESET_ENABLE(quad8_count_preset_enable_read,
>                                    quad8_count_preset_enable_write),
> +       COUNTER_DATA_COUNT_ENUM("irq_trigger", quad8_irq_trigger_get,
> +                               quad8_irq_trigger_set, quad8_irq_trigger_enum),
> +       COUNTER_DATA_COUNT_BOOL("irq_trigger_enable",
> +                               quad8_irq_trigger_enable_read,
> +                               quad8_irq_trigger_enable_write),
>  };
>
>  #define QUAD8_COUNT(_id, _cntname) {                                   \
> @@ -1444,6 +1563,26 @@ static struct counter_count quad8_counts[] = {
>         QUAD8_COUNT(7, "Channel 8 Count")
>  };
>
> +static irqreturn_t quad8_irq_handler(int irq, void *quad8iio)
> +{
> +       struct quad8_iio *const priv = quad8iio;
> +       const unsigned long base = priv->base;
> +       unsigned long irq_status;
> +       unsigned long channel;
> +
> +       irq_status = inb(base + QUAD8_REG_INTERRUPT_STATUS);
> +       if (!irq_status)
> +               return IRQ_NONE;
> +
> +       for_each_set_bit(channel, &irq_status, QUAD8_NUM_COUNTERS)
> +               counter_push_event(&priv->counter, channel);
> +
> +       /* Clear pending interrupts on device */
> +       outb(QUAD8_CHAN_OP_ENABLE_INTERRUPT_FUNC, base + QUAD8_REG_CHAN_OP);
> +
> +       return IRQ_HANDLED;
> +}
> +
>  static int quad8_probe(struct device *dev, unsigned int id)
>  {
>         struct iio_dev *indio_dev;
> @@ -1487,9 +1626,10 @@ static int quad8_probe(struct device *dev, unsigned int id)
>         quad8iio->counter.priv = quad8iio;
>         quad8iio->base = base[id];
>
> -       /* Initialize mutex */
> -       mutex_init(&quad8iio->lock);
> +       raw_spin_lock_init(&quad8iio->lock);
>
> +       /* Reset Index/Interrupt Register */
> +       outb(0x00, base[id] + QUAD8_REG_INDEX_INTERRUPT);
>         /* Reset all counters and disable interrupt function */
>         outb(QUAD8_CHAN_OP_RESET_COUNTERS, base[id] + QUAD8_REG_CHAN_OP);
>         /* Set initial configuration for all counters */
> @@ -1519,8 +1659,8 @@ static int quad8_probe(struct device *dev, unsigned int id)
>         }
>         /* Disable Differential Encoder Cable Status for all channels */
>         outb(0xFF, base[id] + QUAD8_DIFF_ENCODER_CABLE_STATUS);
> -       /* Enable all counters */
> -       outb(QUAD8_CHAN_OP_ENABLE_COUNTERS, base[id] + QUAD8_REG_CHAN_OP);
> +       /* Enable all counters and enable interrupt function */
> +       outb(QUAD8_CHAN_OP_ENABLE_INTERRUPT_FUNC, base[id] + QUAD8_REG_CHAN_OP);
>
>         /* Register IIO device */
>         err = devm_iio_device_register(dev, indio_dev);
> @@ -1528,7 +1668,12 @@ static int quad8_probe(struct device *dev, unsigned int id)
>                 return err;
>
>         /* Register Counter device */
> -       return devm_counter_register(dev, &quad8iio->counter);
> +       err = devm_counter_register(dev, &quad8iio->counter);
> +       if (err)
> +               return err;
> +
> +       return devm_request_irq(dev, irq[id], quad8_irq_handler, IRQF_SHARED,
> +                               quad8iio->counter.name, quad8iio);
>  }
>
>  static struct isa_driver quad8_driver = {
> diff --git a/drivers/counter/Kconfig b/drivers/counter/Kconfig
> index 2de53ab0dd25..bd42df98f522 100644
> --- a/drivers/counter/Kconfig
> +++ b/drivers/counter/Kconfig
> @@ -23,11 +23,11 @@ config 104_QUAD_8
>           A counter's respective error flag may be cleared by performing a write
>           operation on the respective count value attribute. Although the
>           104-QUAD-8 counters have a 25-bit range, only the lower 24 bits may be
> -         set, either directly or via the counter's preset attribute. Interrupts
> -         are not supported by this driver.
> +         set, either directly or via the counter's preset attribute.
>
>           The base port addresses for the devices may be configured via the base
> -         array module parameter.
> +         array module parameter. The interrupt line numbers for the devices may
> +         be configured via the irq array module parameter.
>
>  config STM32_TIMER_CNT
>         tristate "STM32 Timer encoder counter driver"
> --
> 2.27.0
>


Reviewed-by: Syed Nayyar Waris <syednwaris@gmail.com>

Thanks.

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

* Re: [PATCH v4 3/5] counter: Add character device interface
  2020-07-29  0:20   ` David Lechner
@ 2020-07-30 22:49     ` David Lechner
  2020-07-31  8:13       ` Alexandre Belloni
  2020-08-02 21:11       ` William Breathitt Gray
  2020-08-09 14:51     ` William Breathitt Gray
  1 sibling, 2 replies; 18+ messages in thread
From: David Lechner @ 2020-07-30 22:49 UTC (permalink / raw)
  To: William Breathitt Gray, jic23
  Cc: kamel.bouhara, gwendal, alexandre.belloni, linux-iio,
	linux-kernel, linux-stm32, linux-arm-kernel, syednwaris,
	patrick.havelange, fabrice.gasnier, mcoquelin.stm32,
	alexandre.torgue

On 7/28/20 7:20 PM, David Lechner wrote:
> On 7/21/20 2:35 PM, William Breathitt Gray wrote:
>> This patch introduces a character device interface for the Counter
>> subsystem. Device data is exposed through standard character device read
>> operations. Device data is gathered when a Counter event is pushed by
>> the respective Counter device driver. Configuration is handled via ioctl
>> operations on the respective Counter character device node.
> 
> This sounds similar to triggers and buffers in the iio subsystem. And
> I can see how it might be useful in some cases. But I think it would not
> give the desired results when performance is important.
> 

By the way, I really appreciate the work you have done here. When reviewing
code, it is easy to point out what is wrong or we don't like and to not
mention all the parts that are good. And there is a lot of really good work
here already.

I've been working on this all week to try out some of my suggestions and
I'm not getting very far. This is a very difficult problem to solve!

I just wanted to mention this since I responded to this patch series
already but I am still learning and trying things. So I may have more/
different feedback in the future and I may decide some of my suggestions
are not so good. :-)

And one more thing, there was a nice talk at the Embedded Linux
Conference last month about lessons learned from designing a userspace
API for the GPIO subsystem [1]. Unfortunately, there is no video yet,
but the slides might have some helpful ideas about mistakes to avoid.

[1]: https://elinux.org/ELC_2020_Presentations


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

* Re: [PATCH v4 3/5] counter: Add character device interface
  2020-07-30 22:49     ` David Lechner
@ 2020-07-31  8:13       ` Alexandre Belloni
  2020-08-02 21:11       ` William Breathitt Gray
  1 sibling, 0 replies; 18+ messages in thread
From: Alexandre Belloni @ 2020-07-31  8:13 UTC (permalink / raw)
  To: David Lechner
  Cc: William Breathitt Gray, jic23, kamel.bouhara, gwendal, linux-iio,
	linux-kernel, linux-stm32, linux-arm-kernel, syednwaris,
	patrick.havelange, fabrice.gasnier, mcoquelin.stm32,
	alexandre.torgue

On 30/07/2020 17:49:37-0500, David Lechner wrote:
> And one more thing, there was a nice talk at the Embedded Linux
> Conference last month about lessons learned from designing a userspace
> API for the GPIO subsystem [1]. Unfortunately, there is no video yet,
> but the slides might have some helpful ideas about mistakes to avoid.
> 
> [1]: https://elinux.org/ELC_2020_Presentations
> 

The video is available on the original conference platform for one year
after the event, then it will be made available on youtube.


-- 
Alexandre Belloni, Bootlin
Embedded Linux and Kernel engineering
https://bootlin.com

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

* Re: [PATCH v4 3/5] counter: Add character device interface
  2020-07-30 22:49     ` David Lechner
  2020-07-31  8:13       ` Alexandre Belloni
@ 2020-08-02 21:11       ` William Breathitt Gray
  1 sibling, 0 replies; 18+ messages in thread
From: William Breathitt Gray @ 2020-08-02 21:11 UTC (permalink / raw)
  To: David Lechner
  Cc: jic23, kamel.bouhara, gwendal, alexandre.belloni, linux-iio,
	linux-kernel, linux-stm32, linux-arm-kernel, syednwaris,
	patrick.havelange, fabrice.gasnier, mcoquelin.stm32,
	alexandre.torgue


[-- Attachment #1: Type: text/plain, Size: 2356 bytes --]

On Thu, Jul 30, 2020 at 05:49:37PM -0500, David Lechner wrote:
> On 7/28/20 7:20 PM, David Lechner wrote:
> > On 7/21/20 2:35 PM, William Breathitt Gray wrote:
> >> This patch introduces a character device interface for the Counter
> >> subsystem. Device data is exposed through standard character device read
> >> operations. Device data is gathered when a Counter event is pushed by
> >> the respective Counter device driver. Configuration is handled via ioctl
> >> operations on the respective Counter character device node.
> > 
> > This sounds similar to triggers and buffers in the iio subsystem. And
> > I can see how it might be useful in some cases. But I think it would not
> > give the desired results when performance is important.
> > 
> 
> By the way, I really appreciate the work you have done here. When reviewing
> code, it is easy to point out what is wrong or we don't like and to not
> mention all the parts that are good. And there is a lot of really good work
> here already.
> 
> I've been working on this all week to try out some of my suggestions and
> I'm not getting very far. This is a very difficult problem to solve!
> 
> I just wanted to mention this since I responded to this patch series
> already but I am still learning and trying things. So I may have more/
> different feedback in the future and I may decide some of my suggestions
> are not so good. :-)
> 
> And one more thing, there was a nice talk at the Embedded Linux
> Conference last month about lessons learned from designing a userspace
> API for the GPIO subsystem [1]. Unfortunately, there is no video yet,
> but the slides might have some helpful ideas about mistakes to avoid.
> 
> [1]: https://elinux.org/ELC_2020_Presentations

Thanks! I appreciate the words of encouragement. :-)

This is a big endeavor so I'm expecting a lot of mistakes and changes
along the way. Since we're designing a new userspace interface as well,
I want to make sure it's correct before we commit it, because when it's
finally introduced we're basically stuck with it. So I'm happy when
mistakes are found because it saves me from having to live with those
later after the interface is live.

I'll respond to your PATCH 3/5 review later this week or coming weekend
when I get the chance.

Thanks again,

William Breathitt Gray

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]

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

* Re: [PATCH v4 1/5] counter: Internalize sysfs interface code
       [not found]     ` <20200802210415.GA606173@shinobu>
@ 2020-08-03 20:00       ` David Lechner
  2020-08-09 13:42         ` Jonathan Cameron
  2020-08-09 19:15         ` William Breathitt Gray
  0 siblings, 2 replies; 18+ messages in thread
From: David Lechner @ 2020-08-03 20:00 UTC (permalink / raw)
  To: William Breathitt Gray
  Cc: jic23, kamel.bouhara, gwendal, alexandre.belloni, linux-iio,
	linux-kernel, linux-stm32, linux-arm-kernel, syednwaris,
	patrick.havelange, fabrice.gasnier, mcoquelin.stm32,
	alexandre.torgue, David.Laight

On 8/2/20 4:04 PM, William Breathitt Gray wrote:
> On Tue, Jul 28, 2020 at 05:45:53PM -0500, David Lechner wrote:
>> On 7/21/20 2:35 PM, William Breathitt Gray wrote:
>>> This is a reimplementation of the Generic Counter driver interface.

...

>>> -F:	include/linux/counter_enum.h
>>> +F:	include/uapi/linux/counter.h
>>
>> Seems odd to be introducing a uapi header here since this patch doesn't
>> make any changes to userspace.
> 
> These defines are needed by userspace for the character device
> interface, but I see your point that at this point in the patchset they
> don't need to be exposed yet.
> 
> I could create temporary include/linux/counter_types.h to house these
> defines, and then later move them to include/uapi/linux/counter.h in the
> character device interface introduction patch. Do you think I should do
> so?

Since this patch is independent of the chardev changes and probably ready
to merge after one more round of review, I would say it probably makes
sense to just leave them in counter.h for now and move them to uapi when
the chardev interface is finalized. This way, we can just merge this patch
as soon as it is ready.

> 
>>>    
>>>    CPMAC ETHERNET DRIVER
>>>    M:	Florian Fainelli <f.fainelli@gmail.com>
>>> diff --git a/drivers/counter/104-quad-8.c b/drivers/counter/104-quad-8.c
>>> index 78766b6ec271..0f20920073d6 100644
>>> --- a/drivers/counter/104-quad-8.c
>>> +++ b/drivers/counter/104-quad-8.c
>>> @@ -621,7 +621,7 @@ static const struct iio_chan_spec quad8_channels[] = {
>>>    };
>>>    
>>>    static int quad8_signal_read(struct counter_device *counter,
>>> -	struct counter_signal *signal, enum counter_signal_value *val)
>>> +			     struct counter_signal *signal, u8 *val)
>>
>> I'm not a fan of replacing enum types with u8 everywhere in this patch.
>> But if we have to for technical reasons (e.g. causes compiler error if
>> we don't) then it would be helpful to add comments giving the enum type
>> everywhere like this instance where u8 is actually an enum value.
>>
>> If we use u32 as the generic type for enums instead of u8, I think the
>> compiler will happlily let us use enum type and u32 interchangeably and
>> not complain.
> 
> I switched to fixed-width types after the suggestion by David Laight:
> https://lkml.org/lkml/2020/5/3/159. I'll CC David Laight just in case he
> wants to chime in again.
> 
> Enum types would be nice for making the valid values explicit, but there
> is one benefit I have appreciated from the move to fixed-width types:
> there has been a significant reduction of duplicate code; before, we had
> a different read function for each different enum type, but now we use a
> single function to handle them all.

Yes, what I was trying to explain is that by using u32 instead of u8, I
think we can actually do both.

The function pointers in struct counter_device *counter would use u32 as a
generic enum value in the declaration, but then the actual implementations
could still use the proper enum type.

> 
>>> +		device_del(&counter->dev);
>>> +		counter_sysfs_free(counter);
>>
>> Should sysfs be freed before deleting device? I think sysfs might be
>> using dev still.
> 
> I think it's the other way around isn't it? The Counter sysfs memory
> should stay alive for the lifetime of the device. Once the device is
> deleted, there's nothing left to access those struct attributes, so that
> memory can now be freed. Correct me if my reasoning is wrong here.

I think you are right. I was thinking that device_del() would free
memory, but it doesn't. It also looks like other drivers call
device_put() after this, so maybe needed here too?

>>> +static ssize_t counter_data_u8_show(struct device *dev,
>>> +				    struct device_attribute *attr, char *buf)
>>> +{
>>> +	const struct counter_attribute *const a = to_counter_attribute(attr);
>>> +	struct counter_device *const counter = dev_get_drvdata(dev);
>>> +	const struct counter_available *const avail = a->data.priv;
>>> +	int err;
>>> +	u8 data;
>>> +
>>> +	switch (a->type) {
>>
>> I don't understand the use of the word "owner" here. What is being "owned"?
>>
>> Perhaps "component" would be a better choice?
> 
> I wasn't too set on calling this "owner" either, but I'm not sure if
> "component" would make sense either because I wouldn't label a device
> attribute as belonging to any particular component (in fact it's quite
> the opposite).
> 
> Perhaps the word "scope" would be better. What do you think? Or would
> that be too vague as well.

"scope" makes sense to me.

>>> -/**
>>> - * struct counter_signal_ext - Counter Signal extensions
>>> - * @name:	attribute name
>>> - * @read:	read callback for this attribute; may be NULL
>>> - * @write:	write callback for this attribute; may be NULL
>>> - * @priv:	data private to the driver
>>> - */
>>> -struct counter_signal_ext {
>>> +enum counter_data_type {
>>> +	COUNTER_DATA_TYPE_U8,
>>> +	COUNTER_DATA_TYPE_U64,
>>> +	COUNTER_DATA_TYPE_BOOL,
>>> +	COUNTER_DATA_TYPE_SIGNAL,
>>
>> Does this mean signal name?
> 
> This represents the signal values "high" or "low". With the introduction
> of this patchset, these values are no longer strings internally so I
> gave them their own data type here.

Ah, OK. So maybe COUNTER_DATA_TYPE_SIGNAL_LEVEL would be a better name.

> 
>>> +	COUNTER_DATA_TYPE_COUNT_FUNCTION,
>>> +	COUNTER_DATA_TYPE_SYNAPSE_ACTION,
>>> +	COUNTER_DATA_TYPE_ENUM,
>>
>> Why do some enums get their own type while others use a common
>> generic ENUM type?
> 
> COUNTER_DATA_TYPE_ENUM is intended for driver-specific Counter enums.
> This allows driver authors to define their own Counter enums so that we
> don't pollute the Generic Counter interface with enums that are unique
> to individual drivers.
> 
>>> +	COUNTER_DATA_TYPE_COUNT_DIRECTION,
>>> +	COUNTER_DATA_TYPE_COUNT_MODE,
>>
>> Would be nice to group all COUNTER_DATA_TYPE_COUNT_* together
> 
> I assume you're referring to COUNTER_DATA_TYPE_COUNT_FUNCTION being
> separate from these two. That's because a "count function" is actually
> part of the Generic Counter paradigm: it's the trigger operation for the
> Synapse.
> 
> In retrospect, I should have named it "trigger operation" or something
> similar when I developed the paradigm originally, but hindsight is
> 20/20 (I'd probably rename "Synapse" to something else too if I could).
> It's unfortunately too late to rename this because we've exposed it to
> userspace already as a named sysfs attribute.
> 
> Perhaps I can rename this enum constant however to
> COUNTER_DATA_TYPE_FUNCTION, or similar, to differentiate it from the
> Count extensions.
> 

Yes, I think COUNTER_DATA_TYPE_FUNCTION would be sufficient and avoid
confusion.

>>>    /**
>>>     * struct counter_device - Counter data structure
>>> - * @name:		name of the device as it appears in the datasheet
>>> + * @name:		name of the device
>>>     * @parent:		optional parent device providing the counters
>>> - * @device_state:	internal device state container
>>> - * @ops:		callbacks from driver
>>> + * @signal_read:	optional read callback for Signals. The read value of
>>> + *			the respective Signal should be passed back via the
>>> + *			value parameter.
>>> + * @count_read:		optional read callback for Counts. The read value of the
>>> + *			respective Count should be passed back via the value
>>> + *			parameter.
>>> + * @count_write:	optional write callback for Counts. The write value for
>>> + *			the respective Count is passed in via the value
>>> + *			parameter.
>>> + * @function_read:	optional read callback the Count function modes. The
>>> + *			read function mode of the respective Count should be
>>> + *			passed back via the function parameter.
>>> + * @function_write:	option write callback for Count function modes. The
>>> + *			function mode to write for the respective Count is
>>> + *			passed in via the function parameter.
>>> + * @action_read:	optional read callback the Synapse action modes. The
>>> + *			read action mode of the respective Synapse should be
>>> + *			passed back via the action parameter.
>>> + * @action_write:	option write callback for Synapse action modes. The
>>> + *			action mode to write for the respective Synapse is
>>> + *			passed in via the action parameter.
>>>     * @signals:		array of Signals
>>
>> Why not keep the ops struct?
> 
> Defining static ops structures in the drivers seemed to have no
> advantage when those callbacks are always used via the counter_device
> structure. I decided it'd be simpler to just set them directly in the
> counter_device structure then.
> 
> I could reorganize them into an ops structure again if there's enough
> interest.

I've been working on really constrained systems lately where every byte
counts, so this stuck out to me since there would be a copy of all
functions for each counter instance. But probably not that big of a deal
in the Linux kernel. :-)


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

* Re: [PATCH v4 1/5] counter: Internalize sysfs interface code
  2020-08-03 20:00       ` [PATCH v4 1/5] counter: Internalize sysfs interface code David Lechner
@ 2020-08-09 13:42         ` Jonathan Cameron
  2020-08-09 19:15         ` William Breathitt Gray
  1 sibling, 0 replies; 18+ messages in thread
From: Jonathan Cameron @ 2020-08-09 13:42 UTC (permalink / raw)
  To: David Lechner
  Cc: William Breathitt Gray, kamel.bouhara, gwendal,
	alexandre.belloni, linux-iio, linux-kernel, linux-stm32,
	linux-arm-kernel, syednwaris, patrick.havelange, fabrice.gasnier,
	mcoquelin.stm32, alexandre.torgue, David.Laight

On Mon, 3 Aug 2020 15:00:49 -0500
David Lechner <david@lechnology.com> wrote:

> On 8/2/20 4:04 PM, William Breathitt Gray wrote:
> > On Tue, Jul 28, 2020 at 05:45:53PM -0500, David Lechner wrote:  
> >> On 7/21/20 2:35 PM, William Breathitt Gray wrote:  
> >>> This is a reimplementation of the Generic Counter driver interface.  
> 
> ...
> 
> >>> -F:	include/linux/counter_enum.h
> >>> +F:	include/uapi/linux/counter.h  
> >>
> >> Seems odd to be introducing a uapi header here since this patch doesn't
> >> make any changes to userspace.  
> > 
> > These defines are needed by userspace for the character device
> > interface, but I see your point that at this point in the patchset they
> > don't need to be exposed yet.
> > 
> > I could create temporary include/linux/counter_types.h to house these
> > defines, and then later move them to include/uapi/linux/counter.h in the
> > character device interface introduction patch. Do you think I should do
> > so?  
> 
> Since this patch is independent of the chardev changes and probably ready
> to merge after one more round of review, I would say it probably makes
> sense to just leave them in counter.h for now and move them to uapi when
> the chardev interface is finalized. This way, we can just merge this patch
> as soon as it is ready.
> 
Agreed.

...

> >>>    /**
> >>>     * struct counter_device - Counter data structure
> >>> - * @name:		name of the device as it appears in the datasheet
> >>> + * @name:		name of the device
> >>>     * @parent:		optional parent device providing the counters
> >>> - * @device_state:	internal device state container
> >>> - * @ops:		callbacks from driver
> >>> + * @signal_read:	optional read callback for Signals. The read value of
> >>> + *			the respective Signal should be passed back via the
> >>> + *			value parameter.
> >>> + * @count_read:		optional read callback for Counts. The read value of the
> >>> + *			respective Count should be passed back via the value
> >>> + *			parameter.
> >>> + * @count_write:	optional write callback for Counts. The write value for
> >>> + *			the respective Count is passed in via the value
> >>> + *			parameter.
> >>> + * @function_read:	optional read callback the Count function modes. The
> >>> + *			read function mode of the respective Count should be
> >>> + *			passed back via the function parameter.
> >>> + * @function_write:	option write callback for Count function modes. The
> >>> + *			function mode to write for the respective Count is
> >>> + *			passed in via the function parameter.
> >>> + * @action_read:	optional read callback the Synapse action modes. The
> >>> + *			read action mode of the respective Synapse should be
> >>> + *			passed back via the action parameter.
> >>> + * @action_write:	option write callback for Synapse action modes. The
> >>> + *			action mode to write for the respective Synapse is
> >>> + *			passed in via the action parameter.
> >>>     * @signals:		array of Signals  
> >>
> >> Why not keep the ops struct?  
> > 
> > Defining static ops structures in the drivers seemed to have no
> > advantage when those callbacks are always used via the counter_device
> > structure. I decided it'd be simpler to just set them directly in the
> > counter_device structure then.
> > 
> > I could reorganize them into an ops structure again if there's enough
> > interest.  
> 
> I've been working on really constrained systems lately where every byte
> counts, so this stuck out to me since there would be a copy of all
> functions for each counter instance. But probably not that big of a deal
> in the Linux kernel. :-)
> 
In addition to that..

There are other advantages to keeping an ops structure including
easy function order randomization (for security), plus
the fact that we want to make any function pointers build time assignments
if we possibly can.  Makes them harder to attack.

So in more recent kernel code we try to use ops structures wherever possible.

Jonathan

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

* Re: [PATCH v4 0/5] Introduce the Counter character device interface
  2020-07-21 19:35 [PATCH v4 0/5] Introduce the Counter character device interface William Breathitt Gray
                   ` (4 preceding siblings ...)
       [not found] ` <e13d43849f68af8227c6aaa0ef672b459d47e9ab.1595358237.git.vilhelm.gray@gmail.com>
@ 2020-08-09 13:48 ` Jonathan Cameron
  2020-08-09 17:51   ` William Breathitt Gray
  5 siblings, 1 reply; 18+ messages in thread
From: Jonathan Cameron @ 2020-08-09 13:48 UTC (permalink / raw)
  To: William Breathitt Gray
  Cc: kamel.bouhara, gwendal, alexandre.belloni, david, linux-iio,
	linux-kernel, linux-stm32, linux-arm-kernel, syednwaris,
	patrick.havelange, fabrice.gasnier, mcoquelin.stm32,
	alexandre.torgue

On Tue, 21 Jul 2020 15:35:46 -0400
William Breathitt Gray <vilhelm.gray@gmail.com> wrote:

> Changes in v4:
>  - Reimplement character device interface to report Counter events
>  - Implement Counter timestamps
>  - Implement poll() support
>  - Convert microchip-tcb-capture.c to new driver interface
>  - Add IRQ support for the 104-quad-8 Counter driver
> 
> Over the past couple years we have noticed some shortcomings with the
> Counter sysfs interface. Although useful in the majority of situations,
> there are certain use-cases where interacting through sysfs attributes
> can become cumbersome and inefficient. A desire to support more advanced
> functionality such as timestamps, multi-axes positioning tables, and
> other such latency-sensitive applications, has motivated a reevaluation
> of the Counter subsystem. I believe a character device interface will be
> helpful for this more niche area of counter device use.
> 
> To quell any concerns from the offset: this patchset makes no changes to
> the existing Counter sysfs userspace interface -- existing userspace
> applications will continue to work with no modifications necessary. I
> request that driver maintainers please test their applications to verify
> that this is true, and report any discrepancies if they arise.
> 
> However, this patchset does contain a major reimplementation of the
> Counter subsystem core and driver API. A reimplementation was necessary
> in order to separate the sysfs code from the counter device drivers and
> internalize it as a dedicated component of the core Counter subsystem
> module. A minor benefit from all of this is that the sysfs interface is
> now ensured a certain amount of consistency because the translation is
> performed outside of individual counter device drivers.
> 
> Essentially, the reimplementation has enabled counter device drivers to
> pass and handle data as native C datatypes now rather than the sysfs
> strings from before. A high-level view of how a count value is passed
> down from a counter device driver can be exemplified by the following:
> 
>                  ----------------------
>                 / Counter device       \
>                 +----------------------+
>                 | Count register: 0x28 |
>                 +----------------------+
>                         |
>                  -----------------
>                 / raw count data /
>                 -----------------
>                         |
>                         V
>                 +----------------------------+
>                 | Counter device driver      |----------+
>                 +----------------------------+          |
>                 | Processes data from device |   -------------------
>                 |----------------------------|  / driver callbacks /
>                 | Type: u64                  |  -------------------
>                 | Value: 42                  |          |
>                 +----------------------------+          |
>                         |                               |
>                  ----------                             |
>                 / u64     /                             |
>                 ----------                              |
>                         |                               |
>                         |                               V
>                         |               +----------------------+
>                         |               | Counter core         |
>                         |               +----------------------+
>                         |               | Routes device driver |
>                         |               | callbacks to the     |
>                         |               | userspace interfaces |
>                         |               +----------------------+
>                         |                       |
>                         |                -------------------
>                         |               / driver callbacks /
>                         |               -------------------
>                         |                       |
>                 +-------+---------------+       |
>                 |                       |       |
>                 |               +-------|-------+
>                 |               |       |
>                 V               |       V
>         +--------------------+  |  +---------------------+
>         | Counter sysfs      |<-+->| Counter chrdev      |
>         +--------------------+     +---------------------+
>         | Translates to the  |     | Translates to the   |
>         | standard Counter   |     | standard Counter    |
>         | sysfs output       |     | character device    |
>         |--------------------|     |---------------------+
>         | Type: const char * |     | Type: u64           |
>         | Value: "42"        |     | Value: 42           |
>         +--------------------+     +---------------------+
>                 |                               |
>          ---------------                 -----------------------
>         / const char * /                / struct counter_event /
>         ---------------                 -----------------------
>                 |                               |
>                 |                               V
>                 |                       +-----------+
>                 |                       | read      |
>                 |                       +-----------+
>                 |                       \ Count: 42 /
>                 |                        -----------
>                 |
>                 V
>         +--------------------------------------------------+
>         | `/sys/bus/counter/devices/counterX/countY/count` |
>         +--------------------------------------------------+
>         \ Count: "42"                                      /
>          --------------------------------------------------
> 
> Counter device data is exposed through standard character device read
> operations. Device data is gathered when a Counter event is pushed by
> the respective Counter device driver. Configuration is handled via ioctl
> operations on the respective Counter character device node.
> 
> The following are some questions I have about this patchset:
> 
> 1. Should I support multiple file descriptors for the character device
>    in this introduction patchset?
> 
>    I intend to add support for multiple file descriptors to the Counter
>    character device, but I restricted this patchset to a single file
>    descriptor to simplify the code logic for the sake of review. If
>    there is enough interest, I can add support for multiple file
>    descriptors in the next revision; I anticipate that this should be
>    simple to implement through the allocation of a kfifo for each file
>    descriptor during the open callback.

What is the use case?  I can conjecture one easily enough, but I'm not
sure how real it actually is.  We've been around this question a few
times in IIO :)

Certainly makes sense to design an interface that would allow you to
add this support later if needed though.


> 
> 2. Should struct counter_event have a union for different value types,
>    or just a value u8 array?
> 
>    Currently I expose the event data value via a union containing the
>    various possible Counter data types (value_u8 and value_u64). It is
>    up to the user to select the right union member for the data they
>    received. Would it make sense to return this data in a u8 array
>    instead, with the expectation that the user will cast to the
>    necessary data type?

Be careful on alignment if you do that. We would need to ensure that the
buffer is suitable aligned for a cast to work as expected.

> 
> 3. How should errors be returned for Counter data reads performed by
>    Counter events?
> 
>    Counter events are configured with a list of Counter data read
>    operations to perform for the user. Any one of those data reads can
>    return an error code, but not necessarily all of them. Currently, the
>    code exits early when an error code is returned. Should the code
>    instead continue on, saving the error code to the struct
>    counter_event for userspace to handle?

I'd argue that errors are expected to be rare, so it isn't a problem
to just fault out hard on the first one.

> 
> William Breathitt Gray (5):
>   counter: Internalize sysfs interface code
>   docs: counter: Update to reflect sysfs internalization
>   counter: Add character device interface
>   docs: counter: Document character device interface
>   counter: 104-quad-8: Add IRQ support for the ACCES 104-QUAD-8
> 
>  .../ABI/testing/sysfs-bus-counter-104-quad-8  |   32 +
>  Documentation/driver-api/generic-counter.rst  |  363 +++-
>  .../userspace-api/ioctl/ioctl-number.rst      |    1 +
>  MAINTAINERS                                   |    2 +-
>  drivers/counter/104-quad-8.c                  |  753 +++++----
>  drivers/counter/Kconfig                       |    6 +-
>  drivers/counter/Makefile                      |    1 +
>  drivers/counter/counter-chrdev.c              |  441 +++++
>  drivers/counter/counter-chrdev.h              |   16 +
>  drivers/counter/counter-core.c                |  188 +++
>  drivers/counter/counter-sysfs.c               |  849 ++++++++++
>  drivers/counter/counter-sysfs.h               |   14 +
>  drivers/counter/counter.c                     | 1496 -----------------
>  drivers/counter/ftm-quaddec.c                 |   59 +-
>  drivers/counter/microchip-tcb-capture.c       |  104 +-
>  drivers/counter/stm32-lptimer-cnt.c           |  161 +-
>  drivers/counter/stm32-timer-cnt.c             |  139 +-
>  drivers/counter/ti-eqep.c                     |  211 +--
>  include/linux/counter.h                       |  633 +++----
>  include/linux/counter_enum.h                  |   45 -
>  include/uapi/linux/counter.h                  |   90 +
>  21 files changed, 2919 insertions(+), 2685 deletions(-)
>  create mode 100644 drivers/counter/counter-chrdev.c
>  create mode 100644 drivers/counter/counter-chrdev.h
>  create mode 100644 drivers/counter/counter-core.c
>  create mode 100644 drivers/counter/counter-sysfs.c
>  create mode 100644 drivers/counter/counter-sysfs.h
>  delete mode 100644 drivers/counter/counter.c
>  delete mode 100644 include/linux/counter_enum.h
>  create mode 100644 include/uapi/linux/counter.h
> 


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

* Re: [PATCH v4 3/5] counter: Add character device interface
  2020-07-29  0:20   ` David Lechner
  2020-07-30 22:49     ` David Lechner
@ 2020-08-09 14:51     ` William Breathitt Gray
  2020-08-10 23:02       ` David Lechner
  1 sibling, 1 reply; 18+ messages in thread
From: William Breathitt Gray @ 2020-08-09 14:51 UTC (permalink / raw)
  To: David Lechner
  Cc: jic23, kamel.bouhara, gwendal, alexandre.belloni, linux-iio,
	linux-kernel, linux-stm32, linux-arm-kernel, syednwaris,
	patrick.havelange, fabrice.gasnier, mcoquelin.stm32,
	alexandre.torgue


[-- Attachment #1: Type: text/plain, Size: 16477 bytes --]

On Tue, Jul 28, 2020 at 07:20:03PM -0500, David Lechner wrote:
> On 7/21/20 2:35 PM, William Breathitt Gray wrote:
> > This patch introduces a character device interface for the Counter
> > subsystem. Device data is exposed through standard character device read
> > operations. Device data is gathered when a Counter event is pushed by
> > the respective Counter device driver. Configuration is handled via ioctl
> > operations on the respective Counter character device node.
> 
> This sounds similar to triggers and buffers in the iio subsystem. And
> I can see how it might be useful in some cases. But I think it would not
> give the desired results when performance is important.
> 
> Thinking through a few cases here...
> 
> Suppose there was a new counter device that used the I2C bus. This would
> either have to be periodically polled for events or it might have a
> separate GPIO line to notify the MCU. In any case, with the proposed
> implementation, there would be a separate I2C transaction for each data
> point for that event. So none of the data for that event would actually
> be from the same point in time. And with I2C, this time difference could
> be significant.
> 
> With the TI eQEP I have been working with, there are special latched
> registers for some events. To make use of these with events, we would have
> add extensions for each one we want to use (and expose it in sysfs). But
> really, the fact that we are using a latched register should be an
> implementation detail in the driver and not something userspace should have
> to know about.
> 
> So, I'm wondering if it would make sense to keep things simpler and have
> events like the input subsystem where the event value is directly tied
> to the event. It would probably be rare for an event to have more than
> one or two values. And error events probably would not have a value at
> all.
> 
> For example, with the TI eQEP, there is a unit timer time out event.
> This latches the position count, the timer count and the timer period.
> To translate this to an event data structure, the latched time would
> be the event timestamp and the position count would be the event value.
> The timer period should already be known since we would have configured
> the timer ourselves. There is also a count event that works similarly.
> In this case, the latched time would be the event timestamp and the
> latched timer period would be the event value. We would know the count
> already since we get an event for each count (and a separate direction
> change event if the direction changes).

There are use-cases where it would be useful to have the extension reads
occur as close to the event trigger as possible (e.g. multiple-axes
positioning with boundary sensor flags) so I don't think this
functionality should be completely abadoned, but I think your argument
for standard event types makes sense.

We could treat those extensions reads as an optional feature that can be
enabled and configured by ioctls. However, the use-case you are
concerned with, we can redesign Counter events to return specific data
based on the specific event type.

For example, we could have a COUNTER_EVENT_INDEX which occurs when an
Index signal edge is detected, and the return data is the Count value
for that channel; we can also have a COUNTER_EVENT_TIMEOUT which occurs
when a unit timer times out, and returns the data you mentioned you are
interested in seeing.

These Counter event types would be standard, so user applications
wouldn't need to know driver/device implementation details, but instead
just follow the API to get the data they expect for that particular
event type. Would this kind of design work for your needs?

> > 
> > A high-level view of how a count value is passed down from a counter
> > driver is exemplified by the following:
> > 
> >                   ----------------------
> >                  / Counter device       \
> >                  +----------------------+
> >                  | Count register: 0x28 |
> >                  +----------------------+
> >                          |
> >                   -----------------
> >                  / raw count data /
> >                  -----------------
> >                          |
> >                          V
> >                  +----------------------------+
> >                  | Counter device driver      |----------+
> >                  +----------------------------+          |
> >                  | Processes data from device |   -------------------
> >                  |----------------------------|  / driver callbacks /
> >                  | Type: u64                  |  -------------------
> >                  | Value: 42                  |          |
> >                  +----------------------------+          |
> >                          |                               |
> >                   ----------                             |
> >                  / u64     /                             |
> >                  ----------                              |
> >                          |                               |
> >                          |                               V
> >                          |               +----------------------+
> >                          |               | Counter core         |
> >                          |               +----------------------+
> >                          |               | Routes device driver |
> >                          |               | callbacks to the     |
> >                          |               | userspace interfaces |
> >                          |               +----------------------+
> >                          |                       |
> >                          |                -------------------
> >                          |               / driver callbacks /
> >                          |               -------------------
> >                          |                       |
> >                  +-------+---------------+       |
> >                  |                       |       |
> >                  |               +-------|-------+
> >                  |               |       |
> >                  V               |       V
> >          +--------------------+  |  +---------------------+
> >          | Counter sysfs      |<-+->| Counter chrdev      |
> >          +--------------------+     +---------------------+
> >          | Translates to the  |     | Translates to the   |
> >          | standard Counter   |     | standard Counter    |
> >          | sysfs output       |     | character device    |
> >          |--------------------|     |---------------------+
> >          | Type: const char * |     | Type: u64           |
> >          | Value: "42"        |     | Value: 42           |
> >          +--------------------+     +---------------------+
> >                  |                               |
> >           ---------------                 -----------------------
> >          / const char * /                / struct counter_event /
> >          ---------------                 -----------------------
> >                  |                               |
> >                  |                               V
> >                  |                       +-----------+
> >                  |                       | read      |
> >                  |                       +-----------+
> >                  |                       \ Count: 42 /
> >                  |                        -----------
> >                  |
> >                  V
> >          +--------------------------------------------------+
> >          | `/sys/bus/counter/devices/counterX/countY/count` |
> >          +--------------------------------------------------+
> >          \ Count: "42"                                      /
> >           --------------------------------------------------
> > 
> > Counter character device nodes are created under the `/dev` directory as
> > `counterX`, where `X` is the respective counter device id. Defines for
> > the standard Counter data types are exposed via the userspace
> > `include/uapi/linux/counter.h` file.
> > 
> > Counter events
> > --------------
> > Counter device drivers can support Counter events by utilizing the
> > `counter_push_event` function:
> > 
> >      int counter_push_event(struct counter_device *const counter,
> >                             const u8 event);
> > 
> > The event id is specified by the `event` parameter. When this function
> > is called, the Counter data associated with the respective event is
> > gathered, and a `struct counter_event` is generated for each datum and
> > pushed to userspace.
> > 
> > Counter events can be configured by users to report various Counter
> > data of interest. This can be conceptualized as a list of Counter
> > component read calls to perform. For example:
> > 
> >      +------------------------+------------------------+
> >      | Event 0                | Event 1                |
> >      +------------------------+------------------------+
> >      | * Count 0              | * Signal 0             |
> >      | * Count 1              | * Signal 0 Extension 0 |
> >      | * Signal 3             | * Extension 4          |
> >      | * Count 4 Extension 2  |                        |
> >      | * Signal 5 Extension 0 |                        |
> >      +------------------------+------------------------+
> 
> In the current implementation, I can't tell if the event number corresponds
> to the individual counter or some device-specific interrupt bits. In either
> case, it seems like it would be better to have a generic enum of possible
> counter events like overflow, underflow, direction change, etc.

In the current implementation, the event number is arbitrarily chosen by
the driver author. It would be best to have these well defined, and I
think a group of standard Counter events would be the way to go as you
point out. We can define a few common ones we expect for this
introduction patch, and expand it from there if new types of events are
necessary for future drivers.

> > 
> > When `counter_push_event(counter, 1)` is called for example, it will go
> > down the list for Event 1 and execute the read callbacks for Signal 0,
> > Signal 0 Extension 0, and Extension 4 -- the data returned for each is
> > pushed to a kfifo as a `struct counter_event`, which userspace can
> > retrieve via a standard read operation on the respective character
> > device node.
> > 
> > Userspace
> > ---------
> > Userspace applications can configure Counter events via ioctl operations
> > on the Counter character device node. There following ioctl codes are
> > supported and provided by the `linux/counter.h` userspace header file:
> > 
> > * COUNTER_CLEAR_WATCHES_IOCTL:
> >    Clear all Counter watches from all events
> > 
> > * COUNTER_SET_WATCH_IOCTL:
> >    Set a Counter watch on the specified event
> > 
> > To configure events to gather Counter data, users first populate a
> > `struct counter_watch` with the relevant event id and the information
> > for the desired Counter component from which to read, and then pass it
> > via the `COUNTER_SET_WATCH_IOCTL` ioctl command.
> > 
> > Userspace applications can then execute a `read` operation (optionally
> > calling `poll` first) on the Counter character device node to retrieve
> > `struct counter_event` elements with the desired data.
> > 
> > For example, the following userspace code opens `/dev/counter0`,
> > configures Event 0 to gather Count 0 and Count 1, and prints out the
> > data as it becomes available on the character device node:
> > 
> >      #include <fcntl.h>
> >      #include <linux/counter.h>
> >      #include <poll.h>
> >      #include <stdio.h>
> >      #include <sys/ioctl.h>
> >      #include <unistd.h>
> > 
> >      struct counter_watch watches[2] = {
> >              {
> >                      .event = 0,
> >                      .component.owner_type = COUNTER_OWNER_TYPE_COUNT,
> >                      .component.owner_id = 0,
> >                      .component.type = COUNTER_COMPONENT_TYPE_COUNT,
> >              },
> >              {
> >                      .event = 0,
> >                      .component.owner_type = COUNTER_OWNER_TYPE_COUNT,
> >                      .component.owner_id = 1,
> >                      .component.type = COUNTER_COMPONENT_TYPE_COUNT,
> >              },
> >      };
> > 
> >      int main(void)
> >      {
> >              struct pollfd pfd = { .events = POLLIN };
> >              struct counter_event event_data[2];
> > 
> >              pfd.fd = open("/dev/counter0", O_RDWR);
> > 
> >              ioctl(pfd.fd, COUNTER_SET_WATCH_IOCTL, watches);
> >              ioctl(pfd.fd, COUNTER_SET_WATCH_IOCTL, watches + 1);
> 
> What enables events? If an event is enabled for each of these ioctls,
> then we have a race condition where events events from the first watch
> can start to be queued before the second watch is added. So we would
> have to flush the chardev first before polling, otherwise the assumption
> that event_data[0] is owner_id=0 and event_data[1] is owner_id=1 is
> not true.

That's a good point, we could theoretically have a situation where an
event is pushed before the configuration of watches is complete. I'm not
sure if the solution is to implement an enable/disable ioctl to control
when events are recorded, or a flush ioctl to remove the invalid events
in the queue.

> This is also racy if we want to clear watches and set up new watches
> at runtime. There would be a period of time where there were no watches
> and we could miss events.

I'm not sure how typical this use-case is -- would an operator ever want
to change watch configuration on-the-fly? I assumed watches configured
once at the start of a production run, and then stay essentially static
until the production stops.

Well regardless, if we want to support this kind of functionality we
will need to implement a kind of atomic replacement for all watches with
new ones. This shouldn't be too difficult to achieve: buffer the desired
watches instead, and then activate them together atomically via a new
ioctl command.

> With my suggested changes of having fixed values per event and generic
> events, we could just have a single ioctl to enable and disable events.
> This would probably need to take an array of event descriptors as an
> argument where event descriptors contain the component type/id and the
> event to enable.

I agree with having specified data for particular event types, but I
think we should still be able to support general extension watches as an
optional functionality. In fact, I don't think we'll need to implement
enable/disable event ioctl commands.

The current implementation only records events if the user is watching
for them (i.e. a watch has been set); if no one is watching for these
events, they are just silently dropped by the counter_event_push
function. If we implement an ioctl to atomically set the watches, there
is no need to explicitly enable/disable events: events will always
report the specified data for those their respective type -- the watch
data is extra optional data and will start flowing automatically when
atomically activated.

William Breathitt Gray

> > 
> >              for (;;) {
> >                      poll(&pfd, 1, -1);
> > 
> >                      read(pfd.fd, event_data, sizeof(event_data));
> > 
> >                      printf("Timestamp 0: %llu\nCount 0: %llu\n"
> >                             "Timestamp 1: %llu\nCount 1: %llu\n",
> >                             (unsigned long long)event_data[0].timestamp,
> >                             (unsigned long long)event_data[0].value_u64,
> >                             (unsigned long long)event_data[1].timestamp,
> >                             (unsigned long long)event_data[1].value_u64);
> >              }
> > 
> >              return 0;
> >      }
> > 
> > Cc: David Lechner <david@lechnology.com>
> > Cc: Gwendal Grignou <gwendal@chromium.org>
> > Signed-off-by: William Breathitt Gray <vilhelm.gray@gmail.com>
> > ---
> 

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]

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

* Re: [PATCH v4 0/5] Introduce the Counter character device interface
  2020-08-09 13:48 ` [PATCH v4 0/5] Introduce the Counter character device interface Jonathan Cameron
@ 2020-08-09 17:51   ` William Breathitt Gray
  0 siblings, 0 replies; 18+ messages in thread
From: William Breathitt Gray @ 2020-08-09 17:51 UTC (permalink / raw)
  To: Jonathan Cameron
  Cc: kamel.bouhara, gwendal, alexandre.belloni, david, linux-iio,
	linux-kernel, linux-stm32, linux-arm-kernel, syednwaris,
	patrick.havelange, fabrice.gasnier, mcoquelin.stm32,
	alexandre.torgue


[-- Attachment #1: Type: text/plain, Size: 3253 bytes --]

On Sun, Aug 09, 2020 at 02:48:00PM +0100, Jonathan Cameron wrote:
> On Tue, 21 Jul 2020 15:35:46 -0400
> William Breathitt Gray <vilhelm.gray@gmail.com> wrote:
> > The following are some questions I have about this patchset:
> > 
> > 1. Should I support multiple file descriptors for the character device
> >    in this introduction patchset?
> > 
> >    I intend to add support for multiple file descriptors to the Counter
> >    character device, but I restricted this patchset to a single file
> >    descriptor to simplify the code logic for the sake of review. If
> >    there is enough interest, I can add support for multiple file
> >    descriptors in the next revision; I anticipate that this should be
> >    simple to implement through the allocation of a kfifo for each file
> >    descriptor during the open callback.
> 
> What is the use case?  I can conjecture one easily enough, but I'm not
> sure how real it actually is.  We've been around this question a few
> times in IIO :)
> 
> Certainly makes sense to design an interface that would allow you to
> add this support later if needed though.

I don't have any particular use case in mind, but I figured it would be
useful. For example, a counter device can have multiple channels with
their own events, but any particular channel might be counting the
signals of an independent device unrelated to the other channels; in
this scenario, two independent user applications might need access to
the same counter device.

Of course, supporting multiple file descriptors is something that can be
added later so perhaps it's best for us to wait until the need arises
with a real-life use case.

> > 
> > 2. Should struct counter_event have a union for different value types,
> >    or just a value u8 array?
> > 
> >    Currently I expose the event data value via a union containing the
> >    various possible Counter data types (value_u8 and value_u64). It is
> >    up to the user to select the right union member for the data they
> >    received. Would it make sense to return this data in a u8 array
> >    instead, with the expectation that the user will cast to the
> >    necessary data type?
> 
> Be careful on alignment if you do that. We would need to ensure that the
> buffer is suitable aligned for a cast to work as expected.

That's a fair point. It's probably safer to continue with a union which
also has the benefit of making the possible returned types clearer to
see in the code.

> > 
> > 3. How should errors be returned for Counter data reads performed by
> >    Counter events?
> > 
> >    Counter events are configured with a list of Counter data read
> >    operations to perform for the user. Any one of those data reads can
> >    return an error code, but not necessarily all of them. Currently, the
> >    code exits early when an error code is returned. Should the code
> >    instead continue on, saving the error code to the struct
> >    counter_event for userspace to handle?
> 
> I'd argue that errors are expected to be rare, so it isn't a problem
> to just fault out hard on the first one.

All right, that should help keep the error logic simple too then.

William Breathitt Gray

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]

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

* Re: [PATCH v4 1/5] counter: Internalize sysfs interface code
  2020-08-03 20:00       ` [PATCH v4 1/5] counter: Internalize sysfs interface code David Lechner
  2020-08-09 13:42         ` Jonathan Cameron
@ 2020-08-09 19:15         ` William Breathitt Gray
  2020-08-10 22:48           ` David Lechner
  1 sibling, 1 reply; 18+ messages in thread
From: William Breathitt Gray @ 2020-08-09 19:15 UTC (permalink / raw)
  To: David Lechner
  Cc: jic23, kamel.bouhara, gwendal, alexandre.belloni, linux-iio,
	linux-kernel, linux-stm32, linux-arm-kernel, syednwaris,
	patrick.havelange, fabrice.gasnier, mcoquelin.stm32,
	alexandre.torgue, David.Laight


[-- Attachment #1: Type: text/plain, Size: 10975 bytes --]

On Mon, Aug 03, 2020 at 03:00:49PM -0500, David Lechner wrote:
> On 8/2/20 4:04 PM, William Breathitt Gray wrote:
> > On Tue, Jul 28, 2020 at 05:45:53PM -0500, David Lechner wrote:
> >> On 7/21/20 2:35 PM, William Breathitt Gray wrote:
> >>> This is a reimplementation of the Generic Counter driver interface.
> 
> ...
> 
> >>> -F:	include/linux/counter_enum.h
> >>> +F:	include/uapi/linux/counter.h
> >>
> >> Seems odd to be introducing a uapi header here since this patch doesn't
> >> make any changes to userspace.
> > 
> > These defines are needed by userspace for the character device
> > interface, but I see your point that at this point in the patchset they
> > don't need to be exposed yet.
> > 
> > I could create temporary include/linux/counter_types.h to house these
> > defines, and then later move them to include/uapi/linux/counter.h in the
> > character device interface introduction patch. Do you think I should do
> > so?
> 
> Since this patch is independent of the chardev changes and probably ready
> to merge after one more round of review, I would say it probably makes
> sense to just leave them in counter.h for now and move them to uapi when
> the chardev interface is finalized. This way, we can just merge this patch
> as soon as it is ready.

It would be good to isolate out that patch since it's so large. Okay
I'll put these defines in counter.h then and move them to uapi in the
later patch.

> > 
> >>>    
> >>>    CPMAC ETHERNET DRIVER
> >>>    M:	Florian Fainelli <f.fainelli@gmail.com>
> >>> diff --git a/drivers/counter/104-quad-8.c b/drivers/counter/104-quad-8.c
> >>> index 78766b6ec271..0f20920073d6 100644
> >>> --- a/drivers/counter/104-quad-8.c
> >>> +++ b/drivers/counter/104-quad-8.c
> >>> @@ -621,7 +621,7 @@ static const struct iio_chan_spec quad8_channels[] = {
> >>>    };
> >>>    
> >>>    static int quad8_signal_read(struct counter_device *counter,
> >>> -	struct counter_signal *signal, enum counter_signal_value *val)
> >>> +			     struct counter_signal *signal, u8 *val)
> >>
> >> I'm not a fan of replacing enum types with u8 everywhere in this patch.
> >> But if we have to for technical reasons (e.g. causes compiler error if
> >> we don't) then it would be helpful to add comments giving the enum type
> >> everywhere like this instance where u8 is actually an enum value.
> >>
> >> If we use u32 as the generic type for enums instead of u8, I think the
> >> compiler will happlily let us use enum type and u32 interchangeably and
> >> not complain.
> > 
> > I switched to fixed-width types after the suggestion by David Laight:
> > https://lkml.org/lkml/2020/5/3/159. I'll CC David Laight just in case he
> > wants to chime in again.
> > 
> > Enum types would be nice for making the valid values explicit, but there
> > is one benefit I have appreciated from the move to fixed-width types:
> > there has been a significant reduction of duplicate code; before, we had
> > a different read function for each different enum type, but now we use a
> > single function to handle them all.
> 
> Yes, what I was trying to explain is that by using u32 instead of u8, I
> think we can actually do both.
> 
> The function pointers in struct counter_device *counter would use u32 as a
> generic enum value in the declaration, but then the actual implementations
> could still use the proper enum type.

Oh, I see what you mean now. So for example:

    int (*signal_read)(struct counter_device *counter,
                       struct counter_signal *signal, u8 *val)

This will become instead:

    int (*signal_read)(struct counter_device *counter,
                       struct counter_signal *signal, u32 *val)

Then in the driver callback implementation we use the enum type we need:

    enum counter_signal_level signal_level = COUNTER_SIGNAL_HIGH;
    ...
    *val = signal_level;

Is that what you have in mind?

> > 
> >>> +		device_del(&counter->dev);
> >>> +		counter_sysfs_free(counter);
> >>
> >> Should sysfs be freed before deleting device? I think sysfs might be
> >> using dev still.
> > 
> > I think it's the other way around isn't it? The Counter sysfs memory
> > should stay alive for the lifetime of the device. Once the device is
> > deleted, there's nothing left to access those struct attributes, so that
> > memory can now be freed. Correct me if my reasoning is wrong here.
> 
> I think you are right. I was thinking that device_del() would free
> memory, but it doesn't. It also looks like other drivers call
> device_put() after this, so maybe needed here too?

Do you mean put_device()? Hmm, I think you might be right; the
documentation comment states that put_device() should always be used to
give up the reference after a device_add() call. At the very least, I
need to call put_device() after a device_add() failure.

> >>> +static ssize_t counter_data_u8_show(struct device *dev,
> >>> +				    struct device_attribute *attr, char *buf)
> >>> +{
> >>> +	const struct counter_attribute *const a = to_counter_attribute(attr);
> >>> +	struct counter_device *const counter = dev_get_drvdata(dev);
> >>> +	const struct counter_available *const avail = a->data.priv;
> >>> +	int err;
> >>> +	u8 data;
> >>> +
> >>> +	switch (a->type) {
> >>
> >> I don't understand the use of the word "owner" here. What is being "owned"?
> >>
> >> Perhaps "component" would be a better choice?
> > 
> > I wasn't too set on calling this "owner" either, but I'm not sure if
> > "component" would make sense either because I wouldn't label a device
> > attribute as belonging to any particular component (in fact it's quite
> > the opposite).
> > 
> > Perhaps the word "scope" would be better. What do you think? Or would
> > that be too vague as well.
> 
> "scope" makes sense to me.

Okay, I'll make this change then.

> >>> -/**
> >>> - * struct counter_signal_ext - Counter Signal extensions
> >>> - * @name:	attribute name
> >>> - * @read:	read callback for this attribute; may be NULL
> >>> - * @write:	write callback for this attribute; may be NULL
> >>> - * @priv:	data private to the driver
> >>> - */
> >>> -struct counter_signal_ext {
> >>> +enum counter_data_type {
> >>> +	COUNTER_DATA_TYPE_U8,
> >>> +	COUNTER_DATA_TYPE_U64,
> >>> +	COUNTER_DATA_TYPE_BOOL,
> >>> +	COUNTER_DATA_TYPE_SIGNAL,
> >>
> >> Does this mean signal name?
> > 
> > This represents the signal values "high" or "low". With the introduction
> > of this patchset, these values are no longer strings internally so I
> > gave them their own data type here.
> 
> Ah, OK. So maybe COUNTER_DATA_TYPE_SIGNAL_LEVEL would be a better name.

Sure, that name seems sensible to me.

> > 
> >>> +	COUNTER_DATA_TYPE_COUNT_FUNCTION,
> >>> +	COUNTER_DATA_TYPE_SYNAPSE_ACTION,
> >>> +	COUNTER_DATA_TYPE_ENUM,
> >>
> >> Why do some enums get their own type while others use a common
> >> generic ENUM type?
> > 
> > COUNTER_DATA_TYPE_ENUM is intended for driver-specific Counter enums.
> > This allows driver authors to define their own Counter enums so that we
> > don't pollute the Generic Counter interface with enums that are unique
> > to individual drivers.
> > 
> >>> +	COUNTER_DATA_TYPE_COUNT_DIRECTION,
> >>> +	COUNTER_DATA_TYPE_COUNT_MODE,
> >>
> >> Would be nice to group all COUNTER_DATA_TYPE_COUNT_* together
> > 
> > I assume you're referring to COUNTER_DATA_TYPE_COUNT_FUNCTION being
> > separate from these two. That's because a "count function" is actually
> > part of the Generic Counter paradigm: it's the trigger operation for the
> > Synapse.
> > 
> > In retrospect, I should have named it "trigger operation" or something
> > similar when I developed the paradigm originally, but hindsight is
> > 20/20 (I'd probably rename "Synapse" to something else too if I could).
> > It's unfortunately too late to rename this because we've exposed it to
> > userspace already as a named sysfs attribute.
> > 
> > Perhaps I can rename this enum constant however to
> > COUNTER_DATA_TYPE_FUNCTION, or similar, to differentiate it from the
> > Count extensions.
> > 
> 
> Yes, I think COUNTER_DATA_TYPE_FUNCTION would be sufficient and avoid
> confusion.

Okay, I'll make this change then.

> >>>    /**
> >>>     * struct counter_device - Counter data structure
> >>> - * @name:		name of the device as it appears in the datasheet
> >>> + * @name:		name of the device
> >>>     * @parent:		optional parent device providing the counters
> >>> - * @device_state:	internal device state container
> >>> - * @ops:		callbacks from driver
> >>> + * @signal_read:	optional read callback for Signals. The read value of
> >>> + *			the respective Signal should be passed back via the
> >>> + *			value parameter.
> >>> + * @count_read:		optional read callback for Counts. The read value of the
> >>> + *			respective Count should be passed back via the value
> >>> + *			parameter.
> >>> + * @count_write:	optional write callback for Counts. The write value for
> >>> + *			the respective Count is passed in via the value
> >>> + *			parameter.
> >>> + * @function_read:	optional read callback the Count function modes. The
> >>> + *			read function mode of the respective Count should be
> >>> + *			passed back via the function parameter.
> >>> + * @function_write:	option write callback for Count function modes. The
> >>> + *			function mode to write for the respective Count is
> >>> + *			passed in via the function parameter.
> >>> + * @action_read:	optional read callback the Synapse action modes. The
> >>> + *			read action mode of the respective Synapse should be
> >>> + *			passed back via the action parameter.
> >>> + * @action_write:	option write callback for Synapse action modes. The
> >>> + *			action mode to write for the respective Synapse is
> >>> + *			passed in via the action parameter.
> >>>     * @signals:		array of Signals
> >>
> >> Why not keep the ops struct?
> > 
> > Defining static ops structures in the drivers seemed to have no
> > advantage when those callbacks are always used via the counter_device
> > structure. I decided it'd be simpler to just set them directly in the
> > counter_device structure then.
> > 
> > I could reorganize them into an ops structure again if there's enough
> > interest.
> 
> I've been working on really constrained systems lately where every byte
> counts, so this stuck out to me since there would be a copy of all
> functions for each counter instance. But probably not that big of a deal
> in the Linux kernel. :-)

I hadn't considered this before, but that's a decent point. In addition,
considering Jonathan Cameron's comments in the other message about the
benefit of security with making the function pointers build time
assignments, I think I'll bring back the static ops structure afterall.

William Breathitt Gray

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]

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

* Re: [PATCH v4 1/5] counter: Internalize sysfs interface code
  2020-08-09 19:15         ` William Breathitt Gray
@ 2020-08-10 22:48           ` David Lechner
  0 siblings, 0 replies; 18+ messages in thread
From: David Lechner @ 2020-08-10 22:48 UTC (permalink / raw)
  To: William Breathitt Gray
  Cc: jic23, kamel.bouhara, gwendal, alexandre.belloni, linux-iio,
	linux-kernel, linux-stm32, linux-arm-kernel, syednwaris,
	patrick.havelange, fabrice.gasnier, mcoquelin.stm32,
	alexandre.torgue, David.Laight


>>>>>     
>>>>>     CPMAC ETHERNET DRIVER
>>>>>     M:	Florian Fainelli <f.fainelli@gmail.com>
>>>>> diff --git a/drivers/counter/104-quad-8.c b/drivers/counter/104-quad-8.c
>>>>> index 78766b6ec271..0f20920073d6 100644
>>>>> --- a/drivers/counter/104-quad-8.c
>>>>> +++ b/drivers/counter/104-quad-8.c
>>>>> @@ -621,7 +621,7 @@ static const struct iio_chan_spec quad8_channels[] = {
>>>>>     };
>>>>>     
>>>>>     static int quad8_signal_read(struct counter_device *counter,
>>>>> -	struct counter_signal *signal, enum counter_signal_value *val)
>>>>> +			     struct counter_signal *signal, u8 *val)
>>>>
>>>> I'm not a fan of replacing enum types with u8 everywhere in this patch.
>>>> But if we have to for technical reasons (e.g. causes compiler error if
>>>> we don't) then it would be helpful to add comments giving the enum type
>>>> everywhere like this instance where u8 is actually an enum value.
>>>>
>>>> If we use u32 as the generic type for enums instead of u8, I think the
>>>> compiler will happlily let us use enum type and u32 interchangeably and
>>>> not complain.
>>>
>>> I switched to fixed-width types after the suggestion by David Laight:
>>> https://lkml.org/lkml/2020/5/3/159. I'll CC David Laight just in case he
>>> wants to chime in again.
>>>
>>> Enum types would be nice for making the valid values explicit, but there
>>> is one benefit I have appreciated from the move to fixed-width types:
>>> there has been a significant reduction of duplicate code; before, we had
>>> a different read function for each different enum type, but now we use a
>>> single function to handle them all.
>>
>> Yes, what I was trying to explain is that by using u32 instead of u8, I
>> think we can actually do both.
>>
>> The function pointers in struct counter_device *counter would use u32 as a
>> generic enum value in the declaration, but then the actual implementations
>> could still use the proper enum type.
> 
> Oh, I see what you mean now. So for example:
> 
>      int (*signal_read)(struct counter_device *counter,
>                         struct counter_signal *signal, u8 *val)
> 
> This will become instead:
> 
>      int (*signal_read)(struct counter_device *counter,
>                         struct counter_signal *signal, u32 *val)
> 
> Then in the driver callback implementation we use the enum type we need:
> 
>      enum counter_signal_level signal_level = COUNTER_SIGNAL_HIGH;
>      ...
>      *val = signal_level;
> 
> Is that what you have in mind?
> 

Yes.

Additionally, if we have...


       int (*x_write)(struct counter_device *counter,
                      ..., u32 val)
  
We should be able to define the implementation as:

static int my_driver_x_write(struct counter_device *counter,
                              ..., enum some_type val)
{
	...
}

Not sure if it works if val is a pointer though. Little-
endian systems would probably be fine, but maybe not big-
endian combined with -fshort-enums compiler flag.


       int (*x_read)(struct counter_device *counter,
                     ..., u32 *val)
  

static int my_driver_x_read(struct counter_device *counter,
                             ..., enum some_type *val)
{
	...
}


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

* Re: [PATCH v4 3/5] counter: Add character device interface
  2020-08-09 14:51     ` William Breathitt Gray
@ 2020-08-10 23:02       ` David Lechner
  0 siblings, 0 replies; 18+ messages in thread
From: David Lechner @ 2020-08-10 23:02 UTC (permalink / raw)
  To: William Breathitt Gray
  Cc: jic23, kamel.bouhara, gwendal, alexandre.belloni, linux-iio,
	linux-kernel, linux-stm32, linux-arm-kernel, syednwaris,
	patrick.havelange, fabrice.gasnier, mcoquelin.stm32,
	alexandre.torgue

On 8/9/20 9:51 AM, William Breathitt Gray wrote:
> On Tue, Jul 28, 2020 at 07:20:03PM -0500, David Lechner wrote:
>> On 7/21/20 2:35 PM, William Breathitt Gray wrote:
>>> This patch introduces a character device interface for the Counter
>>> subsystem. Device data is exposed through standard character device read
>>> operations. Device data is gathered when a Counter event is pushed by
>>> the respective Counter device driver. Configuration is handled via ioctl
>>> operations on the respective Counter character device node.
>>
>> This sounds similar to triggers and buffers in the iio subsystem. And
>> I can see how it might be useful in some cases. But I think it would not
>> give the desired results when performance is important.
>>
>> Thinking through a few cases here...
>>
>> Suppose there was a new counter device that used the I2C bus. This would
>> either have to be periodically polled for events or it might have a
>> separate GPIO line to notify the MCU. In any case, with the proposed
>> implementation, there would be a separate I2C transaction for each data
>> point for that event. So none of the data for that event would actually
>> be from the same point in time. And with I2C, this time difference could
>> be significant.
>>
>> With the TI eQEP I have been working with, there are special latched
>> registers for some events. To make use of these with events, we would have
>> add extensions for each one we want to use (and expose it in sysfs). But
>> really, the fact that we are using a latched register should be an
>> implementation detail in the driver and not something userspace should have
>> to know about.
>>
>> So, I'm wondering if it would make sense to keep things simpler and have
>> events like the input subsystem where the event value is directly tied
>> to the event. It would probably be rare for an event to have more than
>> one or two values. And error events probably would not have a value at
>> all.
>>
>> For example, with the TI eQEP, there is a unit timer time out event.
>> This latches the position count, the timer count and the timer period.
>> To translate this to an event data structure, the latched time would
>> be the event timestamp and the position count would be the event value.
>> The timer period should already be known since we would have configured
>> the timer ourselves. There is also a count event that works similarly.
>> In this case, the latched time would be the event timestamp and the
>> latched timer period would be the event value. We would know the count
>> already since we get an event for each count (and a separate direction
>> change event if the direction changes).
> 
> There are use-cases where it would be useful to have the extension reads
> occur as close to the event trigger as possible (e.g. multiple-axes
> positioning with boundary sensor flags) so I don't think this
> functionality should be completely abadoned, but I think your argument
> for standard event types makes sense.
> 
> We could treat those extensions reads as an optional feature that can be
> enabled and configured by ioctls. However, the use-case you are
> concerned with, we can redesign Counter events to return specific data
> based on the specific event type.
> 
> For example, we could have a COUNTER_EVENT_INDEX which occurs when an
> Index signal edge is detected, and the return data is the Count value
> for that channel; we can also have a COUNTER_EVENT_TIMEOUT which occurs
> when a unit timer times out, and returns the data you mentioned you are
> interested in seeing.
> 
> These Counter event types would be standard, so user applications
> wouldn't need to know driver/device implementation details, but instead
> just follow the API to get the data they expect for that particular
> event type. Would this kind of design work for your needs?


Yes. After trying (and failing) to implement my suggestions here, I
came to the conclusion that it was not sufficient to only have one
value per event. And it doesn't seem as obvious as I initially thought
which should be the "standard" value for an event in some cases.

>>>
>>> When `counter_push_event(counter, 1)` is called for example, it will go
>>> down the list for Event 1 and execute the read callbacks for Signal 0,
>>> Signal 0 Extension 0, and Extension 4 -- the data returned for each is
>>> pushed to a kfifo as a `struct counter_event`, which userspace can
>>> retrieve via a standard read operation on the respective character
>>> device node.
>>>
>>> Userspace
>>> ---------
>>> Userspace applications can configure Counter events via ioctl operations
>>> on the Counter character device node. There following ioctl codes are
>>> supported and provided by the `linux/counter.h` userspace header file:
>>>
>>> * COUNTER_CLEAR_WATCHES_IOCTL:
>>>     Clear all Counter watches from all events
>>>
>>> * COUNTER_SET_WATCH_IOCTL:
>>>     Set a Counter watch on the specified event
>>>
>>> To configure events to gather Counter data, users first populate a
>>> `struct counter_watch` with the relevant event id and the information
>>> for the desired Counter component from which to read, and then pass it
>>> via the `COUNTER_SET_WATCH_IOCTL` ioctl command.
>>>
>>> Userspace applications can then execute a `read` operation (optionally
>>> calling `poll` first) on the Counter character device node to retrieve
>>> `struct counter_event` elements with the desired data.
>>>
>>> For example, the following userspace code opens `/dev/counter0`,
>>> configures Event 0 to gather Count 0 and Count 1, and prints out the
>>> data as it becomes available on the character device node:
>>>
>>>       #include <fcntl.h>
>>>       #include <linux/counter.h>
>>>       #include <poll.h>
>>>       #include <stdio.h>
>>>       #include <sys/ioctl.h>
>>>       #include <unistd.h>
>>>
>>>       struct counter_watch watches[2] = {
>>>               {
>>>                       .event = 0,
>>>                       .component.owner_type = COUNTER_OWNER_TYPE_COUNT,
>>>                       .component.owner_id = 0,
>>>                       .component.type = COUNTER_COMPONENT_TYPE_COUNT,
>>>               },
>>>               {
>>>                       .event = 0,
>>>                       .component.owner_type = COUNTER_OWNER_TYPE_COUNT,
>>>                       .component.owner_id = 1,
>>>                       .component.type = COUNTER_COMPONENT_TYPE_COUNT,
>>>               },
>>>       };
>>>
>>>       int main(void)
>>>       {
>>>               struct pollfd pfd = { .events = POLLIN };
>>>               struct counter_event event_data[2];
>>>
>>>               pfd.fd = open("/dev/counter0", O_RDWR);
>>>
>>>               ioctl(pfd.fd, COUNTER_SET_WATCH_IOCTL, watches);
>>>               ioctl(pfd.fd, COUNTER_SET_WATCH_IOCTL, watches + 1);
>>
>> What enables events? If an event is enabled for each of these ioctls,
>> then we have a race condition where events events from the first watch
>> can start to be queued before the second watch is added. So we would
>> have to flush the chardev first before polling, otherwise the assumption
>> that event_data[0] is owner_id=0 and event_data[1] is owner_id=1 is
>> not true.
> 
> That's a good point, we could theoretically have a situation where an
> event is pushed before the configuration of watches is complete. I'm not
> sure if the solution is to implement an enable/disable ioctl to control
> when events are recorded, or a flush ioctl to remove the invalid events
> in the queue.
> 
>> This is also racy if we want to clear watches and set up new watches
>> at runtime. There would be a period of time where there were no watches
>> and we could miss events.
> 
> I'm not sure how typical this use-case is -- would an operator ever want
> to change watch configuration on-the-fly? I assumed watches configured
> once at the start of a production run, and then stay essentially static
> until the production stops.

The use case I am thinking of is measuring motor speed in robotics. At
low speed, we need an event for each count increase. But at high speed,
this would be too many events and we instead need a periodic event based
on the timer timeout. A maneuver may require operating at both high and
low speeds without stopping and so we would want to be able to switch
back and forth without interruption.


> 
> Well regardless, if we want to support this kind of functionality we
> will need to implement a kind of atomic replacement for all watches with
> new ones. This shouldn't be too difficult to achieve: buffer the desired
> watches instead, and then activate them together atomically via a new
> ioctl command.
> 
>> With my suggested changes of having fixed values per event and generic
>> events, we could just have a single ioctl to enable and disable events.
>> This would probably need to take an array of event descriptors as an
>> argument where event descriptors contain the component type/id and the
>> event to enable.
> 
> I agree with having specified data for particular event types, but I
> think we should still be able to support general extension watches as an
> optional functionality. In fact, I don't think we'll need to implement
> enable/disable event ioctl commands.
> 
> The current implementation only records events if the user is watching
> for them (i.e. a watch has been set); if no one is watching for these
> events, they are just silently dropped by the counter_event_push
> function. If we implement an ioctl to atomically set the watches, there
> is no need to explicitly enable/disable events: events will always
> report the specified data for those their respective type -- the watch
> data is extra optional data and will start flowing automatically when
> atomically activated.
> 

This sounds reasonable to me.

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

end of thread, back to index

Thread overview: 18+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2020-07-21 19:35 [PATCH v4 0/5] Introduce the Counter character device interface William Breathitt Gray
2020-07-21 19:35 ` [PATCH v4 2/5] docs: counter: Update to reflect sysfs internalization William Breathitt Gray
2020-07-21 19:35 ` [PATCH v4 3/5] counter: Add character device interface William Breathitt Gray
2020-07-29  0:20   ` David Lechner
2020-07-30 22:49     ` David Lechner
2020-07-31  8:13       ` Alexandre Belloni
2020-08-02 21:11       ` William Breathitt Gray
2020-08-09 14:51     ` William Breathitt Gray
2020-08-10 23:02       ` David Lechner
2020-07-21 19:35 ` [PATCH v4 4/5] docs: counter: Document " William Breathitt Gray
2020-07-21 19:35 ` [PATCH v4 5/5] counter: 104-quad-8: Add IRQ support for the ACCES 104-QUAD-8 William Breathitt Gray
2020-07-30 17:07   ` Syed Nayyar Waris
     [not found] ` <e13d43849f68af8227c6aaa0ef672b459d47e9ab.1595358237.git.vilhelm.gray@gmail.com>
     [not found]   ` <7209ac3d-d1ca-1b4c-b22c-8d98b13742e2@lechnology.com>
     [not found]     ` <20200802210415.GA606173@shinobu>
2020-08-03 20:00       ` [PATCH v4 1/5] counter: Internalize sysfs interface code David Lechner
2020-08-09 13:42         ` Jonathan Cameron
2020-08-09 19:15         ` William Breathitt Gray
2020-08-10 22:48           ` David Lechner
2020-08-09 13:48 ` [PATCH v4 0/5] Introduce the Counter character device interface Jonathan Cameron
2020-08-09 17:51   ` William Breathitt Gray

Linux-IIO Archive on lore.kernel.org

Archives are clonable:
	git clone --mirror https://lore.kernel.org/linux-iio/0 linux-iio/git/0.git

	# If you have public-inbox 1.1+ installed, you may
	# initialize and index your mirror using the following commands:
	public-inbox-init -V2 linux-iio linux-iio/ https://lore.kernel.org/linux-iio \
		linux-iio@vger.kernel.org
	public-inbox-index linux-iio

Example config snippet for mirrors

Newsgroup available over NNTP:
	nntp://nntp.lore.kernel.org/org.kernel.vger.linux-iio


AGPL code for this site: git clone https://public-inbox.org/public-inbox.git