diff options
author | Tomoya MORINAGA <tomoya-linux@dsn.okisemi.com> | 2011-02-22 20:03:12 -0500 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@suse.de> | 2011-02-22 20:22:02 -0500 |
commit | da3564ee027e788a5ff8e520fb2d2b00a78b2464 (patch) | |
tree | 31cb96fb375b2c4ed85e48744392903b0828275e /drivers/tty/serial | |
parent | 4c37705877e74c02c968735c2eee0f84914cf557 (diff) |
pch_uart: add multi-scatter processing
Currently, this driver can handle only single scatterlist.
Thus, it can't send data beyond FIFO size.
This patch enables this driver can handle multiple scatter list.
Signed-off-by: Tomoya MORINAGA <tomoya-linux@dsn.okisemi.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
Diffstat (limited to 'drivers/tty/serial')
-rw-r--r-- | drivers/tty/serial/pch_uart.c | 117 |
1 files changed, 89 insertions, 28 deletions
diff --git a/drivers/tty/serial/pch_uart.c b/drivers/tty/serial/pch_uart.c index 3b2fb93e1fa1..c1386eb255d3 100644 --- a/drivers/tty/serial/pch_uart.c +++ b/drivers/tty/serial/pch_uart.c | |||
@@ -226,7 +226,8 @@ struct eg20t_port { | |||
226 | struct pch_dma_slave param_rx; | 226 | struct pch_dma_slave param_rx; |
227 | struct dma_chan *chan_tx; | 227 | struct dma_chan *chan_tx; |
228 | struct dma_chan *chan_rx; | 228 | struct dma_chan *chan_rx; |
229 | struct scatterlist sg_tx; | 229 | struct scatterlist *sg_tx_p; |
230 | int nent; | ||
230 | struct scatterlist sg_rx; | 231 | struct scatterlist sg_rx; |
231 | int tx_dma_use; | 232 | int tx_dma_use; |
232 | void *rx_buf_virt; | 233 | void *rx_buf_virt; |
@@ -595,16 +596,20 @@ static void pch_dma_rx_complete(void *arg) | |||
595 | struct eg20t_port *priv = arg; | 596 | struct eg20t_port *priv = arg; |
596 | struct uart_port *port = &priv->port; | 597 | struct uart_port *port = &priv->port; |
597 | struct tty_struct *tty = tty_port_tty_get(&port->state->port); | 598 | struct tty_struct *tty = tty_port_tty_get(&port->state->port); |
599 | int count; | ||
598 | 600 | ||
599 | if (!tty) { | 601 | if (!tty) { |
600 | pr_debug("%s:tty is busy now", __func__); | 602 | pr_debug("%s:tty is busy now", __func__); |
601 | return; | 603 | return; |
602 | } | 604 | } |
603 | 605 | ||
604 | if (dma_push_rx(priv, priv->trigger_level)) | 606 | dma_sync_sg_for_cpu(port->dev, &priv->sg_rx, 1, DMA_FROM_DEVICE); |
607 | count = dma_push_rx(priv, priv->trigger_level); | ||
608 | if (count) | ||
605 | tty_flip_buffer_push(tty); | 609 | tty_flip_buffer_push(tty); |
606 | |||
607 | tty_kref_put(tty); | 610 | tty_kref_put(tty); |
611 | async_tx_ack(priv->desc_rx); | ||
612 | pch_uart_hal_enable_interrupt(priv, PCH_UART_HAL_RX_INT); | ||
608 | } | 613 | } |
609 | 614 | ||
610 | static void pch_dma_tx_complete(void *arg) | 615 | static void pch_dma_tx_complete(void *arg) |
@@ -612,13 +617,21 @@ static void pch_dma_tx_complete(void *arg) | |||
612 | struct eg20t_port *priv = arg; | 617 | struct eg20t_port *priv = arg; |
613 | struct uart_port *port = &priv->port; | 618 | struct uart_port *port = &priv->port; |
614 | struct circ_buf *xmit = &port->state->xmit; | 619 | struct circ_buf *xmit = &port->state->xmit; |
620 | struct scatterlist *sg = priv->sg_tx_p; | ||
621 | int i; | ||
615 | 622 | ||
616 | xmit->tail += sg_dma_len(&priv->sg_tx); | 623 | for (i = 0; i < priv->nent; i++, sg++) { |
624 | xmit->tail += sg_dma_len(sg); | ||
625 | port->icount.tx += sg_dma_len(sg); | ||
626 | } | ||
617 | xmit->tail &= UART_XMIT_SIZE - 1; | 627 | xmit->tail &= UART_XMIT_SIZE - 1; |
618 | port->icount.tx += sg_dma_len(&priv->sg_tx); | ||
619 | |||
620 | async_tx_ack(priv->desc_tx); | 628 | async_tx_ack(priv->desc_tx); |
629 | dma_unmap_sg(port->dev, sg, priv->nent, DMA_TO_DEVICE); | ||
621 | priv->tx_dma_use = 0; | 630 | priv->tx_dma_use = 0; |
631 | priv->nent = 0; | ||
632 | kfree(priv->sg_tx_p); | ||
633 | if (uart_circ_chars_pending(xmit)) | ||
634 | pch_uart_hal_enable_interrupt(priv, PCH_UART_HAL_TX_INT); | ||
622 | } | 635 | } |
623 | 636 | ||
624 | static int pop_tx(struct eg20t_port *priv, unsigned char *buf, int size) | 637 | static int pop_tx(struct eg20t_port *priv, unsigned char *buf, int size) |
@@ -682,7 +695,7 @@ static int dma_handle_rx(struct eg20t_port *priv) | |||
682 | 695 | ||
683 | sg_init_table(&priv->sg_rx, 1); /* Initialize SG table */ | 696 | sg_init_table(&priv->sg_rx, 1); /* Initialize SG table */ |
684 | 697 | ||
685 | sg_dma_len(sg) = priv->fifo_size; | 698 | sg_dma_len(sg) = priv->trigger_level; |
686 | 699 | ||
687 | sg_set_page(&priv->sg_rx, virt_to_page(priv->rx_buf_virt), | 700 | sg_set_page(&priv->sg_rx, virt_to_page(priv->rx_buf_virt), |
688 | sg_dma_len(sg), (unsigned long)priv->rx_buf_virt & | 701 | sg_dma_len(sg), (unsigned long)priv->rx_buf_virt & |
@@ -692,7 +705,8 @@ static int dma_handle_rx(struct eg20t_port *priv) | |||
692 | 705 | ||
693 | desc = priv->chan_rx->device->device_prep_slave_sg(priv->chan_rx, | 706 | desc = priv->chan_rx->device->device_prep_slave_sg(priv->chan_rx, |
694 | sg, 1, DMA_FROM_DEVICE, | 707 | sg, 1, DMA_FROM_DEVICE, |
695 | DMA_PREP_INTERRUPT); | 708 | DMA_PREP_INTERRUPT | DMA_CTRL_ACK); |
709 | |||
696 | if (!desc) | 710 | if (!desc) |
697 | return 0; | 711 | return 0; |
698 | 712 | ||
@@ -731,6 +745,9 @@ static unsigned int handle_tx(struct eg20t_port *priv) | |||
731 | fifo_size--; | 745 | fifo_size--; |
732 | } | 746 | } |
733 | size = min(xmit->head - xmit->tail, fifo_size); | 747 | size = min(xmit->head - xmit->tail, fifo_size); |
748 | if (size < 0) | ||
749 | size = fifo_size; | ||
750 | |||
734 | tx_size = pop_tx(priv, xmit->buf, size); | 751 | tx_size = pop_tx(priv, xmit->buf, size); |
735 | if (tx_size > 0) { | 752 | if (tx_size > 0) { |
736 | ret = pch_uart_hal_write(priv, xmit->buf, tx_size); | 753 | ret = pch_uart_hal_write(priv, xmit->buf, tx_size); |
@@ -740,8 +757,10 @@ static unsigned int handle_tx(struct eg20t_port *priv) | |||
740 | 757 | ||
741 | priv->tx_empty = tx_empty; | 758 | priv->tx_empty = tx_empty; |
742 | 759 | ||
743 | if (tx_empty) | 760 | if (tx_empty) { |
744 | pch_uart_hal_disable_interrupt(priv, PCH_UART_HAL_TX_INT); | 761 | pch_uart_hal_disable_interrupt(priv, PCH_UART_HAL_TX_INT); |
762 | uart_write_wakeup(port); | ||
763 | } | ||
745 | 764 | ||
746 | return PCH_UART_HANDLED_TX_INT; | 765 | return PCH_UART_HANDLED_TX_INT; |
747 | } | 766 | } |
@@ -750,11 +769,16 @@ static unsigned int dma_handle_tx(struct eg20t_port *priv) | |||
750 | { | 769 | { |
751 | struct uart_port *port = &priv->port; | 770 | struct uart_port *port = &priv->port; |
752 | struct circ_buf *xmit = &port->state->xmit; | 771 | struct circ_buf *xmit = &port->state->xmit; |
753 | struct scatterlist *sg = &priv->sg_tx; | 772 | struct scatterlist *sg; |
754 | int nent; | 773 | int nent; |
755 | int fifo_size; | 774 | int fifo_size; |
756 | int tx_empty; | 775 | int tx_empty; |
757 | struct dma_async_tx_descriptor *desc; | 776 | struct dma_async_tx_descriptor *desc; |
777 | int num; | ||
778 | int i; | ||
779 | int bytes; | ||
780 | int size; | ||
781 | int rem; | ||
758 | 782 | ||
759 | if (!priv->start_tx) { | 783 | if (!priv->start_tx) { |
760 | pr_info("%s:Tx isn't started. (%lu)\n", __func__, jiffies); | 784 | pr_info("%s:Tx isn't started. (%lu)\n", __func__, jiffies); |
@@ -772,37 +796,68 @@ static unsigned int dma_handle_tx(struct eg20t_port *priv) | |||
772 | fifo_size--; | 796 | fifo_size--; |
773 | } | 797 | } |
774 | 798 | ||
775 | pch_uart_hal_disable_interrupt(priv, PCH_UART_HAL_TX_INT); | 799 | bytes = min((int)CIRC_CNT(xmit->head, xmit->tail, |
800 | UART_XMIT_SIZE), CIRC_CNT_TO_END(xmit->head, | ||
801 | xmit->tail, UART_XMIT_SIZE)); | ||
802 | if (!bytes) { | ||
803 | pch_uart_hal_disable_interrupt(priv, PCH_UART_HAL_TX_INT); | ||
804 | uart_write_wakeup(port); | ||
805 | return 0; | ||
806 | } | ||
807 | |||
808 | if (bytes > fifo_size) { | ||
809 | num = bytes / fifo_size + 1; | ||
810 | size = fifo_size; | ||
811 | rem = bytes % fifo_size; | ||
812 | } else { | ||
813 | num = 1; | ||
814 | size = bytes; | ||
815 | rem = bytes; | ||
816 | } | ||
776 | 817 | ||
777 | priv->tx_dma_use = 1; | 818 | priv->tx_dma_use = 1; |
778 | 819 | ||
779 | sg_init_table(&priv->sg_tx, 1); /* Initialize SG table */ | 820 | priv->sg_tx_p = kzalloc(sizeof(struct scatterlist)*num, GFP_ATOMIC); |
821 | |||
822 | sg_init_table(priv->sg_tx_p, num); /* Initialize SG table */ | ||
823 | sg = priv->sg_tx_p; | ||
780 | 824 | ||
781 | sg_set_page(&priv->sg_tx, virt_to_page(xmit->buf), | 825 | for (i = 0; i < num; i++, sg++) { |
782 | UART_XMIT_SIZE, (int)xmit->buf & ~PAGE_MASK); | 826 | if (i == (num - 1)) |
827 | sg_set_page(sg, virt_to_page(xmit->buf), | ||
828 | rem, fifo_size * i); | ||
829 | else | ||
830 | sg_set_page(sg, virt_to_page(xmit->buf), | ||
831 | size, fifo_size * i); | ||
832 | } | ||
783 | 833 | ||
784 | nent = dma_map_sg(port->dev, &priv->sg_tx, 1, DMA_TO_DEVICE); | 834 | sg = priv->sg_tx_p; |
835 | nent = dma_map_sg(port->dev, sg, num, DMA_TO_DEVICE); | ||
785 | if (!nent) { | 836 | if (!nent) { |
786 | pr_err("%s:dma_map_sg Failed\n", __func__); | 837 | pr_err("%s:dma_map_sg Failed\n", __func__); |
787 | return 0; | 838 | return 0; |
788 | } | 839 | } |
789 | 840 | priv->nent = nent; | |
790 | sg->offset = xmit->tail & (UART_XMIT_SIZE - 1); | 841 | |
791 | sg_dma_address(sg) = (sg_dma_address(sg) & ~(UART_XMIT_SIZE - 1)) + | 842 | for (i = 0; i < nent; i++, sg++) { |
792 | sg->offset; | 843 | sg->offset = (xmit->tail & (UART_XMIT_SIZE - 1)) + |
793 | sg_dma_len(sg) = min((int)CIRC_CNT(xmit->head, xmit->tail, | 844 | fifo_size * i; |
794 | UART_XMIT_SIZE), CIRC_CNT_TO_END(xmit->head, | 845 | sg_dma_address(sg) = (sg_dma_address(sg) & |
795 | xmit->tail, UART_XMIT_SIZE)); | 846 | ~(UART_XMIT_SIZE - 1)) + sg->offset; |
847 | if (i == (nent - 1)) | ||
848 | sg_dma_len(sg) = rem; | ||
849 | else | ||
850 | sg_dma_len(sg) = size; | ||
851 | } | ||
796 | 852 | ||
797 | desc = priv->chan_tx->device->device_prep_slave_sg(priv->chan_tx, | 853 | desc = priv->chan_tx->device->device_prep_slave_sg(priv->chan_tx, |
798 | sg, nent, DMA_TO_DEVICE, DMA_PREP_INTERRUPT | DMA_CTRL_ACK); | 854 | priv->sg_tx_p, nent, DMA_TO_DEVICE, |
855 | DMA_PREP_INTERRUPT | DMA_CTRL_ACK); | ||
799 | if (!desc) { | 856 | if (!desc) { |
800 | pr_err("%s:device_prep_slave_sg Failed\n", __func__); | 857 | pr_err("%s:device_prep_slave_sg Failed\n", __func__); |
801 | return 0; | 858 | return 0; |
802 | } | 859 | } |
803 | 860 | dma_sync_sg_for_device(port->dev, priv->sg_tx_p, nent, DMA_TO_DEVICE); | |
804 | dma_sync_sg_for_device(port->dev, sg, 1, DMA_TO_DEVICE); | ||
805 | |||
806 | priv->desc_tx = desc; | 861 | priv->desc_tx = desc; |
807 | desc->callback = pch_dma_tx_complete; | 862 | desc->callback = pch_dma_tx_complete; |
808 | desc->callback_param = priv; | 863 | desc->callback_param = priv; |
@@ -857,10 +912,16 @@ static irqreturn_t pch_uart_interrupt(int irq, void *dev_id) | |||
857 | } | 912 | } |
858 | break; | 913 | break; |
859 | case PCH_UART_IID_RDR: /* Received Data Ready */ | 914 | case PCH_UART_IID_RDR: /* Received Data Ready */ |
860 | if (priv->use_dma) | 915 | if (priv->use_dma) { |
916 | pch_uart_hal_disable_interrupt(priv, | ||
917 | PCH_UART_HAL_RX_INT); | ||
861 | ret = dma_handle_rx(priv); | 918 | ret = dma_handle_rx(priv); |
862 | else | 919 | if (!ret) |
920 | pch_uart_hal_enable_interrupt(priv, | ||
921 | PCH_UART_HAL_RX_INT); | ||
922 | } else { | ||
863 | ret = handle_rx(priv); | 923 | ret = handle_rx(priv); |
924 | } | ||
864 | break; | 925 | break; |
865 | case PCH_UART_IID_RDR_TO: /* Received Data Ready | 926 | case PCH_UART_IID_RDR_TO: /* Received Data Ready |
866 | (FIFO Timeout) */ | 927 | (FIFO Timeout) */ |