[PATCH 2/5] serial: imx: add DMA support for imx6
Huang Shijie
b32955 at freescale.com
Wed Jul 3 01:20:20 EDT 2013
于 2013年07月03日 11:38, Shawn Guo 写道:
>> static void imx_enable_dma(struct imx_port *sport)
>> > +{
>> > + unsigned long temp;
>> > + struct tty_port *port =&sport->port.state->port;
>> > +
>> > + port->low_latency = 1;
>> > + INIT_WORK(&sport->tsk_dma_tx, dma_tx_work);
>> > + INIT_WORK(&sport->tsk_dma_rx, dma_rx_work);
>> > + init_waitqueue_head(&sport->dma_wait);
> I had a hard time to understand why these work and wait queue are
> necessarily needed here.
Blame it on the sdma driver.
For example, we receive some data in the RXFIFO, normally , the
interrupt handler will
arise a DMA to fetch the data. We will call the
dmaengine_prep_slave_sg() to prepare for the DMA,
but sdma will call:
->sdma_prep_slave_sg() -->sdma_load_context() --> sdma_run_channel0()
the sdma_run_channel0() will poll for 500us(we have found the 500us is
not long enough in some case),
that's why i use the work queue:
We should not delay such long in interrupt context.
The same reason for TX.
> I haven't totally understand it. But it looks like to me that the
> implementation might be complexer than it needs to be. Can you please
> take a look at serial-tegra.c to see how the DMA is supported there?
serial-tegra.c arises the dma in the interrupt context. maybe its
dmaengine is better then the imx-sdma.
> It looks much neater than the changes we have here.
>
>> > +
>> > + /* set UCR1 */
>> > + temp = readl(sport->port.membase + UCR1);
>> > + temp |= UCR1_RDMAEN | UCR1_TDMAEN | UCR1_ATDMAEN |
>> > + /* wait for 4 idle frames and enable AGING Timer */
>> > + UCR1_ICD_REG(0);
>> > + writel(temp, sport->port.membase + UCR1);
>> > +
>> > + /* set UCR4 */
>> > + temp = readl(sport->port.membase + UCR4);
>> > + temp |= UCR4_IDDMAEN;
>> > + writel(temp, sport->port.membase + UCR4);
>> > +}
>> > +
>> > +static void imx_disable_dma(struct imx_port *sport)
>> > +{
>> > + unsigned long temp;
>> > + struct tty_port *port =&sport->port.state->port;
>> > +
>> > + /* clear UCR1 */
>> > + temp = readl(sport->port.membase + UCR1);
>> > + temp&= ~(UCR1_RDMAEN | UCR1_TDMAEN | UCR1_ATDMAEN);
>> > + writel(temp, sport->port.membase + UCR1);
>> > +
>> > + /* clear UCR2 */
>> > + temp = readl(sport->port.membase + UCR2);
>> > + temp&= ~(UCR2_CTSC | UCR2_CTS);
>> > + writel(temp, sport->port.membase + UCR2);
>> > +
>> > + /* clear UCR4 */
>> > + temp = readl(sport->port.membase + UCR4);
>> > + temp&= ~UCR4_IDDMAEN;
>> > + writel(temp, sport->port.membase + UCR4);
>> > +
>> > + sport->dma_is_enabled = 0;
>> > + sport->dma_is_inited = 0;
> Shouldn't dma_is_inited be reset in imx_uart_dma_exit() for better?
yes. it's ok to set there.
> (I haven't looked at the necessity of these flags. But please save
> the use of them if we can.)
>
>> > +
>> > + port->low_latency = 0;
>> > +}
>> > +
>> > /* half the RX buffer size */
>> > #define CTSTL 16
>> >
>> > @@ -870,6 +1250,14 @@ static void imx_shutdown(struct uart_port *port)
>> > unsigned long temp;
>> > unsigned long flags;
>> >
>> > + if (sport->dma_is_enabled) {
>> > + /* We have to wait for the DMA to finish. */
>> > + wait_event(sport->dma_wait,
>> > + !sport->dma_is_rxing&& !sport->dma_is_txing);
>> > + imx_stop_rx(port);
>> > + imx_uart_dma_exit(sport);
>> > + }
>> > +
>> > spin_lock_irqsave(&sport->port.lock, flags);
>> > temp = readl(sport->port.membase + UCR2);
>> > temp&= ~(UCR2_TXEN);
>> > @@ -910,6 +1298,10 @@ static void imx_shutdown(struct uart_port *port)
>> > temp&= ~(UCR1_IREN);
>> >
>> > writel(temp, sport->port.membase + UCR1);
>> > +
>> > + if (sport->dma_is_enabled)
>> > + imx_disable_dma(sport);
>> > +
> Shouldn't imx_disable_dma() be called before imx_uart_dma_exit()
> logically?
>
i think it's ok.
I have forgotten why i puted it there. The dma support patch is kept in
my hand for a long time....
I will try to put the imx_disable_dma() in the imx_uart_dma_exit().
>> > spin_unlock_irqrestore(&sport->port.lock, flags);
>> >
>> > clk_disable_unprepare(sport->clk_per);
>> > @@ -956,6 +1348,13 @@ imx_set_termios(struct uart_port *port, struct ktermios *termios,
>> > if (sport->have_rtscts) {
>> > ucr2&= ~UCR2_IRTS;
>> > ucr2 |= UCR2_CTSC;
>> > +
>> > + /* Can we enable the DMA support? */
>> > + if (is_imx6_uart(sport)&& !uart_console(port)
>> > + && !sport->dma_is_inited) {
>> > + if (!imx_uart_dma_init(sport))
>> > + sport->dma_is_inited = 1;
> I think the setting of the flag can just be handled inside
> imx_uart_dma_init().
>
ok.
>> > + }
>> > } else {
>> > termios->c_cflag&= ~CRTSCTS;
>> > }
>> > @@ -1069,6 +1468,10 @@ imx_set_termios(struct uart_port *port, struct ktermios *termios,
>> > if (UART_ENABLE_MS(&sport->port, termios->c_cflag))
>> > imx_enable_ms(&sport->port);
>> >
>> > + if (sport->dma_is_inited&& !sport->dma_is_enabled) {
>> > + imx_enable_dma(sport);
>> > + sport->dma_is_enabled = 1;
> Ditto
>
>> > + }
> I see imx_disable_dma() and imx_uart_dma_exit() are called in
> imx_shutdown(). Why can not imx_uart_dma_init() and imx_enable_dma()
> be called in imx_startup()?
i intend to binding the DMA with the RTS/CTS together.
The setting of rts/cts is not in imx_startup(), but in the
imx_set_termios().
thanks
Huang Shijie
More information about the linux-arm-kernel
mailing list