162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0+
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * 8250_dma.c - DMA Engine API support for 8250.c
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Copyright (C) 2013 Intel Corporation
662306a36Sopenharmony_ci */
762306a36Sopenharmony_ci#include <linux/tty.h>
862306a36Sopenharmony_ci#include <linux/tty_flip.h>
962306a36Sopenharmony_ci#include <linux/serial_reg.h>
1062306a36Sopenharmony_ci#include <linux/dma-mapping.h>
1162306a36Sopenharmony_ci
1262306a36Sopenharmony_ci#include "8250.h"
1362306a36Sopenharmony_ci
1462306a36Sopenharmony_cistatic void __dma_tx_complete(void *param)
1562306a36Sopenharmony_ci{
1662306a36Sopenharmony_ci	struct uart_8250_port	*p = param;
1762306a36Sopenharmony_ci	struct uart_8250_dma	*dma = p->dma;
1862306a36Sopenharmony_ci	struct circ_buf		*xmit = &p->port.state->xmit;
1962306a36Sopenharmony_ci	unsigned long	flags;
2062306a36Sopenharmony_ci	int		ret;
2162306a36Sopenharmony_ci
2262306a36Sopenharmony_ci	dma_sync_single_for_cpu(dma->txchan->device->dev, dma->tx_addr,
2362306a36Sopenharmony_ci				UART_XMIT_SIZE, DMA_TO_DEVICE);
2462306a36Sopenharmony_ci
2562306a36Sopenharmony_ci	spin_lock_irqsave(&p->port.lock, flags);
2662306a36Sopenharmony_ci
2762306a36Sopenharmony_ci	dma->tx_running = 0;
2862306a36Sopenharmony_ci
2962306a36Sopenharmony_ci	uart_xmit_advance(&p->port, dma->tx_size);
3062306a36Sopenharmony_ci
3162306a36Sopenharmony_ci	if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
3262306a36Sopenharmony_ci		uart_write_wakeup(&p->port);
3362306a36Sopenharmony_ci
3462306a36Sopenharmony_ci	ret = serial8250_tx_dma(p);
3562306a36Sopenharmony_ci	if (ret || !dma->tx_running)
3662306a36Sopenharmony_ci		serial8250_set_THRI(p);
3762306a36Sopenharmony_ci
3862306a36Sopenharmony_ci	spin_unlock_irqrestore(&p->port.lock, flags);
3962306a36Sopenharmony_ci}
4062306a36Sopenharmony_ci
4162306a36Sopenharmony_cistatic void __dma_rx_complete(struct uart_8250_port *p)
4262306a36Sopenharmony_ci{
4362306a36Sopenharmony_ci	struct uart_8250_dma	*dma = p->dma;
4462306a36Sopenharmony_ci	struct tty_port		*tty_port = &p->port.state->port;
4562306a36Sopenharmony_ci	struct dma_tx_state	state;
4662306a36Sopenharmony_ci	enum dma_status		dma_status;
4762306a36Sopenharmony_ci	int			count;
4862306a36Sopenharmony_ci
4962306a36Sopenharmony_ci	/*
5062306a36Sopenharmony_ci	 * New DMA Rx can be started during the completion handler before it
5162306a36Sopenharmony_ci	 * could acquire port's lock and it might still be ongoing. Don't to
5262306a36Sopenharmony_ci	 * anything in such case.
5362306a36Sopenharmony_ci	 */
5462306a36Sopenharmony_ci	dma_status = dmaengine_tx_status(dma->rxchan, dma->rx_cookie, &state);
5562306a36Sopenharmony_ci	if (dma_status == DMA_IN_PROGRESS)
5662306a36Sopenharmony_ci		return;
5762306a36Sopenharmony_ci
5862306a36Sopenharmony_ci	count = dma->rx_size - state.residue;
5962306a36Sopenharmony_ci
6062306a36Sopenharmony_ci	tty_insert_flip_string(tty_port, dma->rx_buf, count);
6162306a36Sopenharmony_ci	p->port.icount.rx += count;
6262306a36Sopenharmony_ci	dma->rx_running = 0;
6362306a36Sopenharmony_ci
6462306a36Sopenharmony_ci	tty_flip_buffer_push(tty_port);
6562306a36Sopenharmony_ci}
6662306a36Sopenharmony_ci
6762306a36Sopenharmony_cistatic void dma_rx_complete(void *param)
6862306a36Sopenharmony_ci{
6962306a36Sopenharmony_ci	struct uart_8250_port *p = param;
7062306a36Sopenharmony_ci	struct uart_8250_dma *dma = p->dma;
7162306a36Sopenharmony_ci	unsigned long flags;
7262306a36Sopenharmony_ci
7362306a36Sopenharmony_ci	spin_lock_irqsave(&p->port.lock, flags);
7462306a36Sopenharmony_ci	if (dma->rx_running)
7562306a36Sopenharmony_ci		__dma_rx_complete(p);
7662306a36Sopenharmony_ci
7762306a36Sopenharmony_ci	/*
7862306a36Sopenharmony_ci	 * Cannot be combined with the previous check because __dma_rx_complete()
7962306a36Sopenharmony_ci	 * changes dma->rx_running.
8062306a36Sopenharmony_ci	 */
8162306a36Sopenharmony_ci	if (!dma->rx_running && (serial_lsr_in(p) & UART_LSR_DR))
8262306a36Sopenharmony_ci		p->dma->rx_dma(p);
8362306a36Sopenharmony_ci	spin_unlock_irqrestore(&p->port.lock, flags);
8462306a36Sopenharmony_ci}
8562306a36Sopenharmony_ci
8662306a36Sopenharmony_ciint serial8250_tx_dma(struct uart_8250_port *p)
8762306a36Sopenharmony_ci{
8862306a36Sopenharmony_ci	struct uart_8250_dma		*dma = p->dma;
8962306a36Sopenharmony_ci	struct circ_buf			*xmit = &p->port.state->xmit;
9062306a36Sopenharmony_ci	struct dma_async_tx_descriptor	*desc;
9162306a36Sopenharmony_ci	struct uart_port		*up = &p->port;
9262306a36Sopenharmony_ci	int ret;
9362306a36Sopenharmony_ci
9462306a36Sopenharmony_ci	if (dma->tx_running) {
9562306a36Sopenharmony_ci		if (up->x_char) {
9662306a36Sopenharmony_ci			dmaengine_pause(dma->txchan);
9762306a36Sopenharmony_ci			uart_xchar_out(up, UART_TX);
9862306a36Sopenharmony_ci			dmaengine_resume(dma->txchan);
9962306a36Sopenharmony_ci		}
10062306a36Sopenharmony_ci		return 0;
10162306a36Sopenharmony_ci	} else if (up->x_char) {
10262306a36Sopenharmony_ci		uart_xchar_out(up, UART_TX);
10362306a36Sopenharmony_ci	}
10462306a36Sopenharmony_ci
10562306a36Sopenharmony_ci	if (uart_tx_stopped(&p->port) || uart_circ_empty(xmit)) {
10662306a36Sopenharmony_ci		/* We have been called from __dma_tx_complete() */
10762306a36Sopenharmony_ci		return 0;
10862306a36Sopenharmony_ci	}
10962306a36Sopenharmony_ci
11062306a36Sopenharmony_ci	dma->tx_size = CIRC_CNT_TO_END(xmit->head, xmit->tail, UART_XMIT_SIZE);
11162306a36Sopenharmony_ci
11262306a36Sopenharmony_ci	serial8250_do_prepare_tx_dma(p);
11362306a36Sopenharmony_ci
11462306a36Sopenharmony_ci	desc = dmaengine_prep_slave_single(dma->txchan,
11562306a36Sopenharmony_ci					   dma->tx_addr + xmit->tail,
11662306a36Sopenharmony_ci					   dma->tx_size, DMA_MEM_TO_DEV,
11762306a36Sopenharmony_ci					   DMA_PREP_INTERRUPT | DMA_CTRL_ACK);
11862306a36Sopenharmony_ci	if (!desc) {
11962306a36Sopenharmony_ci		ret = -EBUSY;
12062306a36Sopenharmony_ci		goto err;
12162306a36Sopenharmony_ci	}
12262306a36Sopenharmony_ci
12362306a36Sopenharmony_ci	dma->tx_running = 1;
12462306a36Sopenharmony_ci	desc->callback = __dma_tx_complete;
12562306a36Sopenharmony_ci	desc->callback_param = p;
12662306a36Sopenharmony_ci
12762306a36Sopenharmony_ci	dma->tx_cookie = dmaengine_submit(desc);
12862306a36Sopenharmony_ci
12962306a36Sopenharmony_ci	dma_sync_single_for_device(dma->txchan->device->dev, dma->tx_addr,
13062306a36Sopenharmony_ci				   UART_XMIT_SIZE, DMA_TO_DEVICE);
13162306a36Sopenharmony_ci
13262306a36Sopenharmony_ci	dma_async_issue_pending(dma->txchan);
13362306a36Sopenharmony_ci	serial8250_clear_THRI(p);
13462306a36Sopenharmony_ci	dma->tx_err = 0;
13562306a36Sopenharmony_ci
13662306a36Sopenharmony_ci	return 0;
13762306a36Sopenharmony_cierr:
13862306a36Sopenharmony_ci	dma->tx_err = 1;
13962306a36Sopenharmony_ci	return ret;
14062306a36Sopenharmony_ci}
14162306a36Sopenharmony_ci
14262306a36Sopenharmony_ciint serial8250_rx_dma(struct uart_8250_port *p)
14362306a36Sopenharmony_ci{
14462306a36Sopenharmony_ci	struct uart_8250_dma		*dma = p->dma;
14562306a36Sopenharmony_ci	struct dma_async_tx_descriptor	*desc;
14662306a36Sopenharmony_ci
14762306a36Sopenharmony_ci	if (dma->rx_running)
14862306a36Sopenharmony_ci		return 0;
14962306a36Sopenharmony_ci
15062306a36Sopenharmony_ci	serial8250_do_prepare_rx_dma(p);
15162306a36Sopenharmony_ci
15262306a36Sopenharmony_ci	desc = dmaengine_prep_slave_single(dma->rxchan, dma->rx_addr,
15362306a36Sopenharmony_ci					   dma->rx_size, DMA_DEV_TO_MEM,
15462306a36Sopenharmony_ci					   DMA_PREP_INTERRUPT | DMA_CTRL_ACK);
15562306a36Sopenharmony_ci	if (!desc)
15662306a36Sopenharmony_ci		return -EBUSY;
15762306a36Sopenharmony_ci
15862306a36Sopenharmony_ci	dma->rx_running = 1;
15962306a36Sopenharmony_ci	desc->callback = dma_rx_complete;
16062306a36Sopenharmony_ci	desc->callback_param = p;
16162306a36Sopenharmony_ci
16262306a36Sopenharmony_ci	dma->rx_cookie = dmaengine_submit(desc);
16362306a36Sopenharmony_ci
16462306a36Sopenharmony_ci	dma_async_issue_pending(dma->rxchan);
16562306a36Sopenharmony_ci
16662306a36Sopenharmony_ci	return 0;
16762306a36Sopenharmony_ci}
16862306a36Sopenharmony_ci
16962306a36Sopenharmony_civoid serial8250_rx_dma_flush(struct uart_8250_port *p)
17062306a36Sopenharmony_ci{
17162306a36Sopenharmony_ci	struct uart_8250_dma *dma = p->dma;
17262306a36Sopenharmony_ci
17362306a36Sopenharmony_ci	if (dma->rx_running) {
17462306a36Sopenharmony_ci		dmaengine_pause(dma->rxchan);
17562306a36Sopenharmony_ci		__dma_rx_complete(p);
17662306a36Sopenharmony_ci		dmaengine_terminate_async(dma->rxchan);
17762306a36Sopenharmony_ci	}
17862306a36Sopenharmony_ci}
17962306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(serial8250_rx_dma_flush);
18062306a36Sopenharmony_ci
18162306a36Sopenharmony_ciint serial8250_request_dma(struct uart_8250_port *p)
18262306a36Sopenharmony_ci{
18362306a36Sopenharmony_ci	struct uart_8250_dma	*dma = p->dma;
18462306a36Sopenharmony_ci	phys_addr_t rx_dma_addr = dma->rx_dma_addr ?
18562306a36Sopenharmony_ci				  dma->rx_dma_addr : p->port.mapbase;
18662306a36Sopenharmony_ci	phys_addr_t tx_dma_addr = dma->tx_dma_addr ?
18762306a36Sopenharmony_ci				  dma->tx_dma_addr : p->port.mapbase;
18862306a36Sopenharmony_ci	dma_cap_mask_t		mask;
18962306a36Sopenharmony_ci	struct dma_slave_caps	caps;
19062306a36Sopenharmony_ci	int			ret;
19162306a36Sopenharmony_ci
19262306a36Sopenharmony_ci	/* Default slave configuration parameters */
19362306a36Sopenharmony_ci	dma->rxconf.direction		= DMA_DEV_TO_MEM;
19462306a36Sopenharmony_ci	dma->rxconf.src_addr_width	= DMA_SLAVE_BUSWIDTH_1_BYTE;
19562306a36Sopenharmony_ci	dma->rxconf.src_addr		= rx_dma_addr + UART_RX;
19662306a36Sopenharmony_ci
19762306a36Sopenharmony_ci	dma->txconf.direction		= DMA_MEM_TO_DEV;
19862306a36Sopenharmony_ci	dma->txconf.dst_addr_width	= DMA_SLAVE_BUSWIDTH_1_BYTE;
19962306a36Sopenharmony_ci	dma->txconf.dst_addr		= tx_dma_addr + UART_TX;
20062306a36Sopenharmony_ci
20162306a36Sopenharmony_ci	dma_cap_zero(mask);
20262306a36Sopenharmony_ci	dma_cap_set(DMA_SLAVE, mask);
20362306a36Sopenharmony_ci
20462306a36Sopenharmony_ci	/* Get a channel for RX */
20562306a36Sopenharmony_ci	dma->rxchan = dma_request_slave_channel_compat(mask,
20662306a36Sopenharmony_ci						       dma->fn, dma->rx_param,
20762306a36Sopenharmony_ci						       p->port.dev, "rx");
20862306a36Sopenharmony_ci	if (!dma->rxchan)
20962306a36Sopenharmony_ci		return -ENODEV;
21062306a36Sopenharmony_ci
21162306a36Sopenharmony_ci	/* 8250 rx dma requires dmaengine driver to support pause/terminate */
21262306a36Sopenharmony_ci	ret = dma_get_slave_caps(dma->rxchan, &caps);
21362306a36Sopenharmony_ci	if (ret)
21462306a36Sopenharmony_ci		goto release_rx;
21562306a36Sopenharmony_ci	if (!caps.cmd_pause || !caps.cmd_terminate ||
21662306a36Sopenharmony_ci	    caps.residue_granularity == DMA_RESIDUE_GRANULARITY_DESCRIPTOR) {
21762306a36Sopenharmony_ci		ret = -EINVAL;
21862306a36Sopenharmony_ci		goto release_rx;
21962306a36Sopenharmony_ci	}
22062306a36Sopenharmony_ci
22162306a36Sopenharmony_ci	dmaengine_slave_config(dma->rxchan, &dma->rxconf);
22262306a36Sopenharmony_ci
22362306a36Sopenharmony_ci	/* Get a channel for TX */
22462306a36Sopenharmony_ci	dma->txchan = dma_request_slave_channel_compat(mask,
22562306a36Sopenharmony_ci						       dma->fn, dma->tx_param,
22662306a36Sopenharmony_ci						       p->port.dev, "tx");
22762306a36Sopenharmony_ci	if (!dma->txchan) {
22862306a36Sopenharmony_ci		ret = -ENODEV;
22962306a36Sopenharmony_ci		goto release_rx;
23062306a36Sopenharmony_ci	}
23162306a36Sopenharmony_ci
23262306a36Sopenharmony_ci	/* 8250 tx dma requires dmaengine driver to support terminate */
23362306a36Sopenharmony_ci	ret = dma_get_slave_caps(dma->txchan, &caps);
23462306a36Sopenharmony_ci	if (ret)
23562306a36Sopenharmony_ci		goto err;
23662306a36Sopenharmony_ci	if (!caps.cmd_terminate) {
23762306a36Sopenharmony_ci		ret = -EINVAL;
23862306a36Sopenharmony_ci		goto err;
23962306a36Sopenharmony_ci	}
24062306a36Sopenharmony_ci
24162306a36Sopenharmony_ci	dmaengine_slave_config(dma->txchan, &dma->txconf);
24262306a36Sopenharmony_ci
24362306a36Sopenharmony_ci	/* RX buffer */
24462306a36Sopenharmony_ci	if (!dma->rx_size)
24562306a36Sopenharmony_ci		dma->rx_size = PAGE_SIZE;
24662306a36Sopenharmony_ci
24762306a36Sopenharmony_ci	dma->rx_buf = dma_alloc_coherent(dma->rxchan->device->dev, dma->rx_size,
24862306a36Sopenharmony_ci					&dma->rx_addr, GFP_KERNEL);
24962306a36Sopenharmony_ci	if (!dma->rx_buf) {
25062306a36Sopenharmony_ci		ret = -ENOMEM;
25162306a36Sopenharmony_ci		goto err;
25262306a36Sopenharmony_ci	}
25362306a36Sopenharmony_ci
25462306a36Sopenharmony_ci	/* TX buffer */
25562306a36Sopenharmony_ci	dma->tx_addr = dma_map_single(dma->txchan->device->dev,
25662306a36Sopenharmony_ci					p->port.state->xmit.buf,
25762306a36Sopenharmony_ci					UART_XMIT_SIZE,
25862306a36Sopenharmony_ci					DMA_TO_DEVICE);
25962306a36Sopenharmony_ci	if (dma_mapping_error(dma->txchan->device->dev, dma->tx_addr)) {
26062306a36Sopenharmony_ci		dma_free_coherent(dma->rxchan->device->dev, dma->rx_size,
26162306a36Sopenharmony_ci				  dma->rx_buf, dma->rx_addr);
26262306a36Sopenharmony_ci		ret = -ENOMEM;
26362306a36Sopenharmony_ci		goto err;
26462306a36Sopenharmony_ci	}
26562306a36Sopenharmony_ci
26662306a36Sopenharmony_ci	dev_dbg_ratelimited(p->port.dev, "got both dma channels\n");
26762306a36Sopenharmony_ci
26862306a36Sopenharmony_ci	return 0;
26962306a36Sopenharmony_cierr:
27062306a36Sopenharmony_ci	dma_release_channel(dma->txchan);
27162306a36Sopenharmony_cirelease_rx:
27262306a36Sopenharmony_ci	dma_release_channel(dma->rxchan);
27362306a36Sopenharmony_ci	return ret;
27462306a36Sopenharmony_ci}
27562306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(serial8250_request_dma);
27662306a36Sopenharmony_ci
27762306a36Sopenharmony_civoid serial8250_release_dma(struct uart_8250_port *p)
27862306a36Sopenharmony_ci{
27962306a36Sopenharmony_ci	struct uart_8250_dma *dma = p->dma;
28062306a36Sopenharmony_ci
28162306a36Sopenharmony_ci	if (!dma)
28262306a36Sopenharmony_ci		return;
28362306a36Sopenharmony_ci
28462306a36Sopenharmony_ci	/* Release RX resources */
28562306a36Sopenharmony_ci	dmaengine_terminate_sync(dma->rxchan);
28662306a36Sopenharmony_ci	dma_free_coherent(dma->rxchan->device->dev, dma->rx_size, dma->rx_buf,
28762306a36Sopenharmony_ci			  dma->rx_addr);
28862306a36Sopenharmony_ci	dma_release_channel(dma->rxchan);
28962306a36Sopenharmony_ci	dma->rxchan = NULL;
29062306a36Sopenharmony_ci
29162306a36Sopenharmony_ci	/* Release TX resources */
29262306a36Sopenharmony_ci	dmaengine_terminate_sync(dma->txchan);
29362306a36Sopenharmony_ci	dma_unmap_single(dma->txchan->device->dev, dma->tx_addr,
29462306a36Sopenharmony_ci			 UART_XMIT_SIZE, DMA_TO_DEVICE);
29562306a36Sopenharmony_ci	dma_release_channel(dma->txchan);
29662306a36Sopenharmony_ci	dma->txchan = NULL;
29762306a36Sopenharmony_ci	dma->tx_running = 0;
29862306a36Sopenharmony_ci
29962306a36Sopenharmony_ci	dev_dbg_ratelimited(p->port.dev, "dma channels released\n");
30062306a36Sopenharmony_ci}
30162306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(serial8250_release_dma);
302