From 469daac0faff06209bc1d1390571b860d153a82b Mon Sep 17 00:00:00 2001 From: Yangbo Lu Date: Wed, 27 Sep 2017 10:33:47 +0800 Subject: [PATCH] tty: serial: support layerscape This is a integrated patch for layerscape uart support. Signed-off-by: Nikita Yushchenko Signed-off-by: Yuan Yao Signed-off-by: Stefan Agner Signed-off-by: Yangbo Lu --- drivers/tty/serial/fsl_lpuart.c | 66 ++++++++++++++++++++++++++++------------- 1 file changed, 46 insertions(+), 20 deletions(-) diff --git a/drivers/tty/serial/fsl_lpuart.c b/drivers/tty/serial/fsl_lpuart.c index 76103f2c..61453820 100644 --- a/drivers/tty/serial/fsl_lpuart.c +++ b/drivers/tty/serial/fsl_lpuart.c @@ -231,6 +231,8 @@ #define DEV_NAME "ttyLP" #define UART_NR 6 +static DECLARE_BITMAP(linemap, UART_NR); + struct lpuart_port { struct uart_port port; struct clk *clk; @@ -1348,6 +1350,18 @@ lpuart_set_termios(struct uart_port *port, struct ktermios *termios, /* ask the core to calculate the divisor */ baud = uart_get_baud_rate(port, termios, old, 50, port->uartclk / 16); + /* + * Need to update the Ring buffer length according to the selected + * baud rate and restart Rx DMA path. + * + * Since timer function acqures sport->port.lock, need to stop before + * acquring same lock because otherwise del_timer_sync() can deadlock. + */ + if (old && sport->lpuart_dma_rx_use) { + del_timer_sync(&sport->lpuart_timer); + lpuart_dma_rx_free(&sport->port); + } + spin_lock_irqsave(&sport->port.lock, flags); sport->port.read_status_mask = 0; @@ -1397,22 +1411,11 @@ lpuart_set_termios(struct uart_port *port, struct ktermios *termios, /* restore control register */ writeb(old_cr2, sport->port.membase + UARTCR2); - /* - * If new baud rate is set, we will also need to update the Ring buffer - * length according to the selected baud rate and restart Rx DMA path. - */ - if (old) { - if (sport->lpuart_dma_rx_use) { - del_timer_sync(&sport->lpuart_timer); - lpuart_dma_rx_free(&sport->port); - } - - if (sport->dma_rx_chan && !lpuart_start_rx_dma(sport)) { - sport->lpuart_dma_rx_use = true; + if (old && sport->lpuart_dma_rx_use) { + if (!lpuart_start_rx_dma(sport)) rx_dma_timer_init(sport); - } else { + else sport->lpuart_dma_rx_use = false; - } } spin_unlock_irqrestore(&sport->port.lock, flags); @@ -1640,6 +1643,13 @@ lpuart_console_write(struct console *co, const char *s, unsigned int count) { struct lpuart_port *sport = lpuart_ports[co->index]; unsigned char old_cr2, cr2; + unsigned long flags; + int locked = 1; + + if (sport->port.sysrq || oops_in_progress) + locked = spin_trylock_irqsave(&sport->port.lock, flags); + else + spin_lock_irqsave(&sport->port.lock, flags); /* first save CR2 and then disable interrupts */ cr2 = old_cr2 = readb(sport->port.membase + UARTCR2); @@ -1654,6 +1664,9 @@ lpuart_console_write(struct console *co, const char *s, unsigned int count) barrier(); writeb(old_cr2, sport->port.membase + UARTCR2); + + if (locked) + spin_unlock_irqrestore(&sport->port.lock, flags); } static void @@ -1661,6 +1674,13 @@ lpuart32_console_write(struct console *co, const char *s, unsigned int count) { struct lpuart_port *sport = lpuart_ports[co->index]; unsigned long old_cr, cr; + unsigned long flags; + int locked = 1; + + if (sport->port.sysrq || oops_in_progress) + locked = spin_trylock_irqsave(&sport->port.lock, flags); + else + spin_lock_irqsave(&sport->port.lock, flags); /* first save CR2 and then disable interrupts */ cr = old_cr = lpuart32_read(sport->port.membase + UARTCTRL); @@ -1675,6 +1695,9 @@ lpuart32_console_write(struct console *co, const char *s, unsigned int count) barrier(); lpuart32_write(old_cr, sport->port.membase + UARTCTRL); + + if (locked) + spin_unlock_irqrestore(&sport->port.lock, flags); } /* @@ -1899,9 +1922,13 @@ static int lpuart_probe(struct platform_device *pdev) ret = of_alias_get_id(np, "serial"); if (ret < 0) { - dev_err(&pdev->dev, "failed to get alias id, errno %d\n", ret); - return ret; + ret = find_first_zero_bit(linemap, UART_NR); + if (ret >= UART_NR) { + dev_err(&pdev->dev, "port line is full, add device failed\n"); + return ret; + } } + set_bit(ret, linemap); sport->port.line = ret; sport->lpuart32 = of_device_is_compatible(np, "fsl,ls1021a-lpuart"); @@ -1983,6 +2010,7 @@ static int lpuart_remove(struct platform_device *pdev) struct lpuart_port *sport = platform_get_drvdata(pdev); uart_remove_one_port(&lpuart_reg, &sport->port); + clear_bit(sport->port.line, linemap); clk_disable_unprepare(sport->clk); @@ -2067,12 +2095,10 @@ static int lpuart_resume(struct device *dev) if (sport->lpuart_dma_rx_use) { if (sport->port.irq_wake) { - if (!lpuart_start_rx_dma(sport)) { - sport->lpuart_dma_rx_use = true; + if (!lpuart_start_rx_dma(sport)) rx_dma_timer_init(sport); - } else { + else sport->lpuart_dma_rx_use = false; - } } } -- 2.14.1