Re: [PATCH v8 2/2] counter: add IRQ or GPIO based counter

2021-03-01 Thread William Breathitt Gray
On Mon, Mar 01, 2021 at 09:04:01AM +0100, Oleksij Rempel wrote:
> Add simple IRQ or GPIO base counter. This device is used to measure
> rotation speed of some agricultural devices, so no high frequency on the
> counter pin is expected.
> 
> The maximal measurement frequency depends on the CPU and system load. On
> the idle iMX6S I was able to measure up to 20kHz without count drops.
> 
> Signed-off-by: Oleksij Rempel 
> Reviewed-by: Ahmad Fatoum 

This version looks acceptable for the Counter subsystem.

Thanks,

Reviewed-by: William Breathitt Gray 

> ---
>  MAINTAINERS |   7 +
>  drivers/counter/Kconfig |  10 ++
>  drivers/counter/Makefile|   1 +
>  drivers/counter/interrupt-cnt.c | 244 
>  4 files changed, 262 insertions(+)
>  create mode 100644 drivers/counter/interrupt-cnt.c
> 
> diff --git a/MAINTAINERS b/MAINTAINERS
> index a50a543e3c81..ad0a4455afec 100644
> --- a/MAINTAINERS
> +++ b/MAINTAINERS
> @@ -9217,6 +9217,13 @@ F: include/dt-bindings/interconnect/
>  F:   include/linux/interconnect-provider.h
>  F:   include/linux/interconnect.h
>  
> +INTERRUPT COUNTER DRIVER
> +M:   Oleksij Rempel 
> +R:   Pengutronix Kernel Team 
> +L:   linux-...@vger.kernel.org
> +F:   Documentation/devicetree/bindings/counter/interrupt-counter.yaml
> +F:   drivers/counter/interrupt-cnt.c
> +
>  INVENSENSE ICM-426xx IMU DRIVER
>  M:   Jean-Baptiste Maneyrol 
>  L:   linux-...@vger.kernel.org
> diff --git a/drivers/counter/Kconfig b/drivers/counter/Kconfig
> index 2de53ab0dd25..dcad13229134 100644
> --- a/drivers/counter/Kconfig
> +++ b/drivers/counter/Kconfig
> @@ -29,6 +29,16 @@ config 104_QUAD_8
> The base port addresses for the devices may be configured via the base
> array module parameter.
>  
> +config INTERRUPT_CNT
> + tristate "Interrupt counter driver"
> + depends on GPIOLIB
> + help
> +   Select this option to enable interrupt counter driver. Any interrupt
> +   source can be used by this driver as the event source.
> +
> +   To compile this driver as a module, choose M here: the
> +   module will be called interrupt-cnt.
> +
>  config STM32_TIMER_CNT
>   tristate "STM32 Timer encoder counter driver"
>   depends on MFD_STM32_TIMERS || COMPILE_TEST
> diff --git a/drivers/counter/Makefile b/drivers/counter/Makefile
> index 0a393f71e481..cb646ed2f039 100644
> --- a/drivers/counter/Makefile
> +++ b/drivers/counter/Makefile
> @@ -6,6 +6,7 @@
>  obj-$(CONFIG_COUNTER) += counter.o
>  
>  obj-$(CONFIG_104_QUAD_8) += 104-quad-8.o
> +obj-$(CONFIG_INTERRUPT_CNT)  += interrupt-cnt.o
>  obj-$(CONFIG_STM32_TIMER_CNT)+= stm32-timer-cnt.o
>  obj-$(CONFIG_STM32_LPTIMER_CNT)  += stm32-lptimer-cnt.o
>  obj-$(CONFIG_TI_EQEP)+= ti-eqep.o
> diff --git a/drivers/counter/interrupt-cnt.c b/drivers/counter/interrupt-cnt.c
> new file mode 100644
> index ..a99ee7996977
> --- /dev/null
> +++ b/drivers/counter/interrupt-cnt.c
> @@ -0,0 +1,244 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Copyright (c) 2021 Pengutronix, Oleksij Rempel 
> + */
> +
> +#include 
> +#include 
> +#include 
> +#include 
> +#include 
> +#include 
> +#include 
> +
> +#define INTERRUPT_CNT_NAME "interrupt-cnt"
> +
> +struct interrupt_cnt_priv {
> + atomic_t count;
> + struct counter_device counter;
> + struct gpio_desc *gpio;
> + int irq;
> + bool enabled;
> + struct counter_signal signals;
> + struct counter_synapse synapses;
> + struct counter_count cnts;
> +};
> +
> +static irqreturn_t interrupt_cnt_isr(int irq, void *dev_id)
> +{
> + struct interrupt_cnt_priv *priv = dev_id;
> +
> + atomic_inc(>count);
> +
> + return IRQ_HANDLED;
> +}
> +
> +static ssize_t interrupt_cnt_enable_read(struct counter_device *counter,
> +  struct counter_count *count,
> +  void *private, char *buf)
> +{
> + struct interrupt_cnt_priv *priv = counter->priv;
> +
> + return sysfs_emit(buf, "%d\n", priv->enabled);
> +}
> +
> +static ssize_t interrupt_cnt_enable_write(struct counter_device *counter,
> +   struct counter_count *count,
> +   void *private, const char *buf,
> +   size_t len)
> +{
> + struct interrupt_cnt_priv *priv = counter->priv;
> + bool enable;
> + ssize_t ret;
> +
> + ret = kstrtobool(buf, );
> + if (ret)
> + return ret;
> +
> + if (priv->enabled == enable)
> + return len;
> +
> + if (enable) {
> + priv->enabled = true;
> + enable_irq(priv->irq);
> + } else {
> + disable_irq(priv->irq);
> + priv->enabled = false;
> + }
> +
> + return len;
> +}
> +
> +static const struct counter_count_ext interrupt_cnt_ext[] = {
> + {
> + .name = 

[PATCH v8 2/2] counter: add IRQ or GPIO based counter

2021-03-01 Thread Oleksij Rempel
Add simple IRQ or GPIO base counter. This device is used to measure
rotation speed of some agricultural devices, so no high frequency on the
counter pin is expected.

The maximal measurement frequency depends on the CPU and system load. On
the idle iMX6S I was able to measure up to 20kHz without count drops.

Signed-off-by: Oleksij Rempel 
Reviewed-by: Ahmad Fatoum 
---
 MAINTAINERS |   7 +
 drivers/counter/Kconfig |  10 ++
 drivers/counter/Makefile|   1 +
 drivers/counter/interrupt-cnt.c | 244 
 4 files changed, 262 insertions(+)
 create mode 100644 drivers/counter/interrupt-cnt.c

diff --git a/MAINTAINERS b/MAINTAINERS
index a50a543e3c81..ad0a4455afec 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -9217,6 +9217,13 @@ F:   include/dt-bindings/interconnect/
 F: include/linux/interconnect-provider.h
 F: include/linux/interconnect.h
 
+INTERRUPT COUNTER DRIVER
+M: Oleksij Rempel 
+R: Pengutronix Kernel Team 
+L: linux-...@vger.kernel.org
+F: Documentation/devicetree/bindings/counter/interrupt-counter.yaml
+F: drivers/counter/interrupt-cnt.c
+
 INVENSENSE ICM-426xx IMU DRIVER
 M: Jean-Baptiste Maneyrol 
 L: linux-...@vger.kernel.org
diff --git a/drivers/counter/Kconfig b/drivers/counter/Kconfig
index 2de53ab0dd25..dcad13229134 100644
--- a/drivers/counter/Kconfig
+++ b/drivers/counter/Kconfig
@@ -29,6 +29,16 @@ config 104_QUAD_8
  The base port addresses for the devices may be configured via the base
  array module parameter.
 
+config INTERRUPT_CNT
+   tristate "Interrupt counter driver"
+   depends on GPIOLIB
+   help
+ Select this option to enable interrupt counter driver. Any interrupt
+ source can be used by this driver as the event source.
+
+ To compile this driver as a module, choose M here: the
+ module will be called interrupt-cnt.
+
 config STM32_TIMER_CNT
tristate "STM32 Timer encoder counter driver"
depends on MFD_STM32_TIMERS || COMPILE_TEST
diff --git a/drivers/counter/Makefile b/drivers/counter/Makefile
index 0a393f71e481..cb646ed2f039 100644
--- a/drivers/counter/Makefile
+++ b/drivers/counter/Makefile
@@ -6,6 +6,7 @@
 obj-$(CONFIG_COUNTER) += counter.o
 
 obj-$(CONFIG_104_QUAD_8)   += 104-quad-8.o
+obj-$(CONFIG_INTERRUPT_CNT)+= interrupt-cnt.o
 obj-$(CONFIG_STM32_TIMER_CNT)  += stm32-timer-cnt.o
 obj-$(CONFIG_STM32_LPTIMER_CNT)+= stm32-lptimer-cnt.o
 obj-$(CONFIG_TI_EQEP)  += ti-eqep.o
diff --git a/drivers/counter/interrupt-cnt.c b/drivers/counter/interrupt-cnt.c
new file mode 100644
index ..a99ee7996977
--- /dev/null
+++ b/drivers/counter/interrupt-cnt.c
@@ -0,0 +1,244 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2021 Pengutronix, Oleksij Rempel 
+ */
+
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+
+#define INTERRUPT_CNT_NAME "interrupt-cnt"
+
+struct interrupt_cnt_priv {
+   atomic_t count;
+   struct counter_device counter;
+   struct gpio_desc *gpio;
+   int irq;
+   bool enabled;
+   struct counter_signal signals;
+   struct counter_synapse synapses;
+   struct counter_count cnts;
+};
+
+static irqreturn_t interrupt_cnt_isr(int irq, void *dev_id)
+{
+   struct interrupt_cnt_priv *priv = dev_id;
+
+   atomic_inc(>count);
+
+   return IRQ_HANDLED;
+}
+
+static ssize_t interrupt_cnt_enable_read(struct counter_device *counter,
+struct counter_count *count,
+void *private, char *buf)
+{
+   struct interrupt_cnt_priv *priv = counter->priv;
+
+   return sysfs_emit(buf, "%d\n", priv->enabled);
+}
+
+static ssize_t interrupt_cnt_enable_write(struct counter_device *counter,
+ struct counter_count *count,
+ void *private, const char *buf,
+ size_t len)
+{
+   struct interrupt_cnt_priv *priv = counter->priv;
+   bool enable;
+   ssize_t ret;
+
+   ret = kstrtobool(buf, );
+   if (ret)
+   return ret;
+
+   if (priv->enabled == enable)
+   return len;
+
+   if (enable) {
+   priv->enabled = true;
+   enable_irq(priv->irq);
+   } else {
+   disable_irq(priv->irq);
+   priv->enabled = false;
+   }
+
+   return len;
+}
+
+static const struct counter_count_ext interrupt_cnt_ext[] = {
+   {
+   .name = "enable",
+   .read = interrupt_cnt_enable_read,
+   .write = interrupt_cnt_enable_write,
+   },
+};
+
+static enum counter_synapse_action interrupt_cnt_synapse_actionss[] = {
+   COUNTER_SYNAPSE_ACTION_RISING_EDGE,
+};
+
+static int interrupt_cnt_action_get(struct counter_device *counter,
+