aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/tty/serial
diff options
context:
space:
mode:
authorRobert Baldyga <r.baldyga@samsung.com>2014-12-10 06:49:26 -0500
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>2015-01-09 17:25:39 -0500
commit29bef79908459d20a7a3db654bceb8f3ce4601dc (patch)
tree8bb849de9ccaf8e73e97e88debdc9ee0ae332ff4 /drivers/tty/serial
parent62c37eedb74c8fbf3c92f8f28a871e6bd80c181a (diff)
serial: samsung: add DMA support for TX
Add TX DMA transfers support for samsung serial driver. It's enabled when "dmas" property is defined in serial device-tree node, otherwise TX transfers are prerformed using PIO. TX DMA is used for data segments larger than fifosize to reduce number of interrupts during data transmission. For buffers shorter than fifosize PIO mode is selected. Data blocks for DMA transfers are aligned to cache line size to avoid problems with coherency (some areas of TX circ buffer can be used by CPU during DMA transaction, so we have to ensure that our data is always consistent). Based on previous work of Sylwester Nawrocki and Lukasz Czerwinski. Signed-off-by: Robert Baldyga <r.baldyga@samsung.com> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Diffstat (limited to 'drivers/tty/serial')
-rw-r--r--drivers/tty/serial/samsung.c238
-rw-r--r--drivers/tty/serial/samsung.h3
2 files changed, 225 insertions, 16 deletions
diff --git a/drivers/tty/serial/samsung.c b/drivers/tty/serial/samsung.c
index 825c8192b944..c04618052e53 100644
--- a/drivers/tty/serial/samsung.c
+++ b/drivers/tty/serial/samsung.c
@@ -81,6 +81,8 @@ static void dbg(const char *fmt, ...)
81#define S3C24XX_SERIAL_MAJOR 204 81#define S3C24XX_SERIAL_MAJOR 204
82#define S3C24XX_SERIAL_MINOR 64 82#define S3C24XX_SERIAL_MINOR 64
83 83
84#define S3C24XX_TX_PIO 1
85#define S3C24XX_TX_DMA 2
84/* macros to change one thing to another */ 86/* macros to change one thing to another */
85 87
86#define tx_enabled(port) ((port)->unused[0]) 88#define tx_enabled(port) ((port)->unused[0])
@@ -157,33 +159,217 @@ static void s3c24xx_serial_rx_disable(struct uart_port *port)
157static void s3c24xx_serial_stop_tx(struct uart_port *port) 159static void s3c24xx_serial_stop_tx(struct uart_port *port)
158{ 160{
159 struct s3c24xx_uart_port *ourport = to_ourport(port); 161 struct s3c24xx_uart_port *ourport = to_ourport(port);
162 struct s3c24xx_uart_dma *dma = ourport->dma;
163 struct circ_buf *xmit = &port->state->xmit;
164 struct dma_tx_state state;
165 int count;
160 166
161 if (tx_enabled(port)) { 167 if (!tx_enabled(port))
162 if (s3c24xx_serial_has_interrupt_mask(port)) 168 return;
163 __set_bit(S3C64XX_UINTM_TXD, 169
164 portaddrl(port, S3C64XX_UINTM)); 170 if (s3c24xx_serial_has_interrupt_mask(port))
165 else 171 __set_bit(S3C64XX_UINTM_TXD,
166 disable_irq_nosync(ourport->tx_irq); 172 portaddrl(port, S3C64XX_UINTM));
167 tx_enabled(port) = 0; 173 else
168 if (port->flags & UPF_CONS_FLOW) 174 disable_irq_nosync(ourport->tx_irq);
169 s3c24xx_serial_rx_enable(port); 175
176 if (dma && dma->tx_chan && ourport->tx_in_progress == S3C24XX_TX_DMA) {
177 dmaengine_pause(dma->tx_chan);
178 dmaengine_tx_status(dma->tx_chan, dma->tx_cookie, &state);
179 dmaengine_terminate_all(dma->tx_chan);
180 dma_sync_single_for_cpu(ourport->port.dev,
181 dma->tx_transfer_addr, dma->tx_size, DMA_TO_DEVICE);
182 async_tx_ack(dma->tx_desc);
183 count = dma->tx_bytes_requested - state.residue;
184 xmit->tail = (xmit->tail + count) & (UART_XMIT_SIZE - 1);
185 port->icount.tx += count;
170 } 186 }
187
188 tx_enabled(port) = 0;
189 ourport->tx_in_progress = 0;
190
191 if (port->flags & UPF_CONS_FLOW)
192 s3c24xx_serial_rx_enable(port);
193
194 ourport->tx_mode = 0;
195}
196
197static void s3c24xx_serial_start_next_tx(struct s3c24xx_uart_port *ourport);
198
199static void s3c24xx_serial_tx_dma_complete(void *args)
200{
201 struct s3c24xx_uart_port *ourport = args;
202 struct uart_port *port = &ourport->port;
203 struct circ_buf *xmit = &port->state->xmit;
204 struct s3c24xx_uart_dma *dma = ourport->dma;
205 struct dma_tx_state state;
206 unsigned long flags;
207 int count;
208
209
210 dmaengine_tx_status(dma->tx_chan, dma->tx_cookie, &state);
211 count = dma->tx_bytes_requested - state.residue;
212 async_tx_ack(dma->tx_desc);
213
214 dma_sync_single_for_cpu(ourport->port.dev, dma->tx_transfer_addr,
215 dma->tx_size, DMA_TO_DEVICE);
216
217 spin_lock_irqsave(&port->lock, flags);
218
219 xmit->tail = (xmit->tail + count) & (UART_XMIT_SIZE - 1);
220 port->icount.tx += count;
221 ourport->tx_in_progress = 0;
222
223 if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
224 uart_write_wakeup(port);
225
226 s3c24xx_serial_start_next_tx(ourport);
227 spin_unlock_irqrestore(&port->lock, flags);
228}
229
230static void enable_tx_dma(struct s3c24xx_uart_port *ourport)
231{
232 struct uart_port *port = &ourport->port;
233 u32 ucon;
234
235 /* Mask Tx interrupt */
236 if (s3c24xx_serial_has_interrupt_mask(port))
237 __set_bit(S3C64XX_UINTM_TXD,
238 portaddrl(port, S3C64XX_UINTM));
239 else
240 disable_irq_nosync(ourport->tx_irq);
241
242 /* Enable tx dma mode */
243 ucon = rd_regl(port, S3C2410_UCON);
244 ucon &= ~(S3C64XX_UCON_TXBURST_MASK | S3C64XX_UCON_TXMODE_MASK);
245 ucon |= (dma_get_cache_alignment() >= 16) ?
246 S3C64XX_UCON_TXBURST_16 : S3C64XX_UCON_TXBURST_1;
247 ucon |= S3C64XX_UCON_TXMODE_DMA;
248 wr_regl(port, S3C2410_UCON, ucon);
249
250 ourport->tx_mode = S3C24XX_TX_DMA;
251}
252
253static void enable_tx_pio(struct s3c24xx_uart_port *ourport)
254{
255 struct uart_port *port = &ourport->port;
256 u32 ucon, ufcon;
257
258 /* Set ufcon txtrig */
259 ourport->tx_in_progress = S3C24XX_TX_PIO;
260 ufcon = rd_regl(port, S3C2410_UFCON);
261 wr_regl(port, S3C2410_UFCON, ufcon);
262
263 /* Enable tx pio mode */
264 ucon = rd_regl(port, S3C2410_UCON);
265 ucon &= ~(S3C64XX_UCON_TXMODE_MASK);
266 ucon |= S3C64XX_UCON_TXMODE_CPU;
267 wr_regl(port, S3C2410_UCON, ucon);
268
269 /* Unmask Tx interrupt */
270 if (s3c24xx_serial_has_interrupt_mask(port))
271 __clear_bit(S3C64XX_UINTM_TXD,
272 portaddrl(port, S3C64XX_UINTM));
273 else
274 enable_irq(ourport->tx_irq);
275
276 ourport->tx_mode = S3C24XX_TX_PIO;
171} 277}
172 278
173static void s3c24xx_serial_start_tx(struct uart_port *port) 279static void s3c24xx_serial_start_tx_pio(struct s3c24xx_uart_port *ourport)
280{
281 if (ourport->tx_mode != S3C24XX_TX_PIO)
282 enable_tx_pio(ourport);
283}
284
285static int s3c24xx_serial_start_tx_dma(struct s3c24xx_uart_port *ourport,
286 unsigned int count)
287{
288 struct uart_port *port = &ourport->port;
289 struct circ_buf *xmit = &port->state->xmit;
290 struct s3c24xx_uart_dma *dma = ourport->dma;
291
292
293 if (ourport->tx_mode != S3C24XX_TX_DMA)
294 enable_tx_dma(ourport);
295
296 while (xmit->tail & (dma_get_cache_alignment() - 1)) {
297 if (rd_regl(port, S3C2410_UFSTAT) & ourport->info->tx_fifofull)
298 return 0;
299 wr_regb(port, S3C2410_UTXH, xmit->buf[xmit->tail]);
300 xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1);
301 port->icount.tx++;
302 count--;
303 }
304
305 dma->tx_size = count & ~(dma_get_cache_alignment() - 1);
306 dma->tx_transfer_addr = dma->tx_addr + xmit->tail;
307
308 dma_sync_single_for_device(ourport->port.dev, dma->tx_transfer_addr,
309 dma->tx_size, DMA_TO_DEVICE);
310
311 dma->tx_desc = dmaengine_prep_slave_single(dma->tx_chan,
312 dma->tx_transfer_addr, dma->tx_size,
313 DMA_MEM_TO_DEV, DMA_PREP_INTERRUPT);
314 if (!dma->tx_desc) {
315 dev_err(ourport->port.dev, "Unable to get desc for Tx\n");
316 return -EIO;
317 }
318
319 dma->tx_desc->callback = s3c24xx_serial_tx_dma_complete;
320 dma->tx_desc->callback_param = ourport;
321 dma->tx_bytes_requested = dma->tx_size;
322
323 ourport->tx_in_progress = S3C24XX_TX_DMA;
324 dma->tx_cookie = dmaengine_submit(dma->tx_desc);
325 dma_async_issue_pending(dma->tx_chan);
326 return 0;
327}
328
329static void s3c24xx_serial_start_next_tx(struct s3c24xx_uart_port *ourport)
330{
331 struct uart_port *port = &ourport->port;
332 struct circ_buf *xmit = &port->state->xmit;
333 unsigned long count;
334
335 /* Get data size up to the end of buffer */
336 count = CIRC_CNT_TO_END(xmit->head, xmit->tail, UART_XMIT_SIZE);
337
338 if (!count) {
339 s3c24xx_serial_stop_tx(port);
340 return;
341 }
342
343 if (!ourport->dma || !ourport->dma->tx_chan || count < port->fifosize)
344 s3c24xx_serial_start_tx_pio(ourport);
345 else
346 s3c24xx_serial_start_tx_dma(ourport, count);
347}
348
349void s3c24xx_serial_start_tx(struct uart_port *port)
174{ 350{
175 struct s3c24xx_uart_port *ourport = to_ourport(port); 351 struct s3c24xx_uart_port *ourport = to_ourport(port);
352 struct circ_buf *xmit = &port->state->xmit;
176 353
177 if (!tx_enabled(port)) { 354 if (!tx_enabled(port)) {
178 if (port->flags & UPF_CONS_FLOW) 355 if (port->flags & UPF_CONS_FLOW)
179 s3c24xx_serial_rx_disable(port); 356 s3c24xx_serial_rx_disable(port);
180 357
181 if (s3c24xx_serial_has_interrupt_mask(port))
182 __clear_bit(S3C64XX_UINTM_TXD,
183 portaddrl(port, S3C64XX_UINTM));
184 else
185 enable_irq(ourport->tx_irq);
186 tx_enabled(port) = 1; 358 tx_enabled(port) = 1;
359 if (!ourport->dma || !ourport->dma->tx_chan) {
360 if (s3c24xx_serial_has_interrupt_mask(port))
361 __clear_bit(S3C64XX_UINTM_TXD,
362 portaddrl(port, S3C64XX_UINTM));
363 else
364 enable_irq(ourport->tx_irq);
365
366 s3c24xx_serial_start_tx_pio(ourport);
367 }
368 }
369
370 if (ourport->dma && ourport->dma->tx_chan) {
371 if (!uart_circ_empty(xmit) && !ourport->tx_in_progress)
372 s3c24xx_serial_start_next_tx(ourport);
187 } 373 }
188} 374}
189 375
@@ -333,10 +519,17 @@ static irqreturn_t s3c24xx_serial_tx_chars(int irq, void *id)
333 struct uart_port *port = &ourport->port; 519 struct uart_port *port = &ourport->port;
334 struct circ_buf *xmit = &port->state->xmit; 520 struct circ_buf *xmit = &port->state->xmit;
335 unsigned long flags; 521 unsigned long flags;
336 int count = port->fifosize; 522 int count;
337 523
338 spin_lock_irqsave(&port->lock, flags); 524 spin_lock_irqsave(&port->lock, flags);
339 525
526 count = CIRC_CNT_TO_END(xmit->head, xmit->tail, UART_XMIT_SIZE);
527
528 if (ourport->dma && ourport->dma->tx_chan && count >= port->fifosize) {
529 s3c24xx_serial_start_tx_dma(ourport, count);
530 goto out;
531 }
532
340 if (port->x_char) { 533 if (port->x_char) {
341 wr_regb(port, S3C2410_UTXH, port->x_char); 534 wr_regb(port, S3C2410_UTXH, port->x_char);
342 port->icount.tx++; 535 port->icount.tx++;
@@ -355,6 +548,7 @@ static irqreturn_t s3c24xx_serial_tx_chars(int irq, void *id)
355 548
356 /* try and drain the buffer... */ 549 /* try and drain the buffer... */
357 550
551 count = port->fifosize;
358 while (!uart_circ_empty(xmit) && count-- > 0) { 552 while (!uart_circ_empty(xmit) && count-- > 0) {
359 if (rd_regl(port, S3C2410_UFSTAT) & ourport->info->tx_fifofull) 553 if (rd_regl(port, S3C2410_UFSTAT) & ourport->info->tx_fifofull)
360 break; 554 break;
@@ -572,6 +766,7 @@ static void s3c24xx_serial_shutdown(struct uart_port *port)
572 if (ourport->dma) 766 if (ourport->dma)
573 s3c24xx_serial_release_dma(ourport); 767 s3c24xx_serial_release_dma(ourport);
574 768
769 ourport->tx_in_progress = 0;
575} 770}
576 771
577static int s3c24xx_serial_startup(struct uart_port *port) 772static int s3c24xx_serial_startup(struct uart_port *port)
@@ -650,8 +845,19 @@ static int s3c64xx_serial_startup(struct uart_port *port)
650 tx_enabled(port) = 0; 845 tx_enabled(port) = 0;
651 ourport->tx_claimed = 1; 846 ourport->tx_claimed = 1;
652 847
848 spin_lock_irqsave(&port->lock, flags);
849
850 ufcon = rd_regl(port, S3C2410_UFCON);
851 ufcon |= S3C2410_UFCON_RESETRX | S3C2410_UFCON_RESETTX;
852 wr_regl(port, S3C2410_UFCON, ufcon);
853
854 enable_rx_pio(ourport);
855
856 spin_unlock_irqrestore(&port->lock, flags);
857
653 /* Enable Rx Interrupt */ 858 /* Enable Rx Interrupt */
654 __clear_bit(S3C64XX_UINTM_RXD, portaddrl(port, S3C64XX_UINTM)); 859 __clear_bit(S3C64XX_UINTM_RXD, portaddrl(port, S3C64XX_UINTM));
860
655 dbg("s3c64xx_serial_startup ok\n"); 861 dbg("s3c64xx_serial_startup ok\n");
656 return ret; 862 return ret;
657} 863}
diff --git a/drivers/tty/serial/samsung.h b/drivers/tty/serial/samsung.h
index 8dc250c6b472..e60011c591e8 100644
--- a/drivers/tty/serial/samsung.h
+++ b/drivers/tty/serial/samsung.h
@@ -86,6 +86,9 @@ struct s3c24xx_uart_port {
86 unsigned int rx_irq; 86 unsigned int rx_irq;
87 unsigned int tx_irq; 87 unsigned int tx_irq;
88 88
89 unsigned int tx_in_progress;
90 unsigned int tx_mode;
91
89 struct s3c24xx_uart_info *info; 92 struct s3c24xx_uart_info *info;
90 struct clk *clk; 93 struct clk *clk;
91 struct clk *baudclk; 94 struct clk *baudclk;