Message ID | 20221118145512.509950-13-gsomlo@gmail.com |
---|---|
State | New |
Headers |
Return-Path: <linux-kernel-owner@vger.kernel.org> Delivered-To: ouuuleilei@gmail.com Received: by 2002:adf:f944:0:0:0:0:0 with SMTP id q4csp240371wrr; Fri, 18 Nov 2022 06:59:02 -0800 (PST) X-Google-Smtp-Source: AA0mqf7FnFRtCzwgFQG3YJKj3Jzea9czJqTnycEG6dsj3i3y4IbbdsRLtk2+rn5vKOD1hG2Lw46Z X-Received: by 2002:a17:906:b250:b0:7b2:86d5:8b14 with SMTP id ce16-20020a170906b25000b007b286d58b14mr6357046ejb.230.1668783542700; Fri, 18 Nov 2022 06:59:02 -0800 (PST) ARC-Seal: i=1; a=rsa-sha256; t=1668783542; cv=none; d=google.com; s=arc-20160816; b=Mt5/n6njn9BO/zUQmgShZyvCXww24B29CI5p9pFitVoBwekH7sY8I3NCOEPLT2iiN1 6iDCyoGsh3hmVXgMc12R2M9F72kGU4ZcVOIstSZVkz51woX620xlXPS6ce8F8CpWMz+d K4bSGxh2PzM1bcKpxHpeqhUw4AHgNfrB/CWxJ6XCiOvbNyK2WleZRbV7r7b9HAA9eBQN mm5IroeONj9XH3GIhyMrSaiHmB4FcHsbeEsEtvGkP8hb4sfIMvOm8Z7Y263zgNr3BqUN S9pqgdjeKKfA10V7DPzAkQmpeoR0lnjdHMH1tMLUAqT92gIJTHOUtME7I13gk6MH0XUE ctig== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=list-id:precedence:content-transfer-encoding:mime-version :references:in-reply-to:message-id:date:subject:cc:to:from :dkim-signature; bh=nrG3U4P8tnDM4SCWDYONLDPgsN83+RRu4eA0i7Q0lE8=; b=Ue0ZFTT0PAaf0f8DUzFKSGYa7qLovSRl3VXhSUfJNvgLIAb5CvyKYllnlIfkn48S5X ie2VQ4UcWoRnDtQDbZ/eHxYT7VB+Dj6vHHk1h1G1VBBfGbNg0F7K60o81ES67lKGBnB2 gue1wt0Qdj3F87Va74JQS0eMqeEuHWZ/X5j/rrCsttJq7D0Mt9zpfqTcnI+Zra4+SJI2 UDS2ffSvP9/HhndEgoV4WnWWRVgPdL7jXMe5hTy7cuvFGslRiGzXo4/MCwRtDb2SIvXv HMGk1KELtbL4mwL/mfhoCaMt3dKQKg67zk69j/WX//y7/vs+VACJo6YW3yr4Q9CXs84v 7+BA== ARC-Authentication-Results: i=1; mx.google.com; dkim=pass header.i=@gmail.com header.s=20210112 header.b=XPczYzmI; spf=pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 2620:137:e000::1:20 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=pass (p=NONE sp=QUARANTINE dis=NONE) header.from=gmail.com Received: from out1.vger.email (out1.vger.email. [2620:137:e000::1:20]) by mx.google.com with ESMTP id c17-20020aa7c751000000b0046329e2724dsi3147952eds.86.2022.11.18.06.58.37; Fri, 18 Nov 2022 06:59:02 -0800 (PST) Received-SPF: pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 2620:137:e000::1:20 as permitted sender) client-ip=2620:137:e000::1:20; Authentication-Results: mx.google.com; dkim=pass header.i=@gmail.com header.s=20210112 header.b=XPczYzmI; spf=pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 2620:137:e000::1:20 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=pass (p=NONE sp=QUARANTINE dis=NONE) header.from=gmail.com Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S242154AbiKRO5K (ORCPT <rfc822;kkmonlee@gmail.com> + 99 others); Fri, 18 Nov 2022 09:57:10 -0500 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:55316 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S242145AbiKRO4c (ORCPT <rfc822;linux-kernel@vger.kernel.org>); Fri, 18 Nov 2022 09:56:32 -0500 Received: from mail-qv1-xf2c.google.com (mail-qv1-xf2c.google.com [IPv6:2607:f8b0:4864:20::f2c]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 1BBE3903BB; Fri, 18 Nov 2022 06:55:37 -0800 (PST) Received: by mail-qv1-xf2c.google.com with SMTP id c8so3458991qvn.10; Fri, 18 Nov 2022 06:55:37 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20210112; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=nrG3U4P8tnDM4SCWDYONLDPgsN83+RRu4eA0i7Q0lE8=; b=XPczYzmI7T958Ohirzr9OISoe0OA3mMrj3KmiR1oHYoNd+2Kx7FemEYeiBT5GjGSFp w2ttPETiLoFs3ysNof7dvniSnfaGve6wHDb87qUt19UcyJgyJAP0R000bep5SlygwqTh o4M4sSk/JDdbpMPOY03kyHZ4AvsvotliHbbMCFuV7jDOuawRVSXyjBkyvbijF8FNn/UE /DsoF+rxWWEbdl/mZNaeDFBS4BSQlzbEEbtXegJ1bg+iFIKDJ7jxrv6Qoy7vESwV/q8k HflrZotVOg/JP6poLgjAYmrchvJmq7FGm+66MQt6b20IBWcERZdwt50oTmbh/V8nJmdS V40A== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=nrG3U4P8tnDM4SCWDYONLDPgsN83+RRu4eA0i7Q0lE8=; b=DMhzGwzUwSa25OueuLgY0bsoE0g3KjmlvD3C/CFdanhHmQnJyxCup+8HWz+1eBPhod jL5Hsb3YKmjDeXslHgfDk+yp6iTG91l+Qy/Pi4Da4GMdhJ1hOCtvSfGmKFCJDc9J7Owr T2QJNSsHf/UQC4vBUG/6nANWb2pTuYBDWfI/j40dODhLTyyNYucYrFsSRR5mMDpDB23n uzF4n6CgUDOhUWGEUoUjqyBLrW6ip88P7gEAF+po6PifKvZqCdrcYRYEdS00ISBe+P7U ekGmale+LPyPXk71Q7ntHsAU2eZjWcCQs3Y9P8+LNq5AeAoRq3RUpVkICF/82kXk7Eaa lMug== X-Gm-Message-State: ANoB5pk1kqkwSO6B6lBfrFPXbQrDl2zUnOgz7yNeF7dBvUaJDW3mos+k LTX0CaY9v6ULk+x8Jw6G94RhRA87OTpmKg== X-Received: by 2002:a05:6214:3984:b0:4bb:e31f:a56e with SMTP id ny4-20020a056214398400b004bbe31fa56emr6869841qvb.76.1668783335940; Fri, 18 Nov 2022 06:55:35 -0800 (PST) Received: from glsvmlin.ini.cmu.edu (GLSVMLIN.INI.CMU.EDU. [128.2.16.9]) by smtp.gmail.com with ESMTPSA id m125-20020a378a83000000b006cbc6e1478csm2397269qkd.57.2022.11.18.06.55.34 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 18 Nov 2022 06:55:34 -0800 (PST) From: Gabriel Somlo <gsomlo@gmail.com> To: linux-kernel@vger.kernel.org Cc: linux-serial@vger.kernel.org, gregkh@linuxfoundation.org, jirislaby@kernel.org, kgugala@antmicro.com, mholenko@antmicro.com, joel@jms.id.au, david.abdurachmanov@gmail.com, florent@enjoy-digital.fr, geert@linux-m68k.org, ilpo.jarvinen@linux.intel.com Subject: [PATCH v5 12/14] serial: liteuart: add IRQ support for the RX path Date: Fri, 18 Nov 2022 09:55:10 -0500 Message-Id: <20221118145512.509950-13-gsomlo@gmail.com> X-Mailer: git-send-email 2.38.1 In-Reply-To: <20221118145512.509950-1-gsomlo@gmail.com> References: <20221118145512.509950-1-gsomlo@gmail.com> MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit X-Spam-Status: No, score=-2.1 required=5.0 tests=BAYES_00,DKIM_SIGNED, DKIM_VALID,DKIM_VALID_AU,DKIM_VALID_EF,FREEMAIL_FROM, RCVD_IN_DNSWL_NONE,SPF_HELO_NONE,SPF_PASS autolearn=ham autolearn_force=no version=3.4.6 X-Spam-Checker-Version: SpamAssassin 3.4.6 (2021-04-09) on lindbergh.monkeyblade.net Precedence: bulk List-ID: <linux-kernel.vger.kernel.org> X-Mailing-List: linux-kernel@vger.kernel.org X-getmail-retrieved-from-mailbox: =?utf-8?q?INBOX?= X-GMAIL-THRID: =?utf-8?q?1749846372009089334?= X-GMAIL-MSGID: =?utf-8?q?1749846372009089334?= |
Series |
serial: liteuart: add IRQ support
|
|
Commit Message
Gabriel L. Somlo
Nov. 18, 2022, 2:55 p.m. UTC
Add support for IRQ-driven RX. Support for the TX path will be added in a separate commit. Signed-off-by: Gabriel Somlo <gsomlo@gmail.com> Reviewed-by: Ilpo Järvinen <ilpo.jarvinen@linux.intel.com> --- Changes from v4: - using dev_err() instead of a combo of pr_err() and pr_fmt() - dropped "get irq" comment in probe() > Changes from v3: > - add shadow irq register to support polling mode and avoid reading > hardware mmio irq register to learn which irq flags are enabled > - this also simplifies both liteuart_interrupt() and liteuart_startup() drivers/tty/serial/liteuart.c | 76 +++++++++++++++++++++++++++++++---- 1 file changed, 69 insertions(+), 7 deletions(-)
Comments
On 18. 11. 22, 15:55, Gabriel Somlo wrote: > Add support for IRQ-driven RX. Support for the TX path will be added > in a separate commit. > > Signed-off-by: Gabriel Somlo <gsomlo@gmail.com> > Reviewed-by: Ilpo Järvinen <ilpo.jarvinen@linux.intel.com> > --- > > Changes from v4: > - using dev_err() instead of a combo of pr_err() and pr_fmt() > - dropped "get irq" comment in probe() > >> Changes from v3: >> - add shadow irq register to support polling mode and avoid reading >> hardware mmio irq register to learn which irq flags are enabled >> - this also simplifies both liteuart_interrupt() and liteuart_startup() > > drivers/tty/serial/liteuart.c | 76 +++++++++++++++++++++++++++++++---- > 1 file changed, 69 insertions(+), 7 deletions(-) > > diff --git a/drivers/tty/serial/liteuart.c b/drivers/tty/serial/liteuart.c > index 8a6e176be08e..678c37c952cf 100644 > --- a/drivers/tty/serial/liteuart.c > +++ b/drivers/tty/serial/liteuart.c > @@ -7,6 +7,7 @@ > > #include <linux/bits.h> > #include <linux/console.h> > +#include <linux/interrupt.h> > #include <linux/litex.h> > #include <linux/module.h> > #include <linux/of.h> > @@ -46,6 +47,7 @@ struct liteuart_port { > struct uart_port port; > struct timer_list timer; > u32 id; > + u8 irq_reg; > }; > > #define to_liteuart_port(port) container_of(port, struct liteuart_port, port) > @@ -76,6 +78,19 @@ static void liteuart_putchar(struct uart_port *port, unsigned char ch) > litex_write8(port->membase + OFF_RXTX, ch); > } > > +static void liteuart_update_irq_reg(struct uart_port *port, bool set, u8 mask) > +{ > + struct liteuart_port *uart = to_liteuart_port(port); > + > + if (set) > + uart->irq_reg |= mask; > + else > + uart->irq_reg &= ~mask; > + > + if (port->irq) > + litex_write8(port->membase + OFF_EV_ENABLE, uart->irq_reg); > +} > + > static void liteuart_stop_tx(struct uart_port *port) > { > } > @@ -129,13 +144,27 @@ static void liteuart_rx_chars(struct uart_port *port) > tty_flip_buffer_push(&port->state->port); > } > > +static irqreturn_t liteuart_interrupt(int irq, void *data) > +{ > + struct liteuart_port *uart = data; > + struct uart_port *port = &uart->port; > + u8 isr; > + > + spin_lock(&port->lock); > + isr = litex_read8(port->membase + OFF_EV_PENDING) & uart->irq_reg; > + if (isr & EV_RX) > + liteuart_rx_chars(port); > + spin_unlock(&port->lock); > + > + return IRQ_RETVAL(isr); > +} > + > static void liteuart_timer(struct timer_list *t) > { > struct liteuart_port *uart = from_timer(uart, t, timer); > struct uart_port *port = &uart->port; > > - liteuart_rx_chars(port); > - > + liteuart_interrupt(0, port); Are you sure spin_lock() is safe from this path? I assume so, but have you thought about it? > mod_timer(&uart->timer, jiffies + uart_poll_timeout(port)); > } > > @@ -161,19 +190,46 @@ static unsigned int liteuart_get_mctrl(struct uart_port *port) > static int liteuart_startup(struct uart_port *port) > { > struct liteuart_port *uart = to_liteuart_port(port); > + unsigned long flags; > + int ret; > > - /* disable events */ > - litex_write8(port->membase + OFF_EV_ENABLE, 0); > + if (port->irq) { > + ret = request_irq(port->irq, liteuart_interrupt, 0, > + KBUILD_MODNAME, uart); Just asking: cannot the irq be shared? > + if (ret) { > + dev_err(port->dev, > + "line %d irq %d failed: switch to polling\n", > + port->line, port->irq); That is, it should be only dev_warn(), or? > + port->irq = 0; > + } > + } thanks,
Hi Jiri, On Mon, Nov 21, 2022 at 09:54:34AM +0100, Jiri Slaby wrote: > On 18. 11. 22, 15:55, Gabriel Somlo wrote: > > Add support for IRQ-driven RX. Support for the TX path will be added > > in a separate commit. > > > > Signed-off-by: Gabriel Somlo <gsomlo@gmail.com> > > Reviewed-by: Ilpo Järvinen <ilpo.jarvinen@linux.intel.com> > > --- > > > > Changes from v4: > > - using dev_err() instead of a combo of pr_err() and pr_fmt() > > - dropped "get irq" comment in probe() > > > > > Changes from v3: > > > - add shadow irq register to support polling mode and avoid reading > > > hardware mmio irq register to learn which irq flags are enabled > > > - this also simplifies both liteuart_interrupt() and liteuart_startup() > > > > drivers/tty/serial/liteuart.c | 76 +++++++++++++++++++++++++++++++---- > > 1 file changed, 69 insertions(+), 7 deletions(-) > > > > diff --git a/drivers/tty/serial/liteuart.c b/drivers/tty/serial/liteuart.c > > index 8a6e176be08e..678c37c952cf 100644 > > --- a/drivers/tty/serial/liteuart.c > > +++ b/drivers/tty/serial/liteuart.c > > @@ -7,6 +7,7 @@ > > #include <linux/bits.h> > > #include <linux/console.h> > > +#include <linux/interrupt.h> > > #include <linux/litex.h> > > #include <linux/module.h> > > #include <linux/of.h> > > @@ -46,6 +47,7 @@ struct liteuart_port { > > struct uart_port port; > > struct timer_list timer; > > u32 id; > > + u8 irq_reg; > > }; > > #define to_liteuart_port(port) container_of(port, struct liteuart_port, port) > > @@ -76,6 +78,19 @@ static void liteuart_putchar(struct uart_port *port, unsigned char ch) > > litex_write8(port->membase + OFF_RXTX, ch); > > } > > +static void liteuart_update_irq_reg(struct uart_port *port, bool set, u8 mask) > > +{ > > + struct liteuart_port *uart = to_liteuart_port(port); > > + > > + if (set) > > + uart->irq_reg |= mask; > > + else > > + uart->irq_reg &= ~mask; > > + > > + if (port->irq) > > + litex_write8(port->membase + OFF_EV_ENABLE, uart->irq_reg); > > +} > > + > > static void liteuart_stop_tx(struct uart_port *port) > > { > > } > > @@ -129,13 +144,27 @@ static void liteuart_rx_chars(struct uart_port *port) > > tty_flip_buffer_push(&port->state->port); > > } > > +static irqreturn_t liteuart_interrupt(int irq, void *data) > > +{ > > + struct liteuart_port *uart = data; > > + struct uart_port *port = &uart->port; > > + u8 isr; > > + > > + spin_lock(&port->lock); > > + isr = litex_read8(port->membase + OFF_EV_PENDING) & uart->irq_reg; > > + if (isr & EV_RX) > > + liteuart_rx_chars(port); > > + spin_unlock(&port->lock); > > + > > + return IRQ_RETVAL(isr); > > +} > > + > > static void liteuart_timer(struct timer_list *t) > > { > > struct liteuart_port *uart = from_timer(uart, t, timer); > > struct uart_port *port = &uart->port; > > - liteuart_rx_chars(port); > > - > > + liteuart_interrupt(0, port); > > Are you sure spin_lock() is safe from this path? I assume so, but have you > thought about it? I checked and at that point `in_serving_softirq()` is true. *However*, after studying spin_lock() and friends for a while, I'm not quite clear on whether that unequivocally translates to "yes, we're safe" :) As such, I'm inclined to switch to `spin_lock_irqsave()` and `spin_unlock_irqrestore()` even in the interrupt handler, which is explicitly stated to be "safe from any context": https://www.kernel.org/doc/html/v4.15/kernel-hacking/locking.html#cheat-sheet-for-locking The alternative could be to set `TIMER_IRQSAFE` in `timer_setup()`, but no other tty driver seems to be doing that, so I'd be a bit off the beaten path there... :) Please do let me know what you think about this, particularly if you consider going the spin_lock_irqsave-everywhere-just-to-be-safe route overkill... :) > > mod_timer(&uart->timer, jiffies + uart_poll_timeout(port)); > > } > > @@ -161,19 +190,46 @@ static unsigned int liteuart_get_mctrl(struct uart_port *port) > > static int liteuart_startup(struct uart_port *port) > > { > > struct liteuart_port *uart = to_liteuart_port(port); > > + unsigned long flags; > > + int ret; > > - /* disable events */ > > - litex_write8(port->membase + OFF_EV_ENABLE, 0); > > + if (port->irq) { > > + ret = request_irq(port->irq, liteuart_interrupt, 0, > > + KBUILD_MODNAME, uart); > > Just asking: cannot the irq be shared? Given the way LiteX gateware is currently generated, each irq-triggering device is given its own separate line. I don't think setting the IRQF_SHARED flag actually *hurts* anything (no difference in behavior while testing), but I don't think it's needed ATM. > > + if (ret) { > > + dev_err(port->dev, > > + "line %d irq %d failed: switch to polling\n", > > + port->line, port->irq); > > That is, it should be only dev_warn(), or? Makes sense, will use dev_warn() in v6. Please LMK what you think about spin_lock[_irqsave] (and IRQF_SHARED), and I'll send out v6 with all the necessary chances right after that. Thanks much, --Gabriel > > + port->irq = 0; > > + } > > + } > > thanks, > -- > js > suse labs >
On 21. 11. 22, 19:50, Gabriel L. Somlo wrote: >>> static void liteuart_timer(struct timer_list *t) >>> { >>> struct liteuart_port *uart = from_timer(uart, t, timer); >>> struct uart_port *port = &uart->port; >>> - liteuart_rx_chars(port); >>> - >>> + liteuart_interrupt(0, port); >> >> Are you sure spin_lock() is safe from this path? I assume so, but have you >> thought about it? > > I checked and at that point `in_serving_softirq()` is true. > > *However*, after studying spin_lock() and friends for a while, I'm > not quite clear on whether that unequivocally translates > to "yes, we're safe" :) Depends whether some hard irq context is grabbing the port lock too. If it does, it will spin forever waiting for this soft irq to finish (never happens as it was interrupted). > As such, I'm inclined to switch to `spin_lock_irqsave()` and > `spin_unlock_irqrestore()` even in the interrupt handler, which is > explicitly stated to be "safe from any context": > https://www.kernel.org/doc/html/v4.15/kernel-hacking/locking.html#cheat-sheet-for-locking > The alternative could be to set `TIMER_IRQSAFE` in `timer_setup()`, > but no other tty driver seems to be doing that, so I'd be a bit off > the beaten path there... :) Ah, no. > Please do let me know what you think about this, particularly if you > consider going the spin_lock_irqsave-everywhere-just-to-be-safe route > overkill... :) If you are unsure about the other contexts, irqsave/restore is the way to go. It can be lifted later, if someone investigates harder. thanks,
On Tue, Nov 22, 2022 at 8:44 AM Jiri Slaby <jirislaby@kernel.org> wrote: > On 21. 11. 22, 19:50, Gabriel L. Somlo wrote: > >>> static void liteuart_timer(struct timer_list *t) > >>> { > >>> struct liteuart_port *uart = from_timer(uart, t, timer); > >>> struct uart_port *port = &uart->port; > >>> - liteuart_rx_chars(port); > >>> - > >>> + liteuart_interrupt(0, port); > >> > >> Are you sure spin_lock() is safe from this path? I assume so, but have you > >> thought about it? > > > > I checked and at that point `in_serving_softirq()` is true. > > > > *However*, after studying spin_lock() and friends for a while, I'm > > not quite clear on whether that unequivocally translates > > to "yes, we're safe" :) > > Depends whether some hard irq context is grabbing the port lock too. If > it does, it will spin forever waiting for this soft irq to finish (never > happens as it was interrupted). > > > As such, I'm inclined to switch to `spin_lock_irqsave()` and > > `spin_unlock_irqrestore()` even in the interrupt handler, which is > > explicitly stated to be "safe from any context": > > https://www.kernel.org/doc/html/v4.15/kernel-hacking/locking.html#cheat-sheet-for-locking > > > > > The alternative could be to set `TIMER_IRQSAFE` in `timer_setup()`, > > but no other tty driver seems to be doing that, so I'd be a bit off > > the beaten path there... :) > > Ah, no. > > > Please do let me know what you think about this, particularly if you > > consider going the spin_lock_irqsave-everywhere-just-to-be-safe route > > overkill... :) > > If you are unsure about the other contexts, irqsave/restore is the way > to go. It can be lifted later, if someone investigates harder. Inside the interrupt handler, plain spin_lock() should be OK. Inside the timer handler, I think you need to use spin_lock_irqsave(), as e.g. liteuart_set_termios() (and lots of code in serial_core.c) already uses spin_lock_irqsave(). Besides, on non-SMP, spin_lock() doesn't do anything, so you need to rely on disabling the local interrupt. Gr{oetje,eeting}s, Geert -- Geert Uytterhoeven -- There's lots of Linux beyond ia32 -- geert@linux-m68k.org In personal conversations with technical people, I call myself a hacker. But when I'm talking to journalists I just say "programmer" or something like that. -- Linus Torvalds
On Tue, Nov 22, 2022 at 10:54:45AM +0100, Geert Uytterhoeven wrote: > On Tue, Nov 22, 2022 at 8:44 AM Jiri Slaby <jirislaby@kernel.org> wrote: > > On 21. 11. 22, 19:50, Gabriel L. Somlo wrote: > > >>> static void liteuart_timer(struct timer_list *t) > > >>> { > > >>> struct liteuart_port *uart = from_timer(uart, t, timer); > > >>> struct uart_port *port = &uart->port; > > >>> - liteuart_rx_chars(port); > > >>> - > > >>> + liteuart_interrupt(0, port); > > >> > > >> Are you sure spin_lock() is safe from this path? I assume so, but have you > > >> thought about it? > > > > > > I checked and at that point `in_serving_softirq()` is true. > > > > > > *However*, after studying spin_lock() and friends for a while, I'm > > > not quite clear on whether that unequivocally translates > > > to "yes, we're safe" :) > > > > Depends whether some hard irq context is grabbing the port lock too. If > > it does, it will spin forever waiting for this soft irq to finish (never > > happens as it was interrupted). > > > > > As such, I'm inclined to switch to `spin_lock_irqsave()` and > > > `spin_unlock_irqrestore()` even in the interrupt handler, which is > > > explicitly stated to be "safe from any context": > > > https://www.kernel.org/doc/html/v4.15/kernel-hacking/locking.html#cheat-sheet-for-locking > > > > > > > > > The alternative could be to set `TIMER_IRQSAFE` in `timer_setup()`, > > > but no other tty driver seems to be doing that, so I'd be a bit off > > > the beaten path there... :) > > > > Ah, no. > > > > > Please do let me know what you think about this, particularly if you > > > consider going the spin_lock_irqsave-everywhere-just-to-be-safe route > > > overkill... :) > > > > If you are unsure about the other contexts, irqsave/restore is the way > > to go. It can be lifted later, if someone investigates harder. > > Inside the interrupt handler, plain spin_lock() should be OK. > Inside the timer handler, I think you need to use spin_lock_irqsave(), > as e.g. liteuart_set_termios() (and lots of code in serial_core.c) > already uses spin_lock_irqsave(). > Besides, on non-SMP, spin_lock() doesn't do anything, so you need > to rely on disabling the local interrupt. Thanks Geert for the clarification! I could write two wrappers around the actual code doing the interrupt handler work, one with spin_lock() for the actual irq handler and another with spin_lock_irqsave() for the timer codepath. But to keep things simple I'm inclined to simply use spin_lock_irqsave() and add a comment saying it's because we need it when polling and it's not actually harmful when using IRQ. Thanks, --Gabriel > Gr{oetje,eeting}s, > > Geert > > -- > Geert Uytterhoeven -- There's lots of Linux beyond ia32 -- geert@linux-m68k.org > > In personal conversations with technical people, I call myself a hacker. But > when I'm talking to journalists I just say "programmer" or something like that. > -- Linus Torvalds
diff --git a/drivers/tty/serial/liteuart.c b/drivers/tty/serial/liteuart.c index 8a6e176be08e..678c37c952cf 100644 --- a/drivers/tty/serial/liteuart.c +++ b/drivers/tty/serial/liteuart.c @@ -7,6 +7,7 @@ #include <linux/bits.h> #include <linux/console.h> +#include <linux/interrupt.h> #include <linux/litex.h> #include <linux/module.h> #include <linux/of.h> @@ -46,6 +47,7 @@ struct liteuart_port { struct uart_port port; struct timer_list timer; u32 id; + u8 irq_reg; }; #define to_liteuart_port(port) container_of(port, struct liteuart_port, port) @@ -76,6 +78,19 @@ static void liteuart_putchar(struct uart_port *port, unsigned char ch) litex_write8(port->membase + OFF_RXTX, ch); } +static void liteuart_update_irq_reg(struct uart_port *port, bool set, u8 mask) +{ + struct liteuart_port *uart = to_liteuart_port(port); + + if (set) + uart->irq_reg |= mask; + else + uart->irq_reg &= ~mask; + + if (port->irq) + litex_write8(port->membase + OFF_EV_ENABLE, uart->irq_reg); +} + static void liteuart_stop_tx(struct uart_port *port) { } @@ -129,13 +144,27 @@ static void liteuart_rx_chars(struct uart_port *port) tty_flip_buffer_push(&port->state->port); } +static irqreturn_t liteuart_interrupt(int irq, void *data) +{ + struct liteuart_port *uart = data; + struct uart_port *port = &uart->port; + u8 isr; + + spin_lock(&port->lock); + isr = litex_read8(port->membase + OFF_EV_PENDING) & uart->irq_reg; + if (isr & EV_RX) + liteuart_rx_chars(port); + spin_unlock(&port->lock); + + return IRQ_RETVAL(isr); +} + static void liteuart_timer(struct timer_list *t) { struct liteuart_port *uart = from_timer(uart, t, timer); struct uart_port *port = &uart->port; - liteuart_rx_chars(port); - + liteuart_interrupt(0, port); mod_timer(&uart->timer, jiffies + uart_poll_timeout(port)); } @@ -161,19 +190,46 @@ static unsigned int liteuart_get_mctrl(struct uart_port *port) static int liteuart_startup(struct uart_port *port) { struct liteuart_port *uart = to_liteuart_port(port); + unsigned long flags; + int ret; - /* disable events */ - litex_write8(port->membase + OFF_EV_ENABLE, 0); + if (port->irq) { + ret = request_irq(port->irq, liteuart_interrupt, 0, + KBUILD_MODNAME, uart); + if (ret) { + dev_err(port->dev, + "line %d irq %d failed: switch to polling\n", + port->line, port->irq); + port->irq = 0; + } + } - /* prepare timer for polling */ - timer_setup(&uart->timer, liteuart_timer, 0); - mod_timer(&uart->timer, jiffies + uart_poll_timeout(port)); + spin_lock_irqsave(&port->lock, flags); + /* only enabling rx irqs during startup */ + liteuart_update_irq_reg(port, true, EV_RX); + spin_unlock_irqrestore(&port->lock, flags); + + if (!port->irq) { + timer_setup(&uart->timer, liteuart_timer, 0); + mod_timer(&uart->timer, jiffies + uart_poll_timeout(port)); + } return 0; } static void liteuart_shutdown(struct uart_port *port) { + struct liteuart_port *uart = to_liteuart_port(port); + unsigned long flags; + + spin_lock_irqsave(&port->lock, flags); + liteuart_update_irq_reg(port, false, EV_RX | EV_TX); + spin_unlock_irqrestore(&port->lock, flags); + + if (port->irq) + free_irq(port->irq, port); + else + del_timer_sync(&uart->timer); } static void liteuart_set_termios(struct uart_port *port, struct ktermios *new, @@ -262,6 +318,12 @@ static int liteuart_probe(struct platform_device *pdev) goto err_erase_id; } + ret = platform_get_irq_optional(pdev, 0); + if (ret < 0 && ret != -ENXIO) + return ret; + if (ret > 0) + port->irq = ret; + /* values not from device tree */ port->dev = &pdev->dev; port->iotype = UPIO_MEM;