diff options
-rw-r--r-- | drivers/tty/serial/stm32-usart.c | 37 | ||||
-rw-r--r-- | drivers/tty/serial/stm32-usart.h | 1 |
2 files changed, 34 insertions, 4 deletions
diff --git a/drivers/tty/serial/stm32-usart.c b/drivers/tty/serial/stm32-usart.c index cfdda35499e6..7fd192e1e15d 100644 --- a/drivers/tty/serial/stm32-usart.c +++ b/drivers/tty/serial/stm32-usart.c @@ -577,9 +577,12 @@ static irqreturn_t stm32_usart_interrupt(int irq, void *ptr) * rx errors in dma mode has to be handled ASAP to avoid overrun as the DMA request * line has been masked by HW and rx data are stacking in FIFO. */ - if (((sr & USART_SR_RXNE) && !stm32_usart_rx_dma_enabled(port)) || - ((sr & USART_SR_ERR_MASK) && stm32_usart_rx_dma_enabled(port))) - stm32_usart_receive_chars(port, false); + if (!stm32_port->throttled) { + if (((sr & USART_SR_RXNE) && !stm32_usart_rx_dma_enabled(port)) || + ((sr & USART_SR_ERR_MASK) && stm32_usart_rx_dma_enabled(port))) { + stm32_usart_receive_chars(port, false); + } + } if ((sr & USART_SR_TXE) && !(stm32_port->tx_ch)) { spin_lock(&port->lock); @@ -596,9 +599,11 @@ static irqreturn_t stm32_usart_interrupt(int irq, void *ptr) static irqreturn_t stm32_usart_threaded_interrupt(int irq, void *ptr) { struct uart_port *port = ptr; + struct stm32_port *stm32_port = to_stm32_port(port); /* Receiver timeout irq for DMA RX */ - stm32_usart_receive_chars(port, false); + if (!stm32_port->throttled) + stm32_usart_receive_chars(port, false); return IRQ_HANDLED; } @@ -711,10 +716,19 @@ static void stm32_usart_throttle(struct uart_port *port) unsigned long flags; spin_lock_irqsave(&port->lock, flags); + + /* + * Disable DMA request line if enabled, so the RX data gets queued into the FIFO. + * Hardware flow control is triggered when RX FIFO is full. + */ + if (stm32_usart_rx_dma_enabled(port)) + stm32_usart_clr_bits(port, ofs->cr3, USART_CR3_DMAR); + stm32_usart_clr_bits(port, ofs->cr1, stm32_port->cr1_irq); if (stm32_port->cr3_irq) stm32_usart_clr_bits(port, ofs->cr3, stm32_port->cr3_irq); + stm32_port->throttled = true; spin_unlock_irqrestore(&port->lock, flags); } @@ -730,6 +744,14 @@ static void stm32_usart_unthrottle(struct uart_port *port) if (stm32_port->cr3_irq) stm32_usart_set_bits(port, ofs->cr3, stm32_port->cr3_irq); + /* + * Switch back to DMA mode (re-enable DMA request line). + * Hardware flow control is stopped when FIFO is not full any more. + */ + if (stm32_port->rx_ch) + stm32_usart_set_bits(port, ofs->cr3, USART_CR3_DMAR); + + stm32_port->throttled = false; spin_unlock_irqrestore(&port->lock, flags); } @@ -775,6 +797,13 @@ static int stm32_usart_startup(struct uart_port *port) if (ofs->rqr != UNDEF_REG) writel_relaxed(USART_RQR_RXFRQ, port->membase + ofs->rqr); + /* + * DMA request line not re-enabled at resume when port is throttled. + * It will be re-enabled by unthrottle ops. + */ + if (!stm32_port->throttled) + stm32_usart_set_bits(port, ofs->cr3, USART_CR3_DMAR); + /* RX enabling */ val = stm32_port->cr1_irq | USART_CR1_RE | BIT(cfg->uart_enable_bit); stm32_usart_set_bits(port, ofs->cr1, val); diff --git a/drivers/tty/serial/stm32-usart.h b/drivers/tty/serial/stm32-usart.h index 53bcd032fce7..e23916bfbb60 100644 --- a/drivers/tty/serial/stm32-usart.h +++ b/drivers/tty/serial/stm32-usart.h @@ -265,6 +265,7 @@ struct stm32_port { u32 cr3_irq; /* USART_CR3_RXFTIE */ int last_res; bool tx_dma_busy; /* dma tx busy */ + bool throttled; /* port throttled */ bool hw_flow_control; bool swap; /* swap RX & TX pins */ bool fifoen; |