18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0+ 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * 8250_dma.c - DMA Engine API support for 8250.c 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (C) 2013 Intel Corporation 68c2ecf20Sopenharmony_ci */ 78c2ecf20Sopenharmony_ci#include <linux/tty.h> 88c2ecf20Sopenharmony_ci#include <linux/tty_flip.h> 98c2ecf20Sopenharmony_ci#include <linux/serial_reg.h> 108c2ecf20Sopenharmony_ci#include <linux/dma-mapping.h> 118c2ecf20Sopenharmony_ci 128c2ecf20Sopenharmony_ci#include "8250.h" 138c2ecf20Sopenharmony_ci 148c2ecf20Sopenharmony_cistatic void __dma_tx_complete(void *param) 158c2ecf20Sopenharmony_ci{ 168c2ecf20Sopenharmony_ci struct uart_8250_port *p = param; 178c2ecf20Sopenharmony_ci struct uart_8250_dma *dma = p->dma; 188c2ecf20Sopenharmony_ci struct circ_buf *xmit = &p->port.state->xmit; 198c2ecf20Sopenharmony_ci unsigned long flags; 208c2ecf20Sopenharmony_ci int ret; 218c2ecf20Sopenharmony_ci 228c2ecf20Sopenharmony_ci dma_sync_single_for_cpu(dma->txchan->device->dev, dma->tx_addr, 238c2ecf20Sopenharmony_ci UART_XMIT_SIZE, DMA_TO_DEVICE); 248c2ecf20Sopenharmony_ci 258c2ecf20Sopenharmony_ci spin_lock_irqsave(&p->port.lock, flags); 268c2ecf20Sopenharmony_ci 278c2ecf20Sopenharmony_ci dma->tx_running = 0; 288c2ecf20Sopenharmony_ci 298c2ecf20Sopenharmony_ci xmit->tail += dma->tx_size; 308c2ecf20Sopenharmony_ci xmit->tail &= UART_XMIT_SIZE - 1; 318c2ecf20Sopenharmony_ci p->port.icount.tx += dma->tx_size; 328c2ecf20Sopenharmony_ci 338c2ecf20Sopenharmony_ci if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) 348c2ecf20Sopenharmony_ci uart_write_wakeup(&p->port); 358c2ecf20Sopenharmony_ci 368c2ecf20Sopenharmony_ci ret = serial8250_tx_dma(p); 378c2ecf20Sopenharmony_ci if (ret) 388c2ecf20Sopenharmony_ci serial8250_set_THRI(p); 398c2ecf20Sopenharmony_ci 408c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&p->port.lock, flags); 418c2ecf20Sopenharmony_ci} 428c2ecf20Sopenharmony_ci 438c2ecf20Sopenharmony_cistatic void __dma_rx_complete(void *param) 448c2ecf20Sopenharmony_ci{ 458c2ecf20Sopenharmony_ci struct uart_8250_port *p = param; 468c2ecf20Sopenharmony_ci struct uart_8250_dma *dma = p->dma; 478c2ecf20Sopenharmony_ci struct tty_port *tty_port = &p->port.state->port; 488c2ecf20Sopenharmony_ci struct dma_tx_state state; 498c2ecf20Sopenharmony_ci enum dma_status dma_status; 508c2ecf20Sopenharmony_ci int count; 518c2ecf20Sopenharmony_ci 528c2ecf20Sopenharmony_ci /* 538c2ecf20Sopenharmony_ci * New DMA Rx can be started during the completion handler before it 548c2ecf20Sopenharmony_ci * could acquire port's lock and it might still be ongoing. Don't to 558c2ecf20Sopenharmony_ci * anything in such case. 568c2ecf20Sopenharmony_ci */ 578c2ecf20Sopenharmony_ci dma_status = dmaengine_tx_status(dma->rxchan, dma->rx_cookie, &state); 588c2ecf20Sopenharmony_ci if (dma_status == DMA_IN_PROGRESS) 598c2ecf20Sopenharmony_ci return; 608c2ecf20Sopenharmony_ci 618c2ecf20Sopenharmony_ci count = dma->rx_size - state.residue; 628c2ecf20Sopenharmony_ci 638c2ecf20Sopenharmony_ci tty_insert_flip_string(tty_port, dma->rx_buf, count); 648c2ecf20Sopenharmony_ci p->port.icount.rx += count; 658c2ecf20Sopenharmony_ci dma->rx_running = 0; 668c2ecf20Sopenharmony_ci 678c2ecf20Sopenharmony_ci tty_flip_buffer_push(tty_port); 688c2ecf20Sopenharmony_ci} 698c2ecf20Sopenharmony_ci 708c2ecf20Sopenharmony_cistatic void dma_rx_complete(void *param) 718c2ecf20Sopenharmony_ci{ 728c2ecf20Sopenharmony_ci struct uart_8250_port *p = param; 738c2ecf20Sopenharmony_ci struct uart_8250_dma *dma = p->dma; 748c2ecf20Sopenharmony_ci unsigned long flags; 758c2ecf20Sopenharmony_ci 768c2ecf20Sopenharmony_ci spin_lock_irqsave(&p->port.lock, flags); 778c2ecf20Sopenharmony_ci if (dma->rx_running) 788c2ecf20Sopenharmony_ci __dma_rx_complete(p); 798c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&p->port.lock, flags); 808c2ecf20Sopenharmony_ci} 818c2ecf20Sopenharmony_ci 828c2ecf20Sopenharmony_ciint serial8250_tx_dma(struct uart_8250_port *p) 838c2ecf20Sopenharmony_ci{ 848c2ecf20Sopenharmony_ci struct uart_8250_dma *dma = p->dma; 858c2ecf20Sopenharmony_ci struct circ_buf *xmit = &p->port.state->xmit; 868c2ecf20Sopenharmony_ci struct dma_async_tx_descriptor *desc; 878c2ecf20Sopenharmony_ci struct uart_port *up = &p->port; 888c2ecf20Sopenharmony_ci int ret; 898c2ecf20Sopenharmony_ci 908c2ecf20Sopenharmony_ci if (dma->tx_running) { 918c2ecf20Sopenharmony_ci if (up->x_char) { 928c2ecf20Sopenharmony_ci dmaengine_pause(dma->txchan); 938c2ecf20Sopenharmony_ci uart_xchar_out(up, UART_TX); 948c2ecf20Sopenharmony_ci dmaengine_resume(dma->txchan); 958c2ecf20Sopenharmony_ci } 968c2ecf20Sopenharmony_ci return 0; 978c2ecf20Sopenharmony_ci } else if (up->x_char) { 988c2ecf20Sopenharmony_ci uart_xchar_out(up, UART_TX); 998c2ecf20Sopenharmony_ci } 1008c2ecf20Sopenharmony_ci 1018c2ecf20Sopenharmony_ci if (uart_tx_stopped(&p->port) || uart_circ_empty(xmit)) { 1028c2ecf20Sopenharmony_ci /* We have been called from __dma_tx_complete() */ 1038c2ecf20Sopenharmony_ci serial8250_rpm_put_tx(p); 1048c2ecf20Sopenharmony_ci return 0; 1058c2ecf20Sopenharmony_ci } 1068c2ecf20Sopenharmony_ci 1078c2ecf20Sopenharmony_ci dma->tx_size = CIRC_CNT_TO_END(xmit->head, xmit->tail, UART_XMIT_SIZE); 1088c2ecf20Sopenharmony_ci 1098c2ecf20Sopenharmony_ci desc = dmaengine_prep_slave_single(dma->txchan, 1108c2ecf20Sopenharmony_ci dma->tx_addr + xmit->tail, 1118c2ecf20Sopenharmony_ci dma->tx_size, DMA_MEM_TO_DEV, 1128c2ecf20Sopenharmony_ci DMA_PREP_INTERRUPT | DMA_CTRL_ACK); 1138c2ecf20Sopenharmony_ci if (!desc) { 1148c2ecf20Sopenharmony_ci ret = -EBUSY; 1158c2ecf20Sopenharmony_ci goto err; 1168c2ecf20Sopenharmony_ci } 1178c2ecf20Sopenharmony_ci 1188c2ecf20Sopenharmony_ci dma->tx_running = 1; 1198c2ecf20Sopenharmony_ci desc->callback = __dma_tx_complete; 1208c2ecf20Sopenharmony_ci desc->callback_param = p; 1218c2ecf20Sopenharmony_ci 1228c2ecf20Sopenharmony_ci dma->tx_cookie = dmaengine_submit(desc); 1238c2ecf20Sopenharmony_ci 1248c2ecf20Sopenharmony_ci dma_sync_single_for_device(dma->txchan->device->dev, dma->tx_addr, 1258c2ecf20Sopenharmony_ci UART_XMIT_SIZE, DMA_TO_DEVICE); 1268c2ecf20Sopenharmony_ci 1278c2ecf20Sopenharmony_ci dma_async_issue_pending(dma->txchan); 1288c2ecf20Sopenharmony_ci if (dma->tx_err) { 1298c2ecf20Sopenharmony_ci dma->tx_err = 0; 1308c2ecf20Sopenharmony_ci serial8250_clear_THRI(p); 1318c2ecf20Sopenharmony_ci } 1328c2ecf20Sopenharmony_ci return 0; 1338c2ecf20Sopenharmony_cierr: 1348c2ecf20Sopenharmony_ci dma->tx_err = 1; 1358c2ecf20Sopenharmony_ci return ret; 1368c2ecf20Sopenharmony_ci} 1378c2ecf20Sopenharmony_ci 1388c2ecf20Sopenharmony_ciint serial8250_rx_dma(struct uart_8250_port *p) 1398c2ecf20Sopenharmony_ci{ 1408c2ecf20Sopenharmony_ci struct uart_8250_dma *dma = p->dma; 1418c2ecf20Sopenharmony_ci struct dma_async_tx_descriptor *desc; 1428c2ecf20Sopenharmony_ci 1438c2ecf20Sopenharmony_ci if (dma->rx_running) 1448c2ecf20Sopenharmony_ci return 0; 1458c2ecf20Sopenharmony_ci 1468c2ecf20Sopenharmony_ci desc = dmaengine_prep_slave_single(dma->rxchan, dma->rx_addr, 1478c2ecf20Sopenharmony_ci dma->rx_size, DMA_DEV_TO_MEM, 1488c2ecf20Sopenharmony_ci DMA_PREP_INTERRUPT | DMA_CTRL_ACK); 1498c2ecf20Sopenharmony_ci if (!desc) 1508c2ecf20Sopenharmony_ci return -EBUSY; 1518c2ecf20Sopenharmony_ci 1528c2ecf20Sopenharmony_ci dma->rx_running = 1; 1538c2ecf20Sopenharmony_ci desc->callback = dma_rx_complete; 1548c2ecf20Sopenharmony_ci desc->callback_param = p; 1558c2ecf20Sopenharmony_ci 1568c2ecf20Sopenharmony_ci dma->rx_cookie = dmaengine_submit(desc); 1578c2ecf20Sopenharmony_ci 1588c2ecf20Sopenharmony_ci dma_async_issue_pending(dma->rxchan); 1598c2ecf20Sopenharmony_ci 1608c2ecf20Sopenharmony_ci return 0; 1618c2ecf20Sopenharmony_ci} 1628c2ecf20Sopenharmony_ci 1638c2ecf20Sopenharmony_civoid serial8250_rx_dma_flush(struct uart_8250_port *p) 1648c2ecf20Sopenharmony_ci{ 1658c2ecf20Sopenharmony_ci struct uart_8250_dma *dma = p->dma; 1668c2ecf20Sopenharmony_ci 1678c2ecf20Sopenharmony_ci if (dma->rx_running) { 1688c2ecf20Sopenharmony_ci dmaengine_pause(dma->rxchan); 1698c2ecf20Sopenharmony_ci __dma_rx_complete(p); 1708c2ecf20Sopenharmony_ci dmaengine_terminate_async(dma->rxchan); 1718c2ecf20Sopenharmony_ci } 1728c2ecf20Sopenharmony_ci} 1738c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(serial8250_rx_dma_flush); 1748c2ecf20Sopenharmony_ci 1758c2ecf20Sopenharmony_ciint serial8250_request_dma(struct uart_8250_port *p) 1768c2ecf20Sopenharmony_ci{ 1778c2ecf20Sopenharmony_ci struct uart_8250_dma *dma = p->dma; 1788c2ecf20Sopenharmony_ci phys_addr_t rx_dma_addr = dma->rx_dma_addr ? 1798c2ecf20Sopenharmony_ci dma->rx_dma_addr : p->port.mapbase; 1808c2ecf20Sopenharmony_ci phys_addr_t tx_dma_addr = dma->tx_dma_addr ? 1818c2ecf20Sopenharmony_ci dma->tx_dma_addr : p->port.mapbase; 1828c2ecf20Sopenharmony_ci dma_cap_mask_t mask; 1838c2ecf20Sopenharmony_ci struct dma_slave_caps caps; 1848c2ecf20Sopenharmony_ci int ret; 1858c2ecf20Sopenharmony_ci 1868c2ecf20Sopenharmony_ci /* Default slave configuration parameters */ 1878c2ecf20Sopenharmony_ci dma->rxconf.direction = DMA_DEV_TO_MEM; 1888c2ecf20Sopenharmony_ci dma->rxconf.src_addr_width = DMA_SLAVE_BUSWIDTH_1_BYTE; 1898c2ecf20Sopenharmony_ci dma->rxconf.src_addr = rx_dma_addr + UART_RX; 1908c2ecf20Sopenharmony_ci 1918c2ecf20Sopenharmony_ci dma->txconf.direction = DMA_MEM_TO_DEV; 1928c2ecf20Sopenharmony_ci dma->txconf.dst_addr_width = DMA_SLAVE_BUSWIDTH_1_BYTE; 1938c2ecf20Sopenharmony_ci dma->txconf.dst_addr = tx_dma_addr + UART_TX; 1948c2ecf20Sopenharmony_ci 1958c2ecf20Sopenharmony_ci dma_cap_zero(mask); 1968c2ecf20Sopenharmony_ci dma_cap_set(DMA_SLAVE, mask); 1978c2ecf20Sopenharmony_ci 1988c2ecf20Sopenharmony_ci /* Get a channel for RX */ 1998c2ecf20Sopenharmony_ci dma->rxchan = dma_request_slave_channel_compat(mask, 2008c2ecf20Sopenharmony_ci dma->fn, dma->rx_param, 2018c2ecf20Sopenharmony_ci p->port.dev, "rx"); 2028c2ecf20Sopenharmony_ci if (!dma->rxchan) 2038c2ecf20Sopenharmony_ci return -ENODEV; 2048c2ecf20Sopenharmony_ci 2058c2ecf20Sopenharmony_ci /* 8250 rx dma requires dmaengine driver to support pause/terminate */ 2068c2ecf20Sopenharmony_ci ret = dma_get_slave_caps(dma->rxchan, &caps); 2078c2ecf20Sopenharmony_ci if (ret) 2088c2ecf20Sopenharmony_ci goto release_rx; 2098c2ecf20Sopenharmony_ci if (!caps.cmd_pause || !caps.cmd_terminate || 2108c2ecf20Sopenharmony_ci caps.residue_granularity == DMA_RESIDUE_GRANULARITY_DESCRIPTOR) { 2118c2ecf20Sopenharmony_ci ret = -EINVAL; 2128c2ecf20Sopenharmony_ci goto release_rx; 2138c2ecf20Sopenharmony_ci } 2148c2ecf20Sopenharmony_ci 2158c2ecf20Sopenharmony_ci dmaengine_slave_config(dma->rxchan, &dma->rxconf); 2168c2ecf20Sopenharmony_ci 2178c2ecf20Sopenharmony_ci /* Get a channel for TX */ 2188c2ecf20Sopenharmony_ci dma->txchan = dma_request_slave_channel_compat(mask, 2198c2ecf20Sopenharmony_ci dma->fn, dma->tx_param, 2208c2ecf20Sopenharmony_ci p->port.dev, "tx"); 2218c2ecf20Sopenharmony_ci if (!dma->txchan) { 2228c2ecf20Sopenharmony_ci ret = -ENODEV; 2238c2ecf20Sopenharmony_ci goto release_rx; 2248c2ecf20Sopenharmony_ci } 2258c2ecf20Sopenharmony_ci 2268c2ecf20Sopenharmony_ci /* 8250 tx dma requires dmaengine driver to support terminate */ 2278c2ecf20Sopenharmony_ci ret = dma_get_slave_caps(dma->txchan, &caps); 2288c2ecf20Sopenharmony_ci if (ret) 2298c2ecf20Sopenharmony_ci goto err; 2308c2ecf20Sopenharmony_ci if (!caps.cmd_terminate) { 2318c2ecf20Sopenharmony_ci ret = -EINVAL; 2328c2ecf20Sopenharmony_ci goto err; 2338c2ecf20Sopenharmony_ci } 2348c2ecf20Sopenharmony_ci 2358c2ecf20Sopenharmony_ci dmaengine_slave_config(dma->txchan, &dma->txconf); 2368c2ecf20Sopenharmony_ci 2378c2ecf20Sopenharmony_ci /* RX buffer */ 2388c2ecf20Sopenharmony_ci if (!dma->rx_size) 2398c2ecf20Sopenharmony_ci dma->rx_size = PAGE_SIZE; 2408c2ecf20Sopenharmony_ci 2418c2ecf20Sopenharmony_ci dma->rx_buf = dma_alloc_coherent(dma->rxchan->device->dev, dma->rx_size, 2428c2ecf20Sopenharmony_ci &dma->rx_addr, GFP_KERNEL); 2438c2ecf20Sopenharmony_ci if (!dma->rx_buf) { 2448c2ecf20Sopenharmony_ci ret = -ENOMEM; 2458c2ecf20Sopenharmony_ci goto err; 2468c2ecf20Sopenharmony_ci } 2478c2ecf20Sopenharmony_ci 2488c2ecf20Sopenharmony_ci /* TX buffer */ 2498c2ecf20Sopenharmony_ci dma->tx_addr = dma_map_single(dma->txchan->device->dev, 2508c2ecf20Sopenharmony_ci p->port.state->xmit.buf, 2518c2ecf20Sopenharmony_ci UART_XMIT_SIZE, 2528c2ecf20Sopenharmony_ci DMA_TO_DEVICE); 2538c2ecf20Sopenharmony_ci if (dma_mapping_error(dma->txchan->device->dev, dma->tx_addr)) { 2548c2ecf20Sopenharmony_ci dma_free_coherent(dma->rxchan->device->dev, dma->rx_size, 2558c2ecf20Sopenharmony_ci dma->rx_buf, dma->rx_addr); 2568c2ecf20Sopenharmony_ci ret = -ENOMEM; 2578c2ecf20Sopenharmony_ci goto err; 2588c2ecf20Sopenharmony_ci } 2598c2ecf20Sopenharmony_ci 2608c2ecf20Sopenharmony_ci dev_dbg_ratelimited(p->port.dev, "got both dma channels\n"); 2618c2ecf20Sopenharmony_ci 2628c2ecf20Sopenharmony_ci return 0; 2638c2ecf20Sopenharmony_cierr: 2648c2ecf20Sopenharmony_ci dma_release_channel(dma->txchan); 2658c2ecf20Sopenharmony_cirelease_rx: 2668c2ecf20Sopenharmony_ci dma_release_channel(dma->rxchan); 2678c2ecf20Sopenharmony_ci return ret; 2688c2ecf20Sopenharmony_ci} 2698c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(serial8250_request_dma); 2708c2ecf20Sopenharmony_ci 2718c2ecf20Sopenharmony_civoid serial8250_release_dma(struct uart_8250_port *p) 2728c2ecf20Sopenharmony_ci{ 2738c2ecf20Sopenharmony_ci struct uart_8250_dma *dma = p->dma; 2748c2ecf20Sopenharmony_ci 2758c2ecf20Sopenharmony_ci if (!dma) 2768c2ecf20Sopenharmony_ci return; 2778c2ecf20Sopenharmony_ci 2788c2ecf20Sopenharmony_ci /* Release RX resources */ 2798c2ecf20Sopenharmony_ci dmaengine_terminate_sync(dma->rxchan); 2808c2ecf20Sopenharmony_ci dma_free_coherent(dma->rxchan->device->dev, dma->rx_size, dma->rx_buf, 2818c2ecf20Sopenharmony_ci dma->rx_addr); 2828c2ecf20Sopenharmony_ci dma_release_channel(dma->rxchan); 2838c2ecf20Sopenharmony_ci dma->rxchan = NULL; 2848c2ecf20Sopenharmony_ci 2858c2ecf20Sopenharmony_ci /* Release TX resources */ 2868c2ecf20Sopenharmony_ci dmaengine_terminate_sync(dma->txchan); 2878c2ecf20Sopenharmony_ci dma_unmap_single(dma->txchan->device->dev, dma->tx_addr, 2888c2ecf20Sopenharmony_ci UART_XMIT_SIZE, DMA_TO_DEVICE); 2898c2ecf20Sopenharmony_ci dma_release_channel(dma->txchan); 2908c2ecf20Sopenharmony_ci dma->txchan = NULL; 2918c2ecf20Sopenharmony_ci dma->tx_running = 0; 2928c2ecf20Sopenharmony_ci 2938c2ecf20Sopenharmony_ci dev_dbg_ratelimited(p->port.dev, "dma channels released\n"); 2948c2ecf20Sopenharmony_ci} 2958c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(serial8250_release_dma); 296