-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
serial: 8250: Add support for dmaengine
Add support for dmaengine API. The drivers can implement the struct uart_8250_dma member in struct uart_8250_port and 8250.c can take care of the rest. Signed-off-by: Heikki Krogerus <heikki.krogerus@linux.intel.com> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
- Loading branch information
Heikki Krogerus
authored and
Greg Kroah-Hartman
committed
Jan 16, 2013
1 parent
6a7320c
commit 9ee4b83
Showing
6 changed files
with
304 additions
and
3 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,213 @@ | ||
/* | ||
* 8250_dma.c - DMA Engine API support for 8250.c | ||
* | ||
* Copyright (C) 2013 Intel Corporation | ||
* | ||
* This program is free software; you can redistribute it and/or modify | ||
* it under the terms of the GNU General Public License as published by | ||
* the Free Software Foundation; either version 2 of the License, or | ||
* (at your option) any later version. | ||
*/ | ||
#include <linux/tty.h> | ||
#include <linux/tty_flip.h> | ||
#include <linux/serial_reg.h> | ||
#include <linux/dma-mapping.h> | ||
|
||
#include "8250.h" | ||
|
||
static void __dma_tx_complete(void *param) | ||
{ | ||
struct uart_8250_port *p = param; | ||
struct uart_8250_dma *dma = p->dma; | ||
struct circ_buf *xmit = &p->port.state->xmit; | ||
|
||
dma->tx_running = 0; | ||
|
||
dma_sync_single_for_cpu(dma->txchan->device->dev, dma->tx_addr, | ||
UART_XMIT_SIZE, DMA_TO_DEVICE); | ||
|
||
xmit->tail += dma->tx_size; | ||
xmit->tail &= UART_XMIT_SIZE - 1; | ||
p->port.icount.tx += dma->tx_size; | ||
|
||
if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) | ||
uart_write_wakeup(&p->port); | ||
|
||
if (!uart_circ_empty(xmit) && !uart_tx_stopped(&p->port)) { | ||
serial8250_tx_dma(p); | ||
uart_write_wakeup(&p->port); | ||
} | ||
} | ||
|
||
static void __dma_rx_complete(void *param) | ||
{ | ||
struct uart_8250_port *p = param; | ||
struct uart_8250_dma *dma = p->dma; | ||
struct tty_struct *tty = p->port.state->port.tty; | ||
struct dma_tx_state state; | ||
|
||
dma_sync_single_for_cpu(dma->rxchan->device->dev, dma->rx_addr, | ||
dma->rx_size, DMA_FROM_DEVICE); | ||
|
||
dmaengine_tx_status(dma->rxchan, dma->rx_cookie, &state); | ||
dmaengine_terminate_all(dma->rxchan); | ||
|
||
tty_insert_flip_string(tty, dma->rx_buf, dma->rx_size - state.residue); | ||
p->port.icount.rx += dma->rx_size - state.residue; | ||
|
||
tty_flip_buffer_push(tty); | ||
} | ||
|
||
int serial8250_tx_dma(struct uart_8250_port *p) | ||
{ | ||
struct uart_8250_dma *dma = p->dma; | ||
struct circ_buf *xmit = &p->port.state->xmit; | ||
struct dma_async_tx_descriptor *desc; | ||
|
||
if (dma->tx_running) { | ||
uart_write_wakeup(&p->port); | ||
return -EBUSY; | ||
} | ||
|
||
dma->tx_size = CIRC_CNT_TO_END(xmit->head, xmit->tail, UART_XMIT_SIZE); | ||
|
||
desc = dmaengine_prep_slave_single(dma->txchan, | ||
dma->tx_addr + xmit->tail, | ||
dma->tx_size, DMA_MEM_TO_DEV, | ||
DMA_PREP_INTERRUPT | DMA_CTRL_ACK); | ||
if (!desc) | ||
return -EBUSY; | ||
|
||
dma->tx_running = 1; | ||
|
||
desc->callback = __dma_tx_complete; | ||
desc->callback_param = p; | ||
|
||
dma->tx_cookie = dmaengine_submit(desc); | ||
|
||
dma_sync_single_for_device(dma->txchan->device->dev, dma->tx_addr, | ||
UART_XMIT_SIZE, DMA_TO_DEVICE); | ||
|
||
dma_async_issue_pending(dma->txchan); | ||
|
||
return 0; | ||
} | ||
EXPORT_SYMBOL_GPL(serial8250_tx_dma); | ||
|
||
int serial8250_rx_dma(struct uart_8250_port *p, unsigned int iir) | ||
{ | ||
struct uart_8250_dma *dma = p->dma; | ||
struct dma_async_tx_descriptor *desc; | ||
struct dma_tx_state state; | ||
int dma_status; | ||
|
||
/* | ||
* If RCVR FIFO trigger level was not reached, complete the transfer and | ||
* let 8250.c copy the remaining data. | ||
*/ | ||
if ((iir & 0x3f) == UART_IIR_RX_TIMEOUT) { | ||
dma_status = dmaengine_tx_status(dma->rxchan, dma->rx_cookie, | ||
&state); | ||
if (dma_status == DMA_IN_PROGRESS) { | ||
dmaengine_pause(dma->rxchan); | ||
__dma_rx_complete(p); | ||
} | ||
return -ETIMEDOUT; | ||
} | ||
|
||
desc = dmaengine_prep_slave_single(dma->rxchan, dma->rx_addr, | ||
dma->rx_size, DMA_DEV_TO_MEM, | ||
DMA_PREP_INTERRUPT | DMA_CTRL_ACK); | ||
if (!desc) | ||
return -EBUSY; | ||
|
||
desc->callback = __dma_rx_complete; | ||
desc->callback_param = p; | ||
|
||
dma->rx_cookie = dmaengine_submit(desc); | ||
|
||
dma_sync_single_for_device(dma->rxchan->device->dev, dma->rx_addr, | ||
dma->rx_size, DMA_FROM_DEVICE); | ||
|
||
dma_async_issue_pending(dma->rxchan); | ||
|
||
return 0; | ||
} | ||
EXPORT_SYMBOL_GPL(serial8250_rx_dma); | ||
|
||
int serial8250_request_dma(struct uart_8250_port *p) | ||
{ | ||
struct uart_8250_dma *dma = p->dma; | ||
dma_cap_mask_t mask; | ||
|
||
dma->rxconf.src_addr = p->port.mapbase + UART_RX; | ||
dma->txconf.dst_addr = p->port.mapbase + UART_TX; | ||
|
||
dma_cap_zero(mask); | ||
dma_cap_set(DMA_SLAVE, mask); | ||
|
||
/* Get a channel for RX */ | ||
dma->rxchan = dma_request_channel(mask, dma->fn, dma->rx_param); | ||
if (!dma->rxchan) | ||
return -ENODEV; | ||
|
||
dmaengine_slave_config(dma->rxchan, &dma->rxconf); | ||
|
||
/* Get a channel for TX */ | ||
dma->txchan = dma_request_channel(mask, dma->fn, dma->tx_param); | ||
if (!dma->txchan) { | ||
dma_release_channel(dma->rxchan); | ||
return -ENODEV; | ||
} | ||
|
||
dmaengine_slave_config(dma->txchan, &dma->txconf); | ||
|
||
/* RX buffer */ | ||
if (!dma->rx_size) | ||
dma->rx_size = PAGE_SIZE; | ||
|
||
dma->rx_buf = dma_alloc_coherent(dma->rxchan->device->dev, dma->rx_size, | ||
&dma->rx_addr, GFP_KERNEL); | ||
if (!dma->rx_buf) { | ||
dma_release_channel(dma->rxchan); | ||
dma_release_channel(dma->txchan); | ||
return -ENOMEM; | ||
} | ||
|
||
/* TX buffer */ | ||
dma->tx_addr = dma_map_single(dma->txchan->device->dev, | ||
p->port.state->xmit.buf, | ||
UART_XMIT_SIZE, | ||
DMA_TO_DEVICE); | ||
|
||
dev_dbg_ratelimited(p->port.dev, "got both dma channels\n"); | ||
|
||
return 0; | ||
} | ||
EXPORT_SYMBOL_GPL(serial8250_request_dma); | ||
|
||
void serial8250_release_dma(struct uart_8250_port *p) | ||
{ | ||
struct uart_8250_dma *dma = p->dma; | ||
|
||
if (!dma) | ||
return; | ||
|
||
/* Release RX resources */ | ||
dmaengine_terminate_all(dma->rxchan); | ||
dma_free_coherent(dma->rxchan->device->dev, dma->rx_size, dma->rx_buf, | ||
dma->rx_addr); | ||
dma_release_channel(dma->rxchan); | ||
dma->rxchan = NULL; | ||
|
||
/* Release TX resources */ | ||
dmaengine_terminate_all(dma->txchan); | ||
dma_unmap_single(dma->txchan->device->dev, dma->tx_addr, | ||
UART_XMIT_SIZE, DMA_TO_DEVICE); | ||
dma_release_channel(dma->txchan); | ||
dma->txchan = NULL; | ||
dma->tx_running = 0; | ||
|
||
dev_dbg_ratelimited(p->port.dev, "dma channels released\n"); | ||
} | ||
EXPORT_SYMBOL_GPL(serial8250_release_dma); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters