serial: stm32: fix a recursive locking in stm32_config_rs485
[platform/kernel/linux-rpi.git] / drivers / tty / serial / stm32-usart.c
index e8d7a7b..d096e55 100644 (file)
@@ -105,9 +105,7 @@ static int stm32_config_rs485(struct uart_port *port,
        struct stm32_usart_config *cfg = &stm32_port->info->cfg;
        u32 usartdiv, baud, cr1, cr3;
        bool over8;
-       unsigned long flags;
 
-       spin_lock_irqsave(&port->lock, flags);
        stm32_clr_bits(port, ofs->cr1, BIT(cfg->uart_enable_bit));
 
        port->rs485 = *rs485conf;
@@ -147,7 +145,6 @@ static int stm32_config_rs485(struct uart_port *port,
        }
 
        stm32_set_bits(port, ofs->cr1, BIT(cfg->uart_enable_bit));
-       spin_unlock_irqrestore(&port->lock, flags);
 
        return 0;
 }
@@ -194,8 +191,8 @@ static int stm32_pending_rx(struct uart_port *port, u32 *sr, int *last_res,
        return 0;
 }
 
-static unsigned long
-stm32_get_char(struct uart_port *port, u32 *sr, int *last_res)
+static unsigned long stm32_get_char(struct uart_port *port, u32 *sr,
+                                   int *last_res)
 {
        struct stm32_port *stm32_port = to_stm32_port(port);
        struct stm32_usart_offsets *ofs = &stm32_port->info->ofs;
@@ -205,10 +202,13 @@ stm32_get_char(struct uart_port *port, u32 *sr, int *last_res)
                c = stm32_port->rx_buf[RX_BUF_L - (*last_res)--];
                if ((*last_res) == 0)
                        *last_res = RX_BUF_L;
-               return c;
        } else {
-               return readl_relaxed(port->membase + ofs->rdr);
+               c = readl_relaxed(port->membase + ofs->rdr);
+               /* apply RDR data mask */
+               c &= stm32_port->rdr_mask;
        }
+
+       return c;
 }
 
 static void stm32_receive_chars(struct uart_port *port, bool threaded)
@@ -225,35 +225,51 @@ static void stm32_receive_chars(struct uart_port *port, bool threaded)
 
        while (stm32_pending_rx(port, &sr, &stm32_port->last_res, threaded)) {
                sr |= USART_SR_DUMMY_RX;
-               c = stm32_get_char(port, &sr, &stm32_port->last_res);
                flag = TTY_NORMAL;
-               port->icount.rx++;
 
+               /*
+                * Status bits has to be cleared before reading the RDR:
+                * In FIFO mode, reading the RDR will pop the next data
+                * (if any) along with its status bits into the SR.
+                * Not doing so leads to misalignement between RDR and SR,
+                * and clear status bits of the next rx data.
+                *
+                * Clear errors flags for stm32f7 and stm32h7 compatible
+                * devices. On stm32f4 compatible devices, the error bit is
+                * cleared by the sequence [read SR - read DR].
+                */
+               if ((sr & USART_SR_ERR_MASK) && ofs->icr != UNDEF_REG)
+                       stm32_clr_bits(port, ofs->icr, USART_ICR_ORECF |
+                                      USART_ICR_PECF | USART_ICR_FECF);
+
+               c = stm32_get_char(port, &sr, &stm32_port->last_res);
+               port->icount.rx++;
                if (sr & USART_SR_ERR_MASK) {
-                       if (sr & USART_SR_LBD) {
-                               port->icount.brk++;
-                               if (uart_handle_break(port))
-                                       continue;
-                       } else if (sr & USART_SR_ORE) {
-                               if (ofs->icr != UNDEF_REG)
-                                       writel_relaxed(USART_ICR_ORECF,
-                                                      port->membase +
-                                                      ofs->icr);
+                       if (sr & USART_SR_ORE) {
                                port->icount.overrun++;
                        } else if (sr & USART_SR_PE) {
                                port->icount.parity++;
                        } else if (sr & USART_SR_FE) {
-                               port->icount.frame++;
+                               /* Break detection if character is null */
+                               if (!c) {
+                                       port->icount.brk++;
+                                       if (uart_handle_break(port))
+                                               continue;
+                               } else {
+                                       port->icount.frame++;
+                               }
                        }
 
                        sr &= port->read_status_mask;
 
-                       if (sr & USART_SR_LBD)
-                               flag = TTY_BREAK;
-                       else if (sr & USART_SR_PE)
+                       if (sr & USART_SR_PE) {
                                flag = TTY_PARITY;
-                       else if (sr & USART_SR_FE)
-                               flag = TTY_FRAME;
+                       } else if (sr & USART_SR_FE) {
+                               if (!c)
+                                       flag = TTY_BREAK;
+                               else
+                                       flag = TTY_FRAME;
+                       }
                }
 
                if (uart_handle_sysrq_char(port, c))
@@ -271,21 +287,6 @@ static void stm32_tx_dma_complete(void *arg)
        struct uart_port *port = arg;
        struct stm32_port *stm32port = to_stm32_port(port);
        struct stm32_usart_offsets *ofs = &stm32port->info->ofs;
-       unsigned int isr;
-       int ret;
-
-       ret = readl_relaxed_poll_timeout_atomic(port->membase + ofs->isr,
-                                               isr,
-                                               (isr & USART_SR_TC),
-                                               10, 100000);
-
-       if (ret)
-               dev_err(port->dev, "terminal count not set\n");
-
-       if (ofs->icr == UNDEF_REG)
-               stm32_clr_bits(port, ofs->isr, USART_SR_TC);
-       else
-               stm32_set_bits(port, ofs->icr, USART_CR_TC);
 
        stm32_clr_bits(port, ofs->cr3, USART_CR3_DMAT);
        stm32port->tx_dma_busy = false;
@@ -377,7 +378,6 @@ static void stm32_transmit_chars_dma(struct uart_port *port)
        /* Issue pending DMA TX requests */
        dma_async_issue_pending(stm32port->tx_ch);
 
-       stm32_clr_bits(port, ofs->isr, USART_SR_TC);
        stm32_set_bits(port, ofs->cr3, USART_CR3_DMAT);
 
        xmit->tail = (xmit->tail + count) & (UART_XMIT_SIZE - 1);
@@ -401,15 +401,15 @@ static void stm32_transmit_chars(struct uart_port *port)
                return;
        }
 
-       if (uart_tx_stopped(port)) {
-               stm32_stop_tx(port);
+       if (uart_circ_empty(xmit) || uart_tx_stopped(port)) {
+               stm32_clr_bits(port, ofs->cr1, USART_CR1_TXEIE);
                return;
        }
 
-       if (uart_circ_empty(xmit)) {
-               stm32_stop_tx(port);
-               return;
-       }
+       if (ofs->icr == UNDEF_REG)
+               stm32_clr_bits(port, ofs->isr, USART_SR_TC);
+       else
+               stm32_set_bits(port, ofs->icr, USART_ICR_TCCF);
 
        if (stm32_port->tx_ch)
                stm32_transmit_chars_dma(port);
@@ -420,7 +420,7 @@ static void stm32_transmit_chars(struct uart_port *port)
                uart_write_wakeup(port);
 
        if (uart_circ_empty(xmit))
-               stm32_stop_tx(port);
+               stm32_clr_bits(port, ofs->cr1, USART_CR1_TXEIE);
 }
 
 static irqreturn_t stm32_interrupt(int irq, void *ptr)
@@ -554,7 +554,6 @@ static int stm32_startup(struct uart_port *port)
 {
        struct stm32_port *stm32_port = to_stm32_port(port);
        struct stm32_usart_offsets *ofs = &stm32_port->info->ofs;
-       struct stm32_usart_config *cfg = &stm32_port->info->cfg;
        const char *name = to_platform_device(port->dev)->name;
        u32 val;
        int ret;
@@ -565,15 +564,6 @@ static int stm32_startup(struct uart_port *port)
        if (ret)
                return ret;
 
-       if (cfg->has_wakeup && stm32_port->wakeirq >= 0) {
-               ret = dev_pm_set_dedicated_wake_irq(port->dev,
-                                                   stm32_port->wakeirq);
-               if (ret) {
-                       free_irq(port->irq, port);
-                       return ret;
-               }
-       }
-
        val = USART_CR1_RXNEIE | USART_CR1_TE | USART_CR1_RE;
        if (stm32_port->fifoen)
                val |= USART_CR1_FIFOEN;
@@ -587,18 +577,56 @@ static void stm32_shutdown(struct uart_port *port)
        struct stm32_port *stm32_port = to_stm32_port(port);
        struct stm32_usart_offsets *ofs = &stm32_port->info->ofs;
        struct stm32_usart_config *cfg = &stm32_port->info->cfg;
-       u32 val;
+       u32 val, isr;
+       int ret;
 
        val = USART_CR1_TXEIE | USART_CR1_RXNEIE | USART_CR1_TE | USART_CR1_RE;
        val |= BIT(cfg->uart_enable_bit);
        if (stm32_port->fifoen)
                val |= USART_CR1_FIFOEN;
+
+       ret = readl_relaxed_poll_timeout(port->membase + ofs->isr,
+                                        isr, (isr & USART_SR_TC),
+                                        10, 100000);
+
+       if (ret)
+               dev_err(port->dev, "transmission complete not set\n");
+
        stm32_clr_bits(port, ofs->cr1, val);
 
-       dev_pm_clear_wake_irq(port->dev);
        free_irq(port->irq, port);
 }
 
+unsigned int stm32_get_databits(struct ktermios *termios)
+{
+       unsigned int bits;
+
+       tcflag_t cflag = termios->c_cflag;
+
+       switch (cflag & CSIZE) {
+       /*
+        * CSIZE settings are not necessarily supported in hardware.
+        * CSIZE unsupported configurations are handled here to set word length
+        * to 8 bits word as default configuration and to print debug message.
+        */
+       case CS5:
+               bits = 5;
+               break;
+       case CS6:
+               bits = 6;
+               break;
+       case CS7:
+               bits = 7;
+               break;
+       /* default including CS8 */
+       default:
+               bits = 8;
+               break;
+       }
+
+       return bits;
+}
+
 static void stm32_set_termios(struct uart_port *port, struct ktermios *termios,
                            struct ktermios *old)
 {
@@ -606,7 +634,7 @@ static void stm32_set_termios(struct uart_port *port, struct ktermios *termios,
        struct stm32_usart_offsets *ofs = &stm32_port->info->ofs;
        struct stm32_usart_config *cfg = &stm32_port->info->cfg;
        struct serial_rs485 *rs485conf = &port->rs485;
-       unsigned int baud;
+       unsigned int baud, bits;
        u32 usartdiv, mantissa, fraction, oversampling;
        tcflag_t cflag = termios->c_cflag;
        u32 cr1, cr2, cr3;
@@ -632,16 +660,29 @@ static void stm32_set_termios(struct uart_port *port, struct ktermios *termios,
        if (cflag & CSTOPB)
                cr2 |= USART_CR2_STOP_2B;
 
+       bits = stm32_get_databits(termios);
+       stm32_port->rdr_mask = (BIT(bits) - 1);
+
        if (cflag & PARENB) {
+               bits++;
                cr1 |= USART_CR1_PCE;
-               if ((cflag & CSIZE) == CS8) {
-                       if (cfg->has_7bits_data)
-                               cr1 |= USART_CR1_M0;
-                       else
-                               cr1 |= USART_CR1_M;
-               }
        }
 
+       /*
+        * Word length configuration:
+        * CS8 + parity, 9 bits word aka [M1:M0] = 0b01
+        * CS7 or (CS6 + parity), 7 bits word aka [M1:M0] = 0b10
+        * CS8 or (CS7 + parity), 8 bits word aka [M1:M0] = 0b00
+        * M0 and M1 already cleared by cr1 initialization.
+        */
+       if (bits == 9)
+               cr1 |= USART_CR1_M0;
+       else if ((bits == 7) && cfg->has_7bits_data)
+               cr1 |= USART_CR1_M1;
+       else if (bits != 8)
+               dev_dbg(port->dev, "Unsupported data bits config: %u bits\n"
+                       , bits);
+
        if (cflag & PARODD)
                cr1 |= USART_CR1_PS;
 
@@ -679,14 +720,14 @@ static void stm32_set_termios(struct uart_port *port, struct ktermios *termios,
        if (termios->c_iflag & INPCK)
                port->read_status_mask |= USART_SR_PE | USART_SR_FE;
        if (termios->c_iflag & (IGNBRK | BRKINT | PARMRK))
-               port->read_status_mask |= USART_SR_LBD;
+               port->read_status_mask |= USART_SR_FE;
 
        /* Characters to ignore */
        port->ignore_status_mask = 0;
        if (termios->c_iflag & IGNPAR)
                port->ignore_status_mask = USART_SR_PE | USART_SR_FE;
        if (termios->c_iflag & IGNBRK) {
-               port->ignore_status_mask |= USART_SR_LBD;
+               port->ignore_status_mask |= USART_SR_FE;
                /*
                 * If we're ignoring parity and break indicators,
                 * ignore overruns too (for real raw support).
@@ -1024,11 +1065,18 @@ static int stm32_serial_probe(struct platform_device *pdev)
                ret = device_init_wakeup(&pdev->dev, true);
                if (ret)
                        goto err_uninit;
+
+               ret = dev_pm_set_dedicated_wake_irq(&pdev->dev,
+                                                   stm32port->wakeirq);
+               if (ret)
+                       goto err_nowup;
+
+               device_set_wakeup_enable(&pdev->dev, false);
        }
 
        ret = uart_add_one_port(&stm32_usart_driver, &stm32port->port);
        if (ret)
-               goto err_nowup;
+               goto err_wirq;
 
        ret = stm32_of_dma_rx_probe(stm32port, pdev);
        if (ret)
@@ -1042,6 +1090,10 @@ static int stm32_serial_probe(struct platform_device *pdev)
 
        return 0;
 
+err_wirq:
+       if (stm32port->info->cfg.has_wakeup && stm32port->wakeirq >= 0)
+               dev_pm_clear_wake_irq(&pdev->dev);
+
 err_nowup:
        if (stm32port->info->cfg.has_wakeup && stm32port->wakeirq >= 0)
                device_init_wakeup(&pdev->dev, false);
@@ -1079,8 +1131,10 @@ static int stm32_serial_remove(struct platform_device *pdev)
                                  TX_BUF_L, stm32_port->tx_buf,
                                  stm32_port->tx_dma_buf);
 
-       if (cfg->has_wakeup && stm32_port->wakeirq >= 0)
+       if (cfg->has_wakeup && stm32_port->wakeirq >= 0) {
+               dev_pm_clear_wake_irq(&pdev->dev);
                device_init_wakeup(&pdev->dev, false);
+       }
 
        clk_disable_unprepare(stm32_port->clk);