2021-09-29 03:56:12

by William Breathitt Gray

[permalink] [raw]
Subject: [PATCH v17 0/9] Introduce the Counter character device interface

Changes in v17:
- A couple minor improvements to documentation from review suggestions
- chrdev_lock redeclared as atomic_t; BITMAP was not necessary because
chrdev_lock is a single flag
- test_and_set_bit_lock(), clear_bit_unlock(), and clear_bit(),
replaced respectively with atomic_add_unless(), atomic_dec(), and
atomic_set()
- counter_comp_read_is_equal() and counter_comp_read_is_set() macros
implemented in order to properly test counter_comp structures' read
callback states
- counter_sysfs_add() call performed before counter_chrdev_add() call
in counter_register() in order to match unwinding sequence
- for-loop utilized in counter-example.c in order to simplify code
- counter-example.c returns 1 on error instead of -errno; errno may be
modified after a subsequent library call so we can't depend on it

For convenience, this patchset is also available on my personal git
repo: https://gitlab.com/vilhelmgray/iio/-/tree/counter_chrdev_v17

A Counter character device interface is introduced that allows Counter
events and associated data to be read() by userspace; the
events_configure() and watch_validate() driver callbacks are introduced
to support Counter events; and IRQ support is added to the
104-QUAD-8 driver, serving as an example of how to support the new
Counter events functionality.

William Breathitt Gray (9):
counter: Move counter enums to uapi header
counter: Add character device interface
docs: counter: Document character device interface
tools/counter: Create Counter tools
counter: Implement signalZ_action_component_id sysfs attribute
counter: Implement *_component_id sysfs attributes
counter: Implement events_queue_size sysfs attribute
counter: 104-quad-8: Replace mutex with spinlock
counter: 104-quad-8: Add IRQ support for the ACCES 104-QUAD-8

Documentation/ABI/testing/sysfs-bus-counter | 29 +
Documentation/driver-api/generic-counter.rst | 177 ++++--
.../userspace-api/ioctl/ioctl-number.rst | 1 +
MAINTAINERS | 2 +
drivers/counter/104-quad-8.c | 256 ++++++--
drivers/counter/Kconfig | 6 +-
drivers/counter/Makefile | 2 +-
drivers/counter/counter-chrdev.c | 578 ++++++++++++++++++
drivers/counter/counter-chrdev.h | 14 +
drivers/counter/counter-core.c | 56 +-
drivers/counter/counter-sysfs.c | 123 +++-
include/linux/counter.h | 98 +--
include/uapi/linux/counter.h | 154 +++++
tools/Makefile | 13 +-
tools/counter/Build | 1 +
tools/counter/Makefile | 53 ++
tools/counter/counter_example.c | 92 +++
17 files changed, 1509 insertions(+), 146 deletions(-)
create mode 100644 drivers/counter/counter-chrdev.c
create mode 100644 drivers/counter/counter-chrdev.h
create mode 100644 include/uapi/linux/counter.h
create mode 100644 tools/counter/Build
create mode 100644 tools/counter/Makefile
create mode 100644 tools/counter/counter_example.c


base-commit: a5ae0cfd53aaf031c2e9ba048281776fa67047c2
--
2.33.0


2021-09-29 03:57:43

by William Breathitt Gray

[permalink] [raw]
Subject: [PATCH v17 4/9] tools/counter: Create Counter tools

This creates an example Counter program under tools/counter/*
to exemplify the Counter character device interface.

Cc: Pavel Machek <[email protected]>
Signed-off-by: William Breathitt Gray <[email protected]>
---
MAINTAINERS | 1 +
tools/Makefile | 13 ++---
tools/counter/Build | 1 +
tools/counter/Makefile | 53 +++++++++++++++++++
tools/counter/counter_example.c | 92 +++++++++++++++++++++++++++++++++
5 files changed, 154 insertions(+), 6 deletions(-)
create mode 100644 tools/counter/Build
create mode 100644 tools/counter/Makefile
create mode 100644 tools/counter/counter_example.c

diff --git a/MAINTAINERS b/MAINTAINERS
index 3c95ada1b830..71082000046b 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -4821,6 +4821,7 @@ F: Documentation/driver-api/generic-counter.rst
F: drivers/counter/
F: include/linux/counter.h
F: include/uapi/linux/counter.h
+F: tools/counter/

CP2615 I2C DRIVER
M: Bence Csókás <[email protected]>
diff --git a/tools/Makefile b/tools/Makefile
index 7e9d34ddd74c..5da1fde03a9a 100644
--- a/tools/Makefile
+++ b/tools/Makefile
@@ -12,6 +12,7 @@ help:
@echo ' acpi - ACPI tools'
@echo ' bpf - misc BPF tools'
@echo ' cgroup - cgroup tools'
+ @echo ' counter - counter tools'
@echo ' cpupower - a tool for all things x86 CPU power'
@echo ' debugging - tools for debugging'
@echo ' firewire - the userspace part of nosy, an IEEE-1394 traffic sniffer'
@@ -65,7 +66,7 @@ acpi: FORCE
cpupower: FORCE
$(call descend,power/$@)

-cgroup firewire hv guest bootconfig spi usb virtio vm bpf iio gpio objtool leds wmi pci firmware debugging tracing: FORCE
+cgroup counter firewire hv guest bootconfig spi usb virtio vm bpf iio gpio objtool leds wmi pci firmware debugging tracing: FORCE
$(call descend,$@)

bpf/%: FORCE
@@ -100,7 +101,7 @@ freefall: FORCE
kvm_stat: FORCE
$(call descend,kvm/$@)

-all: acpi cgroup cpupower gpio hv firewire liblockdep \
+all: acpi cgroup counter cpupower gpio hv firewire liblockdep \
perf selftests bootconfig spi turbostat usb \
virtio vm bpf x86_energy_perf_policy \
tmon freefall iio objtool kvm_stat wmi \
@@ -112,7 +113,7 @@ acpi_install:
cpupower_install:
$(call descend,power/$(@:_install=),install)

-cgroup_install firewire_install gpio_install hv_install iio_install perf_install bootconfig_install spi_install usb_install virtio_install vm_install bpf_install objtool_install wmi_install pci_install debugging_install tracing_install:
+cgroup_install counter_install firewire_install gpio_install hv_install iio_install perf_install bootconfig_install spi_install usb_install virtio_install vm_install bpf_install objtool_install wmi_install pci_install debugging_install tracing_install:
$(call descend,$(@:_install=),install)

liblockdep_install:
@@ -133,7 +134,7 @@ freefall_install:
kvm_stat_install:
$(call descend,kvm/$(@:_install=),install)

-install: acpi_install cgroup_install cpupower_install gpio_install \
+install: acpi_install cgroup_install counter_install cpupower_install gpio_install \
hv_install firewire_install iio_install liblockdep_install \
perf_install selftests_install turbostat_install usb_install \
virtio_install vm_install bpf_install x86_energy_perf_policy_install \
@@ -147,7 +148,7 @@ acpi_clean:
cpupower_clean:
$(call descend,power/cpupower,clean)

-cgroup_clean hv_clean firewire_clean bootconfig_clean spi_clean usb_clean virtio_clean vm_clean wmi_clean bpf_clean iio_clean gpio_clean objtool_clean leds_clean pci_clean firmware_clean debugging_clean tracing_clean:
+cgroup_clean counter_clean hv_clean firewire_clean bootconfig_clean spi_clean usb_clean virtio_clean vm_clean wmi_clean bpf_clean iio_clean gpio_clean objtool_clean leds_clean pci_clean firmware_clean debugging_clean tracing_clean:
$(call descend,$(@:_clean=),clean)

liblockdep_clean:
@@ -181,7 +182,7 @@ freefall_clean:
build_clean:
$(call descend,build,clean)

-clean: acpi_clean cgroup_clean cpupower_clean hv_clean firewire_clean \
+clean: acpi_clean cgroup_clean counter_clean cpupower_clean hv_clean firewire_clean \
perf_clean selftests_clean turbostat_clean bootconfig_clean spi_clean usb_clean virtio_clean \
vm_clean bpf_clean iio_clean x86_energy_perf_policy_clean tmon_clean \
freefall_clean build_clean libbpf_clean libsubcmd_clean liblockdep_clean \
diff --git a/tools/counter/Build b/tools/counter/Build
new file mode 100644
index 000000000000..33f4a51d715e
--- /dev/null
+++ b/tools/counter/Build
@@ -0,0 +1 @@
+counter_example-y += counter_example.o
diff --git a/tools/counter/Makefile b/tools/counter/Makefile
new file mode 100644
index 000000000000..5ebc195fd9c0
--- /dev/null
+++ b/tools/counter/Makefile
@@ -0,0 +1,53 @@
+# SPDX-License-Identifier: GPL-2.0
+include ../scripts/Makefile.include
+
+bindir ?= /usr/bin
+
+ifeq ($(srctree),)
+srctree := $(patsubst %/,%,$(dir $(CURDIR)))
+srctree := $(patsubst %/,%,$(dir $(srctree)))
+endif
+
+# Do not use make's built-in rules
+# (this improves performance and avoids hard-to-debug behaviour);
+MAKEFLAGS += -r
+
+override CFLAGS += -O2 -Wall -g -D_GNU_SOURCE -I$(OUTPUT)include
+
+ALL_TARGETS := counter_example
+ALL_PROGRAMS := $(patsubst %,$(OUTPUT)%,$(ALL_TARGETS))
+
+all: $(ALL_PROGRAMS)
+
+export srctree OUTPUT CC LD CFLAGS
+include $(srctree)/tools/build/Makefile.include
+
+#
+# We need the following to be outside of kernel tree
+#
+$(OUTPUT)include/linux/counter.h: ../../include/uapi/linux/counter.h
+ mkdir -p $(OUTPUT)include/linux 2>&1 || true
+ ln -sf $(CURDIR)/../../include/uapi/linux/counter.h $@
+
+prepare: $(OUTPUT)include/linux/counter.h
+
+COUNTER_EXAMPLE := $(OUTPUT)counter_example.o
+$(COUNTER_EXAMPLE): prepare FORCE
+ $(Q)$(MAKE) $(build)=counter_example
+$(OUTPUT)counter_example: $(COUNTER_EXAMPLE)
+ $(QUIET_LINK)$(CC) $(CFLAGS) $(LDFLAGS) $< -o $@
+
+clean:
+ rm -f $(ALL_PROGRAMS)
+ rm -rf $(OUTPUT)include/linux/counter.h
+ find $(if $(OUTPUT),$(OUTPUT),.) -name '*.o' -delete -o -name '\.*.d' -delete
+
+install: $(ALL_PROGRAMS)
+ install -d -m 755 $(DESTDIR)$(bindir); \
+ for program in $(ALL_PROGRAMS); do \
+ install $$program $(DESTDIR)$(bindir); \
+ done
+
+FORCE:
+
+.PHONY: all install clean FORCE prepare
diff --git a/tools/counter/counter_example.c b/tools/counter/counter_example.c
new file mode 100644
index 000000000000..be55287b950f
--- /dev/null
+++ b/tools/counter/counter_example.c
@@ -0,0 +1,92 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/* Counter - example userspace application
+ *
+ * The userspace application opens /dev/counter0, configures the
+ * COUNTER_EVENT_INDEX event channel 0 to gather Count 0 count and Count
+ * 1 count, and prints out the data as it becomes available on the
+ * character device node.
+ *
+ * Copyright (C) 2021 William Breathitt Gray
+ */
+#include <errno.h>
+#include <fcntl.h>
+#include <linux/counter.h>
+#include <stdio.h>
+#include <string.h>
+#include <sys/ioctl.h>
+#include <unistd.h>
+
+static struct counter_watch watches[2] = {
+ {
+ /* Component data: Count 0 count */
+ .component.type = COUNTER_COMPONENT_COUNT,
+ .component.scope = COUNTER_SCOPE_COUNT,
+ .component.parent = 0,
+ /* Event type: Index */
+ .event = COUNTER_EVENT_INDEX,
+ /* Device event channel 0 */
+ .channel = 0,
+ },
+ {
+ /* Component data: Count 1 count */
+ .component.type = COUNTER_COMPONENT_COUNT,
+ .component.scope = COUNTER_SCOPE_COUNT,
+ .component.parent = 1,
+ /* Event type: Index */
+ .event = COUNTER_EVENT_INDEX,
+ /* Device event channel 0 */
+ .channel = 0,
+ },
+};
+
+int main(void)
+{
+ int fd;
+ int ret;
+ int i;
+ struct counter_event event_data[2];
+
+ fd = open("/dev/counter0", O_RDWR);
+ if (fd == -1) {
+ perror("Unable to open /dev/counter0");
+ return 1;
+ }
+
+ for (i = 0; i < 2; i++) {
+ ret = ioctl(fd, COUNTER_ADD_WATCH_IOCTL, watches + i);
+ if (ret == -1) {
+ fprintf(stderr, "Error adding watches[%d]: %s\n", i,
+ strerror(errno));
+ return 1;
+ }
+ }
+ ret = ioctl(fd, COUNTER_ENABLE_EVENTS_IOCTL);
+ if (ret == -1) {
+ perror("Error enabling events");
+ return 1;
+ }
+
+ for (;;) {
+ ret = read(fd, event_data, sizeof(event_data));
+ if (ret == -1) {
+ perror("Failed to read event data");
+ return 1;
+ }
+
+ if (ret != sizeof(event_data)) {
+ fprintf(stderr, "Failed to read event data\n");
+ return -EIO;
+ }
+
+ printf("Timestamp 0: %llu\tCount 0: %llu\n"
+ "Error Message 0: %s\n"
+ "Timestamp 1: %llu\tCount 1: %llu\n"
+ "Error Message 1: %s\n",
+ event_data[0].timestamp, event_data[0].value,
+ strerror(event_data[0].status),
+ event_data[1].timestamp, event_data[1].value,
+ strerror(event_data[1].status));
+ }
+
+ return 0;
+}
--
2.33.0

2021-09-29 03:58:08

by William Breathitt Gray

[permalink] [raw]
Subject: [PATCH v17 9/9] counter: 104-quad-8: Add IRQ support for the ACCES 104-QUAD-8

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.

These four functions correspond respectivefly to the following four
Counter event types: COUNTER_EVENT_OVERFLOW, COUNTER_EVENT_THRESHOLD,
COUNTER_EVENT_OVERFLOW_UNDERFLOW, and COUNTER_EVENT_INDEX. Interrupts
push Counter events to event channel 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.

Acked-by: Syed Nayyar Waris <[email protected]>
Signed-off-by: William Breathitt Gray <[email protected]>
---
drivers/counter/104-quad-8.c | 167 +++++++++++++++++++++++++++++++++--
drivers/counter/Kconfig | 6 +-
2 files changed, 164 insertions(+), 9 deletions(-)

diff --git a/drivers/counter/104-quad-8.c b/drivers/counter/104-quad-8.c
index a56751bf1e9b..1cbd60aaed69 100644
--- a/drivers/counter/104-quad-8.c
+++ b/drivers/counter/104-quad-8.c
@@ -11,6 +11,7 @@
#include <linux/errno.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>
@@ -25,6 +26,10 @@ static unsigned int num_quad8;
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

/**
@@ -38,6 +43,8 @@ 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 current IRQ trigger function configurations
+ * @next_irq_trigger: array of next IRQ 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
@@ -53,13 +60,17 @@ struct quad8 {
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 next_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 {
#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
@@ -378,13 +389,103 @@ static int quad8_action_read(struct counter_device *counter,
}
}

+enum {
+ QUAD8_EVENT_NONE = -1,
+ QUAD8_EVENT_CARRY = 0,
+ QUAD8_EVENT_COMPARE = 1,
+ QUAD8_EVENT_CARRY_BORROW = 2,
+ QUAD8_EVENT_INDEX = 3,
+};
+
+static int quad8_events_configure(struct counter_device *counter)
+{
+ struct quad8 *const priv = counter->priv;
+ unsigned long irq_enabled = 0;
+ unsigned long irqflags;
+ size_t channel;
+ unsigned long ior_cfg;
+ unsigned long base_offset;
+
+ spin_lock_irqsave(&priv->lock, irqflags);
+
+ /* Enable interrupts for the requested channels, disable for the rest */
+ for (channel = 0; channel < QUAD8_NUM_COUNTERS; channel++) {
+ if (priv->next_irq_trigger[channel] == QUAD8_EVENT_NONE)
+ continue;
+
+ if (priv->irq_trigger[channel] != priv->next_irq_trigger[channel]) {
+ /* Save new IRQ function configuration */
+ priv->irq_trigger[channel] = priv->next_irq_trigger[channel];
+
+ /* Load configuration to I/O Control Register */
+ ior_cfg = priv->ab_enable[channel] |
+ priv->preset_enable[channel] << 1 |
+ priv->irq_trigger[channel] << 3;
+ base_offset = priv->base + 2 * channel + 1;
+ outb(QUAD8_CTR_IOR | ior_cfg, base_offset);
+ }
+
+ /* Reset next IRQ trigger function configuration */
+ priv->next_irq_trigger[channel] = QUAD8_EVENT_NONE;
+
+ /* Enable IRQ line */
+ irq_enabled |= BIT(channel);
+ }
+
+ outb(irq_enabled, priv->base + QUAD8_REG_INDEX_INTERRUPT);
+
+ spin_unlock_irqrestore(&priv->lock, irqflags);
+
+ return 0;
+}
+
+static int quad8_watch_validate(struct counter_device *counter,
+ const struct counter_watch *watch)
+{
+ struct quad8 *const priv = counter->priv;
+
+ if (watch->channel > QUAD8_NUM_COUNTERS - 1)
+ return -EINVAL;
+
+ switch (watch->event) {
+ case COUNTER_EVENT_OVERFLOW:
+ if (priv->next_irq_trigger[watch->channel] == QUAD8_EVENT_NONE)
+ priv->next_irq_trigger[watch->channel] = QUAD8_EVENT_CARRY;
+ else if (priv->next_irq_trigger[watch->channel] != QUAD8_EVENT_CARRY)
+ return -EINVAL;
+ return 0;
+ case COUNTER_EVENT_THRESHOLD:
+ if (priv->next_irq_trigger[watch->channel] == QUAD8_EVENT_NONE)
+ priv->next_irq_trigger[watch->channel] = QUAD8_EVENT_COMPARE;
+ else if (priv->next_irq_trigger[watch->channel] != QUAD8_EVENT_COMPARE)
+ return -EINVAL;
+ return 0;
+ case COUNTER_EVENT_OVERFLOW_UNDERFLOW:
+ if (priv->next_irq_trigger[watch->channel] == QUAD8_EVENT_NONE)
+ priv->next_irq_trigger[watch->channel] = QUAD8_EVENT_CARRY_BORROW;
+ else if (priv->next_irq_trigger[watch->channel] != QUAD8_EVENT_CARRY_BORROW)
+ return -EINVAL;
+ return 0;
+ case COUNTER_EVENT_INDEX:
+ if (priv->next_irq_trigger[watch->channel] == QUAD8_EVENT_NONE)
+ priv->next_irq_trigger[watch->channel] = QUAD8_EVENT_INDEX;
+ else if (priv->next_irq_trigger[watch->channel] != QUAD8_EVENT_INDEX)
+ return -EINVAL;
+ return 0;
+ default:
+ return -EINVAL;
+ }
+}
+
static const struct counter_ops quad8_ops = {
.signal_read = quad8_signal_read,
.count_read = quad8_count_read,
.count_write = quad8_count_write,
.function_read = quad8_function_read,
.function_write = quad8_function_write,
- .action_read = quad8_action_read
+ .action_read = quad8_action_read,
+ .events_configure = quad8_events_configure,
+ .watch_validate = quad8_watch_validate,
};

static const char *const quad8_index_polarity_modes[] = {
@@ -579,7 +680,8 @@ static int quad8_count_enable_write(struct counter_device *counter,

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);
@@ -728,7 +830,8 @@ static int quad8_count_preset_enable_write(struct counter_device *counter,

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);
@@ -980,11 +1083,54 @@ static struct counter_count quad8_counts[] = {
QUAD8_COUNT(7, "Channel 8 Count")
};

+static irqreturn_t quad8_irq_handler(int irq, void *private)
+{
+ struct quad8 *const priv = private;
+ const unsigned long base = priv->base;
+ unsigned long irq_status;
+ unsigned long channel;
+ u8 event;
+
+ irq_status = inb(base + QUAD8_REG_INTERRUPT_STATUS);
+ if (!irq_status)
+ return IRQ_NONE;
+
+ for_each_set_bit(channel, &irq_status, QUAD8_NUM_COUNTERS) {
+ switch (priv->irq_trigger[channel]) {
+ case QUAD8_EVENT_CARRY:
+ event = COUNTER_EVENT_OVERFLOW;
+ break;
+ case QUAD8_EVENT_COMPARE:
+ event = COUNTER_EVENT_THRESHOLD;
+ break;
+ case QUAD8_EVENT_CARRY_BORROW:
+ event = COUNTER_EVENT_OVERFLOW_UNDERFLOW;
+ break;
+ case QUAD8_EVENT_INDEX:
+ event = COUNTER_EVENT_INDEX;
+ break;
+ default:
+ /* should never reach this path */
+ WARN_ONCE(true, "invalid interrupt trigger function %u configured for channel %lu\n",
+ priv->irq_trigger[channel], channel);
+ continue;
+ }
+
+ counter_push_event(&priv->counter, event, 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 quad8 *priv;
int i, j;
unsigned int base_offset;
+ int err;

if (!devm_request_region(dev, base[id], QUAD8_EXTENT, dev_name(dev))) {
dev_err(dev, "Unable to lock port addresses (0x%X-0x%X)\n",
@@ -1009,6 +1155,8 @@ static int quad8_probe(struct device *dev, unsigned int id)

spin_lock_init(&priv->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 */
@@ -1035,11 +1183,18 @@ static int quad8_probe(struct device *dev, unsigned int id)
outb(QUAD8_CTR_IOR, base_offset + 1);
/* Disable index function; negative index polarity */
outb(QUAD8_CTR_IDR, base_offset + 1);
+ /* Initialize next IRQ trigger function configuration */
+ priv->next_irq_trigger[i] = QUAD8_EVENT_NONE;
}
/* 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);
+
+ err = devm_request_irq(dev, irq[id], quad8_irq_handler, IRQF_SHARED,
+ priv->counter.name, priv);
+ if (err)
+ return err;

return devm_counter_register(dev, &priv->counter);
}
diff --git a/drivers/counter/Kconfig b/drivers/counter/Kconfig
index d5d2540b30c2..3dcdb681c4e4 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 INTERRUPT_CNT
tristate "Interrupt counter driver"
--
2.33.0

2021-09-30 21:54:20

by Jonathan Cameron

[permalink] [raw]
Subject: Re: [PATCH v17 0/9] Introduce the Counter character device interface

On Wed, 29 Sep 2021 12:15:57 +0900
William Breathitt Gray <[email protected]> wrote:

> Changes in v17:
> - A couple minor improvements to documentation from review suggestions
> - chrdev_lock redeclared as atomic_t; BITMAP was not necessary because
> chrdev_lock is a single flag
> - test_and_set_bit_lock(), clear_bit_unlock(), and clear_bit(),
> replaced respectively with atomic_add_unless(), atomic_dec(), and
> atomic_set()
> - counter_comp_read_is_equal() and counter_comp_read_is_set() macros
> implemented in order to properly test counter_comp structures' read
> callback states
> - counter_sysfs_add() call performed before counter_chrdev_add() call
> in counter_register() in order to match unwinding sequence
> - for-loop utilized in counter-example.c in order to simplify code
> - counter-example.c returns 1 on error instead of -errno; errno may be
> modified after a subsequent library call so we can't depend on it
>
> For convenience, this patchset is also available on my personal git
> repo: https://gitlab.com/vilhelmgray/iio/-/tree/counter_chrdev_v17
>
> A Counter character device interface is introduced that allows Counter
> events and associated data to be read() by userspace; the
> events_configure() and watch_validate() driver callbacks are introduced
> to support Counter events; and IRQ support is added to the
> 104-QUAD-8 driver, serving as an example of how to support the new
> Counter events functionality.

Applied to the togreg branch of iio.git and pushed out as testing for
0-day to see if we missed anything!

I couldn't resist hitting lore.kernel.org for some archaeology on whether
this broke records for a series on the IIO list (i.e. one I'd actually
reviewed :)

It has the dubious honour of being the only series to reach v17 that has
been posted to [email protected] as far as records go back.

Peter Rosin's MUX series got to v15 and that seems to be the previous
record holder joint with an mxs-lradc set from a while back.

Perhaps not a record you wanted to get, but it shows impressive grit
and determination!

Jonathan

>
> William Breathitt Gray (9):
> counter: Move counter enums to uapi header
> counter: Add character device interface
> docs: counter: Document character device interface
> tools/counter: Create Counter tools
> counter: Implement signalZ_action_component_id sysfs attribute
> counter: Implement *_component_id sysfs attributes
> counter: Implement events_queue_size sysfs attribute
> counter: 104-quad-8: Replace mutex with spinlock
> counter: 104-quad-8: Add IRQ support for the ACCES 104-QUAD-8
>
> Documentation/ABI/testing/sysfs-bus-counter | 29 +
> Documentation/driver-api/generic-counter.rst | 177 ++++--
> .../userspace-api/ioctl/ioctl-number.rst | 1 +
> MAINTAINERS | 2 +
> drivers/counter/104-quad-8.c | 256 ++++++--
> drivers/counter/Kconfig | 6 +-
> drivers/counter/Makefile | 2 +-
> drivers/counter/counter-chrdev.c | 578 ++++++++++++++++++
> drivers/counter/counter-chrdev.h | 14 +
> drivers/counter/counter-core.c | 56 +-
> drivers/counter/counter-sysfs.c | 123 +++-
> include/linux/counter.h | 98 +--
> include/uapi/linux/counter.h | 154 +++++
> tools/Makefile | 13 +-
> tools/counter/Build | 1 +
> tools/counter/Makefile | 53 ++
> tools/counter/counter_example.c | 92 +++
> 17 files changed, 1509 insertions(+), 146 deletions(-)
> create mode 100644 drivers/counter/counter-chrdev.c
> create mode 100644 drivers/counter/counter-chrdev.h
> create mode 100644 include/uapi/linux/counter.h
> create mode 100644 tools/counter/Build
> create mode 100644 tools/counter/Makefile
> create mode 100644 tools/counter/counter_example.c
>
>
> base-commit: a5ae0cfd53aaf031c2e9ba048281776fa67047c2