Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

serial: samsung: add DMA support for RX

Add RX DMA transfers support for samsung serial driver. It's enabled
when DMA controller for RX channel is specified in device-tree.

DMA transactions are started when number of bytes in RX FIFO reaches
trigger level, otherwise PIO mode is used. DMA transfer size is always
PAGE_SIZE which can cause large latency when smaller data amount is
transferred, so we always terminate DMA transaction on RX timeout
interrupt. Timeout interval is set to 64 frame times.

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>

authored by

Robert Baldyga and committed by
Greg Kroah-Hartman
b543c301 29bef799

+231 -3
+230 -3
drivers/tty/serial/samsung.c
··· 83 83 84 84 #define S3C24XX_TX_PIO 1 85 85 #define S3C24XX_TX_DMA 2 86 + #define S3C24XX_RX_PIO 1 87 + #define S3C24XX_RX_DMA 2 86 88 /* macros to change one thing to another */ 87 89 88 90 #define tx_enabled(port) ((port)->unused[0]) ··· 375 373 } 376 374 } 377 375 376 + static void s3c24xx_uart_copy_rx_to_tty(struct s3c24xx_uart_port *ourport, 377 + struct tty_port *tty, int count) 378 + { 379 + struct s3c24xx_uart_dma *dma = ourport->dma; 380 + int copied; 381 + 382 + if (!count) 383 + return; 384 + 385 + dma_sync_single_for_cpu(ourport->port.dev, dma->rx_addr, 386 + dma->rx_size, DMA_FROM_DEVICE); 387 + 388 + ourport->port.icount.rx += count; 389 + if (!tty) { 390 + dev_err(ourport->port.dev, "No tty port\n"); 391 + return; 392 + } 393 + copied = tty_insert_flip_string(tty, 394 + ((unsigned char *)(ourport->dma->rx_buf)), count); 395 + if (copied != count) { 396 + WARN_ON(1); 397 + dev_err(ourport->port.dev, "RxData copy to tty layer failed\n"); 398 + } 399 + } 400 + 401 + static int s3c24xx_serial_rx_fifocnt(struct s3c24xx_uart_port *ourport, 402 + unsigned long ufstat); 403 + 404 + static void uart_rx_drain_fifo(struct s3c24xx_uart_port *ourport) 405 + { 406 + struct uart_port *port = &ourport->port; 407 + struct tty_port *tty = &port->state->port; 408 + unsigned int ch, ufstat; 409 + unsigned int count; 410 + 411 + ufstat = rd_regl(port, S3C2410_UFSTAT); 412 + count = s3c24xx_serial_rx_fifocnt(ourport, ufstat); 413 + 414 + if (!count) 415 + return; 416 + 417 + while (count-- > 0) { 418 + ch = rd_regb(port, S3C2410_URXH); 419 + 420 + ourport->port.icount.rx++; 421 + tty_insert_flip_char(tty, ch, TTY_NORMAL); 422 + } 423 + 424 + tty_flip_buffer_push(tty); 425 + } 426 + 378 427 static void s3c24xx_serial_stop_rx(struct uart_port *port) 379 428 { 380 429 struct s3c24xx_uart_port *ourport = to_ourport(port); 430 + struct s3c24xx_uart_dma *dma = ourport->dma; 431 + struct tty_port *t = &port->state->port; 432 + struct dma_tx_state state; 433 + enum dma_status dma_status; 434 + unsigned int received; 381 435 382 436 if (rx_enabled(port)) { 383 437 dbg("s3c24xx_serial_stop_rx: port=%p\n", port); ··· 443 385 else 444 386 disable_irq_nosync(ourport->rx_irq); 445 387 rx_enabled(port) = 0; 388 + } 389 + if (dma && dma->rx_chan) { 390 + dmaengine_pause(dma->tx_chan); 391 + dma_status = dmaengine_tx_status(dma->rx_chan, 392 + dma->rx_cookie, &state); 393 + if (dma_status == DMA_IN_PROGRESS || 394 + dma_status == DMA_PAUSED) { 395 + received = dma->rx_bytes_requested - state.residue; 396 + dmaengine_terminate_all(dma->rx_chan); 397 + s3c24xx_uart_copy_rx_to_tty(ourport, t, received); 398 + } 446 399 } 447 400 } 448 401 ··· 486 417 return (ufstat & info->rx_fifomask) >> info->rx_fifoshift; 487 418 } 488 419 420 + static void s3c64xx_start_rx_dma(struct s3c24xx_uart_port *ourport); 421 + static void s3c24xx_serial_rx_dma_complete(void *args) 422 + { 423 + struct s3c24xx_uart_port *ourport = args; 424 + struct uart_port *port = &ourport->port; 425 + 426 + struct s3c24xx_uart_dma *dma = ourport->dma; 427 + struct tty_port *t = &port->state->port; 428 + struct tty_struct *tty = tty_port_tty_get(&ourport->port.state->port); 429 + 430 + struct dma_tx_state state; 431 + unsigned long flags; 432 + int received; 433 + 434 + dmaengine_tx_status(dma->rx_chan, dma->rx_cookie, &state); 435 + received = dma->rx_bytes_requested - state.residue; 436 + async_tx_ack(dma->rx_desc); 437 + 438 + spin_lock_irqsave(&port->lock, flags); 439 + 440 + if (received) 441 + s3c24xx_uart_copy_rx_to_tty(ourport, t, received); 442 + 443 + if (tty) { 444 + tty_flip_buffer_push(t); 445 + tty_kref_put(tty); 446 + } 447 + 448 + s3c64xx_start_rx_dma(ourport); 449 + 450 + spin_unlock_irqrestore(&port->lock, flags); 451 + } 452 + 453 + static void s3c64xx_start_rx_dma(struct s3c24xx_uart_port *ourport) 454 + { 455 + struct s3c24xx_uart_dma *dma = ourport->dma; 456 + 457 + dma_sync_single_for_device(ourport->port.dev, dma->rx_addr, 458 + dma->rx_size, DMA_FROM_DEVICE); 459 + 460 + dma->rx_desc = dmaengine_prep_slave_single(dma->rx_chan, 461 + dma->rx_addr, dma->rx_size, DMA_DEV_TO_MEM, 462 + DMA_PREP_INTERRUPT); 463 + if (!dma->rx_desc) { 464 + dev_err(ourport->port.dev, "Unable to get desc for Rx\n"); 465 + return; 466 + } 467 + 468 + dma->rx_desc->callback = s3c24xx_serial_rx_dma_complete; 469 + dma->rx_desc->callback_param = ourport; 470 + dma->rx_bytes_requested = dma->rx_size; 471 + 472 + dma->rx_cookie = dmaengine_submit(dma->rx_desc); 473 + dma_async_issue_pending(dma->rx_chan); 474 + } 489 475 490 476 /* ? - where has parity gone?? */ 491 477 #define S3C2410_UERSTAT_PARITY (0x1000) 492 478 493 - static irqreturn_t 494 - s3c24xx_serial_rx_chars(int irq, void *dev_id) 479 + static void enable_rx_dma(struct s3c24xx_uart_port *ourport) 480 + { 481 + struct uart_port *port = &ourport->port; 482 + unsigned int ucon; 483 + 484 + /* set Rx mode to DMA mode */ 485 + ucon = rd_regl(port, S3C2410_UCON); 486 + ucon &= ~(S3C64XX_UCON_RXBURST_MASK | 487 + S3C64XX_UCON_TIMEOUT_MASK | 488 + S3C64XX_UCON_EMPTYINT_EN | 489 + S3C64XX_UCON_DMASUS_EN | 490 + S3C64XX_UCON_TIMEOUT_EN | 491 + S3C64XX_UCON_RXMODE_MASK); 492 + ucon |= S3C64XX_UCON_RXBURST_16 | 493 + 0xf << S3C64XX_UCON_TIMEOUT_SHIFT | 494 + S3C64XX_UCON_EMPTYINT_EN | 495 + S3C64XX_UCON_TIMEOUT_EN | 496 + S3C64XX_UCON_RXMODE_DMA; 497 + wr_regl(port, S3C2410_UCON, ucon); 498 + 499 + ourport->rx_mode = S3C24XX_RX_DMA; 500 + } 501 + 502 + static void enable_rx_pio(struct s3c24xx_uart_port *ourport) 503 + { 504 + struct uart_port *port = &ourport->port; 505 + unsigned int ucon; 506 + 507 + /* set Rx mode to DMA mode */ 508 + ucon = rd_regl(port, S3C2410_UCON); 509 + ucon &= ~(S3C64XX_UCON_TIMEOUT_MASK | 510 + S3C64XX_UCON_EMPTYINT_EN | 511 + S3C64XX_UCON_DMASUS_EN | 512 + S3C64XX_UCON_TIMEOUT_EN | 513 + S3C64XX_UCON_RXMODE_MASK); 514 + ucon |= 0xf << S3C64XX_UCON_TIMEOUT_SHIFT | 515 + S3C64XX_UCON_TIMEOUT_EN | 516 + S3C64XX_UCON_RXMODE_CPU; 517 + wr_regl(port, S3C2410_UCON, ucon); 518 + 519 + ourport->rx_mode = S3C24XX_RX_PIO; 520 + } 521 + 522 + static irqreturn_t s3c24xx_serial_rx_chars_dma(int irq, void *dev_id) 523 + { 524 + unsigned int utrstat, ufstat, received; 525 + struct s3c24xx_uart_port *ourport = dev_id; 526 + struct uart_port *port = &ourport->port; 527 + struct s3c24xx_uart_dma *dma = ourport->dma; 528 + struct tty_struct *tty = tty_port_tty_get(&ourport->port.state->port); 529 + struct tty_port *t = &port->state->port; 530 + unsigned long flags; 531 + struct dma_tx_state state; 532 + 533 + utrstat = rd_regl(port, S3C2410_UTRSTAT); 534 + ufstat = rd_regl(port, S3C2410_UFSTAT); 535 + 536 + spin_lock_irqsave(&port->lock, flags); 537 + 538 + if (!(utrstat & S3C2410_UTRSTAT_TIMEOUT)) { 539 + s3c64xx_start_rx_dma(ourport); 540 + if (ourport->rx_mode == S3C24XX_RX_PIO) 541 + enable_rx_dma(ourport); 542 + goto finish; 543 + } 544 + 545 + if (ourport->rx_mode == S3C24XX_RX_DMA) { 546 + dmaengine_pause(dma->rx_chan); 547 + dmaengine_tx_status(dma->rx_chan, dma->rx_cookie, &state); 548 + dmaengine_terminate_all(dma->rx_chan); 549 + received = dma->rx_bytes_requested - state.residue; 550 + s3c24xx_uart_copy_rx_to_tty(ourport, t, received); 551 + 552 + enable_rx_pio(ourport); 553 + } 554 + 555 + uart_rx_drain_fifo(ourport); 556 + 557 + if (tty) { 558 + tty_flip_buffer_push(t); 559 + tty_kref_put(tty); 560 + } 561 + 562 + wr_regl(port, S3C2410_UTRSTAT, S3C2410_UTRSTAT_TIMEOUT); 563 + 564 + finish: 565 + spin_unlock_irqrestore(&port->lock, flags); 566 + 567 + return IRQ_HANDLED; 568 + } 569 + 570 + static irqreturn_t s3c24xx_serial_rx_chars_pio(int irq, void *dev_id) 495 571 { 496 572 struct s3c24xx_uart_port *ourport = dev_id; 497 573 struct uart_port *port = &ourport->port; ··· 725 511 726 512 out: 727 513 return IRQ_HANDLED; 514 + } 515 + 516 + 517 + static irqreturn_t s3c24xx_serial_rx_chars(int irq, void *dev_id) 518 + { 519 + struct s3c24xx_uart_port *ourport = dev_id; 520 + 521 + if (ourport->dma && ourport->dma->rx_chan) 522 + return s3c24xx_serial_rx_chars_dma(irq, dev_id); 523 + return s3c24xx_serial_rx_chars_pio(irq, dev_id); 728 524 } 729 525 730 526 static irqreturn_t s3c24xx_serial_tx_chars(int irq, void *id) ··· 1042 818 static int s3c64xx_serial_startup(struct uart_port *port) 1043 819 { 1044 820 struct s3c24xx_uart_port *ourport = to_ourport(port); 821 + unsigned long flags; 822 + unsigned int ufcon; 1045 823 int ret; 1046 824 1047 825 dbg("s3c64xx_serial_startup: port=%p (%08llx,%p)\n", ··· 1074 848 spin_lock_irqsave(&port->lock, flags); 1075 849 1076 850 ufcon = rd_regl(port, S3C2410_UFCON); 1077 - ufcon |= S3C2410_UFCON_RESETRX | S3C2410_UFCON_RESETTX; 851 + ufcon |= S3C2410_UFCON_RESETRX | S3C2410_UFCON_RESETTX | 852 + S5PV210_UFCON_RXTRIG8; 1078 853 wr_regl(port, S3C2410_UFCON, ufcon); 1079 854 1080 855 enable_rx_pio(ourport);
+1
drivers/tty/serial/samsung.h
··· 88 88 89 89 unsigned int tx_in_progress; 90 90 unsigned int tx_mode; 91 + unsigned int rx_mode; 91 92 92 93 struct s3c24xx_uart_info *info; 93 94 struct clk *clk;