| From 23ee4a25661c33e6381d41e848a9060ed6d72845 Mon Sep 17 00:00:00 2001 |
| From: Johan Hovold <johan+linaro@kernel.org> |
| Date: Wed, 9 Oct 2024 16:51:05 +0200 |
| Subject: serial: qcom-geni: fix dma rx cancellation |
| |
| From: Johan Hovold <johan+linaro@kernel.org> |
| |
| commit 23ee4a25661c33e6381d41e848a9060ed6d72845 upstream. |
| |
| Make sure to wait for the DMA transfer to complete when cancelling the |
| rx command on stop_rx(). This specifically prevents the DMA completion |
| interrupt from firing after rx has been restarted, something which can |
| lead to an IOMMU fault and hosed rx when the interrupt handler unmaps |
| the DMA buffer for the new command: |
| |
| qcom_geni_serial 988000.serial: serial engine reports 0 RX bytes in! |
| arm-smmu 15000000.iommu: FSR = 00000402 [Format=2 TF], SID=0x563 |
| arm-smmu 15000000.iommu: FSYNR0 = 00210013 [S1CBNDX=33 WNR PLVL=3] |
| Bluetooth: hci0: command 0xfc00 tx timeout |
| Bluetooth: hci0: Reading QCA version information failed (-110) |
| |
| Also add the missing state machine reset which is needed in case |
| cancellation fails. |
| |
| Fixes: 2aaa43c70778 ("tty: serial: qcom-geni-serial: add support for serial engine DMA") |
| Cc: stable@vger.kernel.org # 6.3 |
| Cc: Bartosz Golaszewski <bartosz.golaszewski@linaro.org> |
| Signed-off-by: Johan Hovold <johan+linaro@kernel.org> |
| Link: https://lore.kernel.org/r/20241009145110.16847-5-johan+linaro@kernel.org |
| Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> |
| --- |
| drivers/tty/serial/qcom_geni_serial.c | 18 ++++++++++++++---- |
| 1 file changed, 14 insertions(+), 4 deletions(-) |
| |
| --- a/drivers/tty/serial/qcom_geni_serial.c |
| +++ b/drivers/tty/serial/qcom_geni_serial.c |
| @@ -775,17 +775,27 @@ static void qcom_geni_serial_start_rx_fi |
| static void qcom_geni_serial_stop_rx_dma(struct uart_port *uport) |
| { |
| struct qcom_geni_serial_port *port = to_dev_port(uport); |
| + bool done; |
| |
| if (!qcom_geni_serial_secondary_active(uport)) |
| return; |
| |
| geni_se_cancel_s_cmd(&port->se); |
| - qcom_geni_serial_poll_bit(uport, SE_GENI_S_IRQ_STATUS, |
| - S_CMD_CANCEL_EN, true); |
| - |
| - if (qcom_geni_serial_secondary_active(uport)) |
| + done = qcom_geni_serial_poll_bit(uport, SE_DMA_RX_IRQ_STAT, |
| + RX_EOT, true); |
| + if (done) { |
| + writel(RX_EOT | RX_DMA_DONE, |
| + uport->membase + SE_DMA_RX_IRQ_CLR); |
| + } else { |
| qcom_geni_serial_abort_rx(uport); |
| |
| + writel(1, uport->membase + SE_DMA_RX_FSM_RST); |
| + qcom_geni_serial_poll_bit(uport, SE_DMA_RX_IRQ_STAT, |
| + RX_RESET_DONE, true); |
| + writel(RX_RESET_DONE | RX_DMA_DONE, |
| + uport->membase + SE_DMA_RX_IRQ_CLR); |
| + } |
| + |
| if (port->rx_dma_addr) { |
| geni_se_rx_dma_unprep(&port->se, port->rx_dma_addr, |
| DMA_RX_BUF_SIZE); |