From patchwork Fri Apr 15 21:55:50 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Marek Vasut X-Patchwork-Id: 562028 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id C0441C433EF for ; Fri, 15 Apr 2022 21:56:00 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S232844AbiDOV62 (ORCPT ); Fri, 15 Apr 2022 17:58:28 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:36000 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S232508AbiDOV62 (ORCPT ); Fri, 15 Apr 2022 17:58:28 -0400 Received: from phobos.denx.de (phobos.denx.de [IPv6:2a01:238:438b:c500:173d:9f52:ddab:ee01]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id A5F6E34BBF for ; Fri, 15 Apr 2022 14:55:58 -0700 (PDT) Received: from tr.lan (ip-86-49-12-201.net.upcbroadband.cz [86.49.12.201]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits)) (No client certificate requested) (Authenticated sender: marex@denx.de) by phobos.denx.de (Postfix) with ESMTPSA id 188CC83894; Fri, 15 Apr 2022 23:55:57 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=denx.de; s=phobos-20191101; t=1650059757; bh=Sc/9lPXXzFl+T7U301MThVZf3KxvcYzU6cB1RmoDD3w=; h=From:To:Cc:Subject:Date:From; b=E3BprDWp5iG05mvyyy15lY1SeOg7gw/zOgEuyL/dzTc02jflqaqx9j96uaTgkrkEj kkBPCkTgEsNsbUKQTXig9oczEkvwmc/tNJi2YnGA0lxDieeY0+f+VJ114mi89sexW/ noI+eQryJsFBNU8eBVFgWfwDVmDlWDTHT2HZ5GoKjSK4yEtNgedp4NAKJrLqcVzrGF 1g7rCqjaQo/epnUxhL3WjyywxIa8RfNdVbvcSz0hzLBG15MwHha1/XPT9hipHpDOFI tUoDsATHznr3EVBaJ0v/Kefar8Gow49r9qViXZu+We4V4jn6XAxW5CYJApKkcZyMRG jfLYBbGV/b8tQ== From: Marek Vasut To: linux-gpio@vger.kernel.org Cc: Marek Vasut , Alexandre Torgue , Fabien Dessenne , Linus Walleij , Marc Zyngier , Thomas Gleixner , linux-stm32@st-md-mailman.stormreply.com, linux-arm-kernel@lists.infradead.org Subject: [RFC][PATCH] irqchip/stm32: Retrigger hierarchy for LEVEL triggered IRQs in tasklet Date: Fri, 15 Apr 2022 23:55:50 +0200 Message-Id: <20220415215550.498381-1-marex@denx.de> X-Mailer: git-send-email 2.35.1 MIME-Version: 1.0 X-Virus-Scanned: clamav-milter 0.103.5 at phobos.denx.de X-Virus-Status: Clean Precedence: bulk List-ID: X-Mailing-List: linux-gpio@vger.kernel.org The current EOI handler for LEVEL triggered interrupts calls clk_enable(), register IO, clk_disable(). The clock manipulation requires locking which happens with IRQs disabled in clk_enable_lock(). Move the LEVEL IRQ test and retrigger into dedicated tasklet and schedule the tasklet every time a LEVEL IRQ triggers. This makes EOI fast for majority of IRQs on this platform again, since those are edge triggered IRQs, and LEVEL triggered IRQs are the exception. This also fixes the following splat found when using preempt-rt: ------------[ cut here ]------------ WARNING: CPU: 0 PID: 0 at kernel/locking/rtmutex.c:2040 __rt_mutex_trylock+0x37/0x62 Modules linked in: CPU: 0 PID: 0 Comm: swapper/0 Not tainted 5.10.109-rt65-stable-standard-00068-g6a5afc4b1217 #85 Hardware name: STM32 (Device Tree Support) [] (unwind_backtrace) from [] (show_stack+0xb/0xc) [] (show_stack) from [] (dump_stack+0x6f/0x84) [] (dump_stack) from [] (__warn+0x7f/0xa4) [] (__warn) from [] (warn_slowpath_fmt+0x3b/0x74) [] (warn_slowpath_fmt) from [] (__rt_mutex_trylock+0x37/0x62) [] (__rt_mutex_trylock) from [] (rt_spin_trylock+0x7/0x16) [] (rt_spin_trylock) from [] (clk_enable_lock+0xb/0x80) [] (clk_enable_lock) from [] (clk_core_enable_lock+0x9/0x18) [] (clk_core_enable_lock) from [] (stm32_gpio_get+0x11/0x24) [] (stm32_gpio_get) from [] (stm32_gpio_irq_trigger+0x1f/0x48) [] (stm32_gpio_irq_trigger) from [] (handle_fasteoi_irq+0x71/0xa8) [] (handle_fasteoi_irq) from [] (generic_handle_irq+0x19/0x22) [] (generic_handle_irq) from [] (__handle_domain_irq+0x55/0x64) [] (__handle_domain_irq) from [] (gic_handle_irq+0x53/0x64) [] (gic_handle_irq) from [] (__irq_svc+0x65/0xc0) Exception stack(0xc0e01f18 to 0xc0e01f60) 1f00: 0000300c 00000000 1f20: 0000300c c010ff01 00000000 00000000 c0e00000 c0e07714 00000001 c0e01f78 1f40: c0e07758 00000000 ef7cd0ff c0e01f68 c010554b c0105542 40000033 ffffffff [] (__irq_svc) from [] (arch_cpu_idle+0xc/0x1e) [] (arch_cpu_idle) from [] (default_idle_call+0x21/0x3c) [] (default_idle_call) from [] (do_idle+0xe3/0x1e4) [] (do_idle) from [] (cpu_startup_entry+0x13/0x14) [] (cpu_startup_entry) from [] (start_kernel+0x397/0x3d4) [] (start_kernel) from [<00000000>] (0x0) ---[ end trace 0000000000000002 ]--- Signed-off-by: Marek Vasut Cc: Alexandre Torgue Cc: Fabien Dessenne Cc: Linus Walleij Cc: Marc Zyngier Cc: Thomas Gleixner Cc: linux-stm32@st-md-mailman.stormreply.com Cc: linux-arm-kernel@lists.infradead.org To: linux-gpio@vger.kernel.org --- drivers/pinctrl/stm32/pinctrl-stm32.c | 55 +++++++++++++++++++++------ 1 file changed, 44 insertions(+), 11 deletions(-) diff --git a/drivers/pinctrl/stm32/pinctrl-stm32.c b/drivers/pinctrl/stm32/pinctrl-stm32.c index 242d1c37c6e4b..f4287fc18cf9a 100644 --- a/drivers/pinctrl/stm32/pinctrl-stm32.c +++ b/drivers/pinctrl/stm32/pinctrl-stm32.c @@ -10,6 +10,7 @@ #include #include #include +#include #include #include #include @@ -95,6 +96,7 @@ struct stm32_gpio_bank { u32 bank_ioport_nr; u32 pin_backup[STM32_GPIO_PINS_PER_BANK]; u8 irq_type[STM32_GPIO_PINS_PER_BANK]; + struct tasklet_struct tasklet; }; struct stm32_pinctrl { @@ -307,20 +309,43 @@ static const struct gpio_chip stm32_gpio_template = { .set_config = gpiochip_generic_config, }; +static void stm32_gpio_irq_tasklet(struct tasklet_struct *t) +{ + struct stm32_gpio_bank *bank = from_tasklet(bank, t, tasklet); + struct irq_desc *desc; + struct irq_data *data; + int irq, pin, level; + + /* Retrigger all LEVEL triggered pins which are still asserted. */ + for (pin = 0; pin < STM32_GPIO_PINS_PER_BANK; pin++) { + if (!(bank->irq_type[pin] & IRQ_TYPE_LEVEL_MASK)) + continue; + + level = stm32_gpio_get(&bank->gpio_chip, pin); + if ((level == 0 && bank->irq_type[pin] == IRQ_TYPE_LEVEL_LOW) || + (level == 1 && bank->irq_type[pin] == IRQ_TYPE_LEVEL_HIGH)) { + irq = irq_find_mapping(bank->domain, pin); + + desc = irq_to_desc(irq); + if (!desc) + continue; + + data = irq_desc_get_irq_data(desc); + if (!data) + continue; + + irq_chip_retrigger_hierarchy(data); + } + } +} + static void stm32_gpio_irq_trigger(struct irq_data *d) { struct stm32_gpio_bank *bank = d->domain->host_data; - int level; - - /* Do not access the GPIO if this is not LEVEL triggered IRQ. */ - if (!(bank->irq_type[d->hwirq] & IRQ_TYPE_LEVEL_MASK)) - return; - /* If level interrupt type then retrig */ - level = stm32_gpio_get(&bank->gpio_chip, d->hwirq); - if ((level == 0 && bank->irq_type[d->hwirq] == IRQ_TYPE_LEVEL_LOW) || - (level == 1 && bank->irq_type[d->hwirq] == IRQ_TYPE_LEVEL_HIGH)) - irq_chip_retrigger_hierarchy(d); + /* If this is LEVEL triggered interrupt, retrigger in tasklet. */ + if (bank->irq_type[d->hwirq] & IRQ_TYPE_LEVEL_MASK) + tasklet_schedule(&bank->tasklet); } static void stm32_gpio_irq_eoi(struct irq_data *d) @@ -450,6 +475,8 @@ static int stm32_gpio_domain_alloc(struct irq_domain *d, unsigned long flags; int ret = 0; + tasklet_setup(&bank->tasklet, stm32_gpio_irq_tasklet); + /* * Check first that the IRQ MUX of that line is free. * gpio irq mux is shared between several banks, protect with a lock @@ -475,7 +502,11 @@ static int stm32_gpio_domain_alloc(struct irq_domain *d, irq_domain_set_hwirq_and_chip(d, virq, hwirq, &stm32_gpio_irq_chip, bank); - return irq_domain_alloc_irqs_parent(d, virq, nr_irqs, &parent_fwspec); + ret = irq_domain_alloc_irqs_parent(d, virq, nr_irqs, &parent_fwspec); + if (ret) + tasklet_kill(&bank->tasklet); + + return ret; } static void stm32_gpio_domain_free(struct irq_domain *d, unsigned int virq, @@ -486,6 +517,8 @@ static void stm32_gpio_domain_free(struct irq_domain *d, unsigned int virq, struct irq_data *irq_data = irq_domain_get_irq_data(d, virq); unsigned long flags, hwirq = irq_data->hwirq; + tasklet_kill(&bank->tasklet); + irq_domain_free_irqs_common(d, virq, nr_irqs); spin_lock_irqsave(&pctl->irqmux_lock, flags);