Commit | Line | Data |
---|---|---|
9ee4b83e HK |
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; | |
f8fd1b03 | 23 | unsigned long flags; |
9ee4b83e HK |
24 | |
25 | dma_sync_single_for_cpu(dma->txchan->device->dev, dma->tx_addr, | |
26 | UART_XMIT_SIZE, DMA_TO_DEVICE); | |
27 | ||
f8fd1b03 LP |
28 | spin_lock_irqsave(&p->port.lock, flags); |
29 | ||
30 | dma->tx_running = 0; | |
31 | ||
9ee4b83e HK |
32 | xmit->tail += dma->tx_size; |
33 | xmit->tail &= UART_XMIT_SIZE - 1; | |
34 | p->port.icount.tx += dma->tx_size; | |
35 | ||
36 | if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) | |
37 | uart_write_wakeup(&p->port); | |
38 | ||
5ea5b24d | 39 | if (!uart_circ_empty(xmit) && !uart_tx_stopped(&p->port)) |
9ee4b83e | 40 | serial8250_tx_dma(p); |
f8fd1b03 LP |
41 | |
42 | spin_unlock_irqrestore(&p->port.lock, flags); | |
9ee4b83e HK |
43 | } |
44 | ||
45 | static void __dma_rx_complete(void *param) | |
46 | { | |
47 | struct uart_8250_port *p = param; | |
48 | struct uart_8250_dma *dma = p->dma; | |
6f3fe3b1 | 49 | struct tty_port *tty_port = &p->port.state->port; |
9ee4b83e | 50 | struct dma_tx_state state; |
6f3fe3b1 | 51 | int count; |
9ee4b83e HK |
52 | |
53 | dma_sync_single_for_cpu(dma->rxchan->device->dev, dma->rx_addr, | |
54 | dma->rx_size, DMA_FROM_DEVICE); | |
55 | ||
56 | dmaengine_tx_status(dma->rxchan, dma->rx_cookie, &state); | |
57 | dmaengine_terminate_all(dma->rxchan); | |
58 | ||
6f3fe3b1 | 59 | count = dma->rx_size - state.residue; |
9ee4b83e | 60 | |
6f3fe3b1 HK |
61 | tty_insert_flip_string(tty_port, dma->rx_buf, count); |
62 | p->port.icount.rx += count; | |
63 | ||
64 | tty_flip_buffer_push(tty_port); | |
9ee4b83e HK |
65 | } |
66 | ||
67 | int serial8250_tx_dma(struct uart_8250_port *p) | |
68 | { | |
69 | struct uart_8250_dma *dma = p->dma; | |
70 | struct circ_buf *xmit = &p->port.state->xmit; | |
71 | struct dma_async_tx_descriptor *desc; | |
72 | ||
5ea5b24d HK |
73 | if (uart_tx_stopped(&p->port) || dma->tx_running || |
74 | uart_circ_empty(xmit)) | |
75 | return 0; | |
9ee4b83e HK |
76 | |
77 | dma->tx_size = CIRC_CNT_TO_END(xmit->head, xmit->tail, UART_XMIT_SIZE); | |
78 | ||
79 | desc = dmaengine_prep_slave_single(dma->txchan, | |
80 | dma->tx_addr + xmit->tail, | |
81 | dma->tx_size, DMA_MEM_TO_DEV, | |
82 | DMA_PREP_INTERRUPT | DMA_CTRL_ACK); | |
83 | if (!desc) | |
84 | return -EBUSY; | |
85 | ||
86 | dma->tx_running = 1; | |
87 | ||
88 | desc->callback = __dma_tx_complete; | |
89 | desc->callback_param = p; | |
90 | ||
91 | dma->tx_cookie = dmaengine_submit(desc); | |
92 | ||
93 | dma_sync_single_for_device(dma->txchan->device->dev, dma->tx_addr, | |
94 | UART_XMIT_SIZE, DMA_TO_DEVICE); | |
95 | ||
96 | dma_async_issue_pending(dma->txchan); | |
97 | ||
98 | return 0; | |
99 | } | |
100 | EXPORT_SYMBOL_GPL(serial8250_tx_dma); | |
101 | ||
102 | int serial8250_rx_dma(struct uart_8250_port *p, unsigned int iir) | |
103 | { | |
104 | struct uart_8250_dma *dma = p->dma; | |
105 | struct dma_async_tx_descriptor *desc; | |
106 | struct dma_tx_state state; | |
107 | int dma_status; | |
108 | ||
75df022b HK |
109 | dma_status = dmaengine_tx_status(dma->rxchan, dma->rx_cookie, &state); |
110 | ||
111 | switch (iir & 0x3f) { | |
112 | case UART_IIR_RLSI: | |
113 | /* 8250_core handles errors and break interrupts */ | |
114 | return -EIO; | |
115 | case UART_IIR_RX_TIMEOUT: | |
116 | /* | |
117 | * If RCVR FIFO trigger level was not reached, complete the | |
118 | * transfer and let 8250_core copy the remaining data. | |
119 | */ | |
9ee4b83e HK |
120 | if (dma_status == DMA_IN_PROGRESS) { |
121 | dmaengine_pause(dma->rxchan); | |
122 | __dma_rx_complete(p); | |
123 | } | |
124 | return -ETIMEDOUT; | |
75df022b HK |
125 | default: |
126 | break; | |
9ee4b83e HK |
127 | } |
128 | ||
75df022b HK |
129 | if (dma_status) |
130 | return 0; | |
131 | ||
9ee4b83e HK |
132 | desc = dmaengine_prep_slave_single(dma->rxchan, dma->rx_addr, |
133 | dma->rx_size, DMA_DEV_TO_MEM, | |
134 | DMA_PREP_INTERRUPT | DMA_CTRL_ACK); | |
135 | if (!desc) | |
136 | return -EBUSY; | |
137 | ||
138 | desc->callback = __dma_rx_complete; | |
139 | desc->callback_param = p; | |
140 | ||
141 | dma->rx_cookie = dmaengine_submit(desc); | |
142 | ||
143 | dma_sync_single_for_device(dma->rxchan->device->dev, dma->rx_addr, | |
144 | dma->rx_size, DMA_FROM_DEVICE); | |
145 | ||
146 | dma_async_issue_pending(dma->rxchan); | |
147 | ||
148 | return 0; | |
149 | } | |
150 | EXPORT_SYMBOL_GPL(serial8250_rx_dma); | |
151 | ||
152 | int serial8250_request_dma(struct uart_8250_port *p) | |
153 | { | |
154 | struct uart_8250_dma *dma = p->dma; | |
155 | dma_cap_mask_t mask; | |
156 | ||
ab19479a HK |
157 | /* Default slave configuration parameters */ |
158 | dma->rxconf.direction = DMA_DEV_TO_MEM; | |
159 | dma->rxconf.src_addr_width = DMA_SLAVE_BUSWIDTH_1_BYTE; | |
160 | dma->rxconf.src_addr = p->port.mapbase + UART_RX; | |
161 | ||
162 | dma->txconf.direction = DMA_MEM_TO_DEV; | |
163 | dma->txconf.dst_addr_width = DMA_SLAVE_BUSWIDTH_1_BYTE; | |
164 | dma->txconf.dst_addr = p->port.mapbase + UART_TX; | |
9ee4b83e HK |
165 | |
166 | dma_cap_zero(mask); | |
167 | dma_cap_set(DMA_SLAVE, mask); | |
168 | ||
169 | /* Get a channel for RX */ | |
e4fb3b88 HK |
170 | dma->rxchan = dma_request_slave_channel_compat(mask, |
171 | dma->fn, dma->rx_param, | |
172 | p->port.dev, "rx"); | |
9ee4b83e HK |
173 | if (!dma->rxchan) |
174 | return -ENODEV; | |
175 | ||
176 | dmaengine_slave_config(dma->rxchan, &dma->rxconf); | |
177 | ||
178 | /* Get a channel for TX */ | |
e4fb3b88 HK |
179 | dma->txchan = dma_request_slave_channel_compat(mask, |
180 | dma->fn, dma->tx_param, | |
181 | p->port.dev, "tx"); | |
9ee4b83e HK |
182 | if (!dma->txchan) { |
183 | dma_release_channel(dma->rxchan); | |
184 | return -ENODEV; | |
185 | } | |
186 | ||
187 | dmaengine_slave_config(dma->txchan, &dma->txconf); | |
188 | ||
189 | /* RX buffer */ | |
190 | if (!dma->rx_size) | |
191 | dma->rx_size = PAGE_SIZE; | |
192 | ||
193 | dma->rx_buf = dma_alloc_coherent(dma->rxchan->device->dev, dma->rx_size, | |
194 | &dma->rx_addr, GFP_KERNEL); | |
195 | if (!dma->rx_buf) { | |
196 | dma_release_channel(dma->rxchan); | |
197 | dma_release_channel(dma->txchan); | |
198 | return -ENOMEM; | |
199 | } | |
200 | ||
201 | /* TX buffer */ | |
202 | dma->tx_addr = dma_map_single(dma->txchan->device->dev, | |
203 | p->port.state->xmit.buf, | |
204 | UART_XMIT_SIZE, | |
205 | DMA_TO_DEVICE); | |
206 | ||
207 | dev_dbg_ratelimited(p->port.dev, "got both dma channels\n"); | |
208 | ||
209 | return 0; | |
210 | } | |
211 | EXPORT_SYMBOL_GPL(serial8250_request_dma); | |
212 | ||
213 | void serial8250_release_dma(struct uart_8250_port *p) | |
214 | { | |
215 | struct uart_8250_dma *dma = p->dma; | |
216 | ||
217 | if (!dma) | |
218 | return; | |
219 | ||
220 | /* Release RX resources */ | |
221 | dmaengine_terminate_all(dma->rxchan); | |
222 | dma_free_coherent(dma->rxchan->device->dev, dma->rx_size, dma->rx_buf, | |
223 | dma->rx_addr); | |
224 | dma_release_channel(dma->rxchan); | |
225 | dma->rxchan = NULL; | |
226 | ||
227 | /* Release TX resources */ | |
228 | dmaengine_terminate_all(dma->txchan); | |
229 | dma_unmap_single(dma->txchan->device->dev, dma->tx_addr, | |
230 | UART_XMIT_SIZE, DMA_TO_DEVICE); | |
231 | dma_release_channel(dma->txchan); | |
232 | dma->txchan = NULL; | |
233 | dma->tx_running = 0; | |
234 | ||
235 | dev_dbg_ratelimited(p->port.dev, "dma channels released\n"); | |
236 | } | |
237 | EXPORT_SYMBOL_GPL(serial8250_release_dma); |