From patchwork Fri Oct 6 10:13:38 2017 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Miquel Raynal X-Patchwork-Id: 822326 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Authentication-Results: ozlabs.org; spf=none (mailfrom) smtp.mailfrom=vger.kernel.org (client-ip=209.132.180.67; helo=vger.kernel.org; envelope-from=linux-gpio-owner@vger.kernel.org; receiver=) Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by ozlabs.org (Postfix) with ESMTP id 3y7lqj62lNz9t4R for ; Fri, 6 Oct 2017 21:14:41 +1100 (AEDT) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1751945AbdJFKOT (ORCPT ); Fri, 6 Oct 2017 06:14:19 -0400 Received: from mail.free-electrons.com ([62.4.15.54]:50862 "EHLO mail.free-electrons.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1751907AbdJFKOQ (ORCPT ); Fri, 6 Oct 2017 06:14:16 -0400 Received: by mail.free-electrons.com (Postfix, from userid 110) id B0102209B3; Fri, 6 Oct 2017 12:14:13 +0200 (CEST) X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on mail.free-electrons.com X-Spam-Level: X-Spam-Status: No, score=-1.0 required=5.0 tests=ALL_TRUSTED,SHORTCIRCUIT shortcircuit=ham autolearn=disabled version=3.4.0 Received: from localhost.localdomain (LStLambert-657-1-97-87.w90-63.abo.wanadoo.fr [90.63.216.87]) by mail.free-electrons.com (Postfix) with ESMTPSA id 3FB3120909; Fri, 6 Oct 2017 12:14:13 +0200 (CEST) From: Miquel Raynal To: Greg Kroah-Hartman , Linus Walleij , Jason Cooper , Andrew Lunn , Gregory Clement , Sebastian Hesselbarth , Jiri Slaby , Catalin Marinas , Will Deacon Cc: linux-serial@vger.kernel.org, devicetree@vger.kernel.org, linux-arm-kernel@lists.infradead.org, linux-gpio@vger.kernel.org, Thomas Petazzoni , Antoine Tenart , Nadav Haklai , Wilson Ding , Miquel Raynal Subject: [PATCH 10/16] serial: mvebu-uart: dissociate RX and TX interrupts Date: Fri, 6 Oct 2017 12:13:38 +0200 Message-Id: <20171006101344.15590-11-miquel.raynal@free-electrons.com> X-Mailer: git-send-email 2.11.0 In-Reply-To: <20171006101344.15590-1-miquel.raynal@free-electrons.com> References: <20171006101344.15590-1-miquel.raynal@free-electrons.com> Sender: linux-gpio-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-gpio@vger.kernel.org While the standard UART port can use a single IRQ that 'sums' both RX and TX interrupts, the extended port cannot and has to use two different ISR, one for each direction. The standard port also has the hability to use two separate interrupts (one for each direction). The logic is then: either there is only one unnamed interrupt on the standard port and this interrupt must be used for both directions (this is legacy bindings); or all the interrupts must be described and named 'uart-sum' (if available), 'uart-rx', 'uart-tx' and two separate handlers for each direction will be used. Suggested-by: Allen Yan Signed-off-by: Miquel Raynal --- drivers/tty/serial/mvebu-uart.c | 129 ++++++++++++++++++++++++++++++++++++---- 1 file changed, 118 insertions(+), 11 deletions(-) diff --git a/drivers/tty/serial/mvebu-uart.c b/drivers/tty/serial/mvebu-uart.c index 46d10209637a..b52cbe8c0558 100644 --- a/drivers/tty/serial/mvebu-uart.c +++ b/drivers/tty/serial/mvebu-uart.c @@ -79,7 +79,16 @@ #define MVEBU_UART_TYPE "mvebu-uart" #define DRIVER_NAME "mvebu_serial" -/* Register offsets, different depending on the UART */ +enum { + /* Either there is only one summed IRQ... */ + UART_IRQ_SUM = 0, + /* ...or there are two separate IRQ for RX and TX */ + UART_RX_IRQ = 0, + UART_TX_IRQ, + UART_IRQ_COUNT +}; + +/* Diverging register offsets */ struct uart_regs_layout { unsigned int rbr; unsigned int tsh; @@ -106,6 +115,8 @@ struct mvebu_uart_driver_data { struct mvebu_uart { struct uart_port *port; struct clk *clk; + int irq[UART_IRQ_COUNT]; + unsigned char __iomem *nb; struct mvebu_uart_driver_data *data; }; @@ -313,9 +324,32 @@ static irqreturn_t mvebu_uart_isr(int irq, void *dev_id) unsigned int st = readl(port->membase + UART_STAT); if (st & (STAT_RX_RDY(port) | STAT_OVR_ERR | STAT_FRM_ERR | + STAT_BRK_DET)) + mvebu_uart_rx_chars(port, st); + + if (st & STAT_TX_RDY(port)) + mvebu_uart_tx_chars(port, st); + + return IRQ_HANDLED; +} + +static irqreturn_t mvebu_uart_rx_isr(int irq, void *dev_id) +{ + struct uart_port *port = (struct uart_port *)dev_id; + unsigned int st = readl(port->membase + UART_STAT); + + if (st & (STAT_RX_RDY(port) | STAT_OVR_ERR | STAT_FRM_ERR | STAT_BRK_DET)) mvebu_uart_rx_chars(port, st); + return IRQ_HANDLED; +} + +static irqreturn_t mvebu_uart_tx_isr(int irq, void *dev_id) +{ + struct uart_port *port = (struct uart_port *)dev_id; + unsigned int st = readl(port->membase + UART_STAT); + if (st & STAT_TX_RDY(port)) mvebu_uart_tx_chars(port, st); @@ -324,6 +358,7 @@ static irqreturn_t mvebu_uart_isr(int irq, void *dev_id) static int mvebu_uart_startup(struct uart_port *port) { + struct mvebu_uart *mvuart = to_mvuart(port); unsigned int ctl; int ret; @@ -342,11 +377,37 @@ static int mvebu_uart_startup(struct uart_port *port) ctl |= CTRL_RX_RDY_INT(port); writel(ctl, port->membase + UART_INTR(port)); - ret = request_irq(port->irq, mvebu_uart_isr, port->irqflags, - DRIVER_NAME, port); - if (ret) { - dev_err(port->dev, "failed to request irq\n"); - return ret; + if (!mvuart->irq[UART_TX_IRQ]) { + /* Old bindings with just one interrupt (UART0 only) */ + ret = devm_request_irq(port->dev, mvuart->irq[UART_IRQ_SUM], + mvebu_uart_isr, port->irqflags, + dev_name(port->dev), port); + if (ret) { + dev_err(port->dev, "unable to request IRQ %d\n", + mvuart->irq[UART_IRQ_SUM]); + return ret; + } + } else { + /* New bindings with an IRQ for RX and TX (both UART) */ + ret = devm_request_irq(port->dev, mvuart->irq[UART_RX_IRQ], + mvebu_uart_rx_isr, port->irqflags, + dev_name(port->dev), port); + if (ret) { + dev_err(port->dev, "unable to request IRQ %d\n", + mvuart->irq[UART_RX_IRQ]); + return ret; + } + + ret = devm_request_irq(port->dev, mvuart->irq[UART_TX_IRQ], + mvebu_uart_tx_isr, port->irqflags, + dev_name(port->dev), + port); + if (ret) { + dev_err(port->dev, "unable to request IRQ %d\n", + mvuart->irq[UART_TX_IRQ]); + free_irq(mvuart->irq[UART_RX_IRQ], port); + return ret; + } } return 0; @@ -354,9 +415,16 @@ static int mvebu_uart_startup(struct uart_port *port) static void mvebu_uart_shutdown(struct uart_port *port) { + struct mvebu_uart *mvuart = to_mvuart(port); + writel(0, port->membase + UART_INTR(port)); - free_irq(port->irq, port); + if (!mvuart->irq[UART_TX_IRQ]) { + free_irq(mvuart->irq[UART_IRQ_SUM], port); + } else { + free_irq(mvuart->irq[UART_RX_IRQ], port); + free_irq(mvuart->irq[UART_TX_IRQ], port); + } } static int mvebu_uart_baud_rate_set(struct uart_port *port, unsigned int baud) @@ -658,15 +726,15 @@ static const struct of_device_id mvebu_uart_of_match[]; static int mvebu_uart_probe(struct platform_device *pdev) { struct resource *reg = platform_get_resource(pdev, IORESOURCE_MEM, 0); - struct resource *irq = platform_get_resource(pdev, IORESOURCE_IRQ, 0); const struct of_device_id *match = of_match_device(mvebu_uart_of_match, &pdev->dev); struct uart_port *port; struct mvebu_uart *mvuart; + int irq; int ret; - if (!reg || !irq) { - dev_err(&pdev->dev, "no registers/irq defined\n"); + if (!reg) { + dev_err(&pdev->dev, "no registers defined\n"); return -EINVAL; } @@ -693,7 +761,12 @@ static int mvebu_uart_probe(struct platform_device *pdev) port->flags = UPF_FIXED_PORT; port->line = pdev->id; - port->irq = irq->start; + /* + * IRQ number is not stored in this structure because we may have two of + * them per port (RX and TX). Instead, use the driver UART structure + * array so called ->irq[]. + */ + port->irq = 0; port->irqflags = 0; port->mapbase = reg->start; @@ -728,6 +801,40 @@ static int mvebu_uart_probe(struct platform_device *pdev) port->uartclk = clk_get_rate(mvuart->clk); } + /* Manage interrupts */ + memset(mvuart->irq, 0, UART_IRQ_COUNT); + if (platform_irq_count(pdev) == 1) { + /* Old bindings: no name on the single unamed UART0 IRQ */ + irq = platform_get_irq(pdev, 0); + if (irq < 0) { + dev_err(&pdev->dev, "unable to get UART IRQ\n"); + return irq; + } + + mvuart->irq[UART_IRQ_SUM] = irq; + } else { + /* + * New bindings: named interrupts (RX, TX) for both UARTS, + * only make use of uart-rx and uart-tx interrupts, do not use + * uart-sum of UART0 port. + */ + irq = platform_get_irq_byname(pdev, "uart-rx"); + if (irq < 0) { + dev_err(&pdev->dev, "unable to get 'uart-rx' IRQ\n"); + return irq; + } + + mvuart->irq[UART_RX_IRQ] = irq; + + irq = platform_get_irq_byname(pdev, "uart-tx"); + if (irq < 0) { + dev_err(&pdev->dev, "unable to get 'uart-tx' IRQ\n"); + return irq; + } + + mvuart->irq[UART_TX_IRQ] = irq; + } + /* UART Soft Reset*/ writel(CTRL_SOFT_RST, port->membase + UART_CTRL(port)); udelay(1);