From mboxrd@z Thu Jan 1 00:00:00 1970 Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.remote-ip=209.85.210.43 ARC-Seal: i=1; d=subspace.kernel.org; s=arc20261015; a=rsa-sha256; cv=none; t=1794196282; b=sQBhxD+joTZzqmQzyTzl/VlPmxyNs4FQ7d4xYCTD5nyeoC/59dKAg/cRjnDiFx4pImPu H3yPJz9iuGOsIa8rGZrxqIoueKVLHpG9qfiAJ6mHfzq3s4kePrgMKlJck+eRBIAvoFkUT Byww22CiWXU/Jt7FxwY5t6FkZqfB/SNbbFfHr1iNdNexeNqsVpaYzphHvqSbLM6WjcjF1 JP3/pdshtVVJXcXr7ODa44bHxd/KktOt8Jollk6SUSPxUnV2Lo4hJ+8yx6O5lXJPtj1W5 6qWchZ+eoIwq2MA9sdQQt/RYz4uYOAmRzCLXe7dbPr9QaeEAvpc4O0VLkwEPTpFLpWA== ARC-Message-Signature: i=1; d=subspace.kernel.org; s=arc20261015; a=rsa-sha256; c=relaxed/relaxed; t=1794196282; h=DMARC-Filter:Received:DKIM-Signature:X-Google-DKIM-Signature: X-Gm-Message-State:X-Gm-Gg:X-Received:Received:From:Date:Subject: MIME-Version:Content-Type:Content-Transfer-Encoding:Message-Id: References:In-Reply-To:To:Cc:X-Mailer; bh=4vFtdg3s5rVfzlce/+X3Usio5DzYXumKVb8+ZOgcBJY=; b=1T3IRoJx4SpLLebKqzmNkXFXNtMEYOPFYOG1nDmcIRe3d7WFIvjr4vEmNkM9SsL97xdD Jj9YSPH8lN6lifqL2550P5Q9IcBVmeVlsFZp0RLDX/ZacRtDUHtnSmiKPY6CWyotqS3Dc SNKPGVbCLFuB6B7sThSF/HqLdb/oOXJoQHNO5Yhk6kklr698Q2INmmP2a1Uh6kmjjKN/c RZhvkSvly+o6FbeWccUT3xPzS0V+abwdFh0chgHXcPifoSUJXElOuv3rT3Azwq/wvXvWO 4yjOjDEwLo2xs9SwoEok0sEByGdSHCXtok/1jbpGZDkLN87zcydXViouW9a2Yepteaw== ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass header.from=gmail.com; spf=pass smtp.mailfrom=gmail.com; dkim=pass header.d=gmail.com header.i=@gmail.com header.a=rsa-sha256 header.s=20260707 header.b=J8WT2pXp; arc=none smtp.remote-ip=209.85.210.43 Received: from mail-ot1-f43.google.com (mail-ot1-f43.google.com [209.85.210.43]) (using TLSv1.3 with cipher TLS_AES_128_GCM_SHA256 (128/128 bits) key-exchange X25519 server-signature ECDSA (prime256v1) server-digest SHA256) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id B4F99606A33C for ; Mon, 9 Nov 2026 03:51:21 +0000 (UTC) DMARC-Filter: OpenDMARC Filter v1.4.2 smtp.subspace.kernel.org B4F99606A33C Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=gmail.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.a=rsa-sha256 header.s=20260707 header.b=J8WT2pXp Received: by mail-ot1-f43.google.com with SMTP id 46e09a7af769-843b6f8e2c7so618524a34.1 for ; Sun, 08 Nov 2026 19:51:21 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20260707; t=1794196281; x=1794801081; darn=vger.kernel.org; h=cc:to:in-reply-to:references:message-id:content-transfer-encoding :content-type:mime-version:subject:date:from:from:to:cc:subject:date :message-id:reply-to:content-type; bh=4vFtdg3s5rVfzlce/+X3Usio5DzYXumKVb8+ZOgcBJY=; b=J8WT2pXp4/oH4fNhGJNITy7EGtXfNSPFmHHLwXBw4eN5A1FMGZsuo58JwTfDujUQ/Q YVBpMnM7uax5WCsHxBTu92OqGhqThuGd0wtNIOyL61U8/oAztvEHA8s1fCE9IChvc+0X WtD8O/9T08cgsHHfqDXbzWeoDSYnBsgehCNsxXRv6wJroF0f8vZJ28oJ4u6YScxzPiK6 Wr6EB7KGp9mVg1Gd2zWoCGxdpdDHPAbVqJUztT3gwps0r4vopDioiYWRwDjX9ARgSXIZ SO5CXtKGoXrBvt0HqXnhcNWCSXvjD3bVTs1LrDJbnFcBxQN/6UP57qpcxjqIMY2LKZj3 7aGg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20260707; t=1794196281; x=1794801081; h=cc:to:in-reply-to:references:message-id:content-transfer-encoding :content-type:mime-version:subject:date:from:x-gm-gg :x-gm-message-state:from:to:cc:subject:date:message-id:reply-to :content-type; bh=4vFtdg3s5rVfzlce/+X3Usio5DzYXumKVb8+ZOgcBJY=; b=E4/rtShzpw/fEpsao9uSHG5Ew0f6e3FWpsI0l9zy7KerMbAVmSJ1Jwg2P+8Q6HwXoN QFTkpLJF76EWuJsd6KsusxfckxXCZZ7VYl9adpE51K5f07KX3OBzY3RDkjzAeVb/EXFi ttJtx/YoCdgnBf4LPXK2u3HmHunf6FUwL4SbR623TLtsjAYQOY9TKMy3SbP3Dzr3wmNP mzn9iXPMZkabOmU4X+m3SuWs5o+kq8MzR9Vq9gHOCpmqzYSkDhLrzv3ADqMW7LVr70OX WHLXYV88BUP2/qCsAoM1wp/47UsFwcPWcQ9yLO5yep3/RfoNJjHYF9N/u5htat2esA+y NXlA== X-Gm-Message-State: APuU4Z8wUB6jr/VACcihIydFASofZvdkaaZzcFQyKtcCUiKpDxNnE196 36Pdyea/2kHCIRTtyARMRjG8tC1m2Pw+S77HqAzLqXH0kDrJR+pT6HQ8UedLrGIU X-Gm-Gg: AYBFou3GI2mb/CV+2NUlRDpvR1VWtRXf22RljZhscRQacLkbGeEIuR/TuKE/0EfLflx tcGJPGbzkFZsEfDWKseZZQD/5JFMeW8RNiEuh2s3NfpMFukLvMhssDqgFybZU1YwkxTmstzd967 qAaOCqxkOjcGY5eK2PPn82uZOcoMnxuB80xP7txTy6YyxEEJurGA0AcBUL7VrsY2TKXApcKASSN Q2+FLsAp1cMeH5acYB/p8txGVBMB50DZbDXx/9QmIj3XuR4Iu9ASaUL+GtFv/KzyeqZc+3yMIMK Y/doeVOYD7xn7tsb/u1X5hvYsoCqQD3ipxqBYh3ZwokDk19a+9r6l6qDNnO/A79E3gEWP0ahdQl 3heNeqHukMYTNrJeKobf9gL+r3R+1naOLkskEDlmIjbOewQi38juyuZoq1zimyysXdseZGh0m/R 1B9p/PAKO8fFVw+jv6Y+Zz2G81j2uis9UTfJTDblVkpCGTNH492iTkEPOuwqf49KuuFzjc52dPs 0U1W3fsHJArr8J1oFyMKjyMxpgBazI7o2ohRNS/m/BJXv4LToqFiBwwXEn9XvQXppdPk9nJBPyb m0/4m9OoXx1+fN5PDtsHeImd8DeEPlS5YHEcaNrHzc9mJzb+p2qNtEsbajCXUb7I73dSN0Kh6V0 0PEZlLgecbAecDA+lV7TPPjjZYKcp5Poc/Ee5MdixfUVrxxWTuUdxZ1L8ZGjWpPrAgVcN6iVuk9 AhGK+NXSxy2kHX/yXwBfGdZZ6xvpvOqNEVNTV81X+O2DNnV5tzJlsd X-Received: by 2002:a05:6830:25d1:b0:805:9b38:6f7c with SMTP id 46e09a7af769-85f507d8052mr2520136a34.29.1794196280630; Sun, 08 Nov 2026 19:51:20 -0800 (PST) Received: from [127.0.1.1] (174-29-1-49.hlrn.qwest.net. [174.29.1.49]) by smtp.gmail.com with ESMTPSA id 46e09a7af769-85f4fad32cdsm2800428a34.6.2026.11.08.19.51.19 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Sun, 08 Nov 2026 19:51:20 -0800 (PST) From: James Hilliard Date: Sun, 08 Nov 2026 20:50:15 -0700 Subject: [PATCH v10 6/8] serial: 8250: drain canceled TX DMA before reusing its buffer X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Post: List-Help: List-Subscribe: List-Unsubscribe: List-Owner: Precedence: list MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: 7bit Message-Id: <20261108-submit-serial-8250-dma-cancel-v1-v10-6-e6227ddb95b1@gmail.com> References: <20261108-submit-serial-8250-dma-cancel-v1-v10-0-e6227ddb95b1@gmail.com> In-Reply-To: <20261108-submit-serial-8250-dma-cancel-v1-v10-0-e6227ddb95b1@gmail.com> To: Greg Kroah-Hartman , Jiri Slaby , Raul E Rangel , =?utf-8?q?Ilpo_J=C3=A4rvinen?= , Andy Shevchenko , Heikki Krogerus , Tony Lindgren , Sebastian Andrzej Siewior , Richard Genoud , Nicolas Ferre , Alexandre Belloni , Claudiu Beznea , Jacky Huang , Shan-Chun Hung , =?utf-8?q?Pali_Roh=C3=A1r?= , Frank Li , Sascha Hauer , Pengutronix Kernel Team , Fabio Estevam , "Maciej W. Rozycki" , Timur Tabi , Peter Ujfalusi , Vignesh Raghavendra , Patrice Chotard Cc: linux-kernel@vger.kernel.org, linux-serial@vger.kernel.org, linux-arm-kernel@lists.infradead.org, imx@lists.linux.dev, linuxppc-dev@lists.ozlabs.org, James Hilliard X-Mailer: b4 0.15.2 uart_flush_buffer() resets the transmit kfifo before calling the driver. serial8250_tx_dma_flush() terminates DMA asynchronously and clears tx_running, allowing a new write to replace tx_size while a detached callback has not yet run. That callback can then consume bytes from the new transmission. Reusing the reset kfifo can also overwrite the source of a transfer that has not stopped yet. Reserve the flushed FIFO storage through serial core before returning from the flush callback. Keep tx_running set until a worker synchronizes the terminated channel, suppressing old callbacks and new submissions during that interval. Return the mapping to CPU ownership before releasing the reservation and waking writers. Normal transfers continue to use the FIFO directly. Reuse the locked completion processing after retirement, including OMAP delayed register restoration and transmitter-empty handling. Take a runtime PM reference around worker UART accesses and drain the worker before releasing the channels. Shutdown releases the FIFO reservation after stopping DMA and unmapping its source. Serial core keeps writes and wakeups gated until startup or resume makes the port ready again. Track a flushed FIFO separately from the transfer length. If termination fails, retain the reservation until normal completion and account the completed bytes without advancing the reset FIFO. After successful cancellation, process a zero-length completion once callbacks have drained. The race was found by inspecting cancellation and restart while preparing H616 UART DMA support. Retain the tx_running check in both completion callbacks. Releasing the reservation relies on the DMA provider draining detached callbacks when dmaengine_synchronize() returns; provider callback-lifetime fixes supply that guarantee for controllers which previously lacked it. Fixes: a424a34b8fad ("serial: 8250: Fix TX deadlock when using DMA") Reviewed-by: Raul E Rangel Signed-off-by: James Hilliard --- drivers/tty/serial/8250/8250.h | 20 +++++- drivers/tty/serial/8250/8250_dma.c | 130 ++++++++++++++++++++++++++---------- drivers/tty/serial/8250/8250_omap.c | 21 ++++-- 3 files changed, 125 insertions(+), 46 deletions(-) diff --git a/drivers/tty/serial/8250/8250.h b/drivers/tty/serial/8250/8250.h index 974a47f24084..1e739aceff39 100644 --- a/drivers/tty/serial/8250/8250.h +++ b/drivers/tty/serial/8250/8250.h @@ -11,6 +11,7 @@ #include #include #include +#include #include "../serial_mctrl_gpio.h" @@ -19,6 +20,10 @@ struct uart_8250_dma { int (*rx_dma)(struct uart_8250_port *p); void (*prepare_tx_dma)(struct uart_8250_port *p); void (*prepare_rx_dma)(struct uart_8250_port *p); + /* Completion processing with the port lock held. */ + void (*tx_complete)(struct uart_8250_port *p); + struct work_struct tx_flush_work; + struct uart_8250_port *port; /* Filter function */ dma_filter_fn fn; @@ -52,7 +57,9 @@ struct uart_8250_dma { unsigned char tx_err; unsigned char rx_running; /* Protected by the UART port lock. */ - bool shutting_down; + bool shutting_down:1; + bool tx_terminating:1; + bool tx_flushed:1; }; struct old_serial_port { @@ -400,8 +407,15 @@ static inline int is_omap1510_8250(struct uart_8250_port *pt) #endif #ifdef CONFIG_SERIAL_8250_DMA +static inline bool serial8250_tx_dma_blocked(struct uart_8250_dma *dma) +{ + return dma->shutting_down || dma->tx_terminating; +} + extern int serial8250_tx_dma(struct uart_8250_port *); extern void serial8250_tx_dma_flush(struct uart_8250_port *); +void serial8250_tx_dma_sync_buffer(struct uart_8250_port *p, + struct scatterlist *sgl, unsigned int nents); extern int serial8250_rx_dma(struct uart_8250_port *); extern void serial8250_rx_dma_flush(struct uart_8250_port *); extern int serial8250_request_dma(struct uart_8250_port *); @@ -434,7 +448,7 @@ static inline void serial8250_tx_dma_pause(struct uart_8250_port *p) { struct uart_8250_dma *dma = p->dma; - if (!dma->tx_running) + if (serial8250_tx_dma_blocked(dma) || !dma->tx_running) return; dmaengine_pause(dma->txchan); @@ -444,7 +458,7 @@ static inline void serial8250_tx_dma_resume(struct uart_8250_port *p) { struct uart_8250_dma *dma = p->dma; - if (!dma->tx_running) + if (serial8250_tx_dma_blocked(dma) || !dma->tx_running) return; dmaengine_resume(dma->txchan); diff --git a/drivers/tty/serial/8250/8250_dma.c b/drivers/tty/serial/8250/8250_dma.c index 43c55cb991a5..739a8238e4de 100644 --- a/drivers/tty/serial/8250/8250_dma.c +++ b/drivers/tty/serial/8250/8250_dma.c @@ -11,34 +11,79 @@ #include "8250.h" -static void __dma_tx_complete(void *param) +static void serial8250_tx_dma_complete(struct uart_8250_port *p) { - struct uart_8250_port *p = param; struct uart_8250_dma *dma = p->dma; - unsigned long flags; int ret; - uart_port_lock_irqsave(&p->port, &flags); - if (dma->shutting_down) - goto out; - dma_sync_single_for_cpu(dmaengine_get_dma_device(dma->txchan), dma->tx_addr, UART_XMIT_SIZE, DMA_TO_DEVICE); dma->tx_running = 0; - uart_xmit_advance(&p->port, dma->tx_size); + if (dma->tx_flushed) { + p->port.icount.tx += dma->tx_size; + uart_xmit_unblock(&p->port); + } else { + uart_xmit_advance(&p->port, dma->tx_size); + } uart_write_wakeup_if_needed(&p->port); ret = serial8250_tx_dma(p); if (ret || !dma->tx_running) serial8250_set_THRI(p); +} -out: - uart_port_unlock_irqrestore(&p->port, flags); +static void __dma_tx_complete(void *param) +{ + struct uart_8250_port *p = param; + struct uart_8250_dma *dma = p->dma; + + guard(uart_port_lock_irqsave)(&p->port); + + if (serial8250_tx_dma_blocked(dma) || !dma->tx_running) + return; + serial8250_tx_dma_complete(p); +} + +static void serial8250_tx_dma_flush_work(struct work_struct *work) +{ + struct uart_8250_dma *dma = container_of(work, struct uart_8250_dma, tx_flush_work); + struct uart_8250_port *p = dma->port; + + /* Neither the channel nor its buffer may be reused before this returns. */ + dmaengine_synchronize(dma->txchan); + + guard(serial8250_rpm)(p); + + guard(uart_port_lock_irqsave)(&p->port); + + dma->tx_terminating = false; + dma->tx_size = 0; + if (dma->shutting_down) + return; + dma->tx_complete(p); } +void serial8250_tx_dma_sync_buffer(struct uart_8250_port *p, + struct scatterlist *sgl, unsigned int nents) +{ + struct uart_8250_dma *dma = p->dma; + struct device *dev = dmaengine_get_dma_device(dma->txchan); + struct scatterlist *sg; + unsigned int i; + + /* DMA reads the FIFO directly; its occupied ranges remain reserved. */ + for_each_sg(sgl, sg, nents, i) { + size_t offset = sg_dma_address(sg) - dma->tx_addr; + + dma_sync_single_range_for_device(dev, dma->tx_addr, offset, + sg_dma_len(sg), DMA_TO_DEVICE); + } +} +EXPORT_SYMBOL_GPL(serial8250_tx_dma_sync_buffer); + static void __dma_rx_complete(struct uart_8250_port *p) { struct uart_8250_dma *dma = p->dma; @@ -99,7 +144,7 @@ int serial8250_tx_dma(struct uart_8250_port *p) int i; int ret; - if (dma->shutting_down) + if (serial8250_tx_dma_blocked(dma)) return 0; if (dma->tx_running) { @@ -129,24 +174,25 @@ int serial8250_tx_dma(struct uart_8250_port *p) dma->tx_size += sg_dma_len(sg); serial8250_do_prepare_tx_dma(p); + serial8250_tx_dma_sync_buffer(p, sgl, ret); desc = dmaengine_prep_slave_sg(dma->txchan, sgl, ret, DMA_MEM_TO_DEV, DMA_PREP_INTERRUPT | DMA_CTRL_ACK); if (!desc) { + dma_sync_single_for_cpu(dmaengine_get_dma_device(dma->txchan), dma->tx_addr, + UART_XMIT_SIZE, DMA_TO_DEVICE); ret = -EBUSY; goto err; } dma->tx_running = 1; + dma->tx_flushed = false; + dma->tx_complete = serial8250_tx_dma_complete; desc->callback = __dma_tx_complete; desc->callback_param = p; dma->tx_cookie = dmaengine_submit(desc); - - dma_sync_single_for_device(dmaengine_get_dma_device(dma->txchan), dma->tx_addr, - UART_XMIT_SIZE, DMA_TO_DEVICE); - dma_async_issue_pending(dma->txchan); serial8250_clear_THRI(p); dma->tx_err = 0; @@ -160,32 +206,31 @@ int serial8250_tx_dma(struct uart_8250_port *p) void serial8250_tx_dma_flush(struct uart_8250_port *p) { struct uart_8250_dma *dma = p->dma; + int ret; - if (!dma->tx_running) + if (serial8250_tx_dma_blocked(dma) || !dma->tx_running) return; /* * kfifo_reset() has been called by the serial core, avoid - * advancing and underflowing in __dma_tx_complete(). + * advancing the reset FIFO when the old transfer completes. */ - dma->tx_size = 0; + dma->tx_flushed = true; + /* The core reset the FIFO indices, but DMA may still read its storage. */ + uart_xmit_block(&p->port); /* * We can't use `dmaengine_terminate_sync` because `uart_flush_buffer` is * holding the uart port spinlock. */ - dmaengine_terminate_async(dma->txchan); - - /* - * The callback might or might not run. If it doesn't run, we need to ensure - * that `tx_running` is cleared so that we can schedule new transactions. - * If it does run, then the zombie callback will clear `tx_running` again - * and perform a no-op since `tx_size` was cleared above. - * - * In either case, we ASSUME the DMA transaction will terminate before we - * issue a new `serial8250_tx_dma`. - */ - dma->tx_running = 0; + dma->tx_terminating = true; + ret = dmaengine_terminate_async(dma->txchan); + if (ret) { + /* Keep the buffer owned by the transfer until its callback runs. */ + dma->tx_terminating = false; + return; + } + schedule_work(&dma->tx_flush_work); } int serial8250_rx_dma(struct uart_8250_port *p) @@ -310,21 +355,28 @@ int serial8250_request_dma(struct uart_8250_port *p) goto err; } - /* TX buffer */ + /* Normal TX uses serial core's FIFO; flush reserves it until retirement. */ dma->tx_addr = dma_map_single(tx_dev, p->port.state->port.xmit_buf, UART_XMIT_SIZE, DMA_TO_DEVICE); if (dma_mapping_error(tx_dev, dma->tx_addr)) { - dma_free_coherent(rx_dev, dma->rx_size, - dma->rx_buf, dma->rx_addr); ret = -ENOMEM; - goto err; + goto free_rx; } + dma_sync_single_for_cpu(tx_dev, dma->tx_addr, UART_XMIT_SIZE, DMA_TO_DEVICE); + + dma->port = p; + INIT_WORK(&dma->tx_flush_work, serial8250_tx_dma_flush_work); - scoped_guard(uart_port_lock_irqsave, &p->port) + scoped_guard(uart_port_lock_irqsave, &p->port) { + dma->tx_terminating = false; + dma->tx_flushed = false; dma->shutting_down = false; + } dev_dbg_ratelimited(p->port.dev, "got both dma channels\n"); return 0; +free_rx: + dma_free_coherent(rx_dev, dma->rx_size, dma->rx_buf, dma->rx_addr); err: dma_release_channel(dma->txchan); release_rx: @@ -340,6 +392,8 @@ void serial8250_release_dma(struct uart_8250_port *p) if (!dma) return; + cancel_work_sync(&dma->tx_flush_work); + /* Release RX resources */ dmaengine_terminate_sync(dma->rxchan); dma->rx_running = 0; @@ -354,7 +408,11 @@ void serial8250_release_dma(struct uart_8250_port *p) UART_XMIT_SIZE, DMA_TO_DEVICE); dma_release_channel(dma->txchan); dma->txchan = NULL; - dma->tx_running = 0; + scoped_guard(uart_port_lock_irqsave, &p->port) { + dma->tx_running = 0; + dma->tx_terminating = false; + uart_xmit_unblock(&p->port); + } dev_dbg_ratelimited(p->port.dev, "dma channels released\n"); } diff --git a/drivers/tty/serial/8250/8250_omap.c b/drivers/tty/serial/8250/8250_omap.c index 477d645be9b0..a7cd5b918622 100644 --- a/drivers/tty/serial/8250/8250_omap.c +++ b/drivers/tty/serial/8250/8250_omap.c @@ -1099,7 +1099,12 @@ static void omap_8250_tx_dma_complete(struct uart_8250_port *p) dma->tx_running = 0; - uart_xmit_advance(&p->port, dma->tx_size); + if (dma->tx_flushed) { + p->port.icount.tx += dma->tx_size; + uart_xmit_unblock(&p->port); + } else { + uart_xmit_advance(&p->port, dma->tx_size); + } if (priv->delayed_restore) { priv->delayed_restore = 0; @@ -1131,7 +1136,7 @@ static void omap_8250_dma_tx_complete(void *param) guard(uart_port_lock_irqsave)(&p->port); - if (dma->shutting_down) + if (serial8250_tx_dma_blocked(dma) || !dma->tx_running) return; omap_8250_tx_dma_complete(p); } @@ -1146,7 +1151,7 @@ static int omap_8250_tx_dma(struct uart_8250_port *p) int skip_byte = -1; int ret; - if (dma->shutting_down || dma->tx_running) + if (serial8250_tx_dma_blocked(dma) || dma->tx_running) return 0; if (uart_tx_stopped(&p->port) || kfifo_is_empty(&tport->xmit_fifo)) { @@ -1205,24 +1210,26 @@ static int omap_8250_tx_dma(struct uart_8250_port *p) goto err; } + serial8250_tx_dma_sync_buffer(p, &sg, 1); + desc = dmaengine_prep_slave_sg(dma->txchan, &sg, 1, DMA_MEM_TO_DEV, DMA_PREP_INTERRUPT | DMA_CTRL_ACK); if (!desc) { + dma_sync_single_for_cpu(dmaengine_get_dma_device(dma->txchan), dma->tx_addr, + UART_XMIT_SIZE, DMA_TO_DEVICE); ret = -EBUSY; goto err; } dma->tx_size = sg_dma_len(&sg); dma->tx_running = 1; + dma->tx_flushed = false; + dma->tx_complete = omap_8250_tx_dma_complete; desc->callback = omap_8250_dma_tx_complete; desc->callback_param = p; dma->tx_cookie = dmaengine_submit(desc); - - dma_sync_single_for_device(dmaengine_get_dma_device(dma->txchan), dma->tx_addr, - UART_XMIT_SIZE, DMA_TO_DEVICE); - dma_async_issue_pending(dma->txchan); if (dma->tx_err) dma->tx_err = 0; -- 2.53.0