Skip to content

Commit 9ee4b83

Browse files
Heikki Krogerusgregkh
authored andcommitted
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 <[email protected]> Signed-off-by: Greg Kroah-Hartman <[email protected]>
1 parent 6a7320c commit 9ee4b83

File tree

6 files changed

+304
-3
lines changed

6 files changed

+304
-3
lines changed

drivers/tty/serial/8250/8250.c

Lines changed: 28 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1269,7 +1269,9 @@ static void serial8250_start_tx(struct uart_port *port)
12691269
struct uart_8250_port *up =
12701270
container_of(port, struct uart_8250_port, port);
12711271

1272-
if (!(up->ier & UART_IER_THRI)) {
1272+
if (up->dma && !serial8250_tx_dma(up)) {
1273+
return;
1274+
} else if (!(up->ier & UART_IER_THRI)) {
12731275
up->ier |= UART_IER_THRI;
12741276
serial_port_out(port, UART_IER, up->ier);
12751277

@@ -1467,6 +1469,7 @@ int serial8250_handle_irq(struct uart_port *port, unsigned int iir)
14671469
unsigned long flags;
14681470
struct uart_8250_port *up =
14691471
container_of(port, struct uart_8250_port, port);
1472+
int dma_err = 0;
14701473

14711474
if (iir & UART_IIR_NO_INT)
14721475
return 0;
@@ -1477,8 +1480,13 @@ int serial8250_handle_irq(struct uart_port *port, unsigned int iir)
14771480

14781481
DEBUG_INTR("status = %x...", status);
14791482

1480-
if (status & (UART_LSR_DR | UART_LSR_BI))
1481-
status = serial8250_rx_chars(up, status);
1483+
if (status & (UART_LSR_DR | UART_LSR_BI)) {
1484+
if (up->dma)
1485+
dma_err = serial8250_rx_dma(up, iir);
1486+
1487+
if (!up->dma || dma_err)
1488+
status = serial8250_rx_chars(up, status);
1489+
}
14821490
serial8250_modem_status(up);
14831491
if (status & UART_LSR_THRE)
14841492
serial8250_tx_chars(up);
@@ -2120,6 +2128,18 @@ static int serial8250_startup(struct uart_port *port)
21202128
up->lsr_saved_flags = 0;
21212129
up->msr_saved_flags = 0;
21222130

2131+
/*
2132+
* Request DMA channels for both RX and TX.
2133+
*/
2134+
if (up->dma) {
2135+
retval = serial8250_request_dma(up);
2136+
if (retval) {
2137+
pr_warn_ratelimited("ttyS%d - failed to request DMA\n",
2138+
serial_index(port));
2139+
up->dma = NULL;
2140+
}
2141+
}
2142+
21232143
/*
21242144
* Finally, enable interrupts. Note: Modem status interrupts
21252145
* are set via set_termios(), which will be occurring imminently
@@ -2153,6 +2173,9 @@ static void serial8250_shutdown(struct uart_port *port)
21532173
up->ier = 0;
21542174
serial_port_out(port, UART_IER, 0);
21552175

2176+
if (up->dma)
2177+
serial8250_release_dma(up);
2178+
21562179
spin_lock_irqsave(&port->lock, flags);
21572180
if (port->flags & UPF_FOURPORT) {
21582181
/* reset interrupts on the AST Fourport board */
@@ -3217,6 +3240,8 @@ int serial8250_register_8250_port(struct uart_8250_port *up)
32173240
uart->dl_read = up->dl_read;
32183241
if (up->dl_write)
32193242
uart->dl_write = up->dl_write;
3243+
if (up->dma)
3244+
uart->dma = up->dma;
32203245

32213246
if (serial8250_isa_config != NULL)
32223247
serial8250_isa_config(0, &uart->port,

drivers/tty/serial/8250/8250.h

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,35 @@
1212
*/
1313

1414
#include <linux/serial_8250.h>
15+
#include <linux/dmaengine.h>
16+
17+
struct uart_8250_dma {
18+
dma_filter_fn fn;
19+
void *rx_param;
20+
void *tx_param;
21+
22+
int rx_chan_id;
23+
int tx_chan_id;
24+
25+
struct dma_slave_config rxconf;
26+
struct dma_slave_config txconf;
27+
28+
struct dma_chan *rxchan;
29+
struct dma_chan *txchan;
30+
31+
dma_addr_t rx_addr;
32+
dma_addr_t tx_addr;
33+
34+
dma_cookie_t rx_cookie;
35+
dma_cookie_t tx_cookie;
36+
37+
void *rx_buf;
38+
39+
size_t rx_size;
40+
size_t tx_size;
41+
42+
unsigned char tx_running:1;
43+
};
1544

1645
struct old_serial_port {
1746
unsigned int uart;
@@ -142,3 +171,24 @@ static inline int is_omap1510_8250(struct uart_8250_port *pt)
142171
return 0;
143172
}
144173
#endif
174+
175+
#ifdef CONFIG_SERIAL_8250_DMA
176+
extern int serial8250_tx_dma(struct uart_8250_port *);
177+
extern int serial8250_rx_dma(struct uart_8250_port *, unsigned int iir);
178+
extern int serial8250_request_dma(struct uart_8250_port *);
179+
extern void serial8250_release_dma(struct uart_8250_port *);
180+
#else
181+
static inline int serial8250_tx_dma(struct uart_8250_port *p)
182+
{
183+
return -1;
184+
}
185+
static inline int serial8250_rx_dma(struct uart_8250_port *p, unsigned int iir)
186+
{
187+
return -1;
188+
}
189+
static inline int serial8250_request_dma(struct uart_8250_port *p)
190+
{
191+
return -1;
192+
}
193+
static inline void serial8250_release_dma(struct uart_8250_port *p) { }
194+
#endif

drivers/tty/serial/8250/8250_dma.c

Lines changed: 213 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,213 @@
1+
/*
2+
* 8250_dma.c - DMA Engine API support for 8250.c
3+
*
4+
* Copyright (C) 2013 Intel Corporation
5+
*
6+
* This program is free software; you can redistribute it and/or modify
7+
* it under the terms of the GNU General Public License as published by
8+
* the Free Software Foundation; either version 2 of the License, or
9+
* (at your option) any later version.
10+
*/
11+
#include <linux/tty.h>
12+
#include <linux/tty_flip.h>
13+
#include <linux/serial_reg.h>
14+
#include <linux/dma-mapping.h>
15+
16+
#include "8250.h"
17+
18+
static void __dma_tx_complete(void *param)
19+
{
20+
struct uart_8250_port *p = param;
21+
struct uart_8250_dma *dma = p->dma;
22+
struct circ_buf *xmit = &p->port.state->xmit;
23+
24+
dma->tx_running = 0;
25+
26+
dma_sync_single_for_cpu(dma->txchan->device->dev, dma->tx_addr,
27+
UART_XMIT_SIZE, DMA_TO_DEVICE);
28+
29+
xmit->tail += dma->tx_size;
30+
xmit->tail &= UART_XMIT_SIZE - 1;
31+
p->port.icount.tx += dma->tx_size;
32+
33+
if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
34+
uart_write_wakeup(&p->port);
35+
36+
if (!uart_circ_empty(xmit) && !uart_tx_stopped(&p->port)) {
37+
serial8250_tx_dma(p);
38+
uart_write_wakeup(&p->port);
39+
}
40+
}
41+
42+
static void __dma_rx_complete(void *param)
43+
{
44+
struct uart_8250_port *p = param;
45+
struct uart_8250_dma *dma = p->dma;
46+
struct tty_struct *tty = p->port.state->port.tty;
47+
struct dma_tx_state state;
48+
49+
dma_sync_single_for_cpu(dma->rxchan->device->dev, dma->rx_addr,
50+
dma->rx_size, DMA_FROM_DEVICE);
51+
52+
dmaengine_tx_status(dma->rxchan, dma->rx_cookie, &state);
53+
dmaengine_terminate_all(dma->rxchan);
54+
55+
tty_insert_flip_string(tty, dma->rx_buf, dma->rx_size - state.residue);
56+
p->port.icount.rx += dma->rx_size - state.residue;
57+
58+
tty_flip_buffer_push(tty);
59+
}
60+
61+
int serial8250_tx_dma(struct uart_8250_port *p)
62+
{
63+
struct uart_8250_dma *dma = p->dma;
64+
struct circ_buf *xmit = &p->port.state->xmit;
65+
struct dma_async_tx_descriptor *desc;
66+
67+
if (dma->tx_running) {
68+
uart_write_wakeup(&p->port);
69+
return -EBUSY;
70+
}
71+
72+
dma->tx_size = CIRC_CNT_TO_END(xmit->head, xmit->tail, UART_XMIT_SIZE);
73+
74+
desc = dmaengine_prep_slave_single(dma->txchan,
75+
dma->tx_addr + xmit->tail,
76+
dma->tx_size, DMA_MEM_TO_DEV,
77+
DMA_PREP_INTERRUPT | DMA_CTRL_ACK);
78+
if (!desc)
79+
return -EBUSY;
80+
81+
dma->tx_running = 1;
82+
83+
desc->callback = __dma_tx_complete;
84+
desc->callback_param = p;
85+
86+
dma->tx_cookie = dmaengine_submit(desc);
87+
88+
dma_sync_single_for_device(dma->txchan->device->dev, dma->tx_addr,
89+
UART_XMIT_SIZE, DMA_TO_DEVICE);
90+
91+
dma_async_issue_pending(dma->txchan);
92+
93+
return 0;
94+
}
95+
EXPORT_SYMBOL_GPL(serial8250_tx_dma);
96+
97+
int serial8250_rx_dma(struct uart_8250_port *p, unsigned int iir)
98+
{
99+
struct uart_8250_dma *dma = p->dma;
100+
struct dma_async_tx_descriptor *desc;
101+
struct dma_tx_state state;
102+
int dma_status;
103+
104+
/*
105+
* If RCVR FIFO trigger level was not reached, complete the transfer and
106+
* let 8250.c copy the remaining data.
107+
*/
108+
if ((iir & 0x3f) == UART_IIR_RX_TIMEOUT) {
109+
dma_status = dmaengine_tx_status(dma->rxchan, dma->rx_cookie,
110+
&state);
111+
if (dma_status == DMA_IN_PROGRESS) {
112+
dmaengine_pause(dma->rxchan);
113+
__dma_rx_complete(p);
114+
}
115+
return -ETIMEDOUT;
116+
}
117+
118+
desc = dmaengine_prep_slave_single(dma->rxchan, dma->rx_addr,
119+
dma->rx_size, DMA_DEV_TO_MEM,
120+
DMA_PREP_INTERRUPT | DMA_CTRL_ACK);
121+
if (!desc)
122+
return -EBUSY;
123+
124+
desc->callback = __dma_rx_complete;
125+
desc->callback_param = p;
126+
127+
dma->rx_cookie = dmaengine_submit(desc);
128+
129+
dma_sync_single_for_device(dma->rxchan->device->dev, dma->rx_addr,
130+
dma->rx_size, DMA_FROM_DEVICE);
131+
132+
dma_async_issue_pending(dma->rxchan);
133+
134+
return 0;
135+
}
136+
EXPORT_SYMBOL_GPL(serial8250_rx_dma);
137+
138+
int serial8250_request_dma(struct uart_8250_port *p)
139+
{
140+
struct uart_8250_dma *dma = p->dma;
141+
dma_cap_mask_t mask;
142+
143+
dma->rxconf.src_addr = p->port.mapbase + UART_RX;
144+
dma->txconf.dst_addr = p->port.mapbase + UART_TX;
145+
146+
dma_cap_zero(mask);
147+
dma_cap_set(DMA_SLAVE, mask);
148+
149+
/* Get a channel for RX */
150+
dma->rxchan = dma_request_channel(mask, dma->fn, dma->rx_param);
151+
if (!dma->rxchan)
152+
return -ENODEV;
153+
154+
dmaengine_slave_config(dma->rxchan, &dma->rxconf);
155+
156+
/* Get a channel for TX */
157+
dma->txchan = dma_request_channel(mask, dma->fn, dma->tx_param);
158+
if (!dma->txchan) {
159+
dma_release_channel(dma->rxchan);
160+
return -ENODEV;
161+
}
162+
163+
dmaengine_slave_config(dma->txchan, &dma->txconf);
164+
165+
/* RX buffer */
166+
if (!dma->rx_size)
167+
dma->rx_size = PAGE_SIZE;
168+
169+
dma->rx_buf = dma_alloc_coherent(dma->rxchan->device->dev, dma->rx_size,
170+
&dma->rx_addr, GFP_KERNEL);
171+
if (!dma->rx_buf) {
172+
dma_release_channel(dma->rxchan);
173+
dma_release_channel(dma->txchan);
174+
return -ENOMEM;
175+
}
176+
177+
/* TX buffer */
178+
dma->tx_addr = dma_map_single(dma->txchan->device->dev,
179+
p->port.state->xmit.buf,
180+
UART_XMIT_SIZE,
181+
DMA_TO_DEVICE);
182+
183+
dev_dbg_ratelimited(p->port.dev, "got both dma channels\n");
184+
185+
return 0;
186+
}
187+
EXPORT_SYMBOL_GPL(serial8250_request_dma);
188+
189+
void serial8250_release_dma(struct uart_8250_port *p)
190+
{
191+
struct uart_8250_dma *dma = p->dma;
192+
193+
if (!dma)
194+
return;
195+
196+
/* Release RX resources */
197+
dmaengine_terminate_all(dma->rxchan);
198+
dma_free_coherent(dma->rxchan->device->dev, dma->rx_size, dma->rx_buf,
199+
dma->rx_addr);
200+
dma_release_channel(dma->rxchan);
201+
dma->rxchan = NULL;
202+
203+
/* Release TX resources */
204+
dmaengine_terminate_all(dma->txchan);
205+
dma_unmap_single(dma->txchan->device->dev, dma->tx_addr,
206+
UART_XMIT_SIZE, DMA_TO_DEVICE);
207+
dma_release_channel(dma->txchan);
208+
dma->txchan = NULL;
209+
dma->tx_running = 0;
210+
211+
dev_dbg_ratelimited(p->port.dev, "dma channels released\n");
212+
}
213+
EXPORT_SYMBOL_GPL(serial8250_release_dma);

drivers/tty/serial/8250/Kconfig

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,14 @@ config SERIAL_8250_GSC
8484
depends on SERIAL_8250 && GSC
8585
default SERIAL_8250
8686

87+
config SERIAL_8250_DMA
88+
bool "DMA support for 16550 compatible UART controllers" if EXPERT
89+
depends on SERIAL_8250 && DMADEVICES=y
90+
default SERIAL_8250
91+
help
92+
This builds DMA support that can be used with 8250/16650
93+
compatible UART controllers that support DMA signaling.
94+
8795
config SERIAL_8250_PCI
8896
tristate "8250/16550 PCI device support" if EXPERT
8997
depends on SERIAL_8250 && PCI

drivers/tty/serial/8250/Makefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
obj-$(CONFIG_SERIAL_8250) += 8250_core.o
66
8250_core-y := 8250.o
77
8250_core-$(CONFIG_SERIAL_8250_PNP) += 8250_pnp.o
8+
8250_core-$(CONFIG_SERIAL_8250_DMA) += 8250_dma.o
89
obj-$(CONFIG_SERIAL_8250_GSC) += 8250_gsc.o
910
obj-$(CONFIG_SERIAL_8250_PCI) += 8250_pci.o
1011
obj-$(CONFIG_SERIAL_8250_HP300) += 8250_hp300.o

include/linux/serial_8250.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,8 @@ enum {
5959
PLAT8250_DEV_SM501,
6060
};
6161

62+
struct uart_8250_dma;
63+
6264
/*
6365
* This should be used by drivers which want to register
6466
* their own 8250 ports without registering their own
@@ -91,6 +93,8 @@ struct uart_8250_port {
9193
#define MSR_SAVE_FLAGS UART_MSR_ANY_DELTA
9294
unsigned char msr_saved_flags;
9395

96+
struct uart_8250_dma *dma;
97+
9498
/* 8250 specific callbacks */
9599
int (*dl_read)(struct uart_8250_port *);
96100
void (*dl_write)(struct uart_8250_port *, int);

0 commit comments

Comments
 (0)