diff options
Diffstat (limited to 'drivers/tty')
-rw-r--r-- | drivers/tty/serial/mvebu-uart.c | 131 |
1 files changed, 119 insertions, 12 deletions
diff --git a/drivers/tty/serial/mvebu-uart.c b/drivers/tty/serial/mvebu-uart.c index e52248ec2689..d0e749f6f052 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,38 @@ 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]); + devm_free_irq(port->dev, mvuart->irq[UART_RX_IRQ], + port); + return ret; + } } return 0; @@ -354,9 +416,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]) { + devm_free_irq(port->dev, mvuart->irq[UART_IRQ_SUM], port); + } else { + devm_free_irq(port->dev, mvuart->irq[UART_RX_IRQ], port); + devm_free_irq(port->dev, mvuart->irq[UART_TX_IRQ], port); + } } static int mvebu_uart_baud_rate_set(struct uart_port *port, unsigned int baud) @@ -661,15 +730,14 @@ static int uart_num_counter; 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 ret, id; + int ret, id, irq; - if (!reg || !irq) { - dev_err(&pdev->dev, "no registers/irq defined\n"); + if (!reg) { + dev_err(&pdev->dev, "no registers defined\n"); return -EINVAL; } @@ -700,7 +768,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; @@ -735,6 +808,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); |