From patchwork Fri Feb 26 09:08:29 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Oleksij Rempel X-Patchwork-Id: 12106183 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-16.7 required=3.0 tests=BAYES_00, HEADER_FROM_DIFFERENT_DOMAINS,INCLUDES_CR_TRAILER,INCLUDES_PATCH, MAILING_LIST_MULTI,SPF_HELO_NONE,SPF_PASS,URIBL_BLOCKED,USER_AGENT_GIT autolearn=unavailable autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id B338BC433E0 for ; Fri, 26 Feb 2021 09:12:13 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id 5887364EDC for ; Fri, 26 Feb 2021 09:12:13 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S229835AbhBZJLw (ORCPT ); Fri, 26 Feb 2021 04:11:52 -0500 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:39584 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S229537AbhBZJK4 (ORCPT ); Fri, 26 Feb 2021 04:10:56 -0500 Received: from metis.ext.pengutronix.de (metis.ext.pengutronix.de [IPv6:2001:67c:670:201:290:27ff:fe1d:cc33]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id D5FE4C061794 for ; Fri, 26 Feb 2021 01:08:41 -0800 (PST) Received: from dude.hi.pengutronix.de ([2001:67c:670:100:1d::7]) by metis.ext.pengutronix.de with esmtps (TLS1.3:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.92) (envelope-from ) id 1lFZ6u-00032k-Ex; Fri, 26 Feb 2021 10:08:32 +0100 Received: from ore by dude.hi.pengutronix.de with local (Exim 4.92) (envelope-from ) id 1lFZ6t-0002rI-RW; Fri, 26 Feb 2021 10:08:31 +0100 From: Oleksij Rempel To: Rob Herring , William Breathitt Gray Cc: Oleksij Rempel , Linus Walleij , devicetree@vger.kernel.org, linux-kernel@vger.kernel.org, Pengutronix Kernel Team , David Jander , Robin van der Gracht , linux-iio@vger.kernel.org, Jonathan Cameron Subject: [PATCH v7 1/2] dt-bindings: counter: add interrupt-counter binding Date: Fri, 26 Feb 2021 10:08:29 +0100 Message-Id: <20210226090830.10927-2-o.rempel@pengutronix.de> X-Mailer: git-send-email 2.29.2 In-Reply-To: <20210226090830.10927-1-o.rempel@pengutronix.de> References: <20210226090830.10927-1-o.rempel@pengutronix.de> MIME-Version: 1.0 X-SA-Exim-Connect-IP: 2001:67c:670:100:1d::7 X-SA-Exim-Mail-From: ore@pengutronix.de X-SA-Exim-Scanned: No (on metis.ext.pengutronix.de); SAEximRunCond expanded to false X-PTX-Original-Recipient: linux-iio@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-iio@vger.kernel.org Add binding for the interrupt counter node Signed-off-by: Oleksij Rempel Reviewed-by: Linus Walleij --- .../bindings/counter/interrupt-counter.yaml | 62 +++++++++++++++++++ 1 file changed, 62 insertions(+) create mode 100644 Documentation/devicetree/bindings/counter/interrupt-counter.yaml diff --git a/Documentation/devicetree/bindings/counter/interrupt-counter.yaml b/Documentation/devicetree/bindings/counter/interrupt-counter.yaml new file mode 100644 index 000000000000..fd075d104631 --- /dev/null +++ b/Documentation/devicetree/bindings/counter/interrupt-counter.yaml @@ -0,0 +1,62 @@ +# SPDX-License-Identifier: GPL-2.0 +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/counter/interrupt-counter.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Interrupt counter + +maintainers: + - Oleksij Rempel + +description: | + A generic interrupt counter to measure interrupt frequency. It was developed + and used for agricultural devices to measure rotation speed of wheels or + other tools. Since the direction of rotation is not important, only one + signal line is needed. + Interrupts or gpios are required. If both are defined, the interrupt will + take precedence for counting interrupts. + +properties: + compatible: + const: interrupt-counter + + interrupts: + maxItems: 1 + + gpios: + maxItems: 1 + +required: + - compatible + +anyOf: + - required: [ interrupts-extended ] + - required: [ interrupts ] + - required: [ gpios ] + +additionalProperties: false + +examples: + - | + + #include + #include + + counter-0 { + compatible = "interrupt-counter"; + interrupts-extended = <&gpio 0 IRQ_TYPE_EDGE_RISING>; + }; + + counter-1 { + compatible = "interrupt-counter"; + gpios = <&gpio 2 GPIO_ACTIVE_HIGH>; + }; + + counter-2 { + compatible = "interrupt-counter"; + interrupts-extended = <&gpio 2 IRQ_TYPE_EDGE_RISING>; + gpios = <&gpio 2 GPIO_ACTIVE_HIGH>; + }; + +... From patchwork Fri Feb 26 09:08:30 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Oleksij Rempel X-Patchwork-Id: 12106179 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-16.7 required=3.0 tests=BAYES_00, HEADER_FROM_DIFFERENT_DOMAINS,INCLUDES_CR_TRAILER,INCLUDES_PATCH, MAILING_LIST_MULTI,SPF_HELO_NONE,SPF_PASS,URIBL_BLOCKED,USER_AGENT_GIT autolearn=unavailable autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id D6981C433E0 for ; Fri, 26 Feb 2021 09:11:36 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id 8469564EE7 for ; Fri, 26 Feb 2021 09:11:36 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S230144AbhBZJLR (ORCPT ); Fri, 26 Feb 2021 04:11:17 -0500 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:39404 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S229953AbhBZJKL (ORCPT ); Fri, 26 Feb 2021 04:10:11 -0500 Received: from metis.ext.pengutronix.de (metis.ext.pengutronix.de [IPv6:2001:67c:670:201:290:27ff:fe1d:cc33]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 5C602C06178B for ; Fri, 26 Feb 2021 01:08:39 -0800 (PST) Received: from dude.hi.pengutronix.de ([2001:67c:670:100:1d::7]) by metis.ext.pengutronix.de with esmtps (TLS1.3:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.92) (envelope-from ) id 1lFZ6u-00032l-Ew; Fri, 26 Feb 2021 10:08:32 +0100 Received: from ore by dude.hi.pengutronix.de with local (Exim 4.92) (envelope-from ) id 1lFZ6t-0002rR-SR; Fri, 26 Feb 2021 10:08:31 +0100 From: Oleksij Rempel To: Rob Herring , William Breathitt Gray Cc: Oleksij Rempel , Ahmad Fatoum , devicetree@vger.kernel.org, linux-kernel@vger.kernel.org, Pengutronix Kernel Team , David Jander , Robin van der Gracht , linux-iio@vger.kernel.org, Linus Walleij , Jonathan Cameron Subject: [PATCH v7 2/2] counter: add IRQ or GPIO based counter Date: Fri, 26 Feb 2021 10:08:30 +0100 Message-Id: <20210226090830.10927-3-o.rempel@pengutronix.de> X-Mailer: git-send-email 2.29.2 In-Reply-To: <20210226090830.10927-1-o.rempel@pengutronix.de> References: <20210226090830.10927-1-o.rempel@pengutronix.de> MIME-Version: 1.0 X-SA-Exim-Connect-IP: 2001:67c:670:100:1d::7 X-SA-Exim-Mail-From: ore@pengutronix.de X-SA-Exim-Scanned: No (on metis.ext.pengutronix.de); SAEximRunCond expanded to false X-PTX-Original-Recipient: linux-iio@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-iio@vger.kernel.org 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 | 243 ++++++++++++++++++++++++++++++++ 4 files changed, 261 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-iio@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-iio@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 000000000000..550383b6b591 --- /dev/null +++ b/drivers/counter/interrupt-cnt.c @@ -0,0 +1,243 @@ +// 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(&priv->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, &enable); + 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, + struct counter_count *count, + struct counter_synapse *synapse, + size_t *action) +{ + *action = interrupt_cnt_synapse_actionss[0]; + + return 0; +} + +static int interrupt_cnt_read(struct counter_device *counter, + struct counter_count *count, unsigned long *val) +{ + struct interrupt_cnt_priv *priv = counter->priv; + + *val = atomic_read(&priv->count); + + return 0; +} + +static int interrupt_cnt_write(struct counter_device *counter, + struct counter_count *count, + const unsigned long val) +{ + struct interrupt_cnt_priv *priv = counter->priv; + + atomic_set(&priv->count, val); + + return 0; +} + +static enum counter_count_function interrupt_cnt_functions[] = { + COUNTER_COUNT_FUNCTION_INCREASE, +}; + +static int interrupt_cnt_function_get(struct counter_device *counter, + struct counter_count *count, + size_t *function) +{ + *function = interrupt_cnt_functions[0]; + + return 0; +} + +static int interrupt_cnt_signal_read(struct counter_device *counter, + struct counter_signal *signal, + enum counter_signal_value *val) +{ + struct interrupt_cnt_priv *priv = counter->priv; + int ret; + + ret = gpiod_get_value(priv->gpio); + if (ret < 0) + return ret; + + *val = ret ? COUNTER_SIGNAL_HIGH : COUNTER_SIGNAL_LOW; + + return 0; +} + +static const struct counter_ops interrupt_cnt_ops = { + .action_get = interrupt_cnt_action_get, + .count_read = interrupt_cnt_read, + .count_write = interrupt_cnt_write, + .function_get = interrupt_cnt_function_get, + .signal_read = interrupt_cnt_signal_read, +}; + +static int interrupt_cnt_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct interrupt_cnt_priv *priv; + int ret; + + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + priv->irq = platform_get_irq_optional(pdev, 0); + if (priv->irq == -ENXIO) + priv->irq = 0; + else if (priv->irq < 0) + return dev_err_probe(dev, priv->irq, "failed to get IRQ\n"); + + priv->gpio = devm_gpiod_get_optional(dev, NULL, GPIOD_IN); + if (IS_ERR(priv->gpio)) + return dev_err_probe(dev, PTR_ERR(priv->gpio), "failed to get GPIO\n"); + + if (!priv->irq && !priv->gpio) { + dev_err(dev, "IRQ and GPIO are not found. At least one source should be provided\n"); + return -ENODEV; + } + + if (!priv->irq) { + int irq = gpiod_to_irq(priv->gpio); + + if (irq < 0) + return dev_err_probe(dev, irq, "failed to get IRQ from GPIO\n"); + + priv->irq = irq; + } + + if (priv->gpio) { + priv->signals.name = devm_kasprintf(dev, GFP_KERNEL, "IRQ %d", + priv->irq); + if (!priv->signals.name) + return -ENOMEM; + + priv->counter.signals = &priv->signals; + priv->counter.num_signals = 1; + } + + priv->synapses.actions_list = interrupt_cnt_synapse_actionss; + priv->synapses.num_actions = ARRAY_SIZE(interrupt_cnt_synapse_actionss); + priv->synapses.signal = &priv->signals; + + priv->cnts.name = "Channel 0 Count"; + priv->cnts.functions_list = interrupt_cnt_functions; + priv->cnts.num_functions = ARRAY_SIZE(interrupt_cnt_functions); + priv->cnts.synapses = &priv->synapses; + priv->cnts.num_synapses = 1; + priv->cnts.ext = interrupt_cnt_ext; + priv->cnts.num_ext = ARRAY_SIZE(interrupt_cnt_ext); + + priv->counter.priv = priv; + priv->counter.name = dev_name(dev); + priv->counter.parent = dev; + priv->counter.ops = &interrupt_cnt_ops; + priv->counter.counts = &priv->cnts; + priv->counter.num_counts = 1; + + irq_set_status_flags(priv->irq, IRQ_NOAUTOEN); + ret = devm_request_irq(dev, priv->irq, interrupt_cnt_isr, + IRQF_TRIGGER_RISING | IRQF_NO_THREAD, + dev_name(dev), priv); + if (ret) + return ret; + + return devm_counter_register(dev, &priv->counter); +} + +static const struct of_device_id interrupt_cnt_of_match[] = { + { .compatible = "interrupt-counter", }, + {} +}; +MODULE_DEVICE_TABLE(of, interrupt_cnt_of_match); + +static struct platform_driver interrupt_cnt_driver = { + .probe = interrupt_cnt_probe, + .driver = { + .name = INTERRUPT_CNT_NAME, + .of_match_table = interrupt_cnt_of_match, + }, +}; +module_platform_driver(interrupt_cnt_driver); + +MODULE_ALIAS("platform:interrupt-counter"); +MODULE_AUTHOR("Oleksij Rempel "); +MODULE_DESCRIPTION("Interrupt counter driver"); +MODULE_LICENSE("GPL v2");